From ce28c210871a248ec6d836adeaa75e2c3c665faf Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Fri, 1 Dec 2017 03:31:20 +0100 Subject: DROP openbsc PATH ELEMENT FOR MERGING Change-Id: I8f6dd67b53c1ecff88e17baa4f7417a68466990f --- AUTHORS | 7 + COPYING | 661 ++++ Makefile.am | 14 + README | 32 + configure.ac | 167 + contrib/a-link/sccp-split-by-con.lua | 170 + contrib/bsc-test/README | 1 + contrib/bsc-test/all_dial | 8 + contrib/bsc-test/dial.sh | 11 + contrib/bsc-test/drop-oml.sh | 6 + contrib/bsc-test/drop.sh | 8 + contrib/bsc-test/hangup | 4 + contrib/bsc-test/msc.sh | 8 + contrib/bsc_control.py | 104 + contrib/bt.py | 33 + contrib/convert_to_enum.py | 37 + contrib/gprs/gb-proxy-unblock-bug.py | 58 + contrib/gprs/gprs-bssgp-histogram.lua | 78 + contrib/gprs/gprs-buffer-count.lua | 80 + contrib/gprs/gprs-split-trace-by-tlli.lua | 46 + contrib/gprs/gprs-verify-nu.lua | 59 + contrib/hlr-remove-old.sql | 18 + contrib/mgcp_server.py | 60 + contrib/nat/test_regexp.c | 30 + contrib/rtp/rtp_replay.st | 17 + contrib/rtp/rtp_replay_shared.st | 108 + contrib/rtp/rtp_replay_sip.st | 87 + contrib/rtp/timestamp_rtp.lua | 28 + contrib/sms/fill-hlr.st | 66 + contrib/sms/hlr-query.st | 10 + contrib/sms/sqlite-probe.tap.d | 5 + contrib/systemd/osmo-bsc-mgcp.service | 9 + contrib/systemd/osmo-bsc.service | 10 + contrib/systemd/osmo-nitb.service | 9 + doc/BS11-OML.txt | 31 + doc/Makefile.am | 1 + doc/call-routing.txt | 25 + doc/channel_release.txt | 95 + doc/control-interface.txt | 21 + doc/e1-data-model.txt | 172 + doc/examples/Makefile.am | 23 + doc/examples/osmo-bsc/osmo-bsc.cfg | 100 + doc/examples/osmo-bsc_mgcp/mgcp.cfg | 18 + doc/examples/osmo-bsc_nat/black-list.cfg | 1 + doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg | 64 + doc/examples/osmo-gbproxy/osmo-gbproxy.cfg | 25 + .../osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg | 164 + doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg | 84 + doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg | 148 + doc/examples/osmo-nitb/bs11/openbsc.cfg | 56 + .../osmo-nitb/nanobts/openbsc-multitrx.cfg | 99 + doc/examples/osmo-nitb/nanobts/openbsc.cfg | 77 + .../osmo-nitb/nokia/openbsc_nokia_3trx.cfg | 117 + doc/examples/osmo-nitb/rbs2308/openbsc.cfg | 221 ++ doc/examples/osmo-sgsn/osmo-sgsn.cfg | 26 + doc/gsm-hopping.txt | 54 + doc/handover.txt | 89 + doc/ipa-sccp.txt | 94 + doc/oml-interface.txt | 22 + doc/paging.txt | 48 + git-version-gen | 151 + include/Makefile.am | 3 + include/compat_af_isdn.h | 39 + include/mISDNif.h | 387 +++ include/openbsc/Makefile.am | 19 + include/openbsc/abis_nm.h | 186 ++ include/openbsc/abis_om2000.h | 98 + include/openbsc/abis_rsl.h | 107 + include/openbsc/arfcn_range_encode.h | 26 + include/openbsc/auth.h | 17 + include/openbsc/bsc_api.h | 55 + include/openbsc/bsc_msc.h | 59 + include/openbsc/bsc_nat.h | 480 +++ include/openbsc/bsc_nat_callstats.h | 55 + include/openbsc/bsc_nat_sccp.h | 101 + include/openbsc/bsc_rll.h | 19 + include/openbsc/bss.h | 18 + include/openbsc/chan_alloc.h | 65 + include/openbsc/control_cmd.h | 163 + include/openbsc/control_if.h | 21 + include/openbsc/crc24.h | 10 + include/openbsc/db.h | 82 + include/openbsc/debug.h | 65 + include/openbsc/e1_config.h | 11 + include/openbsc/gb_proxy.h | 38 + include/openbsc/gprs_gmm.h | 19 + include/openbsc/gprs_llc.h | 202 ++ include/openbsc/gprs_sgsn.h | 222 ++ include/openbsc/gsm_04_08.h | 75 + include/openbsc/gsm_04_08_gprs.h | 381 +++ include/openbsc/gsm_04_11.h | 40 + include/openbsc/gsm_04_80.h | 20 + include/openbsc/gsm_data.h | 408 +++ include/openbsc/gsm_data_shared.h | 757 +++++ include/openbsc/gsm_subscriber.h | 111 + include/openbsc/handover.h | 17 + include/openbsc/handover_decision.h | 7 + include/openbsc/ipaccess.h | 65 + include/openbsc/meas_rep.h | 87 + include/openbsc/mgcp.h | 203 ++ include/openbsc/mgcp_internal.h | 172 + include/openbsc/misdn.h | 27 + include/openbsc/mncc.h | 191 ++ include/openbsc/mncc_int.h | 12 + include/openbsc/network_listen.h | 16 + include/openbsc/openbscdefines.h | 34 + include/openbsc/osmo_bsc.h | 51 + include/openbsc/osmo_bsc_grace.h | 31 + include/openbsc/osmo_bsc_rf.h | 64 + include/openbsc/osmo_msc.h | 11 + include/openbsc/osmo_msc_data.h | 116 + include/openbsc/paging.h | 75 + include/openbsc/rest_octets.h | 132 + include/openbsc/rrlp.h | 7 + include/openbsc/rs232.h | 9 + include/openbsc/rtp_proxy.h | 91 + include/openbsc/sgsn.h | 69 + include/openbsc/signal.h | 244 ++ include/openbsc/silent_call.h | 12 + include/openbsc/sms_queue.h | 17 + include/openbsc/socket.h | 14 + include/openbsc/system_information.h | 10 + include/openbsc/token_auth.h | 7 + include/openbsc/transaction.h | 75 + include/openbsc/trau_mux.h | 55 + include/openbsc/trau_upqueue.h | 7 + include/openbsc/ussd.h | 10 + include/openbsc/vty.h | 49 + openbsc.pc.in | 11 + openbsc/AUTHORS | 7 - openbsc/COPYING | 661 ---- openbsc/Makefile.am | 14 - openbsc/README | 32 - openbsc/configure.ac | 167 - openbsc/contrib/a-link/sccp-split-by-con.lua | 170 - openbsc/contrib/bsc-test/README | 1 - openbsc/contrib/bsc-test/all_dial | 8 - openbsc/contrib/bsc-test/dial.sh | 11 - openbsc/contrib/bsc-test/drop-oml.sh | 6 - openbsc/contrib/bsc-test/drop.sh | 8 - openbsc/contrib/bsc-test/hangup | 4 - openbsc/contrib/bsc-test/msc.sh | 8 - openbsc/contrib/bsc_control.py | 104 - openbsc/contrib/bt.py | 33 - openbsc/contrib/convert_to_enum.py | 37 - openbsc/contrib/gprs/gb-proxy-unblock-bug.py | 58 - openbsc/contrib/gprs/gprs-bssgp-histogram.lua | 78 - openbsc/contrib/gprs/gprs-buffer-count.lua | 80 - openbsc/contrib/gprs/gprs-split-trace-by-tlli.lua | 46 - openbsc/contrib/gprs/gprs-verify-nu.lua | 59 - openbsc/contrib/hlr-remove-old.sql | 18 - openbsc/contrib/mgcp_server.py | 60 - openbsc/contrib/nat/test_regexp.c | 30 - openbsc/contrib/rtp/rtp_replay.st | 17 - openbsc/contrib/rtp/rtp_replay_shared.st | 108 - openbsc/contrib/rtp/rtp_replay_sip.st | 87 - openbsc/contrib/rtp/timestamp_rtp.lua | 28 - openbsc/contrib/sms/fill-hlr.st | 66 - openbsc/contrib/sms/hlr-query.st | 10 - openbsc/contrib/sms/sqlite-probe.tap.d | 5 - openbsc/contrib/systemd/osmo-bsc-mgcp.service | 9 - openbsc/contrib/systemd/osmo-bsc.service | 10 - openbsc/contrib/systemd/osmo-nitb.service | 9 - openbsc/doc/BS11-OML.txt | 31 - openbsc/doc/Makefile.am | 1 - openbsc/doc/call-routing.txt | 25 - openbsc/doc/channel_release.txt | 95 - openbsc/doc/control-interface.txt | 21 - openbsc/doc/e1-data-model.txt | 172 - openbsc/doc/examples/Makefile.am | 23 - openbsc/doc/examples/osmo-bsc/osmo-bsc.cfg | 100 - openbsc/doc/examples/osmo-bsc_mgcp/mgcp.cfg | 18 - openbsc/doc/examples/osmo-bsc_nat/black-list.cfg | 1 - openbsc/doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg | 64 - openbsc/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg | 25 - .../osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg | 164 - .../examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg | 84 - .../examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg | 148 - openbsc/doc/examples/osmo-nitb/bs11/openbsc.cfg | 56 - .../osmo-nitb/nanobts/openbsc-multitrx.cfg | 99 - openbsc/doc/examples/osmo-nitb/nanobts/openbsc.cfg | 77 - .../osmo-nitb/nokia/openbsc_nokia_3trx.cfg | 117 - openbsc/doc/examples/osmo-nitb/rbs2308/openbsc.cfg | 221 -- openbsc/doc/examples/osmo-sgsn/osmo-sgsn.cfg | 26 - openbsc/doc/gsm-hopping.txt | 54 - openbsc/doc/handover.txt | 89 - openbsc/doc/ipa-sccp.txt | 94 - openbsc/doc/oml-interface.txt | 22 - openbsc/doc/paging.txt | 48 - openbsc/git-version-gen | 151 - openbsc/include/Makefile.am | 3 - openbsc/include/compat_af_isdn.h | 39 - openbsc/include/mISDNif.h | 387 --- openbsc/include/openbsc/Makefile.am | 19 - openbsc/include/openbsc/abis_nm.h | 186 -- openbsc/include/openbsc/abis_om2000.h | 98 - openbsc/include/openbsc/abis_rsl.h | 107 - openbsc/include/openbsc/arfcn_range_encode.h | 26 - openbsc/include/openbsc/auth.h | 17 - openbsc/include/openbsc/bsc_api.h | 55 - openbsc/include/openbsc/bsc_msc.h | 59 - openbsc/include/openbsc/bsc_nat.h | 480 --- openbsc/include/openbsc/bsc_nat_callstats.h | 55 - openbsc/include/openbsc/bsc_nat_sccp.h | 101 - openbsc/include/openbsc/bsc_rll.h | 19 - openbsc/include/openbsc/bss.h | 18 - openbsc/include/openbsc/chan_alloc.h | 65 - openbsc/include/openbsc/control_cmd.h | 163 - openbsc/include/openbsc/control_if.h | 21 - openbsc/include/openbsc/crc24.h | 10 - openbsc/include/openbsc/db.h | 82 - openbsc/include/openbsc/debug.h | 65 - openbsc/include/openbsc/e1_config.h | 11 - openbsc/include/openbsc/gb_proxy.h | 38 - openbsc/include/openbsc/gprs_gmm.h | 19 - openbsc/include/openbsc/gprs_llc.h | 202 -- openbsc/include/openbsc/gprs_sgsn.h | 222 -- openbsc/include/openbsc/gsm_04_08.h | 75 - openbsc/include/openbsc/gsm_04_08_gprs.h | 381 --- openbsc/include/openbsc/gsm_04_11.h | 40 - openbsc/include/openbsc/gsm_04_80.h | 20 - openbsc/include/openbsc/gsm_data.h | 408 --- openbsc/include/openbsc/gsm_data_shared.h | 757 ----- openbsc/include/openbsc/gsm_subscriber.h | 111 - openbsc/include/openbsc/handover.h | 17 - openbsc/include/openbsc/handover_decision.h | 7 - openbsc/include/openbsc/ipaccess.h | 65 - openbsc/include/openbsc/meas_rep.h | 87 - openbsc/include/openbsc/mgcp.h | 203 -- openbsc/include/openbsc/mgcp_internal.h | 172 - openbsc/include/openbsc/misdn.h | 27 - openbsc/include/openbsc/mncc.h | 191 -- openbsc/include/openbsc/mncc_int.h | 12 - openbsc/include/openbsc/network_listen.h | 16 - openbsc/include/openbsc/openbscdefines.h | 34 - openbsc/include/openbsc/osmo_bsc.h | 51 - openbsc/include/openbsc/osmo_bsc_grace.h | 31 - openbsc/include/openbsc/osmo_bsc_rf.h | 64 - openbsc/include/openbsc/osmo_msc.h | 11 - openbsc/include/openbsc/osmo_msc_data.h | 116 - openbsc/include/openbsc/paging.h | 75 - openbsc/include/openbsc/rest_octets.h | 132 - openbsc/include/openbsc/rrlp.h | 7 - openbsc/include/openbsc/rs232.h | 9 - openbsc/include/openbsc/rtp_proxy.h | 91 - openbsc/include/openbsc/sgsn.h | 69 - openbsc/include/openbsc/signal.h | 244 -- openbsc/include/openbsc/silent_call.h | 12 - openbsc/include/openbsc/sms_queue.h | 17 - openbsc/include/openbsc/socket.h | 14 - openbsc/include/openbsc/system_information.h | 10 - openbsc/include/openbsc/token_auth.h | 7 - openbsc/include/openbsc/transaction.h | 75 - openbsc/include/openbsc/trau_mux.h | 55 - openbsc/include/openbsc/trau_upqueue.h | 7 - openbsc/include/openbsc/ussd.h | 10 - openbsc/include/openbsc/vty.h | 49 - openbsc/openbsc.pc.in | 11 - openbsc/osmoappdesc.py | 52 - openbsc/src/Makefile.am | 13 - openbsc/src/gprs/.gitignore | 2 - openbsc/src/gprs/Makefile.am | 24 - openbsc/src/gprs/crc24.c | 67 - openbsc/src/gprs/gb_proxy.c | 690 ---- openbsc/src/gprs/gb_proxy_main.c | 312 -- openbsc/src/gprs/gb_proxy_vty.c | 106 - openbsc/src/gprs/gprs_gmm.c | 1634 ---------- openbsc/src/gprs/gprs_llc.c | 957 ------ openbsc/src/gprs/gprs_llc_vty.c | 108 - openbsc/src/gprs/gprs_sgsn.c | 404 --- openbsc/src/gprs/gprs_sndcp.c | 620 ---- openbsc/src/gprs/gprs_sndcp.h | 53 - openbsc/src/gprs/gprs_sndcp_vty.c | 72 - openbsc/src/gprs/osmo_sgsn.cfg | 23 - openbsc/src/gprs/sgsn_libgtp.c | 622 ---- openbsc/src/gprs/sgsn_main.c | 380 --- openbsc/src/gprs/sgsn_vty.c | 448 --- openbsc/src/ipaccess/Makefile.am | 32 - openbsc/src/ipaccess/ipaccess-config.c | 1024 ------ openbsc/src/ipaccess/ipaccess-find.c | 208 -- openbsc/src/ipaccess/ipaccess-firmware.c | 135 - openbsc/src/ipaccess/ipaccess-proxy.c | 1202 ------- openbsc/src/ipaccess/network_listen.c | 254 -- openbsc/src/libbsc/Makefile.am | 26 - openbsc/src/libbsc/abis_nm.c | 2741 ---------------- openbsc/src/libbsc/abis_nm_ipaccess.c | 87 - openbsc/src/libbsc/abis_nm_vty.c | 192 -- openbsc/src/libbsc/abis_om2000.c | 1609 ---------- openbsc/src/libbsc/abis_om2000_vty.c | 466 --- openbsc/src/libbsc/abis_rsl.c | 2204 ------------- openbsc/src/libbsc/arfcn_range_encode.c | 305 -- openbsc/src/libbsc/bsc_api.c | 854 ----- openbsc/src/libbsc/bsc_init.c | 501 --- openbsc/src/libbsc/bsc_msc.c | 273 -- openbsc/src/libbsc/bsc_rf_ctrl.c | 530 ---- openbsc/src/libbsc/bsc_rll.c | 141 - openbsc/src/libbsc/bsc_vty.c | 3142 ------------------- openbsc/src/libbsc/bts_ericsson_rbs2000.c | 276 -- openbsc/src/libbsc/bts_init.c | 30 - openbsc/src/libbsc/bts_ipaccess_nanobts.c | 650 ---- openbsc/src/libbsc/bts_nokia_site.c | 1740 ----------- openbsc/src/libbsc/bts_siemens_bs11.c | 607 ---- openbsc/src/libbsc/bts_sysmobts.c | 59 - openbsc/src/libbsc/bts_unknown.c | 40 - openbsc/src/libbsc/chan_alloc.c | 493 --- openbsc/src/libbsc/e1_config.c | 303 -- openbsc/src/libbsc/gsm_04_08_utils.c | 651 ---- openbsc/src/libbsc/gsm_subscriber_base.c | 149 - openbsc/src/libbsc/handover_decision.c | 297 -- openbsc/src/libbsc/handover_logic.c | 374 --- openbsc/src/libbsc/meas_proc.c | 84 - openbsc/src/libbsc/meas_rep.c | 115 - openbsc/src/libbsc/paging.c | 436 --- openbsc/src/libbsc/rest_octets.c | 437 --- openbsc/src/libbsc/system_information.c | 844 ----- openbsc/src/libcommon/Makefile.am | 7 - openbsc/src/libcommon/bsc_version.c | 30 - openbsc/src/libcommon/common_vty.c | 244 -- openbsc/src/libcommon/debug.c | 212 -- openbsc/src/libcommon/gsm_data.c | 461 --- openbsc/src/libcommon/gsm_data_shared.c | 521 ---- openbsc/src/libcommon/socket.c | 111 - openbsc/src/libcommon/talloc_ctx.c | 38 - openbsc/src/libctrl/Makefile.am | 7 - openbsc/src/libctrl/control_cmd.c | 493 --- openbsc/src/libctrl/control_if.c | 707 ----- openbsc/src/libgb/Makefile.am | 9 - openbsc/src/libmgcp/Makefile.am | 7 - openbsc/src/libmgcp/mgcp_network.c | 706 ----- openbsc/src/libmgcp/mgcp_protocol.c | 1203 ------- openbsc/src/libmgcp/mgcp_vty.c | 941 ------ openbsc/src/libmsc/Makefile.am | 23 - openbsc/src/libmsc/auth.c | 132 - openbsc/src/libmsc/db.c | 1412 --------- openbsc/src/libmsc/gsm_04_08.c | 3283 -------------------- openbsc/src/libmsc/gsm_04_11.c | 1004 ------ openbsc/src/libmsc/gsm_04_80.c | 175 -- openbsc/src/libmsc/gsm_subscriber.c | 472 --- openbsc/src/libmsc/mncc.c | 109 - openbsc/src/libmsc/mncc_builtin.c | 424 --- openbsc/src/libmsc/mncc_sock.c | 365 --- openbsc/src/libmsc/osmo_msc.c | 177 -- openbsc/src/libmsc/rrlp.c | 104 - openbsc/src/libmsc/silent_call.c | 143 - openbsc/src/libmsc/smpp_openbsc.c | 553 ---- openbsc/src/libmsc/smpp_smsc.c | 942 ------ openbsc/src/libmsc/smpp_smsc.h | 134 - openbsc/src/libmsc/smpp_utils.c | 62 - openbsc/src/libmsc/smpp_vty.c | 516 --- openbsc/src/libmsc/sms_queue.c | 500 --- openbsc/src/libmsc/token_auth.c | 160 - openbsc/src/libmsc/transaction.c | 164 - openbsc/src/libmsc/ussd.c | 79 - openbsc/src/libmsc/vty_interface_layer3.c | 987 ------ openbsc/src/libtrau/Makefile.am | 7 - openbsc/src/libtrau/rtp_proxy.c | 757 ----- openbsc/src/libtrau/trau_mux.c | 329 -- openbsc/src/libtrau/trau_upqueue.c | 27 - openbsc/src/osmo-bsc/Makefile.am | 20 - openbsc/src/osmo-bsc/osmo_bsc_api.c | 362 --- openbsc/src/osmo-bsc/osmo_bsc_audio.c | 73 - openbsc/src/osmo-bsc/osmo_bsc_bssap.c | 556 ---- openbsc/src/osmo-bsc/osmo_bsc_ctrl.c | 505 --- openbsc/src/osmo-bsc/osmo_bsc_filter.c | 291 -- openbsc/src/osmo-bsc/osmo_bsc_grace.c | 151 - openbsc/src/osmo-bsc/osmo_bsc_main.c | 277 -- openbsc/src/osmo-bsc/osmo_bsc_msc.c | 505 --- openbsc/src/osmo-bsc/osmo_bsc_sccp.c | 300 -- openbsc/src/osmo-bsc/osmo_bsc_vty.c | 599 ---- openbsc/src/osmo-bsc_mgcp/Makefile.am | 10 - openbsc/src/osmo-bsc_mgcp/mgcp_main.c | 304 -- openbsc/src/osmo-bsc_nat/Makefile.am | 17 - openbsc/src/osmo-bsc_nat/bsc_filter.c | 216 -- openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c | 1049 ------- openbsc/src/osmo-bsc_nat/bsc_nat.c | 1609 ---------- openbsc/src/osmo-bsc_nat/bsc_nat_ctrl.c | 324 -- openbsc/src/osmo-bsc_nat/bsc_nat_filter.c | 435 --- openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c | 640 ---- openbsc/src/osmo-bsc_nat/bsc_nat_utils.c | 566 ---- openbsc/src/osmo-bsc_nat/bsc_nat_vty.c | 1160 ------- openbsc/src/osmo-bsc_nat/bsc_sccp.c | 249 -- openbsc/src/osmo-bsc_nat/bsc_ussd.c | 427 --- openbsc/src/osmo-nitb/Makefile.am | 20 - openbsc/src/osmo-nitb/bsc_hack.c | 348 --- openbsc/src/utils/Makefile.am | 21 - openbsc/src/utils/bs11_config.c | 949 ------ openbsc/src/utils/isdnsync.c | 189 -- openbsc/src/utils/smpp_mirror.c | 317 -- openbsc/tests/Makefile.am | 61 - openbsc/tests/abis/Makefile.am | 17 - openbsc/tests/abis/abis_test.c | 169 - openbsc/tests/abis/abis_test.ok | 17 - openbsc/tests/atlocal.in | 2 - openbsc/tests/bsc-nat/Makefile.am | 23 - openbsc/tests/bsc-nat/barr.cfg | 12 - openbsc/tests/bsc-nat/barr_dup.cfg | 2 - openbsc/tests/bsc-nat/bsc_data.c | 255 -- openbsc/tests/bsc-nat/bsc_nat_test.c | 1339 -------- openbsc/tests/bsc-nat/bsc_nat_test.ok | 39 - openbsc/tests/channel/Makefile.am | 12 - openbsc/tests/channel/channel_test.c | 96 - openbsc/tests/channel/channel_test.ok | 2 - openbsc/tests/db/Makefile.am | 17 - openbsc/tests/db/db_test.c | 118 - openbsc/tests/db/db_test.ok | 4 - openbsc/tests/gprs/Makefile.am | 8 - openbsc/tests/gprs/gprs_test.c | 51 - openbsc/tests/gprs/gprs_test.ok | 16 - openbsc/tests/gsm0408/Makefile.am | 11 - openbsc/tests/gsm0408/gsm0408_test.c | 104 - openbsc/tests/gsm0408/gsm0408_test.ok | 5 - openbsc/tests/mgcp/Makefile.am | 14 - openbsc/tests/mgcp/mgcp_test.c | 362 --- openbsc/tests/mgcp/mgcp_test.ok | 31 - openbsc/tests/si/Makefile.am | 12 - openbsc/tests/si/si_test.c | 156 - openbsc/tests/si/si_test.ok | 20 - openbsc/tests/smpp/Makefile.am | 12 - openbsc/tests/smpp/smpp_test.c | 73 - openbsc/tests/smpp/smpp_test.err | 2 - openbsc/tests/smpp/smpp_test.ok | 1 - openbsc/tests/sms.txt | 50 - openbsc/tests/testsuite.at | 61 - openbsc/tests/vty_test_runner.py | 142 - openbsc/tools/hlrstat.pl | 73 - osmoappdesc.py | 52 + src/Makefile.am | 13 + src/gprs/.gitignore | 2 + src/gprs/Makefile.am | 24 + src/gprs/crc24.c | 67 + src/gprs/gb_proxy.c | 690 ++++ src/gprs/gb_proxy_main.c | 312 ++ src/gprs/gb_proxy_vty.c | 106 + src/gprs/gprs_gmm.c | 1634 ++++++++++ src/gprs/gprs_llc.c | 957 ++++++ src/gprs/gprs_llc_vty.c | 108 + src/gprs/gprs_sgsn.c | 404 +++ src/gprs/gprs_sndcp.c | 620 ++++ src/gprs/gprs_sndcp.h | 53 + src/gprs/gprs_sndcp_vty.c | 72 + src/gprs/osmo_sgsn.cfg | 23 + src/gprs/sgsn_libgtp.c | 622 ++++ src/gprs/sgsn_main.c | 380 +++ src/gprs/sgsn_vty.c | 448 +++ src/ipaccess/Makefile.am | 32 + src/ipaccess/ipaccess-config.c | 1024 ++++++ src/ipaccess/ipaccess-find.c | 208 ++ src/ipaccess/ipaccess-firmware.c | 135 + src/ipaccess/ipaccess-proxy.c | 1202 +++++++ src/ipaccess/network_listen.c | 254 ++ src/libbsc/Makefile.am | 26 + src/libbsc/abis_nm.c | 2741 ++++++++++++++++ src/libbsc/abis_nm_ipaccess.c | 87 + src/libbsc/abis_nm_vty.c | 192 ++ src/libbsc/abis_om2000.c | 1609 ++++++++++ src/libbsc/abis_om2000_vty.c | 466 +++ src/libbsc/abis_rsl.c | 2204 +++++++++++++ src/libbsc/arfcn_range_encode.c | 305 ++ src/libbsc/bsc_api.c | 854 +++++ src/libbsc/bsc_init.c | 501 +++ src/libbsc/bsc_msc.c | 273 ++ src/libbsc/bsc_rf_ctrl.c | 530 ++++ src/libbsc/bsc_rll.c | 141 + src/libbsc/bsc_vty.c | 3142 +++++++++++++++++++ src/libbsc/bts_ericsson_rbs2000.c | 276 ++ src/libbsc/bts_init.c | 30 + src/libbsc/bts_ipaccess_nanobts.c | 650 ++++ src/libbsc/bts_nokia_site.c | 1740 +++++++++++ src/libbsc/bts_siemens_bs11.c | 607 ++++ src/libbsc/bts_sysmobts.c | 59 + src/libbsc/bts_unknown.c | 40 + src/libbsc/chan_alloc.c | 493 +++ src/libbsc/e1_config.c | 303 ++ src/libbsc/gsm_04_08_utils.c | 651 ++++ src/libbsc/gsm_subscriber_base.c | 149 + src/libbsc/handover_decision.c | 297 ++ src/libbsc/handover_logic.c | 374 +++ src/libbsc/meas_proc.c | 84 + src/libbsc/meas_rep.c | 115 + src/libbsc/paging.c | 436 +++ src/libbsc/rest_octets.c | 437 +++ src/libbsc/system_information.c | 844 +++++ src/libcommon/Makefile.am | 7 + src/libcommon/bsc_version.c | 30 + src/libcommon/common_vty.c | 244 ++ src/libcommon/debug.c | 212 ++ src/libcommon/gsm_data.c | 461 +++ src/libcommon/gsm_data_shared.c | 521 ++++ src/libcommon/socket.c | 111 + src/libcommon/talloc_ctx.c | 38 + src/libctrl/Makefile.am | 7 + src/libctrl/control_cmd.c | 493 +++ src/libctrl/control_if.c | 707 +++++ src/libgb/Makefile.am | 9 + src/libmgcp/Makefile.am | 7 + src/libmgcp/mgcp_network.c | 706 +++++ src/libmgcp/mgcp_protocol.c | 1203 +++++++ src/libmgcp/mgcp_vty.c | 941 ++++++ src/libmsc/Makefile.am | 23 + src/libmsc/auth.c | 132 + src/libmsc/db.c | 1412 +++++++++ src/libmsc/gsm_04_08.c | 3283 ++++++++++++++++++++ src/libmsc/gsm_04_11.c | 1004 ++++++ src/libmsc/gsm_04_80.c | 175 ++ src/libmsc/gsm_subscriber.c | 472 +++ src/libmsc/mncc.c | 109 + src/libmsc/mncc_builtin.c | 424 +++ src/libmsc/mncc_sock.c | 365 +++ src/libmsc/osmo_msc.c | 177 ++ src/libmsc/rrlp.c | 104 + src/libmsc/silent_call.c | 143 + src/libmsc/smpp_openbsc.c | 553 ++++ src/libmsc/smpp_smsc.c | 942 ++++++ src/libmsc/smpp_smsc.h | 134 + src/libmsc/smpp_utils.c | 62 + src/libmsc/smpp_vty.c | 516 +++ src/libmsc/sms_queue.c | 500 +++ src/libmsc/token_auth.c | 160 + src/libmsc/transaction.c | 164 + src/libmsc/ussd.c | 79 + src/libmsc/vty_interface_layer3.c | 987 ++++++ src/libtrau/Makefile.am | 7 + src/libtrau/rtp_proxy.c | 757 +++++ src/libtrau/trau_mux.c | 329 ++ src/libtrau/trau_upqueue.c | 27 + src/osmo-bsc/Makefile.am | 20 + src/osmo-bsc/osmo_bsc_api.c | 362 +++ src/osmo-bsc/osmo_bsc_audio.c | 73 + src/osmo-bsc/osmo_bsc_bssap.c | 556 ++++ src/osmo-bsc/osmo_bsc_ctrl.c | 505 +++ src/osmo-bsc/osmo_bsc_filter.c | 291 ++ src/osmo-bsc/osmo_bsc_grace.c | 151 + src/osmo-bsc/osmo_bsc_main.c | 277 ++ src/osmo-bsc/osmo_bsc_msc.c | 505 +++ src/osmo-bsc/osmo_bsc_sccp.c | 300 ++ src/osmo-bsc/osmo_bsc_vty.c | 599 ++++ src/osmo-bsc_mgcp/Makefile.am | 10 + src/osmo-bsc_mgcp/mgcp_main.c | 304 ++ src/osmo-bsc_nat/Makefile.am | 17 + src/osmo-bsc_nat/bsc_filter.c | 216 ++ src/osmo-bsc_nat/bsc_mgcp_utils.c | 1049 +++++++ src/osmo-bsc_nat/bsc_nat.c | 1609 ++++++++++ src/osmo-bsc_nat/bsc_nat_ctrl.c | 324 ++ src/osmo-bsc_nat/bsc_nat_filter.c | 435 +++ src/osmo-bsc_nat/bsc_nat_rewrite.c | 640 ++++ src/osmo-bsc_nat/bsc_nat_utils.c | 566 ++++ src/osmo-bsc_nat/bsc_nat_vty.c | 1160 +++++++ src/osmo-bsc_nat/bsc_sccp.c | 249 ++ src/osmo-bsc_nat/bsc_ussd.c | 427 +++ src/osmo-nitb/Makefile.am | 20 + src/osmo-nitb/bsc_hack.c | 348 +++ src/utils/Makefile.am | 21 + src/utils/bs11_config.c | 949 ++++++ src/utils/isdnsync.c | 189 ++ src/utils/smpp_mirror.c | 317 ++ tests/Makefile.am | 61 + tests/abis/Makefile.am | 17 + tests/abis/abis_test.c | 169 + tests/abis/abis_test.ok | 17 + tests/atlocal.in | 2 + tests/bsc-nat/Makefile.am | 23 + tests/bsc-nat/barr.cfg | 12 + tests/bsc-nat/barr_dup.cfg | 2 + tests/bsc-nat/bsc_data.c | 255 ++ tests/bsc-nat/bsc_nat_test.c | 1339 ++++++++ tests/bsc-nat/bsc_nat_test.ok | 39 + tests/channel/Makefile.am | 12 + tests/channel/channel_test.c | 96 + tests/channel/channel_test.ok | 2 + tests/db/Makefile.am | 17 + tests/db/db_test.c | 118 + tests/db/db_test.ok | 4 + tests/gprs/Makefile.am | 8 + tests/gprs/gprs_test.c | 51 + tests/gprs/gprs_test.ok | 16 + tests/gsm0408/Makefile.am | 11 + tests/gsm0408/gsm0408_test.c | 104 + tests/gsm0408/gsm0408_test.ok | 5 + tests/mgcp/Makefile.am | 14 + tests/mgcp/mgcp_test.c | 362 +++ tests/mgcp/mgcp_test.ok | 31 + tests/si/Makefile.am | 12 + tests/si/si_test.c | 156 + tests/si/si_test.ok | 20 + tests/smpp/Makefile.am | 12 + tests/smpp/smpp_test.c | 73 + tests/smpp/smpp_test.err | 2 + tests/smpp/smpp_test.ok | 1 + tests/sms.txt | 50 + tests/testsuite.at | 61 + tests/vty_test_runner.py | 142 + tools/hlrstat.pl | 73 + 592 files changed, 75145 insertions(+), 75145 deletions(-) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 Makefile.am create mode 100644 configure.ac create mode 100644 contrib/a-link/sccp-split-by-con.lua create mode 100644 contrib/bsc-test/README create mode 100644 contrib/bsc-test/all_dial create mode 100755 contrib/bsc-test/dial.sh create mode 100755 contrib/bsc-test/drop-oml.sh create mode 100755 contrib/bsc-test/drop.sh create mode 100644 contrib/bsc-test/hangup create mode 100755 contrib/bsc-test/msc.sh create mode 100755 contrib/bsc_control.py create mode 100755 contrib/bt.py create mode 100755 contrib/convert_to_enum.py create mode 100755 contrib/gprs/gb-proxy-unblock-bug.py create mode 100644 contrib/gprs/gprs-bssgp-histogram.lua create mode 100644 contrib/gprs/gprs-buffer-count.lua create mode 100644 contrib/gprs/gprs-split-trace-by-tlli.lua create mode 100644 contrib/gprs/gprs-verify-nu.lua create mode 100644 contrib/hlr-remove-old.sql create mode 100755 contrib/mgcp_server.py create mode 100644 contrib/nat/test_regexp.c create mode 100644 contrib/rtp/rtp_replay.st create mode 100644 contrib/rtp/rtp_replay_shared.st create mode 100644 contrib/rtp/rtp_replay_sip.st create mode 100644 contrib/rtp/timestamp_rtp.lua create mode 100644 contrib/sms/fill-hlr.st create mode 100644 contrib/sms/hlr-query.st create mode 100644 contrib/sms/sqlite-probe.tap.d create mode 100644 contrib/systemd/osmo-bsc-mgcp.service create mode 100644 contrib/systemd/osmo-bsc.service create mode 100644 contrib/systemd/osmo-nitb.service create mode 100644 doc/BS11-OML.txt create mode 100644 doc/Makefile.am create mode 100644 doc/call-routing.txt create mode 100644 doc/channel_release.txt create mode 100644 doc/control-interface.txt create mode 100644 doc/e1-data-model.txt create mode 100644 doc/examples/Makefile.am create mode 100644 doc/examples/osmo-bsc/osmo-bsc.cfg create mode 100644 doc/examples/osmo-bsc_mgcp/mgcp.cfg create mode 100644 doc/examples/osmo-bsc_nat/black-list.cfg create mode 100644 doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg create mode 100644 doc/examples/osmo-gbproxy/osmo-gbproxy.cfg create mode 100644 doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg create mode 100644 doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg create mode 100644 doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg create mode 100644 doc/examples/osmo-nitb/bs11/openbsc.cfg create mode 100644 doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg create mode 100644 doc/examples/osmo-nitb/nanobts/openbsc.cfg create mode 100644 doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg create mode 100644 doc/examples/osmo-nitb/rbs2308/openbsc.cfg create mode 100644 doc/examples/osmo-sgsn/osmo-sgsn.cfg create mode 100644 doc/gsm-hopping.txt create mode 100644 doc/handover.txt create mode 100644 doc/ipa-sccp.txt create mode 100644 doc/oml-interface.txt create mode 100644 doc/paging.txt create mode 100755 git-version-gen create mode 100644 include/Makefile.am create mode 100644 include/compat_af_isdn.h create mode 100644 include/mISDNif.h create mode 100644 include/openbsc/Makefile.am create mode 100644 include/openbsc/abis_nm.h create mode 100644 include/openbsc/abis_om2000.h create mode 100644 include/openbsc/abis_rsl.h create mode 100644 include/openbsc/arfcn_range_encode.h create mode 100644 include/openbsc/auth.h create mode 100644 include/openbsc/bsc_api.h create mode 100644 include/openbsc/bsc_msc.h create mode 100644 include/openbsc/bsc_nat.h create mode 100644 include/openbsc/bsc_nat_callstats.h create mode 100644 include/openbsc/bsc_nat_sccp.h create mode 100644 include/openbsc/bsc_rll.h create mode 100644 include/openbsc/bss.h create mode 100644 include/openbsc/chan_alloc.h create mode 100644 include/openbsc/control_cmd.h create mode 100644 include/openbsc/control_if.h create mode 100644 include/openbsc/crc24.h create mode 100644 include/openbsc/db.h create mode 100644 include/openbsc/debug.h create mode 100644 include/openbsc/e1_config.h create mode 100644 include/openbsc/gb_proxy.h create mode 100644 include/openbsc/gprs_gmm.h create mode 100644 include/openbsc/gprs_llc.h create mode 100644 include/openbsc/gprs_sgsn.h create mode 100644 include/openbsc/gsm_04_08.h create mode 100644 include/openbsc/gsm_04_08_gprs.h create mode 100644 include/openbsc/gsm_04_11.h create mode 100644 include/openbsc/gsm_04_80.h create mode 100644 include/openbsc/gsm_data.h create mode 100644 include/openbsc/gsm_data_shared.h create mode 100644 include/openbsc/gsm_subscriber.h create mode 100644 include/openbsc/handover.h create mode 100644 include/openbsc/handover_decision.h create mode 100644 include/openbsc/ipaccess.h create mode 100644 include/openbsc/meas_rep.h create mode 100644 include/openbsc/mgcp.h create mode 100644 include/openbsc/mgcp_internal.h create mode 100644 include/openbsc/misdn.h create mode 100644 include/openbsc/mncc.h create mode 100644 include/openbsc/mncc_int.h create mode 100644 include/openbsc/network_listen.h create mode 100644 include/openbsc/openbscdefines.h create mode 100644 include/openbsc/osmo_bsc.h create mode 100644 include/openbsc/osmo_bsc_grace.h create mode 100644 include/openbsc/osmo_bsc_rf.h create mode 100644 include/openbsc/osmo_msc.h create mode 100644 include/openbsc/osmo_msc_data.h create mode 100644 include/openbsc/paging.h create mode 100644 include/openbsc/rest_octets.h create mode 100644 include/openbsc/rrlp.h create mode 100644 include/openbsc/rs232.h create mode 100644 include/openbsc/rtp_proxy.h create mode 100644 include/openbsc/sgsn.h create mode 100644 include/openbsc/signal.h create mode 100644 include/openbsc/silent_call.h create mode 100644 include/openbsc/sms_queue.h create mode 100644 include/openbsc/socket.h create mode 100644 include/openbsc/system_information.h create mode 100644 include/openbsc/token_auth.h create mode 100644 include/openbsc/transaction.h create mode 100644 include/openbsc/trau_mux.h create mode 100644 include/openbsc/trau_upqueue.h create mode 100644 include/openbsc/ussd.h create mode 100644 include/openbsc/vty.h create mode 100644 openbsc.pc.in delete mode 100644 openbsc/AUTHORS delete mode 100644 openbsc/COPYING delete mode 100644 openbsc/Makefile.am delete mode 100644 openbsc/README delete mode 100644 openbsc/configure.ac delete mode 100644 openbsc/contrib/a-link/sccp-split-by-con.lua delete mode 100644 openbsc/contrib/bsc-test/README delete mode 100644 openbsc/contrib/bsc-test/all_dial delete mode 100755 openbsc/contrib/bsc-test/dial.sh delete mode 100755 openbsc/contrib/bsc-test/drop-oml.sh delete mode 100755 openbsc/contrib/bsc-test/drop.sh delete mode 100644 openbsc/contrib/bsc-test/hangup delete mode 100755 openbsc/contrib/bsc-test/msc.sh delete mode 100755 openbsc/contrib/bsc_control.py delete mode 100755 openbsc/contrib/bt.py delete mode 100755 openbsc/contrib/convert_to_enum.py delete mode 100755 openbsc/contrib/gprs/gb-proxy-unblock-bug.py delete mode 100644 openbsc/contrib/gprs/gprs-bssgp-histogram.lua delete mode 100644 openbsc/contrib/gprs/gprs-buffer-count.lua delete mode 100644 openbsc/contrib/gprs/gprs-split-trace-by-tlli.lua delete mode 100644 openbsc/contrib/gprs/gprs-verify-nu.lua delete mode 100644 openbsc/contrib/hlr-remove-old.sql delete mode 100755 openbsc/contrib/mgcp_server.py delete mode 100644 openbsc/contrib/nat/test_regexp.c delete mode 100644 openbsc/contrib/rtp/rtp_replay.st delete mode 100644 openbsc/contrib/rtp/rtp_replay_shared.st delete mode 100644 openbsc/contrib/rtp/rtp_replay_sip.st delete mode 100644 openbsc/contrib/rtp/timestamp_rtp.lua delete mode 100644 openbsc/contrib/sms/fill-hlr.st delete mode 100644 openbsc/contrib/sms/hlr-query.st delete mode 100644 openbsc/contrib/sms/sqlite-probe.tap.d delete mode 100644 openbsc/contrib/systemd/osmo-bsc-mgcp.service delete mode 100644 openbsc/contrib/systemd/osmo-bsc.service delete mode 100644 openbsc/contrib/systemd/osmo-nitb.service delete mode 100644 openbsc/doc/BS11-OML.txt delete mode 100644 openbsc/doc/Makefile.am delete mode 100644 openbsc/doc/call-routing.txt delete mode 100644 openbsc/doc/channel_release.txt delete mode 100644 openbsc/doc/control-interface.txt delete mode 100644 openbsc/doc/e1-data-model.txt delete mode 100644 openbsc/doc/examples/Makefile.am delete mode 100644 openbsc/doc/examples/osmo-bsc/osmo-bsc.cfg delete mode 100644 openbsc/doc/examples/osmo-bsc_mgcp/mgcp.cfg delete mode 100644 openbsc/doc/examples/osmo-bsc_nat/black-list.cfg delete mode 100644 openbsc/doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg delete mode 100644 openbsc/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg delete mode 100644 openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg delete mode 100644 openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg delete mode 100644 openbsc/doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg delete mode 100644 openbsc/doc/examples/osmo-nitb/bs11/openbsc.cfg delete mode 100644 openbsc/doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg delete mode 100644 openbsc/doc/examples/osmo-nitb/nanobts/openbsc.cfg delete mode 100644 openbsc/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg delete mode 100644 openbsc/doc/examples/osmo-nitb/rbs2308/openbsc.cfg delete mode 100644 openbsc/doc/examples/osmo-sgsn/osmo-sgsn.cfg delete mode 100644 openbsc/doc/gsm-hopping.txt delete mode 100644 openbsc/doc/handover.txt delete mode 100644 openbsc/doc/ipa-sccp.txt delete mode 100644 openbsc/doc/oml-interface.txt delete mode 100644 openbsc/doc/paging.txt delete mode 100755 openbsc/git-version-gen 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_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/bss.h delete mode 100644 openbsc/include/openbsc/chan_alloc.h delete mode 100644 openbsc/include/openbsc/control_cmd.h delete mode 100644 openbsc/include/openbsc/control_if.h delete mode 100644 openbsc/include/openbsc/crc24.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_gmm.h delete mode 100644 openbsc/include/openbsc/gprs_llc.h delete mode 100644 openbsc/include/openbsc/gprs_sgsn.h delete mode 100644 openbsc/include/openbsc/gsm_04_08.h delete mode 100644 openbsc/include/openbsc/gsm_04_08_gprs.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/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/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/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/network_listen.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/osmo_msc_data.h delete mode 100644 openbsc/include/openbsc/paging.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/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/vty.h delete mode 100644 openbsc/openbsc.pc.in delete mode 100644 openbsc/osmoappdesc.py 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_vty.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_vty.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.h delete mode 100644 openbsc/src/gprs/gprs_sndcp_vty.c delete mode 100644 openbsc/src/gprs/osmo_sgsn.cfg 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/ipaccess/Makefile.am delete mode 100644 openbsc/src/ipaccess/ipaccess-config.c delete mode 100644 openbsc/src/ipaccess/ipaccess-find.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_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_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_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_subscriber_base.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/paging.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/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/socket.c delete mode 100644 openbsc/src/libcommon/talloc_ctx.c delete mode 100644 openbsc/src/libctrl/Makefile.am delete mode 100644 openbsc/src/libctrl/control_cmd.c delete mode 100644 openbsc/src/libctrl/control_if.c delete mode 100644 openbsc/src/libgb/Makefile.am delete mode 100644 openbsc/src/libmgcp/Makefile.am delete mode 100644 openbsc/src/libmgcp/mgcp_network.c delete mode 100644 openbsc/src/libmgcp/mgcp_protocol.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/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/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_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/smpp_mirror.c delete mode 100644 openbsc/tests/Makefile.am delete mode 100644 openbsc/tests/abis/Makefile.am delete mode 100644 openbsc/tests/abis/abis_test.c delete mode 100644 openbsc/tests/abis/abis_test.ok delete mode 100644 openbsc/tests/atlocal.in delete mode 100644 openbsc/tests/bsc-nat/Makefile.am delete mode 100644 openbsc/tests/bsc-nat/barr.cfg delete mode 100644 openbsc/tests/bsc-nat/barr_dup.cfg delete mode 100644 openbsc/tests/bsc-nat/bsc_data.c delete mode 100644 openbsc/tests/bsc-nat/bsc_nat_test.c delete mode 100644 openbsc/tests/bsc-nat/bsc_nat_test.ok delete mode 100644 openbsc/tests/channel/Makefile.am delete mode 100644 openbsc/tests/channel/channel_test.c delete mode 100644 openbsc/tests/channel/channel_test.ok delete mode 100644 openbsc/tests/db/Makefile.am delete mode 100644 openbsc/tests/db/db_test.c delete mode 100644 openbsc/tests/db/db_test.ok delete mode 100644 openbsc/tests/gprs/Makefile.am delete mode 100644 openbsc/tests/gprs/gprs_test.c delete mode 100644 openbsc/tests/gprs/gprs_test.ok delete mode 100644 openbsc/tests/gsm0408/Makefile.am delete mode 100644 openbsc/tests/gsm0408/gsm0408_test.c delete mode 100644 openbsc/tests/gsm0408/gsm0408_test.ok delete mode 100644 openbsc/tests/mgcp/Makefile.am delete mode 100644 openbsc/tests/mgcp/mgcp_test.c delete mode 100644 openbsc/tests/mgcp/mgcp_test.ok delete mode 100644 openbsc/tests/si/Makefile.am delete mode 100644 openbsc/tests/si/si_test.c delete mode 100644 openbsc/tests/si/si_test.ok delete mode 100644 openbsc/tests/smpp/Makefile.am delete mode 100644 openbsc/tests/smpp/smpp_test.c delete mode 100644 openbsc/tests/smpp/smpp_test.err delete mode 100644 openbsc/tests/smpp/smpp_test.ok delete mode 100644 openbsc/tests/sms.txt delete mode 100644 openbsc/tests/testsuite.at delete mode 100644 openbsc/tests/vty_test_runner.py delete mode 100755 openbsc/tools/hlrstat.pl create mode 100644 osmoappdesc.py create mode 100644 src/Makefile.am create mode 100644 src/gprs/.gitignore create mode 100644 src/gprs/Makefile.am create mode 100644 src/gprs/crc24.c create mode 100644 src/gprs/gb_proxy.c create mode 100644 src/gprs/gb_proxy_main.c create mode 100644 src/gprs/gb_proxy_vty.c create mode 100644 src/gprs/gprs_gmm.c create mode 100644 src/gprs/gprs_llc.c create mode 100644 src/gprs/gprs_llc_vty.c create mode 100644 src/gprs/gprs_sgsn.c create mode 100644 src/gprs/gprs_sndcp.c create mode 100644 src/gprs/gprs_sndcp.h create mode 100644 src/gprs/gprs_sndcp_vty.c create mode 100644 src/gprs/osmo_sgsn.cfg create mode 100644 src/gprs/sgsn_libgtp.c create mode 100644 src/gprs/sgsn_main.c create mode 100644 src/gprs/sgsn_vty.c create mode 100644 src/ipaccess/Makefile.am create mode 100644 src/ipaccess/ipaccess-config.c create mode 100644 src/ipaccess/ipaccess-find.c create mode 100644 src/ipaccess/ipaccess-firmware.c create mode 100644 src/ipaccess/ipaccess-proxy.c create mode 100644 src/ipaccess/network_listen.c create mode 100644 src/libbsc/Makefile.am create mode 100644 src/libbsc/abis_nm.c create mode 100644 src/libbsc/abis_nm_ipaccess.c create mode 100644 src/libbsc/abis_nm_vty.c create mode 100644 src/libbsc/abis_om2000.c create mode 100644 src/libbsc/abis_om2000_vty.c create mode 100644 src/libbsc/abis_rsl.c create mode 100644 src/libbsc/arfcn_range_encode.c create mode 100644 src/libbsc/bsc_api.c create mode 100644 src/libbsc/bsc_init.c create mode 100644 src/libbsc/bsc_msc.c create mode 100644 src/libbsc/bsc_rf_ctrl.c create mode 100644 src/libbsc/bsc_rll.c create mode 100644 src/libbsc/bsc_vty.c create mode 100644 src/libbsc/bts_ericsson_rbs2000.c create mode 100644 src/libbsc/bts_init.c create mode 100644 src/libbsc/bts_ipaccess_nanobts.c create mode 100644 src/libbsc/bts_nokia_site.c create mode 100644 src/libbsc/bts_siemens_bs11.c create mode 100644 src/libbsc/bts_sysmobts.c create mode 100644 src/libbsc/bts_unknown.c create mode 100644 src/libbsc/chan_alloc.c create mode 100644 src/libbsc/e1_config.c create mode 100644 src/libbsc/gsm_04_08_utils.c create mode 100644 src/libbsc/gsm_subscriber_base.c create mode 100644 src/libbsc/handover_decision.c create mode 100644 src/libbsc/handover_logic.c create mode 100644 src/libbsc/meas_proc.c create mode 100644 src/libbsc/meas_rep.c create mode 100644 src/libbsc/paging.c create mode 100644 src/libbsc/rest_octets.c create mode 100644 src/libbsc/system_information.c create mode 100644 src/libcommon/Makefile.am create mode 100644 src/libcommon/bsc_version.c create mode 100644 src/libcommon/common_vty.c create mode 100644 src/libcommon/debug.c create mode 100644 src/libcommon/gsm_data.c create mode 100644 src/libcommon/gsm_data_shared.c create mode 100644 src/libcommon/socket.c create mode 100644 src/libcommon/talloc_ctx.c create mode 100644 src/libctrl/Makefile.am create mode 100644 src/libctrl/control_cmd.c create mode 100644 src/libctrl/control_if.c create mode 100644 src/libgb/Makefile.am create mode 100644 src/libmgcp/Makefile.am create mode 100644 src/libmgcp/mgcp_network.c create mode 100644 src/libmgcp/mgcp_protocol.c create mode 100644 src/libmgcp/mgcp_vty.c create mode 100644 src/libmsc/Makefile.am create mode 100644 src/libmsc/auth.c create mode 100644 src/libmsc/db.c create mode 100644 src/libmsc/gsm_04_08.c create mode 100644 src/libmsc/gsm_04_11.c create mode 100644 src/libmsc/gsm_04_80.c create mode 100644 src/libmsc/gsm_subscriber.c create mode 100644 src/libmsc/mncc.c create mode 100644 src/libmsc/mncc_builtin.c create mode 100644 src/libmsc/mncc_sock.c create mode 100644 src/libmsc/osmo_msc.c create mode 100644 src/libmsc/rrlp.c create mode 100644 src/libmsc/silent_call.c create mode 100644 src/libmsc/smpp_openbsc.c create mode 100644 src/libmsc/smpp_smsc.c create mode 100644 src/libmsc/smpp_smsc.h create mode 100644 src/libmsc/smpp_utils.c create mode 100644 src/libmsc/smpp_vty.c create mode 100644 src/libmsc/sms_queue.c create mode 100644 src/libmsc/token_auth.c create mode 100644 src/libmsc/transaction.c create mode 100644 src/libmsc/ussd.c create mode 100644 src/libmsc/vty_interface_layer3.c create mode 100644 src/libtrau/Makefile.am create mode 100644 src/libtrau/rtp_proxy.c create mode 100644 src/libtrau/trau_mux.c create mode 100644 src/libtrau/trau_upqueue.c create mode 100644 src/osmo-bsc/Makefile.am create mode 100644 src/osmo-bsc/osmo_bsc_api.c create mode 100644 src/osmo-bsc/osmo_bsc_audio.c create mode 100644 src/osmo-bsc/osmo_bsc_bssap.c create mode 100644 src/osmo-bsc/osmo_bsc_ctrl.c create mode 100644 src/osmo-bsc/osmo_bsc_filter.c create mode 100644 src/osmo-bsc/osmo_bsc_grace.c create mode 100644 src/osmo-bsc/osmo_bsc_main.c create mode 100644 src/osmo-bsc/osmo_bsc_msc.c create mode 100644 src/osmo-bsc/osmo_bsc_sccp.c create mode 100644 src/osmo-bsc/osmo_bsc_vty.c create mode 100644 src/osmo-bsc_mgcp/Makefile.am create mode 100644 src/osmo-bsc_mgcp/mgcp_main.c create mode 100644 src/osmo-bsc_nat/Makefile.am create mode 100644 src/osmo-bsc_nat/bsc_filter.c create mode 100644 src/osmo-bsc_nat/bsc_mgcp_utils.c create mode 100644 src/osmo-bsc_nat/bsc_nat.c create mode 100644 src/osmo-bsc_nat/bsc_nat_ctrl.c create mode 100644 src/osmo-bsc_nat/bsc_nat_filter.c create mode 100644 src/osmo-bsc_nat/bsc_nat_rewrite.c create mode 100644 src/osmo-bsc_nat/bsc_nat_utils.c create mode 100644 src/osmo-bsc_nat/bsc_nat_vty.c create mode 100644 src/osmo-bsc_nat/bsc_sccp.c create mode 100644 src/osmo-bsc_nat/bsc_ussd.c create mode 100644 src/osmo-nitb/Makefile.am create mode 100644 src/osmo-nitb/bsc_hack.c create mode 100644 src/utils/Makefile.am create mode 100644 src/utils/bs11_config.c create mode 100644 src/utils/isdnsync.c create mode 100644 src/utils/smpp_mirror.c create mode 100644 tests/Makefile.am create mode 100644 tests/abis/Makefile.am create mode 100644 tests/abis/abis_test.c create mode 100644 tests/abis/abis_test.ok create mode 100644 tests/atlocal.in create mode 100644 tests/bsc-nat/Makefile.am create mode 100644 tests/bsc-nat/barr.cfg create mode 100644 tests/bsc-nat/barr_dup.cfg create mode 100644 tests/bsc-nat/bsc_data.c create mode 100644 tests/bsc-nat/bsc_nat_test.c create mode 100644 tests/bsc-nat/bsc_nat_test.ok create mode 100644 tests/channel/Makefile.am create mode 100644 tests/channel/channel_test.c create mode 100644 tests/channel/channel_test.ok create mode 100644 tests/db/Makefile.am create mode 100644 tests/db/db_test.c create mode 100644 tests/db/db_test.ok create mode 100644 tests/gprs/Makefile.am create mode 100644 tests/gprs/gprs_test.c create mode 100644 tests/gprs/gprs_test.ok create mode 100644 tests/gsm0408/Makefile.am create mode 100644 tests/gsm0408/gsm0408_test.c create mode 100644 tests/gsm0408/gsm0408_test.ok create mode 100644 tests/mgcp/Makefile.am create mode 100644 tests/mgcp/mgcp_test.c create mode 100644 tests/mgcp/mgcp_test.ok create mode 100644 tests/si/Makefile.am create mode 100644 tests/si/si_test.c create mode 100644 tests/si/si_test.ok create mode 100644 tests/smpp/Makefile.am create mode 100644 tests/smpp/smpp_test.c create mode 100644 tests/smpp/smpp_test.err create mode 100644 tests/smpp/smpp_test.ok create mode 100644 tests/sms.txt create mode 100644 tests/testsuite.at create mode 100644 tests/vty_test_runner.py create mode 100755 tools/hlrstat.pl diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..daf60e447 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,7 @@ +Harald Welte +Holger Freyther +Jan Luebbe +Stefan Schmidt +Daniel Willmann +Andreas Eversberg +Sylvain Munaut <246tnt@gmail.com> diff --git a/COPYING b/COPYING new file mode 100644 index 000000000..dba13ed2d --- /dev/null +++ b/COPYING @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 000000000..d7329ba33 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,14 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 + +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +SUBDIRS = doc include src tests + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = openbsc.pc + +BUILT_SOURCES = $(top_srcdir)/.version +EXTRA_DIST = git-version-gen osmoappdesc.py +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version diff --git a/README b/README index e69de29bb..fa01ff4cd 100644 --- a/README +++ b/README @@ -0,0 +1,32 @@ +About OpenBSC +============= + +OpenBSC is a minimalistic implementation of the GSM Network, with +particular emphasis on the functionality typically provided by the BSC, +MSC, HLR, VLR and SMSC. + +Its currently supported interfaces towards the BTS are: + + * Classic A-bis over E1 using a mISDN based E1 interface. In other + words, you can connect existing GSM Base Transceiver Station (BTS) + through E1 to OpenBSC. So far, we have only tested the Siemens BS-11 + Test reports with other BTS are much appreciated! + + * A-bis over IP as used by the ip.access nanoBTS product family + +You can find the project documentation at http://openbsc.gnumonks.org/ + +This project is still in its early days, and there are lots of areas where it +doesn't behave as per GSM spec. + + Harald Welte + + +libosmocore +=========== + +Please note that as of March 2010, OpenBSC has a dependency to a library +called "libosmocore". You can obtain that library from + + git://git.osmocom.org/libosmocore.git + diff --git a/configure.ac b/configure.ac new file mode 100644 index 000000000..0227d2698 --- /dev/null +++ b/configure.ac @@ -0,0 +1,167 @@ +dnl Process this file with autoconf to produce a configure script +AC_INIT([openbsc], + m4_esyscmd([./git-version-gen .tarball-version]), + [openbsc-devel@lists.openbsc.org]) + +AM_INIT_AUTOMAKE([dist-bzip2]) +AC_CONFIG_TESTDIR(tests) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_RANLIB + +dnl checks for libraries +AC_SEARCH_LIBS(crypt, crypt, + [LIBCRYPT="-lcrypt"; AC_DEFINE([VTY_CRYPT_PW], [], [Use crypt functionality of vty.])]) + +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.5.3.60) +PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0) +PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.6.0) +PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.1.0) +PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.2) + +# Enabke/disable the NAT? +AC_ARG_ENABLE([nat], [AS_HELP_STRING([--enable-nat], [Build the BSC NAT. Requires SCCP])], + [osmo_ac_build_nat="$enableval"],[osmo_ac_build_nat="no"]) +if test "$osmo_ac_build_nat" = "yes" ; then + PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.0.2) +fi +AM_CONDITIONAL(BUILD_NAT, test "x$osmo_ac_build_nat" = "xyes") +AC_SUBST(osmo_ac_build_nat) + +# Enable/disable the BSC? +AC_ARG_ENABLE([osmo-bsc], [AS_HELP_STRING([--enable-osmo-bsc], [Build the Osmo BSC])], + [osmo_ac_build_bsc="$enableval"]) +if test "$osmo_ac_build_bsc" = "yes" ; then + PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.0.6) +fi +AM_CONDITIONAL(BUILD_BSC, test "x$osmo_ac_build_bsc" = "xyes") + +# Enable/disable smpp support in the nitb? +AC_ARG_ENABLE([smpp], [AS_HELP_STRING([--enable-smpp], [Build the SMPP interface])], + [osmo_ac_build_smpp="$enableval"],[osmo_ac_build_smpp="no"]) +if test "$osmo_ac_build_smpp" = "yes" ; then + PKG_CHECK_MODULES(LIBSMPP34, libsmpp34 >= 1.10) + AC_DEFINE(BUILD_SMPP, 1, [Define if we want to build SMPP]) +fi +AM_CONDITIONAL(BUILD_SMPP, test "x$osmo_ac_build_smpp" = "xyes") +AC_SUBST(osmo_ac_build_smpp) + + +found_libgtp=yes +PKG_CHECK_MODULES(LIBGTP, libgtp, , found_libgtp=no) +AM_CONDITIONAL(HAVE_LIBGTP, test "$found_libgtp" = yes) + +dnl checks for header files +AC_HEADER_STDC +AC_CHECK_HEADERS(dahdi/user.h,,AC_MSG_WARN(DAHDI input driver will not be built)) +AC_CHECK_HEADERS(dbi/dbd.h,,AC_MSG_ERROR(DBI library is not installed)) + + +dnl Checks for typedefs, structures and compiler characteristics + +# The following test is taken from WebKit's webkit.m4 +saved_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -fvisibility=hidden " +AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])], + [ AC_MSG_RESULT([yes]) + SYMBOL_VISIBILITY="-fvisibility=hidden"], + AC_MSG_RESULT([no])) +CFLAGS="$saved_CFLAGS" +AC_SUBST(SYMBOL_VISIBILITY) + +# Coverage build taken from WebKit's configure.in +AC_MSG_CHECKING([whether to enable code coverage support]) +AC_ARG_ENABLE(coverage, + AC_HELP_STRING([--enable-coverage], + [enable code coverage support [default=no]]), + [],[enable_coverage="no"]) +AC_MSG_RESULT([$enable_coverage]) +if test "$enable_coverage" = "yes"; then + COVERAGE_CFLAGS="-ftest-coverage -fprofile-arcs" + COVERAGE_LDFLAGS="-ftest-coverage -fprofile-arcs" + AC_SUBST([COVERAGE_CFLAGS]) + AC_SUBST([COVERAGE_LDFLAGS]) +fi + +AC_DEFUN([CHECK_TM_INCLUDES_TM_GMTOFF], [ + AC_CACHE_CHECK( + [whether struct tm has tm_gmtoff member], + osmo_cv_tm_includes_tm_gmtoff, + [AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ + #include + ], [ + time_t t = time(NULL); + struct tm* lt = localtime(&t); + int off = lt->tm_gmtoff; + ]) + ], + osmo_cv_tm_includes_tm_gmtoff=yes, + osmo_cv_tm_includes_tm_gmtoff=no + )] + ) + if test "x$osmo_cv_tm_includes_tm_gmtoff" = xyes; then + AC_DEFINE(HAVE_TM_GMTOFF_IN_TM, 1, + [Define if struct tm has tm_gmtoff member.]) + fi +]) + +CHECK_TM_INCLUDES_TM_GMTOFF + +AC_ARG_ENABLE([vty_tests], + AC_HELP_STRING([--enable-vty-tests], + [Include the VTY tests in make check [default=no]]), + [enable_vty_tests="$enableval"],[enable_vty_tests="no"]) +if test "x$enable_vty_tests" = "xyes" ; then + AM_PATH_PYTHON + AC_CHECK_PROG(OSMOTESTVTY_CHECK,osmotestvty.py,yes) + if test "x$OSMOTESTVTY_CHECK" != "xyes" ; then + AC_MSG_ERROR([Please install osmocom-python to run the vty tests.]) + fi +fi +AC_MSG_CHECKING([whether to enable VTY tests]) +AC_MSG_RESULT([$enable_vty_tests]) +AM_CONDITIONAL(ENABLE_VTY_TESTS, test "x$enable_vty_tests" = "xyes") + +dnl Generate the output +AM_CONFIG_HEADER(bscconfig.h) + +AC_OUTPUT( + openbsc.pc + include/openbsc/Makefile + include/Makefile + src/Makefile + src/libtrau/Makefile + src/libbsc/Makefile + src/libctrl/Makefile + src/libmsc/Makefile + src/libmgcp/Makefile + src/libcommon/Makefile + src/osmo-nitb/Makefile + src/osmo-bsc/Makefile + src/osmo-bsc_nat/Makefile + src/osmo-bsc_mgcp/Makefile + src/ipaccess/Makefile + src/utils/Makefile + src/gprs/Makefile + tests/Makefile + tests/atlocal + tests/gsm0408/Makefile + tests/db/Makefile + tests/channel/Makefile + tests/bsc-nat/Makefile + tests/mgcp/Makefile + tests/gprs/Makefile + tests/si/Makefile + tests/abis/Makefile + tests/smpp/Makefile + doc/Makefile + doc/examples/Makefile + Makefile) diff --git a/contrib/a-link/sccp-split-by-con.lua b/contrib/a-link/sccp-split-by-con.lua new file mode 100644 index 000000000..f5d5502ae --- /dev/null +++ b/contrib/a-link/sccp-split-by-con.lua @@ -0,0 +1,170 @@ +-- Split trace based on SCCP Source +-- There are still bugs to find... bugs bugs bugs... hmm +do + local function init_listener() + print("CREATED LISTENER") + local tap = Listener.new("ip", "sccp && (ip.src == 172.16.1.81 || ip.dst == 172.16.1.81)") + local sccp_type_field = Field.new("sccp.message_type") + local sccp_src_field = Field.new("sccp.slr") + local sccp_dst_field = Field.new("sccp.dlr") + local msg_type_field = Field.new("gsm_a.dtap_msg_mm_type") + local lu_rej_field = Field.new("gsm_a.dtap.rej_cause") + local ip_src_field = Field.new("ip.src") + local ip_dst_field = Field.new("ip.dst") + + -- + local bssmap_msgtype_field = Field.new("gsm_a.bssmap_msgtype") + -- assignment failure 0x03 + -- + + -- + local dtap_cause_field = Field.new("gsm_a_dtap.cause") + local dtap_cc_field = Field.new("gsm_a.dtap_msg_cc_type") + + local connections = {} + + function check_failure(con) + check_lu_reject(con) + check_disconnect(con) + check_failures(con) + end + + -- cipher mode reject + function check_failures(con) + local msgtype = bssmap_msgtype_field() + if not msgtype then + return + end + + msgtype = tonumber(msgtype) + if msgtype == 89 then + print("Cipher mode reject") + con[4] = true + elseif msgtype == 0x03 then + print("Assignment failure") + con[4] = true + elseif msgtype == 0x22 then + print("Clear Request... RF failure?") + con[4] = true + end + end + + -- check if a DISCONNECT is normal + function check_disconnect(con) + local msg_type = dtap_cc_field() + if not msg_type then + return + end + + if tonumber(msg_type) ~= 0x25 then + return + end + + local cause = dtap_cause_field() + if not cause then + return + end + + cause = tonumber(cause) + if cause ~= 0x10 then + print("DISCONNECT != Normal") + con[4] = true + end + end + + -- check if we have a LU Reject + function check_lu_reject(con) + local msg_type = msg_type_field() + if not msg_type then + return + end + + msg_type = tonumber(tostring(msg_type)) + if msg_type == 0x04 then + print("LU REJECT with " .. tostring(lu_rej_field())) + con[4] = true + end + end + + function tap.packet(pinfo,tvb,ip) + local ip_src = tostring(ip_src_field()) + local ip_dst = tostring(ip_dst_field()) + local sccp_type = tonumber(tostring(sccp_type_field())) + local sccp_src = sccp_src_field() + local sccp_dst = sccp_dst_field() + + local con + + if sccp_type == 0x01 then + elseif sccp_type == 0x2 then + local src = string.format("%s-%s", ip_src, tostring(sccp_src)) + local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) + local datestring = os.date("%Y%m%d%H%M%S") + local pcap_name = string.format("alink_trace_%s-%s_%s.pcap", src, dst, datestring) + local dumper = Dumper.new_for_current(pcap_name) + + local con = { ip_src, tostring(sccp_src), tostring(sccp_dst), false, dumper, pcap_name } + + dumper:dump_current() + connections[src] = con + connections[dst] = con + elseif sccp_type == 0x4 then + -- close a connection... remove it from the list + local src = string.format("%s-%s", ip_src, tostring(sccp_src)) + local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) + + local con = connections[src] + if not con then + return + end + + con[5]:dump_current() + con[5]:flush() + + -- this causes a crash on unpacted wireshark + con[5]:close() + + -- the connection had a failure + if con[4] == true then + local datestring = os.date("%Y%m%d%H%M%S") + local new_name = string.format("alink_failure_%s_%s-%s.pcap", datestring, con[2], con[3]) + os.rename(con[6], new_name) + else + os.remove(con[6]) + end + + + -- clear the old connection + connections[src] = nil + connections[dst] = nil + + elseif sccp_type == 0x5 then + -- not handled yet... we should verify stuff here... + local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) + local con = connections[dst] + if not con then + return + end + con[5]:dump_current() + elseif sccp_type == 0x6 then + local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) + local con = connections[dst] + if not con then + print("DON'T KNOW THIS CONNECTION for " .. ip_dst) + return + end + con[5]:dump_current() + check_failure(con) + end + + end + function tap.draw() + print("DRAW") + end + function tap.reset() + print("RESET") + end + end + + init_listener() +end diff --git a/contrib/bsc-test/README b/contrib/bsc-test/README new file mode 100644 index 000000000..adb222e21 --- /dev/null +++ b/contrib/bsc-test/README @@ -0,0 +1 @@ +Some crazy scripts call testing... and MSC link failure simulation diff --git a/contrib/bsc-test/all_dial b/contrib/bsc-test/all_dial new file mode 100644 index 000000000..96e5f00b3 --- /dev/null +++ b/contrib/bsc-test/all_dial @@ -0,0 +1,8 @@ +ABORT BUSY +ABORT 'NO CARRIER' +ABORT 'OK' + +'' AT +SAY "Dialing a number\n" +'OK' ATD05660066; + diff --git a/contrib/bsc-test/dial.sh b/contrib/bsc-test/dial.sh new file mode 100755 index 000000000..e5e19f63e --- /dev/null +++ b/contrib/bsc-test/dial.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Evil dial script.. + +while true; +do + chat -v -f all_dial < /dev/ttyACM0 > /dev/ttyACM0 + sleep 5s + chat -v -f hangup < /dev/ttyACM0 > /dev/ttyACM0 + sleep 2s +done + diff --git a/contrib/bsc-test/drop-oml.sh b/contrib/bsc-test/drop-oml.sh new file mode 100755 index 000000000..84eead7b7 --- /dev/null +++ b/contrib/bsc-test/drop-oml.sh @@ -0,0 +1,6 @@ +#!/bin/sh +sleep 3 +echo "enable" +sleep 1 +echo "drop bts connection 0 oml" +sleep 1 diff --git a/contrib/bsc-test/drop.sh b/contrib/bsc-test/drop.sh new file mode 100755 index 000000000..c7b66ba72 --- /dev/null +++ b/contrib/bsc-test/drop.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +while true; +do + echo "Going to drop the OML connection" + ./drop-oml.sh | telnet 127.0.0.1 4242 + sleep 58m +done diff --git a/contrib/bsc-test/hangup b/contrib/bsc-test/hangup new file mode 100644 index 000000000..cad6870fd --- /dev/null +++ b/contrib/bsc-test/hangup @@ -0,0 +1,4 @@ +TIMEOUT 10 +'' ^Z +SAY "Waiting for hangup confirm\n" +'' ATH; diff --git a/contrib/bsc-test/msc.sh b/contrib/bsc-test/msc.sh new file mode 100755 index 000000000..bec011d4c --- /dev/null +++ b/contrib/bsc-test/msc.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +while true; +do + echo "Kill the osmo-bsc" + /usr/bin/kill -s SIGUSR2 `pidof osmo-bsc` + sleep 58s +done diff --git a/contrib/bsc_control.py b/contrib/bsc_control.py new file mode 100755 index 000000000..9dc01e3bf --- /dev/null +++ b/contrib/bsc_control.py @@ -0,0 +1,104 @@ +#!/usr/bin/python + +import sys,os +from optparse import OptionParser +import socket +import struct + +verbose = False + +def prefix_ipa_ctrl_header(data): + return struct.pack(">HBB", len(data)+1, 0xee, 0) + data + +def remove_ipa_ctrl_header(data): + if (len(data) < 4): + raise BaseException("Answer too short!") + (plen, ipa_proto, osmo_proto) = struct.unpack(">HBB", data[:4]) + if (plen + 3 > len(data)): + print "Warning: Wrong payload length (expected %i, got %i)" % (plen, len(data) - 3) + if (ipa_proto != 0xee or osmo_proto != 0): + raise BaseException("Wrong protocol in answer!") + + return data[4:plen+3], data[plen+3:] + +def connect(host, port): + if verbose: + print "Connecting to host %s:%i" % (host, port) + + sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sck.setblocking(1) + sck.connect((host, port)) + return sck + +def send(sck, data): + if verbose: + print "Sending \"%s\"" %(data) + data = prefix_ipa_ctrl_header(data) + sck.send(data) + +def do_set(var, value, id, sck): + setmsg = "SET %s %s %s" %(options.id, var, value) + send(sck, setmsg) + +def do_get(var, id, sck): + getmsg = "GET %s %s" %(options.id, var) + send(sck, getmsg) + +parser = OptionParser("Usage: %prog [options] var [value]") +parser.add_option("-d", "--host", dest="host", + help="connect to HOST", metavar="HOST") +parser.add_option("-p", "--port", dest="port", type="int", + help="use PORT", metavar="PORT", default=4249) +parser.add_option("-g", "--get", action="store_true", + dest="cmd_get", help="perform GET operation") +parser.add_option("-s", "--set", action="store_true", + dest="cmd_set", help="perform SET operation") +parser.add_option("-i", "--id", dest="id", default="1", + help="set id manually", metavar="ID") +parser.add_option("-v", "--verbose", action="store_true", + dest="verbose", help="be verbose", default=False) +parser.add_option("-m", "--monitor", action="store_true", + dest="monitor", help="monitor the connection for traps", default=False) + +(options, args) = parser.parse_args() + +verbose = options.verbose + +if options.cmd_set and options.cmd_get: + parser.error("Get and set options are mutually exclusive!") + +if not (options.cmd_get or options.cmd_set or options.monitor): + parser.error("One of -m, -g, or -s must be set") + +if not (options.host): + parser.error("Destination host and port required!") + +sock = connect(options.host, options.port) + +if options.cmd_set: + if len(args) < 2: + parser.error("Set requires var and value arguments") + do_set(args[0], ' '.join(args[1:]), options.id, sock) + +if options.cmd_get: + if len(args) != 1: + parser.error("Get requires the var argument") + do_get(args[0], options.id, sock) + +data = sock.recv(1024) +while (len(data)>0): + (answer, data) = remove_ipa_ctrl_header(data) + print "Got message:", answer + +if options.monitor: + while (True): + data = sock.recv(1024) + if len(data) == 0: + print "Connection is gone." + break + + while (len(data)>0): + (answer, data) = remove_ipa_ctrl_header(data) + print "Got message:", answer + +sock.close() diff --git a/contrib/bt.py b/contrib/bt.py new file mode 100755 index 000000000..1b111efc8 --- /dev/null +++ b/contrib/bt.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +import os + +f = open("unbalanced") +lines = [] +for line in f: + lines.append(line) + +filenames = {} + +output = [] +for line in lines: + if "[0x" in line: + start = line.find("[") + end = line.find("]") + addr = line[start+1:end] + try: + file = filenames[addr] + except KeyError: + r = os.popen("addr2line -fs -e ./bsc_hack %s" % addr) + all = r.read().replace("\n", ",") + file = all + filenames[addr] = file + + line = line.replace(addr, file) + output.append(line) + +g = open("unbalanced.2", "w") +g.write("".join(output)) + + + diff --git a/contrib/convert_to_enum.py b/contrib/convert_to_enum.py new file mode 100755 index 000000000..bcd6f2cee --- /dev/null +++ b/contrib/convert_to_enum.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +# +# Convert ETSI documents to an enum +# + +import re, sys + +def convert(string): + string = string.strip().replace(" ", "").rjust(8, "0") + var = 0 + offset = 7 + for char in string: + assert offset >= 0 + var = var | (int(char) << offset) + offset = offset - 1 + + return var + +def string(name): + name = name.replace(" ", "_") + name = name.replace('"', "") + name = name.replace('/', '_') + name = name.replace('(', '_') + name = name.replace(')', '_') + return "%s_%s" % (sys.argv[2], name.upper()) + +file = open(sys.argv[1]) + + +for line in file: + m = re.match(r"[ \t]*(?P[01 ]+)[ ]+(?P[a-zA-Z /0-9()]+)", line[:-1]) + + if m: + print "\t%s\t\t= %d," % (string(m.groupdict()["name"]), convert(m.groupdict()["value"])) + else: + print line[:-1] diff --git a/contrib/gprs/gb-proxy-unblock-bug.py b/contrib/gprs/gb-proxy-unblock-bug.py new file mode 100755 index 000000000..0cd4b871f --- /dev/null +++ b/contrib/gprs/gb-proxy-unblock-bug.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +""" +demonstrate a unblock bug on the GB Proxy.. +""" + +bts_ns_reset = "\x02\x00\x81\x01\x01\x82\x1f\xe7\x04\x82\x1f\xe7" +ns_reset_ack = "\x03\x01\x82\x1f\xe7\x04\x82\x1f\xe7" + +bts_ns_unblock = "\x06" +ns_unblock_ack = "\x07" + +bts_bvc_reset_0 = "\x00\x00\x00\x00\x22\x04\x82\x00\x00\x07\x81\x03\x3b\x81\x02" +ns_bvc_reset_0_ack = "\x00\x00\x00\x00\x23\x04\x82\x00\x00" + +bts_bvc_reset_8167 = "\x00\x00\x00\x00\x22\x04\x82\x1f\xe7\x07\x81\x08\x08\x88\x72\xf4\x80\x10\x1c\x00\x9c\x40" + + +import socket +socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +socket.bind(("0.0.0.0", 0)) +socket.setblocking(1) + + +import sys +port = int(sys.argv[1]) +print "Sending data to port: %d" % port + +def send_and_receive(packet): + socket.sendto(packet, ("127.0.0.1", port)) + + try: + data, addr = socket.recvfrom(4096) + except socket.error, e: + print "ERROR", e + import sys + sys.exit(0) + return data + +#send stuff once + +to_send = [ + (bts_ns_reset, ns_reset_ack, "reset ack"), + (bts_ns_unblock, ns_unblock_ack, "unblock ack"), + (bts_bvc_reset_0, ns_bvc_reset_0_ack, "BVCI=0 reset ack"), +] + + +for (out, inp, type) in to_send: + res = send_and_receive(out) + if res != inp: + print "Failed to get the %s" % type + sys.exit(-1) + +import time +time.sleep(3) +res = send_and_receive(bts_bvc_reset_8167) +print "Sent all messages... check wireshark for the last response" diff --git a/contrib/gprs/gprs-bssgp-histogram.lua b/contrib/gprs/gprs-bssgp-histogram.lua new file mode 100644 index 000000000..b1ab5df7f --- /dev/null +++ b/contrib/gprs/gprs-bssgp-histogram.lua @@ -0,0 +1,78 @@ +-- Simple LUA script to print the size of BSSGP messages over their type... + +do + local ip_bucket = {} + + local pdu_types = {} + pdu_types[ 6] = "PAGING" + pdu_types[11] = "SUSPEND" + pdu_types[12] = "SUSPEND-ACK" + pdu_types[32] = "BVC-BLOCK" + pdu_types[33] = "BVC-BLOCK-ACK" + pdu_types[34] = "BVC-RESET" + pdu_types[35] = "BVC-RESET-ACK" + pdu_types[36] = "UNBLOCK" + pdu_types[37] = "UNBLOCK-ACK" + pdu_types[38] = "FLOW-CONTROL-BVC" + pdu_types[39] = "FLOW-CONTROL-BVC-ACK" + pdu_types[40] = "FLOW-CONTROL-MS" + pdu_types[41] = "FLOW-CONTROL-MS-ACK" + pdu_types[44] = "LLC-DISCARDED" + + local function init_listener() + -- handle the port as NS over IP + local udp_port_table = DissectorTable.get("udp.port") + local gprs_ns_dis = Dissector.get("gprs_ns") + udp_port_table:add(23000,gprs_ns_dis) + + -- bssgp filters + local bssgp_pdu_get = Field.new("bssgp.pdu_type") + local udp_length_get = Field.new("udp.length") + + local tap = Listener.new("ip", "udp.port == 23000") + function tap.packet(pinfo,tvb,ip) + local pdu = bssgp_pdu_get() + local len = udp_length_get() + + -- only handle bssgp, but we also want the IP frame + if not pdu then + return + end + + pdu = tostring(pdu) + if tonumber(pdu) == 0 or tonumber(pdu) == 1 then + return + end + + local ip_src = tostring(ip.ip_src) + local bssgp_histo = ip_bucket[ip_src] + if not bssgp_histo then + bssgp_histo = {} + ip_bucket[ip_src] = bssgp_histo + end + + local key = pdu + local bucket = bssgp_histo[key] + if not bucket then + bucket = {} + bssgp_histo[key] = bucket + end + + table.insert(bucket, tostring(len)) + print("IP: " .. ip_src .. " PDU: " .. pdu_types[tonumber(pdu)] .. " Length: " .. tostring(len)) + end + + function tap.draw() + -- well... this will not be called... +-- for ip,bssgp_histo in pairs(dumpers) do +-- print("IP " .. ip) +-- end + end + + function tap.reset() + -- well... this will not be called... + end + end + + init_listener() +end diff --git a/contrib/gprs/gprs-buffer-count.lua b/contrib/gprs/gprs-buffer-count.lua new file mode 100644 index 000000000..ca8864ad1 --- /dev/null +++ b/contrib/gprs/gprs-buffer-count.lua @@ -0,0 +1,80 @@ +-- I count the buffer space needed for LLC PDUs in the worse case and print it + +do + local function init_listener() + -- handle the port as NS over IP + local udp_port_table = DissectorTable.get("udp.port") + local gprs_ns_dis = Dissector.get("gprs_ns") + udp_port_table:add(23000,gprs_ns_dis) + + -- bssgp filters + local bssgp_pdu_get = Field.new("bssgp.pdu_type") + local bssgp_delay_get = Field.new("bssgp.delay_val") + local llcgprs_get = Field.new("llcgprs") + local pdus = nil + + print("START...") + + local tap = Listener.new("ip", "udp.port == 23000 && bssgp.pdu_type == 0") + function tap.packet(pinfo,tvb,ip) + local pdu = bssgp_pdu_get() + local len = llcgprs_get().len + local delay = bssgp_delay_get() + + -- only handle bssgp, but we also want the IP frame + if not pdu then + return + end + + if tonumber(tostring(delay)) == 65535 then + pdus = { next = pdus, + len = len, + expires = -1 } + else + local off = tonumber(tostring(delay)) / 100.0 + pdus = { next = pdus, + len = len, + expires = pinfo.rel_ts + off } + end + local now_time = tonumber(tostring(pinfo.rel_ts)) + local now_size = 0 + local l = pdus + local prev = nil + local count = 0 + while l do + if now_time < l.expires or l.expires == -1 then + now_size = now_size + l.len + prev = l + l = l.next + count = count + 1 + else + -- delete things + if prev == nil then + pdus = nil + l = nil + else + prev.next = l.next + l = l.next + end + end + end +-- print("TOTAL: " .. now_time .. " PDU_SIZE: " .. now_size) + print(now_time .. " " .. now_size / 1024.0 .. " " .. count) +-- print("NOW: " .. tostring(pinfo.rel_ts) .. " Delay: " .. tostring(delay) .. " Length: " .. tostring(len)) + end + + function tap.draw() + -- well... this will not be called... +-- for ip,bssgp_histo in pairs(dumpers) do +-- print("IP " .. ip) +-- end + print("END") + end + + function tap.reset() + -- well... this will not be called... + end + end + + init_listener() +end diff --git a/contrib/gprs/gprs-split-trace-by-tlli.lua b/contrib/gprs/gprs-split-trace-by-tlli.lua new file mode 100644 index 000000000..018c377c5 --- /dev/null +++ b/contrib/gprs/gprs-split-trace-by-tlli.lua @@ -0,0 +1,46 @@ +-- Create a file named by_ip/''ip_addess''.cap with all ip traffic of each ip host. (works for tshark only) +-- Dump files are created for both source and destination hosts +do + local dir = "by_tlli" + local dumpers = {} + local function init_listener() + local udp_port_table = DissectorTable.get("udp.port") + local gprs_ns_dis = Dissector.get("gprs_ns") + udp_port_table:add(23000,gprs_ns_dis) + + local field_tlli = Field.new("bssgp.tlli") + local tap = Listener.new("ip", "udp.port == 23000") + + -- we will be called once for every IP Header. + -- If there's more than one IP header in a given packet we'll dump the packet once per every header + function tap.packet(pinfo,tvb,ip) + local tlli = field_tlli() + if not tlli then + return + end + + local tlli_str = tostring(tlli) + tlli_dmp = dumpers[tlli_str] + if not tlli_dmp then + local tlli_hex = string.format("0x%x", tonumber(tlli_str)) + print("Creating dump for TLLI " .. tlli_hex) + tlli_dmp = Dumper.new_for_current(dir .. "/" .. tlli_hex .. ".pcap") + dumpers[tlli_str] = tlli_dmp + end + tlli_dmp:dump_current() + tlli_dmp:flush() + end + function tap.draw() + for tlli,dumper in pairs(dumpers) do + dumper:flush() + end + end + function tap.reset() + for tlli,dumper in pairs(dumpers) do + dumper:close() + end + dumpers = {} + end + end + init_listener() +end diff --git a/contrib/gprs/gprs-verify-nu.lua b/contrib/gprs/gprs-verify-nu.lua new file mode 100644 index 000000000..e44fdd16f --- /dev/null +++ b/contrib/gprs/gprs-verify-nu.lua @@ -0,0 +1,59 @@ +-- This script verifies that the N(U) is increasing... +-- +do + local nu_state_src = {} + + local function init_listener() + -- handle the port as NS over IP + local udp_port_table = DissectorTable.get("udp.port") + local gprs_ns_dis = Dissector.get("gprs_ns") + udp_port_table:add(23000,gprs_ns_dis) + + -- we want to look here... + local llc_sapi_get = Field.new("llcgprs.sapib") + local llc_nu_get = Field.new("llcgprs.nu") + local bssgp_tlli_get = Field.new("bssgp.tlli") + + local tap = Listener.new("ip", "udp.port == 23000") + function tap.packet(pinfo,tvb,ip) + local llc_sapi = llc_sapi_get() + local llc_nu = llc_nu_get() + local bssgp_tlli = bssgp_tlli_get() + + if not llc_sapi or not llc_nu or not bssgp_tlli then + return + end + + local ip_src = tostring(ip.ip_src) + local bssgp_tlli = tostring(bssgp_tlli) + local llc_nu = tostring(llc_nu) + local llc_sapi = tostring(llc_sapi) + + local src_key = ip_src .. "-" .. bssgp_tlli .. "-" .. llc_sapi + local last_nu = nu_state_src[src_key] + if not last_nu then + -- print("Establishing mapping for " .. src_key) + nu_state_src[src_key] = llc_nu + return + end + + local function tohex(number) + return string.format("0x%x", tonumber(number)) + end + + nu_state_src[src_key] = llc_nu + if tonumber(last_nu) + 1 ~= tonumber(llc_nu) then + print("JUMP in N(U) on TLLI " .. tohex(bssgp_tlli) .. " and SAPI: " .. llc_sapi .. " src: " .. ip_src) + print("\t last: " .. last_nu .. " now: " .. llc_nu) + end + end + + function tap.draw() + end + + function tap.reset() + end + end + init_listener() +end + diff --git a/contrib/hlr-remove-old.sql b/contrib/hlr-remove-old.sql new file mode 100644 index 000000000..626a331e1 --- /dev/null +++ b/contrib/hlr-remove-old.sql @@ -0,0 +1,18 @@ +-- Remove old data from the database +DELETE FROM Subscriber + WHERE id != 1 AND datetime('now', '-10 days') > updated AND authorized != 1; +DELETE FROM Equipment + WHERE datetime('now', '-10 days') > updated; +DELETE FROM EquipmentWatch + WHERE datetime('now', '-10 days') > updated; +DELETE FROM SMS + WHERE datetime('now', '-10 days') > created; +DELETE FROM VLR + WHERE datetime('now', '-10 days') > updated; +DELETE FROM ApduBlobs + WHERE datetime('now', '-10 days') > created; +DELETE FROM Counters + WHERE datetime('now', '-10 days') > timestamp; +DELETE FROM RateCounters + WHERE datetime('now', '-10 days') > timestamp; +VACUUM; diff --git a/contrib/mgcp_server.py b/contrib/mgcp_server.py new file mode 100755 index 000000000..05c489db5 --- /dev/null +++ b/contrib/mgcp_server.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# Simple server for mgcp... send audit, receive response.. + +import socket, time + +MGCP_GATEWAY_PORT = 2427 +MGCP_CALLAGENT_PORT = 2727 + +rsip_resp = """200 321321332\r\n""" +audit_packet = """AUEP %d 13@mgw MGCP 1.0\r\n""" +crcx_packet = """CRCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n""" +dlcx_packet = """DLCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\n""" +mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 6666 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n""" + +def hexdump(src, length=8): + """Recipe is from http://code.activestate.com/recipes/142812/""" + result = [] + digits = 4 if isinstance(src, unicode) else 2 + for i in xrange(0, len(src), length): + s = src[i:i+length] + hexa = b' '.join(["%0*X" % (digits, ord(x)) for x in s]) + text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s]) + result.append( b"%04X %-*s %s" % (i, length*(digits + 1), hexa, text) ) + return b'\n'.join(result) + +server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +server_socket.bind(("127.0.0.1", MGCP_CALLAGENT_PORT)) +server_socket.setblocking(1) + +last_ci = 1 +def send_and_receive(packet): + global last_ci + server_socket.sendto(packet, ("127.0.0.1", MGCP_GATEWAY_PORT)) + try: + data, addr = server_socket.recvfrom(4096) + + # attempt to store the CI of the response + list = data.split("\n") + for item in list: + if item.startswith("I: "): + last_ci = int(item[3:]) + + print hexdump(data), addr + except socket.error, e: + print e + pass + +def generate_tid(): + import random + return random.randint(0, 65123) + + + +while True: + send_and_receive(audit_packet % generate_tid()) + send_and_receive(crcx_packet % generate_tid() ) + send_and_receive(mdcx_packet % (generate_tid(), last_ci)) + send_and_receive(dlcx_packet % (generate_tid(), last_ci)) + + time.sleep(3) diff --git a/contrib/nat/test_regexp.c b/contrib/nat/test_regexp.c new file mode 100644 index 000000000..808a703ca --- /dev/null +++ b/contrib/nat/test_regexp.c @@ -0,0 +1,30 @@ +/* make test_regexp */ +#include +#include +#include + + +int main(int argc, char **argv) +{ + regex_t reg; + regmatch_t matches[2]; + + if (argc != 4) { + printf("Invoke with: test_regexp REGEXP REPLACE NR\n"); + return -1; + } + + if (regcomp(®, argv[1], REG_EXTENDED) != 0) { + fprintf(stderr, "Regexp '%s' is not valid.\n", argv[1]); + return -1; + } + + if (regexec(®, argv[3], 2, matches, 0) == 0 && matches[1].rm_eo != -1) + printf("New Number: %s%s\n", argv[2], &argv[3][matches[1].rm_so]); + else + printf("No match.\n"); + + regfree(®); + + return 0; +} diff --git a/contrib/rtp/rtp_replay.st b/contrib/rtp/rtp_replay.st new file mode 100644 index 000000000..a281570cf --- /dev/null +++ b/contrib/rtp/rtp_replay.st @@ -0,0 +1,17 @@ +" +Simple UDP replay from the state files +" + +PackageLoader fileInPackage: #Sockets. +FileStream fileIn: 'rtp_replay_shared.st'. + + +Eval [ + | replay | + + + replay := RTPReplay on: 'rtp_ssrc6976010.240.240.1_to_10.240.240.50.state'. + + Transcript nextPutAll: 'Going to stream now'; nl. + replay streamAudio: '127.0.0.1' port: 4000. +] diff --git a/contrib/rtp/rtp_replay_shared.st b/contrib/rtp/rtp_replay_shared.st new file mode 100644 index 000000000..dd32aed7a --- /dev/null +++ b/contrib/rtp/rtp_replay_shared.st @@ -0,0 +1,108 @@ +" +Simple UDP replay from the state files +" + +PackageLoader fileInPackage: #Sockets. + +Object subclass: SDPUtils [ + "Look into using PetitParser." + SDPUtils class >> findPort: aSDP [ + aSDP linesDo: [:line | + (line startsWith: 'm=audio ') ifTrue: [ + | stream | + stream := line readStream + skip: 'm=audio ' size; + yourself. + ^ Number readFrom: stream. + ] + ]. + + ^ self error: 'Not found'. + ] + + SDPUtils class >> findHost: aSDP [ + aSDP linesDo: [:line | + (line startsWith: 'c=IN IP4 ') ifTrue: [ + | stream | + ^ stream := line readStream + skip: 'c=IN IP4 ' size; + upToEnd. + ] + ]. + + ^ self error: 'Not found'. + ] +] + +Object subclass: RTPReplay [ + | filename socket | + RTPReplay class >> on: aFile [ + ^ self new + initialize; + file: aFile; yourself + ] + + initialize [ + socket := Sockets.DatagramSocket new. + ] + + file: aFile [ + filename := aFile + ] + + localPort [ + ^ socket port + ] + + streamAudio: aHost port: aPort [ + | file last_time last_image udp_send dest | + + last_time := nil. + last_image := nil. + file := FileStream open: filename. + + "Send the payload" + dest := Sockets.SocketAddress byName: aHost. + udp_send := [:payload | | datagram | + datagram := Sockets.Datagram data: payload contents address: dest port: aPort. + socket nextPut: datagram + ]. + + [file atEnd] whileFalse: [ + | lineStream time data now_image | + lineStream := file nextLine readStream. + + "Read the time, skip the blank, parse the data" + time := Number readFrom: lineStream. + lineStream skip: 1. + + data := WriteStream on: (ByteArray new: 30). + [lineStream atEnd] whileFalse: [ + | hex | + hex := lineStream next: 2. + data nextPut: (Number readFrom: hex readStream radix: 16). + ]. + + last_time isNil + ifTrue: [ + "First time, send it right now" + last_time := time. + last_image := Time millisecondClockValue. + udp_send value: data. + ] + ifFalse: [ + | wait_image new_image_time | + + "How long to wait?" + wait_image := last_image + ((time - last_time) * 1000). + [ wait_image > Time millisecondClockValue ] + whileTrue: [Processor yield]. + + udp_send value: data. + last_time := time. + last_image := wait_image. + ] + ] + ] +] + diff --git a/contrib/rtp/rtp_replay_sip.st b/contrib/rtp/rtp_replay_sip.st new file mode 100644 index 000000000..5f844df1d --- /dev/null +++ b/contrib/rtp/rtp_replay_sip.st @@ -0,0 +1,87 @@ +""" +Create a SIP connection and then stream... +""" + +PackageLoader + fileInPackage: #OsmoSIP. + +"Load for the replay code" +FileStream fileIn: 'rtp_replay_shared.st'. + + +Osmo.SIPCall subclass: StreamCall [ + | sem stream | + + createCall: aSDP [ + | sdp | + stream := RTPReplay on: 'rtp_ssrc6976010.240.240.1_to_10.240.240.50.state'. + sdp := aSDP % {stream localPort}. + ^ super createCall: sdp. + ] + + sem: aSemaphore [ + sem := aSemaphore + ] + + sessionNew [ + | host port | + Transcript nextPutAll: 'The call has started'; nl. + Transcript nextPutAll: sdp_result; nl. + + host := SDPUtils findHost: sdp_result. + port := SDPUtils findPort: sdp_result. + + [ + stream streamAudio: host port: port. + Transcript nextPutAll: 'Streaming has finished.'; nl. + ] fork. + ] + + sessionFailed [ + sem signal + ] + + sessionEnd [ + sem signal + ] +] + +Eval [ + | transport agent call sem sdp_fr sdp_amr | + + + sdp_fr := (WriteStream on: String new) + nextPutAll: 'v=0'; cr; nl; + nextPutAll: 'o=twinkle 1739517580 1043400482 IN IP4 127.0.0.1'; cr; nl; + nextPutAll: 's=-'; cr; nl; + nextPutAll: 'c=IN IP4 127.0.0.1'; cr; nl; + nextPutAll: 't=0 0'; cr; nl; + nextPutAll: 'm=audio %1 RTP/AVP 0 101'; cr; nl; + nextPutAll: 'a=rtpmap:0 PCMU/8000'; cr; nl; + nextPutAll: 'a=rtpmap:101 telephone-event/8000'; cr; nl; + nextPutAll: 'a=fmtp:101 0-15'; cr; nl; + nextPutAll: 'a=ptime:20'; cr; nl; + contents. + + sem := Semaphore new. + transport := Osmo.SIPUdpTransport + startOn: '0.0.0.0' port: 5066. + agent := Osmo.SIPUserAgent createOn: transport. + transport start. + + call := (StreamCall + fromUser: 'sip:1000@sip.zecke.osmocom.org' + host: '127.0.0.1' + port: 5060 + to: 'sip:123456@127.0.0.1' + on: agent) + sem: sem; yourself. + + call createCall: sdp_fr. + + + "Wait for the stream to have ended" + sem wait. + + (Delay forSeconds: 4) wait. +] diff --git a/contrib/rtp/timestamp_rtp.lua b/contrib/rtp/timestamp_rtp.lua new file mode 100644 index 000000000..c18a06bed --- /dev/null +++ b/contrib/rtp/timestamp_rtp.lua @@ -0,0 +1,28 @@ +print("Ni hao") + + +do + local tap = Listener.new("ip", "rtp") + local rtp_ssrc = Field.new("rtp.ssrc") + local frame_time = Field.new("frame.time_relative") + local rtp = Field.new("rtp") + + function tap.packet(pinfo, tvb, ip) + local ip_src, ip_dst = tostring(ip.ip_src), tostring(ip.ip_dst) + local rtp_data = rtp() + local filename = "rtp_ssrc" .. rtp_ssrc() "_src_" .. ip_src .. "_to_" .. ip_dst .. ".state" + local f = io.open(filename, "a") + + f:write(tostring(frame_time()) .. " ") + f:write(tostring(rtp_data.value)) + f:write("\n") + f:close() + end + + function tap.draw() + print("DRAW") + end + function tap.reset() + print("RESET") + end +end diff --git a/contrib/sms/fill-hlr.st b/contrib/sms/fill-hlr.st new file mode 100644 index 000000000..da0643ecf --- /dev/null +++ b/contrib/sms/fill-hlr.st @@ -0,0 +1,66 @@ +"I create output for some simple SQL statements for the HLR db" + + +Eval [ + +"Create tables if they don't exist" +Transcript show: 'CREATE TABLE SMS ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created TIMESTAMP NOT NULL, + sent TIMESTAMP, + sender_id INTEGER NOT NULL, + receiver_id INTEGER NOT NULL, + deliver_attempts INTEGER NOT NULL DEFAULT 0, + valid_until TIMESTAMP, + reply_path_req INTEGER NOT NULL, + status_rep_req INTEGER NOT NULL, + protocol_id INTEGER NOT NULL, + data_coding_scheme INTEGER NOT NULL, + ud_hdr_ind INTEGER NOT NULL, + dest_addr TEXT, + user_data BLOB, + header BLOB, + text TEXT);'; nl; + show: 'CREATE TABLE Subscriber ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created TIMESTAMP NOT NULL, + updated TIMESTAMP NOT NULL, + imsi NUMERIC UNIQUE NOT NULL, + name TEXT, + extension TEXT UNIQUE, + authorized INTEGER NOT NULL DEFAULT 0, + tmsi TEXT UNIQUE, + lac INTEGER NOT NULL DEFAULT 0);'; nl. + +"Create some dummy subscribers" +num_sub := 1000. +num_sms := 30. +lac := 1. + +Transcript show: 'BEGIN;'; nl. + +1 to: num_sub do: [:each | + Transcript show: 'INSERT INTO Subscriber + (imsi, created, updated, authorized, lac, extension) + VALUES + (%1, datetime(''now''), datetime(''now''), 1, %2, %3);' % + {(274090000000000 + each). lac. each}; nl. +]. + +1 to: num_sms do: [:sms | + 1 to: num_sub do: [:sub | + Transcript show: 'INSERT INTO SMS + (created, sender_id, receiver_id, valid_until, + reply_path_req, status_rep_req, protocol_id, + data_coding_scheme, ud_hdr_ind, dest_addr, + text) VALUES + (datetime(''now''), 1, %1, ''2222-2-2'', + 0, 0, 0, + 0, 0, ''123456'', + ''abc'');' % {sub}; nl. + ] +]. + +Transcript show: 'COMMIT;'; nl. + +] diff --git a/contrib/sms/hlr-query.st b/contrib/sms/hlr-query.st new file mode 100644 index 000000000..bd3f97a4a --- /dev/null +++ b/contrib/sms/hlr-query.st @@ -0,0 +1,10 @@ +"Query for one SMS" + +Eval [ +1 to: 100 do: [:each | + Transcript show: 'SELECT SMS.* FROM SMS + JOIN Subscriber ON SMS.receiver_id = Subscriber.id + WHERE SMS.id >= 1 AND SMS.sent IS NULL AND Subscriber.lac > 0 + ORDER BY SMS.id LIMIT 1;'; nl. +]. +] diff --git a/contrib/sms/sqlite-probe.tap.d b/contrib/sms/sqlite-probe.tap.d new file mode 100644 index 000000000..e75cdfcfa --- /dev/null +++ b/contrib/sms/sqlite-probe.tap.d @@ -0,0 +1,5 @@ +probe process("/usr/lib/libsqlite3.so.0.8.6").function("sqlite3_get_table") +{ + a = user_string($zSql); + printf("sqlite3_get_table called '%s'\n", a); +} diff --git a/contrib/systemd/osmo-bsc-mgcp.service b/contrib/systemd/osmo-bsc-mgcp.service new file mode 100644 index 000000000..4b3df2c06 --- /dev/null +++ b/contrib/systemd/osmo-bsc-mgcp.service @@ -0,0 +1,9 @@ +[Unit] +Description=OpenBSC MGCP + +[Service] +Type=simple +Restart=always +ExecStart=/usr/bin/osmo-bsc_mgcp -s -c /etc/osmocom/osmo-bsc-mgcp.cfg +Restart=always +RestartSec=2 diff --git a/contrib/systemd/osmo-bsc.service b/contrib/systemd/osmo-bsc.service new file mode 100644 index 000000000..772d472a3 --- /dev/null +++ b/contrib/systemd/osmo-bsc.service @@ -0,0 +1,10 @@ +[Unit] +Description=OpenBSC BSC +Requires=osmo-bsc-mgcp.service + +[Service] +Type=simple +Restart=always +ExecStart=/usr/bin/osmo-bsc -c /etc/osmocom/osmo-bsc.cfg -s +Restart=always +RestartSec=2 diff --git a/contrib/systemd/osmo-nitb.service b/contrib/systemd/osmo-nitb.service new file mode 100644 index 000000000..203aeb15c --- /dev/null +++ b/contrib/systemd/osmo-nitb.service @@ -0,0 +1,9 @@ +[Unit] +Description=OpenBSC Network In the Box (NITB) + +[Service] +Type=simple +Restart=always +ExecStart=/usr/bin/osmo-nitb -s -C -c /etc/osmocom/osmo-nitb.cfg -l /var/lib/osmocom/hlr.sqlite3 +Restart=always +RestartSec=2 diff --git a/doc/BS11-OML.txt b/doc/BS11-OML.txt new file mode 100644 index 000000000..e5c3299c9 --- /dev/null +++ b/doc/BS11-OML.txt @@ -0,0 +1,31 @@ +The Siemens BS-11 supports the following additional GSM 12.21 OML operations: + + +CREATE OBJECT + +abis_om_fom_hdr.obj_class can be +A3: +A5: ALCO, BBSIG, CCLK, GPSU, LI, PA +A8: EnvaBTSE +A9: BPORT + +the abis_om_obj_inst.trx_nr field indicates the index of object, whereas the +abis_om_fom_hdr.bts_nr indicates the type of the object. + +enum abis_bs11_objtype { + BS11_OBJ_ALCO = 0x01, + BS11_OBJ_BBSIG = 0x02, /* obj_class: 0,1 */ + BS11_OBJ_TRX1 = 0x03, /* only DEACTIVATE TRX1 */ + BS11_OBJ_CCLK = 0x04, + BS11_OBJ_GPSU = 0x06, + BS11_OBJ_LI = 0x07, + BS11_OBJ_PA = 0x09, /* obj_class: 0, 1*/ +}; + +In case of CREATE ENVABTSE, the abis_om_obj_inst.trx_nr indicates the EnvaBTSEx +number. + +In case of A9 (CREAETE BPORT), the abis_om_obj_inst.bts_nr indicates which BPORT +shall be used. + + diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 000000000..aee2d7baa --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = examples diff --git a/doc/call-routing.txt b/doc/call-routing.txt new file mode 100644 index 000000000..3402f9e33 --- /dev/null +++ b/doc/call-routing.txt @@ -0,0 +1,25 @@ +Call routing in OpenBSC + +Flow of events: + + # MO call initiated by MS, CHANNEL RQD, IMMEDIATE ASSIGN + # MS sends CC SETUP message, we assume already on TCH/H FACCH + # OpenBSC does a subscriber lookup based on the target extension + * If a subscriber is found: + # send CALL PROCEEDING message to MO + # page the MT subscriber and ask itI to ask for TCH/H + # once paging completes, we have the TCH/H for the MT end + # send SETUP to MT + # receive CALL CONFIRMED from MT + # set-up the TRAU mux mapping between the E1 subslots for both TCH/H + # receive ALERTING from MT, route ALERTING to MO + # receive CONNECT from MT, confirm to MT with CONNECT_ACK + # send a CONNECT message to MO, receive CONNECT_ACK from MO + * If subscriber is not found: + # send RELEASE COMPLETE with apropriate cause to MO (1: unalloacated 3: no route) + + + +Thoughts about RR/MM: + +* we allocate RR/MM entities on demand, when we need them diff --git a/doc/channel_release.txt b/doc/channel_release.txt new file mode 100644 index 000000000..c9cdfebca --- /dev/null +++ b/doc/channel_release.txt @@ -0,0 +1,95 @@ + +GSM 04.08 7.1.7 / 9.1.7 RR CHANNEL RELESE + +RSL 08.58 3.4 / ? RLL Link Release Request + +RSL 08.58 4.6 / 8.4.5 DEACTivate SACCH + * Deactivate SACCH according to Channel Release Proc 04.08 + * to be sent after RR CHANNEL RELEASE is sent to MS + +RSL 08.58 4.7 / 8.4.14 RF CHANnel RELease + * tells the BTS to release a radio channel + * "when an activated radio channel is no longer needed" + * BTS responds with RF CHANnel RELease ACKnowledge + + +GSM 04.08 3.4.13: RR connection release procedure + +* network sends RR CHANNEL RELEASE to MS on the DCCH + * start T3109 + * deactivate SACCH +* MS disconnects main signalling link (by sending DISC) + * all other data links are disconnected by local end link release +* network receives DISC (BTS sends RLL REL IND to BSC) + * stop T3109 + * start T3111 +* when T3111 times out, the network can reuse the channls +* if T3109 times out, the network deactivates the channels + and can reuse them + * this probably means simply RF CHANnel RELease + + +== Implementation in OpenBSC == + +There are two possible reasons a gsm_subscriber_connection +will be released. One is a network failure, the other is +the completion of an operation/transaction. + +=== Failure === +The BSC API will call the gsm_04_08.c:gsm0408_clear_request callback +and the MSC part will release all transactions, operations and such +and the channels will be released as error case. + +=== Success === +Every time an 'operation' or 'transaction' is finished msc_release_connection +will be called and it will determine if the gsm_subscriber_connection can +be released. + +In case it can be released bsc_api.c:gsm0808_clear will be called +which will release all lchan's associated with the connection. For the +primary channel a SACH Deactivate will be send with the release +reason NORMAL RELEASE. + + +bsc_api.c:gsm0808_clear + * Release a channel used for handover + * Release the primary lchan with normal release, SACH deactivate + +chan_alloc.c:lchan_release(chan, sacch_deactivate, reason) + * Start the release procedure. It is working in steps with callbacks + coming from the abis_rsl.c code. + * Release all SAPI's > 0 as local end (The BTS should send a + REL_CONF a message) + * Send SACH Deactivate on SAPI=0 if required. + * Start T3109 (stop it when the main signalling link is disconnected) + or when the channel released. On timeout start the error handling. + * abis_rsl.c schedules the RSL_MT_RF_CHAN_REL once all SAPI's are + released and after T3111 has timed out or there is an error. + +RX of RELease INDication: + * Calls internal rsl_handle_release which might release the RF. + +RX of RELease CONFirmation: + * Calls internal rsl_handle_release which might release the RF. + +* RX of RF_CHAN_REL_ACK + * call lchan_free() + + +=== Integration with SMS === + +* RX of CP_ERROR or unimplemented MT + * trigger trans_free() which will msc_release_connection() + +* CP TC1* expired while waiting for CP-ACK + * trigger trans_free() which will msc_release_connection() + +* RX of RP_ERROR + * trigger trans_free() which will msc_release_connection() + +* TX of CP-ACK in MT DELIVER + * trigger trans_free() which will msc_release_connection() + +* RX of CP-ACK in MO SUBMIT + * trigger trans_free() which will msc_release_connection() + diff --git a/doc/control-interface.txt b/doc/control-interface.txt new file mode 100644 index 000000000..b43cafc35 --- /dev/null +++ b/doc/control-interface.txt @@ -0,0 +1,21 @@ +The protocol for the control interface is wrapped inside the ip.access header +with the IPAC_PROTO_OSMO protocol ID (0xee). Inside the ip.access header is +a struct ipaccess_head_ext with protocol ID 0x00 which indicates the control +interface. + +After that the actual protocol is text based: + +* Getting the value of a variable +-> GET +<- GET_REPLY +or ERROR + +* Setting the value of a variable +-> SET +<- SET_REPLY +or ERROR + +* A value changes which triggers a trap +<- TRAP + + needs to be unique within a connection. '0' is not allowed diff --git a/doc/e1-data-model.txt b/doc/e1-data-model.txt new file mode 100644 index 000000000..509004fe9 --- /dev/null +++ b/doc/e1-data-model.txt @@ -0,0 +1,172 @@ +E1 related data model + +This data model describes the physical relationship of the individual +parts in the network, it is not the logical/protocol side of the GSM +network. + +A BTS is connected to the BSC by some physical link. It could be an actual +E1 link, but it could also be abis-over-IP with a mixture of TCP and RTP/UDP. + +To further complicate the fact, multiple BTS can share one such pysical +link. On a single E1 line, we can easily accomodate up to three BTS with +two TRX each. + +Thus, it is best for OpenBSC to have some kind of abstraction layer. The BSC's +view of a BTS connected to it. We call this 'bts_link'. A bts_link can be +* all the TCP and UDP streams of a Abis-over-IP BTS +* a set of E1 timeslots for OML, RSL and TRAU connections on a E1 link +* a serial line exclusively used for OML messages (T-Link) + +A bts_link can be registered with the OpenBSC core at runtime. + +struct trx_link { + struct gsm_bts_trx *trx; +}; + +struct bts_link { + struct gsm_bts *bts; + struct trx_link trx_links[NUM_TRX]; +}; + +Interface from stack to input core: +====================================================================== +int abis_rsl_sendmsg(struct msgb *msg); + send a message through a RSL link to the TRX specified by the caller in + msg->trx. + +int abis_rsl_rcvmsg(struct msgb *msg); + receive a message from a RSL link from the TRX specified by the + caller in msg->trx. + +int abis_nm_sendmsg(struct msgb *msg); + send a message through a OML link to the BTS specified by the caller in + msg->trx->bts. The caller can just use bts->c0 to get the first TRX + in a BTS. (OML messages are not really sent to a TRX but to the BTS) + +int abis_nm_rcvmsg(struct msgb *msg); + receive a message from a OML link from the BTS specified by the caller + in msg->trx->bts. The caller can just use bts->c0 to get the first + TRX in a BTS. + +int abis_link_event(int event, void *data); + signal some event (such as layer 1 connect/disconnect) from the + input core to the stack. + +int subch_demux_in(mx, const uint8_t *data, int len); + receive 'len' bytes from a given E1 timeslot (TRAU frames) + +int subchan_mux_out(mx, uint8_t *data, int len); + obtain 'len' bytes of output data to be sent on E1 timeslot + +Intrface by Input Core for Input Plugins +====================================================================== + +int btslink_register_plugin(); + + +Configuration for the E1 input module +====================================================================== + +BTS + BTS number + number of TRX + OML link + E1 line number + timeslot number + [subslot number] + SAPI + TEI + for each TRX + RSL link + E1 line number + timeslot number + [subslot number] + SAPI + TEI + for each TS + E1 line number + timeslot number + subslot number + + +E1 input module data model +====================================================================== + + +enum e1inp_sign_type { + E1INP_SIGN_NONE, + E1INP_SIGN_OML, + E1INP_SIGN_RSL, +}; + +struct e1inp_sign_link { + /* list of signalling links */ + struct llist_head list; + + enum e1inp_sign_type type; + + /* trx for msg->trx of received msgs */ + struct gsm_bts_trx *trx; + + /* msgb queue of to-be-transmitted msgs */ + struct llist_head tx_list; + + /* SAPI and TEI on the E1 TS */ + uint8_t sapi; + uint8_t tei; +} + +enum e1inp_ts_type { + E1INP_TS_TYPE_NONE, + E1INP_TS_TYPE_SIGN, + E1INP_TS_TYPE_TRAU, +}; + +/* A timeslot in the E1 interface */ +struct e1inp_ts { + enum e1inp_ts_type type; + struct e1inp_line *line; + union { + struct { + struct llist_head sign_links; + } sign; + struct { + /* subchannel demuxer for frames from E1 */ + struct subch_demux demux; + /* subchannel muxer for frames to E1 */ + struct subch_mux mux; + } trau; + }; + union { + struct { + /* mISDN driver has one fd for each ts */ + struct osmo_fd; + } misdn; + } driver; +}; + +struct e1inp_line { + unsigned int num; + char *name; + + struct e1inp_ts ts[NR_E1_TS]; + + char *e1inp_driver; + void *driver_data; +}; + +/* Call from the Stack: configuration of this TS has changed */ +int e1inp_update_ts(struct e1inp_ts *ts); + +/* Receive a packet from the E1 driver */ +int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg, + uint8_t tei, uint8_t sapi); + +/* Send a packet, callback function in the driver */ +int e1driver_tx_ts(struct e1inp_ts *ts, struct msgb *msg) + + +struct e1inp_driver { + const char *name; + int (*want_write)(struct e1inp_ts *ts); +}; diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am new file mode 100644 index 000000000..8f14fdcf7 --- /dev/null +++ b/doc/examples/Makefile.am @@ -0,0 +1,23 @@ + +CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,' + +dist-hook: + for f in $$($(CFG_FILES)); do \ + j="$(distdir)/$$f" && \ + mkdir -p "$$(dirname $$j)" && \ + $(INSTALL_DATA) $(srcdir)/$$f $$j; \ + done + +install-data-hook: + for f in $$($(CFG_FILES)); do \ + j="$(DESTDIR)$(docdir)/examples/$$f" && \ + mkdir -p "$$(dirname $$j)" && \ + $(INSTALL_DATA) $(srcdir)/$$f $$j; \ + done + +uninstall-hook: + @$(PRE_UNINSTALL) + for f in $$($(CFG_FILES)); do \ + j="$(DESTDIR)$(docdir)/examples/$$f" && \ + $(RM) $$j; \ + done diff --git a/doc/examples/osmo-bsc/osmo-bsc.cfg b/doc/examples/osmo-bsc/osmo-bsc.cfg new file mode 100644 index 000000000..d7461964f --- /dev/null +++ b/doc/examples/osmo-bsc/osmo-bsc.cfg @@ -0,0 +1,100 @@ +! +! OsmoBSC (0.9.14+gitr1+3d331c0062bb0c9694dbd4d1eab7adc58138c3ae) configuration saved from vty +!! +password foo +! +! +line vty + no login +! +e1_input + e1_line 0 driver ipa +network + network country code 1 + mobile network code 1 + short name OsmoBSC + long name OsmoBSC + auth policy closed + location updating reject cause 13 + encryption a5 0 + neci 1 + paging any use tch 0 + rrlp mode none + mm info 1 + handover 0 + handover window rxlev averaging 10 + handover window rxqual averaging 1 + handover window rxlev neighbor averaging 10 + handover power budget interval 6 + handover power budget hysteresis 3 + handover maximum distance 9999 + timer t3101 10 + timer t3103 0 + timer t3105 0 + timer t3107 0 + timer t3109 0 + timer t3111 0 + timer t3113 60 + timer t3115 0 + timer t3117 0 + timer t3119 0 + timer t3122 0 + timer t3141 0 + dtx-used 0 + subscriber-keep-in-ram 0 + bts 0 + type nanobts + band DCS1800 + cell_identity 0 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + ms max power 15 + cell reselection hysteresis 4 + rxlev access min 0 + channel allocator ascending + rach tx integer 9 + rach max transmission 7 + ip.access unit_id 0 0 + oml ip.access stream_id 255 line 0 + neighbor-list mode manual-si5 + neighbor-list add arfcn 100 + neighbor-list add arfcn 200 + si5 neighbor-list add arfcn 10 + si5 neighbor-list add arfcn 20 + gprs mode none + trx 0 + rf_locked 0 + arfcn 871 + nominal power 23 + max_power_red 20 + rsl e1 tei 0 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + hopping enabled 0 + timeslot 1 + phys_chan_config TCH/F + hopping enabled 0 + timeslot 2 + phys_chan_config TCH/F + hopping enabled 0 + timeslot 3 + phys_chan_config TCH/F + hopping enabled 0 + timeslot 4 + phys_chan_config TCH/F + hopping enabled 0 + timeslot 5 + phys_chan_config TCH/F + hopping enabled 0 + timeslot 6 + phys_chan_config TCH/F + hopping enabled 0 + timeslot 7 + phys_chan_config TCH/F + hopping enabled 0 +msc + ip.access rtp-base 4000 + timeout-ping 20 + timeout-pong 5 + dest 192.168.100.11 6666 0 diff --git a/doc/examples/osmo-bsc_mgcp/mgcp.cfg b/doc/examples/osmo-bsc_mgcp/mgcp.cfg new file mode 100644 index 000000000..7409dd00d --- /dev/null +++ b/doc/examples/osmo-bsc_mgcp/mgcp.cfg @@ -0,0 +1,18 @@ +! +! MGCP configuration hand edited +! ! +password foo +! +line vty + no login +! +mgcp +! local ip 213.167.134.14 + bts ip 172.16.252.43 + bind ip 127.0.0.1 + bind port 2427 + rtp base 4000 + sdp audio payload number 98 + sdp audio payload name AMR/8000 + number endpoints 31 + loop 1 diff --git a/doc/examples/osmo-bsc_nat/black-list.cfg b/doc/examples/osmo-bsc_nat/black-list.cfg new file mode 100644 index 000000000..d36179d37 --- /dev/null +++ b/doc/examples/osmo-bsc_nat/black-list.cfg @@ -0,0 +1 @@ +678012512671923:6:6: diff --git a/doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg b/doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg new file mode 100644 index 000000000..93cb98075 --- /dev/null +++ b/doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg @@ -0,0 +1,64 @@ +! +! OsmoBSCNAT (0.12.0.266-2daa9) configuration saved from vty +!! +! +log stderr + logging filter all 1 + logging color 1 + logging timestamp 0 + logging level all everything + logging level rll notice + logging level cc notice + logging level mm notice + logging level rr notice + logging level rsl notice + logging level nm info + logging level mncc notice + logging level pag notice + logging level meas notice + logging level sccp notice + logging level msc notice + logging level mgcp notice + logging level ho notice + logging level db notice + logging level ref notice + logging level gprs everything + logging level ns info + logging level bssgp everything + logging level llc everything + logging level sndcp everything + logging level nat notice + logging level ctrl notice + logging level smpp everything + logging level lglobal notice + logging level llapd notice + logging level linp notice + logging level lmux notice + logging level lmi notice + logging level lmib notice + logging level lsms notice +! +line vty + no login +! +mgcp + bind ip 0.0.0.0 + bind port 2427 + rtp bts-base 4000 + rtp net-base 16000 + rtp ip-dscp 0 + no rtcp-omit + sdp audio-payload number 126 + sdp audio-payload name AMR/8000 + loop 0 + number endpoints 1 + call-agent ip 127.0.0.1 + rtp transcoder-base 0 + transcoder-remote-base 4000 +nat + msc ip 127.0.0.1 + msc port 5000 + timeout auth 2 + timeout ping 20 + timeout pong 5 + ip-dscp 0 diff --git a/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg b/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg new file mode 100644 index 000000000..0c3917a3e --- /dev/null +++ b/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg @@ -0,0 +1,25 @@ +! +! Osmocom Gb Proxy (0.9.0.404-6463) configuration saved from vty +!! +! +line vty + no login +! +gbproxy + sgsn nsei 101 +ns + nse 101 nsvci 101 + nse 101 remote-role sgsn + nse 101 encapsulation udp + nse 101 remote-ip 192.168.100.239 + nse 101 remote-port 7777 + timer tns-block 3 + timer tns-block-retries 3 + timer tns-reset 3 + timer tns-reset-retries 3 + timer tns-test 30 + timer tns-alive 3 + timer tns-alive-retries 10 + encapsulation framerelay-gre enabled 0 + encapsulation framerelay-gre local-ip 0.0.0.0 + encapsulation udp local-port 23000 diff --git a/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg b/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg new file mode 100644 index 000000000..36065e254 --- /dev/null +++ b/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg @@ -0,0 +1,164 @@ +! +! OpenBSC (0.9.0.845-57c4) configuration saved from vty +!! +password foo +! +line vty + no login +! +e1_input + e1_line 0 driver misdn +network + network country code 1 + mobile network code 1 + short name OpenBSC + long name OpenBSC + auth policy closed + location updating reject cause 13 + encryption a5 0 + neci 1 + rrlp mode none + mm info 0 + handover 0 + handover window rxlev averaging 10 + handover window rxqual averaging 1 + handover window rxlev neighbor averaging 10 + handover power budget interval 6 + handover power budget hysteresis 3 + handover maximum distance 9999 + timer t3101 10 + timer t3103 0 + timer t3105 0 + timer t3107 0 + timer t3109 4 + timer t3111 0 + timer t3113 60 + timer t3115 0 + timer t3117 0 + timer t3119 0 + timer t3141 0 + bts 0 + type bs11 + band GSM900 + cell_identity 0 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + ms max power 15 + cell reselection hysteresis 4 + rxlev access min 0 + channel allocator descending + rach tx integer 9 + rach max transmission 7 + oml e1 line 0 timeslot 1 sub-slot full + oml e1 tei 25 + gprs mode none + trx 0 + rf_locked 0 + arfcn 121 + nominal power 24 + max_power_red 12 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 1 + timeslot 0 + phys_chan_config CCCH + hopping enabled 0 + e1 line 0 timeslot 1 sub-slot full + timeslot 1 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 2 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 2 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 2 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 3 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 3 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 3 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 3 sub-slot 3 + trx 1 + rf_locked 0 + arfcn 119 + nominal power 24 + max_power_red 12 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 2 + timeslot 0 + phys_chan_config SDCCH8 + hopping enabled 1 + hopping sequence-number 0 + hopping maio 0 + hopping arfcn add 117 + hopping arfcn add 119 + timeslot 1 + phys_chan_config TCH/F + hopping enabled 1 + hopping sequence-number 0 + hopping maio 0 + hopping arfcn add 117 + hopping arfcn add 119 + e1 line 0 timeslot 4 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + hopping enabled 1 + hopping sequence-number 0 + hopping maio 0 + hopping arfcn add 117 + hopping arfcn add 119 + e1 line 0 timeslot 4 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + hopping enabled 1 + hopping sequence-number 0 + hopping maio 0 + hopping arfcn add 117 + hopping arfcn add 119 + e1 line 0 timeslot 4 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + hopping enabled 1 + hopping sequence-number 0 + hopping maio 0 + hopping arfcn add 117 + hopping arfcn add 119 + e1 line 0 timeslot 5 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + hopping enabled 1 + hopping sequence-number 0 + hopping maio 0 + hopping arfcn add 117 + hopping arfcn add 119 + e1 line 0 timeslot 5 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + hopping enabled 1 + hopping sequence-number 0 + hopping maio 0 + hopping arfcn add 117 + hopping arfcn add 119 + e1 line 0 timeslot 5 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + hopping enabled 1 + hopping sequence-number 0 + hopping maio 0 + hopping arfcn add 117 + hopping arfcn add 119 + e1 line 0 timeslot 5 sub-slot 3 diff --git a/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg b/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg new file mode 100644 index 000000000..ebe6cc6cb --- /dev/null +++ b/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg @@ -0,0 +1,84 @@ +! +! OpenBSC configuration saved from vty +! ! +password foo +! +line vty + no login +! +e1_input + e1_line 0 driver misdn +network + network country code 1 + mobile network code 1 + short name OpenBSC + long name OpenBSC + timer t3101 10 + timer t3113 60 + bts 0 + type bs11 + band GSM900 + cell_identity 1 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + oml e1 line 0 timeslot 1 sub-slot full + oml e1 tei 25 + trx 0 + arfcn 121 + max_power_red 0 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 1 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + e1 line 0 timeslot 1 sub-slot full + timeslot 1 + phys_chan_config SDCCH8 + e1 line 0 timeslot 2 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 3 + trx 1 + arfcn 123 + max_power_red 0 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 2 + timeslot 0 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 0 + timeslot 1 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 3 diff --git a/doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg b/doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg new file mode 100644 index 000000000..9ae800342 --- /dev/null +++ b/doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg @@ -0,0 +1,148 @@ +! +! OpenBSC configuration saved from vty +! ! +password foo +! +line vty + no login +! +network + network country code 1 + mobile network code 1 + short name OpenBSC + long name OpenBSC + timer t3101 10 + timer t3113 60 + bts 0 + type bs11 + band GSM900 + cell_identity 1 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + oml e1 line 0 timeslot 1 sub-slot full + oml e1 tei 25 + trx 0 + arfcn 121 + max_power_red 0 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 1 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + e1 line 0 timeslot 1 sub-slot full + timeslot 1 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 3 + trx 1 + arfcn 123 + max_power_red 0 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 2 + timeslot 0 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 0 + timeslot 1 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 4 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 5 sub-slot 3 + bts 1 + type bs11 + band GSM900 + location_area_code 2 + training_sequence_code 7 + base_station_id_code 63 + oml e1 line 1 timeslot 6 sub-slot full + oml e1 tei 25 + trx 0 + arfcn 122 + max_power_red 0 + rsl e1 line 1 timeslot 6 sub-slot full + rsl e1 tei 1 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + e1 line 1 timeslot 7 sub-slot 0 + timeslot 1 + phys_chan_config SDCCH8 + e1 line 1 timeslot 7 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 1 timeslot 7 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 1 timeslot 7 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 1 timeslot 8 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 1 timeslot 8 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 1 timeslot 8 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 1 timeslot 8 sub-slot 3 + trx 1 + arfcn 124 + max_power_red 0 + rsl e1 line 1 timeslot 6 sub-slot full + rsl e1 tei 2 + timeslot 0 + phys_chan_config TCH/F + e1 line 1 timeslot 9 sub-slot 0 + timeslot 1 + phys_chan_config TCH/F + e1 line 1 timeslot 9 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 1 timeslot 9 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 1 timeslot 9 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 1 timeslot 10 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 1 timeslot 10 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 1 timeslot 10 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 1 timeslot 10 sub-slot 3 diff --git a/doc/examples/osmo-nitb/bs11/openbsc.cfg b/doc/examples/osmo-nitb/bs11/openbsc.cfg new file mode 100644 index 000000000..542edd6ad --- /dev/null +++ b/doc/examples/osmo-nitb/bs11/openbsc.cfg @@ -0,0 +1,56 @@ +! +! OpenBSC configuration saved from vty +! ! +password foo +! +line vty + no login +! +e1_input + e1_line 0 driver misdn +network + network country code 1 + mobile network code 1 + short name OpenBSC + long name OpenBSC + timer t3101 10 + timer t3113 60 + bts 0 + type bs11 + band GSM900 + cell_identity 1 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + oml e1 line 0 timeslot 1 sub-slot full + oml e1 tei 25 + trx 0 + arfcn 121 + max_power_red 0 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 1 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + e1 line 0 timeslot 1 sub-slot full + timeslot 1 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 2 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 3 sub-slot 3 + diff --git a/doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg b/doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg new file mode 100644 index 000000000..bea0b7dfc --- /dev/null +++ b/doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg @@ -0,0 +1,99 @@ +! +! OpenBSC configuration saved from vty +! ! +password foo +! +line vty + no login +! +e1_input + e1_line 0 driver ipa +network + network country code 1 + mobile network code 1 + short name OpenBSC + long name OpenBSC + auth policy closed + location updating reject cause 13 + encryption a5 0 + neci 1 + rrlp mode none + mm info 0 + handover 0 + handover window rxlev averaging 10 + handover window rxqual averaging 1 + handover window rxlev neighbor averaging 10 + handover power budget interval 6 + handover power budget hysteresis 3 + handover maximum distance 9999 + timer t3101 10 + timer t3103 0 + timer t3105 0 + timer t3107 0 + timer t3109 4 + timer t3111 0 + timer t3113 60 + timer t3115 0 + timer t3117 0 + timer t3119 0 + timer t3141 0 + bts 0 + type nanobts + band DCS1800 + cell_identity 0 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + ms max power 15 + cell reselection hysteresis 4 + rxlev access min 0 + channel allocator ascending + rach tx integer 9 + rach max transmission 7 + ip.access unit_id 1800 0 + oml ip.access stream_id 255 line 0 + gprs mode none + trx 0 + rf_locked 0 + arfcn 871 + nominal power 23 + max_power_red 0 + rsl e1 tei 0 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + timeslot 1 + phys_chan_config SDCCH8 + timeslot 2 + phys_chan_config TCH/F + timeslot 3 + phys_chan_config TCH/F + timeslot 4 + phys_chan_config TCH/F + timeslot 5 + phys_chan_config TCH/F + timeslot 6 + phys_chan_config TCH/F + timeslot 7 + phys_chan_config TCH/F + trx 1 + rf_locked 0 + arfcn 873 + nominal power 23 + max_power_red 0 + rsl e1 tei 0 + timeslot 0 + phys_chan_config SDCCH8 + timeslot 1 + phys_chan_config TCH/F + timeslot 2 + phys_chan_config TCH/F + timeslot 3 + phys_chan_config TCH/F + timeslot 4 + phys_chan_config TCH/F + timeslot 5 + phys_chan_config TCH/F + timeslot 6 + phys_chan_config TCH/F + timeslot 7 + phys_chan_config TCH/F diff --git a/doc/examples/osmo-nitb/nanobts/openbsc.cfg b/doc/examples/osmo-nitb/nanobts/openbsc.cfg new file mode 100644 index 000000000..7a443087c --- /dev/null +++ b/doc/examples/osmo-nitb/nanobts/openbsc.cfg @@ -0,0 +1,77 @@ +! +! OpenBSC configuration saved from vty +! ! +password foo +! +line vty + no login +! +e1_input + e1_line 0 driver ipa +network + network country code 1 + mobile network code 1 + short name OpenBSC + long name OpenBSC + auth policy closed + location updating reject cause 13 + encryption a5 0 + neci 1 + rrlp mode none + mm info 1 + handover 0 + handover window rxlev averaging 10 + handover window rxqual averaging 1 + handover window rxlev neighbor averaging 10 + handover power budget interval 6 + handover power budget hysteresis 3 + handover maximum distance 9999 + timer t3101 10 + timer t3103 0 + timer t3105 0 + timer t3107 0 + timer t3109 4 + timer t3111 0 + timer t3113 60 + timer t3115 0 + timer t3117 0 + timer t3119 0 + timer t3141 0 + bts 0 + type nanobts + band DCS1800 + cell_identity 0 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + ms max power 15 + cell reselection hysteresis 4 + rxlev access min 0 + channel allocator ascending + rach tx integer 9 + rach max transmission 7 + ip.access unit_id 1801 0 + oml ip.access stream_id 255 line 0 + gprs mode none + trx 0 + rf_locked 0 + arfcn 514 + nominal power 23 + max_power_red 20 + rsl e1 tei 0 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + timeslot 1 + phys_chan_config SDCCH8 + timeslot 2 + phys_chan_config TCH/F + timeslot 3 + phys_chan_config TCH/F + timeslot 4 + phys_chan_config TCH/F + timeslot 5 + phys_chan_config TCH/F + timeslot 6 + phys_chan_config TCH/F + timeslot 7 + phys_chan_config TCH/F diff --git a/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg b/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg new file mode 100644 index 000000000..f4c715c76 --- /dev/null +++ b/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg @@ -0,0 +1,117 @@ +! +! OpenBSC configuration saved from vty +! ! +password foo +! +line vty + no login +! +e1_input + e1_line 0 driver misdn +network + network country code 1 + mobile network code 1 + short name OpenBSC + long name OpenBSC + timer t3101 10 + timer t3113 60 + bts 0 + type nokia_site + band GSM1800 + cell_identity 1 + location_area_code 1 + base_station_id_code 63 + training_sequence_code 7 + + oml e1 line 0 timeslot 1 sub-slot full + oml e1 tei 1 + + trx 0 + arfcn 866 + max_power_red 24 + rsl e1 line 0 timeslot 2 sub-slot full + rsl e1 tei 1 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + e1 line 0 timeslot 6 sub-slot full + timeslot 1 + phys_chan_config SDCCH8 + e1 line 0 timeslot 6 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 6 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 6 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 7 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 7 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 7 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 7 sub-slot 3 + + trx 1 + arfcn 870 + max_power_red 24 + rsl e1 line 0 timeslot 3 sub-slot full + rsl e1 tei 2 + timeslot 0 + phys_chan_config TCH/F + e1 line 0 timeslot 8 sub-slot 0 + timeslot 1 + phys_chan_config TCH/F + e1 line 0 timeslot 8 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 8 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 8 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 9 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 9 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 9 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 9 sub-slot 3 + + trx 2 + arfcn 874 + max_power_red 24 + rsl e1 line 0 timeslot 4 sub-slot full + rsl e1 tei 3 + timeslot 0 + phys_chan_config TCH/F + e1 line 0 timeslot 10 sub-slot 0 + timeslot 1 + phys_chan_config TCH/F + e1 line 0 timeslot 10 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + e1 line 0 timeslot 10 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + e1 line 0 timeslot 10 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + e1 line 0 timeslot 11 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + e1 line 0 timeslot 11 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + e1 line 0 timeslot 11 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + e1 line 0 timeslot 11 sub-slot 3 diff --git a/doc/examples/osmo-nitb/rbs2308/openbsc.cfg b/doc/examples/osmo-nitb/rbs2308/openbsc.cfg new file mode 100644 index 000000000..97cd9df50 --- /dev/null +++ b/doc/examples/osmo-nitb/rbs2308/openbsc.cfg @@ -0,0 +1,221 @@ +! +! OpenBSC (0.9.11.308-62d46) configuration saved from vty +!! +password foo +! +line vty + no login +! +network + network country code 262 + mobile network code 42 + short name OpenBSC + long name OpenBSC + auth policy closed + location updating reject cause 13 + encryption a5 0 + neci 0 + paging any use tch 0 + rrlp mode none + mm info 0 + handover 0 + handover window rxlev averaging 10 + handover window rxqual averaging 1 + handover window rxlev neighbor averaging 10 + handover power budget interval 6 + handover power budget hysteresis 3 + handover maximum distance 9999 + timer t3101 10 + timer t3103 0 + timer t3105 0 + timer t3107 0 + timer t3109 4 + timer t3111 0 + timer t3113 60 + timer t3115 0 + timer t3117 0 + timer t3119 0 + timer t3122 0 + timer t3141 0 + dtx-used 0 + subscriber-keep-in-ram 0 + bts 0 + type rbs2000 + band GSM900 + cell_identity 0 + location_area_code 1 + training_sequence_code 7 + base_station_id_code 63 + ms max power 15 + cell reselection hysteresis 4 + rxlev access min 0 + channel allocator descending + rach tx integer 9 + rach max transmission 7 + oml e1 line 0 timeslot 1 sub-slot full + oml e1 tei 62 + neighbor-list mode automatic + gprs mode none + is-connection-list add 4 512 12 + is-connection-list add 16 524 12 + is-connection-list add 28 536 12 + is-connection-list add 40 548 12 + trx 0 + rf_locked 0 + arfcn 55 + nominal power 24 + max_power_red 12 + rsl e1 line 0 timeslot 1 sub-slot full + rsl e1 tei 0 + timeslot 0 + phys_chan_config CCCH+SDCCH4 + hopping enabled 0 + e1 line 0 timeslot 1 sub-slot full + timeslot 1 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 2 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 2 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 2 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 3 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 3 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 3 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 3 sub-slot 3 + trx 1 + rf_locked 0 + arfcn 57 + nominal power 24 + max_power_red 12 + rsl e1 line 0 timeslot 4 sub-slot full + rsl e1 tei 1 + timeslot 0 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 5 sub-slot 0 + timeslot 1 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 5 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 5 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 5 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 6 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 6 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 6 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 6 sub-slot 3 + trx 2 + rf_locked 0 + arfcn 59 + nominal power 24 + max_power_red 12 + rsl e1 line 0 timeslot 7 sub-slot full + rsl e1 tei 2 + timeslot 0 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 8 sub-slot 0 + timeslot 1 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 8 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 8 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 8 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 9 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 9 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 9 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 9 sub-slot 3 + trx 3 + rf_locked 0 + arfcn 61 + nominal power 24 + max_power_red 12 + rsl e1 line 0 timeslot 10 sub-slot full + rsl e1 tei 3 + timeslot 0 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 11 sub-slot 0 + timeslot 1 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 11 sub-slot 1 + timeslot 2 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 11 sub-slot 2 + timeslot 3 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 11 sub-slot 3 + timeslot 4 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 12 sub-slot 0 + timeslot 5 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 12 sub-slot 1 + timeslot 6 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 12 sub-slot 2 + timeslot 7 + phys_chan_config TCH/F + hopping enabled 0 + e1 line 0 timeslot 12 sub-slot 3 + +e1_input + e1_line 0 driver dahdi diff --git a/doc/examples/osmo-sgsn/osmo-sgsn.cfg b/doc/examples/osmo-sgsn/osmo-sgsn.cfg new file mode 100644 index 000000000..49559830a --- /dev/null +++ b/doc/examples/osmo-sgsn/osmo-sgsn.cfg @@ -0,0 +1,26 @@ +! +! Osmocom SGSN configuration +! +! +line vty + no login +! +sgsn + gtp local-ip 127.0.0.1 + ggsn 0 remote-ip 127.0.0.1 + ggsn 0 gtp-version 1 +! +ns + timer tns-block 3 + timer tns-block-retries 3 + timer tns-reset 3 + timer tns-reset-retries 3 + timer tns-test 30 + timer tns-alive 3 + timer tns-alive-retries 10 + encapsulation udp local-ip 127.0.0.1 + encapsulation udp local-port 23000 + encapsulation framerelay-gre enabled 0 +! +bssgp +! diff --git a/doc/gsm-hopping.txt b/doc/gsm-hopping.txt new file mode 100644 index 000000000..c964963c0 --- /dev/null +++ b/doc/gsm-hopping.txt @@ -0,0 +1,54 @@ +according to GSM 05.02: + +general parameters from CCCH: +* CA cell allocation of ARFCN's (System Information / BCCH) +* FN: TDMA frame number (t1,t2,t3') in SCH + +specific parameters from channel assignment: +* MA: mobile allocation, defines set of ARFCN's, up to 64 +* MAIO: index +* HSN: hopping sequence generator number (0..64) + + +hopping sequence generation (6.2.3): + +uint8_t rntable[114] = { + 48, 98, 63, 1, 36, 95, 78, 102, 94, 73, + 0, 64, 25, 81, 76, 59, 124, 23, 104, 100, + 101, 47, 118, 85, 18, 56, 96, 86, 54, 2, + 80, 34, 127, 13, 6, 89, 57, 103, 12, 74, + 55, 111, 75, 38, 109, 71, 112, 29, 11, 88, + 87, 19, 3, 68, 110, 26, 33, 31, 8, 45, + 82, 58, 40, 107, 32, 5, 106, 92, 62, 67, + 77, 108, 122, 37, 60, 66, 121, 42, 51, 126, + 117, 114, 4, 90, 43, 52, 53, 113, 120, 72, + 16, 49, 7, 79, 119, 61, 22, 84, 9, 97, + 125, 99, 17, 123 +}; + +/* mai=0 represents lowest ARFCN in the MA */ + + +uint8_t hopping_mai(uint8_t hsn, uint32_t fn, uint8_t maio, + uint8_t t1, uint8_t t2, uint8_t t3_) +{ + uint8_t mai; + + if (hsn == 0) /* cyclic hopping */ + mai = (fn + maio) % n; + else { + uint32_t m, m_, t_, s; + + m = t2 + rntable[(hsn xor (t1 % 64)) + t3]; + m_ = m % (2^NBIN); + t_ = t3 % (2^NBIN); + if (m_ < n then) + s = m_; + else + s = (m_ + t_) % n; + mai = (s + maio) % n; + } + + return mai; +} + diff --git a/doc/handover.txt b/doc/handover.txt new file mode 100644 index 000000000..ac19e8725 --- /dev/null +++ b/doc/handover.txt @@ -0,0 +1,89 @@ +Ideas about a handover algorithm +====================================================================== + +This is mostly based on the results presented in Chapter 8 of "Performance +Enhancements in a Frequency Hopping GSM Network" by Thomas Toftegaard Nielsen +and Joeroen Wigard. + + +=== Reasons for performing handover === + +Section 2.1.1: Handover used in their CAPACITY simulation: + +1) Interference Handover + +Average RXLEV is satisfactory high, but average RXQUAL too low indicates +interference to the channel. Handover should be made. + +2) Bad Quality + +Averaged RXQUAL is lower than a threshold + +3) Low Level / Signal Strength + +Average RXLEV is lower than a threshold + +4) Distance Handover + +MS is too far away from a cell (measured by TA) + +5) Power budget / Better Cell + +RX Level of neighbor cell is at least "HO Margin dB" dB better than the +current serving cell. + +=== Ideal parameters for HO algorithm === + +Chapter 8, Section 2.2, Table 24: + +Window RXLEV averaging: 10 SACCH frames (no weighting) +Window RXQUAL averaging: 1 SACCH frame (no averaging) +Level Threashold: 1 of the last 1 AV-RXLEV values < -110dBm +Quality Threshold: 3 of the last 4 AV-RXQUAL values >= 5 +Interference Threshold: 1 of the last AV-RXLEV > -85 dBm & + 3 of the last 4 AV-RXQUAL values >= 5 +Power Budget: Level of neighbor cell > 3 dB better +Power Budget Interval: Every 6 SACCH frames (6 seconds ?!?) +Distance Handover: Disabled +Evaluation rule 1: RXLEV of the candidate cell a tleast -104 dBm +Evaluation rule 2: Level of candidate cell > 3dB better own cell +Timer Successful HO: 5 SACCH frames +Timer Unsuccessful HO: 1 SACCH frame + +In a non-frequency hopping case, RXQUAL threshold can be decreased to +RXLEV >= 4 + +When frequency hopping is enabled, the following additional parameters +should be introduced: + +* No intra-cell handover +* Use a HO Margin of 2dB + +=== Handover Channel Reservation === + +In loaded network, each cell should reserve some channels for handovers, +rather than using all of them for new call establishment. This reduces the +need to drop calls due to failing handovers, at the expense of failing new call +attempts. + +=== Dynamic HO Margin === + +The handover margin (hysteresis) should depend on the RXQUAL. Optimal results +were achieved with the following settings: +* RXQUAL <= 4: 9 dB +* RXQUAL == 5: 6 dB +* RXQUAL >= 6: 1 dB + + + +== Actual Handover on a protocol level == + +After the BSC has decided a handover shall be done, it has to + +# allocate a channel at the new BTS +# allocate a handover reference +# activate the channel on the BTS side using RSL CHANNEL ACTIVATION, + indicating the HO reference +# BTS responds with CHAN ACT ACK, including GSM frame number +# BSC sends 04.08 HO CMD to MS using old BTS + diff --git a/doc/ipa-sccp.txt b/doc/ipa-sccp.txt new file mode 100644 index 000000000..5d6719e98 --- /dev/null +++ b/doc/ipa-sccp.txt @@ -0,0 +1,94 @@ + +IPA SCCP message flow in the BSC + +February, 2013 Holger Hans Peter Freyther + +CONTENTS + +1. SCCP inside the IPA header +2. Supported SCCP message types +3. Receiving SCCP messages +4. Sending SCCP messages + + +1. SCCP inside the IPA header + +Many Soft-MSCs implement something that is called SCCP/lite. This means +that SCCP messages are transported inside a small multiplexing protocol +over TCP/IP. This is an alternative to a full SIGTRAN implementation. + +The multiplexing protocol is the same as used with the sysmoBTS and the +ip.access nanoBTS. It is a three byte header with two bytes for the length +in network byte order and one byte for the type. The type to be used for +SCCP is 0xFD. + + struct ipa_header { + uint16_t length_in_network_order; + uint8_t type; + } __attribute__((packed)); + + + +2. Supported SCCP message types + +To implement GSM 08.08 only a subset of SCCP messages need to be implemented. +For transporting paging and reset messages SCCP UDT messages are used. For +the connections with a Mobile Station (MS) a SCCP connection is opened. This +means that the SCCP CR, SCCP CC, SCCP CREF, SCCP RLC, SCCP RLSD, SCCP DT1 +and SCCP IT messages are supported. + + +3. Receiving SCCP UDT messages + +This is an illustration of the flow of messages. The IPA multiplexing protocol +is used for various protocols. This means there is a central place where the +multiplexing stream terminates. The stream is terminated in the osmo_bsc_msc.c +file and the ipaccess_a_fd_cb method. For SCCP messages the SCCP dispatching +sccp_system_incoming method is called. This function is implemented in the +libosmo-sccp library. + +To receive UDT messages osmo_bsc_sccp.c:osmo_bsc_sccp_init is using the +sccp_set_read function to register a callback for UDT messages. The callback +is msc_sccp_read and it is calling bsc_handle_udt that is implemented in the +osmo_bsc_bssap.c. This function will handle the GSM 08.08 BSSAP messages. +Currently only the reset acknowledge and the paging messages are handled. + +The BSC currently does not accept incoming SCCP messages and is only opening +SCCP connections to the MSC. When opening a connection the callbacks for state +changes (connection confirmed, released, release complete) are set and a routine +for handling incoming data. This registration is done in the osmo_bsc_sccp.c +file and the bsc_create_new_connection method. The name of the callback is +msc_outgoing_sccp_data and this will call bsc_handle_dt1 that is implemented +in the osmo_bsc_bssap.c file. This will forward the messages to the right +Mobile Station (MS). + + +4. Sending SCCP messages + +There are three parts to sending that will be explained below. The first part +is to send an entire SCCP frame (which includes the GSM 08.08 data) to the +MSC. This is done by first registering the low level sending. sccp_system_init +is called with the function that is responsible for sending a message. The +msc_sccp_write_ipa will call the msc_queue_write function with the data and +the right MSC connection. Below the msc_queue_write the IPA header will be +prepended to the msg and then send to the MSC. + +The BSC supports multiple different A-link connections, the decision to pick +the right MSC is done in this method. It is either done via the SCCP connection +or the ctx pointer. + +When the BSC is starting a BSS RESET message will be sent to the MSC. The reset +is created in osmo_bsc_msc.c:initialize_if_needed and sccp_write is called with +the GSM 08.08 data and the connection to use. The libosmo-sccp library will +embed it into a SCCP UDT message and call the msc_sccp_write_ipa method. + +When a new SCCP connection is to be created the bsc_create_new_connection +in the osmo_bsc_sccp.c file. The sccp_connection_socket method will create +the context for a SCCP connection. The state and data callback will be used +to be notified about data and changes. Once the connection is configured the +bsc_open_connection will be called that will ask the libosmo-sccp library to +create a SCCP CR message using the sccp_connection_connect method. For active +connections the sccp_connection_write method will be called. + + + diff --git a/doc/oml-interface.txt b/doc/oml-interface.txt new file mode 100644 index 000000000..02bead77a --- /dev/null +++ b/doc/oml-interface.txt @@ -0,0 +1,22 @@ +oml interface design notes + +problems: + +* there is no way how to tag a command sent to the BTS, with the response + having the same tag to identify the originator of the command +* therefore, we can have e.g. both the BSC and the OML interface send a + SET ATTRIBUTE message, where the responses would end up at the wrong + query. +* The BTS has 10s to ACK/NACK a command. We do not run any timers. + +the only possible solutions i can imagine: +* have some kind of exclusive locking, where the OML interface gets blocked + from the BSC and is exclusively assigned to the OML console until all commands + of the OML console have terminated. This can either be done explicitly + dynamically or on demand + +* use the OML interface synchronously, i.e. always wait for the response from + the BTS before + +* unilateral / unsolicited messages need to be broadcasted to both the BSC and + the OML console diff --git a/doc/paging.txt b/doc/paging.txt new file mode 100644 index 000000000..c597c22b4 --- /dev/null +++ b/doc/paging.txt @@ -0,0 +1,48 @@ + +GSM Paging implementation in OpenBSC + +== Code structure == + +The code is implemented in the libbsc/paging.c file. The external +interface is documented/specified in the include/openbsc/paging.h +header file. The code is used by the NITB and BSC application. + + +== Implementation == + +Paging can be initiated in two ways. The standard way is to page by +LAC. Each BTS has its own list/queue of outstanding paging operation. +When a subscriber is paged one "struct paging_request" per BTS will +be allocated and added to the tail of the list. The BTS is supposed +to be configured to not repeat the paging. + +A paging_request will remain in the queue until a paging response or at +the expiry of the T3113. Every 500 milliseconds a RSL paging command is +send to the BTS. The 500 milliseconds is a throttling to not crash the +ip.access nanoBTS. Once one paging_request has been handled it will be +put at the end of the queue/list and the available slots for the BTS +will be decreased. + +The available slots will be updated based on the paging load information +element of the CCCH Load indication. If no paging slots are considered +to be available and no load indication is sent a timer is started. The +current timeout is 500 milliseconds and at the expiry of the timer the +available slots will be set to 20. + +OpenBSC has the " paging free <-1-1024>" configuration option. In case +there are less free channels than required no paging request will be +sent to the BTS. Instead it will be attempted to send the paging request +at the next timeout (500 milliseconds). + +== Limitation == + +The paging throughput could be higher but this has lead to crashes on the +ip.access nanoBTS in the past. + +== Configuration == + +=== ip.access nanoBTS === + +The current CCCH Load indication threshold is 10% and the period is 1 second. +The code can be found inside the src/libbsc/bts_ipaccess_nanobts.c inside the +nanobts_attr_bts array. diff --git a/git-version-gen b/git-version-gen new file mode 100755 index 000000000..f2ad4a762 --- /dev/null +++ b/git-version-gen @@ -0,0 +1,151 @@ +#!/bin/sh +# Print a version string. +scriptversion=2010-01-28.01 + +# Copyright (C) 2007-2010 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# It is probably wise to add these two files to .gitignore, so that you +# don't accidentally commit either generated file. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .tarball-version will +# exist in distribution tarballs. +# +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + +case $# in + 1) ;; + *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; +esac + +tarball_version_file=$1 +nl=' +' + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || exit 1 + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test -z "$v" \ + && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 +fi + +if test -n "$v" +then + : # use $v +elif test -d ./../.git \ + && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && case $v in + [0-9]*) ;; + v[0-9]*) ;; + *) (exit 1) ;; + esac +then + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb + case $v in + *-*-*) : git describe is okay three part flavor ;; + *-*) + : git describe is older two part flavor + # Recreate the number of commits and rewrite such that the + # result is the same as if we were using the newer version + # of git describe. + vtag=`echo "$v" | sed 's/-.*//'` + numcommits=`git rev-list "$vtag"..HEAD | wc -l` + v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; + ;; + esac + + # Change the first '-' to a '.', so version-comparing tools work properly. + # Remove the "g" in git describe's output string, to save a byte. + v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; +else + v=UNKNOWN +fi + +v=`echo "$v" |sed 's/^v//'` + +# Don't declare a version "dirty" merely because a time stamp has changed. +git status > /dev/null 2>&1 + +dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= +case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; +esac + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d '\012' + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 000000000..4596b6e3c --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = openbsc + +noinst_HEADERS = mISDNif.h compat_af_isdn.h diff --git a/include/compat_af_isdn.h b/include/compat_af_isdn.h new file mode 100644 index 000000000..56cbfb3f2 --- /dev/null +++ b/include/compat_af_isdn.h @@ -0,0 +1,39 @@ +#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/include/mISDNif.h b/include/mISDNif.h new file mode 100644 index 000000000..8e065d24b --- /dev/null +++ b/include/mISDNif.h @@ -0,0 +1,387 @@ +/* + * + * 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/include/openbsc/Makefile.am b/include/openbsc/Makefile.am new file mode 100644 index 000000000..93b30bb0c --- /dev/null +++ b/include/openbsc/Makefile.am @@ -0,0 +1,19 @@ +noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ + gsm_subscriber.h gsm_04_11.h debug.h signal.h \ + misdn.h chan_alloc.h paging.h \ + trau_mux.h rs232.h openbscdefines.h rtp_proxy.h \ + bsc_rll.h mncc.h transaction.h ussd.h gsm_04_80.h \ + silent_call.h mgcp.h meas_rep.h rest_octets.h \ + system_information.h handover.h mgcp_internal.h \ + vty.h socket.h e1_config.h trau_upqueue.h token_auth.h \ + handover_decision.h rrlp.h control_if.h \ + crc24.h gprs_llc.h gprs_gmm.h \ + gb_proxy.h gprs_sgsn.h gsm_04_08_gprs.h sgsn.h \ + auth.h osmo_msc.h bsc_msc.h bsc_nat.h \ + osmo_bsc_rf.h osmo_bsc.h network_listen.h bsc_nat_sccp.h \ + osmo_msc_data.h osmo_bsc_grace.h sms_queue.h abis_om2000.h \ + bss.h gsm_data_shared.h control_cmd.h ipaccess.h mncc_int.h \ + arfcn_range_encode.h + +openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h +openbscdir = $(includedir)/openbsc diff --git a/include/openbsc/abis_nm.h b/include/openbsc/abis_nm.h new file mode 100644 index 000000000..4d6f866ee --- /dev/null +++ b/include/openbsc/abis_nm.h @@ -0,0 +1,186 @@ +/* GSM Network Management messages on the A-bis interface + * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ + +/* (C) 2008-2009 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#ifndef _NM_H +#define _NM_H + +#include +#include +#include + +struct cell_global_id { + uint16_t mcc; + uint16_t mnc; + uint16_t lac; + uint16_t ci; +}; + +/* The BCCH info from an ip.access test, in host byte order + * and already parsed... */ +struct ipac_bcch_info { + struct llist_head list; + + uint16_t info_type; + uint8_t freq_qual; + uint16_t arfcn; + uint8_t rx_lev; + uint8_t rx_qual; + int16_t freq_err; + uint16_t frame_offset; + uint32_t frame_nr_offset; + uint8_t bsic; + struct cell_global_id cgi; + uint8_t ba_list_si2[16]; + uint8_t ba_list_si2bis[16]; + uint8_t ba_list_si2ter[16]; + uint8_t ca_list_si1[16]; +}; + +/* PUBLIC */ + +struct msgb; + +struct abis_nm_cfg { + /* callback for unidirectional reports */ + int (*report_cb)(struct msgb *, + struct abis_om_fom_hdr *); + /* callback for software activate requests from BTS */ + int (*sw_act_req)(struct msgb *); +}; + +struct abis_nm_sw_descr { + /* where does it start? how long is it? */ + const uint8_t *start; + size_t len; + + /* the parsed data */ + const uint8_t *file_id; + uint16_t file_id_len; + const uint8_t *file_ver; + uint16_t file_ver_len; +}; + +extern int abis_nm_rcvmsg(struct msgb *msg); + +int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const uint8_t *buf, int len); +int abis_nm_rx(struct msgb *msg); +int abis_nm_opstart(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2); +int abis_nm_chg_adm_state(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, + uint8_t i1, uint8_t i2, enum abis_nm_adm_state adm_state); +int abis_nm_establish_tei(struct gsm_bts *bts, uint8_t trx_nr, + uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, + uint8_t tei); +int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, + uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot); +int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, + uint8_t e1_port, uint8_t e1_timeslot, + uint8_t e1_subslot); +int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, + uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, + uint8_t *attr, uint8_t attr_len); +int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len); +int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len); +int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb); +int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1, + uint8_t i2, uint8_t i3, int nack, uint8_t *attr, int att_len); +int abis_nm_raw_msg(struct gsm_bts *bts, int len, uint8_t *msg); +int abis_nm_event_reports(struct gsm_bts *bts, int on); +int abis_nm_reset_resource(struct gsm_bts *bts); +int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, + uint8_t win_size, int forced, + gsm_cbfn *cbfn, void *cb_data); +int abis_nm_software_load_status(struct gsm_bts *bts); +int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, + gsm_cbfn *cbfn, void *cb_data); + +int abis_nm_conn_mdrop_link(struct gsm_bts *bts, uint8_t e1_port0, uint8_t ts0, + uint8_t e1_port1, uint8_t ts1); + +int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class, + uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, + uint8_t test_nr, uint8_t auton_report, struct msgb *msg); + +/* Siemens / BS-11 specific */ +int abis_nm_bs11_reset_resource(struct gsm_bts *bts); +int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin); +int abis_nm_bs11_create_object(struct gsm_bts *bts, enum abis_bs11_objtype type, + uint8_t idx, uint8_t attr_len, const uint8_t *attr); +int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, uint8_t idx); +int abis_nm_bs11_create_bport(struct gsm_bts *bts, uint8_t idx); +int abis_nm_bs11_delete_object(struct gsm_bts *bts, + enum abis_bs11_objtype type, uint8_t idx); +int abis_nm_bs11_delete_bport(struct gsm_bts *bts, uint8_t idx); +int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, uint8_t e1_port, + uint8_t e1_timeslot, uint8_t e1_subslot, uint8_t tei); +int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts); +int abis_nm_bs11_get_serno(struct gsm_bts *bts); +int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, uint8_t level); +int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx); +int abis_nm_bs11_logon(struct gsm_bts *bts, uint8_t level, const char *name, int on); +int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on); +int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on); +int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password); +int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked); +int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts); +int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value); +int abis_nm_bs11_get_cclk(struct gsm_bts *bts); +int abis_nm_bs11_get_state(struct gsm_bts *bts); +int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, + uint8_t win_size, int forced, gsm_cbfn *cbfn); +int abis_nm_bs11_set_ext_time(struct gsm_bts *bts); +int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, uint8_t bport); +int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, uint8_t bport, enum abis_bs11_line_cfg line_cfg); +int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect); +int abis_nm_bs11_restart(struct gsm_bts *bts); + +/* ip.access nanoBTS specific commands */ +int abis_nm_ipaccess_msg(struct gsm_bts *bts, uint8_t msg_type, + uint8_t obj_class, uint8_t bts_nr, + uint8_t trx_nr, uint8_t ts_nr, + uint8_t *attr, int attr_len); +int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, uint8_t *attr, + int attr_len); +int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx); +int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class, + uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, + uint8_t *attr, uint8_t attr_len); +int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, + uint32_t ip, uint16_t port, uint8_t stream); +void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts); +int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf); +const char *ipacc_testres_name(uint8_t res); + +/* Functions calling into other code parts */ +int nm_is_running(struct gsm_nm_state *s); + +int abis_nm_vty_init(void); + +void abis_nm_clear_queue(struct gsm_bts *bts); + +int _abis_nm_sendmsg(struct msgb *msg); + +void abis_nm_queue_send_next(struct gsm_bts *bts); /* for bs11_config. */ + +int abis_nm_parse_sw_config(const uint8_t *data, const size_t len, + struct abis_nm_sw_descr *res, const int res_len); +int abis_nm_select_newest_sw(const struct abis_nm_sw_descr *sw, const size_t len); + +#endif /* _NM_H */ diff --git a/include/openbsc/abis_om2000.h b/include/openbsc/abis_om2000.h new file mode 100644 index 000000000..2ff72705b --- /dev/null +++ b/include/openbsc/abis_om2000.h @@ -0,0 +1,98 @@ +#ifndef OPENBSC_ABIS_OM2K_H +#define OPENBSC_ABIS_OM2K_H +/* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface + * implemented based on protocol trace analysis, no formal documentation */ + +/* (C) 2010-2011 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +enum abis_om2k_mo_cls { + OM2K_MO_CLS_TRXC = 0x01, + OM2K_MO_CLS_TS = 0x03, + OM2K_MO_CLS_TF = 0x04, + OM2K_MO_CLS_IS = 0x05, + OM2K_MO_CLS_CON = 0x06, + OM2K_MO_CLS_DP = 0x07, + OM2K_MO_CLS_CF = 0x0a, + OM2K_MO_CLS_TX = 0x0b, + OM2K_MO_CLS_RX = 0x0c, +}; + +enum om2k_mo_state { + OM2K_MO_S_RESET = 0, + OM2K_MO_S_STARTED, + OM2K_MO_S_ENABLED, + OM2K_MO_S_DISABLED, +}; + +struct abis_om2k_mo { + uint8_t class; + uint8_t bts; + uint8_t assoc_so; + uint8_t inst; +} __attribute__ ((packed)); + +/* on-wire format for IS conn group */ +struct om2k_is_conn_grp { + uint16_t icp1; + uint16_t icp2; + uint8_t cont_idx; +} __attribute__ ((packed)); + +/* internal data formant for IS conn group */ +struct is_conn_group { + struct llist_head list; + uint16_t icp1; + uint16_t icp2; + uint8_t ci; +}; + +extern const struct abis_om2k_mo om2k_mo_cf; +extern const struct abis_om2k_mo om2k_mo_is; +extern const struct abis_om2k_mo om2k_mo_con; +extern const struct abis_om2k_mo om2k_mo_tf; + +extern const struct value_string om2k_mo_class_short_vals[]; + +int abis_om2k_rcvmsg(struct msgb *msg); + +extern const struct abis_om2k_mo om2k_mo_cf; + +int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); +int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, + uint8_t operational); +int abis_om2k_tx_is_conf_req(struct gsm_bts *bts); +int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts); +int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx); +int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx); +int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts); + +int abis_om2k_vty_init(void); + +struct vty; +void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts); + +#endif /* OPENBCS_ABIS_OM2K_H */ diff --git a/include/openbsc/abis_rsl.h b/include/openbsc/abis_rsl.h new file mode 100644 index 000000000..06f0a721c --- /dev/null +++ b/include/openbsc/abis_rsl.h @@ -0,0 +1,107 @@ +/* GSM Radio Signalling Link messages on the A-bis interface + * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ + +/* (C) 2008 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#ifndef _RSL_H +#define _RSL_H + +#include + +#include + +struct gsm_bts; +struct gsm_lchan; +struct gsm_subscriber; +struct gsm_bts_trx_ts; + + +int rsl_bcch_info(struct gsm_bts_trx *trx, uint8_t type, + const uint8_t *data, int len); +int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, + const uint8_t *data, int len); +int rsl_chan_activate(struct gsm_bts_trx *trx, uint8_t chan_nr, + uint8_t act_type, + struct rsl_ie_chan_mode *chan_mode, + struct rsl_ie_chan_ident *chan_ident, + uint8_t bs_power, uint8_t ms_power, + uint8_t ta); +int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type, + uint8_t ta, uint8_t ho_ref); +int rsl_chan_mode_modify_req(struct gsm_lchan *ts); +int rsl_encryption_cmd(struct msgb *msg); +int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len, + uint8_t *ms_ident, uint8_t chan_needed); +int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val); + +int rsl_data_request(struct msgb *msg, uint8_t link_id); +int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id); +int rsl_relase_request(struct gsm_lchan *lchan, uint8_t link_id); + +/* Siemens vendor-specific RSL extensions */ +int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci); + +/* ip.access specfic RSL extensions */ +int rsl_ipacc_crcx(struct gsm_lchan *lchan); +int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip, + uint16_t port, uint8_t rtp_payload2); +int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan); +int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act); + +int abis_rsl_rcvmsg(struct msgb *msg); + +uint64_t str_to_imsi(const char *imsi_str); +int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id, + enum rsl_rel_mode release_mode); + +int rsl_lchan_set_state(struct gsm_lchan *lchan, int); + +/* 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, + uint8_t cb_command, const uint8_t *data, int len); + +/* some Nokia specific stuff */ +int rsl_nokia_si_begin(struct gsm_bts_trx *trx); +int rsl_nokia_si_end(struct gsm_bts_trx *trx); + +/* required for Nokia BTS power control */ +int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction); + + +int rsl_release_sapis_from(struct gsm_lchan *lchan, int start, + enum rsl_rel_mode release_mode); +int rsl_start_t3109(struct gsm_lchan *lchan); + +int rsl_direct_rf_release(struct gsm_lchan *lchan); + +#endif /* RSL_MT_H */ + diff --git a/include/openbsc/arfcn_range_encode.h b/include/openbsc/arfcn_range_encode.h new file mode 100644 index 000000000..7a6fff000 --- /dev/null +++ b/include/openbsc/arfcn_range_encode.h @@ -0,0 +1,26 @@ +#ifndef ARFCN_RANGE_ENCODE_H +#define ARFCN_RANGE_ENCODE_H + +#include + +enum { + ARFCN_RANGE_INVALID = -1, + ARFCN_RANGE_128 = 127, + ARFCN_RANGE_256 = 255, + ARFCN_RANGE_512 = 511, + ARFCN_RANGE_1024 = 1023, +}; + +#define RANGE_ENC_MAX_ARFCNS 29 + +int range_enc_determine_range(const int *arfcns, int size, int *f0_out); +int range_enc_arfcns(const int rng, const int *arfcns, int sze, int *out, int idx); +int range_enc_find_index(const int rng, const int *arfcns, int size); +int range_enc_filter_arfcns(const int rng, 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/include/openbsc/auth.h b/include/openbsc/auth.h new file mode 100644 index 000000000..2364fb3d2 --- /dev/null +++ b/include/openbsc/auth.h @@ -0,0 +1,17 @@ +#ifndef _AUTH_H +#define _AUTH_H + +struct gsm_auth_tuple; +struct gsm_subscriber; + +enum auth_action { + AUTH_NOT_AVAIL = 0, /* No auth tuple available */ + AUTH_DO_AUTH_THAN_CIPH = 1, /* Firsth authenticate, then cipher */ + AUTH_DO_CIPH = 2, /* Only ciphering */ + AUTH_DO_AUTH = 3, /* Only authentication, no ciphering */ +}; + +int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr, int key_seq); + +#endif /* _AUTH_H */ diff --git a/include/openbsc/bsc_api.h b/include/openbsc/bsc_api.h new file mode 100644 index 000000000..f52984f39 --- /dev/null +++ b/include/openbsc/bsc_api.h @@ -0,0 +1,55 @@ +/* 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 gsm48_multi_rate_conf *conf); +}; + +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/include/openbsc/bsc_msc.h b/include/openbsc/bsc_msc.h new file mode 100644 index 000000000..9543002a7 --- /dev/null +++ b/include/openbsc/bsc_msc.h @@ -0,0 +1,59 @@ +/* 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 + +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; + + 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 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(const char *token); + +#endif diff --git a/include/openbsc/bsc_nat.h b/include/openbsc/bsc_nat.h new file mode 100644 index 000000000..195031822 --- /dev/null +++ b/include/openbsc/bsc_nat.h @@ -0,0 +1,480 @@ +/* + * (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 +#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; + +enum { + NAT_CON_TYPE_NONE, + NAT_CON_TYPE_LU, + NAT_CON_TYPE_CM_SERV_REQ, + NAT_CON_TYPE_PAG_RESP, + NAT_CON_TYPE_SSA, + NAT_CON_TYPE_LOCAL_REJECT, + NAT_CON_TYPE_OTHER, +}; + +/* + * 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; + + /* the fd we use to communicate */ + struct osmo_wqueue write_queue; + + /* 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; + + char *token; + int nr; + + char *description; + + /* imsi white and blacklist */ + char *acc_lst_name; + + int forbid_paging; + int paging_group; + + /* audio handling */ + int max_endpoints; + + /* backpointer */ + struct bsc_nat *nat; + + struct bsc_config_stats stats; + + struct llist_head lac_list; +}; + +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; +}; + +enum bsc_nat_acc_ctr { + ACC_LIST_BSC_FILTER, + ACC_LIST_NAT_FILTER, +}; + +struct bsc_nat_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_nat_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; +}; + +/** + * 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; + + /* msc things */ + struct llist_head dests; + struct bsc_msc_dest *main_dest; + struct bsc_msc_connection *msc_con; + char *token; + + /* timeouts */ + int auth_timeout; + int ping_timeout; + int pong_timeout; + + struct bsc_endpoint *bsc_endpoints; + + /* filter */ + char *acc_lst_name; + + /* Barring of subscribers with a rb tree */ + struct rb_root imsi_black_list; + char *imsi_black_list_fn; + + /* number rewriting */ + char *num_rewr_name; + struct llist_head num_rewr; + + char *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; + + /* 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 osmo_timer_list auth_timeout; +}; + +struct bsc_nat_reject_cause { + int lu_reject_cause; + int cm_reject_cause; +}; + +/* create and init the structures */ +struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token); +struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num); +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); + +/** + * Content filtering. + */ +int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, + struct bsc_nat_parsed *, int *con_type, char **imsi, + struct bsc_nat_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_nat_reject_cause *cause); + +/** + * 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); +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); + +/* IMSI allow/deny handling */ +struct bsc_nat_acc_lst *bsc_nat_acc_lst_find(struct bsc_nat *nat, const char *name); +struct bsc_nat_acc_lst *bsc_nat_acc_lst_get(struct bsc_nat *nat, const char *name); +void bsc_nat_acc_lst_delete(struct bsc_nat_acc_lst *lst); + +struct bsc_nat_acc_lst_entry *bsc_nat_acc_lst_entry_create(struct bsc_nat_acc_lst *); +int bsc_nat_lst_check_allow(struct bsc_nat_acc_lst *lst, const char *imsi); + +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_check_ussd(struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, struct msgb *msg); +int bsc_close_ussd_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; +}; + +void bsc_nat_num_rewr_entry_adapt(void *ctx, struct llist_head *head, const struct osmo_config_list *); + +struct bsc_nat_barr_entry { + struct rb_node node; + + char *imsi; + int cm_reject_cause; + int lu_reject_cause; +}; + +int bsc_nat_barr_adapt(void *ctx, struct rb_root *rbtree, const struct osmo_config_list *); +int bsc_nat_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu); + +void bsc_nat_send_mgcp_to_msc(struct bsc_nat *bsc_nat, struct msgb *msg); +void bsc_nat_handle_mgcp(struct bsc_nat *bsc, struct msgb *msg); + +struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, int port); +void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending); +int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg); + +int bsc_nat_extract_lac(struct bsc_connection *bsc, struct nat_sccp_connection *con, + struct bsc_nat_parsed *parsed, struct msgb *msg); + +#endif diff --git a/include/openbsc/bsc_nat_callstats.h b/include/openbsc/bsc_nat_callstats.h new file mode 100644 index 000000000..64f9bfc0a --- /dev/null +++ b/include/openbsc/bsc_nat_callstats.h @@ -0,0 +1,55 @@ +/* + * (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/include/openbsc/bsc_nat_sccp.h b/include/openbsc/bsc_nat_sccp.h new file mode 100644 index 000000000..34aa6320a --- /dev/null +++ b/include/openbsc/bsc_nat_sccp.h @@ -0,0 +1,101 @@ +/* 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 + +/* + * 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; + + /* 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_type; + int con_local; + int authorized; + int imsi_checked; + char *imsi; + + 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/include/openbsc/bsc_rll.h b/include/openbsc/bsc_rll.h new file mode 100644 index 000000000..729ba6037 --- /dev/null +++ b/include/openbsc/bsc_rll.h @@ -0,0 +1,19 @@ +#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/include/openbsc/bss.h b/include/openbsc/bss.h new file mode 100644 index 000000000..49df547a1 --- /dev/null +++ b/include/openbsc/bss.h @@ -0,0 +1,18 @@ +#ifndef _BSS_H_ +#define _BSS_H_ + +struct gsm_network; +struct msgb; + +/* start and stop network */ +extern int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *), const char *cfg_file); +extern int bsc_shutdown_net(struct gsm_network *net); + +/* register all supported BTS */ +extern int bts_init(void); +extern int bts_model_bs11_init(void); +extern int bts_model_rbs2k_init(void); +extern int bts_model_nanobts_init(void); +extern int bts_model_nokia_site_init(void); +extern int bts_model_sysmobts_init(void); +#endif diff --git a/include/openbsc/chan_alloc.h b/include/openbsc/chan_alloc.h new file mode 100644 index 000000000..f2d3c781d --- /dev/null +++ b/include/openbsc/chan_alloc.h @@ -0,0 +1,65 @@ +/* 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; + +/* Special allocator for C0 of BTS */ +struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts, + enum gsm_phys_chan_config pchan); + +/* Regular physical channel allocator */ +struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts, + enum gsm_phys_chan_config pchan); + +/* Regular physical channel (TS) */ +void ts_free(struct gsm_bts_trx_ts *ts); + +/* 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_UNKNOWN]; +}; + +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/include/openbsc/control_cmd.h b/include/openbsc/control_cmd.h new file mode 100644 index 000000000..a0ea055ce --- /dev/null +++ b/include/openbsc/control_cmd.h @@ -0,0 +1,163 @@ +#ifndef _CONTROL_CMD_H +#define _CONTROL_CMD_H + +#include +#include +#include + +#include + +#include + +#define CTRL_CMD_ERROR -1 +#define CTRL_CMD_HANDLED 0 +#define CTRL_CMD_REPLY 1 + +struct ctrl_handle; + +enum ctrl_node_type { + CTRL_NODE_ROOT, /* Root elements */ + CTRL_NODE_BTS, /* BTS specific (net.btsN.) */ + CTRL_NODE_TRX, /* TRX specific (net.btsN.trxM.) */ + CTRL_NODE_TS, /* TS specific (net.btsN.trxM.tsI.) */ + _LAST_CTRL_NODE +}; + +enum ctrl_type { + CTRL_TYPE_UNKNOWN, + CTRL_TYPE_GET, + CTRL_TYPE_SET, + CTRL_TYPE_GET_REPLY, + CTRL_TYPE_SET_REPLY, + CTRL_TYPE_TRAP, + CTRL_TYPE_ERROR +}; + +struct ctrl_connection { + struct llist_head list_entry; + + /* The queue for sending data back */ + struct osmo_wqueue write_queue; + + /* Callback if the connection was closed */ + void (*closed_cb)(struct ctrl_connection *conn); + + /* Pending commands for this connection */ + struct llist_head cmds; +}; + +struct ctrl_cmd { + struct ctrl_connection *ccon; + enum ctrl_type type; + char *id; + void *node; + char *variable; + char *value; + char *reply; +}; + +struct ctrl_cmd_struct { + int nr_commands; + char **command; +}; + +struct ctrl_cmd_element { + const char *name; + const char *param; + struct ctrl_cmd_struct strcmd; + int (*set)(struct ctrl_cmd *cmd, void *data); + int (*get)(struct ctrl_cmd *cmd, void *data); + int (*verify)(struct ctrl_cmd *cmd, const char *value, void *data); +}; + +struct ctrl_cmd_map { + char *cmd; + enum ctrl_type type; +}; + +int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data); +int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd); +int ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data); +int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd); +int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd); +struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg); +struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd); +struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd); +struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type); +struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd); + +#define CTRL_CMD_DEFINE_RANGE(cmdname, cmdstr, dtype, element, min, max) \ +static int get_##cmdname(struct ctrl_cmd *cmd, void *_data) \ +{ \ + dtype *node = cmd->node; \ + cmd->reply = talloc_asprintf(cmd, "%i", node->element); \ + if (!cmd->reply) { \ + cmd->reply = "OOM"; \ + return CTRL_CMD_ERROR; \ + } \ + return CTRL_CMD_REPLY; \ +} \ +static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \ +{ \ + dtype *node = cmd->node; \ + int tmp = atoi(cmd->value); \ + node->element = tmp; \ + return get_##cmdname(cmd, _data); \ +} \ +static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *_data) \ +{ \ + int tmp = atoi(value); \ + if ((tmp >= min)&&(tmp <= max)) { \ + return 0; \ + } \ + cmd->reply = "Input not within the range"; \ + return -1; \ +} \ +static struct ctrl_cmd_element cmd_##cmdname = { \ + .name = cmdstr, \ + .param = NULL, \ + .get = &get_##cmdname, \ + .set = &set_##cmdname, \ + .verify = &verify_##cmdname, \ +} + +#define CTRL_CMD_DEFINE_STRING(cmdname, cmdstr, dtype, element) \ +static int get_##cmdname(struct ctrl_cmd *cmd, void *_data) \ +{ \ + dtype *data = cmd->node; \ + cmd->reply = talloc_asprintf(cmd, "%s", data->element); \ + if (!cmd->reply) { \ + cmd->reply = "OOM"; \ + return CTRL_CMD_ERROR; \ + } \ + return CTRL_CMD_REPLY; \ +} \ +static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \ +{ \ + dtype *data = cmd->node; \ + bsc_replace_string(cmd->node, &data->element, cmd->value); \ + return get_##cmdname(cmd, _data); \ +} \ +static struct ctrl_cmd_element cmd_##cmdname = { \ + .name = cmdstr, \ + .param = NULL, \ + .get = &get_##cmdname, \ + .set = &set_##cmdname, \ + .verify = NULL, \ +} + +#define CTRL_CMD_DEFINE(cmdname, cmdstr) \ +static int get_##cmdname(struct ctrl_cmd *cmd, void *data); \ +static int set_##cmdname(struct ctrl_cmd *cmd, void *data); \ +static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data); \ +static struct ctrl_cmd_element cmd_##cmdname = { \ + .name = cmdstr, \ + .param = NULL, \ + .get = &get_##cmdname, \ + .set = &set_##cmdname, \ + .verify = &verify_##cmdname, \ +} + +struct gsm_network; + +#endif /* _CONTROL_CMD_H */ diff --git a/include/openbsc/control_if.h b/include/openbsc/control_if.h new file mode 100644 index 000000000..6afc86d76 --- /dev/null +++ b/include/openbsc/control_if.h @@ -0,0 +1,21 @@ +#ifndef _CONTROL_IF_H +#define _CONTROL_IF_H + +#include +#include +#include + +struct ctrl_handle { + struct osmo_fd listen_fd; + struct gsm_network *gsmnet; + + /* List of control connections */ + struct llist_head ccon_list; +}; + +int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd); +int ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data); +struct ctrl_handle *controlif_setup(struct gsm_network *gsmnet, uint16_t port); + +#endif /* _CONTROL_IF_H */ + diff --git a/include/openbsc/crc24.h b/include/openbsc/crc24.h new file mode 100644 index 000000000..756638c03 --- /dev/null +++ b/include/openbsc/crc24.h @@ -0,0 +1,10 @@ +#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/include/openbsc/db.h b/include/openbsc/db.h new file mode 100644 index 000000000..25c2aea70 --- /dev/null +++ b/include/openbsc/db.h @@ -0,0 +1,82 @@ +/* (C) 2008 by Jan Luebbe + * (C) 2009 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#ifndef _DB_H +#define _DB_H + +#include "gsm_subscriber.h" + +struct gsm_equipment; +struct gsm_network; +struct gsm_auth_info; +struct gsm_auth_tuple; +struct gsm_sms; +struct gsm_subscriber; + +/* one time initialisation */ +int db_init(const char *name); +int db_prepare(void); +int db_fini(void); + +/* subscriber management */ +struct gsm_subscriber *db_create_subscriber(struct gsm_network *net, + char *imsi); +struct gsm_subscriber *db_get_subscriber(struct gsm_network *net, + enum gsm_subscriber_field field, + const char *subscr); +int db_sync_subscriber(struct gsm_subscriber *subscriber); +int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id)); +int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber); +int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber); +int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t* token); +int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char *imei); +int db_sync_equipment(struct gsm_equipment *equip); +int db_subscriber_update(struct gsm_subscriber *subscriber); + +/* 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_sent(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/include/openbsc/debug.h b/include/openbsc/debug.h new file mode 100644 index 000000000..d851181c7 --- /dev/null +++ b/include/openbsc/debug.h @@ -0,0 +1,65 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +#include +#include + +#define DEBUG +#include + +/* Debug Areas of the code */ +enum { + DRLL, + DCC, + DMM, + DRR, + DRSL, + DNM, + DMNCC, + DPAG, + DMEAS, + DSCCP, + DMSC, + DMGCP, + DHO, + DDB, + DREF, + DGPRS, + DNS, + DBSSGP, + DLLC, + DSNDCP, + DNAT, + DCTRL, + DSMPP, + Debug_LastEntry, +}; + +/* context */ +#define BSC_CTX_LCHAN 0 +#define BSC_CTX_SUBSCR 1 +#define BSC_CTX_BTS 2 +#define BSC_CTX_SCCP 3 + +/* target */ + +enum { + //DEBUG_FILTER_ALL = 1 << 0, + LOG_FILTER_IMSI = 1 << 1, + LOG_FILTER_NSVC = 1 << 2, + LOG_FILTER_BVC = 1 << 3, +}; + +/* we don't need a header dependency for this... */ +struct gprs_nsvc; +struct bssgp_bvc_ctx; + +void log_set_imsi_filter(struct log_target *target, const char *imsi); +void log_set_nsvc_filter(struct log_target *target, + struct gprs_nsvc *nsvc); +void log_set_bvc_filter(struct log_target *target, + struct bssgp_bvc_ctx *bctx); + +extern const struct log_info log_info; + +#endif /* _DEBUG_H */ diff --git a/include/openbsc/e1_config.h b/include/openbsc/e1_config.h new file mode 100644 index 000000000..538c0b09d --- /dev/null +++ b/include/openbsc/e1_config.h @@ -0,0 +1,11 @@ +#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/include/openbsc/gb_proxy.h b/include/openbsc/gb_proxy.h new file mode 100644 index 000000000..2c8ddb334 --- /dev/null +++ b/include/openbsc/gb_proxy.h @@ -0,0 +1,38 @@ +#ifndef _GB_PROXY_H +#define _GB_PROXY_H + + +#include + +#include +#include + +struct gbproxy_config { + /* parsed from config file */ + uint16_t nsip_sgsn_nsei; + + /* misc */ + struct gprs_ns_inst *nsi; +}; + +extern struct gbproxy_config gbcfg; +extern struct cmd_element show_gbproxy_cmd; + +/* gb_proxy_vty .c */ + +int gbproxy_vty_init(void); +int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg); + + +/* gb_proxy.c */ + +/* Main input function for Gb proxy */ +int gbprox_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci); + +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); + +#endif diff --git a/include/openbsc/gprs_gmm.h b/include/openbsc/gprs_gmm.h new file mode 100644 index 000000000..f6b3e5e7c --- /dev/null +++ b/include/openbsc/gprs_gmm.h @@ -0,0 +1,19 @@ +#ifndef _GPRS_GMM_H +#define _GPRS_GMM_H + +#include +#include + +int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause); +int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid, + uint8_t cause, uint8_t pco_len, uint8_t *pco_v); +int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp); +int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp); + +int gsm0408_gprs_rcvmsg(struct msgb *msg, struct gprs_llc_llme *llme); + +int 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); + +#endif /* _GPRS_GMM_H */ diff --git a/include/openbsc/gprs_llc.h b/include/openbsc/gprs_llc.h new file mode 100644 index 000000000..4be7b1f18 --- /dev/null +++ b/include/openbsc/gprs_llc.h @@ -0,0 +1,202 @@ +#ifndef _GPRS_LLC_H +#define _GPRS_LLC_H + +#include +#include + +/* Section 4.7 LLC Layer Structure */ +enum gprs_llc_sapi { + GPRS_SAPI_GMM = 1, + GPRS_SAPI_TOM2 = 2, + GPRS_SAPI_SNDCP3 = 3, + GPRS_SAPI_SNDCP5 = 5, + GPRS_SAPI_SMS = 7, + GPRS_SAPI_TOM8 = 8, + GPRS_SAPI_SNDCP9 = 9, + GPRS_SAPI_SNDCP11 = 11, +}; + +/* Section 6.4 Commands and Responses */ +enum gprs_llc_u_cmd { + GPRS_LLC_U_DM_RESP = 0x01, + GPRS_LLC_U_DISC_CMD = 0x04, + GPRS_LLC_U_UA_RESP = 0x06, + GPRS_LLC_U_SABM_CMD = 0x07, + GPRS_LLC_U_FRMR_RESP = 0x08, + GPRS_LLC_U_XID = 0x0b, + GPRS_LLC_U_NULL_CMD = 0x00, +}; + +/* Section 6.4.1.6 / Table 6 */ +enum gprs_llc_xid_type { + GPRS_LLC_XID_T_VERSION = 0, + GPRS_LLC_XID_T_IOV_UI = 1, + GPRS_LLC_XID_T_IOV_I = 2, + GPRS_LLC_XID_T_T200 = 3, + GPRS_LLC_XID_T_N200 = 4, + GPRS_LLC_XID_T_N201_U = 5, + GPRS_LLC_XID_T_N201_I = 6, + GPRS_LLC_XID_T_mD = 7, + GPRS_LLC_XID_T_mU = 8, + GPRS_LLC_XID_T_kD = 9, + GPRS_LLC_XID_T_kU = 10, + GPRS_LLC_XID_T_L3_PAR = 11, + GPRS_LLC_XID_T_RESET = 12, +}; + +/* TS 04.64 Section 7.1.2 Table 7: LLC layer primitives (GMM/SNDCP/SMS/TOM) */ +/* TS 04.65 Section 5.1.2 Table 2: Service primitives used by SNDCP */ +enum gprs_llc_primitive { + /* GMM <-> LLME */ + LLGMM_ASSIGN_REQ, /* GMM tells us new TLLI: TLLI old, TLLI new, Kc, CiphAlg */ + LLGMM_RESET_REQ, /* GMM tells us to perform XID negotiation: TLLI */ + LLGMM_RESET_CNF, /* LLC informs GMM that XID has completed: TLLI */ + LLGMM_SUSPEND_REQ, /* GMM tells us MS has suspended: TLLI, Page */ + LLGMM_RESUME_REQ, /* GMM tells us MS has resumed: TLLI */ + LLGMM_PAGE_IND, /* LLC asks GMM to page MS: TLLI */ + LLGMM_IOV_REQ, /* GMM tells us to perform XID: TLLI */ + LLGMM_STATUS_IND, /* LLC informs GMM about error: TLLI, Cause */ + /* LLE <-> (GMM/SNDCP/SMS/TOM) */ + LL_RESET_IND, /* TLLI */ + LL_ESTABLISH_REQ, /* TLLI, XID Req */ + LL_ESTABLISH_IND, /* TLLI, XID Req, N201-I, N201-U */ + LL_ESTABLISH_RESP, /* TLLI, XID Negotiated */ + LL_ESTABLISH_CONF, /* TLLI, XID Neg, N201-i, N201-U */ + LL_RELEASE_REQ, /* TLLI, Local */ + LL_RELEASE_IND, /* TLLI, Cause */ + LL_RELEASE_CONF, /* TLLI */ + LL_XID_REQ, /* TLLI, XID Requested */ + LL_XID_IND, /* TLLI, XID Req, N201-I, N201-U */ + LL_XID_RESP, /* TLLI, XID Negotiated */ + LL_XID_CONF, /* TLLI, XID Neg, N201-I, N201-U */ + LL_DATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ + LL_DATA_IND, /* TLLI, SN-PDU */ + LL_DATA_CONF, /* TLLI, Ref */ + LL_UNITDATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ + LL_UNITDATA_IND, /* TLLI, SN-PDU */ + LL_STATUS_IND, /* TLLI, Cause */ +}; + +/* Section 4.5.2 Logical Link States + Annex C.2 */ +enum gprs_llc_lle_state { + GPRS_LLES_UNASSIGNED = 1, /* No TLLI yet */ + GPRS_LLES_ASSIGNED_ADM = 2, /* TLLI assigned */ + GPRS_LLES_LOCAL_EST = 3, /* Local Establishment */ + GPRS_LLES_REMOTE_EST = 4, /* Remote Establishment */ + GPRS_LLES_ABM = 5, + GPRS_LLES_LOCAL_REL = 6, /* Local Release */ + GPRS_LLES_TIMER_REC = 7, /* Timer Recovery */ +}; + +enum gprs_llc_llme_state { + GPRS_LLMS_UNASSIGNED = 1, /* No TLLI yet */ + GPRS_LLMS_ASSIGNED = 2, /* TLLI assigned */ +}; + +/* Section 8.9.9 LLC layer parameter default values */ +struct gprs_llc_params { + uint16_t iov_i_exp; + uint16_t t200_201; + uint16_t n200; + uint16_t n201_u; + uint16_t n201_i; + uint16_t mD; + uint16_t mU; + uint16_t kD; + uint16_t kU; +}; + +/* Section 4.7.1: Logical Link Entity: One per DLCI (TLLI + SAPI) */ +struct gprs_llc_lle { + struct llist_head list; + + uint32_t sapi; + + struct gprs_llc_llme *llme; + + enum gprs_llc_lle_state state; + + struct osmo_timer_list t200; + struct osmo_timer_list t201; /* wait for acknowledgement */ + + uint16_t v_sent; + uint16_t v_ack; + uint16_t v_recv; + + uint16_t vu_send; + uint16_t vu_recv; + + /* non-standard LLC state */ + uint16_t vu_recv_last; + uint16_t vu_recv_duplicates; + + /* Overflow Counter for ABM */ + uint32_t oc_i_send; + uint32_t oc_i_recv; + + /* Overflow Counter for unconfirmed transfer */ + uint32_t oc_ui_send; + uint32_t oc_ui_recv; + + unsigned int retrans_ctr; + + struct gprs_llc_params params; +}; + +#define NUM_SAPIS 16 + +struct gprs_llc_llme { + struct llist_head list; + + enum gprs_llc_llme_state state; + + uint32_t tlli; + uint32_t old_tlli; + + /* Crypto parameters */ + enum gprs_ciph_algo algo; + uint8_t kc[8]; + + /* over which BSSGP BTS ctx do we need to transmit */ + uint16_t bvci; + uint16_t nsei; + struct gprs_llc_lle lle[NUM_SAPIS]; +}; + +extern struct llist_head gprs_llc_llmes; + +/* BSSGP-UL-UNITDATA.ind */ +int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv); + +/* LL-UNITDATA.req */ +int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command, + void *mmctx); + +/* Chapter 7.2.1.2 LLGMM-RESET.req */ +int gprs_llgmm_reset(struct gprs_llc_llme *llme); + +/* 04.64 Chapter 7.2.1.1 LLGMM-ASSIGN */ +int gprs_llgmm_assign(struct gprs_llc_llme *llme, + uint32_t old_tlli, uint32_t new_tlli, + enum gprs_ciph_algo alg, const uint8_t *kc); + +int gprs_llc_init(const char *cipher_plugin_path); +int gprs_llc_vty_init(void); + +/** + * \short Check if N(U) should be considered a retransmit + * + * Implements the range check as of GSM 04.64 8.4.2 + * Receipt of unacknowledged information. + * + * @returns Returns 1 if (V(UR)-32) <= N(U) < V(UR) + * @param nu N(U) unconfirmed sequence number of the UI frame + * @param vur V(UR) unconfirmend received state variable + */ +static inline int gprs_llc_is_retransmit(uint16_t nu, uint16_t vur) +{ + int delta = (vur - nu) & 0x1ff; + return 0 < delta && delta < 32; +} + +#endif diff --git a/include/openbsc/gprs_sgsn.h b/include/openbsc/gprs_sgsn.h new file mode 100644 index 000000000..8074d8f9b --- /dev/null +++ b/include/openbsc/gprs_sgsn.h @@ -0,0 +1,222 @@ +#ifndef _GPRS_SGSN_H +#define _GPRS_SGSN_H + +#include +#include + +#include + +#include + +#include + +#define GSM_IMSI_LENGTH 17 +#define GSM_IMEI_LENGTH 17 +#define GSM_EXTENSION_LENGTH 15 + +struct gprs_llc_lle; + +/* TS 04.08 4.1.3.3 GMM mobility management states on the network side */ +enum gprs_mm_state { + GMM_DEREGISTERED, /* 4.1.3.3.1.1 */ + GMM_COMMON_PROC_INIT, /* 4.1.3.3.1.2 */ + GMM_REGISTERED_NORMAL, /* 4.1.3.3.2.1 */ + GMM_REGISTERED_SUSPENDED, /* 4.1.3.3.2.2 */ + GMM_DEREGISTERED_INIT, /* 4.1.3.3.1.4 */ +}; + +enum gprs_mm_ctr { + GMM_CTR_PKTS_SIG_IN, + GMM_CTR_PKTS_SIG_OUT, + GMM_CTR_PKTS_UDATA_IN, + GMM_CTR_PKTS_UDATA_OUT, + GMM_CTR_BYTES_UDATA_IN, + GMM_CTR_BYTES_UDATA_OUT, + GMM_CTR_PDP_CTX_ACT, + GMM_CTR_SUSPEND, + GMM_CTR_PAGING_PS, + GMM_CTR_PAGING_CS, + GMM_CTR_RA_UPDATE, +}; + +enum gprs_pdp_ctx { + PDP_CTR_PKTS_UDATA_IN, + PDP_CTR_PKTS_UDATA_OUT, + PDP_CTR_BYTES_UDATA_IN, + PDP_CTR_BYTES_UDATA_OUT, +}; + +enum gprs_t3350_mode { + GMM_T3350_MODE_ATT, + GMM_T3350_MODE_RAU, + GMM_T3350_MODE_PTMSI_REALL, +}; + +#define MS_RADIO_ACCESS_CAPA + +/* According to TS 03.60, Table 5: SGSN MM and PDP Contexts */ +/* Extended by 3GPP TS 23.060, Table 6: SGSN MM and PDP Contexts */ +struct sgsn_mm_ctx { + struct llist_head list; + + char imsi[GSM_IMSI_LENGTH]; + enum gprs_mm_state mm_state; + uint32_t p_tmsi; + uint32_t p_tmsi_old; /* old P-TMSI before new is confirmed */ + uint32_t p_tmsi_sig; + char imei[GSM_IMEI_LENGTH]; + /* Opt: Software Version Numbber / TS 23.195 */ + char msisdn[GSM_EXTENSION_LENGTH]; + struct gprs_ra_id ra; + uint16_t cell_id; + uint32_t cell_id_age; + uint16_t sac; /* Iu: Service Area Code */ + uint32_t sac_age;/* Iu: Service Area Code age */ + /* VLR number */ + uint32_t new_sgsn_addr; + /* Authentication Triplets */ + /* Kc */ + /* Iu: CK, IK, KSI */ + /* CKSN */ + enum gprs_ciph_algo ciph_algo; + struct { + uint8_t len; + uint8_t buf[50]; /* GSM 04.08 10.5.5.12a, extended in TS 24.008 */ + } ms_radio_access_capa; + struct { + uint8_t len; + uint8_t buf[8]; /* GSM 04.08 10.5.5.12, extended in TS 24.008 */ + } ms_network_capa; + uint16_t drx_parms; + int mnrg; /* MS reported to HLR? */ + int ngaf; /* MS reported to MSC/VLR? */ + int ppf; /* paging for GPRS + non-GPRS? */ + /* SMS Parameters */ + int recovery; + uint8_t radio_prio_sms; + + struct llist_head pdp_list; + + /* Additional bits not present in the GSM TS */ + struct gprs_llc_llme *llme; + uint32_t tlli; + uint32_t tlli_new; + uint16_t nsei; + uint16_t bvci; + struct rate_ctr_group *ctrg; + struct osmo_timer_list timer; + unsigned int T; /* Txxxx number */ + unsigned int num_T_exp; /* number of consecutive T expirations */ + + enum gprs_t3350_mode t3350_mode; + uint8_t t3370_id_type; +}; + +/* look-up a SGSN MM context based on TLLI + RAI */ +struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli, + const struct gprs_ra_id *raid); +struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t tmsi); +struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi); + +/* Allocate a new SGSN MM context */ +struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli, + const struct gprs_ra_id *raid); +void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm); + + +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 */ + struct sgsn_ggsn_ctx *ggsn; /* which GGSN serves this PDP */ + struct rate_ctr_group *ctrg; + + //unsigned int id; + struct pdp_t *lib; /* pointer to libgtp PDP ctx */ + enum pdp_ctx_state state; + enum pdp_type type; + uint32_t address; + char *apn_subscribed; + //char *apn_used; + uint16_t nsapi; /* SNDCP */ + uint16_t sapi; /* LLC */ + uint8_t ti; /* transaction identifier */ + int vplmn_allowed; + uint32_t qos_profile_subscr; + //uint32_t qos_profile_req; + //uint32_t qos_profile_neg; + uint8_t radio_prio; + uint32_t tx_npdu_nr; + uint32_t rx_npdu_nr; + uint32_t tx_gtp_snd; + uint32_t rx_gtp_snu; + //uint32_t charging_id; + int reordering_reqd; + + struct osmo_timer_list timer; + unsigned int T; /* Txxxx number */ + unsigned int num_T_exp; /* number of consecutive T expirations */ +}; + + +/* 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_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); +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 *description; +}; + +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); + +/* 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); + +#endif /* _GPRS_SGSN_H */ diff --git a/include/openbsc/gsm_04_08.h b/include/openbsc/gsm_04_08.h new file mode 100644 index 000000000..2f5aaa98c --- /dev/null +++ b/include/openbsc/gsm_04_08.h @@ -0,0 +1,75 @@ +#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; + +#define GSM48_ALLOC_SIZE 2048 +#define GSM48_ALLOC_HEADROOM 256 + +static inline struct msgb *gsm48_msgb_alloc(void) +{ + return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM, + "GSM 04.08"); +} + +/* config options controlling the behaviour of the lower leves */ +void gsm0408_allow_everyone(int allow); +void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause); +void gsm0408_clear_all_trans(struct gsm_network *net, int protocol); +int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg); + +int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id); +int gsm0408_new_conn(struct gsm_subscriber_connection *conn); +enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *bts, uint8_t ra); +/* don't use "enum gsm_chreq_reason_t" to avoid circular dependency */ +int get_reason_by_chreq(uint8_t ra, int neci); +void gsm_net_update_ctype(struct gsm_network *net); + +int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn); +int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, uint8_t *rand, int key_seq); +int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn); +int gsm48_send_rr_release(struct gsm_lchan *lchan); +int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv); +int gsm48_send_rr_app_info(struct gsm_subscriber_connection *conn, uint8_t apdu_id, + uint8_t apdu_len, const uint8_t *apdu); +int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_class); +int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, + uint8_t power_command, uint8_t ho_ref); + +int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg); + +/* convert a ASCII phone number to call-control BCD */ +int encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len, + int h_len, const char *input); +int decode_bcd_number(char *output, int output_len, const uint8_t *bcd_lv, + int h_len); + +int send_siemens_mrpci(struct gsm_lchan *lchan, uint8_t *classmark2_lv); +int gsm48_extract_mi(uint8_t *classmark2, int length, char *mi_string, uint8_t *mi_type); +int gsm48_paging_extract_mi(struct gsm48_pag_resp *pag, int length, char *mi_string, uint8_t *mi_type); +int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, struct msgb *msg, struct gsm_subscriber *subscr); + +int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t lchan_mode); +int gsm48_rx_rr_modif_ack(struct msgb *msg); +int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg); + +struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value); +struct msgb *gsm48_create_loc_upd_rej(uint8_t cause); +void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, + const struct gsm_lchan *lchan); + +void release_security_operation(struct gsm_subscriber_connection *conn); +void allocate_security_operation(struct gsm_subscriber_connection *conn); + +#endif diff --git a/include/openbsc/gsm_04_08_gprs.h b/include/openbsc/gsm_04_08_gprs.h new file mode 100644 index 000000000..cd2a08c59 --- /dev/null +++ b/include/openbsc/gsm_04_08_gprs.h @@ -0,0 +1,381 @@ +#ifndef _GSM48_GPRS_H +#define _GSM48_GPRS_H + +#include +#include + +/* Table 10.4 / 10.4a, GPRS Mobility Management (GMM) */ +#define GSM48_MT_GMM_ATTACH_REQ 0x01 +#define GSM48_MT_GMM_ATTACH_ACK 0x02 +#define GSM48_MT_GMM_ATTACH_COMPL 0x03 +#define GSM48_MT_GMM_ATTACH_REJ 0x04 +#define GSM48_MT_GMM_DETACH_REQ 0x05 +#define GSM48_MT_GMM_DETACH_ACK 0x06 + +#define GSM48_MT_GMM_RA_UPD_REQ 0x08 +#define GSM48_MT_GMM_RA_UPD_ACK 0x09 +#define GSM48_MT_GMM_RA_UPD_COMPL 0x0a +#define GSM48_MT_GMM_RA_UPD_REJ 0x0b + +#define GSM48_MT_GMM_PTMSI_REALL_CMD 0x10 +#define GSM48_MT_GMM_PTMSI_REALL_COMPL 0x11 +#define GSM48_MT_GMM_AUTH_CIPH_REQ 0x12 +#define GSM48_MT_GMM_AUTH_CIPH_RESP 0x13 +#define GSM48_MT_GMM_AUTH_CIPH_REJ 0x14 +#define GSM48_MT_GMM_ID_REQ 0x15 +#define GSM48_MT_GMM_ID_RESP 0x16 +#define GSM48_MT_GMM_STATUS 0x20 +#define GSM48_MT_GMM_INFO 0x21 + +/* Table 10.4a, GPRS Session Management (GSM) */ +#define GSM48_MT_GSM_ACT_PDP_REQ 0x41 +#define GSM48_MT_GSM_ACT_PDP_ACK 0x42 +#define GSM48_MT_GSM_ACT_PDP_REJ 0x43 +#define GSM48_MT_GSM_REQ_PDP_ACT 0x44 +#define GSM48_MT_GSM_REQ_PDP_ACT_REJ 0x45 +#define GSM48_MT_GSM_DEACT_PDP_REQ 0x46 +#define GSM48_MT_GSM_DEACT_PDP_ACK 0x47 +#define GSM48_MT_GSM_ACT_AA_PDP_REQ 0x50 +#define GSM48_MT_GSM_ACT_AA_PDP_ACK 0x51 +#define GSM48_MT_GSM_ACT_AA_PDP_REJ 0x52 +#define GSM48_MT_GSM_DEACT_AA_PDP_REQ 0x53 +#define GSM48_MT_GSM_DEACT_AA_PDP_ACK 0x54 +#define GSM48_MT_GSM_STATUS 0x55 + +/* Chapter 10.5.5.2 / Table 10.5.135 */ +#define GPRS_ATT_T_ATTACH 1 +#define GPRS_ATT_T_ATT_WHILE_IMSI 2 +#define GPRS_ATT_T_COMBINED 3 + +/* Chapter 10.5.5.5 / Table 10.5.138 */ +#define GPRS_DET_T_MO_GPRS 1 +#define GPRS_DET_T_MO_IMSI 2 +#define GPRS_DET_T_MO_COMBINED 3 +/* Network to MS direction */ +#define GPRS_DET_T_MT_REATT_REQ 1 +#define GPRS_DET_T_MT_REATT_NOTREQ 2 +#define GPRS_DET_T_MT_IMSI 3 + +/* Chapter 10.5.5.18 / Table 105.150 */ +#define GPRS_UPD_T_RA 0 +#define GPRS_UPD_T_RA_LA 1 +#define GPRS_UPD_T_RA_LA_IMSI_ATT 2 +#define GPRS_UPD_T_PERIODIC 3 + +enum gsm48_gprs_ie_mm { + GSM48_IE_GMM_CIPH_CKSN = 0x08, /* 10.5.1.2 */ + GSM48_IE_GMM_TIMER_READY = 0x17, /* 10.5.7.3 */ + GSM48_IE_GMM_ALLOC_PTMSI = 0x18, /* 10.5.1.4 */ + GSM48_IE_GMM_PTMSI_SIG = 0x19, /* 10.5.5.8 */ + GSM48_IE_GMM_AUTH_RAND = 0x21, /* 10.5.3.1 */ + GSM48_IE_GMM_AUTH_SRES = 0x22, /* 10.5.3.2 */ + GSM48_IE_GMM_IMEISV = 0x23, /* 10.5.1.4 */ + GSM48_IE_GMM_DRX_PARAM = 0x27, /* 10.5.5.6 */ + GSM48_IE_GMM_MS_NET_CAPA = 0x31, /* 10.5.5.12 */ + GSM48_IE_GMM_PDP_CTX_STATUS = 0x32, /* 10.5.7.1 */ + GSM48_IE_GMM_PS_LCS_CAPA = 0x33, /* 10.5.5.22 */ + GSM48_IE_GMM_GMM_MBMS_CTX_ST = 0x35, /* 10.5.7.6 */ +}; + +enum gsm48_gprs_ie_sm { + GSM48_IE_GSM_APN = 0x28, /* 10.5.6.1 */ + GSM48_IE_GSM_PROTO_CONF_OPT = 0x27, /* 10.5.6.3 */ + GSM48_IE_GSM_PDP_ADDR = 0x2b, /* 10.5.6.4 */ + GSM48_IE_GSM_AA_TMR = 0x29, /* 10.5.7.3 */ + GSM48_IE_GSM_NAME_FULL = 0x43, /* 10.5.3.5a */ + GSM48_IE_GSM_NAME_SHORT = 0x45, /* 10.5.3.5a */ + GSM48_IE_GSM_TIMEZONE = 0x46, /* 10.5.3.8 */ + GSM48_IE_GSM_UTC_AND_TZ = 0x47, /* 10.5.3.9 */ + GSM48_IE_GSM_LSA_ID = 0x48, /* 10.5.3.11 */ + + /* Fake IEs that are not present on the Layer3 air interface, + * but which we use to simplify internal APIs */ + OSMO_IE_GSM_REQ_QOS = 0xfd, + OSMO_IE_GSM_REQ_PDP_ADDR = 0xfe, +}; + +/* Chapter 9.4.15 / Table 9.4.15 */ +struct gsm48_ra_upd_ack { + uint8_t force_stby:4, /* 10.5.5.7 */ + upd_result:4; /* 10.5.5.17 */ + uint8_t ra_upd_timer; /* 10.5.7.3 */ + struct gsm48_ra_id ra_id; /* 10.5.5.15 */ + uint8_t data[0]; +} __attribute__((packed)); + +/* Chapter 10.5.7.3 */ +enum gsm48_gprs_tmr_unit { + GPRS_TMR_2SECONDS = 0 << 5, + GPRS_TMR_MINUTE = 1 << 5, + GPRS_TMR_6MINUTE = 2 << 5, + GPRS_TMR_DEACTIVATED = 3 << 5, +}; + +/* Chapter 9.4.2 / Table 9.4.2 */ +struct gsm48_attach_ack { + uint8_t att_result:4, /* 10.5.5.7 */ + force_stby:4; /* 10.5.5.1 */ + uint8_t ra_upd_timer; /* 10.5.7.3 */ + uint8_t radio_prio; /* 10.5.7.2 */ + struct gsm48_ra_id ra_id; /* 10.5.5.15 */ + uint8_t data[0]; +} __attribute__((packed)); + +/* Chapter 9.4.9 / Table 9.4.9 */ +struct gsm48_auth_ciph_req { + uint8_t ciph_alg:4, /* 10.5.5.3 */ + imeisv_req:4; /* 10.5.5.10 */ + uint8_t force_stby:4, /* 10.5.5.7 */ + ac_ref_nr:4; /* 10.5.5.19 */ + uint8_t data[0]; +} __attribute__((packed)); +/* optional: TV RAND, TV CKSN */ + +struct gsm48_auth_ciph_resp { + uint8_t ac_ref_nr:4, + spare:4; + uint8_t data[0]; +} __attribute__((packed)); + +/* Chapter 9.5.1 / Table 9.5.1 */ +struct gsm48_act_pdp_ctx_req { + uint8_t req_nsapi; + uint8_t req_llc_sapi; + uint8_t data[0]; +} __attribute__((packed)); + +/* Chapter 10.5.5.14 / Table 10.5.147 */ +enum gsm48_gmm_cause { + GMM_CAUSE_IMSI_UNKNOWN = 0x02, + GMM_CAUSE_ILLEGAL_MS = 0x03, + GMM_CAUSE_ILLEGAL_ME = 0x06, + GMM_CAUSE_GPRS_NOTALLOWED = 0x07, + GMM_CAUSE_GPRS_OTHER_NOTALLOWED = 0x08, + GMM_CAUSE_MS_ID_NOT_DERIVED = 0x09, + GMM_CAUSE_IMPL_DETACHED = 0x0a, + GMM_CAUSE_PLMN_NOTALLOWED = 0x0b, + GMM_CAUSE_LA_NOTALLOWED = 0x0c, + GMM_CAUSE_ROAMING_NOTALLOWED = 0x0d, + GMM_CAUSE_NO_GPRS_PLMN = 0x0e, + GMM_CAUSE_MSC_TEMP_NOTREACH = 0x10, + GMM_CAUSE_NET_FAIL = 0x11, + GMM_CAUSE_CONGESTION = 0x16, + GMM_CAUSE_SEM_INCORR_MSG = 0x5f, + GMM_CAUSE_INV_MAND_INFO = 0x60, + GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL = 0x61, + GMM_CAUSE_MSGT_INCOMP_P_STATE = 0x62, + GMM_CAUSE_IE_NOTEXIST_NOTIMPL = 0x63, + GMM_CAUSE_COND_IE_ERR = 0x64, + GMM_CAUSE_MSG_INCOMP_P_STATE = 0x65, + GMM_CAUSE_PROTO_ERR_UNSPEC = 0x6f, +}; + +/* Chapter 10.4.6.6 / Table 10.5.157 */ +enum gsm48_gsm_cause { + GSM_CAUSE_INSUFF_RSRC = 0x1a, + GSM_CAUSE_MISSING_APN = 0x1b, + GSM_CAUSE_UNKNOWN_PDP = 0x1c, + GSM_CAUSE_AUTH_FAILED = 0x1d, + GSM_CAUSE_ACT_REJ_GGSN = 0x1e, + GSM_CAUSE_ACT_REJ_UNSPEC = 0x1f, + GSM_CAUSE_SERV_OPT_NOTSUPP = 0x20, + GSM_CAUSE_REQ_SERV_OPT_NOTSUB = 0x21, + GSM_CAUSE_SERV_OPT_TEMP_OOO = 0x22, + GSM_CAUSE_NSAPI_IN_USE = 0x23, + GSM_CAUSE_DEACT_REGULAR = 0x24, + GSM_CAUSE_QOS_NOT_ACCEPTED = 0x25, + GSM_CAUSE_NET_FAIL = 0x26, + GSM_CAUSE_REACT_RQD = 0x27, + GSM_CAUSE_FEATURE_NOTSUPP = 0x28, + GSM_CAUSE_INVALID_TRANS_ID = 0x51, + GSM_CAUSE_SEM_INCORR_MSG = 0x5f, + GSM_CAUSE_INV_MAND_INFO = 0x60, + GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL = 0x61, + GSM_CAUSE_MSGT_INCOMP_P_STATE = 0x62, + GSM_CAUSE_IE_NOTEXIST_NOTIMPL = 0x63, + GSM_CAUSE_COND_IE_ERR = 0x64, + GSM_CAUSE_MSG_INCOMP_P_STATE = 0x65, + GSM_CAUSE_PROTO_ERR_UNSPEC = 0x6f, +}; + +/* Section 6.1.2.2: Session management states on the network side */ +enum gsm48_pdp_state { + PDP_S_INACTIVE, + PDP_S_ACTIVE_PENDING, + PDP_S_ACTIVE, + PDP_S_INACTIVE_PENDING, + PDP_S_MODIFY_PENDING, +}; + +/* Table 10.5.155/3GPP TS 24.008 */ +enum gsm48_pdp_type_org { + PDP_TYPE_ORG_ETSI = 0x00, + PDP_TYPE_ORG_IETF = 0x01, +}; +enum gsm48_pdp_type_nr { + PDP_TYPE_N_ETSI_RESERVED = 0x00, + PDP_TYPE_N_ETSI_PPP = 0x01, + PDP_TYPE_N_IETF_IPv4 = 0x21, + PDP_TYPE_N_IETF_IPv6 = 0x57, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_reliab_class { + GSM48_QOS_RC_LLC_ACK_RLC_ACK_DATA_PROT = 2, + GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT = 3, + GSM48_QOS_RC_LLC_UN_RLC_UN_PROT_DATA = 4, + GSM48_QOS_RC_LLC_UN_RLC_UN_DATA_UN = 5, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_preced_class { + GSM48_QOS_PC_HIGH = 1, + GSM48_QOS_PC_NORMAL = 2, + GSM48_QOS_PC_LOW = 3, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_peak_tput { + GSM48_QOS_PEAK_TPUT_1000bps = 1, + GSM48_QOS_PEAK_TPUT_2000bps = 2, + GSM48_QOS_PEAK_TPUT_4000bps = 3, + GSM48_QOS_PEAK_TPUT_8000bps = 4, + GSM48_QOS_PEAK_TPUT_16000bps = 5, + GSM48_QOS_PEAK_TPUT_32000bps = 6, + GSM48_QOS_PEAK_TPUT_64000bps = 7, + GSM48_QOS_PEAK_TPUT_128000bps = 8, + GSM48_QOS_PEAK_TPUT_256000bps = 9, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_mean_tput { + GSM48_QOS_MEAN_TPUT_100bph = 1, + GSM48_QOS_MEAN_TPUT_200bph = 2, + GSM48_QOS_MEAN_TPUT_500bph = 3, + GSM48_QOS_MEAN_TPUT_1000bph = 4, + GSM48_QOS_MEAN_TPUT_2000bph = 5, + GSM48_QOS_MEAN_TPUT_5000bph = 6, + GSM48_QOS_MEAN_TPUT_10000bph = 7, + GSM48_QOS_MEAN_TPUT_20000bph = 8, + GSM48_QOS_MEAN_TPUT_50000bph = 9, + GSM48_QOS_MEAN_TPUT_100kbph = 10, + GSM48_QOS_MEAN_TPUT_200kbph = 11, + GSM48_QOS_MEAN_TPUT_500kbph = 0xc, + GSM48_QOS_MEAN_TPUT_1Mbph = 0xd, + GSM48_QOS_MEAN_TPUT_2Mbph = 0xe, + GSM48_QOS_MEAN_TPUT_5Mbph = 0xf, + GSM48_QOS_MEAN_TPUT_10Mbph = 0x10, + GSM48_QOS_MEAN_TPUT_20Mbph = 0x11, + GSM48_QOS_MEAN_TPUT_50Mbph = 0x12, + GSM48_QOS_MEAN_TPUT_BEST_EFFORT = 0x1f, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_err_sdu { + GSM48_QOS_ERRSDU_NODETECT = 1, + GSM48_QOS_ERRSDU_YES = 2, + GSM48_QOS_ERRSDU_NO = 3, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_deliv_order { + GSM48_QOS_DO_ORDERED = 1, + GSM48_QOS_DO_UNORDERED = 2, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_traf_class { + GSM48_QOS_TC_CONVERSATIONAL = 1, + GSM48_QOS_TC_STREAMING = 2, + GSM48_QOS_TC_INTERACTIVE = 3, + GSM48_QOS_TC_BACKGROUND = 4, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_max_sdu_size { + /* values below in 10 octet granularity */ + GSM48_QOS_MAXSDU_1502 = 0x97, + GSM48_QOS_MAXSDU_1510 = 0x98, + GSM48_QOS_MAXSDU_1520 = 0x99, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_max_bitrate { + GSM48_QOS_MBRATE_1k = 0x01, + GSM48_QOS_MBRATE_63k = 0x3f, + GSM48_QOS_MBRATE_64k = 0x40, + GSM48_QOS_MBRATE_568k = 0x7f, + GSM48_QOS_MBRATE_576k = 0x80, + GSM48_QOS_MBRATE_8640k = 0xfe, + GSM48_QOS_MBRATE_0k = 0xff, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_resid_ber { + GSM48_QOS_RBER_5e_2 = 0x01, + GSM48_QOS_RBER_1e_2 = 0x02, + GSM48_QOS_RBER_5e_3 = 0x03, + GSM48_QOS_RBER_4e_3 = 0x04, + GSM48_QOS_RBER_1e_3 = 0x05, + GSM48_QOS_RBER_1e_4 = 0x06, + GSM48_QOS_RBER_1e_5 = 0x07, + GSM48_QOS_RBER_1e_6 = 0x08, + GSM48_QOS_RBER_6e_8 = 0x09, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +enum gsm48_qos_sdu_err { + GSM48_QOS_SERR_1e_2 = 0x01, + GSM48_QOS_SERR_7e_2 = 0x02, + GSM48_QOS_SERR_1e_3 = 0x03, + GSM48_QOS_SERR_1e_4 = 0x04, + GSM48_QOS_SERR_1e_5 = 0x05, + GSM48_QOS_SERR_1e_6 = 0x06, + GSM48_QOS_SERR_1e_1 = 0x07, +}; + +/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ +struct gsm48_qos { + /* octet 3 */ + uint8_t reliab_class:3; + uint8_t delay_class:3; + uint8_t spare:2; + /* octet 4 */ + uint8_t preced_class:3; + uint8_t spare2:1; + uint8_t peak_tput:4; + /* octet 5 */ + uint8_t mean_tput:5; + uint8_t spare3:3; + /* octet 6 */ + uint8_t deliv_err_sdu:3; + uint8_t deliv_order:2; + uint8_t traf_class:3; + /* octet 7 */ + uint8_t max_sdu_size; + /* octet 8 */ + uint8_t max_bitrate_up; + /* octet 9 */ + uint8_t max_bitrate_down; + /* octet 10 */ + uint8_t sdu_err_ratio:4; + uint8_t resid_ber:4; + /* octet 11 */ + uint8_t handling_prio:2; + uint8_t xfer_delay:6; + /* octet 12 */ + uint8_t guar_bitrate_up; + /* octet 13 */ + uint8_t guar_bitrate_down; + /* octet 14 */ + uint8_t src_stats_desc:4; + uint8_t sig_ind:1; + uint8_t spare5:3; + /* octet 15 */ + uint8_t max_bitrate_down_ext; + /* octet 16 */ + uint8_t guar_bitrate_down_ext; +}; + + +#endif /* _GSM48_GPRS_H */ diff --git a/include/openbsc/gsm_04_11.h b/include/openbsc/gsm_04_11.h new file mode 100644 index 000000000..72b694825 --- /dev/null +++ b/include/openbsc/gsm_04_11.h @@ -0,0 +1,40 @@ +#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); +#endif diff --git a/include/openbsc/gsm_04_80.h b/include/openbsc/gsm_04_80.h new file mode 100644 index 000000000..0a6065234 --- /dev/null +++ b/include/openbsc/gsm_04_80.h @@ -0,0 +1,20 @@ +#ifndef _GSM_04_80_H +#define _GSM_04_80_H + +#include +#include +#include + +struct gsm_subscriber_connection; + +int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, + const struct msgb *in_msg, const char* response_text, + const struct ussd_request *req); +int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, + const struct msgb *msg, + const struct ussd_request *request); + +int gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn, int level, const char *text); +int gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn); + +#endif diff --git a/include/openbsc/gsm_data.h b/include/openbsc/gsm_data.h new file mode 100644 index 000000000..874150535 --- /dev/null +++ b/include/openbsc/gsm_data.h @@ -0,0 +1,408 @@ +#ifndef _GSM_DATA_H +#define _GSM_DATA_H + +#include + +#include +#include + +#include + +/** annotations for msgb ownership */ +#define __uses + +#define OBSC_NM_W_ACK_CB(__msgb) (__msgb)->cb[3] + +struct mncc_sock_state; + +#define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3] + +enum gsm_security_event { + GSM_SECURITY_NOAVAIL, + GSM_SECURITY_AUTH_FAILED, + GSM_SECURITY_SUCCEEDED, + GSM_SECURITY_ALREADY, +}; + +struct msgb; +typedef int gsm_cbfn(unsigned int hooknum, + unsigned int event, + struct msgb *msg, + void *data, void *param); + +/* Real authentication information containing Ki */ +enum gsm_auth_algo { + AUTH_ALGO_NONE, + AUTH_ALGO_XOR, + AUTH_ALGO_COMP128v1, +}; + +struct gsm_auth_info { + enum gsm_auth_algo auth_algo; + unsigned int a3a8_ki_len; + uint8_t a3a8_ki[16]; +}; + +struct gsm_auth_tuple { + int use_count; + int key_seq; + uint8_t rand[16]; + uint8_t sres[4]; + uint8_t kc[8]; +}; +#define GSM_KEY_SEQ_INVAL 7 /* GSM 04.08 - 10.5.1.2 */ + +/* + * LOCATION UPDATING REQUEST state + * + * Our current operation is: + * - Get imei/tmsi + * - Accept/Reject according to global policy + */ +struct gsm_loc_updating_operation { + struct osmo_timer_list updating_timer; + unsigned int waiting_for_imsi : 1; + unsigned int waiting_for_imei : 1; + unsigned int key_seq : 4; +}; + +/* + * AUTHENTICATION/CIPHERING state + */ +struct gsm_security_operation { + struct gsm_auth_tuple atuple; + gsm_cbfn *cb; + void *cb_data; +}; + +/* + * A dummy to keep a connection up for at least + * a couple of seconds to work around MSC issues. + */ +struct gsm_anchor_operation { + struct osmo_timer_list timeout; +}; + +/* Maximum number of neighbor cells whose average we track */ +#define MAX_NEIGH_MEAS 10 +/* Maximum size of the averaging window for neighbor cells */ +#define MAX_WIN_NEIGH_AVG 10 + +/* processed neighbor measurements for one cell */ +struct neigh_meas_proc { + uint16_t arfcn; + uint8_t bsic; + uint8_t rxlev[MAX_WIN_NEIGH_AVG]; + unsigned int rxlev_cnt; + uint8_t last_seen_nr; +}; + +/* the per subscriber data for lchan */ +struct gsm_subscriber_connection { + struct llist_head entry; + + /* To whom we are allocated at the moment */ + struct gsm_subscriber *subscr; + + /* LU expiration handling */ + uint8_t expire_timer_stopped; + + /* + * 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; + int put_channel; + + /* bsc structures */ + struct osmo_bsc_sccp_con *sccp_con; + + /* back pointers */ + int in_release; + struct gsm_lchan *lchan; + struct gsm_lchan *ho_lchan; + struct gsm_bts *bts; + + /* for assignment handling */ + struct osmo_timer_list T10; + struct gsm_lchan *secondary_lchan; + +}; + + +#define ROLE_BSC +#include "gsm_data_shared.h" + + +/* Some statistics of our network */ +struct gsmnet_stats { + struct { + struct osmo_counter *total; + struct osmo_counter *no_channel; + } chreq; + struct { + struct osmo_counter *attempted; + struct osmo_counter *no_channel; /* no channel available */ + struct osmo_counter *timeout; /* T3103 timeout */ + struct osmo_counter *completed; /* HO COMPL received */ + struct osmo_counter *failed; /* HO FAIL received */ + } handover; + struct { + struct osmo_counter *attach; + struct osmo_counter *normal; + struct osmo_counter *periodic; + struct osmo_counter *detach; + } loc_upd_type; + struct { + struct osmo_counter *reject; + struct osmo_counter *accept; + } loc_upd_resp; + struct { + struct osmo_counter *attempted; + struct osmo_counter *detached; + struct osmo_counter *completed; + struct osmo_counter *expired; + } paging; + struct { + struct osmo_counter *submitted; /* MO SMS submissions */ + struct osmo_counter *no_receiver; + struct osmo_counter *delivered; /* MT SMS deliveries */ + struct osmo_counter *rp_err_mem; + struct osmo_counter *rp_err_other; + } sms; + struct { + struct osmo_counter *mo_setup; + struct osmo_counter *mo_connect_ack; + struct osmo_counter *mt_setup; + struct osmo_counter *mt_connect; + } call; + struct { + struct osmo_counter *rf_fail; + struct osmo_counter *rll_err; + } chan; + struct { + struct osmo_counter *oml_fail; + struct osmo_counter *rsl_fail; + } bts; +}; + +enum gsm_auth_policy { + GSM_AUTH_POLICY_CLOSED, /* only subscribers authorized in DB */ + GSM_AUTH_POLICY_ACCEPT_ALL, /* accept everyone, even if not authorized in DB */ + GSM_AUTH_POLICY_TOKEN, /* accept first, send token per sms, then revoke authorization */ +}; + +#define GSM_T3101_DEFAULT 10 +#define GSM_T3105_DEFAULT 40 +#define GSM_T3113_DEFAULT 60 +#define GSM_T3122_DEFAULT 10 + +struct gsm_network { + /* global parameters */ + uint16_t country_code; + uint16_t network_code; + char *name_long; + char *name_short; + enum gsm_auth_policy auth_policy; + enum gsm48_reject_value reject_cause; + int a5_encryption; + int neci; + int send_mm_info; + struct { + int active; + /* Window RXLEV averaging */ + unsigned int win_rxlev_avg; /* number of SACCH frames */ + /* Window RXQUAL averaging */ + unsigned int win_rxqual_avg; /* number of SACCH frames */ + /* Window RXLEV neighbouring cells averaging */ + unsigned int win_rxlev_avg_neigh; /* number of SACCH frames */ + + /* how often should we check for power budget HO */ + unsigned int pwr_interval; /* SACCH frames */ + /* how much better does a neighbor cell have to be ? */ + unsigned int pwr_hysteresis; /* dBm */ + /* maximum distacne before we try a handover */ + unsigned int max_distance; /* TA values */ + } handover; + + struct gsmnet_stats stats; + + /* layer 4 */ + struct mncc_sock_state *mncc_state; + int (*mncc_recv) (struct gsm_network *net, struct msgb *msg); + struct llist_head upqueue; + struct llist_head trans_list; + struct bsc_api *bsc_api; + + unsigned int num_bts; + struct llist_head bts_list; + + /* timer values */ + int T3101; + int T3103; + int T3105; + int T3107; + int T3109; + int T3111; + int T3113; + int T3115; + int T3117; + int T3119; + int T3122; + int T3141; + + /* timer to expire old location updates */ + struct osmo_timer_list subscr_expire_timer; + + /* Radio Resource Location Protocol (TS 04.31) */ + struct { + enum rrlp_mode mode; + } rrlp; + + /* enable the DTXu and DTXd for this network */ + int dtx_enabled; + + enum gsm_chan_t ctype_by_chreq[16]; + + /* Use a TCH for handling requests of type paging any */ + int pag_any_tch; + + /* MSC data in case we are a true BSC */ + struct osmo_bsc_data *bsc_data; + + /* subscriber related features */ + int keep_subscr; + struct gsm_sms_queue *sms_queue; + + /* control interface */ + struct ctrl_handle *ctrl; +}; + +struct osmo_esme; + +enum gsm_sms_source_id { + SMS_SOURCE_UNKNOWN = 0, + SMS_SOURCE_MS, /* received from MS */ + SMS_SOURCE_VTY, /* received from VTY */ + SMS_SOURCE_SMPP, /* received via SMPP */ +}; + +#define SMS_HDR_SIZE 128 +#define SMS_TEXT_SIZE 256 + +struct gsm_sms_addr { + uint8_t ton; + uint8_t npi; + char addr[21+1]; +}; + +struct gsm_sms { + unsigned long long id; + struct gsm_subscriber *sender; + struct gsm_subscriber *receiver; + struct gsm_sms_addr src, dst; + enum gsm_sms_source_id source; + + struct { + struct osmo_esme *esme; + uint32_t sequence_nr; + int transaction_mode; + char msg_id[16]; + } smpp; + + unsigned long validity_minutes; + uint8_t reply_path_req; + uint8_t status_rep_req; + uint8_t ud_hdr_ind; + uint8_t protocol_id; + uint8_t data_coding_scheme; + uint8_t msg_ref; + uint8_t user_data_len; + uint8_t user_data[SMS_TEXT_SIZE]; + + char text[SMS_TEXT_SIZE]; +}; + +struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_code, + int (*mncc_recv)(struct gsm_network *, struct msgb *)); +int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type); + +struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num); + +/* Get reference to a neighbor cell on a given BCCH ARFCN */ +struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, + uint16_t arfcn, uint8_t bsic); + +enum gsm_bts_type parse_btstype(const char *arg); +const char *btstype2str(enum gsm_bts_type type); +struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, + struct gsm_bts *start_bts); + +extern void *tall_bsc_ctx; +extern int ipacc_rtp_direct; + +/* this actaully refers to the IPA transport, not the BTS model */ +static inline int is_ipaccess_bts(struct gsm_bts *bts) +{ + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_OSMO_SYSMO: + return 1; + default: + break; + } + return 0; +} + +static inline int is_siemens_bts(struct gsm_bts *bts) +{ + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + 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); +const char *bts_gprs_mode_name(enum bts_gprs_mode mode); + +int gsm48_ra_id_by_bts(uint8_t *buf, struct gsm_bts *bts); +void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts); +struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan); + +int gsm_btsmodel_set_feature(struct gsm_bts_model *model, enum gsm_bts_features feat); +int gsm_bts_model_register(struct gsm_bts_model *model); + +struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan); +void subscr_con_free(struct gsm_subscriber_connection *conn); + +struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, + enum gsm_bts_type type, + uint8_t tsc, uint8_t bsic); +void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr, + uint8_t e1_ts, uint8_t e1_ts_ss); + +void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked); +int gsm_bts_has_feature(struct gsm_bts *bts, enum gsm_bts_features feat); +struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr); + +/* 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]; + +#endif /* _GSM_DATA_H */ diff --git a/include/openbsc/gsm_data_shared.h b/include/openbsc/gsm_data_shared.h new file mode 100644 index 000000000..3ef145765 --- /dev/null +++ b/include/openbsc/gsm_data_shared.h @@ -0,0 +1,757 @@ +#ifndef _GSM_DATA_SHAREDH +#define _GSM_DATA_SHAREDH + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#ifndef ROLE_BSC +#include +#endif + +struct osmo_bsc_data; + +struct osmo_bsc_sccp_con; +struct gsm_sms_queue; + +/* RRLP mode of operation */ +enum rrlp_mode { + RRLP_MODE_NONE, + RRLP_MODE_MS_BASED, + RRLP_MODE_MS_PREF, + RRLP_MODE_ASS_PREF, +}; + +/* Channel Request reason */ +enum gsm_chreq_reason_t { + GSM_CHREQ_REASON_EMERG, + GSM_CHREQ_REASON_PAG, + GSM_CHREQ_REASON_CALL, + GSM_CHREQ_REASON_LOCATION_UPD, + GSM_CHREQ_REASON_OTHER, +}; + +#define TRX_NR_TS 8 +#define TS_MAX_LCHAN 8 + +#define HARDCODED_ARFCN 123 +#define HARDCODED_TSC 7 +#define HARDCODED_BSIC 0x3f /* NCC = 7 / BCC = 7 */ + +/* for multi-drop config */ +#define HARDCODED_BTS0_TS 1 +#define HARDCODED_BTS1_TS 6 +#define HARDCODED_BTS2_TS 11 + +/* reserved according to GSM 03.03 § 2.4 */ +#define GSM_RESERVED_TMSI 0xFFFFFFFF + +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; + struct abis_om_obj_inst obj_inst; + const char *name; + struct gsm_nm_state nm_state; + struct tlv_parsed *nm_attr; + struct gsm_bts *bts; +}; + +#define MAX_A5_KEY_LEN (128/8) +#define A38_XOR_MIN_KEY_LEN 12 +#define A38_XOR_MAX_KEY_LEN 16 +#define A38_COMP128_KEY_LEN 16 +#define RSL_ENC_ALG_A5(x) (x+1) + +/* is the data link established? who established it? */ +#define LCHAN_SAPI_UNUSED 0 +#define LCHAN_SAPI_MS 1 +#define LCHAN_SAPI_NET 2 + +/* 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 amr_mode { + uint8_t mode; + uint8_t threshold; + uint8_t hysteresis; +}; +struct amr_multirate_conf { + uint8_t gsm48_ie[2]; + struct amr_mode 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; + /* Power levels for MS and BTS */ + uint8_t bs_power; + uint8_t ms_power; + /* Encryption information */ + struct { + uint8_t alg_id; + uint8_t key_len; + uint8_t key[MAX_A5_KEY_LEN]; + } encr; + + /* AMR bits */ + struct gsm48_multi_rate_conf mr_conf; + + /* Established data link layer services */ + uint8_t sapis[8]; + int sacch_deact; + + struct { + uint32_t bound_ip; + uint32_t connect_ip; + uint16_t bound_port; + uint16_t connect_port; + uint16_t conn_id; + uint8_t rtp_payload; + uint8_t rtp_payload2; + uint8_t speech_mode; +#ifdef ROLE_BSC + struct rtp_socket *rtp_socket; +#else + struct osmo_rtp_socket *rtp_socket; +#endif + } abis_ip; + + uint8_t rqd_ta; + +#ifdef ROLE_BSC + struct osmo_timer_list T3101; + struct osmo_timer_list T3109; + struct osmo_timer_list T3111; + struct osmo_timer_list error_timer; + struct osmo_timer_list act_timer; + uint8_t error_cause; + + /* table of neighbor cell measurements */ + struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS]; + + /* cache of last measurement reports on this lchan */ + struct gsm_meas_rep meas_rep[6]; + int meas_rep_idx; + + /* GSM Random Access data */ + struct gsm48_req_ref *rqd_ref; + + struct gsm_subscriber_connection *conn; +#else + /* Number of different GsmL1_Sapi_t used in osmo_bts_sysmo is 23. + * Currently we don't share these headers so this is a magic number. */ + struct llist_head sapi_cmds; + uint8_t sapis_dl[23]; + uint8_t sapis_ul[23]; + struct lapdm_channel lapdm_ch; + struct llist_head dl_tch_queue; + struct { + /* bitmask of all SI that are present/valid in si_buf */ + uint32_t valid; + uint32_t last; + /* buffers where we put the pre-computed SI */ + sysinfo_buf_t buf[_MAX_SYSINFO_TYPE]; + } si; + struct { + uint8_t flags; + /* RSL measurment result number, 0 at lchan_act */ + uint8_t res_nr; + /* current Tx power level of the BTS */ + uint8_t bts_tx_pwr; + /* number of measurements stored in array below */ + uint8_t num_ul_meas; + struct bts_ul_meas uplink[MAX_NUM_UL_MEAS]; + /* last L1 header from the MS */ + uint8_t l1_info[2]; + struct { + uint8_t rxlev_full; + uint8_t rxlev_sub; + uint8_t rxqual_full; + uint8_t rxqual_sub; + } res; + } meas; + struct { + struct amr_multirate_conf amr_mr; + struct { + uint8_t buf[16]; + uint8_t len; + } last_sid; + } tch; + /* BTS-side ciphering state (rx only, bi-directional, ...) */ + uint8_t ciph_state; + 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; +#endif +}; + +#define TS_F_PDCH_MODE 0x1000 +/* One Timeslot in a TRX */ +struct gsm_bts_trx_ts { + struct gsm_bts_trx *trx; + /* number of this timeslot at the TRX */ + uint8_t nr; + + enum gsm_phys_chan_config pchan; + + unsigned int flags; + struct gsm_abis_mo mo; + struct tlv_parsed nm_attr; + uint8_t nm_chan_comb; + int tsc; /* -1 == use BTS TSC */ + + struct { + /* Parameters below are configured by VTY */ + int enabled; + uint8_t maio; + uint8_t hsn; + struct bitvec arfcns; + uint8_t arfcns_data[1024/8]; + /* This is the pre-computed MA for channel assignments */ + struct bitvec ma; + uint8_t ma_len; /* part of ma_data that is used */ + uint8_t ma_data[8]; /* 10.5.2.21: max 8 bytes value part */ + } hopping; + + /* To which E1 subslot are we connected */ + struct gsm_e1_subslot e1_link; + + struct gsm_lchan lchan[TS_MAX_LCHAN]; +}; + +/* One TRX in a BTS */ +struct gsm_bts_trx { + /* list header in bts->trx_list */ + struct llist_head list; + + struct gsm_bts *bts; + /* number of this TRX in the BTS */ + uint8_t nr; + /* human readable name / description */ + char *description; + /* how do we talk RSL with this TRX? */ + struct gsm_e1_subslot rsl_e1_link; + uint8_t rsl_tei; +#ifdef ROLE_BSC + struct e1inp_sign_link *rsl_link; +#else + struct ipabis_link *rsl_link; +#endif + /* 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 */ + + struct { + void *l1h; + } role_bts; + + union { + struct { + struct { + struct gsm_abis_mo mo; + } bbsig; + struct { + struct gsm_abis_mo mo; + } pa; + } bs11; + struct { + unsigned int test_state; + uint8_t test_nr; + struct rxlev_stats rxlev_stat; + } ipaccess; + }; + struct gsm_bts_trx_ts ts[TRX_NR_TS]; +}; + +#define GSM_BTS_SI(bts, i) (void *)(bts->si_buf[i]) + +enum gsm_bts_type { + GSM_BTS_TYPE_UNKNOWN, + GSM_BTS_TYPE_BS11, + GSM_BTS_TYPE_NANOBTS, + GSM_BTS_TYPE_RBS2000, + GSM_BTS_TYPE_NOKIA_SITE, + GSM_BTS_TYPE_OSMO_SYSMO, + _NUM_GSM_BTS_TYPE +}; + +struct vty; + +struct gsm_bts_model { + struct llist_head list; + + enum gsm_bts_type type; + const char *name; + + bool started; + int (*start)(struct gsm_network *net); + int (*oml_rcvmsg)(struct msgb *msg); + + void (*e1line_bind_ops)(struct e1inp_line *line); + + void (*config_write_bts)(struct vty *vty, struct gsm_bts *bts); + void (*config_write_trx)(struct vty *vty, struct gsm_bts_trx *trx); + void (*config_write_ts)(struct vty *vty, struct gsm_bts_trx_ts *ts); + + struct tlv_definition nm_att_tlvdef; + + struct bitvec features; + uint8_t _features_data[128/8]; +}; + +enum gsm_bts_features { + BTS_FEAT_HSCSD, + BTS_FEAT_GPRS, + BTS_FEAT_EGPRS, + BTS_FEAT_ECSD, + BTS_FEAT_HOPPING, +}; + +/* + * This keeps track of the paging status of one BTS. It + * includes a number of pending requests, a back pointer + * to the gsm_bts, a timer and some more state. + */ +struct gsm_bts_paging_state { + /* pending requests */ + struct llist_head pending_requests; + struct gsm_bts *bts; + + struct osmo_timer_list work_timer; + struct osmo_timer_list credit_timer; + + /* free chans needed */ + int free_chans_need; + + /* load */ + uint16_t available_slots; +}; + +struct gsm_envabtse { + struct gsm_abis_mo mo; +}; + +struct gsm_bts_gprs_nsvc { + struct gsm_bts *bts; + /* data read via VTY config file, to configure the BTS + * via OML from BSC */ + int id; + uint16_t nsvci; + uint16_t local_port; /* on the BTS */ + uint16_t remote_port; /* on the SGSN */ + uint32_t remote_ip; /* on the SGSN */ + + struct gsm_abis_mo mo; +}; + +enum gprs_rlc_par { + RLC_T3142, + RLC_T3169, + RLC_T3191, + RLC_T3193, + RLC_T3195, + RLC_N3101, + RLC_N3103, + RLC_N3105, + CV_COUNTDOWN, + T_DL_TBF_EXT, /* ms */ + T_UL_TBF_EXT, /* ms */ + _NUM_RLC_PAR +}; + +enum gprs_cs { + GPRS_CS1, + GPRS_CS2, + GPRS_CS3, + GPRS_CS4, + GPRS_MCS1, + GPRS_MCS2, + GPRS_MCS3, + GPRS_MCS4, + GPRS_MCS5, + GPRS_MCS6, + GPRS_MCS7, + GPRS_MCS8, + GPRS_MCS9, + _NUM_GRPS_CS +}; + +struct gprs_rlc_cfg { + uint16_t parameter[_NUM_RLC_PAR]; + struct { + uint16_t repeat_time; /* ms */ + uint8_t repeat_count; + } paging; + uint32_t cs_mask; /* bitmask of gprs_cs */ + uint8_t initial_cs; + uint8_t initial_mcs; +}; + + +enum neigh_list_manual_mode { + NL_MODE_AUTOMATIC = 0, + NL_MODE_MANUAL = 1, + NL_MODE_MANUAL_SI5SEP = 2, /* SI2 and SI5 have separate neighbor lists */ +}; + +enum bts_loc_fix { + BTS_LOC_FIX_INVALID = 0, + BTS_LOC_FIX_2D = 1, + BTS_LOC_FIX_3D = 2, +}; + +extern const struct value_string bts_loc_fix_names[]; + +struct bts_location { + struct llist_head list; + time_t tstamp; + enum bts_loc_fix valid; + double lat; + double lon; + double height; +}; + +/* One BTS */ +struct gsm_bts { + /* list header in net->bts_list */ + struct llist_head list; + + /* Geographical location of the BTS */ + struct llist_head loc_list; + + /* number of ths BTS in network */ + uint8_t nr; + /* human readable name / description */ + char *description; + /* Cell Identity */ + uint16_t cell_identity; + /* location area code of this BTS */ + uint16_t location_area_code; + /* Training Sequence Code */ + uint8_t tsc; + /* Base Station Identification Code (BSIC) */ + uint8_t bsic; + /* type of BTS */ + enum gsm_bts_type type; + struct gsm_bts_model *model; + enum gsm_band band; + /* maximum Tx power that the MS is permitted to use in this cell */ + int ms_max_power; + + /* how do we talk OML with this TRX? */ + struct gsm_e1_subslot oml_e1_link; + uint8_t oml_tei; + struct e1inp_sign_link *oml_link; + + /* Abis network management O&M handle */ + struct abis_nm_h *nmh; + + struct gsm_abis_mo mo; + + /* number of this BTS on given E1 link */ + uint8_t bts_nr; + + /* paging state and control */ + struct gsm_bts_paging_state paging; + + /* CCCH is on C0 */ + struct gsm_bts_trx *c0; + + struct { + struct gsm_abis_mo mo; + } site_mgr; + + /* bitmask of all SI that are present/valid in si_buf */ + uint32_t si_valid; + /* buffers where we put the pre-computed SI */ + sysinfo_buf_t si_buf[_MAX_SYSINFO_TYPE]; + + /* TimeZone hours, mins, and bts specific */ + struct { + int hr; + int mn; + int override; + } tz; + + /* ip.accesss Unit ID's have Site/BTS/TRX layout */ + union { + struct { + uint16_t site_id; + uint16_t bts_id; + uint32_t flags; + uint32_t rsl_ip; + } ip_access; + struct { + struct { + struct gsm_abis_mo mo; + } cclk; + struct { + struct gsm_abis_mo mo; + } rack; + struct gsm_envabtse envabtse[4]; + } bs11; + struct { + struct { + struct gsm_abis_mo mo; + struct llist_head conn_groups; + } is; + struct { + struct gsm_abis_mo mo; + struct llist_head conn_groups; + } con; + struct { + struct gsm_abis_mo mo; + } dp; + struct { + struct gsm_abis_mo mo; + } tf; + } rbs2000; + struct { + uint8_t bts_type; + unsigned int configured:1, + skip_reset:1, + did_reset:1, + wait_reset:1; + struct osmo_timer_list reset_timer; + } nokia; + }; + + /* Not entirely sure how ip.access specific this is */ + struct { + enum bts_gprs_mode mode; + struct { + struct gsm_abis_mo mo; + uint16_t nsei; + uint8_t timer[7]; + } nse; + struct { + struct gsm_abis_mo mo; + uint16_t bvci; + uint8_t timer[11]; + struct gprs_rlc_cfg rlc_cfg; + } cell; + struct gsm_bts_gprs_nsvc nsvc[2]; + uint8_t rac; + uint8_t net_ctrl_ord; + } gprs; + + /* RACH NM values */ + int rach_b_thresh; + int rach_ldavg_slots; + + /* transceivers */ + int num_trx; + struct llist_head trx_list; + +#ifdef ROLE_BSC + /* Abis NM queue */ + struct llist_head abis_queue; + int abis_nm_pend; + + struct gsm_network *network; + + /* should the channel allocator allocate channels from high TRX to TRX0, + * rather than starting from TRX0 and go upwards? */ + int chan_alloc_reverse; + + enum neigh_list_manual_mode neigh_list_manual_mode; + /* parameters from which we build SYSTEM INFORMATION */ + struct { + struct gsm48_rach_control rach_control; + uint8_t ncc_permitted; + struct gsm48_cell_sel_par cell_sel_par; + struct gsm48_si_selection_params cell_ro_sel_par; /* rest octet */ + struct gsm48_cell_options cell_options; + struct gsm48_control_channel_descr chan_desc; + struct bitvec neigh_list; + struct bitvec cell_alloc; + struct bitvec si5_neigh_list; + struct { + /* bitmask large enough for all possible ARFCN's */ + uint8_t neigh_list[1024/8]; + uint8_t cell_alloc[1024/8]; + /* If the user wants a different neighbor list in SI5 than in SI2 */ + uint8_t si5_neigh_list[1024/8]; + } data; + } si_common; + + /* do we use static (user-defined) system information messages? (bitmask) */ + uint32_t si_mode_static; + + /* exclude the BTS from the global RF Lock handling */ + int excl_from_rf_lock; +#endif /* ROLE_BSC */ + void *role; +}; + + +struct gsm_bts *gsm_bts_alloc(void *talloc_ctx); +struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts); + +struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num); + + +const struct value_string gsm_pchant_names[10]; +const struct value_string gsm_pchant_descs[10]; +const struct value_string gsm_lchant_names[6]; +const char *gsm_pchan_name(enum gsm_phys_chan_config c); +enum gsm_phys_chan_config gsm_pchan_parse(const char *name); +const char *gsm_lchant_name(enum gsm_chan_t c); +const char *gsm_chreq_name(enum gsm_chreq_reason_t c); +char *gsm_trx_name(const struct gsm_bts_trx *trx); +char *gsm_ts_name(const struct gsm_bts_trx_ts *ts); +char *gsm_lchan_name(const struct gsm_lchan *lchan); +const char *gsm_lchans_name(enum gsm_lchan_state s); + + +void gsm_abis_mo_reset(struct gsm_abis_mo *mo); + +struct gsm_abis_mo * +gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class, + const struct abis_om_obj_inst *obj_inst); + +struct gsm_nm_state * +gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class, + const struct abis_om_obj_inst *obj_inst); +void * +gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class, + const struct abis_om_obj_inst *obj_inst); + +/* reset the state of all MO in the BTS */ +void gsm_bts_mo_reset(struct gsm_bts *bts); + +uint8_t gsm_ts2chan_nr(const struct gsm_bts_trx_ts *ts, uint8_t lchan_nr); +uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan); + +/* + * help with parsing regexps + */ +int gsm_parse_reg(void *ctx, regex_t *reg, char **str, + int argc, const char **argv) __attribute__ ((warn_unused_result)); + + + +#endif diff --git a/include/openbsc/gsm_subscriber.h b/include/openbsc/gsm_subscriber.h new file mode 100644 index 000000000..6b577b0bf --- /dev/null +++ b/include/openbsc/gsm_subscriber.h @@ -0,0 +1,111 @@ +#ifndef _GSM_SUBSCR_H +#define _GSM_SUBSCR_H + +#include "gsm_data.h" +#include + +#define GSM_IMEI_LENGTH 17 +#define GSM_IMSI_LENGTH 17 +#define GSM_NAME_LENGTH 160 + +#define GSM_EXTENSION_LENGTH 15 /* MSISDN can only be 15 digits length */ +#define GSM_MIN_EXTEN 20000 +#define GSM_MAX_EXTEN 49999 + +#define GSM_SUBSCRIBER_FIRST_CONTACT 0x00000001 +#define tmsi_from_string(str) strtoul(str, NULL, 10) + +#define GSM_SUBSCRIBER_NO_EXPIRATION 0x0 + +struct vty; + +struct gsm_equipment { + long long unsigned int id; + char imei[GSM_IMEI_LENGTH]; + char name[GSM_NAME_LENGTH]; + + struct gsm48_classmark1 classmark1; + uint8_t classmark2_len; + uint8_t classmark2[3]; + uint8_t classmark3_len; + uint8_t classmark3[14]; +}; + +struct gsm_subscriber { + struct gsm_network *net; + long long unsigned int id; + char imsi[GSM_IMSI_LENGTH]; + uint32_t tmsi; + uint16_t lac; + char name[GSM_NAME_LENGTH]; + char extension[GSM_EXTENSION_LENGTH]; + int authorized; + time_t expire_lu; + + /* 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 in_callback; + struct llist_head requests; +}; + +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_get_by_tmsi(struct gsm_network *net, + uint32_t tmsi); +struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net, + const char *imsi); +struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net, + const char *ext); +struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net, + unsigned long long id); +struct gsm_subscriber *subscr_get_or_create(struct gsm_network *net, + const char *imsi); +int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason); +void subscr_put_channel(struct gsm_subscriber *subscr); +void subscr_get_channel(struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *param); +struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_network *net, + uint32_t tmsi); +struct gsm_subscriber *subscr_active_by_imsi(struct gsm_network *net, + const char *imsi); + +int subscr_pending_requests(struct gsm_subscriber *subscr); +int subscr_pending_clear(struct gsm_subscriber *subscr); +int subscr_pending_dump(struct gsm_subscriber *subscr, struct vty *vty); +int subscr_pending_kick(struct gsm_subscriber *subscr); + +char *subscr_name(struct gsm_subscriber *subscr); + +int subscr_purge_inactive(struct gsm_network *net); +void subscr_update_from_db(struct gsm_subscriber *subscr); +void subscr_expire(struct gsm_network *net); +int subscr_update_expire_lu(struct gsm_subscriber *subscr, struct gsm_bts *bts); + +/* internal */ +struct gsm_subscriber *subscr_alloc(void); +extern struct llist_head active_subscribers; + +#endif /* _GSM_SUBSCR_H */ diff --git a/include/openbsc/handover.h b/include/openbsc/handover.h new file mode 100644 index 000000000..bd0d8ad6c --- /dev/null +++ b/include/openbsc/handover.h @@ -0,0 +1,17 @@ +#ifndef _HANDOVER_H +#define _HANDOVER_H + +struct gsm_subscriber_connection; + +/* Hand over the specified logical channel to the specified new BTS. + * This is the main entry point for the actual handover algorithm, + * after it has decided it wants to initiate HO to a specific BTS */ +int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts); + +/* clear any operation for this connection */ +void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan); + +/* Return the old lchan or NULL. This is meant for audio handling */ +struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan); + +#endif /* _HANDOVER_H */ diff --git a/include/openbsc/handover_decision.h b/include/openbsc/handover_decision.h new file mode 100644 index 000000000..81078b05d --- /dev/null +++ b/include/openbsc/handover_decision.h @@ -0,0 +1,7 @@ +#ifndef _HANDOVER_DECISION_H +#define _HANDOVER_DECISION_H + +void on_dso_load_ho_dec(void); + +#endif /* _HANDOVER_DECISION_H */ + diff --git a/include/openbsc/ipaccess.h b/include/openbsc/ipaccess.h new file mode 100644 index 000000000..a25a348b6 --- /dev/null +++ b/include/openbsc/ipaccess.h @@ -0,0 +1,65 @@ +#ifndef _IPACCESS_H +#define _IPACCESS_H + +#include +#include "gsm_subscriber.h" +#include +#include + +struct ipac_msgt_sccp_state { + uint8_t src_ref[3]; + uint8_t dst_ref[3]; + uint8_t trans_id; + uint8_t invoke_id; + char imsi[GSM_IMSI_LENGTH]; + uint8_t data[0]; +} __attribute__((packed)); + +/* + * @add_remove 0 for remove, 1 for add, 3 to asK + * @nr_lacs Number of extra lacs inside this package + * @lac One lac entry + */ +struct ipac_ext_lac_cmd { + uint8_t add_remove; + uint8_t nr_extra_lacs; + uint16_t lac; + uint8_t data[0]; +} __attribute__((packed)); + +/* + * methods for parsing and sending a message + */ +int ipaccess_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd); +void ipaccess_prepend_header(struct msgb *msg, int proto); +void ipaccess_prepend_header_ext(struct msgb *msg, int proto); +int ipaccess_send_pong(int fd); +int ipaccess_send_id_ack(int fd); +int ipaccess_send_id_req(int fd); + +const char *ipaccess_idtag_name(uint8_t tag); +int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len); +int ipaccess_parse_unitid(const char *str, uint16_t *site_id, uint16_t *bts_id, uint16_t *trx_id); + +int ipaccess_drop_oml(struct gsm_bts *bts); +int 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/include/openbsc/meas_rep.h b/include/openbsc/meas_rep.h new file mode 100644 index 000000000..f235df6aa --- /dev/null +++ b/include/openbsc/meas_rep.h @@ -0,0 +1,87 @@ +#ifndef _MEAS_REP_H +#define _MEAS_REP_H + +#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; +}; + +/* RX Level and RX Quality */ +struct gsm_rx_lev_qual { + uint8_t rx_lev; + uint8_t rx_qual; +}; + +/* unidirectional measumrement report */ +struct gsm_meas_rep_unidir { + struct gsm_rx_lev_qual full; + struct gsm_rx_lev_qual sub; +}; + +#define MEAS_REP_F_UL_DTX 0x01 +#define MEAS_REP_F_DL_VALID 0x02 +#define MEAS_REP_F_BA1 0x04 +#define MEAS_REP_F_DL_DTX 0x08 +#define MEAS_REP_F_MS_TO 0x10 +#define MEAS_REP_F_MS_L1 0x20 +#define MEAS_REP_F_FPC 0x40 + +/* parsed uplink and downlink measurement result */ +struct gsm_meas_rep { + /* back-pointer to the logical channel */ + struct gsm_lchan *lchan; + + /* number of the measurement report */ + uint8_t nr; + /* flags, see MEAS_REP_F_* */ + unsigned int flags; + + /* uplink and downlink rxlev, rxqual; full and sub */ + struct gsm_meas_rep_unidir ul; + struct gsm_meas_rep_unidir dl; + + uint8_t bs_power; + uint8_t ms_timing_offset; + struct { + int8_t pwr; /* MS power in dBm */ + uint8_t ta; /* MS timing advance */ + } ms_l1; + + /* neighbor measurement reports for up to 6 cells */ + int num_cell; + struct gsm_meas_rep_cell cell[6]; +}; + +enum meas_rep_field { + MEAS_REP_DL_RXLEV_FULL, + MEAS_REP_DL_RXLEV_SUB, + MEAS_REP_DL_RXQUAL_FULL, + MEAS_REP_DL_RXQUAL_SUB, + MEAS_REP_UL_RXLEV_FULL, + MEAS_REP_UL_RXLEV_SUB, + MEAS_REP_UL_RXQUAL_FULL, + MEAS_REP_UL_RXQUAL_SUB, +}; + +/* 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/include/openbsc/mgcp.h b/include/openbsc/mgcp.h new file mode 100644 index 000000000..811dcfda7 --- /dev/null +++ b/include/openbsc/mgcp.h @@ -0,0 +1,203 @@ +/* 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 "debug.h" + +#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; + +#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); + +#define PORT_ALLOC_STATIC 0 +#define PORT_ALLOC_DYNAMIC 1 + +/** + * This holds information on how to allocate ports + */ +struct mgcp_port_range { + int mode; + + /* pre-allocated from a base? */ + int base_port; + + /* dynamically allocated */ + int range_start; + int range_end; + int last_port; +}; + +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_loop; + + int omit_rtcp; + + /* spec handling */ + int force_realloc; + + unsigned int number_endpoints; + struct mgcp_endpoint *endpoints; +}; + +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; + + 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; + + 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; +}; + +/* config management */ +struct mgcp_config *mgcp_config_alloc(void); +int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg); +int mgcp_vty_init(void); +int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg); +void mgcp_free_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); + +/* + * 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); + + +#endif diff --git a/include/openbsc/mgcp_internal.h b/include/openbsc/mgcp_internal.h new file mode 100644 index 000000000..d5bd3ddc6 --- /dev/null +++ b/include/openbsc/mgcp_internal.h @@ -0,0 +1,172 @@ +/* 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 . + * + */ + +#ifndef OPENBSC_MGCP_DATA_H +#define OPENBSC_MGCP_DATA_H + +#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, +}; + +enum mgcp_trunk_type { + MGCP_TRUNK_VIRTUAL, + MGCP_TRUNK_E1, +}; + +struct mgcp_rtp_state { + int initialized; + int patch; + + uint32_t orig_ssrc; + uint32_t ssrc; + + uint16_t base_seq; + uint16_t max_seq; + int seq_offset; + int cycles; + + uint32_t last_timestamp; + int32_t timestamp_offset; + uint32_t jitter; + int32_t transit; +}; + +struct mgcp_rtp_end { + /* statistics */ + unsigned int packets; + unsigned int octets; + struct in_addr addr; + + /* in network byte order */ + int rtp_port, rtcp_port; + + /* per endpoint data */ + int payload_type; + char *fmtp_extra; + + /* + * 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; +}; + +enum mgcp_type { + MGCP_RTP_DEFAULT = 0, + MGCP_RTP_TRANSCODED, +}; + +struct mgcp_endpoint { + int allocated; + uint32_t ci; + char *callid; + char *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; + + /* SSRC/seq/ts patching for loop */ + int allow_patch; + + /* fields for re-transmission */ + char *last_trans; + char *last_response; + + /* tap for the endpoint */ + struct mgcp_rtp_tap taps[MGCP_TAP_COUNT]; +}; + +#define ENDPOINT_NUMBER(endp) abs(endp - endp->tcfg->endpoints) + +struct mgcp_msg_ptr { + unsigned int start; + unsigned int length; +}; + +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_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 *); + + +#endif diff --git a/include/openbsc/misdn.h b/include/openbsc/misdn.h new file mode 100644 index 000000000..9851ad32c --- /dev/null +++ b/include/openbsc/misdn.h @@ -0,0 +1,27 @@ +/* (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/include/openbsc/mncc.h b/include/openbsc/mncc.h new file mode 100644 index 000000000..4a99bb11b --- /dev/null +++ b/include/openbsc/mncc.h @@ -0,0 +1,191 @@ +/* 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 GSM_TCHF_FRAME 0x0300 +#define GSM_TCHF_FRAME_EFR 0x0301 + +#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 2 +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; +}; + +char *get_mncc_name(int value); +void mncc_set_cause(struct gsm_mncc *data, int loc, int val); +void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg); + +/* input from CC code into mncc_builtin */ +int int_mncc_recv(struct gsm_network *net, struct msgb *msg); + +/* input from CC code into mncc_sock */ +int mncc_sock_from_cc(struct gsm_network *net, struct msgb *msg); + +int mncc_sock_init(struct gsm_network *gsmnet); + +#endif diff --git a/include/openbsc/mncc_int.h b/include/openbsc/mncc_int.h new file mode 100644 index 000000000..4f30f08c0 --- /dev/null +++ b/include/openbsc/mncc_int.h @@ -0,0 +1,12 @@ +#ifndef _MNCC_INT_H +#define _MNCC_INT_H + +#include + +struct mncc_int { + uint8_t def_codec[2]; +}; + +extern struct mncc_int mncc_int; + +#endif diff --git a/include/openbsc/network_listen.h b/include/openbsc/network_listen.h new file mode 100644 index 000000000..67d1f4ef7 --- /dev/null +++ b/include/openbsc/network_listen.h @@ -0,0 +1,16 @@ +#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/include/openbsc/openbscdefines.h b/include/openbsc/openbscdefines.h new file mode 100644 index 000000000..c6ac153b8 --- /dev/null +++ b/include/openbsc/openbscdefines.h @@ -0,0 +1,34 @@ +/* + * (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/include/openbsc/osmo_bsc.h b/include/openbsc/osmo_bsc.h new file mode 100644 index 000000000..1d216ac25 --- /dev/null +++ b/include/openbsc/osmo_bsc.h @@ -0,0 +1,51 @@ +/* OpenBSC BSC code */ + +#ifndef OSMO_BSC_H +#define OSMO_BSC_H + +#include "bsc_api.h" + +#define BSS_SEND_USSD 1 + +struct sccp_connection; +struct osmo_msc_data; +struct bsc_msc_connection; + +struct osmo_bsc_sccp_con { + struct llist_head entry; + + int ciphering_handled; + int rtp_port; + + /* SCCP connection realted */ + struct sccp_connection *sccp; + struct osmo_msc_data *msc; + struct osmo_timer_list sccp_it_timeout; + struct osmo_timer_list sccp_cc_timeout; + + struct llist_head sccp_queue; + unsigned int sccp_queue_size; + + struct gsm_subscriber_connection *conn; + uint8_t new_subscriber; +}; + +struct bsc_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); +int bsc_create_new_connection(struct gsm_subscriber_connection *conn, + struct osmo_msc_data *msc); +int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp); + +struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, struct msgb *); +int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg); +int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg); +int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn); + +int bsc_handle_udt(struct osmo_msc_data *msc, struct msgb *msg, unsigned int length); +int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int len); + +int bsc_ctrl_cmds_install(); + +#endif diff --git a/include/openbsc/osmo_bsc_grace.h b/include/openbsc/osmo_bsc_grace.h new file mode 100644 index 000000000..e9c1a0a07 --- /dev/null +++ b/include/openbsc/osmo_bsc_grace.h @@ -0,0 +1,31 @@ +/* + * (C) 2010-2013 by Holger Hans Peter Freyther + * (C) 2010-2013 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#ifndef OSMO_BSC_GRACE_H +#define OSMO_BSC_GRACE_H + +#include "gsm_data.h" + +struct osmo_msc_data; + +int bsc_grace_allow_new_connection(struct gsm_network *net, struct gsm_bts *bts); +int bsc_grace_paging_request(struct gsm_subscriber *sub, int type, struct osmo_msc_data *msc); + +#endif diff --git a/include/openbsc/osmo_bsc_rf.h b/include/openbsc/osmo_bsc_rf.h new file mode 100644 index 000000000..a67e1bda4 --- /dev/null +++ b/include/openbsc/osmo_bsc_rf.h @@ -0,0 +1,64 @@ +#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; + + /* 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/include/openbsc/osmo_msc.h b/include/openbsc/osmo_msc.h new file mode 100644 index 000000000..beb3f5e4c --- /dev/null +++ b/include/openbsc/osmo_msc.h @@ -0,0 +1,11 @@ +/* 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/include/openbsc/osmo_msc_data.h b/include/openbsc/osmo_msc_data.h new file mode 100644 index 000000000..ba93a089d --- /dev/null +++ b/include/openbsc/osmo_msc_data.h @@ -0,0 +1,116 @@ +/* + * Data for the true BSC + * + * (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 . + * + */ + +#ifndef _OSMO_MSC_DATA_H +#define _OSMO_MSC_DATA_H + +#include "bsc_msc.h" + +#include +#include + +#include + +struct osmo_bsc_rf; +struct gsm_network; + +struct gsm_audio_support { + uint8_t hr : 1, + ver : 7; +}; + +enum { + MSC_CON_TYPE_NORMAL, + MSC_CON_TYPE_LOCAL, +}; + +struct osmo_msc_data { + struct llist_head entry; + + /* Back pointer */ + struct gsm_network *network; + + int allow_emerg; + int type; + + /* local call routing */ + char *local_pref; + regex_t local_pref_reg; + + + /* Connection data */ + char *bsc_token; + int ping_timeout; + int pong_timeout; + struct osmo_timer_list ping_timer; + struct osmo_timer_list pong_timer; + struct bsc_msc_connection *msc_con; + int core_ncc; + int core_mcc; + 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; +}; + +/* + * 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; +}; + + +int osmo_bsc_msc_init(struct osmo_msc_data *msc); +int osmo_bsc_sccp_init(struct gsm_network *gsmnet); +int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto); + +int osmo_bsc_audio_init(struct gsm_network *network); + +struct osmo_msc_data *osmo_msc_data_find(struct gsm_network *, int); +struct osmo_msc_data *osmo_msc_data_alloc(struct gsm_network *, int); + + +#endif diff --git a/include/openbsc/paging.h b/include/openbsc/paging.h new file mode 100644 index 000000000..2a10f4ef1 --- /dev/null +++ b/include/openbsc/paging.h @@ -0,0 +1,75 @@ +/* Paging helper and manager.... */ +/* (C) 2009 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#ifndef PAGING_H +#define PAGING_H + +#include +#include + +#include +#include "gsm_data.h" +#include "gsm_subscriber.h" +#include + +/** + * A pending paging request + */ +struct gsm_paging_request { + /* list_head for list of all paging requests */ + struct llist_head entry; + /* the subscriber which we're paging. Later gsm_paging_request + * should probably become a part of the gsm_subscriber struct? */ + struct gsm_subscriber *subscr; + /* back-pointer to the BTS on which we are paging */ + struct gsm_bts *bts; + /* what kind of channel type do we ask the MS to establish */ + int chan_type; + + /* Timer 3113: how long do we try to page? */ + struct osmo_timer_list T3113; + + /* How often did we ask the BTS to page? */ + int attempts; + + /* callback to be called in case paging completes */ + gsm_cbfn *cbfn; + void *cbfn_param; +}; + +/* schedule paging request */ +int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *data); +int paging_request_bts(struct gsm_bts *bts, struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *data); + +/* stop paging requests */ +void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr, + struct gsm_subscriber_connection *conn, + struct msgb *msg); + +/* update paging load */ +void paging_update_buffer_space(struct gsm_bts *bts, uint16_t); + +/* pending paging requests */ +unsigned int paging_pending_requests_nr(struct gsm_bts *bts); + +void *paging_get_data(struct gsm_bts *bts, struct gsm_subscriber *subscr); + +#endif diff --git a/include/openbsc/rest_octets.h b/include/openbsc/rest_octets.h new file mode 100644 index 000000000..56d4e0de8 --- /dev/null +++ b/include/openbsc/rest_octets.h @@ -0,0 +1,132 @@ +#ifndef _REST_OCTETS_H +#define _REST_OCTETS_H + +#include + +/* generate SI1 rest octets */ +int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net); + +struct gsm48_si_selection_params { + uint16_t penalty_time:5, + temp_offs:3, + cell_resel_off:6, + cbq:1, + present:1; +}; + +struct gsm48_si_power_offset { + uint8_t power_offset:2, + present:1; +}; + +struct gsm48_si3_gprs_ind { + uint8_t si13_position:1, + ra_colour:3, + present:1; +}; + +struct gsm48_lsa_params { + uint32_t prio_thr:3, + lsa_offset:3, + mcc:12, + mnc:12; + unsigned int present; +}; + +struct gsm48_si_ro_info { + struct gsm48_si_selection_params selection_params; + struct gsm48_si_power_offset power_offset; + uint8_t si2ter_indicator; + uint8_t early_cm_ctrl; + struct { + uint8_t where:3, + present:1; + } scheduling; + struct gsm48_si3_gprs_ind gprs_ind; + + /* SI 4 specific */ + struct gsm48_lsa_params lsa_params; + uint16_t cell_id; + uint8_t break_ind; /* do we have SI7 + SI8 ? */ +}; + + +/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ +int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3); + +/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ +int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4); + +enum pbcch_carrier_type { + PBCCH_BCCH, + PBCCH_ARFCN, + PBCCH_MAIO +}; + +/* TS 03.60 Chapter 6.3.3.1: Network Mode of Operation */ +enum gprs_nmo { + GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */ + GPRS_NMO_II = 1, /* all paging on CCCH */ + GPRS_NMO_III = 2, /* no paging coordination */ +}; + +/* TS 04.60 12.24 */ +struct gprs_cell_options { + enum gprs_nmo nmo; + /* T3168: wait for packet uplink assignment message */ + uint32_t t3168; /* in milliseconds */ + /* T3192: wait for release of the TBF after reception of the final block */ + uint32_t t3192; /* in milliseconds */ + uint32_t drx_timer_max;/* in seconds */ + uint32_t bs_cv_max; + + uint8_t ext_info_present; + struct { + uint8_t egprs_supported; + uint8_t use_egprs_p_ch_req; + uint8_t bep_period; + uint8_t pfc_supported; + uint8_t dtm_supported; + uint8_t bss_paging_coordination; + } ext_info; +}; + +/* TS 04.60 Table 12.9.2 */ +struct gprs_power_ctrl_pars { + uint8_t alpha; + uint8_t t_avg_w; + uint8_t t_avg_t; + uint8_t pc_meas_chan; + uint8_t n_avg_i; +}; + +struct gsm48_si13_info { + struct gprs_cell_options cell_opts; + struct gprs_power_ctrl_pars pwr_ctrl_pars; + uint8_t bcch_change_mark; + uint8_t si_change_field; + uint8_t pbcch_present; + + union { + struct { + uint8_t rac; + uint8_t spgc_ccch_sup; + uint8_t net_ctrl_ord; + uint8_t prio_acc_thr; + } no_pbcch; + struct { + uint8_t psi1_rep_per; + uint8_t pb; + uint8_t tsc; + uint8_t tn; + enum pbcch_carrier_type carrier_type; + uint16_t arfcn; + uint8_t maio; + } pbcch; + }; +}; + +/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */ +int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13); + +#endif /* _REST_OCTETS_H */ diff --git a/include/openbsc/rrlp.h b/include/openbsc/rrlp.h new file mode 100644 index 000000000..c89402a2d --- /dev/null +++ b/include/openbsc/rrlp.h @@ -0,0 +1,7 @@ +#ifndef _RRLP_H +#define _RRLP_H + +void on_dso_load_rrlp(void); + +#endif /* _RRLP_H */ + diff --git a/include/openbsc/rs232.h b/include/openbsc/rs232.h new file mode 100644 index 000000000..61187ca62 --- /dev/null +++ b/include/openbsc/rs232.h @@ -0,0 +1,9 @@ +#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/include/openbsc/rtp_proxy.h b/include/openbsc/rtp_proxy.h new file mode 100644 index 000000000..26cac0df4 --- /dev/null +++ b/include/openbsc/rtp_proxy.h @@ -0,0 +1,91 @@ +#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 + +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/include/openbsc/sgsn.h b/include/openbsc/sgsn.h new file mode 100644 index 000000000..f7af7509b --- /dev/null +++ b/include/openbsc/sgsn.h @@ -0,0 +1,69 @@ +#ifndef _SGSN_H +#define _SGSN_H + + +#include + +#include +#include + +struct sgsn_config { + /* parsed from config file */ + + char *gtp_statedir; + struct sockaddr_in gtp_listenaddr; + + /* misc */ + struct gprs_ns_inst *nsi; + + int acl_enabled; + struct llist_head imsi_acl; +}; + +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; +}; + +extern struct sgsn_instance *sgsn; + +/* sgsn_vty.c */ + +int sgsn_vty_init(void); +int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg); + +/* sgsn.c */ + +/* Main input function for Gb proxy */ +int sgsn_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci); + + +struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn, + struct sgsn_mm_ctx *mmctx, + uint16_t nsapi, + struct tlv_parsed *tp); +int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx); + +/* gprs_sndcp.c */ + +/* Entry point for the SNSM-ACTIVATE.indication */ +int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi); +/* Entry point for the SNSM-DEACTIVATE.indication */ +int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi); +/* Called by SNDCP when it has received/re-assembled a N-PDU */ +int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi, + struct msgb *msg, uint32_t npdu_len, uint8_t *npdu); +int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi, + void *mmcontext); +int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, + uint8_t *hdr, uint16_t len); + +#endif diff --git a/include/openbsc/signal.h b/include/openbsc/signal.h new file mode 100644 index 000000000..39319f1c9 --- /dev/null +++ b/include/openbsc/signal.h @@ -0,0 +1,244 @@ +/* Generic signalling/notification infrastructure */ +/* (C) 2009-2010 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_PAGING signals */ +enum signal_paging { + S_PAGING_SUCCEEDED, + S_PAGING_EXPIRED, +}; + +/* SS_SMS signals */ +enum signal_sms { + S_SMS_SUBMITTED, /* A SMS has been successfully submitted to us */ + S_SMS_DELIVERED, /* A SMS has been successfully delivered to a MS */ + S_SMS_SMMA, /* A MS tells us it has more space available */ + S_SMS_MEM_EXCEEDED, /* A MS tells us it has no more space available */ + S_SMS_UNKNOWN_ERROR, /* A MS tells us it has an error */ +}; + +/* SS_ABISIP signals */ +enum signal_abisip { + S_ABISIP_CRCX_ACK, + S_ABISIP_MDCX_ACK, + S_ABISIP_DLCX_IND, +}; + +/* SS_NM signals */ +enum signal_nm { + S_NM_SW_ACTIV_REP, /* GSM 12.21 software activated report */ + S_NM_FAIL_REP, /* GSM 12.21 failure event report */ + S_NM_NACK, /* GSM 12.21 various NM_MT_*_NACK happened */ + S_NM_IPACC_NACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_NACK happened */ + S_NM_IPACC_ACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_ACK happened */ + S_NM_IPACC_RESTART_ACK, /* nanoBTS has send a restart ack */ + S_NM_IPACC_RESTART_NACK,/* nanoBTS has send a restart ack */ + S_NM_TEST_REP, /* GSM 12.21 Test Report */ + S_NM_STATECHG_OPER, /* Operational State changed*/ + S_NM_STATECHG_ADM, /* Administrative State changed */ + S_NM_OM2K_CONF_RES, /* OM2K Configuration Result */ +}; + +/* SS_LCHAN signals */ +enum signal_lchan { + /* + * The lchan got freed with an use_count != 0 and error + * recovery needs to be carried out from within the + * signal handler. + */ + S_LCHAN_UNEXPECTED_RELEASE, + S_LCHAN_ACTIVATE_ACK, /* 08.58 Channel Activate ACK */ + S_LCHAN_ACTIVATE_NACK, /* 08.58 Channel Activate NACK */ + S_LCHAN_HANDOVER_COMPL, /* 04.08 Handover Completed */ + S_LCHAN_HANDOVER_FAIL, /* 04.08 Handover Failed */ + S_LCHAN_HANDOVER_DETECT, /* 08.58 Handover Detect */ + S_LCHAN_MEAS_REP, /* 08.58 Measurement Report */ +}; + +/* SS_CHALLOC signals */ +enum signal_challoc { + S_CHALLOC_ALLOC_FAIL, /* allocation of lchan has failed */ + S_CHALLOC_FREED, /* lchan has been successfully freed */ +}; + +/* SS_SUBSCR signals */ +enum signal_subscr { + S_SUBSCR_ATTACHED, + S_SUBSCR_DETACHED, + S_SUBSCR_IDENTITY, /* we've received some identity information */ +}; + +/* SS_SCALL signals */ +enum signal_scall { + S_SCALL_SUCCESS, + S_SCALL_EXPIRED, + S_SCALL_DETACHED, +}; + +/* SS_IPAC_NWL signals */ +enum signal_ipaccess { + S_IPAC_NWL_COMPLETE, +}; + +enum signal_global { + S_GLOBAL_BTS_CLOSE_OM, +}; + +/* SS_RF signals */ +enum signal_rf { + S_RF_OFF, + S_RF_ON, + S_RF_GRACE, +}; + +struct gsm_subscriber; + +struct paging_signal_data { + struct gsm_subscriber *subscr; + struct gsm_bts *bts; + + int paging_result; + + /* NULL in case the paging didn't work */ + struct gsm_subscriber_connection *conn; +}; + +struct scall_signal_data { + struct gsm_subscriber *subscr; + struct gsm_subscriber_connection *conn; + void *data; +}; + +struct ipacc_ack_signal_data { + struct gsm_bts_trx *trx; + uint8_t msg_type; +}; + +struct abis_om2k_mo; + +struct nm_statechg_signal_data { + struct gsm_bts *bts; + uint8_t obj_class; + void *obj; + struct gsm_nm_state *old_state; + struct gsm_nm_state *new_state; + + /* This pointer is vaold for TS 12.21 MO */ + struct abis_om_obj_inst *obj_inst; + /* This pointer is vaold for RBS2000 MO */ + struct abis_om2k_mo *om2k_mo; +}; + +struct nm_om2k_signal_data { + struct gsm_bts *bts; + void *obj; + struct abis_om2k_mo *om2k_mo; + + uint8_t accordance_ind; +}; + +struct nm_nack_signal_data { + struct msgb *msg; + struct gsm_bts *bts; + uint8_t mt; +}; + +struct challoc_signal_data { + struct gsm_bts *bts; + struct gsm_lchan *lchan; + enum gsm_chan_t type; +}; + +struct rf_signal_data { + struct gsm_network *net; +}; + +struct sms_signal_data { + /* The transaction where this occured */ + struct gsm_trans *trans; + /* Can be NULL for SMMA */ + struct gsm_sms *sms; + /* int paging result. Only the ones with > 0 */ + int paging_result; +}; + +struct lchan_signal_data { + /* The lchan the signal happened on */ + struct gsm_lchan *lchan; + /* Measurement reports on this lchan */ + struct gsm_meas_rep *mr; +}; + +/* MSC signals */ +enum signal_msc { + S_MSC_LOST, + S_MSC_CONNECTED, + S_MSC_AUTHENTICATED, +}; + +struct osmo_msc_data; +struct msc_signal_data { + struct osmo_msc_data *data; +}; + +/* SS_CCCH signals */ +enum signal_ccch { + S_CCCH_PAGING_LOAD, + S_CCCH_RACH_LOAD, +}; + +struct ccch_signal_data { + struct gsm_bts *bts; + uint16_t pg_buf_space; + uint16_t rach_slot_count; + uint16_t rach_busy_count; + uint16_t rach_access_count; +}; + +#endif diff --git a/include/openbsc/silent_call.h b/include/openbsc/silent_call.h new file mode 100644 index 000000000..2492903c2 --- /dev/null +++ b/include/openbsc/silent_call.h @@ -0,0 +1,12 @@ +#ifndef _SILENT_CALL_H +#define _SILENT_CALL_H + +struct gsm_subscriber_connection; + +extern int gsm_silent_call_start(struct gsm_subscriber *subscr, + void *data, int type); +extern int gsm_silent_call_stop(struct gsm_subscriber *subscr); +extern int silent_call_rx(struct gsm_subscriber_connection *conn, struct msgb *msg); +extern int silent_call_reroute(struct gsm_subscriber_connection *conn, struct msgb *msg); + +#endif /* _SILENT_CALL_H */ diff --git a/include/openbsc/sms_queue.h b/include/openbsc/sms_queue.h new file mode 100644 index 000000000..2a8bd5850 --- /dev/null +++ b/include/openbsc/sms_queue.h @@ -0,0 +1,17 @@ +#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/include/openbsc/socket.h b/include/openbsc/socket.h new file mode 100644 index 000000000..0fd85f104 --- /dev/null +++ b/include/openbsc/socket.h @@ -0,0 +1,14 @@ +#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/include/openbsc/system_information.h b/include/openbsc/system_information.h new file mode 100644 index 000000000..6a5684821 --- /dev/null +++ b/include/openbsc/system_information.h @@ -0,0 +1,10 @@ +#ifndef _SYSTEM_INFO_H +#define _SYSTEM_INFO_H + +#include + +struct gsm_bts; + +int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type type); + +#endif diff --git a/include/openbsc/token_auth.h b/include/openbsc/token_auth.h new file mode 100644 index 000000000..47dc7aa94 --- /dev/null +++ b/include/openbsc/token_auth.h @@ -0,0 +1,7 @@ +#ifndef _TOKEN_AUTH_H +#define _TOKEN_AUTH_H + +void on_dso_load_token(void); + +#endif /* _TOKEN_AUTH_H */ + diff --git a/include/openbsc/transaction.h b/include/openbsc/transaction.h new file mode 100644 index 000000000..b6c859c71 --- /dev/null +++ b/include/openbsc/transaction.h @@ -0,0 +1,75 @@ +#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; + + /* 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 gsm_network **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 *subscr, + 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_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_subscriber *subscr, + uint8_t protocol, uint8_t ti_flag); +int trans_has_conn(const struct gsm_subscriber_connection *conn); + +#endif diff --git a/include/openbsc/trau_mux.h b/include/openbsc/trau_mux.h new file mode 100644 index 000000000..2c01b06db --- /dev/null +++ b/include/openbsc/trau_mux.h @@ -0,0 +1,55 @@ +/* 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 + +/* 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); + +/* callback invoked if we receive TRAU frames */ +int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv); diff --git a/include/openbsc/trau_upqueue.h b/include/openbsc/trau_upqueue.h new file mode 100644 index 000000000..ecc765843 --- /dev/null +++ b/include/openbsc/trau_upqueue.h @@ -0,0 +1,7 @@ +#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/include/openbsc/ussd.h b/include/openbsc/ussd.h new file mode 100644 index 000000000..266546811 --- /dev/null +++ b/include/openbsc/ussd.h @@ -0,0 +1,10 @@ +#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/include/openbsc/vty.h b/include/openbsc/vty.h new file mode 100644 index 000000000..183fc2510 --- /dev/null +++ b/include/openbsc/vty.h @@ -0,0 +1,49 @@ +#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; +extern struct cmd_element ournode_exit_cmd; +extern struct cmd_element ournode_end_cmd; + +enum bsc_vty_node { + GSMNET_NODE = _LAST_OSMOVTY_NODE + 1, + BTS_NODE, + TRX_NODE, + TS_NODE, + SUBSCR_NODE, + MGCP_NODE, + GBPROXY_NODE, + SGSN_NODE, + OML_NODE, + NAT_NODE, + NAT_BSC_NODE, + MSC_NODE, + OM2K_NODE, + TRUNK_NODE, + PGROUP_NODE, + MNCC_INT_NODE, + BSC_NODE, + SMPP_NODE, + SMPP_ESME_NODE, +}; + +extern int bsc_vty_is_config_node(struct vty *vty, int node); +extern void bsc_replace_string(void *ctx, char **dst, const char *newstr); + +struct log_info; +int bsc_vty_init(const struct log_info *cat); +int bsc_vty_init_extra(void); + +#endif diff --git a/openbsc.pc.in b/openbsc.pc.in new file mode 100644 index 000000000..aba07e296 --- /dev/null +++ b/openbsc.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/ + +Name: OpenBSC +Description: OpenBSC base station controller +Requires: +Version: @VERSION@ +Libs: -L${libdir} -lopenbsc +Cflags: -I${includedir} diff --git a/openbsc/AUTHORS b/openbsc/AUTHORS deleted file mode 100644 index daf60e447..000000000 --- a/openbsc/AUTHORS +++ /dev/null @@ -1,7 +0,0 @@ -Harald Welte -Holger Freyther -Jan Luebbe -Stefan Schmidt -Daniel Willmann -Andreas Eversberg -Sylvain Munaut <246tnt@gmail.com> diff --git a/openbsc/COPYING b/openbsc/COPYING deleted file mode 100644 index dba13ed2d..000000000 --- a/openbsc/COPYING +++ /dev/null @@ -1,661 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. diff --git a/openbsc/Makefile.am b/openbsc/Makefile.am deleted file mode 100644 index d7329ba33..000000000 --- a/openbsc/Makefile.am +++ /dev/null @@ -1,14 +0,0 @@ -AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 - -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -SUBDIRS = doc include src tests - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = openbsc.pc - -BUILT_SOURCES = $(top_srcdir)/.version -EXTRA_DIST = git-version-gen osmoappdesc.py -$(top_srcdir)/.version: - echo $(VERSION) > $@-t && mv $@-t $@ -dist-hook: - echo $(VERSION) > $(distdir)/.tarball-version diff --git a/openbsc/README b/openbsc/README deleted file mode 100644 index fa01ff4cd..000000000 --- a/openbsc/README +++ /dev/null @@ -1,32 +0,0 @@ -About OpenBSC -============= - -OpenBSC is a minimalistic implementation of the GSM Network, with -particular emphasis on the functionality typically provided by the BSC, -MSC, HLR, VLR and SMSC. - -Its currently supported interfaces towards the BTS are: - - * Classic A-bis over E1 using a mISDN based E1 interface. In other - words, you can connect existing GSM Base Transceiver Station (BTS) - through E1 to OpenBSC. So far, we have only tested the Siemens BS-11 - Test reports with other BTS are much appreciated! - - * A-bis over IP as used by the ip.access nanoBTS product family - -You can find the project documentation at http://openbsc.gnumonks.org/ - -This project is still in its early days, and there are lots of areas where it -doesn't behave as per GSM spec. - - Harald Welte - - -libosmocore -=========== - -Please note that as of March 2010, OpenBSC has a dependency to a library -called "libosmocore". You can obtain that library from - - git://git.osmocom.org/libosmocore.git - diff --git a/openbsc/configure.ac b/openbsc/configure.ac deleted file mode 100644 index 0227d2698..000000000 --- a/openbsc/configure.ac +++ /dev/null @@ -1,167 +0,0 @@ -dnl Process this file with autoconf to produce a configure script -AC_INIT([openbsc], - m4_esyscmd([./git-version-gen .tarball-version]), - [openbsc-devel@lists.openbsc.org]) - -AM_INIT_AUTOMAKE([dist-bzip2]) -AC_CONFIG_TESTDIR(tests) - -dnl kernel style compile messages -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) - -dnl checks for programs -AC_PROG_MAKE_SET -AC_PROG_CC -AC_PROG_INSTALL -AC_PROG_RANLIB - -dnl checks for libraries -AC_SEARCH_LIBS(crypt, crypt, - [LIBCRYPT="-lcrypt"; AC_DEFINE([VTY_CRYPT_PW], [], [Use crypt functionality of vty.])]) - -PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.5.3.60) -PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0) -PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.6.0) -PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.1.0) -PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.2) - -# Enabke/disable the NAT? -AC_ARG_ENABLE([nat], [AS_HELP_STRING([--enable-nat], [Build the BSC NAT. Requires SCCP])], - [osmo_ac_build_nat="$enableval"],[osmo_ac_build_nat="no"]) -if test "$osmo_ac_build_nat" = "yes" ; then - PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.0.2) -fi -AM_CONDITIONAL(BUILD_NAT, test "x$osmo_ac_build_nat" = "xyes") -AC_SUBST(osmo_ac_build_nat) - -# Enable/disable the BSC? -AC_ARG_ENABLE([osmo-bsc], [AS_HELP_STRING([--enable-osmo-bsc], [Build the Osmo BSC])], - [osmo_ac_build_bsc="$enableval"]) -if test "$osmo_ac_build_bsc" = "yes" ; then - PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.0.6) -fi -AM_CONDITIONAL(BUILD_BSC, test "x$osmo_ac_build_bsc" = "xyes") - -# Enable/disable smpp support in the nitb? -AC_ARG_ENABLE([smpp], [AS_HELP_STRING([--enable-smpp], [Build the SMPP interface])], - [osmo_ac_build_smpp="$enableval"],[osmo_ac_build_smpp="no"]) -if test "$osmo_ac_build_smpp" = "yes" ; then - PKG_CHECK_MODULES(LIBSMPP34, libsmpp34 >= 1.10) - AC_DEFINE(BUILD_SMPP, 1, [Define if we want to build SMPP]) -fi -AM_CONDITIONAL(BUILD_SMPP, test "x$osmo_ac_build_smpp" = "xyes") -AC_SUBST(osmo_ac_build_smpp) - - -found_libgtp=yes -PKG_CHECK_MODULES(LIBGTP, libgtp, , found_libgtp=no) -AM_CONDITIONAL(HAVE_LIBGTP, test "$found_libgtp" = yes) - -dnl checks for header files -AC_HEADER_STDC -AC_CHECK_HEADERS(dahdi/user.h,,AC_MSG_WARN(DAHDI input driver will not be built)) -AC_CHECK_HEADERS(dbi/dbd.h,,AC_MSG_ERROR(DBI library is not installed)) - - -dnl Checks for typedefs, structures and compiler characteristics - -# The following test is taken from WebKit's webkit.m4 -saved_CFLAGS="$CFLAGS" -CFLAGS="$CFLAGS -fvisibility=hidden " -AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) -AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])], - [ AC_MSG_RESULT([yes]) - SYMBOL_VISIBILITY="-fvisibility=hidden"], - AC_MSG_RESULT([no])) -CFLAGS="$saved_CFLAGS" -AC_SUBST(SYMBOL_VISIBILITY) - -# Coverage build taken from WebKit's configure.in -AC_MSG_CHECKING([whether to enable code coverage support]) -AC_ARG_ENABLE(coverage, - AC_HELP_STRING([--enable-coverage], - [enable code coverage support [default=no]]), - [],[enable_coverage="no"]) -AC_MSG_RESULT([$enable_coverage]) -if test "$enable_coverage" = "yes"; then - COVERAGE_CFLAGS="-ftest-coverage -fprofile-arcs" - COVERAGE_LDFLAGS="-ftest-coverage -fprofile-arcs" - AC_SUBST([COVERAGE_CFLAGS]) - AC_SUBST([COVERAGE_LDFLAGS]) -fi - -AC_DEFUN([CHECK_TM_INCLUDES_TM_GMTOFF], [ - AC_CACHE_CHECK( - [whether struct tm has tm_gmtoff member], - osmo_cv_tm_includes_tm_gmtoff, - [AC_LINK_IFELSE([ - AC_LANG_PROGRAM([ - #include - ], [ - time_t t = time(NULL); - struct tm* lt = localtime(&t); - int off = lt->tm_gmtoff; - ]) - ], - osmo_cv_tm_includes_tm_gmtoff=yes, - osmo_cv_tm_includes_tm_gmtoff=no - )] - ) - if test "x$osmo_cv_tm_includes_tm_gmtoff" = xyes; then - AC_DEFINE(HAVE_TM_GMTOFF_IN_TM, 1, - [Define if struct tm has tm_gmtoff member.]) - fi -]) - -CHECK_TM_INCLUDES_TM_GMTOFF - -AC_ARG_ENABLE([vty_tests], - AC_HELP_STRING([--enable-vty-tests], - [Include the VTY tests in make check [default=no]]), - [enable_vty_tests="$enableval"],[enable_vty_tests="no"]) -if test "x$enable_vty_tests" = "xyes" ; then - AM_PATH_PYTHON - AC_CHECK_PROG(OSMOTESTVTY_CHECK,osmotestvty.py,yes) - if test "x$OSMOTESTVTY_CHECK" != "xyes" ; then - AC_MSG_ERROR([Please install osmocom-python to run the vty tests.]) - fi -fi -AC_MSG_CHECKING([whether to enable VTY tests]) -AC_MSG_RESULT([$enable_vty_tests]) -AM_CONDITIONAL(ENABLE_VTY_TESTS, test "x$enable_vty_tests" = "xyes") - -dnl Generate the output -AM_CONFIG_HEADER(bscconfig.h) - -AC_OUTPUT( - openbsc.pc - include/openbsc/Makefile - include/Makefile - src/Makefile - src/libtrau/Makefile - src/libbsc/Makefile - src/libctrl/Makefile - src/libmsc/Makefile - src/libmgcp/Makefile - src/libcommon/Makefile - src/osmo-nitb/Makefile - src/osmo-bsc/Makefile - src/osmo-bsc_nat/Makefile - src/osmo-bsc_mgcp/Makefile - src/ipaccess/Makefile - src/utils/Makefile - src/gprs/Makefile - tests/Makefile - tests/atlocal - tests/gsm0408/Makefile - tests/db/Makefile - tests/channel/Makefile - tests/bsc-nat/Makefile - tests/mgcp/Makefile - tests/gprs/Makefile - tests/si/Makefile - tests/abis/Makefile - tests/smpp/Makefile - doc/Makefile - doc/examples/Makefile - Makefile) diff --git a/openbsc/contrib/a-link/sccp-split-by-con.lua b/openbsc/contrib/a-link/sccp-split-by-con.lua deleted file mode 100644 index f5d5502ae..000000000 --- a/openbsc/contrib/a-link/sccp-split-by-con.lua +++ /dev/null @@ -1,170 +0,0 @@ --- Split trace based on SCCP Source --- There are still bugs to find... bugs bugs bugs... hmm -do - local function init_listener() - print("CREATED LISTENER") - local tap = Listener.new("ip", "sccp && (ip.src == 172.16.1.81 || ip.dst == 172.16.1.81)") - local sccp_type_field = Field.new("sccp.message_type") - local sccp_src_field = Field.new("sccp.slr") - local sccp_dst_field = Field.new("sccp.dlr") - local msg_type_field = Field.new("gsm_a.dtap_msg_mm_type") - local lu_rej_field = Field.new("gsm_a.dtap.rej_cause") - local ip_src_field = Field.new("ip.src") - local ip_dst_field = Field.new("ip.dst") - - -- - local bssmap_msgtype_field = Field.new("gsm_a.bssmap_msgtype") - -- assignment failure 0x03 - -- - - -- - local dtap_cause_field = Field.new("gsm_a_dtap.cause") - local dtap_cc_field = Field.new("gsm_a.dtap_msg_cc_type") - - local connections = {} - - function check_failure(con) - check_lu_reject(con) - check_disconnect(con) - check_failures(con) - end - - -- cipher mode reject - function check_failures(con) - local msgtype = bssmap_msgtype_field() - if not msgtype then - return - end - - msgtype = tonumber(msgtype) - if msgtype == 89 then - print("Cipher mode reject") - con[4] = true - elseif msgtype == 0x03 then - print("Assignment failure") - con[4] = true - elseif msgtype == 0x22 then - print("Clear Request... RF failure?") - con[4] = true - end - end - - -- check if a DISCONNECT is normal - function check_disconnect(con) - local msg_type = dtap_cc_field() - if not msg_type then - return - end - - if tonumber(msg_type) ~= 0x25 then - return - end - - local cause = dtap_cause_field() - if not cause then - return - end - - cause = tonumber(cause) - if cause ~= 0x10 then - print("DISCONNECT != Normal") - con[4] = true - end - end - - -- check if we have a LU Reject - function check_lu_reject(con) - local msg_type = msg_type_field() - if not msg_type then - return - end - - msg_type = tonumber(tostring(msg_type)) - if msg_type == 0x04 then - print("LU REJECT with " .. tostring(lu_rej_field())) - con[4] = true - end - end - - function tap.packet(pinfo,tvb,ip) - local ip_src = tostring(ip_src_field()) - local ip_dst = tostring(ip_dst_field()) - local sccp_type = tonumber(tostring(sccp_type_field())) - local sccp_src = sccp_src_field() - local sccp_dst = sccp_dst_field() - - local con - - if sccp_type == 0x01 then - elseif sccp_type == 0x2 then - local src = string.format("%s-%s", ip_src, tostring(sccp_src)) - local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) - local datestring = os.date("%Y%m%d%H%M%S") - local pcap_name = string.format("alink_trace_%s-%s_%s.pcap", src, dst, datestring) - local dumper = Dumper.new_for_current(pcap_name) - - local con = { ip_src, tostring(sccp_src), tostring(sccp_dst), false, dumper, pcap_name } - - dumper:dump_current() - connections[src] = con - connections[dst] = con - elseif sccp_type == 0x4 then - -- close a connection... remove it from the list - local src = string.format("%s-%s", ip_src, tostring(sccp_src)) - local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) - - local con = connections[src] - if not con then - return - end - - con[5]:dump_current() - con[5]:flush() - - -- this causes a crash on unpacted wireshark - con[5]:close() - - -- the connection had a failure - if con[4] == true then - local datestring = os.date("%Y%m%d%H%M%S") - local new_name = string.format("alink_failure_%s_%s-%s.pcap", datestring, con[2], con[3]) - os.rename(con[6], new_name) - else - os.remove(con[6]) - end - - - -- clear the old connection - connections[src] = nil - connections[dst] = nil - - elseif sccp_type == 0x5 then - -- not handled yet... we should verify stuff here... - local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) - local con = connections[dst] - if not con then - return - end - con[5]:dump_current() - elseif sccp_type == 0x6 then - local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) - local con = connections[dst] - if not con then - print("DON'T KNOW THIS CONNECTION for " .. ip_dst) - return - end - con[5]:dump_current() - check_failure(con) - end - - end - function tap.draw() - print("DRAW") - end - function tap.reset() - print("RESET") - end - end - - init_listener() -end diff --git a/openbsc/contrib/bsc-test/README b/openbsc/contrib/bsc-test/README deleted file mode 100644 index adb222e21..000000000 --- a/openbsc/contrib/bsc-test/README +++ /dev/null @@ -1 +0,0 @@ -Some crazy scripts call testing... and MSC link failure simulation diff --git a/openbsc/contrib/bsc-test/all_dial b/openbsc/contrib/bsc-test/all_dial deleted file mode 100644 index 96e5f00b3..000000000 --- a/openbsc/contrib/bsc-test/all_dial +++ /dev/null @@ -1,8 +0,0 @@ -ABORT BUSY -ABORT 'NO CARRIER' -ABORT 'OK' - -'' AT -SAY "Dialing a number\n" -'OK' ATD05660066; - diff --git a/openbsc/contrib/bsc-test/dial.sh b/openbsc/contrib/bsc-test/dial.sh deleted file mode 100755 index e5e19f63e..000000000 --- a/openbsc/contrib/bsc-test/dial.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# Evil dial script.. - -while true; -do - chat -v -f all_dial < /dev/ttyACM0 > /dev/ttyACM0 - sleep 5s - chat -v -f hangup < /dev/ttyACM0 > /dev/ttyACM0 - sleep 2s -done - diff --git a/openbsc/contrib/bsc-test/drop-oml.sh b/openbsc/contrib/bsc-test/drop-oml.sh deleted file mode 100755 index 84eead7b7..000000000 --- a/openbsc/contrib/bsc-test/drop-oml.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -sleep 3 -echo "enable" -sleep 1 -echo "drop bts connection 0 oml" -sleep 1 diff --git a/openbsc/contrib/bsc-test/drop.sh b/openbsc/contrib/bsc-test/drop.sh deleted file mode 100755 index c7b66ba72..000000000 --- a/openbsc/contrib/bsc-test/drop.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -while true; -do - echo "Going to drop the OML connection" - ./drop-oml.sh | telnet 127.0.0.1 4242 - sleep 58m -done diff --git a/openbsc/contrib/bsc-test/hangup b/openbsc/contrib/bsc-test/hangup deleted file mode 100644 index cad6870fd..000000000 --- a/openbsc/contrib/bsc-test/hangup +++ /dev/null @@ -1,4 +0,0 @@ -TIMEOUT 10 -'' ^Z -SAY "Waiting for hangup confirm\n" -'' ATH; diff --git a/openbsc/contrib/bsc-test/msc.sh b/openbsc/contrib/bsc-test/msc.sh deleted file mode 100755 index bec011d4c..000000000 --- a/openbsc/contrib/bsc-test/msc.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -while true; -do - echo "Kill the osmo-bsc" - /usr/bin/kill -s SIGUSR2 `pidof osmo-bsc` - sleep 58s -done diff --git a/openbsc/contrib/bsc_control.py b/openbsc/contrib/bsc_control.py deleted file mode 100755 index 9dc01e3bf..000000000 --- a/openbsc/contrib/bsc_control.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/python - -import sys,os -from optparse import OptionParser -import socket -import struct - -verbose = False - -def prefix_ipa_ctrl_header(data): - return struct.pack(">HBB", len(data)+1, 0xee, 0) + data - -def remove_ipa_ctrl_header(data): - if (len(data) < 4): - raise BaseException("Answer too short!") - (plen, ipa_proto, osmo_proto) = struct.unpack(">HBB", data[:4]) - if (plen + 3 > len(data)): - print "Warning: Wrong payload length (expected %i, got %i)" % (plen, len(data) - 3) - if (ipa_proto != 0xee or osmo_proto != 0): - raise BaseException("Wrong protocol in answer!") - - return data[4:plen+3], data[plen+3:] - -def connect(host, port): - if verbose: - print "Connecting to host %s:%i" % (host, port) - - sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sck.setblocking(1) - sck.connect((host, port)) - return sck - -def send(sck, data): - if verbose: - print "Sending \"%s\"" %(data) - data = prefix_ipa_ctrl_header(data) - sck.send(data) - -def do_set(var, value, id, sck): - setmsg = "SET %s %s %s" %(options.id, var, value) - send(sck, setmsg) - -def do_get(var, id, sck): - getmsg = "GET %s %s" %(options.id, var) - send(sck, getmsg) - -parser = OptionParser("Usage: %prog [options] var [value]") -parser.add_option("-d", "--host", dest="host", - help="connect to HOST", metavar="HOST") -parser.add_option("-p", "--port", dest="port", type="int", - help="use PORT", metavar="PORT", default=4249) -parser.add_option("-g", "--get", action="store_true", - dest="cmd_get", help="perform GET operation") -parser.add_option("-s", "--set", action="store_true", - dest="cmd_set", help="perform SET operation") -parser.add_option("-i", "--id", dest="id", default="1", - help="set id manually", metavar="ID") -parser.add_option("-v", "--verbose", action="store_true", - dest="verbose", help="be verbose", default=False) -parser.add_option("-m", "--monitor", action="store_true", - dest="monitor", help="monitor the connection for traps", default=False) - -(options, args) = parser.parse_args() - -verbose = options.verbose - -if options.cmd_set and options.cmd_get: - parser.error("Get and set options are mutually exclusive!") - -if not (options.cmd_get or options.cmd_set or options.monitor): - parser.error("One of -m, -g, or -s must be set") - -if not (options.host): - parser.error("Destination host and port required!") - -sock = connect(options.host, options.port) - -if options.cmd_set: - if len(args) < 2: - parser.error("Set requires var and value arguments") - do_set(args[0], ' '.join(args[1:]), options.id, sock) - -if options.cmd_get: - if len(args) != 1: - parser.error("Get requires the var argument") - do_get(args[0], options.id, sock) - -data = sock.recv(1024) -while (len(data)>0): - (answer, data) = remove_ipa_ctrl_header(data) - print "Got message:", answer - -if options.monitor: - while (True): - data = sock.recv(1024) - if len(data) == 0: - print "Connection is gone." - break - - while (len(data)>0): - (answer, data) = remove_ipa_ctrl_header(data) - print "Got message:", answer - -sock.close() diff --git a/openbsc/contrib/bt.py b/openbsc/contrib/bt.py deleted file mode 100755 index 1b111efc8..000000000 --- a/openbsc/contrib/bt.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python - -import os - -f = open("unbalanced") -lines = [] -for line in f: - lines.append(line) - -filenames = {} - -output = [] -for line in lines: - if "[0x" in line: - start = line.find("[") - end = line.find("]") - addr = line[start+1:end] - try: - file = filenames[addr] - except KeyError: - r = os.popen("addr2line -fs -e ./bsc_hack %s" % addr) - all = r.read().replace("\n", ",") - file = all - filenames[addr] = file - - line = line.replace(addr, file) - output.append(line) - -g = open("unbalanced.2", "w") -g.write("".join(output)) - - - diff --git a/openbsc/contrib/convert_to_enum.py b/openbsc/contrib/convert_to_enum.py deleted file mode 100755 index bcd6f2cee..000000000 --- a/openbsc/contrib/convert_to_enum.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -# -# Convert ETSI documents to an enum -# - -import re, sys - -def convert(string): - string = string.strip().replace(" ", "").rjust(8, "0") - var = 0 - offset = 7 - for char in string: - assert offset >= 0 - var = var | (int(char) << offset) - offset = offset - 1 - - return var - -def string(name): - name = name.replace(" ", "_") - name = name.replace('"', "") - name = name.replace('/', '_') - name = name.replace('(', '_') - name = name.replace(')', '_') - return "%s_%s" % (sys.argv[2], name.upper()) - -file = open(sys.argv[1]) - - -for line in file: - m = re.match(r"[ \t]*(?P[01 ]+)[ ]+(?P[a-zA-Z /0-9()]+)", line[:-1]) - - if m: - print "\t%s\t\t= %d," % (string(m.groupdict()["name"]), convert(m.groupdict()["value"])) - else: - print line[:-1] diff --git a/openbsc/contrib/gprs/gb-proxy-unblock-bug.py b/openbsc/contrib/gprs/gb-proxy-unblock-bug.py deleted file mode 100755 index 0cd4b871f..000000000 --- a/openbsc/contrib/gprs/gb-proxy-unblock-bug.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python - -""" -demonstrate a unblock bug on the GB Proxy.. -""" - -bts_ns_reset = "\x02\x00\x81\x01\x01\x82\x1f\xe7\x04\x82\x1f\xe7" -ns_reset_ack = "\x03\x01\x82\x1f\xe7\x04\x82\x1f\xe7" - -bts_ns_unblock = "\x06" -ns_unblock_ack = "\x07" - -bts_bvc_reset_0 = "\x00\x00\x00\x00\x22\x04\x82\x00\x00\x07\x81\x03\x3b\x81\x02" -ns_bvc_reset_0_ack = "\x00\x00\x00\x00\x23\x04\x82\x00\x00" - -bts_bvc_reset_8167 = "\x00\x00\x00\x00\x22\x04\x82\x1f\xe7\x07\x81\x08\x08\x88\x72\xf4\x80\x10\x1c\x00\x9c\x40" - - -import socket -socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -socket.bind(("0.0.0.0", 0)) -socket.setblocking(1) - - -import sys -port = int(sys.argv[1]) -print "Sending data to port: %d" % port - -def send_and_receive(packet): - socket.sendto(packet, ("127.0.0.1", port)) - - try: - data, addr = socket.recvfrom(4096) - except socket.error, e: - print "ERROR", e - import sys - sys.exit(0) - return data - -#send stuff once - -to_send = [ - (bts_ns_reset, ns_reset_ack, "reset ack"), - (bts_ns_unblock, ns_unblock_ack, "unblock ack"), - (bts_bvc_reset_0, ns_bvc_reset_0_ack, "BVCI=0 reset ack"), -] - - -for (out, inp, type) in to_send: - res = send_and_receive(out) - if res != inp: - print "Failed to get the %s" % type - sys.exit(-1) - -import time -time.sleep(3) -res = send_and_receive(bts_bvc_reset_8167) -print "Sent all messages... check wireshark for the last response" diff --git a/openbsc/contrib/gprs/gprs-bssgp-histogram.lua b/openbsc/contrib/gprs/gprs-bssgp-histogram.lua deleted file mode 100644 index b1ab5df7f..000000000 --- a/openbsc/contrib/gprs/gprs-bssgp-histogram.lua +++ /dev/null @@ -1,78 +0,0 @@ --- Simple LUA script to print the size of BSSGP messages over their type... - -do - local ip_bucket = {} - - local pdu_types = {} - pdu_types[ 6] = "PAGING" - pdu_types[11] = "SUSPEND" - pdu_types[12] = "SUSPEND-ACK" - pdu_types[32] = "BVC-BLOCK" - pdu_types[33] = "BVC-BLOCK-ACK" - pdu_types[34] = "BVC-RESET" - pdu_types[35] = "BVC-RESET-ACK" - pdu_types[36] = "UNBLOCK" - pdu_types[37] = "UNBLOCK-ACK" - pdu_types[38] = "FLOW-CONTROL-BVC" - pdu_types[39] = "FLOW-CONTROL-BVC-ACK" - pdu_types[40] = "FLOW-CONTROL-MS" - pdu_types[41] = "FLOW-CONTROL-MS-ACK" - pdu_types[44] = "LLC-DISCARDED" - - local function init_listener() - -- handle the port as NS over IP - local udp_port_table = DissectorTable.get("udp.port") - local gprs_ns_dis = Dissector.get("gprs_ns") - udp_port_table:add(23000,gprs_ns_dis) - - -- bssgp filters - local bssgp_pdu_get = Field.new("bssgp.pdu_type") - local udp_length_get = Field.new("udp.length") - - local tap = Listener.new("ip", "udp.port == 23000") - function tap.packet(pinfo,tvb,ip) - local pdu = bssgp_pdu_get() - local len = udp_length_get() - - -- only handle bssgp, but we also want the IP frame - if not pdu then - return - end - - pdu = tostring(pdu) - if tonumber(pdu) == 0 or tonumber(pdu) == 1 then - return - end - - local ip_src = tostring(ip.ip_src) - local bssgp_histo = ip_bucket[ip_src] - if not bssgp_histo then - bssgp_histo = {} - ip_bucket[ip_src] = bssgp_histo - end - - local key = pdu - local bucket = bssgp_histo[key] - if not bucket then - bucket = {} - bssgp_histo[key] = bucket - end - - table.insert(bucket, tostring(len)) - print("IP: " .. ip_src .. " PDU: " .. pdu_types[tonumber(pdu)] .. " Length: " .. tostring(len)) - end - - function tap.draw() - -- well... this will not be called... --- for ip,bssgp_histo in pairs(dumpers) do --- print("IP " .. ip) --- end - end - - function tap.reset() - -- well... this will not be called... - end - end - - init_listener() -end diff --git a/openbsc/contrib/gprs/gprs-buffer-count.lua b/openbsc/contrib/gprs/gprs-buffer-count.lua deleted file mode 100644 index ca8864ad1..000000000 --- a/openbsc/contrib/gprs/gprs-buffer-count.lua +++ /dev/null @@ -1,80 +0,0 @@ --- I count the buffer space needed for LLC PDUs in the worse case and print it - -do - local function init_listener() - -- handle the port as NS over IP - local udp_port_table = DissectorTable.get("udp.port") - local gprs_ns_dis = Dissector.get("gprs_ns") - udp_port_table:add(23000,gprs_ns_dis) - - -- bssgp filters - local bssgp_pdu_get = Field.new("bssgp.pdu_type") - local bssgp_delay_get = Field.new("bssgp.delay_val") - local llcgprs_get = Field.new("llcgprs") - local pdus = nil - - print("START...") - - local tap = Listener.new("ip", "udp.port == 23000 && bssgp.pdu_type == 0") - function tap.packet(pinfo,tvb,ip) - local pdu = bssgp_pdu_get() - local len = llcgprs_get().len - local delay = bssgp_delay_get() - - -- only handle bssgp, but we also want the IP frame - if not pdu then - return - end - - if tonumber(tostring(delay)) == 65535 then - pdus = { next = pdus, - len = len, - expires = -1 } - else - local off = tonumber(tostring(delay)) / 100.0 - pdus = { next = pdus, - len = len, - expires = pinfo.rel_ts + off } - end - local now_time = tonumber(tostring(pinfo.rel_ts)) - local now_size = 0 - local l = pdus - local prev = nil - local count = 0 - while l do - if now_time < l.expires or l.expires == -1 then - now_size = now_size + l.len - prev = l - l = l.next - count = count + 1 - else - -- delete things - if prev == nil then - pdus = nil - l = nil - else - prev.next = l.next - l = l.next - end - end - end --- print("TOTAL: " .. now_time .. " PDU_SIZE: " .. now_size) - print(now_time .. " " .. now_size / 1024.0 .. " " .. count) --- print("NOW: " .. tostring(pinfo.rel_ts) .. " Delay: " .. tostring(delay) .. " Length: " .. tostring(len)) - end - - function tap.draw() - -- well... this will not be called... --- for ip,bssgp_histo in pairs(dumpers) do --- print("IP " .. ip) --- end - print("END") - end - - function tap.reset() - -- well... this will not be called... - end - end - - init_listener() -end diff --git a/openbsc/contrib/gprs/gprs-split-trace-by-tlli.lua b/openbsc/contrib/gprs/gprs-split-trace-by-tlli.lua deleted file mode 100644 index 018c377c5..000000000 --- a/openbsc/contrib/gprs/gprs-split-trace-by-tlli.lua +++ /dev/null @@ -1,46 +0,0 @@ --- Create a file named by_ip/''ip_addess''.cap with all ip traffic of each ip host. (works for tshark only) --- Dump files are created for both source and destination hosts -do - local dir = "by_tlli" - local dumpers = {} - local function init_listener() - local udp_port_table = DissectorTable.get("udp.port") - local gprs_ns_dis = Dissector.get("gprs_ns") - udp_port_table:add(23000,gprs_ns_dis) - - local field_tlli = Field.new("bssgp.tlli") - local tap = Listener.new("ip", "udp.port == 23000") - - -- we will be called once for every IP Header. - -- If there's more than one IP header in a given packet we'll dump the packet once per every header - function tap.packet(pinfo,tvb,ip) - local tlli = field_tlli() - if not tlli then - return - end - - local tlli_str = tostring(tlli) - tlli_dmp = dumpers[tlli_str] - if not tlli_dmp then - local tlli_hex = string.format("0x%x", tonumber(tlli_str)) - print("Creating dump for TLLI " .. tlli_hex) - tlli_dmp = Dumper.new_for_current(dir .. "/" .. tlli_hex .. ".pcap") - dumpers[tlli_str] = tlli_dmp - end - tlli_dmp:dump_current() - tlli_dmp:flush() - end - function tap.draw() - for tlli,dumper in pairs(dumpers) do - dumper:flush() - end - end - function tap.reset() - for tlli,dumper in pairs(dumpers) do - dumper:close() - end - dumpers = {} - end - end - init_listener() -end diff --git a/openbsc/contrib/gprs/gprs-verify-nu.lua b/openbsc/contrib/gprs/gprs-verify-nu.lua deleted file mode 100644 index e44fdd16f..000000000 --- a/openbsc/contrib/gprs/gprs-verify-nu.lua +++ /dev/null @@ -1,59 +0,0 @@ --- This script verifies that the N(U) is increasing... --- -do - local nu_state_src = {} - - local function init_listener() - -- handle the port as NS over IP - local udp_port_table = DissectorTable.get("udp.port") - local gprs_ns_dis = Dissector.get("gprs_ns") - udp_port_table:add(23000,gprs_ns_dis) - - -- we want to look here... - local llc_sapi_get = Field.new("llcgprs.sapib") - local llc_nu_get = Field.new("llcgprs.nu") - local bssgp_tlli_get = Field.new("bssgp.tlli") - - local tap = Listener.new("ip", "udp.port == 23000") - function tap.packet(pinfo,tvb,ip) - local llc_sapi = llc_sapi_get() - local llc_nu = llc_nu_get() - local bssgp_tlli = bssgp_tlli_get() - - if not llc_sapi or not llc_nu or not bssgp_tlli then - return - end - - local ip_src = tostring(ip.ip_src) - local bssgp_tlli = tostring(bssgp_tlli) - local llc_nu = tostring(llc_nu) - local llc_sapi = tostring(llc_sapi) - - local src_key = ip_src .. "-" .. bssgp_tlli .. "-" .. llc_sapi - local last_nu = nu_state_src[src_key] - if not last_nu then - -- print("Establishing mapping for " .. src_key) - nu_state_src[src_key] = llc_nu - return - end - - local function tohex(number) - return string.format("0x%x", tonumber(number)) - end - - nu_state_src[src_key] = llc_nu - if tonumber(last_nu) + 1 ~= tonumber(llc_nu) then - print("JUMP in N(U) on TLLI " .. tohex(bssgp_tlli) .. " and SAPI: " .. llc_sapi .. " src: " .. ip_src) - print("\t last: " .. last_nu .. " now: " .. llc_nu) - end - end - - function tap.draw() - end - - function tap.reset() - end - end - init_listener() -end - diff --git a/openbsc/contrib/hlr-remove-old.sql b/openbsc/contrib/hlr-remove-old.sql deleted file mode 100644 index 626a331e1..000000000 --- a/openbsc/contrib/hlr-remove-old.sql +++ /dev/null @@ -1,18 +0,0 @@ --- Remove old data from the database -DELETE FROM Subscriber - WHERE id != 1 AND datetime('now', '-10 days') > updated AND authorized != 1; -DELETE FROM Equipment - WHERE datetime('now', '-10 days') > updated; -DELETE FROM EquipmentWatch - WHERE datetime('now', '-10 days') > updated; -DELETE FROM SMS - WHERE datetime('now', '-10 days') > created; -DELETE FROM VLR - WHERE datetime('now', '-10 days') > updated; -DELETE FROM ApduBlobs - WHERE datetime('now', '-10 days') > created; -DELETE FROM Counters - WHERE datetime('now', '-10 days') > timestamp; -DELETE FROM RateCounters - WHERE datetime('now', '-10 days') > timestamp; -VACUUM; diff --git a/openbsc/contrib/mgcp_server.py b/openbsc/contrib/mgcp_server.py deleted file mode 100755 index 05c489db5..000000000 --- a/openbsc/contrib/mgcp_server.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python -# Simple server for mgcp... send audit, receive response.. - -import socket, time - -MGCP_GATEWAY_PORT = 2427 -MGCP_CALLAGENT_PORT = 2727 - -rsip_resp = """200 321321332\r\n""" -audit_packet = """AUEP %d 13@mgw MGCP 1.0\r\n""" -crcx_packet = """CRCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n""" -dlcx_packet = """DLCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\n""" -mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 6666 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n""" - -def hexdump(src, length=8): - """Recipe is from http://code.activestate.com/recipes/142812/""" - result = [] - digits = 4 if isinstance(src, unicode) else 2 - for i in xrange(0, len(src), length): - s = src[i:i+length] - hexa = b' '.join(["%0*X" % (digits, ord(x)) for x in s]) - text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s]) - result.append( b"%04X %-*s %s" % (i, length*(digits + 1), hexa, text) ) - return b'\n'.join(result) - -server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -server_socket.bind(("127.0.0.1", MGCP_CALLAGENT_PORT)) -server_socket.setblocking(1) - -last_ci = 1 -def send_and_receive(packet): - global last_ci - server_socket.sendto(packet, ("127.0.0.1", MGCP_GATEWAY_PORT)) - try: - data, addr = server_socket.recvfrom(4096) - - # attempt to store the CI of the response - list = data.split("\n") - for item in list: - if item.startswith("I: "): - last_ci = int(item[3:]) - - print hexdump(data), addr - except socket.error, e: - print e - pass - -def generate_tid(): - import random - return random.randint(0, 65123) - - - -while True: - send_and_receive(audit_packet % generate_tid()) - send_and_receive(crcx_packet % generate_tid() ) - send_and_receive(mdcx_packet % (generate_tid(), last_ci)) - send_and_receive(dlcx_packet % (generate_tid(), last_ci)) - - time.sleep(3) diff --git a/openbsc/contrib/nat/test_regexp.c b/openbsc/contrib/nat/test_regexp.c deleted file mode 100644 index 808a703ca..000000000 --- a/openbsc/contrib/nat/test_regexp.c +++ /dev/null @@ -1,30 +0,0 @@ -/* make test_regexp */ -#include -#include -#include - - -int main(int argc, char **argv) -{ - regex_t reg; - regmatch_t matches[2]; - - if (argc != 4) { - printf("Invoke with: test_regexp REGEXP REPLACE NR\n"); - return -1; - } - - if (regcomp(®, argv[1], REG_EXTENDED) != 0) { - fprintf(stderr, "Regexp '%s' is not valid.\n", argv[1]); - return -1; - } - - if (regexec(®, argv[3], 2, matches, 0) == 0 && matches[1].rm_eo != -1) - printf("New Number: %s%s\n", argv[2], &argv[3][matches[1].rm_so]); - else - printf("No match.\n"); - - regfree(®); - - return 0; -} diff --git a/openbsc/contrib/rtp/rtp_replay.st b/openbsc/contrib/rtp/rtp_replay.st deleted file mode 100644 index a281570cf..000000000 --- a/openbsc/contrib/rtp/rtp_replay.st +++ /dev/null @@ -1,17 +0,0 @@ -" -Simple UDP replay from the state files -" - -PackageLoader fileInPackage: #Sockets. -FileStream fileIn: 'rtp_replay_shared.st'. - - -Eval [ - | replay | - - - replay := RTPReplay on: 'rtp_ssrc6976010.240.240.1_to_10.240.240.50.state'. - - Transcript nextPutAll: 'Going to stream now'; nl. - replay streamAudio: '127.0.0.1' port: 4000. -] diff --git a/openbsc/contrib/rtp/rtp_replay_shared.st b/openbsc/contrib/rtp/rtp_replay_shared.st deleted file mode 100644 index dd32aed7a..000000000 --- a/openbsc/contrib/rtp/rtp_replay_shared.st +++ /dev/null @@ -1,108 +0,0 @@ -" -Simple UDP replay from the state files -" - -PackageLoader fileInPackage: #Sockets. - -Object subclass: SDPUtils [ - "Look into using PetitParser." - SDPUtils class >> findPort: aSDP [ - aSDP linesDo: [:line | - (line startsWith: 'm=audio ') ifTrue: [ - | stream | - stream := line readStream - skip: 'm=audio ' size; - yourself. - ^ Number readFrom: stream. - ] - ]. - - ^ self error: 'Not found'. - ] - - SDPUtils class >> findHost: aSDP [ - aSDP linesDo: [:line | - (line startsWith: 'c=IN IP4 ') ifTrue: [ - | stream | - ^ stream := line readStream - skip: 'c=IN IP4 ' size; - upToEnd. - ] - ]. - - ^ self error: 'Not found'. - ] -] - -Object subclass: RTPReplay [ - | filename socket | - RTPReplay class >> on: aFile [ - ^ self new - initialize; - file: aFile; yourself - ] - - initialize [ - socket := Sockets.DatagramSocket new. - ] - - file: aFile [ - filename := aFile - ] - - localPort [ - ^ socket port - ] - - streamAudio: aHost port: aPort [ - | file last_time last_image udp_send dest | - - last_time := nil. - last_image := nil. - file := FileStream open: filename. - - "Send the payload" - dest := Sockets.SocketAddress byName: aHost. - udp_send := [:payload | | datagram | - datagram := Sockets.Datagram data: payload contents address: dest port: aPort. - socket nextPut: datagram - ]. - - [file atEnd] whileFalse: [ - | lineStream time data now_image | - lineStream := file nextLine readStream. - - "Read the time, skip the blank, parse the data" - time := Number readFrom: lineStream. - lineStream skip: 1. - - data := WriteStream on: (ByteArray new: 30). - [lineStream atEnd] whileFalse: [ - | hex | - hex := lineStream next: 2. - data nextPut: (Number readFrom: hex readStream radix: 16). - ]. - - last_time isNil - ifTrue: [ - "First time, send it right now" - last_time := time. - last_image := Time millisecondClockValue. - udp_send value: data. - ] - ifFalse: [ - | wait_image new_image_time | - - "How long to wait?" - wait_image := last_image + ((time - last_time) * 1000). - [ wait_image > Time millisecondClockValue ] - whileTrue: [Processor yield]. - - udp_send value: data. - last_time := time. - last_image := wait_image. - ] - ] - ] -] - diff --git a/openbsc/contrib/rtp/rtp_replay_sip.st b/openbsc/contrib/rtp/rtp_replay_sip.st deleted file mode 100644 index 5f844df1d..000000000 --- a/openbsc/contrib/rtp/rtp_replay_sip.st +++ /dev/null @@ -1,87 +0,0 @@ -""" -Create a SIP connection and then stream... -""" - -PackageLoader - fileInPackage: #OsmoSIP. - -"Load for the replay code" -FileStream fileIn: 'rtp_replay_shared.st'. - - -Osmo.SIPCall subclass: StreamCall [ - | sem stream | - - createCall: aSDP [ - | sdp | - stream := RTPReplay on: 'rtp_ssrc6976010.240.240.1_to_10.240.240.50.state'. - sdp := aSDP % {stream localPort}. - ^ super createCall: sdp. - ] - - sem: aSemaphore [ - sem := aSemaphore - ] - - sessionNew [ - | host port | - Transcript nextPutAll: 'The call has started'; nl. - Transcript nextPutAll: sdp_result; nl. - - host := SDPUtils findHost: sdp_result. - port := SDPUtils findPort: sdp_result. - - [ - stream streamAudio: host port: port. - Transcript nextPutAll: 'Streaming has finished.'; nl. - ] fork. - ] - - sessionFailed [ - sem signal - ] - - sessionEnd [ - sem signal - ] -] - -Eval [ - | transport agent call sem sdp_fr sdp_amr | - - - sdp_fr := (WriteStream on: String new) - nextPutAll: 'v=0'; cr; nl; - nextPutAll: 'o=twinkle 1739517580 1043400482 IN IP4 127.0.0.1'; cr; nl; - nextPutAll: 's=-'; cr; nl; - nextPutAll: 'c=IN IP4 127.0.0.1'; cr; nl; - nextPutAll: 't=0 0'; cr; nl; - nextPutAll: 'm=audio %1 RTP/AVP 0 101'; cr; nl; - nextPutAll: 'a=rtpmap:0 PCMU/8000'; cr; nl; - nextPutAll: 'a=rtpmap:101 telephone-event/8000'; cr; nl; - nextPutAll: 'a=fmtp:101 0-15'; cr; nl; - nextPutAll: 'a=ptime:20'; cr; nl; - contents. - - sem := Semaphore new. - transport := Osmo.SIPUdpTransport - startOn: '0.0.0.0' port: 5066. - agent := Osmo.SIPUserAgent createOn: transport. - transport start. - - call := (StreamCall - fromUser: 'sip:1000@sip.zecke.osmocom.org' - host: '127.0.0.1' - port: 5060 - to: 'sip:123456@127.0.0.1' - on: agent) - sem: sem; yourself. - - call createCall: sdp_fr. - - - "Wait for the stream to have ended" - sem wait. - - (Delay forSeconds: 4) wait. -] diff --git a/openbsc/contrib/rtp/timestamp_rtp.lua b/openbsc/contrib/rtp/timestamp_rtp.lua deleted file mode 100644 index c18a06bed..000000000 --- a/openbsc/contrib/rtp/timestamp_rtp.lua +++ /dev/null @@ -1,28 +0,0 @@ -print("Ni hao") - - -do - local tap = Listener.new("ip", "rtp") - local rtp_ssrc = Field.new("rtp.ssrc") - local frame_time = Field.new("frame.time_relative") - local rtp = Field.new("rtp") - - function tap.packet(pinfo, tvb, ip) - local ip_src, ip_dst = tostring(ip.ip_src), tostring(ip.ip_dst) - local rtp_data = rtp() - local filename = "rtp_ssrc" .. rtp_ssrc() "_src_" .. ip_src .. "_to_" .. ip_dst .. ".state" - local f = io.open(filename, "a") - - f:write(tostring(frame_time()) .. " ") - f:write(tostring(rtp_data.value)) - f:write("\n") - f:close() - end - - function tap.draw() - print("DRAW") - end - function tap.reset() - print("RESET") - end -end diff --git a/openbsc/contrib/sms/fill-hlr.st b/openbsc/contrib/sms/fill-hlr.st deleted file mode 100644 index da0643ecf..000000000 --- a/openbsc/contrib/sms/fill-hlr.st +++ /dev/null @@ -1,66 +0,0 @@ -"I create output for some simple SQL statements for the HLR db" - - -Eval [ - -"Create tables if they don't exist" -Transcript show: 'CREATE TABLE SMS ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - created TIMESTAMP NOT NULL, - sent TIMESTAMP, - sender_id INTEGER NOT NULL, - receiver_id INTEGER NOT NULL, - deliver_attempts INTEGER NOT NULL DEFAULT 0, - valid_until TIMESTAMP, - reply_path_req INTEGER NOT NULL, - status_rep_req INTEGER NOT NULL, - protocol_id INTEGER NOT NULL, - data_coding_scheme INTEGER NOT NULL, - ud_hdr_ind INTEGER NOT NULL, - dest_addr TEXT, - user_data BLOB, - header BLOB, - text TEXT);'; nl; - show: 'CREATE TABLE Subscriber ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - created TIMESTAMP NOT NULL, - updated TIMESTAMP NOT NULL, - imsi NUMERIC UNIQUE NOT NULL, - name TEXT, - extension TEXT UNIQUE, - authorized INTEGER NOT NULL DEFAULT 0, - tmsi TEXT UNIQUE, - lac INTEGER NOT NULL DEFAULT 0);'; nl. - -"Create some dummy subscribers" -num_sub := 1000. -num_sms := 30. -lac := 1. - -Transcript show: 'BEGIN;'; nl. - -1 to: num_sub do: [:each | - Transcript show: 'INSERT INTO Subscriber - (imsi, created, updated, authorized, lac, extension) - VALUES - (%1, datetime(''now''), datetime(''now''), 1, %2, %3);' % - {(274090000000000 + each). lac. each}; nl. -]. - -1 to: num_sms do: [:sms | - 1 to: num_sub do: [:sub | - Transcript show: 'INSERT INTO SMS - (created, sender_id, receiver_id, valid_until, - reply_path_req, status_rep_req, protocol_id, - data_coding_scheme, ud_hdr_ind, dest_addr, - text) VALUES - (datetime(''now''), 1, %1, ''2222-2-2'', - 0, 0, 0, - 0, 0, ''123456'', - ''abc'');' % {sub}; nl. - ] -]. - -Transcript show: 'COMMIT;'; nl. - -] diff --git a/openbsc/contrib/sms/hlr-query.st b/openbsc/contrib/sms/hlr-query.st deleted file mode 100644 index bd3f97a4a..000000000 --- a/openbsc/contrib/sms/hlr-query.st +++ /dev/null @@ -1,10 +0,0 @@ -"Query for one SMS" - -Eval [ -1 to: 100 do: [:each | - Transcript show: 'SELECT SMS.* FROM SMS - JOIN Subscriber ON SMS.receiver_id = Subscriber.id - WHERE SMS.id >= 1 AND SMS.sent IS NULL AND Subscriber.lac > 0 - ORDER BY SMS.id LIMIT 1;'; nl. -]. -] diff --git a/openbsc/contrib/sms/sqlite-probe.tap.d b/openbsc/contrib/sms/sqlite-probe.tap.d deleted file mode 100644 index e75cdfcfa..000000000 --- a/openbsc/contrib/sms/sqlite-probe.tap.d +++ /dev/null @@ -1,5 +0,0 @@ -probe process("/usr/lib/libsqlite3.so.0.8.6").function("sqlite3_get_table") -{ - a = user_string($zSql); - printf("sqlite3_get_table called '%s'\n", a); -} diff --git a/openbsc/contrib/systemd/osmo-bsc-mgcp.service b/openbsc/contrib/systemd/osmo-bsc-mgcp.service deleted file mode 100644 index 4b3df2c06..000000000 --- a/openbsc/contrib/systemd/osmo-bsc-mgcp.service +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=OpenBSC MGCP - -[Service] -Type=simple -Restart=always -ExecStart=/usr/bin/osmo-bsc_mgcp -s -c /etc/osmocom/osmo-bsc-mgcp.cfg -Restart=always -RestartSec=2 diff --git a/openbsc/contrib/systemd/osmo-bsc.service b/openbsc/contrib/systemd/osmo-bsc.service deleted file mode 100644 index 772d472a3..000000000 --- a/openbsc/contrib/systemd/osmo-bsc.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=OpenBSC BSC -Requires=osmo-bsc-mgcp.service - -[Service] -Type=simple -Restart=always -ExecStart=/usr/bin/osmo-bsc -c /etc/osmocom/osmo-bsc.cfg -s -Restart=always -RestartSec=2 diff --git a/openbsc/contrib/systemd/osmo-nitb.service b/openbsc/contrib/systemd/osmo-nitb.service deleted file mode 100644 index 203aeb15c..000000000 --- a/openbsc/contrib/systemd/osmo-nitb.service +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=OpenBSC Network In the Box (NITB) - -[Service] -Type=simple -Restart=always -ExecStart=/usr/bin/osmo-nitb -s -C -c /etc/osmocom/osmo-nitb.cfg -l /var/lib/osmocom/hlr.sqlite3 -Restart=always -RestartSec=2 diff --git a/openbsc/doc/BS11-OML.txt b/openbsc/doc/BS11-OML.txt deleted file mode 100644 index e5c3299c9..000000000 --- a/openbsc/doc/BS11-OML.txt +++ /dev/null @@ -1,31 +0,0 @@ -The Siemens BS-11 supports the following additional GSM 12.21 OML operations: - - -CREATE OBJECT - -abis_om_fom_hdr.obj_class can be -A3: -A5: ALCO, BBSIG, CCLK, GPSU, LI, PA -A8: EnvaBTSE -A9: BPORT - -the abis_om_obj_inst.trx_nr field indicates the index of object, whereas the -abis_om_fom_hdr.bts_nr indicates the type of the object. - -enum abis_bs11_objtype { - BS11_OBJ_ALCO = 0x01, - BS11_OBJ_BBSIG = 0x02, /* obj_class: 0,1 */ - BS11_OBJ_TRX1 = 0x03, /* only DEACTIVATE TRX1 */ - BS11_OBJ_CCLK = 0x04, - BS11_OBJ_GPSU = 0x06, - BS11_OBJ_LI = 0x07, - BS11_OBJ_PA = 0x09, /* obj_class: 0, 1*/ -}; - -In case of CREATE ENVABTSE, the abis_om_obj_inst.trx_nr indicates the EnvaBTSEx -number. - -In case of A9 (CREAETE BPORT), the abis_om_obj_inst.bts_nr indicates which BPORT -shall be used. - - diff --git a/openbsc/doc/Makefile.am b/openbsc/doc/Makefile.am deleted file mode 100644 index aee2d7baa..000000000 --- a/openbsc/doc/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -SUBDIRS = examples diff --git a/openbsc/doc/call-routing.txt b/openbsc/doc/call-routing.txt deleted file mode 100644 index 3402f9e33..000000000 --- a/openbsc/doc/call-routing.txt +++ /dev/null @@ -1,25 +0,0 @@ -Call routing in OpenBSC - -Flow of events: - - # MO call initiated by MS, CHANNEL RQD, IMMEDIATE ASSIGN - # MS sends CC SETUP message, we assume already on TCH/H FACCH - # OpenBSC does a subscriber lookup based on the target extension - * If a subscriber is found: - # send CALL PROCEEDING message to MO - # page the MT subscriber and ask itI to ask for TCH/H - # once paging completes, we have the TCH/H for the MT end - # send SETUP to MT - # receive CALL CONFIRMED from MT - # set-up the TRAU mux mapping between the E1 subslots for both TCH/H - # receive ALERTING from MT, route ALERTING to MO - # receive CONNECT from MT, confirm to MT with CONNECT_ACK - # send a CONNECT message to MO, receive CONNECT_ACK from MO - * If subscriber is not found: - # send RELEASE COMPLETE with apropriate cause to MO (1: unalloacated 3: no route) - - - -Thoughts about RR/MM: - -* we allocate RR/MM entities on demand, when we need them diff --git a/openbsc/doc/channel_release.txt b/openbsc/doc/channel_release.txt deleted file mode 100644 index c9cdfebca..000000000 --- a/openbsc/doc/channel_release.txt +++ /dev/null @@ -1,95 +0,0 @@ - -GSM 04.08 7.1.7 / 9.1.7 RR CHANNEL RELESE - -RSL 08.58 3.4 / ? RLL Link Release Request - -RSL 08.58 4.6 / 8.4.5 DEACTivate SACCH - * Deactivate SACCH according to Channel Release Proc 04.08 - * to be sent after RR CHANNEL RELEASE is sent to MS - -RSL 08.58 4.7 / 8.4.14 RF CHANnel RELease - * tells the BTS to release a radio channel - * "when an activated radio channel is no longer needed" - * BTS responds with RF CHANnel RELease ACKnowledge - - -GSM 04.08 3.4.13: RR connection release procedure - -* network sends RR CHANNEL RELEASE to MS on the DCCH - * start T3109 - * deactivate SACCH -* MS disconnects main signalling link (by sending DISC) - * all other data links are disconnected by local end link release -* network receives DISC (BTS sends RLL REL IND to BSC) - * stop T3109 - * start T3111 -* when T3111 times out, the network can reuse the channls -* if T3109 times out, the network deactivates the channels - and can reuse them - * this probably means simply RF CHANnel RELease - - -== Implementation in OpenBSC == - -There are two possible reasons a gsm_subscriber_connection -will be released. One is a network failure, the other is -the completion of an operation/transaction. - -=== Failure === -The BSC API will call the gsm_04_08.c:gsm0408_clear_request callback -and the MSC part will release all transactions, operations and such -and the channels will be released as error case. - -=== Success === -Every time an 'operation' or 'transaction' is finished msc_release_connection -will be called and it will determine if the gsm_subscriber_connection can -be released. - -In case it can be released bsc_api.c:gsm0808_clear will be called -which will release all lchan's associated with the connection. For the -primary channel a SACH Deactivate will be send with the release -reason NORMAL RELEASE. - - -bsc_api.c:gsm0808_clear - * Release a channel used for handover - * Release the primary lchan with normal release, SACH deactivate - -chan_alloc.c:lchan_release(chan, sacch_deactivate, reason) - * Start the release procedure. It is working in steps with callbacks - coming from the abis_rsl.c code. - * Release all SAPI's > 0 as local end (The BTS should send a - REL_CONF a message) - * Send SACH Deactivate on SAPI=0 if required. - * Start T3109 (stop it when the main signalling link is disconnected) - or when the channel released. On timeout start the error handling. - * abis_rsl.c schedules the RSL_MT_RF_CHAN_REL once all SAPI's are - released and after T3111 has timed out or there is an error. - -RX of RELease INDication: - * Calls internal rsl_handle_release which might release the RF. - -RX of RELease CONFirmation: - * Calls internal rsl_handle_release which might release the RF. - -* RX of RF_CHAN_REL_ACK - * call lchan_free() - - -=== Integration with SMS === - -* RX of CP_ERROR or unimplemented MT - * trigger trans_free() which will msc_release_connection() - -* CP TC1* expired while waiting for CP-ACK - * trigger trans_free() which will msc_release_connection() - -* RX of RP_ERROR - * trigger trans_free() which will msc_release_connection() - -* TX of CP-ACK in MT DELIVER - * trigger trans_free() which will msc_release_connection() - -* RX of CP-ACK in MO SUBMIT - * trigger trans_free() which will msc_release_connection() - diff --git a/openbsc/doc/control-interface.txt b/openbsc/doc/control-interface.txt deleted file mode 100644 index b43cafc35..000000000 --- a/openbsc/doc/control-interface.txt +++ /dev/null @@ -1,21 +0,0 @@ -The protocol for the control interface is wrapped inside the ip.access header -with the IPAC_PROTO_OSMO protocol ID (0xee). Inside the ip.access header is -a struct ipaccess_head_ext with protocol ID 0x00 which indicates the control -interface. - -After that the actual protocol is text based: - -* Getting the value of a variable --> GET -<- GET_REPLY -or ERROR - -* Setting the value of a variable --> SET -<- SET_REPLY -or ERROR - -* A value changes which triggers a trap -<- TRAP - - needs to be unique within a connection. '0' is not allowed diff --git a/openbsc/doc/e1-data-model.txt b/openbsc/doc/e1-data-model.txt deleted file mode 100644 index 509004fe9..000000000 --- a/openbsc/doc/e1-data-model.txt +++ /dev/null @@ -1,172 +0,0 @@ -E1 related data model - -This data model describes the physical relationship of the individual -parts in the network, it is not the logical/protocol side of the GSM -network. - -A BTS is connected to the BSC by some physical link. It could be an actual -E1 link, but it could also be abis-over-IP with a mixture of TCP and RTP/UDP. - -To further complicate the fact, multiple BTS can share one such pysical -link. On a single E1 line, we can easily accomodate up to three BTS with -two TRX each. - -Thus, it is best for OpenBSC to have some kind of abstraction layer. The BSC's -view of a BTS connected to it. We call this 'bts_link'. A bts_link can be -* all the TCP and UDP streams of a Abis-over-IP BTS -* a set of E1 timeslots for OML, RSL and TRAU connections on a E1 link -* a serial line exclusively used for OML messages (T-Link) - -A bts_link can be registered with the OpenBSC core at runtime. - -struct trx_link { - struct gsm_bts_trx *trx; -}; - -struct bts_link { - struct gsm_bts *bts; - struct trx_link trx_links[NUM_TRX]; -}; - -Interface from stack to input core: -====================================================================== -int abis_rsl_sendmsg(struct msgb *msg); - send a message through a RSL link to the TRX specified by the caller in - msg->trx. - -int abis_rsl_rcvmsg(struct msgb *msg); - receive a message from a RSL link from the TRX specified by the - caller in msg->trx. - -int abis_nm_sendmsg(struct msgb *msg); - send a message through a OML link to the BTS specified by the caller in - msg->trx->bts. The caller can just use bts->c0 to get the first TRX - in a BTS. (OML messages are not really sent to a TRX but to the BTS) - -int abis_nm_rcvmsg(struct msgb *msg); - receive a message from a OML link from the BTS specified by the caller - in msg->trx->bts. The caller can just use bts->c0 to get the first - TRX in a BTS. - -int abis_link_event(int event, void *data); - signal some event (such as layer 1 connect/disconnect) from the - input core to the stack. - -int subch_demux_in(mx, const uint8_t *data, int len); - receive 'len' bytes from a given E1 timeslot (TRAU frames) - -int subchan_mux_out(mx, uint8_t *data, int len); - obtain 'len' bytes of output data to be sent on E1 timeslot - -Intrface by Input Core for Input Plugins -====================================================================== - -int btslink_register_plugin(); - - -Configuration for the E1 input module -====================================================================== - -BTS - BTS number - number of TRX - OML link - E1 line number - timeslot number - [subslot number] - SAPI - TEI - for each TRX - RSL link - E1 line number - timeslot number - [subslot number] - SAPI - TEI - for each TS - E1 line number - timeslot number - subslot number - - -E1 input module data model -====================================================================== - - -enum e1inp_sign_type { - E1INP_SIGN_NONE, - E1INP_SIGN_OML, - E1INP_SIGN_RSL, -}; - -struct e1inp_sign_link { - /* list of signalling links */ - struct llist_head list; - - enum e1inp_sign_type type; - - /* trx for msg->trx of received msgs */ - struct gsm_bts_trx *trx; - - /* msgb queue of to-be-transmitted msgs */ - struct llist_head tx_list; - - /* SAPI and TEI on the E1 TS */ - uint8_t sapi; - uint8_t tei; -} - -enum e1inp_ts_type { - E1INP_TS_TYPE_NONE, - E1INP_TS_TYPE_SIGN, - E1INP_TS_TYPE_TRAU, -}; - -/* A timeslot in the E1 interface */ -struct e1inp_ts { - enum e1inp_ts_type type; - struct e1inp_line *line; - union { - struct { - struct llist_head sign_links; - } sign; - struct { - /* subchannel demuxer for frames from E1 */ - struct subch_demux demux; - /* subchannel muxer for frames to E1 */ - struct subch_mux mux; - } trau; - }; - union { - struct { - /* mISDN driver has one fd for each ts */ - struct osmo_fd; - } misdn; - } driver; -}; - -struct e1inp_line { - unsigned int num; - char *name; - - struct e1inp_ts ts[NR_E1_TS]; - - char *e1inp_driver; - void *driver_data; -}; - -/* Call from the Stack: configuration of this TS has changed */ -int e1inp_update_ts(struct e1inp_ts *ts); - -/* Receive a packet from the E1 driver */ -int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg, - uint8_t tei, uint8_t sapi); - -/* Send a packet, callback function in the driver */ -int e1driver_tx_ts(struct e1inp_ts *ts, struct msgb *msg) - - -struct e1inp_driver { - const char *name; - int (*want_write)(struct e1inp_ts *ts); -}; diff --git a/openbsc/doc/examples/Makefile.am b/openbsc/doc/examples/Makefile.am deleted file mode 100644 index 8f14fdcf7..000000000 --- a/openbsc/doc/examples/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ - -CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,' - -dist-hook: - for f in $$($(CFG_FILES)); do \ - j="$(distdir)/$$f" && \ - mkdir -p "$$(dirname $$j)" && \ - $(INSTALL_DATA) $(srcdir)/$$f $$j; \ - done - -install-data-hook: - for f in $$($(CFG_FILES)); do \ - j="$(DESTDIR)$(docdir)/examples/$$f" && \ - mkdir -p "$$(dirname $$j)" && \ - $(INSTALL_DATA) $(srcdir)/$$f $$j; \ - done - -uninstall-hook: - @$(PRE_UNINSTALL) - for f in $$($(CFG_FILES)); do \ - j="$(DESTDIR)$(docdir)/examples/$$f" && \ - $(RM) $$j; \ - done diff --git a/openbsc/doc/examples/osmo-bsc/osmo-bsc.cfg b/openbsc/doc/examples/osmo-bsc/osmo-bsc.cfg deleted file mode 100644 index d7461964f..000000000 --- a/openbsc/doc/examples/osmo-bsc/osmo-bsc.cfg +++ /dev/null @@ -1,100 +0,0 @@ -! -! OsmoBSC (0.9.14+gitr1+3d331c0062bb0c9694dbd4d1eab7adc58138c3ae) configuration saved from vty -!! -password foo -! -! -line vty - no login -! -e1_input - e1_line 0 driver ipa -network - network country code 1 - mobile network code 1 - short name OsmoBSC - long name OsmoBSC - auth policy closed - location updating reject cause 13 - encryption a5 0 - neci 1 - paging any use tch 0 - rrlp mode none - mm info 1 - handover 0 - handover window rxlev averaging 10 - handover window rxqual averaging 1 - handover window rxlev neighbor averaging 10 - handover power budget interval 6 - handover power budget hysteresis 3 - handover maximum distance 9999 - timer t3101 10 - timer t3103 0 - timer t3105 0 - timer t3107 0 - timer t3109 0 - timer t3111 0 - timer t3113 60 - timer t3115 0 - timer t3117 0 - timer t3119 0 - timer t3122 0 - timer t3141 0 - dtx-used 0 - subscriber-keep-in-ram 0 - bts 0 - type nanobts - band DCS1800 - cell_identity 0 - location_area_code 1 - training_sequence_code 7 - base_station_id_code 63 - ms max power 15 - cell reselection hysteresis 4 - rxlev access min 0 - channel allocator ascending - rach tx integer 9 - rach max transmission 7 - ip.access unit_id 0 0 - oml ip.access stream_id 255 line 0 - neighbor-list mode manual-si5 - neighbor-list add arfcn 100 - neighbor-list add arfcn 200 - si5 neighbor-list add arfcn 10 - si5 neighbor-list add arfcn 20 - gprs mode none - trx 0 - rf_locked 0 - arfcn 871 - nominal power 23 - max_power_red 20 - rsl e1 tei 0 - timeslot 0 - phys_chan_config CCCH+SDCCH4 - hopping enabled 0 - timeslot 1 - phys_chan_config TCH/F - hopping enabled 0 - timeslot 2 - phys_chan_config TCH/F - hopping enabled 0 - timeslot 3 - phys_chan_config TCH/F - hopping enabled 0 - timeslot 4 - phys_chan_config TCH/F - hopping enabled 0 - timeslot 5 - phys_chan_config TCH/F - hopping enabled 0 - timeslot 6 - phys_chan_config TCH/F - hopping enabled 0 - timeslot 7 - phys_chan_config TCH/F - hopping enabled 0 -msc - ip.access rtp-base 4000 - timeout-ping 20 - timeout-pong 5 - dest 192.168.100.11 6666 0 diff --git a/openbsc/doc/examples/osmo-bsc_mgcp/mgcp.cfg b/openbsc/doc/examples/osmo-bsc_mgcp/mgcp.cfg deleted file mode 100644 index 7409dd00d..000000000 --- a/openbsc/doc/examples/osmo-bsc_mgcp/mgcp.cfg +++ /dev/null @@ -1,18 +0,0 @@ -! -! MGCP configuration hand edited -! ! -password foo -! -line vty - no login -! -mgcp -! local ip 213.167.134.14 - bts ip 172.16.252.43 - bind ip 127.0.0.1 - bind port 2427 - rtp base 4000 - sdp audio payload number 98 - sdp audio payload name AMR/8000 - number endpoints 31 - loop 1 diff --git a/openbsc/doc/examples/osmo-bsc_nat/black-list.cfg b/openbsc/doc/examples/osmo-bsc_nat/black-list.cfg deleted file mode 100644 index d36179d37..000000000 --- a/openbsc/doc/examples/osmo-bsc_nat/black-list.cfg +++ /dev/null @@ -1 +0,0 @@ -678012512671923:6:6: diff --git a/openbsc/doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg b/openbsc/doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg deleted file mode 100644 index 93cb98075..000000000 --- a/openbsc/doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg +++ /dev/null @@ -1,64 +0,0 @@ -! -! OsmoBSCNAT (0.12.0.266-2daa9) configuration saved from vty -!! -! -log stderr - logging filter all 1 - logging color 1 - logging timestamp 0 - logging level all everything - logging level rll notice - logging level cc notice - logging level mm notice - logging level rr notice - logging level rsl notice - logging level nm info - logging level mncc notice - logging level pag notice - logging level meas notice - logging level sccp notice - logging level msc notice - logging level mgcp notice - logging level ho notice - logging level db notice - logging level ref notice - logging level gprs everything - logging level ns info - logging level bssgp everything - logging level llc everything - logging level sndcp everything - logging level nat notice - logging level ctrl notice - logging level smpp everything - logging level lglobal notice - logging level llapd notice - logging level linp notice - logging level lmux notice - logging level lmi notice - logging level lmib notice - logging level lsms notice -! -line vty - no login -! -mgcp - bind ip 0.0.0.0 - bind port 2427 - rtp bts-base 4000 - rtp net-base 16000 - rtp ip-dscp 0 - no rtcp-omit - sdp audio-payload number 126 - sdp audio-payload name AMR/8000 - loop 0 - number endpoints 1 - call-agent ip 127.0.0.1 - rtp transcoder-base 0 - transcoder-remote-base 4000 -nat - msc ip 127.0.0.1 - msc port 5000 - timeout auth 2 - timeout ping 20 - timeout pong 5 - ip-dscp 0 diff --git a/openbsc/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg b/openbsc/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg deleted file mode 100644 index 0c3917a3e..000000000 --- a/openbsc/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg +++ /dev/null @@ -1,25 +0,0 @@ -! -! Osmocom Gb Proxy (0.9.0.404-6463) configuration saved from vty -!! -! -line vty - no login -! -gbproxy - sgsn nsei 101 -ns - nse 101 nsvci 101 - nse 101 remote-role sgsn - nse 101 encapsulation udp - nse 101 remote-ip 192.168.100.239 - nse 101 remote-port 7777 - timer tns-block 3 - timer tns-block-retries 3 - timer tns-reset 3 - timer tns-reset-retries 3 - timer tns-test 30 - timer tns-alive 3 - timer tns-alive-retries 10 - encapsulation framerelay-gre enabled 0 - encapsulation framerelay-gre local-ip 0.0.0.0 - encapsulation udp local-port 23000 diff --git a/openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg b/openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg deleted file mode 100644 index 36065e254..000000000 --- a/openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg +++ /dev/null @@ -1,164 +0,0 @@ -! -! OpenBSC (0.9.0.845-57c4) configuration saved from vty -!! -password foo -! -line vty - no login -! -e1_input - e1_line 0 driver misdn -network - network country code 1 - mobile network code 1 - short name OpenBSC - long name OpenBSC - auth policy closed - location updating reject cause 13 - encryption a5 0 - neci 1 - rrlp mode none - mm info 0 - handover 0 - handover window rxlev averaging 10 - handover window rxqual averaging 1 - handover window rxlev neighbor averaging 10 - handover power budget interval 6 - handover power budget hysteresis 3 - handover maximum distance 9999 - timer t3101 10 - timer t3103 0 - timer t3105 0 - timer t3107 0 - timer t3109 4 - timer t3111 0 - timer t3113 60 - timer t3115 0 - timer t3117 0 - timer t3119 0 - timer t3141 0 - bts 0 - type bs11 - band GSM900 - cell_identity 0 - location_area_code 1 - training_sequence_code 7 - base_station_id_code 63 - ms max power 15 - cell reselection hysteresis 4 - rxlev access min 0 - channel allocator descending - rach tx integer 9 - rach max transmission 7 - oml e1 line 0 timeslot 1 sub-slot full - oml e1 tei 25 - gprs mode none - trx 0 - rf_locked 0 - arfcn 121 - nominal power 24 - max_power_red 12 - rsl e1 line 0 timeslot 1 sub-slot full - rsl e1 tei 1 - timeslot 0 - phys_chan_config CCCH - hopping enabled 0 - e1 line 0 timeslot 1 sub-slot full - timeslot 1 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 2 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 2 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 2 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 3 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 3 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 3 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 3 sub-slot 3 - trx 1 - rf_locked 0 - arfcn 119 - nominal power 24 - max_power_red 12 - rsl e1 line 0 timeslot 1 sub-slot full - rsl e1 tei 2 - timeslot 0 - phys_chan_config SDCCH8 - hopping enabled 1 - hopping sequence-number 0 - hopping maio 0 - hopping arfcn add 117 - hopping arfcn add 119 - timeslot 1 - phys_chan_config TCH/F - hopping enabled 1 - hopping sequence-number 0 - hopping maio 0 - hopping arfcn add 117 - hopping arfcn add 119 - e1 line 0 timeslot 4 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - hopping enabled 1 - hopping sequence-number 0 - hopping maio 0 - hopping arfcn add 117 - hopping arfcn add 119 - e1 line 0 timeslot 4 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - hopping enabled 1 - hopping sequence-number 0 - hopping maio 0 - hopping arfcn add 117 - hopping arfcn add 119 - e1 line 0 timeslot 4 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - hopping enabled 1 - hopping sequence-number 0 - hopping maio 0 - hopping arfcn add 117 - hopping arfcn add 119 - e1 line 0 timeslot 5 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - hopping enabled 1 - hopping sequence-number 0 - hopping maio 0 - hopping arfcn add 117 - hopping arfcn add 119 - e1 line 0 timeslot 5 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - hopping enabled 1 - hopping sequence-number 0 - hopping maio 0 - hopping arfcn add 117 - hopping arfcn add 119 - e1 line 0 timeslot 5 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - hopping enabled 1 - hopping sequence-number 0 - hopping maio 0 - hopping arfcn add 117 - hopping arfcn add 119 - e1 line 0 timeslot 5 sub-slot 3 diff --git a/openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg b/openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg deleted file mode 100644 index ebe6cc6cb..000000000 --- a/openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg +++ /dev/null @@ -1,84 +0,0 @@ -! -! OpenBSC configuration saved from vty -! ! -password foo -! -line vty - no login -! -e1_input - e1_line 0 driver misdn -network - network country code 1 - mobile network code 1 - short name OpenBSC - long name OpenBSC - timer t3101 10 - timer t3113 60 - bts 0 - type bs11 - band GSM900 - cell_identity 1 - location_area_code 1 - training_sequence_code 7 - base_station_id_code 63 - oml e1 line 0 timeslot 1 sub-slot full - oml e1 tei 25 - trx 0 - arfcn 121 - max_power_red 0 - rsl e1 line 0 timeslot 1 sub-slot full - rsl e1 tei 1 - timeslot 0 - phys_chan_config CCCH+SDCCH4 - e1 line 0 timeslot 1 sub-slot full - timeslot 1 - phys_chan_config SDCCH8 - e1 line 0 timeslot 2 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - e1 line 0 timeslot 2 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - e1 line 0 timeslot 2 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - e1 line 0 timeslot 3 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - e1 line 0 timeslot 3 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - e1 line 0 timeslot 3 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - e1 line 0 timeslot 3 sub-slot 3 - trx 1 - arfcn 123 - max_power_red 0 - rsl e1 line 0 timeslot 1 sub-slot full - rsl e1 tei 2 - timeslot 0 - phys_chan_config TCH/F - e1 line 0 timeslot 4 sub-slot 0 - timeslot 1 - phys_chan_config TCH/F - e1 line 0 timeslot 4 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - e1 line 0 timeslot 4 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - e1 line 0 timeslot 4 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - e1 line 0 timeslot 5 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - e1 line 0 timeslot 5 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - e1 line 0 timeslot 5 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - e1 line 0 timeslot 5 sub-slot 3 diff --git a/openbsc/doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg b/openbsc/doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg deleted file mode 100644 index 9ae800342..000000000 --- a/openbsc/doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg +++ /dev/null @@ -1,148 +0,0 @@ -! -! OpenBSC configuration saved from vty -! ! -password foo -! -line vty - no login -! -network - network country code 1 - mobile network code 1 - short name OpenBSC - long name OpenBSC - timer t3101 10 - timer t3113 60 - bts 0 - type bs11 - band GSM900 - cell_identity 1 - location_area_code 1 - training_sequence_code 7 - base_station_id_code 63 - oml e1 line 0 timeslot 1 sub-slot full - oml e1 tei 25 - trx 0 - arfcn 121 - max_power_red 0 - rsl e1 line 0 timeslot 1 sub-slot full - rsl e1 tei 1 - timeslot 0 - phys_chan_config CCCH+SDCCH4 - e1 line 0 timeslot 1 sub-slot full - timeslot 1 - phys_chan_config TCH/F - e1 line 0 timeslot 2 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - e1 line 0 timeslot 2 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - e1 line 0 timeslot 2 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - e1 line 0 timeslot 3 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - e1 line 0 timeslot 3 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - e1 line 0 timeslot 3 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - e1 line 0 timeslot 3 sub-slot 3 - trx 1 - arfcn 123 - max_power_red 0 - rsl e1 line 0 timeslot 1 sub-slot full - rsl e1 tei 2 - timeslot 0 - phys_chan_config TCH/F - e1 line 0 timeslot 4 sub-slot 0 - timeslot 1 - phys_chan_config TCH/F - e1 line 0 timeslot 4 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - e1 line 0 timeslot 4 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - e1 line 0 timeslot 4 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - e1 line 0 timeslot 5 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - e1 line 0 timeslot 5 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - e1 line 0 timeslot 5 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - e1 line 0 timeslot 5 sub-slot 3 - bts 1 - type bs11 - band GSM900 - location_area_code 2 - training_sequence_code 7 - base_station_id_code 63 - oml e1 line 1 timeslot 6 sub-slot full - oml e1 tei 25 - trx 0 - arfcn 122 - max_power_red 0 - rsl e1 line 1 timeslot 6 sub-slot full - rsl e1 tei 1 - timeslot 0 - phys_chan_config CCCH+SDCCH4 - e1 line 1 timeslot 7 sub-slot 0 - timeslot 1 - phys_chan_config SDCCH8 - e1 line 1 timeslot 7 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - e1 line 1 timeslot 7 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - e1 line 1 timeslot 7 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - e1 line 1 timeslot 8 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - e1 line 1 timeslot 8 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - e1 line 1 timeslot 8 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - e1 line 1 timeslot 8 sub-slot 3 - trx 1 - arfcn 124 - max_power_red 0 - rsl e1 line 1 timeslot 6 sub-slot full - rsl e1 tei 2 - timeslot 0 - phys_chan_config TCH/F - e1 line 1 timeslot 9 sub-slot 0 - timeslot 1 - phys_chan_config TCH/F - e1 line 1 timeslot 9 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - e1 line 1 timeslot 9 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - e1 line 1 timeslot 9 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - e1 line 1 timeslot 10 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - e1 line 1 timeslot 10 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - e1 line 1 timeslot 10 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - e1 line 1 timeslot 10 sub-slot 3 diff --git a/openbsc/doc/examples/osmo-nitb/bs11/openbsc.cfg b/openbsc/doc/examples/osmo-nitb/bs11/openbsc.cfg deleted file mode 100644 index 542edd6ad..000000000 --- a/openbsc/doc/examples/osmo-nitb/bs11/openbsc.cfg +++ /dev/null @@ -1,56 +0,0 @@ -! -! OpenBSC configuration saved from vty -! ! -password foo -! -line vty - no login -! -e1_input - e1_line 0 driver misdn -network - network country code 1 - mobile network code 1 - short name OpenBSC - long name OpenBSC - timer t3101 10 - timer t3113 60 - bts 0 - type bs11 - band GSM900 - cell_identity 1 - location_area_code 1 - training_sequence_code 7 - base_station_id_code 63 - oml e1 line 0 timeslot 1 sub-slot full - oml e1 tei 25 - trx 0 - arfcn 121 - max_power_red 0 - rsl e1 line 0 timeslot 1 sub-slot full - rsl e1 tei 1 - timeslot 0 - phys_chan_config CCCH+SDCCH4 - e1 line 0 timeslot 1 sub-slot full - timeslot 1 - phys_chan_config TCH/F - e1 line 0 timeslot 2 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - e1 line 0 timeslot 2 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - e1 line 0 timeslot 2 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - e1 line 0 timeslot 3 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - e1 line 0 timeslot 3 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - e1 line 0 timeslot 3 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - e1 line 0 timeslot 3 sub-slot 3 - diff --git a/openbsc/doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg b/openbsc/doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg deleted file mode 100644 index bea0b7dfc..000000000 --- a/openbsc/doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg +++ /dev/null @@ -1,99 +0,0 @@ -! -! OpenBSC configuration saved from vty -! ! -password foo -! -line vty - no login -! -e1_input - e1_line 0 driver ipa -network - network country code 1 - mobile network code 1 - short name OpenBSC - long name OpenBSC - auth policy closed - location updating reject cause 13 - encryption a5 0 - neci 1 - rrlp mode none - mm info 0 - handover 0 - handover window rxlev averaging 10 - handover window rxqual averaging 1 - handover window rxlev neighbor averaging 10 - handover power budget interval 6 - handover power budget hysteresis 3 - handover maximum distance 9999 - timer t3101 10 - timer t3103 0 - timer t3105 0 - timer t3107 0 - timer t3109 4 - timer t3111 0 - timer t3113 60 - timer t3115 0 - timer t3117 0 - timer t3119 0 - timer t3141 0 - bts 0 - type nanobts - band DCS1800 - cell_identity 0 - location_area_code 1 - training_sequence_code 7 - base_station_id_code 63 - ms max power 15 - cell reselection hysteresis 4 - rxlev access min 0 - channel allocator ascending - rach tx integer 9 - rach max transmission 7 - ip.access unit_id 1800 0 - oml ip.access stream_id 255 line 0 - gprs mode none - trx 0 - rf_locked 0 - arfcn 871 - nominal power 23 - max_power_red 0 - rsl e1 tei 0 - timeslot 0 - phys_chan_config CCCH+SDCCH4 - timeslot 1 - phys_chan_config SDCCH8 - timeslot 2 - phys_chan_config TCH/F - timeslot 3 - phys_chan_config TCH/F - timeslot 4 - phys_chan_config TCH/F - timeslot 5 - phys_chan_config TCH/F - timeslot 6 - phys_chan_config TCH/F - timeslot 7 - phys_chan_config TCH/F - trx 1 - rf_locked 0 - arfcn 873 - nominal power 23 - max_power_red 0 - rsl e1 tei 0 - timeslot 0 - phys_chan_config SDCCH8 - timeslot 1 - phys_chan_config TCH/F - timeslot 2 - phys_chan_config TCH/F - timeslot 3 - phys_chan_config TCH/F - timeslot 4 - phys_chan_config TCH/F - timeslot 5 - phys_chan_config TCH/F - timeslot 6 - phys_chan_config TCH/F - timeslot 7 - phys_chan_config TCH/F diff --git a/openbsc/doc/examples/osmo-nitb/nanobts/openbsc.cfg b/openbsc/doc/examples/osmo-nitb/nanobts/openbsc.cfg deleted file mode 100644 index 7a443087c..000000000 --- a/openbsc/doc/examples/osmo-nitb/nanobts/openbsc.cfg +++ /dev/null @@ -1,77 +0,0 @@ -! -! OpenBSC configuration saved from vty -! ! -password foo -! -line vty - no login -! -e1_input - e1_line 0 driver ipa -network - network country code 1 - mobile network code 1 - short name OpenBSC - long name OpenBSC - auth policy closed - location updating reject cause 13 - encryption a5 0 - neci 1 - rrlp mode none - mm info 1 - handover 0 - handover window rxlev averaging 10 - handover window rxqual averaging 1 - handover window rxlev neighbor averaging 10 - handover power budget interval 6 - handover power budget hysteresis 3 - handover maximum distance 9999 - timer t3101 10 - timer t3103 0 - timer t3105 0 - timer t3107 0 - timer t3109 4 - timer t3111 0 - timer t3113 60 - timer t3115 0 - timer t3117 0 - timer t3119 0 - timer t3141 0 - bts 0 - type nanobts - band DCS1800 - cell_identity 0 - location_area_code 1 - training_sequence_code 7 - base_station_id_code 63 - ms max power 15 - cell reselection hysteresis 4 - rxlev access min 0 - channel allocator ascending - rach tx integer 9 - rach max transmission 7 - ip.access unit_id 1801 0 - oml ip.access stream_id 255 line 0 - gprs mode none - trx 0 - rf_locked 0 - arfcn 514 - nominal power 23 - max_power_red 20 - rsl e1 tei 0 - timeslot 0 - phys_chan_config CCCH+SDCCH4 - timeslot 1 - phys_chan_config SDCCH8 - timeslot 2 - phys_chan_config TCH/F - timeslot 3 - phys_chan_config TCH/F - timeslot 4 - phys_chan_config TCH/F - timeslot 5 - phys_chan_config TCH/F - timeslot 6 - phys_chan_config TCH/F - timeslot 7 - phys_chan_config TCH/F diff --git a/openbsc/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg b/openbsc/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg deleted file mode 100644 index f4c715c76..000000000 --- a/openbsc/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg +++ /dev/null @@ -1,117 +0,0 @@ -! -! OpenBSC configuration saved from vty -! ! -password foo -! -line vty - no login -! -e1_input - e1_line 0 driver misdn -network - network country code 1 - mobile network code 1 - short name OpenBSC - long name OpenBSC - timer t3101 10 - timer t3113 60 - bts 0 - type nokia_site - band GSM1800 - cell_identity 1 - location_area_code 1 - base_station_id_code 63 - training_sequence_code 7 - - oml e1 line 0 timeslot 1 sub-slot full - oml e1 tei 1 - - trx 0 - arfcn 866 - max_power_red 24 - rsl e1 line 0 timeslot 2 sub-slot full - rsl e1 tei 1 - timeslot 0 - phys_chan_config CCCH+SDCCH4 - e1 line 0 timeslot 6 sub-slot full - timeslot 1 - phys_chan_config SDCCH8 - e1 line 0 timeslot 6 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - e1 line 0 timeslot 6 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - e1 line 0 timeslot 6 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - e1 line 0 timeslot 7 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - e1 line 0 timeslot 7 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - e1 line 0 timeslot 7 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - e1 line 0 timeslot 7 sub-slot 3 - - trx 1 - arfcn 870 - max_power_red 24 - rsl e1 line 0 timeslot 3 sub-slot full - rsl e1 tei 2 - timeslot 0 - phys_chan_config TCH/F - e1 line 0 timeslot 8 sub-slot 0 - timeslot 1 - phys_chan_config TCH/F - e1 line 0 timeslot 8 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - e1 line 0 timeslot 8 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - e1 line 0 timeslot 8 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - e1 line 0 timeslot 9 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - e1 line 0 timeslot 9 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - e1 line 0 timeslot 9 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - e1 line 0 timeslot 9 sub-slot 3 - - trx 2 - arfcn 874 - max_power_red 24 - rsl e1 line 0 timeslot 4 sub-slot full - rsl e1 tei 3 - timeslot 0 - phys_chan_config TCH/F - e1 line 0 timeslot 10 sub-slot 0 - timeslot 1 - phys_chan_config TCH/F - e1 line 0 timeslot 10 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - e1 line 0 timeslot 10 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - e1 line 0 timeslot 10 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - e1 line 0 timeslot 11 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - e1 line 0 timeslot 11 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - e1 line 0 timeslot 11 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - e1 line 0 timeslot 11 sub-slot 3 diff --git a/openbsc/doc/examples/osmo-nitb/rbs2308/openbsc.cfg b/openbsc/doc/examples/osmo-nitb/rbs2308/openbsc.cfg deleted file mode 100644 index 97cd9df50..000000000 --- a/openbsc/doc/examples/osmo-nitb/rbs2308/openbsc.cfg +++ /dev/null @@ -1,221 +0,0 @@ -! -! OpenBSC (0.9.11.308-62d46) configuration saved from vty -!! -password foo -! -line vty - no login -! -network - network country code 262 - mobile network code 42 - short name OpenBSC - long name OpenBSC - auth policy closed - location updating reject cause 13 - encryption a5 0 - neci 0 - paging any use tch 0 - rrlp mode none - mm info 0 - handover 0 - handover window rxlev averaging 10 - handover window rxqual averaging 1 - handover window rxlev neighbor averaging 10 - handover power budget interval 6 - handover power budget hysteresis 3 - handover maximum distance 9999 - timer t3101 10 - timer t3103 0 - timer t3105 0 - timer t3107 0 - timer t3109 4 - timer t3111 0 - timer t3113 60 - timer t3115 0 - timer t3117 0 - timer t3119 0 - timer t3122 0 - timer t3141 0 - dtx-used 0 - subscriber-keep-in-ram 0 - bts 0 - type rbs2000 - band GSM900 - cell_identity 0 - location_area_code 1 - training_sequence_code 7 - base_station_id_code 63 - ms max power 15 - cell reselection hysteresis 4 - rxlev access min 0 - channel allocator descending - rach tx integer 9 - rach max transmission 7 - oml e1 line 0 timeslot 1 sub-slot full - oml e1 tei 62 - neighbor-list mode automatic - gprs mode none - is-connection-list add 4 512 12 - is-connection-list add 16 524 12 - is-connection-list add 28 536 12 - is-connection-list add 40 548 12 - trx 0 - rf_locked 0 - arfcn 55 - nominal power 24 - max_power_red 12 - rsl e1 line 0 timeslot 1 sub-slot full - rsl e1 tei 0 - timeslot 0 - phys_chan_config CCCH+SDCCH4 - hopping enabled 0 - e1 line 0 timeslot 1 sub-slot full - timeslot 1 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 2 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 2 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 2 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 3 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 3 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 3 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 3 sub-slot 3 - trx 1 - rf_locked 0 - arfcn 57 - nominal power 24 - max_power_red 12 - rsl e1 line 0 timeslot 4 sub-slot full - rsl e1 tei 1 - timeslot 0 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 5 sub-slot 0 - timeslot 1 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 5 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 5 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 5 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 6 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 6 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 6 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 6 sub-slot 3 - trx 2 - rf_locked 0 - arfcn 59 - nominal power 24 - max_power_red 12 - rsl e1 line 0 timeslot 7 sub-slot full - rsl e1 tei 2 - timeslot 0 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 8 sub-slot 0 - timeslot 1 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 8 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 8 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 8 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 9 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 9 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 9 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 9 sub-slot 3 - trx 3 - rf_locked 0 - arfcn 61 - nominal power 24 - max_power_red 12 - rsl e1 line 0 timeslot 10 sub-slot full - rsl e1 tei 3 - timeslot 0 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 11 sub-slot 0 - timeslot 1 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 11 sub-slot 1 - timeslot 2 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 11 sub-slot 2 - timeslot 3 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 11 sub-slot 3 - timeslot 4 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 12 sub-slot 0 - timeslot 5 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 12 sub-slot 1 - timeslot 6 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 12 sub-slot 2 - timeslot 7 - phys_chan_config TCH/F - hopping enabled 0 - e1 line 0 timeslot 12 sub-slot 3 - -e1_input - e1_line 0 driver dahdi diff --git a/openbsc/doc/examples/osmo-sgsn/osmo-sgsn.cfg b/openbsc/doc/examples/osmo-sgsn/osmo-sgsn.cfg deleted file mode 100644 index 49559830a..000000000 --- a/openbsc/doc/examples/osmo-sgsn/osmo-sgsn.cfg +++ /dev/null @@ -1,26 +0,0 @@ -! -! Osmocom SGSN configuration -! -! -line vty - no login -! -sgsn - gtp local-ip 127.0.0.1 - ggsn 0 remote-ip 127.0.0.1 - ggsn 0 gtp-version 1 -! -ns - timer tns-block 3 - timer tns-block-retries 3 - timer tns-reset 3 - timer tns-reset-retries 3 - timer tns-test 30 - timer tns-alive 3 - timer tns-alive-retries 10 - encapsulation udp local-ip 127.0.0.1 - encapsulation udp local-port 23000 - encapsulation framerelay-gre enabled 0 -! -bssgp -! diff --git a/openbsc/doc/gsm-hopping.txt b/openbsc/doc/gsm-hopping.txt deleted file mode 100644 index c964963c0..000000000 --- a/openbsc/doc/gsm-hopping.txt +++ /dev/null @@ -1,54 +0,0 @@ -according to GSM 05.02: - -general parameters from CCCH: -* CA cell allocation of ARFCN's (System Information / BCCH) -* FN: TDMA frame number (t1,t2,t3') in SCH - -specific parameters from channel assignment: -* MA: mobile allocation, defines set of ARFCN's, up to 64 -* MAIO: index -* HSN: hopping sequence generator number (0..64) - - -hopping sequence generation (6.2.3): - -uint8_t rntable[114] = { - 48, 98, 63, 1, 36, 95, 78, 102, 94, 73, - 0, 64, 25, 81, 76, 59, 124, 23, 104, 100, - 101, 47, 118, 85, 18, 56, 96, 86, 54, 2, - 80, 34, 127, 13, 6, 89, 57, 103, 12, 74, - 55, 111, 75, 38, 109, 71, 112, 29, 11, 88, - 87, 19, 3, 68, 110, 26, 33, 31, 8, 45, - 82, 58, 40, 107, 32, 5, 106, 92, 62, 67, - 77, 108, 122, 37, 60, 66, 121, 42, 51, 126, - 117, 114, 4, 90, 43, 52, 53, 113, 120, 72, - 16, 49, 7, 79, 119, 61, 22, 84, 9, 97, - 125, 99, 17, 123 -}; - -/* mai=0 represents lowest ARFCN in the MA */ - - -uint8_t hopping_mai(uint8_t hsn, uint32_t fn, uint8_t maio, - uint8_t t1, uint8_t t2, uint8_t t3_) -{ - uint8_t mai; - - if (hsn == 0) /* cyclic hopping */ - mai = (fn + maio) % n; - else { - uint32_t m, m_, t_, s; - - m = t2 + rntable[(hsn xor (t1 % 64)) + t3]; - m_ = m % (2^NBIN); - t_ = t3 % (2^NBIN); - if (m_ < n then) - s = m_; - else - s = (m_ + t_) % n; - mai = (s + maio) % n; - } - - return mai; -} - diff --git a/openbsc/doc/handover.txt b/openbsc/doc/handover.txt deleted file mode 100644 index ac19e8725..000000000 --- a/openbsc/doc/handover.txt +++ /dev/null @@ -1,89 +0,0 @@ -Ideas about a handover algorithm -====================================================================== - -This is mostly based on the results presented in Chapter 8 of "Performance -Enhancements in a Frequency Hopping GSM Network" by Thomas Toftegaard Nielsen -and Joeroen Wigard. - - -=== Reasons for performing handover === - -Section 2.1.1: Handover used in their CAPACITY simulation: - -1) Interference Handover - -Average RXLEV is satisfactory high, but average RXQUAL too low indicates -interference to the channel. Handover should be made. - -2) Bad Quality - -Averaged RXQUAL is lower than a threshold - -3) Low Level / Signal Strength - -Average RXLEV is lower than a threshold - -4) Distance Handover - -MS is too far away from a cell (measured by TA) - -5) Power budget / Better Cell - -RX Level of neighbor cell is at least "HO Margin dB" dB better than the -current serving cell. - -=== Ideal parameters for HO algorithm === - -Chapter 8, Section 2.2, Table 24: - -Window RXLEV averaging: 10 SACCH frames (no weighting) -Window RXQUAL averaging: 1 SACCH frame (no averaging) -Level Threashold: 1 of the last 1 AV-RXLEV values < -110dBm -Quality Threshold: 3 of the last 4 AV-RXQUAL values >= 5 -Interference Threshold: 1 of the last AV-RXLEV > -85 dBm & - 3 of the last 4 AV-RXQUAL values >= 5 -Power Budget: Level of neighbor cell > 3 dB better -Power Budget Interval: Every 6 SACCH frames (6 seconds ?!?) -Distance Handover: Disabled -Evaluation rule 1: RXLEV of the candidate cell a tleast -104 dBm -Evaluation rule 2: Level of candidate cell > 3dB better own cell -Timer Successful HO: 5 SACCH frames -Timer Unsuccessful HO: 1 SACCH frame - -In a non-frequency hopping case, RXQUAL threshold can be decreased to -RXLEV >= 4 - -When frequency hopping is enabled, the following additional parameters -should be introduced: - -* No intra-cell handover -* Use a HO Margin of 2dB - -=== Handover Channel Reservation === - -In loaded network, each cell should reserve some channels for handovers, -rather than using all of them for new call establishment. This reduces the -need to drop calls due to failing handovers, at the expense of failing new call -attempts. - -=== Dynamic HO Margin === - -The handover margin (hysteresis) should depend on the RXQUAL. Optimal results -were achieved with the following settings: -* RXQUAL <= 4: 9 dB -* RXQUAL == 5: 6 dB -* RXQUAL >= 6: 1 dB - - - -== Actual Handover on a protocol level == - -After the BSC has decided a handover shall be done, it has to - -# allocate a channel at the new BTS -# allocate a handover reference -# activate the channel on the BTS side using RSL CHANNEL ACTIVATION, - indicating the HO reference -# BTS responds with CHAN ACT ACK, including GSM frame number -# BSC sends 04.08 HO CMD to MS using old BTS - diff --git a/openbsc/doc/ipa-sccp.txt b/openbsc/doc/ipa-sccp.txt deleted file mode 100644 index 5d6719e98..000000000 --- a/openbsc/doc/ipa-sccp.txt +++ /dev/null @@ -1,94 +0,0 @@ - -IPA SCCP message flow in the BSC - -February, 2013 Holger Hans Peter Freyther - -CONTENTS - -1. SCCP inside the IPA header -2. Supported SCCP message types -3. Receiving SCCP messages -4. Sending SCCP messages - - -1. SCCP inside the IPA header - -Many Soft-MSCs implement something that is called SCCP/lite. This means -that SCCP messages are transported inside a small multiplexing protocol -over TCP/IP. This is an alternative to a full SIGTRAN implementation. - -The multiplexing protocol is the same as used with the sysmoBTS and the -ip.access nanoBTS. It is a three byte header with two bytes for the length -in network byte order and one byte for the type. The type to be used for -SCCP is 0xFD. - - struct ipa_header { - uint16_t length_in_network_order; - uint8_t type; - } __attribute__((packed)); - - - -2. Supported SCCP message types - -To implement GSM 08.08 only a subset of SCCP messages need to be implemented. -For transporting paging and reset messages SCCP UDT messages are used. For -the connections with a Mobile Station (MS) a SCCP connection is opened. This -means that the SCCP CR, SCCP CC, SCCP CREF, SCCP RLC, SCCP RLSD, SCCP DT1 -and SCCP IT messages are supported. - - -3. Receiving SCCP UDT messages - -This is an illustration of the flow of messages. The IPA multiplexing protocol -is used for various protocols. This means there is a central place where the -multiplexing stream terminates. The stream is terminated in the osmo_bsc_msc.c -file and the ipaccess_a_fd_cb method. For SCCP messages the SCCP dispatching -sccp_system_incoming method is called. This function is implemented in the -libosmo-sccp library. - -To receive UDT messages osmo_bsc_sccp.c:osmo_bsc_sccp_init is using the -sccp_set_read function to register a callback for UDT messages. The callback -is msc_sccp_read and it is calling bsc_handle_udt that is implemented in the -osmo_bsc_bssap.c. This function will handle the GSM 08.08 BSSAP messages. -Currently only the reset acknowledge and the paging messages are handled. - -The BSC currently does not accept incoming SCCP messages and is only opening -SCCP connections to the MSC. When opening a connection the callbacks for state -changes (connection confirmed, released, release complete) are set and a routine -for handling incoming data. This registration is done in the osmo_bsc_sccp.c -file and the bsc_create_new_connection method. The name of the callback is -msc_outgoing_sccp_data and this will call bsc_handle_dt1 that is implemented -in the osmo_bsc_bssap.c file. This will forward the messages to the right -Mobile Station (MS). - - -4. Sending SCCP messages - -There are three parts to sending that will be explained below. The first part -is to send an entire SCCP frame (which includes the GSM 08.08 data) to the -MSC. This is done by first registering the low level sending. sccp_system_init -is called with the function that is responsible for sending a message. The -msc_sccp_write_ipa will call the msc_queue_write function with the data and -the right MSC connection. Below the msc_queue_write the IPA header will be -prepended to the msg and then send to the MSC. - -The BSC supports multiple different A-link connections, the decision to pick -the right MSC is done in this method. It is either done via the SCCP connection -or the ctx pointer. - -When the BSC is starting a BSS RESET message will be sent to the MSC. The reset -is created in osmo_bsc_msc.c:initialize_if_needed and sccp_write is called with -the GSM 08.08 data and the connection to use. The libosmo-sccp library will -embed it into a SCCP UDT message and call the msc_sccp_write_ipa method. - -When a new SCCP connection is to be created the bsc_create_new_connection -in the osmo_bsc_sccp.c file. The sccp_connection_socket method will create -the context for a SCCP connection. The state and data callback will be used -to be notified about data and changes. Once the connection is configured the -bsc_open_connection will be called that will ask the libosmo-sccp library to -create a SCCP CR message using the sccp_connection_connect method. For active -connections the sccp_connection_write method will be called. - - - diff --git a/openbsc/doc/oml-interface.txt b/openbsc/doc/oml-interface.txt deleted file mode 100644 index 02bead77a..000000000 --- a/openbsc/doc/oml-interface.txt +++ /dev/null @@ -1,22 +0,0 @@ -oml interface design notes - -problems: - -* there is no way how to tag a command sent to the BTS, with the response - having the same tag to identify the originator of the command -* therefore, we can have e.g. both the BSC and the OML interface send a - SET ATTRIBUTE message, where the responses would end up at the wrong - query. -* The BTS has 10s to ACK/NACK a command. We do not run any timers. - -the only possible solutions i can imagine: -* have some kind of exclusive locking, where the OML interface gets blocked - from the BSC and is exclusively assigned to the OML console until all commands - of the OML console have terminated. This can either be done explicitly - dynamically or on demand - -* use the OML interface synchronously, i.e. always wait for the response from - the BTS before - -* unilateral / unsolicited messages need to be broadcasted to both the BSC and - the OML console diff --git a/openbsc/doc/paging.txt b/openbsc/doc/paging.txt deleted file mode 100644 index c597c22b4..000000000 --- a/openbsc/doc/paging.txt +++ /dev/null @@ -1,48 +0,0 @@ - -GSM Paging implementation in OpenBSC - -== Code structure == - -The code is implemented in the libbsc/paging.c file. The external -interface is documented/specified in the include/openbsc/paging.h -header file. The code is used by the NITB and BSC application. - - -== Implementation == - -Paging can be initiated in two ways. The standard way is to page by -LAC. Each BTS has its own list/queue of outstanding paging operation. -When a subscriber is paged one "struct paging_request" per BTS will -be allocated and added to the tail of the list. The BTS is supposed -to be configured to not repeat the paging. - -A paging_request will remain in the queue until a paging response or at -the expiry of the T3113. Every 500 milliseconds a RSL paging command is -send to the BTS. The 500 milliseconds is a throttling to not crash the -ip.access nanoBTS. Once one paging_request has been handled it will be -put at the end of the queue/list and the available slots for the BTS -will be decreased. - -The available slots will be updated based on the paging load information -element of the CCCH Load indication. If no paging slots are considered -to be available and no load indication is sent a timer is started. The -current timeout is 500 milliseconds and at the expiry of the timer the -available slots will be set to 20. - -OpenBSC has the " paging free <-1-1024>" configuration option. In case -there are less free channels than required no paging request will be -sent to the BTS. Instead it will be attempted to send the paging request -at the next timeout (500 milliseconds). - -== Limitation == - -The paging throughput could be higher but this has lead to crashes on the -ip.access nanoBTS in the past. - -== Configuration == - -=== ip.access nanoBTS === - -The current CCCH Load indication threshold is 10% and the period is 1 second. -The code can be found inside the src/libbsc/bts_ipaccess_nanobts.c inside the -nanobts_attr_bts array. diff --git a/openbsc/git-version-gen b/openbsc/git-version-gen deleted file mode 100755 index f2ad4a762..000000000 --- a/openbsc/git-version-gen +++ /dev/null @@ -1,151 +0,0 @@ -#!/bin/sh -# Print a version string. -scriptversion=2010-01-28.01 - -# Copyright (C) 2007-2010 Free Software Foundation, Inc. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. -# It may be run two ways: -# - from a git repository in which the "git describe" command below -# produces useful output (thus requiring at least one signed tag) -# - from a non-git-repo directory containing a .tarball-version file, which -# presumes this script is invoked like "./git-version-gen .tarball-version". - -# In order to use intra-version strings in your project, you will need two -# separate generated version string files: -# -# .tarball-version - present only in a distribution tarball, and not in -# a checked-out repository. Created with contents that were learned at -# the last time autoconf was run, and used by git-version-gen. Must not -# be present in either $(srcdir) or $(builddir) for git-version-gen to -# give accurate answers during normal development with a checked out tree, -# but must be present in a tarball when there is no version control system. -# Therefore, it cannot be used in any dependencies. GNUmakefile has -# hooks to force a reconfigure at distribution time to get the value -# correct, without penalizing normal development with extra reconfigures. -# -# .version - present in a checked-out repository and in a distribution -# tarball. Usable in dependencies, particularly for files that don't -# want to depend on config.h but do want to track version changes. -# Delete this file prior to any autoconf run where you want to rebuild -# files to pick up a version string change; and leave it stale to -# minimize rebuild time after unrelated changes to configure sources. -# -# It is probably wise to add these two files to .gitignore, so that you -# don't accidentally commit either generated file. -# -# Use the following line in your configure.ac, so that $(VERSION) will -# automatically be up-to-date each time configure is run (and note that -# since configure.ac no longer includes a version string, Makefile rules -# should not depend on configure.ac for version updates). -# -# AC_INIT([GNU project], -# m4_esyscmd([build-aux/git-version-gen .tarball-version]), -# [bug-project@example]) -# -# Then use the following lines in your Makefile.am, so that .version -# will be present for dependencies, and so that .tarball-version will -# exist in distribution tarballs. -# -# BUILT_SOURCES = $(top_srcdir)/.version -# $(top_srcdir)/.version: -# echo $(VERSION) > $@-t && mv $@-t $@ -# dist-hook: -# echo $(VERSION) > $(distdir)/.tarball-version - -case $# in - 1) ;; - *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; -esac - -tarball_version_file=$1 -nl=' -' - -# First see if there is a tarball-only version file. -# then try "git describe", then default. -if test -f $tarball_version_file -then - v=`cat $tarball_version_file` || exit 1 - case $v in - *$nl*) v= ;; # reject multi-line output - [0-9]*) ;; - *) v= ;; - esac - test -z "$v" \ - && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 -fi - -if test -n "$v" -then - : # use $v -elif test -d ./../.git \ - && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ - || git describe --abbrev=4 HEAD 2>/dev/null` \ - && case $v in - [0-9]*) ;; - v[0-9]*) ;; - *) (exit 1) ;; - esac -then - # Is this a new git that lists number of commits since the last - # tag or the previous older version that did not? - # Newer: v6.10-77-g0f8faeb - # Older: v6.10-g0f8faeb - case $v in - *-*-*) : git describe is okay three part flavor ;; - *-*) - : git describe is older two part flavor - # Recreate the number of commits and rewrite such that the - # result is the same as if we were using the newer version - # of git describe. - vtag=`echo "$v" | sed 's/-.*//'` - numcommits=`git rev-list "$vtag"..HEAD | wc -l` - v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; - ;; - esac - - # Change the first '-' to a '.', so version-comparing tools work properly. - # Remove the "g" in git describe's output string, to save a byte. - v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; -else - v=UNKNOWN -fi - -v=`echo "$v" |sed 's/^v//'` - -# Don't declare a version "dirty" merely because a time stamp has changed. -git status > /dev/null 2>&1 - -dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= -case "$dirty" in - '') ;; - *) # Append the suffix only if there isn't one already. - case $v in - *-dirty) ;; - *) v="$v-dirty" ;; - esac ;; -esac - -# Omit the trailing newline, so that m4_esyscmd can use the result directly. -echo "$v" | tr -d '\012' - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-end: "$" -# End: diff --git a/openbsc/include/Makefile.am b/openbsc/include/Makefile.am deleted file mode 100644 index 4596b6e3c..000000000 --- a/openbsc/include/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -SUBDIRS = openbsc - -noinst_HEADERS = mISDNif.h compat_af_isdn.h diff --git a/openbsc/include/compat_af_isdn.h b/openbsc/include/compat_af_isdn.h deleted file mode 100644 index 56cbfb3f2..000000000 --- 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 8e065d24b..000000000 --- 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 93b30bb0c..000000000 --- a/openbsc/include/openbsc/Makefile.am +++ /dev/null @@ -1,19 +0,0 @@ -noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ - gsm_subscriber.h gsm_04_11.h debug.h signal.h \ - misdn.h chan_alloc.h paging.h \ - trau_mux.h rs232.h openbscdefines.h rtp_proxy.h \ - bsc_rll.h mncc.h transaction.h ussd.h gsm_04_80.h \ - silent_call.h mgcp.h meas_rep.h rest_octets.h \ - system_information.h handover.h mgcp_internal.h \ - vty.h socket.h e1_config.h trau_upqueue.h token_auth.h \ - handover_decision.h rrlp.h control_if.h \ - crc24.h gprs_llc.h gprs_gmm.h \ - gb_proxy.h gprs_sgsn.h gsm_04_08_gprs.h sgsn.h \ - auth.h osmo_msc.h bsc_msc.h bsc_nat.h \ - osmo_bsc_rf.h osmo_bsc.h network_listen.h bsc_nat_sccp.h \ - osmo_msc_data.h osmo_bsc_grace.h sms_queue.h abis_om2000.h \ - bss.h gsm_data_shared.h control_cmd.h ipaccess.h mncc_int.h \ - arfcn_range_encode.h - -openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h -openbscdir = $(includedir)/openbsc diff --git a/openbsc/include/openbsc/abis_nm.h b/openbsc/include/openbsc/abis_nm.h deleted file mode 100644 index 4d6f866ee..000000000 --- a/openbsc/include/openbsc/abis_nm.h +++ /dev/null @@ -1,186 +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 - -struct cell_global_id { - uint16_t mcc; - uint16_t mnc; - uint16_t lac; - uint16_t ci; -}; - -/* The BCCH info from an ip.access test, in host byte order - * and already parsed... */ -struct ipac_bcch_info { - struct llist_head list; - - uint16_t info_type; - uint8_t freq_qual; - uint16_t arfcn; - uint8_t rx_lev; - uint8_t rx_qual; - int16_t freq_err; - uint16_t frame_offset; - uint32_t frame_nr_offset; - uint8_t bsic; - struct cell_global_id cgi; - uint8_t ba_list_si2[16]; - uint8_t ba_list_si2bis[16]; - uint8_t ba_list_si2ter[16]; - uint8_t ca_list_si1[16]; -}; - -/* PUBLIC */ - -struct msgb; - -struct abis_nm_cfg { - /* callback for unidirectional reports */ - int (*report_cb)(struct msgb *, - struct abis_om_fom_hdr *); - /* callback for software activate requests from BTS */ - int (*sw_act_req)(struct msgb *); -}; - -struct abis_nm_sw_descr { - /* where does it start? how long is it? */ - const uint8_t *start; - size_t len; - - /* the parsed data */ - const uint8_t *file_id; - uint16_t file_id_len; - const uint8_t *file_ver; - uint16_t file_ver_len; -}; - -extern int abis_nm_rcvmsg(struct msgb *msg); - -int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const uint8_t *buf, int len); -int abis_nm_rx(struct msgb *msg); -int abis_nm_opstart(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2); -int abis_nm_chg_adm_state(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, - uint8_t i1, uint8_t i2, enum abis_nm_adm_state adm_state); -int abis_nm_establish_tei(struct gsm_bts *bts, uint8_t trx_nr, - uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, - uint8_t tei); -int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, - uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot); -int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, - uint8_t e1_port, uint8_t e1_timeslot, - uint8_t e1_subslot); -int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - uint8_t *attr, uint8_t attr_len); -int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len); -int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len); -int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb); -int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1, - uint8_t i2, uint8_t i3, int nack, uint8_t *attr, int att_len); -int abis_nm_raw_msg(struct gsm_bts *bts, int len, uint8_t *msg); -int abis_nm_event_reports(struct gsm_bts *bts, int on); -int abis_nm_reset_resource(struct gsm_bts *bts); -int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, - uint8_t win_size, int forced, - gsm_cbfn *cbfn, void *cb_data); -int abis_nm_software_load_status(struct gsm_bts *bts); -int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, - gsm_cbfn *cbfn, void *cb_data); - -int abis_nm_conn_mdrop_link(struct gsm_bts *bts, uint8_t e1_port0, uint8_t ts0, - uint8_t e1_port1, uint8_t ts1); - -int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - uint8_t test_nr, uint8_t auton_report, struct msgb *msg); - -/* Siemens / BS-11 specific */ -int abis_nm_bs11_reset_resource(struct gsm_bts *bts); -int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin); -int abis_nm_bs11_create_object(struct gsm_bts *bts, enum abis_bs11_objtype type, - uint8_t idx, uint8_t attr_len, const uint8_t *attr); -int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, uint8_t idx); -int abis_nm_bs11_create_bport(struct gsm_bts *bts, uint8_t idx); -int abis_nm_bs11_delete_object(struct gsm_bts *bts, - enum abis_bs11_objtype type, uint8_t idx); -int abis_nm_bs11_delete_bport(struct gsm_bts *bts, uint8_t idx); -int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, uint8_t e1_port, - uint8_t e1_timeslot, uint8_t e1_subslot, uint8_t tei); -int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts); -int abis_nm_bs11_get_serno(struct gsm_bts *bts); -int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, uint8_t level); -int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx); -int abis_nm_bs11_logon(struct gsm_bts *bts, uint8_t level, const char *name, int on); -int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on); -int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on); -int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password); -int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked); -int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts); -int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value); -int abis_nm_bs11_get_cclk(struct gsm_bts *bts); -int abis_nm_bs11_get_state(struct gsm_bts *bts); -int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, - uint8_t win_size, int forced, gsm_cbfn *cbfn); -int abis_nm_bs11_set_ext_time(struct gsm_bts *bts); -int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, uint8_t bport); -int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, uint8_t bport, enum abis_bs11_line_cfg line_cfg); -int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect); -int abis_nm_bs11_restart(struct gsm_bts *bts); - -/* ip.access nanoBTS specific commands */ -int abis_nm_ipaccess_msg(struct gsm_bts *bts, uint8_t msg_type, - uint8_t obj_class, uint8_t bts_nr, - uint8_t trx_nr, uint8_t ts_nr, - uint8_t *attr, int attr_len); -int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, uint8_t *attr, - int attr_len); -int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx); -int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - uint8_t *attr, uint8_t attr_len); -int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, - uint32_t ip, uint16_t port, uint8_t stream); -void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts); -int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf); -const char *ipacc_testres_name(uint8_t res); - -/* Functions calling into other code parts */ -int nm_is_running(struct gsm_nm_state *s); - -int abis_nm_vty_init(void); - -void abis_nm_clear_queue(struct gsm_bts *bts); - -int _abis_nm_sendmsg(struct msgb *msg); - -void abis_nm_queue_send_next(struct gsm_bts *bts); /* for bs11_config. */ - -int abis_nm_parse_sw_config(const uint8_t *data, const size_t len, - struct abis_nm_sw_descr *res, const int res_len); -int abis_nm_select_newest_sw(const struct abis_nm_sw_descr *sw, const size_t len); - -#endif /* _NM_H */ diff --git a/openbsc/include/openbsc/abis_om2000.h b/openbsc/include/openbsc/abis_om2000.h deleted file mode 100644 index 2ff72705b..000000000 --- a/openbsc/include/openbsc/abis_om2000.h +++ /dev/null @@ -1,98 +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, -}; - -struct abis_om2k_mo { - uint8_t class; - uint8_t bts; - uint8_t assoc_so; - uint8_t inst; -} __attribute__ ((packed)); - -/* on-wire format for IS conn group */ -struct om2k_is_conn_grp { - uint16_t icp1; - uint16_t icp2; - uint8_t cont_idx; -} __attribute__ ((packed)); - -/* internal data formant for IS conn group */ -struct is_conn_group { - struct llist_head list; - uint16_t icp1; - uint16_t icp2; - uint8_t ci; -}; - -extern const struct abis_om2k_mo om2k_mo_cf; -extern const struct abis_om2k_mo om2k_mo_is; -extern const struct abis_om2k_mo om2k_mo_con; -extern const struct abis_om2k_mo om2k_mo_tf; - -extern const struct value_string om2k_mo_class_short_vals[]; - -int abis_om2k_rcvmsg(struct msgb *msg); - -extern const struct abis_om2k_mo om2k_mo_cf; - -int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t operational); -int abis_om2k_tx_is_conf_req(struct gsm_bts *bts); -int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts); -int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx); -int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx); -int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts); - -int abis_om2k_vty_init(void); - -struct vty; -void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts); - -#endif /* OPENBCS_ABIS_OM2K_H */ diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h deleted file mode 100644 index 06f0a721c..000000000 --- a/openbsc/include/openbsc/abis_rsl.h +++ /dev/null @@ -1,107 +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 - -struct gsm_bts; -struct gsm_lchan; -struct gsm_subscriber; -struct gsm_bts_trx_ts; - - -int rsl_bcch_info(struct gsm_bts_trx *trx, uint8_t type, - const uint8_t *data, int len); -int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, - const uint8_t *data, int len); -int rsl_chan_activate(struct gsm_bts_trx *trx, uint8_t chan_nr, - uint8_t act_type, - struct rsl_ie_chan_mode *chan_mode, - struct rsl_ie_chan_ident *chan_ident, - uint8_t bs_power, uint8_t ms_power, - uint8_t ta); -int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type, - uint8_t ta, uint8_t ho_ref); -int rsl_chan_mode_modify_req(struct gsm_lchan *ts); -int rsl_encryption_cmd(struct msgb *msg); -int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len, - uint8_t *ms_ident, uint8_t chan_needed); -int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val); - -int rsl_data_request(struct msgb *msg, uint8_t link_id); -int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id); -int rsl_relase_request(struct gsm_lchan *lchan, uint8_t link_id); - -/* Siemens vendor-specific RSL extensions */ -int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci); - -/* ip.access specfic RSL extensions */ -int rsl_ipacc_crcx(struct gsm_lchan *lchan); -int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip, - uint16_t port, uint8_t rtp_payload2); -int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan); -int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act); - -int abis_rsl_rcvmsg(struct msgb *msg); - -uint64_t str_to_imsi(const char *imsi_str); -int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id, - enum rsl_rel_mode release_mode); - -int rsl_lchan_set_state(struct gsm_lchan *lchan, int); - -/* 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, - uint8_t cb_command, const uint8_t *data, int len); - -/* some Nokia specific stuff */ -int rsl_nokia_si_begin(struct gsm_bts_trx *trx); -int rsl_nokia_si_end(struct gsm_bts_trx *trx); - -/* required for Nokia BTS power control */ -int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction); - - -int rsl_release_sapis_from(struct gsm_lchan *lchan, int start, - enum rsl_rel_mode release_mode); -int rsl_start_t3109(struct gsm_lchan *lchan); - -int rsl_direct_rf_release(struct gsm_lchan *lchan); - -#endif /* RSL_MT_H */ - diff --git a/openbsc/include/openbsc/arfcn_range_encode.h b/openbsc/include/openbsc/arfcn_range_encode.h deleted file mode 100644 index 7a6fff000..000000000 --- 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 { - ARFCN_RANGE_INVALID = -1, - ARFCN_RANGE_128 = 127, - ARFCN_RANGE_256 = 255, - ARFCN_RANGE_512 = 511, - ARFCN_RANGE_1024 = 1023, -}; - -#define RANGE_ENC_MAX_ARFCNS 29 - -int range_enc_determine_range(const int *arfcns, int size, int *f0_out); -int range_enc_arfcns(const int rng, const int *arfcns, int sze, int *out, int idx); -int range_enc_find_index(const int rng, const int *arfcns, int size); -int range_enc_filter_arfcns(const int rng, 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 2364fb3d2..000000000 --- a/openbsc/include/openbsc/auth.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _AUTH_H -#define _AUTH_H - -struct gsm_auth_tuple; -struct gsm_subscriber; - -enum auth_action { - AUTH_NOT_AVAIL = 0, /* No auth tuple available */ - AUTH_DO_AUTH_THAN_CIPH = 1, /* Firsth authenticate, then cipher */ - AUTH_DO_CIPH = 2, /* Only ciphering */ - AUTH_DO_AUTH = 3, /* Only authentication, no ciphering */ -}; - -int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr, int key_seq); - -#endif /* _AUTH_H */ diff --git a/openbsc/include/openbsc/bsc_api.h b/openbsc/include/openbsc/bsc_api.h deleted file mode 100644 index f52984f39..000000000 --- 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 gsm48_multi_rate_conf *conf); -}; - -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 9543002a7..000000000 --- a/openbsc/include/openbsc/bsc_msc.h +++ /dev/null @@ -1,59 +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 - -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; - - 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 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(const char *token); - -#endif diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h deleted file mode 100644 index 195031822..000000000 --- a/openbsc/include/openbsc/bsc_nat.h +++ /dev/null @@ -1,480 +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 -#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; - -enum { - NAT_CON_TYPE_NONE, - NAT_CON_TYPE_LU, - NAT_CON_TYPE_CM_SERV_REQ, - NAT_CON_TYPE_PAG_RESP, - NAT_CON_TYPE_SSA, - NAT_CON_TYPE_LOCAL_REJECT, - NAT_CON_TYPE_OTHER, -}; - -/* - * 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; - - /* the fd we use to communicate */ - struct osmo_wqueue write_queue; - - /* 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; - - char *token; - int nr; - - char *description; - - /* imsi white and blacklist */ - char *acc_lst_name; - - int forbid_paging; - int paging_group; - - /* audio handling */ - int max_endpoints; - - /* backpointer */ - struct bsc_nat *nat; - - struct bsc_config_stats stats; - - struct llist_head lac_list; -}; - -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; -}; - -enum bsc_nat_acc_ctr { - ACC_LIST_BSC_FILTER, - ACC_LIST_NAT_FILTER, -}; - -struct bsc_nat_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_nat_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; -}; - -/** - * 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; - - /* msc things */ - struct llist_head dests; - struct bsc_msc_dest *main_dest; - struct bsc_msc_connection *msc_con; - char *token; - - /* timeouts */ - int auth_timeout; - int ping_timeout; - int pong_timeout; - - struct bsc_endpoint *bsc_endpoints; - - /* filter */ - char *acc_lst_name; - - /* Barring of subscribers with a rb tree */ - struct rb_root imsi_black_list; - char *imsi_black_list_fn; - - /* number rewriting */ - char *num_rewr_name; - struct llist_head num_rewr; - - char *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; - - /* 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 osmo_timer_list auth_timeout; -}; - -struct bsc_nat_reject_cause { - int lu_reject_cause; - int cm_reject_cause; -}; - -/* create and init the structures */ -struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token); -struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num); -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); - -/** - * Content filtering. - */ -int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, - struct bsc_nat_parsed *, int *con_type, char **imsi, - struct bsc_nat_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_nat_reject_cause *cause); - -/** - * 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); -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); - -/* IMSI allow/deny handling */ -struct bsc_nat_acc_lst *bsc_nat_acc_lst_find(struct bsc_nat *nat, const char *name); -struct bsc_nat_acc_lst *bsc_nat_acc_lst_get(struct bsc_nat *nat, const char *name); -void bsc_nat_acc_lst_delete(struct bsc_nat_acc_lst *lst); - -struct bsc_nat_acc_lst_entry *bsc_nat_acc_lst_entry_create(struct bsc_nat_acc_lst *); -int bsc_nat_lst_check_allow(struct bsc_nat_acc_lst *lst, const char *imsi); - -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_check_ussd(struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, struct msgb *msg); -int bsc_close_ussd_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; -}; - -void bsc_nat_num_rewr_entry_adapt(void *ctx, struct llist_head *head, const struct osmo_config_list *); - -struct bsc_nat_barr_entry { - struct rb_node node; - - char *imsi; - int cm_reject_cause; - int lu_reject_cause; -}; - -int bsc_nat_barr_adapt(void *ctx, struct rb_root *rbtree, const struct osmo_config_list *); -int bsc_nat_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu); - -void bsc_nat_send_mgcp_to_msc(struct bsc_nat *bsc_nat, struct msgb *msg); -void bsc_nat_handle_mgcp(struct bsc_nat *bsc, struct msgb *msg); - -struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, int port); -void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending); -int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg); - -int bsc_nat_extract_lac(struct bsc_connection *bsc, struct nat_sccp_connection *con, - struct bsc_nat_parsed *parsed, struct msgb *msg); - -#endif diff --git a/openbsc/include/openbsc/bsc_nat_callstats.h b/openbsc/include/openbsc/bsc_nat_callstats.h deleted file mode 100644 index 64f9bfc0a..000000000 --- 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 34aa6320a..000000000 --- a/openbsc/include/openbsc/bsc_nat_sccp.h +++ /dev/null @@ -1,101 +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 - -/* - * 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; - - /* 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_type; - int con_local; - int authorized; - int imsi_checked; - char *imsi; - - 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 729ba6037..000000000 --- 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/bss.h b/openbsc/include/openbsc/bss.h deleted file mode 100644 index 49df547a1..000000000 --- a/openbsc/include/openbsc/bss.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _BSS_H_ -#define _BSS_H_ - -struct gsm_network; -struct msgb; - -/* start and stop network */ -extern int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *), const char *cfg_file); -extern int bsc_shutdown_net(struct gsm_network *net); - -/* register all supported BTS */ -extern int bts_init(void); -extern int bts_model_bs11_init(void); -extern int bts_model_rbs2k_init(void); -extern int bts_model_nanobts_init(void); -extern int bts_model_nokia_site_init(void); -extern int bts_model_sysmobts_init(void); -#endif diff --git a/openbsc/include/openbsc/chan_alloc.h b/openbsc/include/openbsc/chan_alloc.h deleted file mode 100644 index f2d3c781d..000000000 --- a/openbsc/include/openbsc/chan_alloc.h +++ /dev/null @@ -1,65 +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; - -/* Special allocator for C0 of BTS */ -struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts, - enum gsm_phys_chan_config pchan); - -/* Regular physical channel allocator */ -struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts, - enum gsm_phys_chan_config pchan); - -/* Regular physical channel (TS) */ -void ts_free(struct gsm_bts_trx_ts *ts); - -/* 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_UNKNOWN]; -}; - -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/control_cmd.h b/openbsc/include/openbsc/control_cmd.h deleted file mode 100644 index a0ea055ce..000000000 --- a/openbsc/include/openbsc/control_cmd.h +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef _CONTROL_CMD_H -#define _CONTROL_CMD_H - -#include -#include -#include - -#include - -#include - -#define CTRL_CMD_ERROR -1 -#define CTRL_CMD_HANDLED 0 -#define CTRL_CMD_REPLY 1 - -struct ctrl_handle; - -enum ctrl_node_type { - CTRL_NODE_ROOT, /* Root elements */ - CTRL_NODE_BTS, /* BTS specific (net.btsN.) */ - CTRL_NODE_TRX, /* TRX specific (net.btsN.trxM.) */ - CTRL_NODE_TS, /* TS specific (net.btsN.trxM.tsI.) */ - _LAST_CTRL_NODE -}; - -enum ctrl_type { - CTRL_TYPE_UNKNOWN, - CTRL_TYPE_GET, - CTRL_TYPE_SET, - CTRL_TYPE_GET_REPLY, - CTRL_TYPE_SET_REPLY, - CTRL_TYPE_TRAP, - CTRL_TYPE_ERROR -}; - -struct ctrl_connection { - struct llist_head list_entry; - - /* The queue for sending data back */ - struct osmo_wqueue write_queue; - - /* Callback if the connection was closed */ - void (*closed_cb)(struct ctrl_connection *conn); - - /* Pending commands for this connection */ - struct llist_head cmds; -}; - -struct ctrl_cmd { - struct ctrl_connection *ccon; - enum ctrl_type type; - char *id; - void *node; - char *variable; - char *value; - char *reply; -}; - -struct ctrl_cmd_struct { - int nr_commands; - char **command; -}; - -struct ctrl_cmd_element { - const char *name; - const char *param; - struct ctrl_cmd_struct strcmd; - int (*set)(struct ctrl_cmd *cmd, void *data); - int (*get)(struct ctrl_cmd *cmd, void *data); - int (*verify)(struct ctrl_cmd *cmd, const char *value, void *data); -}; - -struct ctrl_cmd_map { - char *cmd; - enum ctrl_type type; -}; - -int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data); -int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd); -int ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data); -int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd); -int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd); -struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg); -struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd); -struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd); -struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type); -struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd); - -#define CTRL_CMD_DEFINE_RANGE(cmdname, cmdstr, dtype, element, min, max) \ -static int get_##cmdname(struct ctrl_cmd *cmd, void *_data) \ -{ \ - dtype *node = cmd->node; \ - cmd->reply = talloc_asprintf(cmd, "%i", node->element); \ - if (!cmd->reply) { \ - cmd->reply = "OOM"; \ - return CTRL_CMD_ERROR; \ - } \ - return CTRL_CMD_REPLY; \ -} \ -static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \ -{ \ - dtype *node = cmd->node; \ - int tmp = atoi(cmd->value); \ - node->element = tmp; \ - return get_##cmdname(cmd, _data); \ -} \ -static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *_data) \ -{ \ - int tmp = atoi(value); \ - if ((tmp >= min)&&(tmp <= max)) { \ - return 0; \ - } \ - cmd->reply = "Input not within the range"; \ - return -1; \ -} \ -static struct ctrl_cmd_element cmd_##cmdname = { \ - .name = cmdstr, \ - .param = NULL, \ - .get = &get_##cmdname, \ - .set = &set_##cmdname, \ - .verify = &verify_##cmdname, \ -} - -#define CTRL_CMD_DEFINE_STRING(cmdname, cmdstr, dtype, element) \ -static int get_##cmdname(struct ctrl_cmd *cmd, void *_data) \ -{ \ - dtype *data = cmd->node; \ - cmd->reply = talloc_asprintf(cmd, "%s", data->element); \ - if (!cmd->reply) { \ - cmd->reply = "OOM"; \ - return CTRL_CMD_ERROR; \ - } \ - return CTRL_CMD_REPLY; \ -} \ -static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \ -{ \ - dtype *data = cmd->node; \ - bsc_replace_string(cmd->node, &data->element, cmd->value); \ - return get_##cmdname(cmd, _data); \ -} \ -static struct ctrl_cmd_element cmd_##cmdname = { \ - .name = cmdstr, \ - .param = NULL, \ - .get = &get_##cmdname, \ - .set = &set_##cmdname, \ - .verify = NULL, \ -} - -#define CTRL_CMD_DEFINE(cmdname, cmdstr) \ -static int get_##cmdname(struct ctrl_cmd *cmd, void *data); \ -static int set_##cmdname(struct ctrl_cmd *cmd, void *data); \ -static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data); \ -static struct ctrl_cmd_element cmd_##cmdname = { \ - .name = cmdstr, \ - .param = NULL, \ - .get = &get_##cmdname, \ - .set = &set_##cmdname, \ - .verify = &verify_##cmdname, \ -} - -struct gsm_network; - -#endif /* _CONTROL_CMD_H */ diff --git a/openbsc/include/openbsc/control_if.h b/openbsc/include/openbsc/control_if.h deleted file mode 100644 index 6afc86d76..000000000 --- a/openbsc/include/openbsc/control_if.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _CONTROL_IF_H -#define _CONTROL_IF_H - -#include -#include -#include - -struct ctrl_handle { - struct osmo_fd listen_fd; - struct gsm_network *gsmnet; - - /* List of control connections */ - struct llist_head ccon_list; -}; - -int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd); -int ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data); -struct ctrl_handle *controlif_setup(struct gsm_network *gsmnet, uint16_t port); - -#endif /* _CONTROL_IF_H */ - diff --git a/openbsc/include/openbsc/crc24.h b/openbsc/include/openbsc/crc24.h deleted file mode 100644 index 756638c03..000000000 --- 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/db.h b/openbsc/include/openbsc/db.h deleted file mode 100644 index 25c2aea70..000000000 --- a/openbsc/include/openbsc/db.h +++ /dev/null @@ -1,82 +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 "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(struct gsm_network *net, - char *imsi); -struct gsm_subscriber *db_get_subscriber(struct gsm_network *net, - enum gsm_subscriber_field field, - const char *subscr); -int db_sync_subscriber(struct gsm_subscriber *subscriber); -int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id)); -int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber); -int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber); -int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t* token); -int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char *imei); -int db_sync_equipment(struct gsm_equipment *equip); -int db_subscriber_update(struct gsm_subscriber *subscriber); - -/* 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_sent(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 d851181c7..000000000 --- a/openbsc/include/openbsc/debug.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef _DEBUG_H -#define _DEBUG_H - -#include -#include - -#define DEBUG -#include - -/* Debug Areas of the code */ -enum { - DRLL, - DCC, - DMM, - DRR, - DRSL, - DNM, - DMNCC, - DPAG, - DMEAS, - DSCCP, - DMSC, - DMGCP, - DHO, - DDB, - DREF, - DGPRS, - DNS, - DBSSGP, - DLLC, - DSNDCP, - DNAT, - DCTRL, - DSMPP, - Debug_LastEntry, -}; - -/* context */ -#define BSC_CTX_LCHAN 0 -#define BSC_CTX_SUBSCR 1 -#define BSC_CTX_BTS 2 -#define BSC_CTX_SCCP 3 - -/* target */ - -enum { - //DEBUG_FILTER_ALL = 1 << 0, - LOG_FILTER_IMSI = 1 << 1, - LOG_FILTER_NSVC = 1 << 2, - LOG_FILTER_BVC = 1 << 3, -}; - -/* we don't need a header dependency for this... */ -struct gprs_nsvc; -struct bssgp_bvc_ctx; - -void log_set_imsi_filter(struct log_target *target, const char *imsi); -void log_set_nsvc_filter(struct log_target *target, - struct gprs_nsvc *nsvc); -void log_set_bvc_filter(struct log_target *target, - struct bssgp_bvc_ctx *bctx); - -extern const struct log_info log_info; - -#endif /* _DEBUG_H */ diff --git a/openbsc/include/openbsc/e1_config.h b/openbsc/include/openbsc/e1_config.h deleted file mode 100644 index 538c0b09d..000000000 --- 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 2c8ddb334..000000000 --- a/openbsc/include/openbsc/gb_proxy.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _GB_PROXY_H -#define _GB_PROXY_H - - -#include - -#include -#include - -struct gbproxy_config { - /* parsed from config file */ - uint16_t nsip_sgsn_nsei; - - /* misc */ - struct gprs_ns_inst *nsi; -}; - -extern struct gbproxy_config gbcfg; -extern struct cmd_element show_gbproxy_cmd; - -/* gb_proxy_vty .c */ - -int gbproxy_vty_init(void); -int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg); - - -/* gb_proxy.c */ - -/* Main input function for Gb proxy */ -int gbprox_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci); - -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); - -#endif diff --git a/openbsc/include/openbsc/gprs_gmm.h b/openbsc/include/openbsc/gprs_gmm.h deleted file mode 100644 index f6b3e5e7c..000000000 --- a/openbsc/include/openbsc/gprs_gmm.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _GPRS_GMM_H -#define _GPRS_GMM_H - -#include -#include - -int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause); -int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid, - uint8_t cause, uint8_t pco_len, uint8_t *pco_v); -int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp); -int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp); - -int gsm0408_gprs_rcvmsg(struct msgb *msg, struct gprs_llc_llme *llme); - -int 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); - -#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 4be7b1f18..000000000 --- a/openbsc/include/openbsc/gprs_llc.h +++ /dev/null @@ -1,202 +0,0 @@ -#ifndef _GPRS_LLC_H -#define _GPRS_LLC_H - -#include -#include - -/* Section 4.7 LLC Layer Structure */ -enum gprs_llc_sapi { - GPRS_SAPI_GMM = 1, - GPRS_SAPI_TOM2 = 2, - GPRS_SAPI_SNDCP3 = 3, - GPRS_SAPI_SNDCP5 = 5, - GPRS_SAPI_SMS = 7, - GPRS_SAPI_TOM8 = 8, - GPRS_SAPI_SNDCP9 = 9, - GPRS_SAPI_SNDCP11 = 11, -}; - -/* Section 6.4 Commands and Responses */ -enum gprs_llc_u_cmd { - GPRS_LLC_U_DM_RESP = 0x01, - GPRS_LLC_U_DISC_CMD = 0x04, - GPRS_LLC_U_UA_RESP = 0x06, - GPRS_LLC_U_SABM_CMD = 0x07, - GPRS_LLC_U_FRMR_RESP = 0x08, - GPRS_LLC_U_XID = 0x0b, - GPRS_LLC_U_NULL_CMD = 0x00, -}; - -/* Section 6.4.1.6 / Table 6 */ -enum gprs_llc_xid_type { - GPRS_LLC_XID_T_VERSION = 0, - GPRS_LLC_XID_T_IOV_UI = 1, - GPRS_LLC_XID_T_IOV_I = 2, - GPRS_LLC_XID_T_T200 = 3, - GPRS_LLC_XID_T_N200 = 4, - GPRS_LLC_XID_T_N201_U = 5, - GPRS_LLC_XID_T_N201_I = 6, - GPRS_LLC_XID_T_mD = 7, - GPRS_LLC_XID_T_mU = 8, - GPRS_LLC_XID_T_kD = 9, - GPRS_LLC_XID_T_kU = 10, - GPRS_LLC_XID_T_L3_PAR = 11, - GPRS_LLC_XID_T_RESET = 12, -}; - -/* TS 04.64 Section 7.1.2 Table 7: LLC layer primitives (GMM/SNDCP/SMS/TOM) */ -/* TS 04.65 Section 5.1.2 Table 2: Service primitives used by SNDCP */ -enum gprs_llc_primitive { - /* GMM <-> LLME */ - LLGMM_ASSIGN_REQ, /* GMM tells us new TLLI: TLLI old, TLLI new, Kc, CiphAlg */ - LLGMM_RESET_REQ, /* GMM tells us to perform XID negotiation: TLLI */ - LLGMM_RESET_CNF, /* LLC informs GMM that XID has completed: TLLI */ - LLGMM_SUSPEND_REQ, /* GMM tells us MS has suspended: TLLI, Page */ - LLGMM_RESUME_REQ, /* GMM tells us MS has resumed: TLLI */ - LLGMM_PAGE_IND, /* LLC asks GMM to page MS: TLLI */ - LLGMM_IOV_REQ, /* GMM tells us to perform XID: TLLI */ - LLGMM_STATUS_IND, /* LLC informs GMM about error: TLLI, Cause */ - /* LLE <-> (GMM/SNDCP/SMS/TOM) */ - LL_RESET_IND, /* TLLI */ - LL_ESTABLISH_REQ, /* TLLI, XID Req */ - LL_ESTABLISH_IND, /* TLLI, XID Req, N201-I, N201-U */ - LL_ESTABLISH_RESP, /* TLLI, XID Negotiated */ - LL_ESTABLISH_CONF, /* TLLI, XID Neg, N201-i, N201-U */ - LL_RELEASE_REQ, /* TLLI, Local */ - LL_RELEASE_IND, /* TLLI, Cause */ - LL_RELEASE_CONF, /* TLLI */ - LL_XID_REQ, /* TLLI, XID Requested */ - LL_XID_IND, /* TLLI, XID Req, N201-I, N201-U */ - LL_XID_RESP, /* TLLI, XID Negotiated */ - LL_XID_CONF, /* TLLI, XID Neg, N201-I, N201-U */ - LL_DATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ - LL_DATA_IND, /* TLLI, SN-PDU */ - LL_DATA_CONF, /* TLLI, Ref */ - LL_UNITDATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ - LL_UNITDATA_IND, /* TLLI, SN-PDU */ - LL_STATUS_IND, /* TLLI, Cause */ -}; - -/* Section 4.5.2 Logical Link States + Annex C.2 */ -enum gprs_llc_lle_state { - GPRS_LLES_UNASSIGNED = 1, /* No TLLI yet */ - GPRS_LLES_ASSIGNED_ADM = 2, /* TLLI assigned */ - GPRS_LLES_LOCAL_EST = 3, /* Local Establishment */ - GPRS_LLES_REMOTE_EST = 4, /* Remote Establishment */ - GPRS_LLES_ABM = 5, - GPRS_LLES_LOCAL_REL = 6, /* Local Release */ - GPRS_LLES_TIMER_REC = 7, /* Timer Recovery */ -}; - -enum gprs_llc_llme_state { - GPRS_LLMS_UNASSIGNED = 1, /* No TLLI yet */ - GPRS_LLMS_ASSIGNED = 2, /* TLLI assigned */ -}; - -/* Section 8.9.9 LLC layer parameter default values */ -struct gprs_llc_params { - uint16_t iov_i_exp; - uint16_t t200_201; - uint16_t n200; - uint16_t n201_u; - uint16_t n201_i; - uint16_t mD; - uint16_t mU; - uint16_t kD; - uint16_t kU; -}; - -/* Section 4.7.1: Logical Link Entity: One per DLCI (TLLI + SAPI) */ -struct gprs_llc_lle { - struct llist_head list; - - uint32_t sapi; - - struct gprs_llc_llme *llme; - - enum gprs_llc_lle_state state; - - struct osmo_timer_list t200; - struct osmo_timer_list t201; /* wait for acknowledgement */ - - uint16_t v_sent; - uint16_t v_ack; - uint16_t v_recv; - - uint16_t vu_send; - uint16_t vu_recv; - - /* non-standard LLC state */ - uint16_t vu_recv_last; - uint16_t vu_recv_duplicates; - - /* Overflow Counter for ABM */ - uint32_t oc_i_send; - uint32_t oc_i_recv; - - /* Overflow Counter for unconfirmed transfer */ - uint32_t oc_ui_send; - uint32_t oc_ui_recv; - - unsigned int retrans_ctr; - - struct gprs_llc_params params; -}; - -#define NUM_SAPIS 16 - -struct gprs_llc_llme { - struct llist_head list; - - enum gprs_llc_llme_state state; - - uint32_t tlli; - uint32_t old_tlli; - - /* Crypto parameters */ - enum gprs_ciph_algo algo; - uint8_t kc[8]; - - /* over which BSSGP BTS ctx do we need to transmit */ - uint16_t bvci; - uint16_t nsei; - struct gprs_llc_lle lle[NUM_SAPIS]; -}; - -extern struct llist_head gprs_llc_llmes; - -/* BSSGP-UL-UNITDATA.ind */ -int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv); - -/* LL-UNITDATA.req */ -int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command, - void *mmctx); - -/* Chapter 7.2.1.2 LLGMM-RESET.req */ -int gprs_llgmm_reset(struct gprs_llc_llme *llme); - -/* 04.64 Chapter 7.2.1.1 LLGMM-ASSIGN */ -int gprs_llgmm_assign(struct gprs_llc_llme *llme, - uint32_t old_tlli, uint32_t new_tlli, - enum gprs_ciph_algo alg, const uint8_t *kc); - -int gprs_llc_init(const char *cipher_plugin_path); -int gprs_llc_vty_init(void); - -/** - * \short Check if N(U) should be considered a retransmit - * - * Implements the range check as of GSM 04.64 8.4.2 - * Receipt of unacknowledged information. - * - * @returns Returns 1 if (V(UR)-32) <= N(U) < V(UR) - * @param nu N(U) unconfirmed sequence number of the UI frame - * @param vur V(UR) unconfirmend received state variable - */ -static inline int gprs_llc_is_retransmit(uint16_t nu, uint16_t vur) -{ - int delta = (vur - nu) & 0x1ff; - return 0 < delta && delta < 32; -} - -#endif diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h deleted file mode 100644 index 8074d8f9b..000000000 --- a/openbsc/include/openbsc/gprs_sgsn.h +++ /dev/null @@ -1,222 +0,0 @@ -#ifndef _GPRS_SGSN_H -#define _GPRS_SGSN_H - -#include -#include - -#include - -#include - -#include - -#define GSM_IMSI_LENGTH 17 -#define GSM_IMEI_LENGTH 17 -#define GSM_EXTENSION_LENGTH 15 - -struct gprs_llc_lle; - -/* TS 04.08 4.1.3.3 GMM mobility management states on the network side */ -enum gprs_mm_state { - GMM_DEREGISTERED, /* 4.1.3.3.1.1 */ - GMM_COMMON_PROC_INIT, /* 4.1.3.3.1.2 */ - GMM_REGISTERED_NORMAL, /* 4.1.3.3.2.1 */ - GMM_REGISTERED_SUSPENDED, /* 4.1.3.3.2.2 */ - GMM_DEREGISTERED_INIT, /* 4.1.3.3.1.4 */ -}; - -enum gprs_mm_ctr { - GMM_CTR_PKTS_SIG_IN, - GMM_CTR_PKTS_SIG_OUT, - GMM_CTR_PKTS_UDATA_IN, - GMM_CTR_PKTS_UDATA_OUT, - GMM_CTR_BYTES_UDATA_IN, - GMM_CTR_BYTES_UDATA_OUT, - GMM_CTR_PDP_CTX_ACT, - GMM_CTR_SUSPEND, - GMM_CTR_PAGING_PS, - GMM_CTR_PAGING_CS, - GMM_CTR_RA_UPDATE, -}; - -enum gprs_pdp_ctx { - PDP_CTR_PKTS_UDATA_IN, - PDP_CTR_PKTS_UDATA_OUT, - PDP_CTR_BYTES_UDATA_IN, - PDP_CTR_BYTES_UDATA_OUT, -}; - -enum gprs_t3350_mode { - GMM_T3350_MODE_ATT, - GMM_T3350_MODE_RAU, - GMM_T3350_MODE_PTMSI_REALL, -}; - -#define MS_RADIO_ACCESS_CAPA - -/* According to TS 03.60, Table 5: SGSN MM and PDP Contexts */ -/* Extended by 3GPP TS 23.060, Table 6: SGSN MM and PDP Contexts */ -struct sgsn_mm_ctx { - struct llist_head list; - - char imsi[GSM_IMSI_LENGTH]; - enum gprs_mm_state mm_state; - uint32_t p_tmsi; - uint32_t p_tmsi_old; /* old P-TMSI before new is confirmed */ - uint32_t p_tmsi_sig; - char imei[GSM_IMEI_LENGTH]; - /* Opt: Software Version Numbber / TS 23.195 */ - char msisdn[GSM_EXTENSION_LENGTH]; - struct gprs_ra_id ra; - uint16_t cell_id; - uint32_t cell_id_age; - uint16_t sac; /* Iu: Service Area Code */ - uint32_t sac_age;/* Iu: Service Area Code age */ - /* VLR number */ - uint32_t new_sgsn_addr; - /* Authentication Triplets */ - /* Kc */ - /* Iu: CK, IK, KSI */ - /* CKSN */ - enum gprs_ciph_algo ciph_algo; - struct { - uint8_t len; - uint8_t buf[50]; /* GSM 04.08 10.5.5.12a, extended in TS 24.008 */ - } ms_radio_access_capa; - struct { - uint8_t len; - uint8_t buf[8]; /* GSM 04.08 10.5.5.12, extended in TS 24.008 */ - } ms_network_capa; - uint16_t drx_parms; - int mnrg; /* MS reported to HLR? */ - int ngaf; /* MS reported to MSC/VLR? */ - int ppf; /* paging for GPRS + non-GPRS? */ - /* SMS Parameters */ - int recovery; - uint8_t radio_prio_sms; - - struct llist_head pdp_list; - - /* Additional bits not present in the GSM TS */ - struct gprs_llc_llme *llme; - uint32_t tlli; - uint32_t tlli_new; - uint16_t nsei; - uint16_t bvci; - struct rate_ctr_group *ctrg; - struct osmo_timer_list timer; - unsigned int T; /* Txxxx number */ - unsigned int num_T_exp; /* number of consecutive T expirations */ - - enum gprs_t3350_mode t3350_mode; - uint8_t t3370_id_type; -}; - -/* look-up a SGSN MM context based on TLLI + RAI */ -struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli, - const struct gprs_ra_id *raid); -struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t tmsi); -struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi); - -/* Allocate a new SGSN MM context */ -struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli, - const struct gprs_ra_id *raid); -void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm); - - -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 */ - struct sgsn_ggsn_ctx *ggsn; /* which GGSN serves this PDP */ - struct rate_ctr_group *ctrg; - - //unsigned int id; - struct pdp_t *lib; /* pointer to libgtp PDP ctx */ - enum pdp_ctx_state state; - enum pdp_type type; - uint32_t address; - char *apn_subscribed; - //char *apn_used; - uint16_t nsapi; /* SNDCP */ - uint16_t sapi; /* LLC */ - uint8_t ti; /* transaction identifier */ - int vplmn_allowed; - uint32_t qos_profile_subscr; - //uint32_t qos_profile_req; - //uint32_t qos_profile_neg; - uint8_t radio_prio; - uint32_t tx_npdu_nr; - uint32_t rx_npdu_nr; - uint32_t tx_gtp_snd; - uint32_t rx_gtp_snu; - //uint32_t charging_id; - int reordering_reqd; - - struct osmo_timer_list timer; - unsigned int T; /* Txxxx number */ - unsigned int num_T_exp; /* number of consecutive T expirations */ -}; - - -/* 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_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); -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 *description; -}; - -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); - -/* 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); - -#endif /* _GPRS_SGSN_H */ diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h deleted file mode 100644 index 2f5aaa98c..000000000 --- a/openbsc/include/openbsc/gsm_04_08.h +++ /dev/null @@ -1,75 +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; - -#define GSM48_ALLOC_SIZE 2048 -#define GSM48_ALLOC_HEADROOM 256 - -static inline struct msgb *gsm48_msgb_alloc(void) -{ - return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM, - "GSM 04.08"); -} - -/* config options controlling the behaviour of the lower leves */ -void gsm0408_allow_everyone(int allow); -void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause); -void gsm0408_clear_all_trans(struct gsm_network *net, int protocol); -int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg); - -int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id); -int gsm0408_new_conn(struct gsm_subscriber_connection *conn); -enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *bts, uint8_t ra); -/* don't use "enum gsm_chreq_reason_t" to avoid circular dependency */ -int get_reason_by_chreq(uint8_t ra, int neci); -void gsm_net_update_ctype(struct gsm_network *net); - -int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn); -int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, uint8_t *rand, int key_seq); -int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn); -int gsm48_send_rr_release(struct gsm_lchan *lchan); -int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv); -int gsm48_send_rr_app_info(struct gsm_subscriber_connection *conn, uint8_t apdu_id, - uint8_t apdu_len, const uint8_t *apdu); -int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_class); -int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, - uint8_t power_command, uint8_t ho_ref); - -int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg); - -/* convert a ASCII phone number to call-control BCD */ -int encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len, - int h_len, const char *input); -int decode_bcd_number(char *output, int output_len, const uint8_t *bcd_lv, - int h_len); - -int send_siemens_mrpci(struct gsm_lchan *lchan, uint8_t *classmark2_lv); -int gsm48_extract_mi(uint8_t *classmark2, int length, char *mi_string, uint8_t *mi_type); -int gsm48_paging_extract_mi(struct gsm48_pag_resp *pag, int length, char *mi_string, uint8_t *mi_type); -int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, struct msgb *msg, struct gsm_subscriber *subscr); - -int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t lchan_mode); -int gsm48_rx_rr_modif_ack(struct msgb *msg); -int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg); - -struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value); -struct msgb *gsm48_create_loc_upd_rej(uint8_t cause); -void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, - const struct gsm_lchan *lchan); - -void release_security_operation(struct gsm_subscriber_connection *conn); -void allocate_security_operation(struct gsm_subscriber_connection *conn); - -#endif diff --git a/openbsc/include/openbsc/gsm_04_08_gprs.h b/openbsc/include/openbsc/gsm_04_08_gprs.h deleted file mode 100644 index cd2a08c59..000000000 --- a/openbsc/include/openbsc/gsm_04_08_gprs.h +++ /dev/null @@ -1,381 +0,0 @@ -#ifndef _GSM48_GPRS_H -#define _GSM48_GPRS_H - -#include -#include - -/* Table 10.4 / 10.4a, GPRS Mobility Management (GMM) */ -#define GSM48_MT_GMM_ATTACH_REQ 0x01 -#define GSM48_MT_GMM_ATTACH_ACK 0x02 -#define GSM48_MT_GMM_ATTACH_COMPL 0x03 -#define GSM48_MT_GMM_ATTACH_REJ 0x04 -#define GSM48_MT_GMM_DETACH_REQ 0x05 -#define GSM48_MT_GMM_DETACH_ACK 0x06 - -#define GSM48_MT_GMM_RA_UPD_REQ 0x08 -#define GSM48_MT_GMM_RA_UPD_ACK 0x09 -#define GSM48_MT_GMM_RA_UPD_COMPL 0x0a -#define GSM48_MT_GMM_RA_UPD_REJ 0x0b - -#define GSM48_MT_GMM_PTMSI_REALL_CMD 0x10 -#define GSM48_MT_GMM_PTMSI_REALL_COMPL 0x11 -#define GSM48_MT_GMM_AUTH_CIPH_REQ 0x12 -#define GSM48_MT_GMM_AUTH_CIPH_RESP 0x13 -#define GSM48_MT_GMM_AUTH_CIPH_REJ 0x14 -#define GSM48_MT_GMM_ID_REQ 0x15 -#define GSM48_MT_GMM_ID_RESP 0x16 -#define GSM48_MT_GMM_STATUS 0x20 -#define GSM48_MT_GMM_INFO 0x21 - -/* Table 10.4a, GPRS Session Management (GSM) */ -#define GSM48_MT_GSM_ACT_PDP_REQ 0x41 -#define GSM48_MT_GSM_ACT_PDP_ACK 0x42 -#define GSM48_MT_GSM_ACT_PDP_REJ 0x43 -#define GSM48_MT_GSM_REQ_PDP_ACT 0x44 -#define GSM48_MT_GSM_REQ_PDP_ACT_REJ 0x45 -#define GSM48_MT_GSM_DEACT_PDP_REQ 0x46 -#define GSM48_MT_GSM_DEACT_PDP_ACK 0x47 -#define GSM48_MT_GSM_ACT_AA_PDP_REQ 0x50 -#define GSM48_MT_GSM_ACT_AA_PDP_ACK 0x51 -#define GSM48_MT_GSM_ACT_AA_PDP_REJ 0x52 -#define GSM48_MT_GSM_DEACT_AA_PDP_REQ 0x53 -#define GSM48_MT_GSM_DEACT_AA_PDP_ACK 0x54 -#define GSM48_MT_GSM_STATUS 0x55 - -/* Chapter 10.5.5.2 / Table 10.5.135 */ -#define GPRS_ATT_T_ATTACH 1 -#define GPRS_ATT_T_ATT_WHILE_IMSI 2 -#define GPRS_ATT_T_COMBINED 3 - -/* Chapter 10.5.5.5 / Table 10.5.138 */ -#define GPRS_DET_T_MO_GPRS 1 -#define GPRS_DET_T_MO_IMSI 2 -#define GPRS_DET_T_MO_COMBINED 3 -/* Network to MS direction */ -#define GPRS_DET_T_MT_REATT_REQ 1 -#define GPRS_DET_T_MT_REATT_NOTREQ 2 -#define GPRS_DET_T_MT_IMSI 3 - -/* Chapter 10.5.5.18 / Table 105.150 */ -#define GPRS_UPD_T_RA 0 -#define GPRS_UPD_T_RA_LA 1 -#define GPRS_UPD_T_RA_LA_IMSI_ATT 2 -#define GPRS_UPD_T_PERIODIC 3 - -enum gsm48_gprs_ie_mm { - GSM48_IE_GMM_CIPH_CKSN = 0x08, /* 10.5.1.2 */ - GSM48_IE_GMM_TIMER_READY = 0x17, /* 10.5.7.3 */ - GSM48_IE_GMM_ALLOC_PTMSI = 0x18, /* 10.5.1.4 */ - GSM48_IE_GMM_PTMSI_SIG = 0x19, /* 10.5.5.8 */ - GSM48_IE_GMM_AUTH_RAND = 0x21, /* 10.5.3.1 */ - GSM48_IE_GMM_AUTH_SRES = 0x22, /* 10.5.3.2 */ - GSM48_IE_GMM_IMEISV = 0x23, /* 10.5.1.4 */ - GSM48_IE_GMM_DRX_PARAM = 0x27, /* 10.5.5.6 */ - GSM48_IE_GMM_MS_NET_CAPA = 0x31, /* 10.5.5.12 */ - GSM48_IE_GMM_PDP_CTX_STATUS = 0x32, /* 10.5.7.1 */ - GSM48_IE_GMM_PS_LCS_CAPA = 0x33, /* 10.5.5.22 */ - GSM48_IE_GMM_GMM_MBMS_CTX_ST = 0x35, /* 10.5.7.6 */ -}; - -enum gsm48_gprs_ie_sm { - GSM48_IE_GSM_APN = 0x28, /* 10.5.6.1 */ - GSM48_IE_GSM_PROTO_CONF_OPT = 0x27, /* 10.5.6.3 */ - GSM48_IE_GSM_PDP_ADDR = 0x2b, /* 10.5.6.4 */ - GSM48_IE_GSM_AA_TMR = 0x29, /* 10.5.7.3 */ - GSM48_IE_GSM_NAME_FULL = 0x43, /* 10.5.3.5a */ - GSM48_IE_GSM_NAME_SHORT = 0x45, /* 10.5.3.5a */ - GSM48_IE_GSM_TIMEZONE = 0x46, /* 10.5.3.8 */ - GSM48_IE_GSM_UTC_AND_TZ = 0x47, /* 10.5.3.9 */ - GSM48_IE_GSM_LSA_ID = 0x48, /* 10.5.3.11 */ - - /* Fake IEs that are not present on the Layer3 air interface, - * but which we use to simplify internal APIs */ - OSMO_IE_GSM_REQ_QOS = 0xfd, - OSMO_IE_GSM_REQ_PDP_ADDR = 0xfe, -}; - -/* Chapter 9.4.15 / Table 9.4.15 */ -struct gsm48_ra_upd_ack { - uint8_t force_stby:4, /* 10.5.5.7 */ - upd_result:4; /* 10.5.5.17 */ - uint8_t ra_upd_timer; /* 10.5.7.3 */ - struct gsm48_ra_id ra_id; /* 10.5.5.15 */ - uint8_t data[0]; -} __attribute__((packed)); - -/* Chapter 10.5.7.3 */ -enum gsm48_gprs_tmr_unit { - GPRS_TMR_2SECONDS = 0 << 5, - GPRS_TMR_MINUTE = 1 << 5, - GPRS_TMR_6MINUTE = 2 << 5, - GPRS_TMR_DEACTIVATED = 3 << 5, -}; - -/* Chapter 9.4.2 / Table 9.4.2 */ -struct gsm48_attach_ack { - uint8_t att_result:4, /* 10.5.5.7 */ - force_stby:4; /* 10.5.5.1 */ - uint8_t ra_upd_timer; /* 10.5.7.3 */ - uint8_t radio_prio; /* 10.5.7.2 */ - struct gsm48_ra_id ra_id; /* 10.5.5.15 */ - uint8_t data[0]; -} __attribute__((packed)); - -/* Chapter 9.4.9 / Table 9.4.9 */ -struct gsm48_auth_ciph_req { - uint8_t ciph_alg:4, /* 10.5.5.3 */ - imeisv_req:4; /* 10.5.5.10 */ - uint8_t force_stby:4, /* 10.5.5.7 */ - ac_ref_nr:4; /* 10.5.5.19 */ - uint8_t data[0]; -} __attribute__((packed)); -/* optional: TV RAND, TV CKSN */ - -struct gsm48_auth_ciph_resp { - uint8_t ac_ref_nr:4, - spare:4; - uint8_t data[0]; -} __attribute__((packed)); - -/* Chapter 9.5.1 / Table 9.5.1 */ -struct gsm48_act_pdp_ctx_req { - uint8_t req_nsapi; - uint8_t req_llc_sapi; - uint8_t data[0]; -} __attribute__((packed)); - -/* Chapter 10.5.5.14 / Table 10.5.147 */ -enum gsm48_gmm_cause { - GMM_CAUSE_IMSI_UNKNOWN = 0x02, - GMM_CAUSE_ILLEGAL_MS = 0x03, - GMM_CAUSE_ILLEGAL_ME = 0x06, - GMM_CAUSE_GPRS_NOTALLOWED = 0x07, - GMM_CAUSE_GPRS_OTHER_NOTALLOWED = 0x08, - GMM_CAUSE_MS_ID_NOT_DERIVED = 0x09, - GMM_CAUSE_IMPL_DETACHED = 0x0a, - GMM_CAUSE_PLMN_NOTALLOWED = 0x0b, - GMM_CAUSE_LA_NOTALLOWED = 0x0c, - GMM_CAUSE_ROAMING_NOTALLOWED = 0x0d, - GMM_CAUSE_NO_GPRS_PLMN = 0x0e, - GMM_CAUSE_MSC_TEMP_NOTREACH = 0x10, - GMM_CAUSE_NET_FAIL = 0x11, - GMM_CAUSE_CONGESTION = 0x16, - GMM_CAUSE_SEM_INCORR_MSG = 0x5f, - GMM_CAUSE_INV_MAND_INFO = 0x60, - GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL = 0x61, - GMM_CAUSE_MSGT_INCOMP_P_STATE = 0x62, - GMM_CAUSE_IE_NOTEXIST_NOTIMPL = 0x63, - GMM_CAUSE_COND_IE_ERR = 0x64, - GMM_CAUSE_MSG_INCOMP_P_STATE = 0x65, - GMM_CAUSE_PROTO_ERR_UNSPEC = 0x6f, -}; - -/* Chapter 10.4.6.6 / Table 10.5.157 */ -enum gsm48_gsm_cause { - GSM_CAUSE_INSUFF_RSRC = 0x1a, - GSM_CAUSE_MISSING_APN = 0x1b, - GSM_CAUSE_UNKNOWN_PDP = 0x1c, - GSM_CAUSE_AUTH_FAILED = 0x1d, - GSM_CAUSE_ACT_REJ_GGSN = 0x1e, - GSM_CAUSE_ACT_REJ_UNSPEC = 0x1f, - GSM_CAUSE_SERV_OPT_NOTSUPP = 0x20, - GSM_CAUSE_REQ_SERV_OPT_NOTSUB = 0x21, - GSM_CAUSE_SERV_OPT_TEMP_OOO = 0x22, - GSM_CAUSE_NSAPI_IN_USE = 0x23, - GSM_CAUSE_DEACT_REGULAR = 0x24, - GSM_CAUSE_QOS_NOT_ACCEPTED = 0x25, - GSM_CAUSE_NET_FAIL = 0x26, - GSM_CAUSE_REACT_RQD = 0x27, - GSM_CAUSE_FEATURE_NOTSUPP = 0x28, - GSM_CAUSE_INVALID_TRANS_ID = 0x51, - GSM_CAUSE_SEM_INCORR_MSG = 0x5f, - GSM_CAUSE_INV_MAND_INFO = 0x60, - GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL = 0x61, - GSM_CAUSE_MSGT_INCOMP_P_STATE = 0x62, - GSM_CAUSE_IE_NOTEXIST_NOTIMPL = 0x63, - GSM_CAUSE_COND_IE_ERR = 0x64, - GSM_CAUSE_MSG_INCOMP_P_STATE = 0x65, - GSM_CAUSE_PROTO_ERR_UNSPEC = 0x6f, -}; - -/* Section 6.1.2.2: Session management states on the network side */ -enum gsm48_pdp_state { - PDP_S_INACTIVE, - PDP_S_ACTIVE_PENDING, - PDP_S_ACTIVE, - PDP_S_INACTIVE_PENDING, - PDP_S_MODIFY_PENDING, -}; - -/* Table 10.5.155/3GPP TS 24.008 */ -enum gsm48_pdp_type_org { - PDP_TYPE_ORG_ETSI = 0x00, - PDP_TYPE_ORG_IETF = 0x01, -}; -enum gsm48_pdp_type_nr { - PDP_TYPE_N_ETSI_RESERVED = 0x00, - PDP_TYPE_N_ETSI_PPP = 0x01, - PDP_TYPE_N_IETF_IPv4 = 0x21, - PDP_TYPE_N_IETF_IPv6 = 0x57, -}; - -/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ -enum gsm48_qos_reliab_class { - GSM48_QOS_RC_LLC_ACK_RLC_ACK_DATA_PROT = 2, - GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT = 3, - GSM48_QOS_RC_LLC_UN_RLC_UN_PROT_DATA = 4, - GSM48_QOS_RC_LLC_UN_RLC_UN_DATA_UN = 5, -}; - -/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ -enum gsm48_qos_preced_class { - GSM48_QOS_PC_HIGH = 1, - GSM48_QOS_PC_NORMAL = 2, - GSM48_QOS_PC_LOW = 3, -}; - -/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ -enum gsm48_qos_peak_tput { - GSM48_QOS_PEAK_TPUT_1000bps = 1, - GSM48_QOS_PEAK_TPUT_2000bps = 2, - GSM48_QOS_PEAK_TPUT_4000bps = 3, - GSM48_QOS_PEAK_TPUT_8000bps = 4, - GSM48_QOS_PEAK_TPUT_16000bps = 5, - GSM48_QOS_PEAK_TPUT_32000bps = 6, - GSM48_QOS_PEAK_TPUT_64000bps = 7, - GSM48_QOS_PEAK_TPUT_128000bps = 8, - GSM48_QOS_PEAK_TPUT_256000bps = 9, -}; - -/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ -enum gsm48_qos_mean_tput { - GSM48_QOS_MEAN_TPUT_100bph = 1, - GSM48_QOS_MEAN_TPUT_200bph = 2, - GSM48_QOS_MEAN_TPUT_500bph = 3, - GSM48_QOS_MEAN_TPUT_1000bph = 4, - GSM48_QOS_MEAN_TPUT_2000bph = 5, - GSM48_QOS_MEAN_TPUT_5000bph = 6, - GSM48_QOS_MEAN_TPUT_10000bph = 7, - GSM48_QOS_MEAN_TPUT_20000bph = 8, - GSM48_QOS_MEAN_TPUT_50000bph = 9, - GSM48_QOS_MEAN_TPUT_100kbph = 10, - GSM48_QOS_MEAN_TPUT_200kbph = 11, - GSM48_QOS_MEAN_TPUT_500kbph = 0xc, - GSM48_QOS_MEAN_TPUT_1Mbph = 0xd, - GSM48_QOS_MEAN_TPUT_2Mbph = 0xe, - GSM48_QOS_MEAN_TPUT_5Mbph = 0xf, - GSM48_QOS_MEAN_TPUT_10Mbph = 0x10, - GSM48_QOS_MEAN_TPUT_20Mbph = 0x11, - GSM48_QOS_MEAN_TPUT_50Mbph = 0x12, - GSM48_QOS_MEAN_TPUT_BEST_EFFORT = 0x1f, -}; - -/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ -enum gsm48_qos_err_sdu { - GSM48_QOS_ERRSDU_NODETECT = 1, - GSM48_QOS_ERRSDU_YES = 2, - GSM48_QOS_ERRSDU_NO = 3, -}; - -/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ -enum gsm48_qos_deliv_order { - GSM48_QOS_DO_ORDERED = 1, - GSM48_QOS_DO_UNORDERED = 2, -}; - -/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ -enum gsm48_qos_traf_class { - GSM48_QOS_TC_CONVERSATIONAL = 1, - GSM48_QOS_TC_STREAMING = 2, - GSM48_QOS_TC_INTERACTIVE = 3, - GSM48_QOS_TC_BACKGROUND = 4, -}; - -/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ -enum gsm48_qos_max_sdu_size { - /* values below in 10 octet granularity */ - GSM48_QOS_MAXSDU_1502 = 0x97, - GSM48_QOS_MAXSDU_1510 = 0x98, - GSM48_QOS_MAXSDU_1520 = 0x99, -}; - -/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ -enum gsm48_qos_max_bitrate { - GSM48_QOS_MBRATE_1k = 0x01, - GSM48_QOS_MBRATE_63k = 0x3f, - GSM48_QOS_MBRATE_64k = 0x40, - GSM48_QOS_MBRATE_568k = 0x7f, - GSM48_QOS_MBRATE_576k = 0x80, - GSM48_QOS_MBRATE_8640k = 0xfe, - GSM48_QOS_MBRATE_0k = 0xff, -}; - -/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ -enum gsm48_qos_resid_ber { - GSM48_QOS_RBER_5e_2 = 0x01, - GSM48_QOS_RBER_1e_2 = 0x02, - GSM48_QOS_RBER_5e_3 = 0x03, - GSM48_QOS_RBER_4e_3 = 0x04, - GSM48_QOS_RBER_1e_3 = 0x05, - GSM48_QOS_RBER_1e_4 = 0x06, - GSM48_QOS_RBER_1e_5 = 0x07, - GSM48_QOS_RBER_1e_6 = 0x08, - GSM48_QOS_RBER_6e_8 = 0x09, -}; - -/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ -enum gsm48_qos_sdu_err { - GSM48_QOS_SERR_1e_2 = 0x01, - GSM48_QOS_SERR_7e_2 = 0x02, - GSM48_QOS_SERR_1e_3 = 0x03, - GSM48_QOS_SERR_1e_4 = 0x04, - GSM48_QOS_SERR_1e_5 = 0x05, - GSM48_QOS_SERR_1e_6 = 0x06, - GSM48_QOS_SERR_1e_1 = 0x07, -}; - -/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ -struct gsm48_qos { - /* octet 3 */ - uint8_t reliab_class:3; - uint8_t delay_class:3; - uint8_t spare:2; - /* octet 4 */ - uint8_t preced_class:3; - uint8_t spare2:1; - uint8_t peak_tput:4; - /* octet 5 */ - uint8_t mean_tput:5; - uint8_t spare3:3; - /* octet 6 */ - uint8_t deliv_err_sdu:3; - uint8_t deliv_order:2; - uint8_t traf_class:3; - /* octet 7 */ - uint8_t max_sdu_size; - /* octet 8 */ - uint8_t max_bitrate_up; - /* octet 9 */ - uint8_t max_bitrate_down; - /* octet 10 */ - uint8_t sdu_err_ratio:4; - uint8_t resid_ber:4; - /* octet 11 */ - uint8_t handling_prio:2; - uint8_t xfer_delay:6; - /* octet 12 */ - uint8_t guar_bitrate_up; - /* octet 13 */ - uint8_t guar_bitrate_down; - /* octet 14 */ - uint8_t src_stats_desc:4; - uint8_t sig_ind:1; - uint8_t spare5:3; - /* octet 15 */ - uint8_t max_bitrate_down_ext; - /* octet 16 */ - uint8_t guar_bitrate_down_ext; -}; - - -#endif /* _GSM48_GPRS_H */ diff --git a/openbsc/include/openbsc/gsm_04_11.h b/openbsc/include/openbsc/gsm_04_11.h deleted file mode 100644 index 72b694825..000000000 --- a/openbsc/include/openbsc/gsm_04_11.h +++ /dev/null @@ -1,40 +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); -#endif diff --git a/openbsc/include/openbsc/gsm_04_80.h b/openbsc/include/openbsc/gsm_04_80.h deleted file mode 100644 index 0a6065234..000000000 --- a/openbsc/include/openbsc/gsm_04_80.h +++ /dev/null @@ -1,20 +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 ussd_request *req); -int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, - const struct msgb *msg, - const struct ussd_request *request); - -int gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn, int level, const char *text); -int gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn); - -#endif diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h deleted file mode 100644 index 874150535..000000000 --- a/openbsc/include/openbsc/gsm_data.h +++ /dev/null @@ -1,408 +0,0 @@ -#ifndef _GSM_DATA_H -#define _GSM_DATA_H - -#include - -#include -#include - -#include - -/** annotations for msgb ownership */ -#define __uses - -#define OBSC_NM_W_ACK_CB(__msgb) (__msgb)->cb[3] - -struct mncc_sock_state; - -#define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3] - -enum gsm_security_event { - GSM_SECURITY_NOAVAIL, - GSM_SECURITY_AUTH_FAILED, - GSM_SECURITY_SUCCEEDED, - GSM_SECURITY_ALREADY, -}; - -struct msgb; -typedef int gsm_cbfn(unsigned int hooknum, - unsigned int event, - struct msgb *msg, - void *data, void *param); - -/* Real authentication information containing Ki */ -enum gsm_auth_algo { - AUTH_ALGO_NONE, - AUTH_ALGO_XOR, - AUTH_ALGO_COMP128v1, -}; - -struct gsm_auth_info { - enum gsm_auth_algo auth_algo; - unsigned int a3a8_ki_len; - uint8_t a3a8_ki[16]; -}; - -struct gsm_auth_tuple { - int use_count; - int key_seq; - uint8_t rand[16]; - uint8_t sres[4]; - uint8_t kc[8]; -}; -#define GSM_KEY_SEQ_INVAL 7 /* GSM 04.08 - 10.5.1.2 */ - -/* - * LOCATION UPDATING REQUEST state - * - * Our current operation is: - * - Get imei/tmsi - * - Accept/Reject according to global policy - */ -struct gsm_loc_updating_operation { - struct osmo_timer_list updating_timer; - unsigned int waiting_for_imsi : 1; - unsigned int waiting_for_imei : 1; - unsigned int key_seq : 4; -}; - -/* - * AUTHENTICATION/CIPHERING state - */ -struct gsm_security_operation { - struct gsm_auth_tuple atuple; - gsm_cbfn *cb; - void *cb_data; -}; - -/* - * A dummy to keep a connection up for at least - * a couple of seconds to work around MSC issues. - */ -struct gsm_anchor_operation { - struct osmo_timer_list timeout; -}; - -/* Maximum number of neighbor cells whose average we track */ -#define MAX_NEIGH_MEAS 10 -/* Maximum size of the averaging window for neighbor cells */ -#define MAX_WIN_NEIGH_AVG 10 - -/* processed neighbor measurements for one cell */ -struct neigh_meas_proc { - uint16_t arfcn; - uint8_t bsic; - uint8_t rxlev[MAX_WIN_NEIGH_AVG]; - unsigned int rxlev_cnt; - uint8_t last_seen_nr; -}; - -/* the per subscriber data for lchan */ -struct gsm_subscriber_connection { - struct llist_head entry; - - /* To whom we are allocated at the moment */ - struct gsm_subscriber *subscr; - - /* LU expiration handling */ - uint8_t expire_timer_stopped; - - /* - * 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; - int put_channel; - - /* bsc structures */ - struct osmo_bsc_sccp_con *sccp_con; - - /* back pointers */ - int in_release; - struct gsm_lchan *lchan; - struct gsm_lchan *ho_lchan; - struct gsm_bts *bts; - - /* for assignment handling */ - struct osmo_timer_list T10; - struct gsm_lchan *secondary_lchan; - -}; - - -#define ROLE_BSC -#include "gsm_data_shared.h" - - -/* Some statistics of our network */ -struct gsmnet_stats { - struct { - struct osmo_counter *total; - struct osmo_counter *no_channel; - } chreq; - struct { - struct osmo_counter *attempted; - struct osmo_counter *no_channel; /* no channel available */ - struct osmo_counter *timeout; /* T3103 timeout */ - struct osmo_counter *completed; /* HO COMPL received */ - struct osmo_counter *failed; /* HO FAIL received */ - } handover; - struct { - struct osmo_counter *attach; - struct osmo_counter *normal; - struct osmo_counter *periodic; - struct osmo_counter *detach; - } loc_upd_type; - struct { - struct osmo_counter *reject; - struct osmo_counter *accept; - } loc_upd_resp; - struct { - struct osmo_counter *attempted; - struct osmo_counter *detached; - struct osmo_counter *completed; - struct osmo_counter *expired; - } paging; - struct { - struct osmo_counter *submitted; /* MO SMS submissions */ - struct osmo_counter *no_receiver; - struct osmo_counter *delivered; /* MT SMS deliveries */ - struct osmo_counter *rp_err_mem; - struct osmo_counter *rp_err_other; - } sms; - struct { - struct osmo_counter *mo_setup; - struct osmo_counter *mo_connect_ack; - struct osmo_counter *mt_setup; - struct osmo_counter *mt_connect; - } call; - struct { - struct osmo_counter *rf_fail; - struct osmo_counter *rll_err; - } chan; - struct { - struct osmo_counter *oml_fail; - struct osmo_counter *rsl_fail; - } bts; -}; - -enum gsm_auth_policy { - GSM_AUTH_POLICY_CLOSED, /* only subscribers authorized in DB */ - GSM_AUTH_POLICY_ACCEPT_ALL, /* accept everyone, even if not authorized in DB */ - GSM_AUTH_POLICY_TOKEN, /* accept first, send token per sms, then revoke authorization */ -}; - -#define GSM_T3101_DEFAULT 10 -#define GSM_T3105_DEFAULT 40 -#define GSM_T3113_DEFAULT 60 -#define GSM_T3122_DEFAULT 10 - -struct gsm_network { - /* global parameters */ - uint16_t country_code; - uint16_t network_code; - char *name_long; - char *name_short; - enum gsm_auth_policy auth_policy; - enum gsm48_reject_value reject_cause; - int a5_encryption; - int neci; - int send_mm_info; - struct { - int active; - /* Window RXLEV averaging */ - unsigned int win_rxlev_avg; /* number of SACCH frames */ - /* Window RXQUAL averaging */ - unsigned int win_rxqual_avg; /* number of SACCH frames */ - /* Window RXLEV neighbouring cells averaging */ - unsigned int win_rxlev_avg_neigh; /* number of SACCH frames */ - - /* how often should we check for power budget HO */ - unsigned int pwr_interval; /* SACCH frames */ - /* how much better does a neighbor cell have to be ? */ - unsigned int pwr_hysteresis; /* dBm */ - /* maximum distacne before we try a handover */ - unsigned int max_distance; /* TA values */ - } handover; - - struct gsmnet_stats stats; - - /* layer 4 */ - struct mncc_sock_state *mncc_state; - int (*mncc_recv) (struct gsm_network *net, struct msgb *msg); - struct llist_head upqueue; - struct llist_head trans_list; - struct bsc_api *bsc_api; - - unsigned int num_bts; - struct llist_head bts_list; - - /* timer values */ - int T3101; - int T3103; - int T3105; - int T3107; - int T3109; - int T3111; - int T3113; - int T3115; - int T3117; - int T3119; - int T3122; - int T3141; - - /* timer to expire old location updates */ - struct osmo_timer_list subscr_expire_timer; - - /* Radio Resource Location Protocol (TS 04.31) */ - struct { - enum rrlp_mode mode; - } rrlp; - - /* enable the DTXu and DTXd for this network */ - int dtx_enabled; - - enum gsm_chan_t ctype_by_chreq[16]; - - /* Use a TCH for handling requests of type paging any */ - int pag_any_tch; - - /* MSC data in case we are a true BSC */ - struct osmo_bsc_data *bsc_data; - - /* subscriber related features */ - int keep_subscr; - struct gsm_sms_queue *sms_queue; - - /* control interface */ - struct ctrl_handle *ctrl; -}; - -struct osmo_esme; - -enum gsm_sms_source_id { - SMS_SOURCE_UNKNOWN = 0, - SMS_SOURCE_MS, /* received from MS */ - SMS_SOURCE_VTY, /* received from VTY */ - SMS_SOURCE_SMPP, /* received via SMPP */ -}; - -#define SMS_HDR_SIZE 128 -#define SMS_TEXT_SIZE 256 - -struct gsm_sms_addr { - uint8_t ton; - uint8_t npi; - char addr[21+1]; -}; - -struct gsm_sms { - unsigned long long id; - struct gsm_subscriber *sender; - struct gsm_subscriber *receiver; - struct gsm_sms_addr src, dst; - enum gsm_sms_source_id source; - - struct { - struct osmo_esme *esme; - uint32_t sequence_nr; - int transaction_mode; - char msg_id[16]; - } smpp; - - unsigned long validity_minutes; - uint8_t reply_path_req; - uint8_t status_rep_req; - uint8_t ud_hdr_ind; - uint8_t protocol_id; - uint8_t data_coding_scheme; - uint8_t msg_ref; - uint8_t user_data_len; - uint8_t user_data[SMS_TEXT_SIZE]; - - char text[SMS_TEXT_SIZE]; -}; - -struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_code, - int (*mncc_recv)(struct gsm_network *, struct msgb *)); -int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type); - -struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num); - -/* Get reference to a neighbor cell on a given BCCH ARFCN */ -struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, - uint16_t arfcn, uint8_t bsic); - -enum gsm_bts_type parse_btstype(const char *arg); -const char *btstype2str(enum gsm_bts_type type); -struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, - struct gsm_bts *start_bts); - -extern void *tall_bsc_ctx; -extern int ipacc_rtp_direct; - -/* this actaully refers to the IPA transport, not the BTS model */ -static inline int is_ipaccess_bts(struct gsm_bts *bts) -{ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMO_SYSMO: - return 1; - default: - break; - } - return 0; -} - -static inline int is_siemens_bts(struct gsm_bts *bts) -{ - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - 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); -const char *bts_gprs_mode_name(enum bts_gprs_mode mode); - -int gsm48_ra_id_by_bts(uint8_t *buf, struct gsm_bts *bts); -void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts); -struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan); - -int gsm_btsmodel_set_feature(struct gsm_bts_model *model, enum gsm_bts_features feat); -int gsm_bts_model_register(struct gsm_bts_model *model); - -struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan); -void subscr_con_free(struct gsm_subscriber_connection *conn); - -struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, - enum gsm_bts_type type, - uint8_t tsc, uint8_t bsic); -void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr, - uint8_t e1_ts, uint8_t e1_ts_ss); - -void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked); -int gsm_bts_has_feature(struct gsm_bts *bts, enum gsm_bts_features feat); -struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr); - -/* 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]; - -#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 3ef145765..000000000 --- a/openbsc/include/openbsc/gsm_data_shared.h +++ /dev/null @@ -1,757 +0,0 @@ -#ifndef _GSM_DATA_SHAREDH -#define _GSM_DATA_SHAREDH - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#ifndef ROLE_BSC -#include -#endif - -struct osmo_bsc_data; - -struct osmo_bsc_sccp_con; -struct gsm_sms_queue; - -/* RRLP mode of operation */ -enum rrlp_mode { - RRLP_MODE_NONE, - RRLP_MODE_MS_BASED, - RRLP_MODE_MS_PREF, - RRLP_MODE_ASS_PREF, -}; - -/* Channel Request reason */ -enum gsm_chreq_reason_t { - GSM_CHREQ_REASON_EMERG, - GSM_CHREQ_REASON_PAG, - GSM_CHREQ_REASON_CALL, - GSM_CHREQ_REASON_LOCATION_UPD, - GSM_CHREQ_REASON_OTHER, -}; - -#define TRX_NR_TS 8 -#define TS_MAX_LCHAN 8 - -#define HARDCODED_ARFCN 123 -#define HARDCODED_TSC 7 -#define HARDCODED_BSIC 0x3f /* NCC = 7 / BCC = 7 */ - -/* for multi-drop config */ -#define HARDCODED_BTS0_TS 1 -#define HARDCODED_BTS1_TS 6 -#define HARDCODED_BTS2_TS 11 - -/* reserved according to GSM 03.03 § 2.4 */ -#define GSM_RESERVED_TMSI 0xFFFFFFFF - -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; - struct abis_om_obj_inst obj_inst; - const char *name; - struct gsm_nm_state nm_state; - struct tlv_parsed *nm_attr; - struct gsm_bts *bts; -}; - -#define MAX_A5_KEY_LEN (128/8) -#define A38_XOR_MIN_KEY_LEN 12 -#define A38_XOR_MAX_KEY_LEN 16 -#define A38_COMP128_KEY_LEN 16 -#define RSL_ENC_ALG_A5(x) (x+1) - -/* is the data link established? who established it? */ -#define LCHAN_SAPI_UNUSED 0 -#define LCHAN_SAPI_MS 1 -#define LCHAN_SAPI_NET 2 - -/* 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 amr_mode { - uint8_t mode; - uint8_t threshold; - uint8_t hysteresis; -}; -struct amr_multirate_conf { - uint8_t gsm48_ie[2]; - struct amr_mode 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; - /* Power levels for MS and BTS */ - uint8_t bs_power; - uint8_t ms_power; - /* Encryption information */ - struct { - uint8_t alg_id; - uint8_t key_len; - uint8_t key[MAX_A5_KEY_LEN]; - } encr; - - /* AMR bits */ - struct gsm48_multi_rate_conf mr_conf; - - /* Established data link layer services */ - uint8_t sapis[8]; - int sacch_deact; - - struct { - uint32_t bound_ip; - uint32_t connect_ip; - uint16_t bound_port; - uint16_t connect_port; - uint16_t conn_id; - uint8_t rtp_payload; - uint8_t rtp_payload2; - uint8_t speech_mode; -#ifdef ROLE_BSC - struct rtp_socket *rtp_socket; -#else - struct osmo_rtp_socket *rtp_socket; -#endif - } abis_ip; - - uint8_t rqd_ta; - -#ifdef ROLE_BSC - struct osmo_timer_list T3101; - struct osmo_timer_list T3109; - struct osmo_timer_list T3111; - struct osmo_timer_list error_timer; - struct osmo_timer_list act_timer; - uint8_t error_cause; - - /* table of neighbor cell measurements */ - struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS]; - - /* cache of last measurement reports on this lchan */ - struct gsm_meas_rep meas_rep[6]; - int meas_rep_idx; - - /* GSM Random Access data */ - struct gsm48_req_ref *rqd_ref; - - struct gsm_subscriber_connection *conn; -#else - /* Number of different GsmL1_Sapi_t used in osmo_bts_sysmo is 23. - * Currently we don't share these headers so this is a magic number. */ - struct llist_head sapi_cmds; - uint8_t sapis_dl[23]; - uint8_t sapis_ul[23]; - struct lapdm_channel lapdm_ch; - struct llist_head dl_tch_queue; - struct { - /* bitmask of all SI that are present/valid in si_buf */ - uint32_t valid; - uint32_t last; - /* buffers where we put the pre-computed SI */ - sysinfo_buf_t buf[_MAX_SYSINFO_TYPE]; - } si; - struct { - uint8_t flags; - /* RSL measurment result number, 0 at lchan_act */ - uint8_t res_nr; - /* current Tx power level of the BTS */ - uint8_t bts_tx_pwr; - /* number of measurements stored in array below */ - uint8_t num_ul_meas; - struct bts_ul_meas uplink[MAX_NUM_UL_MEAS]; - /* last L1 header from the MS */ - uint8_t l1_info[2]; - struct { - uint8_t rxlev_full; - uint8_t rxlev_sub; - uint8_t rxqual_full; - uint8_t rxqual_sub; - } res; - } meas; - struct { - struct amr_multirate_conf amr_mr; - struct { - uint8_t buf[16]; - uint8_t len; - } last_sid; - } tch; - /* BTS-side ciphering state (rx only, bi-directional, ...) */ - uint8_t ciph_state; - 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; -#endif -}; - -#define TS_F_PDCH_MODE 0x1000 -/* One Timeslot in a TRX */ -struct gsm_bts_trx_ts { - struct gsm_bts_trx *trx; - /* number of this timeslot at the TRX */ - uint8_t nr; - - enum gsm_phys_chan_config pchan; - - unsigned int flags; - struct gsm_abis_mo mo; - struct tlv_parsed nm_attr; - uint8_t nm_chan_comb; - int tsc; /* -1 == use BTS TSC */ - - struct { - /* Parameters below are configured by VTY */ - int enabled; - uint8_t maio; - uint8_t hsn; - struct bitvec arfcns; - uint8_t arfcns_data[1024/8]; - /* This is the pre-computed MA for channel assignments */ - struct bitvec ma; - uint8_t ma_len; /* part of ma_data that is used */ - uint8_t ma_data[8]; /* 10.5.2.21: max 8 bytes value part */ - } hopping; - - /* To which E1 subslot are we connected */ - struct gsm_e1_subslot e1_link; - - struct gsm_lchan lchan[TS_MAX_LCHAN]; -}; - -/* One TRX in a BTS */ -struct gsm_bts_trx { - /* list header in bts->trx_list */ - struct llist_head list; - - struct gsm_bts *bts; - /* number of this TRX in the BTS */ - uint8_t nr; - /* human readable name / description */ - char *description; - /* how do we talk RSL with this TRX? */ - struct gsm_e1_subslot rsl_e1_link; - uint8_t rsl_tei; -#ifdef ROLE_BSC - struct e1inp_sign_link *rsl_link; -#else - struct ipabis_link *rsl_link; -#endif - /* 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 */ - - struct { - void *l1h; - } role_bts; - - union { - struct { - struct { - struct gsm_abis_mo mo; - } bbsig; - struct { - struct gsm_abis_mo mo; - } pa; - } bs11; - struct { - unsigned int test_state; - uint8_t test_nr; - struct rxlev_stats rxlev_stat; - } ipaccess; - }; - struct gsm_bts_trx_ts ts[TRX_NR_TS]; -}; - -#define GSM_BTS_SI(bts, i) (void *)(bts->si_buf[i]) - -enum gsm_bts_type { - GSM_BTS_TYPE_UNKNOWN, - GSM_BTS_TYPE_BS11, - GSM_BTS_TYPE_NANOBTS, - GSM_BTS_TYPE_RBS2000, - GSM_BTS_TYPE_NOKIA_SITE, - GSM_BTS_TYPE_OSMO_SYSMO, - _NUM_GSM_BTS_TYPE -}; - -struct vty; - -struct gsm_bts_model { - struct llist_head list; - - enum gsm_bts_type type; - const char *name; - - bool started; - int (*start)(struct gsm_network *net); - int (*oml_rcvmsg)(struct msgb *msg); - - void (*e1line_bind_ops)(struct e1inp_line *line); - - void (*config_write_bts)(struct vty *vty, struct gsm_bts *bts); - void (*config_write_trx)(struct vty *vty, struct gsm_bts_trx *trx); - void (*config_write_ts)(struct vty *vty, struct gsm_bts_trx_ts *ts); - - struct tlv_definition nm_att_tlvdef; - - struct bitvec features; - uint8_t _features_data[128/8]; -}; - -enum gsm_bts_features { - BTS_FEAT_HSCSD, - BTS_FEAT_GPRS, - BTS_FEAT_EGPRS, - BTS_FEAT_ECSD, - BTS_FEAT_HOPPING, -}; - -/* - * This keeps track of the paging status of one BTS. It - * includes a number of pending requests, a back pointer - * to the gsm_bts, a timer and some more state. - */ -struct gsm_bts_paging_state { - /* pending requests */ - struct llist_head pending_requests; - struct gsm_bts *bts; - - struct osmo_timer_list work_timer; - struct osmo_timer_list credit_timer; - - /* free chans needed */ - int free_chans_need; - - /* load */ - uint16_t available_slots; -}; - -struct gsm_envabtse { - struct gsm_abis_mo mo; -}; - -struct gsm_bts_gprs_nsvc { - struct gsm_bts *bts; - /* data read via VTY config file, to configure the BTS - * via OML from BSC */ - int id; - uint16_t nsvci; - uint16_t local_port; /* on the BTS */ - uint16_t remote_port; /* on the SGSN */ - uint32_t remote_ip; /* on the SGSN */ - - struct gsm_abis_mo mo; -}; - -enum gprs_rlc_par { - RLC_T3142, - RLC_T3169, - RLC_T3191, - RLC_T3193, - RLC_T3195, - RLC_N3101, - RLC_N3103, - RLC_N3105, - CV_COUNTDOWN, - T_DL_TBF_EXT, /* ms */ - T_UL_TBF_EXT, /* ms */ - _NUM_RLC_PAR -}; - -enum gprs_cs { - GPRS_CS1, - GPRS_CS2, - GPRS_CS3, - GPRS_CS4, - GPRS_MCS1, - GPRS_MCS2, - GPRS_MCS3, - GPRS_MCS4, - GPRS_MCS5, - GPRS_MCS6, - GPRS_MCS7, - GPRS_MCS8, - GPRS_MCS9, - _NUM_GRPS_CS -}; - -struct gprs_rlc_cfg { - uint16_t parameter[_NUM_RLC_PAR]; - struct { - uint16_t repeat_time; /* ms */ - uint8_t repeat_count; - } paging; - uint32_t cs_mask; /* bitmask of gprs_cs */ - uint8_t initial_cs; - uint8_t initial_mcs; -}; - - -enum neigh_list_manual_mode { - NL_MODE_AUTOMATIC = 0, - NL_MODE_MANUAL = 1, - NL_MODE_MANUAL_SI5SEP = 2, /* SI2 and SI5 have separate neighbor lists */ -}; - -enum bts_loc_fix { - BTS_LOC_FIX_INVALID = 0, - BTS_LOC_FIX_2D = 1, - BTS_LOC_FIX_3D = 2, -}; - -extern const struct value_string bts_loc_fix_names[]; - -struct bts_location { - struct llist_head list; - time_t tstamp; - enum bts_loc_fix valid; - double lat; - double lon; - double height; -}; - -/* One BTS */ -struct gsm_bts { - /* list header in net->bts_list */ - struct llist_head list; - - /* Geographical location of the BTS */ - struct llist_head loc_list; - - /* number of ths BTS in network */ - uint8_t nr; - /* human readable name / description */ - char *description; - /* Cell Identity */ - uint16_t cell_identity; - /* location area code of this BTS */ - uint16_t location_area_code; - /* Training Sequence Code */ - uint8_t tsc; - /* Base Station Identification Code (BSIC) */ - uint8_t bsic; - /* type of BTS */ - enum gsm_bts_type type; - struct gsm_bts_model *model; - enum gsm_band band; - /* maximum Tx power that the MS is permitted to use in this cell */ - int ms_max_power; - - /* how do we talk OML with this TRX? */ - struct gsm_e1_subslot oml_e1_link; - uint8_t oml_tei; - struct e1inp_sign_link *oml_link; - - /* Abis network management O&M handle */ - struct abis_nm_h *nmh; - - struct gsm_abis_mo mo; - - /* number of this BTS on given E1 link */ - uint8_t bts_nr; - - /* paging state and control */ - struct gsm_bts_paging_state paging; - - /* CCCH is on C0 */ - struct gsm_bts_trx *c0; - - struct { - struct gsm_abis_mo mo; - } site_mgr; - - /* bitmask of all SI that are present/valid in si_buf */ - uint32_t si_valid; - /* buffers where we put the pre-computed SI */ - sysinfo_buf_t si_buf[_MAX_SYSINFO_TYPE]; - - /* TimeZone hours, mins, and bts specific */ - struct { - int hr; - int mn; - int override; - } tz; - - /* ip.accesss Unit ID's have Site/BTS/TRX layout */ - union { - struct { - uint16_t site_id; - uint16_t bts_id; - uint32_t flags; - uint32_t rsl_ip; - } ip_access; - struct { - struct { - struct gsm_abis_mo mo; - } cclk; - struct { - struct gsm_abis_mo mo; - } rack; - struct gsm_envabtse envabtse[4]; - } bs11; - struct { - struct { - struct gsm_abis_mo mo; - struct llist_head conn_groups; - } is; - struct { - struct gsm_abis_mo mo; - struct llist_head conn_groups; - } con; - struct { - struct gsm_abis_mo mo; - } dp; - struct { - struct gsm_abis_mo mo; - } tf; - } rbs2000; - struct { - uint8_t bts_type; - unsigned int configured:1, - skip_reset:1, - did_reset:1, - wait_reset:1; - struct osmo_timer_list reset_timer; - } nokia; - }; - - /* Not entirely sure how ip.access specific this is */ - struct { - enum bts_gprs_mode mode; - struct { - struct gsm_abis_mo mo; - uint16_t nsei; - uint8_t timer[7]; - } nse; - struct { - struct gsm_abis_mo mo; - uint16_t bvci; - uint8_t timer[11]; - struct gprs_rlc_cfg rlc_cfg; - } cell; - struct gsm_bts_gprs_nsvc nsvc[2]; - uint8_t rac; - uint8_t net_ctrl_ord; - } gprs; - - /* RACH NM values */ - int rach_b_thresh; - int rach_ldavg_slots; - - /* transceivers */ - int num_trx; - struct llist_head trx_list; - -#ifdef ROLE_BSC - /* Abis NM queue */ - struct llist_head abis_queue; - int abis_nm_pend; - - struct gsm_network *network; - - /* should the channel allocator allocate channels from high TRX to TRX0, - * rather than starting from TRX0 and go upwards? */ - int chan_alloc_reverse; - - enum neigh_list_manual_mode neigh_list_manual_mode; - /* parameters from which we build SYSTEM INFORMATION */ - struct { - struct gsm48_rach_control rach_control; - uint8_t ncc_permitted; - struct gsm48_cell_sel_par cell_sel_par; - struct gsm48_si_selection_params cell_ro_sel_par; /* rest octet */ - struct gsm48_cell_options cell_options; - struct gsm48_control_channel_descr chan_desc; - struct bitvec neigh_list; - struct bitvec cell_alloc; - struct bitvec si5_neigh_list; - struct { - /* bitmask large enough for all possible ARFCN's */ - uint8_t neigh_list[1024/8]; - uint8_t cell_alloc[1024/8]; - /* If the user wants a different neighbor list in SI5 than in SI2 */ - uint8_t si5_neigh_list[1024/8]; - } data; - } si_common; - - /* do we use static (user-defined) system information messages? (bitmask) */ - uint32_t si_mode_static; - - /* exclude the BTS from the global RF Lock handling */ - int excl_from_rf_lock; -#endif /* ROLE_BSC */ - void *role; -}; - - -struct gsm_bts *gsm_bts_alloc(void *talloc_ctx); -struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts); - -struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num); - - -const struct value_string gsm_pchant_names[10]; -const struct value_string gsm_pchant_descs[10]; -const struct value_string gsm_lchant_names[6]; -const char *gsm_pchan_name(enum gsm_phys_chan_config c); -enum gsm_phys_chan_config gsm_pchan_parse(const char *name); -const char *gsm_lchant_name(enum gsm_chan_t c); -const char *gsm_chreq_name(enum gsm_chreq_reason_t c); -char *gsm_trx_name(const struct gsm_bts_trx *trx); -char *gsm_ts_name(const struct gsm_bts_trx_ts *ts); -char *gsm_lchan_name(const struct gsm_lchan *lchan); -const char *gsm_lchans_name(enum gsm_lchan_state s); - - -void gsm_abis_mo_reset(struct gsm_abis_mo *mo); - -struct gsm_abis_mo * -gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class, - const struct abis_om_obj_inst *obj_inst); - -struct gsm_nm_state * -gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class, - const struct abis_om_obj_inst *obj_inst); -void * -gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class, - const struct abis_om_obj_inst *obj_inst); - -/* reset the state of all MO in the BTS */ -void gsm_bts_mo_reset(struct gsm_bts *bts); - -uint8_t gsm_ts2chan_nr(const struct gsm_bts_trx_ts *ts, uint8_t lchan_nr); -uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan); - -/* - * help with parsing regexps - */ -int gsm_parse_reg(void *ctx, regex_t *reg, char **str, - int argc, const char **argv) __attribute__ ((warn_unused_result)); - - - -#endif diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h deleted file mode 100644 index 6b577b0bf..000000000 --- a/openbsc/include/openbsc/gsm_subscriber.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef _GSM_SUBSCR_H -#define _GSM_SUBSCR_H - -#include "gsm_data.h" -#include - -#define GSM_IMEI_LENGTH 17 -#define GSM_IMSI_LENGTH 17 -#define GSM_NAME_LENGTH 160 - -#define GSM_EXTENSION_LENGTH 15 /* MSISDN can only be 15 digits length */ -#define GSM_MIN_EXTEN 20000 -#define GSM_MAX_EXTEN 49999 - -#define GSM_SUBSCRIBER_FIRST_CONTACT 0x00000001 -#define tmsi_from_string(str) strtoul(str, NULL, 10) - -#define GSM_SUBSCRIBER_NO_EXPIRATION 0x0 - -struct vty; - -struct gsm_equipment { - long long unsigned int id; - char imei[GSM_IMEI_LENGTH]; - char name[GSM_NAME_LENGTH]; - - struct gsm48_classmark1 classmark1; - uint8_t classmark2_len; - uint8_t classmark2[3]; - uint8_t classmark3_len; - uint8_t classmark3[14]; -}; - -struct gsm_subscriber { - struct gsm_network *net; - long long unsigned int id; - char imsi[GSM_IMSI_LENGTH]; - uint32_t tmsi; - uint16_t lac; - char name[GSM_NAME_LENGTH]; - char extension[GSM_EXTENSION_LENGTH]; - int authorized; - time_t expire_lu; - - /* 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 in_callback; - struct llist_head requests; -}; - -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_get_by_tmsi(struct gsm_network *net, - uint32_t tmsi); -struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net, - const char *imsi); -struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net, - const char *ext); -struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net, - unsigned long long id); -struct gsm_subscriber *subscr_get_or_create(struct gsm_network *net, - const char *imsi); -int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason); -void subscr_put_channel(struct gsm_subscriber *subscr); -void subscr_get_channel(struct gsm_subscriber *subscr, - int type, gsm_cbfn *cbfn, void *param); -struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_network *net, - uint32_t tmsi); -struct gsm_subscriber *subscr_active_by_imsi(struct gsm_network *net, - const char *imsi); - -int subscr_pending_requests(struct gsm_subscriber *subscr); -int subscr_pending_clear(struct gsm_subscriber *subscr); -int subscr_pending_dump(struct gsm_subscriber *subscr, struct vty *vty); -int subscr_pending_kick(struct gsm_subscriber *subscr); - -char *subscr_name(struct gsm_subscriber *subscr); - -int subscr_purge_inactive(struct gsm_network *net); -void subscr_update_from_db(struct gsm_subscriber *subscr); -void subscr_expire(struct gsm_network *net); -int subscr_update_expire_lu(struct gsm_subscriber *subscr, struct gsm_bts *bts); - -/* internal */ -struct gsm_subscriber *subscr_alloc(void); -extern struct llist_head active_subscribers; - -#endif /* _GSM_SUBSCR_H */ diff --git a/openbsc/include/openbsc/handover.h b/openbsc/include/openbsc/handover.h deleted file mode 100644 index bd0d8ad6c..000000000 --- a/openbsc/include/openbsc/handover.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _HANDOVER_H -#define _HANDOVER_H - -struct gsm_subscriber_connection; - -/* Hand over the specified logical channel to the specified new BTS. - * This is the main entry point for the actual handover algorithm, - * after it has decided it wants to initiate HO to a specific BTS */ -int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts); - -/* clear any operation for this connection */ -void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan); - -/* Return the old lchan or NULL. This is meant for audio handling */ -struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan); - -#endif /* _HANDOVER_H */ diff --git a/openbsc/include/openbsc/handover_decision.h b/openbsc/include/openbsc/handover_decision.h deleted file mode 100644 index 81078b05d..000000000 --- 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 a25a348b6..000000000 --- a/openbsc/include/openbsc/ipaccess.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef _IPACCESS_H -#define _IPACCESS_H - -#include -#include "gsm_subscriber.h" -#include -#include - -struct ipac_msgt_sccp_state { - uint8_t src_ref[3]; - uint8_t dst_ref[3]; - uint8_t trans_id; - uint8_t invoke_id; - char imsi[GSM_IMSI_LENGTH]; - uint8_t data[0]; -} __attribute__((packed)); - -/* - * @add_remove 0 for remove, 1 for add, 3 to asK - * @nr_lacs Number of extra lacs inside this package - * @lac One lac entry - */ -struct ipac_ext_lac_cmd { - uint8_t add_remove; - uint8_t nr_extra_lacs; - uint16_t lac; - uint8_t data[0]; -} __attribute__((packed)); - -/* - * methods for parsing and sending a message - */ -int ipaccess_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd); -void ipaccess_prepend_header(struct msgb *msg, int proto); -void ipaccess_prepend_header_ext(struct msgb *msg, int proto); -int ipaccess_send_pong(int fd); -int ipaccess_send_id_ack(int fd); -int ipaccess_send_id_req(int fd); - -const char *ipaccess_idtag_name(uint8_t tag); -int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len); -int ipaccess_parse_unitid(const char *str, uint16_t *site_id, uint16_t *bts_id, uint16_t *trx_id); - -int ipaccess_drop_oml(struct gsm_bts *bts); -int 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/meas_rep.h b/openbsc/include/openbsc/meas_rep.h deleted file mode 100644 index f235df6aa..000000000 --- a/openbsc/include/openbsc/meas_rep.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef _MEAS_REP_H -#define _MEAS_REP_H - -#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; -}; - -/* RX Level and RX Quality */ -struct gsm_rx_lev_qual { - uint8_t rx_lev; - uint8_t rx_qual; -}; - -/* unidirectional measumrement report */ -struct gsm_meas_rep_unidir { - struct gsm_rx_lev_qual full; - struct gsm_rx_lev_qual sub; -}; - -#define MEAS_REP_F_UL_DTX 0x01 -#define MEAS_REP_F_DL_VALID 0x02 -#define MEAS_REP_F_BA1 0x04 -#define MEAS_REP_F_DL_DTX 0x08 -#define MEAS_REP_F_MS_TO 0x10 -#define MEAS_REP_F_MS_L1 0x20 -#define MEAS_REP_F_FPC 0x40 - -/* parsed uplink and downlink measurement result */ -struct gsm_meas_rep { - /* back-pointer to the logical channel */ - struct gsm_lchan *lchan; - - /* number of the measurement report */ - uint8_t nr; - /* flags, see MEAS_REP_F_* */ - unsigned int flags; - - /* uplink and downlink rxlev, rxqual; full and sub */ - struct gsm_meas_rep_unidir ul; - struct gsm_meas_rep_unidir dl; - - uint8_t bs_power; - uint8_t ms_timing_offset; - struct { - int8_t pwr; /* MS power in dBm */ - uint8_t ta; /* MS timing advance */ - } ms_l1; - - /* neighbor measurement reports for up to 6 cells */ - int num_cell; - struct gsm_meas_rep_cell cell[6]; -}; - -enum meas_rep_field { - MEAS_REP_DL_RXLEV_FULL, - MEAS_REP_DL_RXLEV_SUB, - MEAS_REP_DL_RXQUAL_FULL, - MEAS_REP_DL_RXQUAL_SUB, - MEAS_REP_UL_RXLEV_FULL, - MEAS_REP_UL_RXLEV_SUB, - MEAS_REP_UL_RXQUAL_FULL, - MEAS_REP_UL_RXQUAL_SUB, -}; - -/* 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 811dcfda7..000000000 --- a/openbsc/include/openbsc/mgcp.h +++ /dev/null @@ -1,203 +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 "debug.h" - -#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; - -#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); - -#define PORT_ALLOC_STATIC 0 -#define PORT_ALLOC_DYNAMIC 1 - -/** - * This holds information on how to allocate ports - */ -struct mgcp_port_range { - int mode; - - /* pre-allocated from a base? */ - int base_port; - - /* dynamically allocated */ - int range_start; - int range_end; - int last_port; -}; - -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_loop; - - int omit_rtcp; - - /* spec handling */ - int force_realloc; - - unsigned int number_endpoints; - struct mgcp_endpoint *endpoints; -}; - -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; - - 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; - - 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; -}; - -/* config management */ -struct mgcp_config *mgcp_config_alloc(void); -int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg); -int mgcp_vty_init(void); -int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg); -void mgcp_free_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); - -/* - * 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); - - -#endif diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h deleted file mode 100644 index d5bd3ddc6..000000000 --- a/openbsc/include/openbsc/mgcp_internal.h +++ /dev/null @@ -1,172 +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 . - * - */ - -#ifndef OPENBSC_MGCP_DATA_H -#define OPENBSC_MGCP_DATA_H - -#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, -}; - -enum mgcp_trunk_type { - MGCP_TRUNK_VIRTUAL, - MGCP_TRUNK_E1, -}; - -struct mgcp_rtp_state { - int initialized; - int patch; - - uint32_t orig_ssrc; - uint32_t ssrc; - - uint16_t base_seq; - uint16_t max_seq; - int seq_offset; - int cycles; - - uint32_t last_timestamp; - int32_t timestamp_offset; - uint32_t jitter; - int32_t transit; -}; - -struct mgcp_rtp_end { - /* statistics */ - unsigned int packets; - unsigned int octets; - struct in_addr addr; - - /* in network byte order */ - int rtp_port, rtcp_port; - - /* per endpoint data */ - int payload_type; - char *fmtp_extra; - - /* - * 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; -}; - -enum mgcp_type { - MGCP_RTP_DEFAULT = 0, - MGCP_RTP_TRANSCODED, -}; - -struct mgcp_endpoint { - int allocated; - uint32_t ci; - char *callid; - char *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; - - /* SSRC/seq/ts patching for loop */ - int allow_patch; - - /* fields for re-transmission */ - char *last_trans; - char *last_response; - - /* tap for the endpoint */ - struct mgcp_rtp_tap taps[MGCP_TAP_COUNT]; -}; - -#define ENDPOINT_NUMBER(endp) abs(endp - endp->tcfg->endpoints) - -struct mgcp_msg_ptr { - unsigned int start; - unsigned int length; -}; - -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_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 *); - - -#endif diff --git a/openbsc/include/openbsc/misdn.h b/openbsc/include/openbsc/misdn.h deleted file mode 100644 index 9851ad32c..000000000 --- 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 4a99bb11b..000000000 --- a/openbsc/include/openbsc/mncc.h +++ /dev/null @@ -1,191 +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 GSM_TCHF_FRAME 0x0300 -#define GSM_TCHF_FRAME_EFR 0x0301 - -#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 2 -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; -}; - -char *get_mncc_name(int value); -void mncc_set_cause(struct gsm_mncc *data, int loc, int val); -void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg); - -/* input from CC code into mncc_builtin */ -int int_mncc_recv(struct gsm_network *net, struct msgb *msg); - -/* input from CC code into mncc_sock */ -int mncc_sock_from_cc(struct gsm_network *net, struct msgb *msg); - -int mncc_sock_init(struct gsm_network *gsmnet); - -#endif diff --git a/openbsc/include/openbsc/mncc_int.h b/openbsc/include/openbsc/mncc_int.h deleted file mode 100644 index 4f30f08c0..000000000 --- a/openbsc/include/openbsc/mncc_int.h +++ /dev/null @@ -1,12 +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; - -#endif diff --git a/openbsc/include/openbsc/network_listen.h b/openbsc/include/openbsc/network_listen.h deleted file mode 100644 index 67d1f4ef7..000000000 --- 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/openbscdefines.h b/openbsc/include/openbsc/openbscdefines.h deleted file mode 100644 index c6ac153b8..000000000 --- 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 1d216ac25..000000000 --- a/openbsc/include/openbsc/osmo_bsc.h +++ /dev/null @@ -1,51 +0,0 @@ -/* OpenBSC BSC code */ - -#ifndef OSMO_BSC_H -#define OSMO_BSC_H - -#include "bsc_api.h" - -#define BSS_SEND_USSD 1 - -struct sccp_connection; -struct osmo_msc_data; -struct bsc_msc_connection; - -struct osmo_bsc_sccp_con { - struct llist_head entry; - - int ciphering_handled; - int rtp_port; - - /* SCCP connection realted */ - struct sccp_connection *sccp; - struct osmo_msc_data *msc; - struct osmo_timer_list sccp_it_timeout; - struct osmo_timer_list sccp_cc_timeout; - - struct llist_head sccp_queue; - unsigned int sccp_queue_size; - - struct gsm_subscriber_connection *conn; - uint8_t new_subscriber; -}; - -struct bsc_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); -int bsc_create_new_connection(struct gsm_subscriber_connection *conn, - struct osmo_msc_data *msc); -int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp); - -struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, struct msgb *); -int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg); -int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg); -int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn); - -int bsc_handle_udt(struct osmo_msc_data *msc, struct msgb *msg, unsigned int length); -int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int len); - -int bsc_ctrl_cmds_install(); - -#endif diff --git a/openbsc/include/openbsc/osmo_bsc_grace.h b/openbsc/include/openbsc/osmo_bsc_grace.h deleted file mode 100644 index e9c1a0a07..000000000 --- a/openbsc/include/openbsc/osmo_bsc_grace.h +++ /dev/null @@ -1,31 +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 "gsm_data.h" - -struct osmo_msc_data; - -int bsc_grace_allow_new_connection(struct gsm_network *net, struct gsm_bts *bts); -int bsc_grace_paging_request(struct gsm_subscriber *sub, int type, struct osmo_msc_data *msc); - -#endif diff --git a/openbsc/include/openbsc/osmo_bsc_rf.h b/openbsc/include/openbsc/osmo_bsc_rf.h deleted file mode 100644 index a67e1bda4..000000000 --- a/openbsc/include/openbsc/osmo_bsc_rf.h +++ /dev/null @@ -1,64 +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; - - /* 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 beb3f5e4c..000000000 --- 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/osmo_msc_data.h b/openbsc/include/openbsc/osmo_msc_data.h deleted file mode 100644 index ba93a089d..000000000 --- a/openbsc/include/openbsc/osmo_msc_data.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Data for the true BSC - * - * (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 . - * - */ - -#ifndef _OSMO_MSC_DATA_H -#define _OSMO_MSC_DATA_H - -#include "bsc_msc.h" - -#include -#include - -#include - -struct osmo_bsc_rf; -struct gsm_network; - -struct gsm_audio_support { - uint8_t hr : 1, - ver : 7; -}; - -enum { - MSC_CON_TYPE_NORMAL, - MSC_CON_TYPE_LOCAL, -}; - -struct osmo_msc_data { - struct llist_head entry; - - /* Back pointer */ - struct gsm_network *network; - - int allow_emerg; - int type; - - /* local call routing */ - char *local_pref; - regex_t local_pref_reg; - - - /* Connection data */ - char *bsc_token; - int ping_timeout; - int pong_timeout; - struct osmo_timer_list ping_timer; - struct osmo_timer_list pong_timer; - struct bsc_msc_connection *msc_con; - int core_ncc; - int core_mcc; - 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; -}; - -/* - * 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; -}; - - -int osmo_bsc_msc_init(struct osmo_msc_data *msc); -int osmo_bsc_sccp_init(struct gsm_network *gsmnet); -int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto); - -int osmo_bsc_audio_init(struct gsm_network *network); - -struct osmo_msc_data *osmo_msc_data_find(struct gsm_network *, int); -struct osmo_msc_data *osmo_msc_data_alloc(struct gsm_network *, int); - - -#endif diff --git a/openbsc/include/openbsc/paging.h b/openbsc/include/openbsc/paging.h deleted file mode 100644 index 2a10f4ef1..000000000 --- a/openbsc/include/openbsc/paging.h +++ /dev/null @@ -1,75 +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 "gsm_data.h" -#include "gsm_subscriber.h" -#include - -/** - * A pending paging request - */ -struct gsm_paging_request { - /* list_head for list of all paging requests */ - struct llist_head entry; - /* the subscriber which we're paging. Later gsm_paging_request - * should probably become a part of the gsm_subscriber struct? */ - struct gsm_subscriber *subscr; - /* back-pointer to the BTS on which we are paging */ - struct gsm_bts *bts; - /* what kind of channel type do we ask the MS to establish */ - int chan_type; - - /* Timer 3113: how long do we try to page? */ - struct osmo_timer_list T3113; - - /* How often did we ask the BTS to page? */ - int attempts; - - /* callback to be called in case paging completes */ - gsm_cbfn *cbfn; - void *cbfn_param; -}; - -/* schedule paging request */ -int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr, - int type, gsm_cbfn *cbfn, void *data); -int paging_request_bts(struct gsm_bts *bts, struct gsm_subscriber *subscr, - int type, gsm_cbfn *cbfn, void *data); - -/* stop paging requests */ -void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr, - struct gsm_subscriber_connection *conn, - struct msgb *msg); - -/* update paging load */ -void paging_update_buffer_space(struct gsm_bts *bts, uint16_t); - -/* pending paging requests */ -unsigned int paging_pending_requests_nr(struct gsm_bts *bts); - -void *paging_get_data(struct gsm_bts *bts, struct gsm_subscriber *subscr); - -#endif diff --git a/openbsc/include/openbsc/rest_octets.h b/openbsc/include/openbsc/rest_octets.h deleted file mode 100644 index 56d4e0de8..000000000 --- a/openbsc/include/openbsc/rest_octets.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef _REST_OCTETS_H -#define _REST_OCTETS_H - -#include - -/* generate SI1 rest octets */ -int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net); - -struct gsm48_si_selection_params { - uint16_t penalty_time:5, - temp_offs:3, - cell_resel_off:6, - cbq:1, - present:1; -}; - -struct gsm48_si_power_offset { - uint8_t power_offset:2, - present:1; -}; - -struct gsm48_si3_gprs_ind { - uint8_t si13_position:1, - ra_colour:3, - present:1; -}; - -struct gsm48_lsa_params { - uint32_t prio_thr:3, - lsa_offset:3, - mcc:12, - mnc:12; - unsigned int present; -}; - -struct gsm48_si_ro_info { - struct gsm48_si_selection_params selection_params; - struct gsm48_si_power_offset power_offset; - uint8_t si2ter_indicator; - uint8_t early_cm_ctrl; - struct { - uint8_t where:3, - present:1; - } scheduling; - struct gsm48_si3_gprs_ind gprs_ind; - - /* SI 4 specific */ - struct gsm48_lsa_params lsa_params; - uint16_t cell_id; - uint8_t break_ind; /* do we have SI7 + SI8 ? */ -}; - - -/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ -int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3); - -/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ -int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4); - -enum pbcch_carrier_type { - PBCCH_BCCH, - PBCCH_ARFCN, - PBCCH_MAIO -}; - -/* TS 03.60 Chapter 6.3.3.1: Network Mode of Operation */ -enum gprs_nmo { - GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */ - GPRS_NMO_II = 1, /* all paging on CCCH */ - GPRS_NMO_III = 2, /* no paging coordination */ -}; - -/* TS 04.60 12.24 */ -struct gprs_cell_options { - enum gprs_nmo nmo; - /* T3168: wait for packet uplink assignment message */ - uint32_t t3168; /* in milliseconds */ - /* T3192: wait for release of the TBF after reception of the final block */ - uint32_t t3192; /* in milliseconds */ - uint32_t drx_timer_max;/* in seconds */ - uint32_t bs_cv_max; - - uint8_t ext_info_present; - struct { - uint8_t egprs_supported; - uint8_t use_egprs_p_ch_req; - uint8_t bep_period; - uint8_t pfc_supported; - uint8_t dtm_supported; - uint8_t bss_paging_coordination; - } ext_info; -}; - -/* TS 04.60 Table 12.9.2 */ -struct gprs_power_ctrl_pars { - uint8_t alpha; - uint8_t t_avg_w; - uint8_t t_avg_t; - uint8_t pc_meas_chan; - uint8_t n_avg_i; -}; - -struct gsm48_si13_info { - struct gprs_cell_options cell_opts; - struct gprs_power_ctrl_pars pwr_ctrl_pars; - uint8_t bcch_change_mark; - uint8_t si_change_field; - uint8_t pbcch_present; - - union { - struct { - uint8_t rac; - uint8_t spgc_ccch_sup; - uint8_t net_ctrl_ord; - uint8_t prio_acc_thr; - } no_pbcch; - struct { - uint8_t psi1_rep_per; - uint8_t pb; - uint8_t tsc; - uint8_t tn; - enum pbcch_carrier_type carrier_type; - uint16_t arfcn; - uint8_t maio; - } pbcch; - }; -}; - -/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */ -int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13); - -#endif /* _REST_OCTETS_H */ diff --git a/openbsc/include/openbsc/rrlp.h b/openbsc/include/openbsc/rrlp.h deleted file mode 100644 index c89402a2d..000000000 --- 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 61187ca62..000000000 --- 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 26cac0df4..000000000 --- a/openbsc/include/openbsc/rtp_proxy.h +++ /dev/null @@ -1,91 +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 - -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 f7af7509b..000000000 --- a/openbsc/include/openbsc/sgsn.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef _SGSN_H -#define _SGSN_H - - -#include - -#include -#include - -struct sgsn_config { - /* parsed from config file */ - - char *gtp_statedir; - struct sockaddr_in gtp_listenaddr; - - /* misc */ - struct gprs_ns_inst *nsi; - - int acl_enabled; - struct llist_head imsi_acl; -}; - -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; -}; - -extern struct sgsn_instance *sgsn; - -/* sgsn_vty.c */ - -int sgsn_vty_init(void); -int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg); - -/* sgsn.c */ - -/* Main input function for Gb proxy */ -int sgsn_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci); - - -struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn, - struct sgsn_mm_ctx *mmctx, - uint16_t nsapi, - struct tlv_parsed *tp); -int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx); - -/* gprs_sndcp.c */ - -/* Entry point for the SNSM-ACTIVATE.indication */ -int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi); -/* Entry point for the SNSM-DEACTIVATE.indication */ -int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi); -/* Called by SNDCP when it has received/re-assembled a N-PDU */ -int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi, - struct msgb *msg, uint32_t npdu_len, uint8_t *npdu); -int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi, - void *mmcontext); -int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, - uint8_t *hdr, uint16_t len); - -#endif diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h deleted file mode 100644 index 39319f1c9..000000000 --- a/openbsc/include/openbsc/signal.h +++ /dev/null @@ -1,244 +0,0 @@ -/* Generic signalling/notification infrastructure */ -/* (C) 2009-2010 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_PAGING signals */ -enum signal_paging { - S_PAGING_SUCCEEDED, - S_PAGING_EXPIRED, -}; - -/* SS_SMS signals */ -enum signal_sms { - S_SMS_SUBMITTED, /* A SMS has been successfully submitted to us */ - S_SMS_DELIVERED, /* A SMS has been successfully delivered to a MS */ - S_SMS_SMMA, /* A MS tells us it has more space available */ - S_SMS_MEM_EXCEEDED, /* A MS tells us it has no more space available */ - S_SMS_UNKNOWN_ERROR, /* A MS tells us it has an error */ -}; - -/* SS_ABISIP signals */ -enum signal_abisip { - S_ABISIP_CRCX_ACK, - S_ABISIP_MDCX_ACK, - S_ABISIP_DLCX_IND, -}; - -/* SS_NM signals */ -enum signal_nm { - S_NM_SW_ACTIV_REP, /* GSM 12.21 software activated report */ - S_NM_FAIL_REP, /* GSM 12.21 failure event report */ - S_NM_NACK, /* GSM 12.21 various NM_MT_*_NACK happened */ - S_NM_IPACC_NACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_NACK happened */ - S_NM_IPACC_ACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_ACK happened */ - S_NM_IPACC_RESTART_ACK, /* nanoBTS has send a restart ack */ - S_NM_IPACC_RESTART_NACK,/* nanoBTS has send a restart ack */ - S_NM_TEST_REP, /* GSM 12.21 Test Report */ - S_NM_STATECHG_OPER, /* Operational State changed*/ - S_NM_STATECHG_ADM, /* Administrative State changed */ - S_NM_OM2K_CONF_RES, /* OM2K Configuration Result */ -}; - -/* SS_LCHAN signals */ -enum signal_lchan { - /* - * The lchan got freed with an use_count != 0 and error - * recovery needs to be carried out from within the - * signal handler. - */ - S_LCHAN_UNEXPECTED_RELEASE, - S_LCHAN_ACTIVATE_ACK, /* 08.58 Channel Activate ACK */ - S_LCHAN_ACTIVATE_NACK, /* 08.58 Channel Activate NACK */ - S_LCHAN_HANDOVER_COMPL, /* 04.08 Handover Completed */ - S_LCHAN_HANDOVER_FAIL, /* 04.08 Handover Failed */ - S_LCHAN_HANDOVER_DETECT, /* 08.58 Handover Detect */ - S_LCHAN_MEAS_REP, /* 08.58 Measurement Report */ -}; - -/* SS_CHALLOC signals */ -enum signal_challoc { - S_CHALLOC_ALLOC_FAIL, /* allocation of lchan has failed */ - S_CHALLOC_FREED, /* lchan has been successfully freed */ -}; - -/* SS_SUBSCR signals */ -enum signal_subscr { - S_SUBSCR_ATTACHED, - S_SUBSCR_DETACHED, - S_SUBSCR_IDENTITY, /* we've received some identity information */ -}; - -/* SS_SCALL signals */ -enum signal_scall { - S_SCALL_SUCCESS, - S_SCALL_EXPIRED, - S_SCALL_DETACHED, -}; - -/* SS_IPAC_NWL signals */ -enum signal_ipaccess { - S_IPAC_NWL_COMPLETE, -}; - -enum signal_global { - S_GLOBAL_BTS_CLOSE_OM, -}; - -/* SS_RF signals */ -enum signal_rf { - S_RF_OFF, - S_RF_ON, - S_RF_GRACE, -}; - -struct gsm_subscriber; - -struct paging_signal_data { - struct gsm_subscriber *subscr; - struct gsm_bts *bts; - - int paging_result; - - /* NULL in case the paging didn't work */ - struct gsm_subscriber_connection *conn; -}; - -struct scall_signal_data { - struct gsm_subscriber *subscr; - struct gsm_subscriber_connection *conn; - void *data; -}; - -struct ipacc_ack_signal_data { - struct gsm_bts_trx *trx; - uint8_t msg_type; -}; - -struct abis_om2k_mo; - -struct nm_statechg_signal_data { - struct gsm_bts *bts; - uint8_t obj_class; - void *obj; - struct gsm_nm_state *old_state; - struct gsm_nm_state *new_state; - - /* This pointer is vaold for TS 12.21 MO */ - struct abis_om_obj_inst *obj_inst; - /* This pointer is vaold for RBS2000 MO */ - struct abis_om2k_mo *om2k_mo; -}; - -struct nm_om2k_signal_data { - struct gsm_bts *bts; - void *obj; - struct abis_om2k_mo *om2k_mo; - - uint8_t accordance_ind; -}; - -struct nm_nack_signal_data { - struct msgb *msg; - struct gsm_bts *bts; - uint8_t mt; -}; - -struct challoc_signal_data { - struct gsm_bts *bts; - struct gsm_lchan *lchan; - enum gsm_chan_t type; -}; - -struct rf_signal_data { - struct gsm_network *net; -}; - -struct sms_signal_data { - /* The transaction where this occured */ - struct gsm_trans *trans; - /* Can be NULL for SMMA */ - struct gsm_sms *sms; - /* int paging result. Only the ones with > 0 */ - int paging_result; -}; - -struct lchan_signal_data { - /* The lchan the signal happened on */ - struct gsm_lchan *lchan; - /* Measurement reports on this lchan */ - struct gsm_meas_rep *mr; -}; - -/* MSC signals */ -enum signal_msc { - S_MSC_LOST, - S_MSC_CONNECTED, - S_MSC_AUTHENTICATED, -}; - -struct osmo_msc_data; -struct msc_signal_data { - struct osmo_msc_data *data; -}; - -/* SS_CCCH signals */ -enum signal_ccch { - S_CCCH_PAGING_LOAD, - S_CCCH_RACH_LOAD, -}; - -struct ccch_signal_data { - struct gsm_bts *bts; - uint16_t pg_buf_space; - uint16_t rach_slot_count; - uint16_t rach_busy_count; - uint16_t rach_access_count; -}; - -#endif diff --git a/openbsc/include/openbsc/silent_call.h b/openbsc/include/openbsc/silent_call.h deleted file mode 100644 index 2492903c2..000000000 --- a/openbsc/include/openbsc/silent_call.h +++ /dev/null @@ -1,12 +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); -extern int silent_call_rx(struct gsm_subscriber_connection *conn, struct msgb *msg); -extern int silent_call_reroute(struct gsm_subscriber_connection *conn, struct msgb *msg); - -#endif /* _SILENT_CALL_H */ diff --git a/openbsc/include/openbsc/sms_queue.h b/openbsc/include/openbsc/sms_queue.h deleted file mode 100644 index 2a8bd5850..000000000 --- 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 0fd85f104..000000000 --- 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 6a5684821..000000000 --- a/openbsc/include/openbsc/system_information.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _SYSTEM_INFO_H -#define _SYSTEM_INFO_H - -#include - -struct gsm_bts; - -int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type type); - -#endif diff --git a/openbsc/include/openbsc/token_auth.h b/openbsc/include/openbsc/token_auth.h deleted file mode 100644 index 47dc7aa94..000000000 --- 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 b6c859c71..000000000 --- a/openbsc/include/openbsc/transaction.h +++ /dev/null @@ -1,75 +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; - - /* 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 gsm_network **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 *subscr, - 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_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_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 2c01b06db..000000000 --- a/openbsc/include/openbsc/trau_mux.h +++ /dev/null @@ -1,55 +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 - -/* 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); - -/* callback invoked if we receive TRAU frames */ -int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv); diff --git a/openbsc/include/openbsc/trau_upqueue.h b/openbsc/include/openbsc/trau_upqueue.h deleted file mode 100644 index ecc765843..000000000 --- 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 266546811..000000000 --- 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/vty.h b/openbsc/include/openbsc/vty.h deleted file mode 100644 index 183fc2510..000000000 --- a/openbsc/include/openbsc/vty.h +++ /dev/null @@ -1,49 +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; -extern struct cmd_element ournode_exit_cmd; -extern struct cmd_element ournode_end_cmd; - -enum bsc_vty_node { - GSMNET_NODE = _LAST_OSMOVTY_NODE + 1, - BTS_NODE, - TRX_NODE, - TS_NODE, - SUBSCR_NODE, - MGCP_NODE, - GBPROXY_NODE, - SGSN_NODE, - OML_NODE, - NAT_NODE, - NAT_BSC_NODE, - MSC_NODE, - OM2K_NODE, - TRUNK_NODE, - PGROUP_NODE, - MNCC_INT_NODE, - BSC_NODE, - SMPP_NODE, - SMPP_ESME_NODE, -}; - -extern int bsc_vty_is_config_node(struct vty *vty, int node); -extern void bsc_replace_string(void *ctx, char **dst, const char *newstr); - -struct log_info; -int bsc_vty_init(const struct log_info *cat); -int bsc_vty_init_extra(void); - -#endif diff --git a/openbsc/openbsc.pc.in b/openbsc/openbsc.pc.in deleted file mode 100644 index aba07e296..000000000 --- a/openbsc/openbsc.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@/ - -Name: OpenBSC -Description: OpenBSC base station controller -Requires: -Version: @VERSION@ -Libs: -L${libdir} -lopenbsc -Cflags: -I${includedir} diff --git a/openbsc/osmoappdesc.py b/openbsc/osmoappdesc.py deleted file mode 100644 index 9d6fbe67e..000000000 --- a/openbsc/osmoappdesc.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python - -# (C) 2013 by Katerina Barone-Adesi -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program. If not, see - - -# Most systems won't be able to use these, so they're separated out -nitb_e1_configs = [ - "doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg", - "doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg", - "doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg", - "doc/examples/osmo-nitb/bs11/openbsc.cfg", - "doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg", - "doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg", - "doc/examples/osmo-nitb/rbs2308/openbsc.cfg" -] - - -app_configs = { - "osmo-bsc": ["doc/examples/osmo-bsc/osmo-bsc.cfg"], - "nat": ["doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"], - "mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"], - "gbproxy": ["doc/examples/osmo-gbproxy/osmo-gbproxy.cfg"], - "sgsn": ["doc/examples/osmo-sgsn/osmo-sgsn.cfg"], - "nitb": ["doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg", - "doc/examples/osmo-nitb/nanobts/openbsc.cfg"] -} - - -apps = [(4242, "src/osmo-bsc/osmo-bsc", "OsmoBSC", "osmo-bsc"), - (4244, "src/osmo-bsc_nat/osmo-bsc_nat", "OsmoBSCNAT", "nat"), - (4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp"), - (4246, "src/gprs/osmo-gbproxy", "OsmoGbProxy", "gbproxy"), - (4245, "src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn"), - (4242, "src/osmo-nitb/osmo-nitb", "OpenBSC", "nitb") - ] - -vty_command = ["./src/osmo-nitb/osmo-nitb", "-c", - "doc/examples/osmo-nitb/nanobts/openbsc.cfg"] - -vty_app = apps[-1] diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am deleted file mode 100644 index 2cdbb9d1c..000000000 --- a/openbsc/src/Makefile.am +++ /dev/null @@ -1,13 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) - -SUBDIRS = libcommon libmgcp libbsc libmsc libtrau libctrl osmo-nitb osmo-bsc_mgcp utils ipaccess gprs - -# Conditional modules -if BUILD_NAT -SUBDIRS += osmo-bsc_nat -endif -if BUILD_BSC -SUBDIRS += osmo-bsc -endif diff --git a/openbsc/src/gprs/.gitignore b/openbsc/src/gprs/.gitignore deleted file mode 100644 index 7cfefbac2..000000000 --- a/openbsc/src/gprs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -gsn_restart -osmo_*.cfg* diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am deleted file mode 100644 index f4b135467..000000000 --- a/openbsc/src/gprs/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) $(LIBOSMOGB_CFLAGS) $(COVERAGE_CFLAGS) -OSMO_LIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOGB_LIBS) - -noinst_HEADERS = gprs_sndcp.h - -if HAVE_LIBGTP -bin_PROGRAMS = osmo-gbproxy osmo-sgsn -else -bin_PROGRAMS = osmo-gbproxy -endif - -osmo_gbproxy_SOURCES = gb_proxy.c gb_proxy_main.c gb_proxy_vty.c -osmo_gbproxy_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ - $(OSMO_LIBS) - -osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c gprs_sndcp_vty.c \ - sgsn_main.c sgsn_vty.c sgsn_libgtp.c \ - gprs_llc.c gprs_llc_vty.c crc24.c -osmo_sgsn_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ - -lgtp $(OSMO_LIBS) diff --git a/openbsc/src/gprs/crc24.c b/openbsc/src/gprs/crc24.c deleted file mode 100644 index 1a420ed66..000000000 --- a/openbsc/src/gprs/crc24.c +++ /dev/null @@ -1,67 +0,0 @@ -/* GPRS LLC CRC-24 Implementation */ - -/* (C) 2008-2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License 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 d753abed1..000000000 --- a/openbsc/src/gprs/gb_proxy.c +++ /dev/null @@ -1,690 +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 - -struct gbprox_peer { - struct llist_head list; - - /* NS-VC over which we send/receive data to this BVC */ - struct gprs_nsvc *nsvc; - - /* 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]; -}; - -/* Linked list of all Gb peers (except SGSN) */ -static LLIST_HEAD(gbprox_bts_peers); - -/* Find the gbprox_peer by its BVCI */ -static struct gbprox_peer *peer_by_bvci(uint16_t bvci) -{ - struct gbprox_peer *peer; - llist_for_each_entry(peer, &gbprox_bts_peers, list) { - if (peer->bvci == bvci) - return peer; - } - return NULL; -} - -static struct gbprox_peer *peer_by_nsvc(struct gprs_nsvc *nsvc) -{ - struct gbprox_peer *peer; - llist_for_each_entry(peer, &gbprox_bts_peers, list) { - if (peer->nsvc == nsvc) - return peer; - } - return NULL; -} - -/* look-up a peer by its Routeing Area Code (RAC) */ -static struct gbprox_peer *peer_by_rac(const uint8_t *ra) -{ - struct gbprox_peer *peer; - llist_for_each_entry(peer, &gbprox_bts_peers, list) { - if (!memcmp(peer->ra, ra, 6)) - return peer; - } - return NULL; -} - -/* look-up a peer by its Location Area Code (LAC) */ -static struct gbprox_peer *peer_by_lac(const uint8_t *la) -{ - struct gbprox_peer *peer; - llist_for_each_entry(peer, &gbprox_bts_peers, list) { - if (!memcmp(peer->ra, la, 5)) - return peer; - } - return NULL; -} - -static struct gbprox_peer *peer_alloc(uint16_t bvci) -{ - struct gbprox_peer *peer; - - peer = talloc_zero(tall_bsc_ctx, struct gbprox_peer); - if (!peer) - return NULL; - - peer->bvci = bvci; - llist_add(&peer->list, &gbprox_bts_peers); - - return peer; -} - -#if 0 -static void peer_free(struct gbprox_peer *peer) -{ - llist_del(&peer->list); - talloc_free(peer); -} -#endif - -/* FIXME: this needs to go to libosmocore/msgb.c */ -static struct msgb *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; - - new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data); - new_msg->l2h = new_msg->_data + (msg->l2h - msg->_data); - new_msg->l3h = new_msg->_data + (msg->l3h - msg->_data); - 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); - - new_cb->bssgph = new_msg->_data + (old_cb->bssgph - msg->_data); - 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 */ - 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; -} - -/* 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); -} - -/* feed a message down the NS-VC associated with the specified peer */ -static int gbprox_relay2sgsn(struct msgb *old_msg, 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 = msgb_copy(old_msg, "msgb_relay2sgsn"); - - DEBUGP(DGPRS, "NSEI=%u proxying BTS->SGSN (NS_BVCI=%u, NSEI=%u)\n", - msgb_nsei(msg), ns_bvci, gbcfg.nsip_sgsn_nsei); - - msgb_bvci(msg) = ns_bvci; - msgb_nsei(msg) = gbcfg.nsip_sgsn_nsei; - - strip_ns_hdr(msg); - - return gprs_ns_sendmsg(bssgp_nsi, msg); -} - -/* feed a message down the NS-VC associated with the specified peer */ -static int gbprox_relay2peer(struct msgb *old_msg, struct gbprox_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 = msgb_copy(old_msg, "msgb_relay2peer"); - - DEBUGP(DGPRS, "NSEI=%u proxying SGSN->BSS (NS_BVCI=%u, NSEI=%u)\n", - msgb_nsei(msg), ns_bvci, peer->nsvc->nsei); - - msgb_bvci(msg) = ns_bvci; - msgb_nsei(msg) = peer->nsvc->nsei; - - /* Strip the old NS header, it will be replaced with a new one */ - strip_ns_hdr(msg); - - return gprs_ns_sendmsg(bssgp_nsi, msg); -} - -static int block_unblock_peer(uint16_t ptp_bvci, uint8_t pdu_type) -{ - struct gbprox_peer *peer; - - peer = peer_by_bvci(ptp_bvci); - if (!peer) { - LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", - ptp_bvci); - return -ENOENT; - } - - switch (pdu_type) { - case BSSGP_PDUT_BVC_BLOCK_ACK: - peer->blocked = 1; - break; - case BSSGP_PDUT_BVC_UNBLOCK_ACK: - peer->blocked = 0; - 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 msgb *msg, uint16_t ptp_bvci, - uint16_t ns_bvci) -{ - struct gbprox_peer *peer; - - peer = peer_by_bvci(ptp_bvci); - if (!peer) { - LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", - ptp_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 signalling message from a BSS-side NS-VC */ -static int gbprox_rx_sig_from_bss(struct msgb *msg, struct gprs_nsvc *nsvc, - 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 gbprox_peer *from_peer; - struct gprs_ra_id raid; - - if (ns_bvci != 0 && ns_bvci != 1) { - LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u BVCI=%u is not signalling\n", - nsvc->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", nsvc->nsei); - return -EINVAL; - } - - bssgp_tlv_parse(&tp, bgph->data, data_len); - - switch (pdu_type) { - case BSSGP_PDUT_SUSPEND: - case BSSGP_PDUT_RESUME: - /* We implement RAC snooping during SUSPEND/RESUME, since - * it establishes a relationsip between BVCI/peer and the - * routeing area code. 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 = peer_by_nsvc(nsvc); - 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 " - "RAC snooping: RAC %u-%u-%u-%u behind BVCI=%u, " - "NSVCI=%u\n",nsvc->nsei, raid.mcc, raid.mnc, raid.lac, - raid.rac , from_peer->bvci, nsvc->nsvci); - /* 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", - nsvc->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", nsvc->nsei); - return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, - nsvc->nsei, 0, ns_bvci); - } - from_peer = peer_by_bvci(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 NSVCI=%u/NSEI=%u\n", bvci, - nsvc->nsvci, nsvc->nsei); - from_peer = peer_alloc(bvci); - from_peer->nsvc = nsvc; - } - 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", nsvc->nsei, - bvci, raid.mcc, raid.mnc, raid.lac, - raid.rac); - } - } - break; - } - - /* Normally, we can simply pass on all signalling messages from BSS to - * SGSN */ - return gbprox_relay2sgsn(msg, ns_bvci); -err_no_peer: - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) cannot find peer based on RAC\n", - nsvc->nsei); - return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg); -err_mand_ie: - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) missing mandatory RA IE\n", - nsvc->nsei); - 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 msgb *msg, struct tlv_parsed *tp, - struct gprs_nsvc *nsvc, uint16_t ns_bvci) -{ - struct gbprox_peer *peer = NULL; - - LOGP(DGPRS, LOGL_INFO, "NSEI=%u(SGSN) BSSGP PAGING ", - nsvc->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); - } else if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { - peer = peer_by_rac(TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); - LOGPC(DGPRS, LOGL_INFO, "routing by RAC to peer BVCI=%u\n", - peer ? peer->bvci : -1); - } else if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { - peer = peer_by_lac(TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA)); - LOGPC(DGPRS, LOGL_INFO, "routing by LAC to peer BVCI=%u\n", - peer ? peer->bvci : -1); - } else - LOGPC(DGPRS, LOGL_INFO, "\n"); - - if (!peer) { - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) BSSGP PAGING: " - "unable to route, missing IE\n", nsvc->nsei); - 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 msgb *msg, struct tlv_parsed *tp, - struct gprs_nsvc *nsvc, uint16_t ns_bvci) -{ - struct gbprox_peer *peer; - uint16_t ptp_bvci; - - if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, - NULL, 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 = peer_by_bvci(ptp_bvci); - if (!peer) { - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u BVCI=%u: Cannot find BSS\n", - nsvc->nsei, ptp_bvci); - return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, - NULL, 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, &gbprox_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 msgb *msg, struct gprs_nsvc *nsvc, - 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 gbprox_peer *peer; - uint16_t bvci; - int rc = 0; - - if (ns_bvci != 0 && ns_bvci != 1) { - LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BVCI=%u is not " - "signalling\n", nsvc->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", nsvc->nsei); - return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); - } - - rc = bssgp_tlv_parse(&tp, bgph->data, data_len); - - switch (pdu_type) { - case BSSGP_PDUT_BVC_RESET: - rc = rx_reset_from_sgsn(msg, &tp, nsvc, ns_bvci); - break; - case BSSGP_PDUT_FLUSH_LL: - case BSSGP_PDUT_BVC_RESET_ACK: - /* 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(msg, bvci, ns_bvci); - break; - case BSSGP_PDUT_PAGING_PS: - case BSSGP_PDUT_PAGING_CS: - /* process the paging request (LAC/RAC lookup) */ - rc = gbprox_rx_paging(msg, &tp, nsvc, ns_bvci); - break; - case BSSGP_PDUT_STATUS: - /* Some exception has occurred */ - LOGP(DGPRS, LOGL_NOTICE, - "NSEI=%u(SGSN) BSSGP STATUS ", nsvc->nsei); - if (!TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) { - LOGPC(DGPRS, LOGL_NOTICE, "\n"); - goto err_mand_ie; - } - LOGPC(DGPRS, LOGL_NOTICE, - "cause=0x%02x(%s) ", *TLVP_VAL(&tp, BSSGP_IE_CAUSE), - bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE))); - if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { - uint16_t bvci = tlvp_val16_unal(&tp, BSSGP_IE_BVCI); - LOGPC(DGPRS, LOGL_NOTICE, - "BVCI=%u\n", ntohs(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: - /* RAC IE is mandatory */ - if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) - goto err_mand_ie; - peer = peer_by_rac(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", nsvc->nsei, - pdu_type == BSSGP_PDUT_BVC_UNBLOCK_ACK ? "UN":""); - /* should we send STATUS ? */ - } else { - /* Mark BVC as (un)blocked */ - block_unblock_peer(bvci, pdu_type); - } - rc = gbprox_relay2bvci(msg, bvci, ns_bvci); - break; - case BSSGP_PDUT_SGSN_INVOKE_TRACE: - LOGP(DGPRS, LOGL_ERROR, - "NSEI=%u(SGSN) BSSGP INVOKE TRACE not supported\n",nsvc->nsei); - rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg); - break; - default: - LOGP(DGPRS, LOGL_NOTICE, "BSSGP PDU type 0x%02x unknown\n", - pdu_type); - rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); - break; - } - - return rc; -err_mand_ie: - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) missing mandatory IE\n", - nsvc->nsei); - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); -err_no_peer: - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) cannot find peer based on RAC\n", - nsvc->nsei); - return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg); -} - -/* Main input function for Gb proxy */ -int gbprox_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci) -{ - int rc; - struct gbprox_peer *peer; - - /* Only BVCI=0 messages need special treatment */ - if (ns_bvci == 0 || ns_bvci == 1) { - if (nsvc->remote_end_is_sgsn) - rc = gbprox_rx_sig_from_sgsn(msg, nsvc, ns_bvci); - else - rc = gbprox_rx_sig_from_bss(msg, nsvc, ns_bvci); - } else { - /* All other BVCI are PTP and thus can be simply forwarded */ - if (!nsvc->remote_end_is_sgsn) { - return gbprox_relay2sgsn(msg, ns_bvci); - } - /* else: SGSN -> BSS direction */ - peer = peer_by_bvci(ns_bvci); - if (!peer) { - LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for " - "BVCI=%u via NSVC=%u/NSEI=%u\n", ns_bvci, - nsvc->nsvci, nsvc->nsei); - peer = peer_alloc(ns_bvci); - peer->nsvc = nsvc; - } - if (peer->blocked) { - LOGP(DGPRS, LOGL_NOTICE, "Dropping PDU for " - "blocked BVCI=%u via NSVC=%u/NSEI=%u\n", - ns_bvci, nsvc->nsvci, nsvc->nsei); - return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, NULL, msg); - } - rc = gbprox_relay2peer(msg, peer, 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 ns_signal_data *nssd = signal_data; - struct gprs_nsvc *nsvc = nssd->nsvc; - struct gbprox_peer *peer; - - if (subsys != SS_L_NS) - return 0; - - if (signal == S_NS_RESET && nsvc->nsei == gbcfg.nsip_sgsn_nsei) { - /* 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"); - 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 = peer_by_nsvc(nsvc); - 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 { - /* iterate over all BTS peers and send the respective PDU */ - llist_for_each_entry(peer, &gbprox_bts_peers, list) { - switch (signal) { - case S_NS_RESET: - gprs_ns_tx_reset(peer->nsvc, nssd->cause); - break; - case S_NS_BLOCK: - gprs_ns_tx_block(peer->nsvc, nssd->cause); - break; - case S_NS_UNBLOCK: - gprs_ns_tx_unblock(peer->nsvc); - break; - } - } - } - return 0; -} - - -#include - -gDEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy", - SHOW_STR "Display information about the Gb proxy") -{ - struct gbprox_peer *peer; - - llist_for_each_entry(peer, &gbprox_bts_peers, list) { - struct gprs_nsvc *nsvc = peer->nsvc; - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, peer->ra); - - vty_out(vty, "NSEI %5u, NS-VC %5u, PTP-BVCI %5u, " - "RAC %u-%u-%u-%u", - nsvc->nsei, nsvc->nsvci, peer->bvci, - raid.mcc, raid.mnc, raid.lac, raid.rac); - if (nsvc->ll == GPRS_NS_LL_UDP || nsvc->ll == GPRS_NS_LL_FR_GRE) - vty_out(vty, " %s:%u", - inet_ntoa(nsvc->ip.bts_addr.sin_addr), - ntohs(nsvc->ip.bts_addr.sin_port)); - if (peer->blocked) - vty_out(vty, " [BVC-BLOCKED]"); - - vty_out(vty, "%s", VTY_NEWLINE); - } - return CMD_SUCCESS; -} diff --git a/openbsc/src/gprs/gb_proxy_main.c b/openbsc/src/gprs/gb_proxy_main.c deleted file mode 100644 index b5b11fdea..000000000 --- a/openbsc/src/gprs/gb_proxy_main.c +++ /dev/null @@ -1,312 +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 "../../bscconfig.h" - -/* this is here for the vty... it will never be called */ -void subscr_put() { abort(); } - -#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; -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(msg, nsvc, bvci); - 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 = strdup(optarg); - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - case 'V': - print_version(1); - exit(0); - break; - default: - break; - } - } -} - -extern void *tall_msgb_ctx; - -extern enum node_type bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OsmoGbProxy", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - -/* default categories */ -static struct log_info_cat gprs_categories[] = { - [DGPRS] = { - .name = "DGPRS", - .description = "GPRS Packet Service", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DNS] = { - .name = "DNS", - .description = "GPRS Network Service (NS)", - .enabled = 1, .loglevel = LOGL_INFO, - }, - [DBSSGP] = { - .name = "DBSSGP", - .description = "GPRS BSS Gateway Protocol (BSSGP)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, -}; - -static const struct log_info gprs_log_info = { - .filter_fn = gprs_log_filter_fn, - .cat = gprs_categories, - .num_cat = ARRAY_SIZE(gprs_categories), -}; - -int main(int argc, char **argv) -{ - struct gsm_network dummy_network; - int rc; - - tall_bsc_ctx = talloc_named_const(NULL, 0, "nsip_proxy"); - tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); - - signal(SIGINT, &signal_handler); - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - signal(SIGUSR2, &signal_handler); - osmo_init_ignore_signals(); - - osmo_init_logging(&gprs_log_info); - - vty_info.copyright = openbsc_copyright; - vty_init(&vty_info); - logging_vty_add_cmds(&gprs_log_info); - gbproxy_vty_init(); - - handle_options(argc, argv); - - rate_ctr_init(tall_bsc_ctx); - - rc = telnet_init(tall_bsc_ctx, &dummy_network, 4246); - if (rc < 0) - exit(1); - - bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_bsc_ctx); - if (!bssgp_nsi) { - LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); - exit(1); - } - 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, NULL); - - rc = gbproxy_parse_config(config_file, &gbcfg); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n"); - exit(2); - } - - if (!gprs_nsvc_by_nsei(gbcfg.nsi, gbcfg.nsip_sgsn_nsei)) { - LOGP(DGPRS, LOGL_FATAL, "You cannot proxy to NSEI %u " - "without creating that NSEI before\n", - gbcfg.nsip_sgsn_nsei); - exit(2); - } - - rc = gprs_ns_nsip_listen(bssgp_nsi); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n"); - exit(2); - } - - rc = gprs_ns_frgre_listen(bssgp_nsi); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE " - "socket. Do you have CAP_NET_RAW?\n"); - exit(2); - } - - if (daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - perror("Error during daemonize"); - exit(1); - } - } - - /* Reset all the persistent NS-VCs that we've read from the config */ - gbprox_reset_persistent_nsvcs(bssgp_nsi); - - while (1) { - rc = osmo_select_main(0); - if (rc < 0) - exit(3); - } - - exit(0); -} diff --git a/openbsc/src/gprs/gb_proxy_vty.c b/openbsc/src/gprs/gb_proxy_vty.c deleted file mode 100644 index bfa1f3bd1..000000000 --- a/openbsc/src/gprs/gb_proxy_vty.c +++ /dev/null @@ -1,106 +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 - -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 int config_write_gbproxy(struct vty *vty) -{ - vty_out(vty, "gbproxy%s", VTY_NEWLINE); - - vty_out(vty, " sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei, - 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 port = atoi(argv[0]); - - g_cfg->nsip_sgsn_nsei = port; - return CMD_SUCCESS; -} - -int gbproxy_vty_init(void) -{ - install_element_ve(&show_gbproxy_cmd); - - install_element(CONFIG_NODE, &cfg_gbproxy_cmd); - install_node(&gbproxy_node, config_write_gbproxy); - install_default(GBPROXY_NODE); - install_element(GBPROXY_NODE, &ournode_exit_cmd); - install_element(GBPROXY_NODE, &ournode_end_cmd); - install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_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_gmm.c b/openbsc/src/gprs/gprs_gmm.c deleted file mode 100644 index 1f4d22c0f..000000000 --- a/openbsc/src/gprs/gprs_gmm.c +++ /dev/null @@ -1,1634 +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-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 - -#define PTMSI_ALLOC - -/* Section 11.2.2 / Table 11.4 MM timers netwokr side */ -#define GSM0408_T3322_SECS 6 /* DETACH_REQ -> DETACH_ACC */ -#define GSM0408_T3350_SECS 6 /* waiting for ATT/RAU/TMSI COMPL */ -#define GSM0408_T3360_SECS 6 /* waiting for AUTH/CIPH RESP */ -#define GSM0408_T3370_SECS 6 /* waiting for ID RESP */ - -/* Section 11.2.2 / Table 11.4a MM timers netwokr side */ -#define GSM0408_T3313_SECS 30 /* waiting for paging response */ -#define GSM0408_T3314_SECS 44 /* force to STBY on expiry */ -#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 */ - -extern struct sgsn_instance *sgsn; - -/* Protocol related stuff, should go into libosmocore */ - -/* 10.5.5.14 GPRS MM Cause / Table 10.5.147 */ -const struct value_string gmm_cause_names[] = { - { GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown in HLR" }, - { GMM_CAUSE_ILLEGAL_MS, "Illegal MS" }, - { GMM_CAUSE_ILLEGAL_ME, "Illegal ME" }, - { GMM_CAUSE_GPRS_NOTALLOWED, "GPRS services not allowed" }, - { GMM_CAUSE_GPRS_OTHER_NOTALLOWED, - "GPRS services and non-GPRS services not allowed" }, - { GMM_CAUSE_MS_ID_NOT_DERIVED, - "MS identity cannot be derived by the network" }, - { GMM_CAUSE_IMPL_DETACHED, "Implicitly detached" }, - { GMM_CAUSE_PLMN_NOTALLOWED, "PLMN not allowed" }, - { GMM_CAUSE_LA_NOTALLOWED, "Location Area not allowed" }, - { GMM_CAUSE_ROAMING_NOTALLOWED, - "Roaming not allowed in this location area" }, - { GMM_CAUSE_NO_GPRS_PLMN, - "GPRS services not allowed in this PLMN" }, - { GMM_CAUSE_MSC_TEMP_NOTREACH, "MSC temporarily not reachable" }, - { GMM_CAUSE_NET_FAIL, "Network failure" }, - { GMM_CAUSE_CONGESTION, "Congestion" }, - { GMM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" }, - { GMM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" }, - { GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL, - "Message type non-existant or not implemented" }, - { GMM_CAUSE_MSGT_INCOMP_P_STATE, - "Message type not compatible with protocol state" }, - { GMM_CAUSE_IE_NOTEXIST_NOTIMPL, - "Information element non-existent or not implemented" }, - { GMM_CAUSE_COND_IE_ERR, "Conditional IE error" }, - { GMM_CAUSE_MSG_INCOMP_P_STATE, - "Message not compatible with protocol state " }, - { GMM_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, - { 0, NULL } -}; - -/* 10.5.6.6 SM Cause / Table 10.5.157 */ -const struct value_string gsm_cause_names[] = { - { GSM_CAUSE_INSUFF_RSRC, "Insufficient resources" }, - { GSM_CAUSE_MISSING_APN, "Missing or unknown APN" }, - { GSM_CAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" }, - { GSM_CAUSE_AUTH_FAILED, "User Authentication failed" }, - { GSM_CAUSE_ACT_REJ_GGSN, "Activation rejected by GGSN" }, - { GSM_CAUSE_ACT_REJ_UNSPEC, "Activation rejected, unspecified" }, - { GSM_CAUSE_SERV_OPT_NOTSUPP, "Service option not supported" }, - { GSM_CAUSE_REQ_SERV_OPT_NOTSUB, - "Requested service option not subscribed" }, - { GSM_CAUSE_SERV_OPT_TEMP_OOO, - "Service option temporarily out of order" }, - { GSM_CAUSE_NSAPI_IN_USE, "NSAPI already used" }, - { GSM_CAUSE_DEACT_REGULAR, "Regular deactivation" }, - { GSM_CAUSE_QOS_NOT_ACCEPTED, "QoS not accepted" }, - { GSM_CAUSE_NET_FAIL, "Network Failure" }, - { GSM_CAUSE_REACT_RQD, "Reactivation required" }, - { GSM_CAUSE_FEATURE_NOTSUPP, "Feature not supported " }, - { GSM_CAUSE_INVALID_TRANS_ID, "Invalid transaction identifier" }, - { GSM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" }, - { GSM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" }, - { GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL, - "Message type non-existant or not implemented" }, - { GSM_CAUSE_MSGT_INCOMP_P_STATE, - "Message type not compatible with protocol state" }, - { GSM_CAUSE_IE_NOTEXIST_NOTIMPL, - "Information element non-existent or not implemented" }, - { GSM_CAUSE_COND_IE_ERR, "Conditional IE error" }, - { GSM_CAUSE_MSG_INCOMP_P_STATE, - "Message not compatible with protocol state " }, - { GSM_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, - { 0, NULL } -}; - -/* 10.5.5.2 */ -const struct value_string gprs_att_t_strs[] = { - { GPRS_ATT_T_ATTACH, "GPRS attach" }, - { GPRS_ATT_T_ATT_WHILE_IMSI, "GPRS attach while IMSI attached" }, - { GPRS_ATT_T_COMBINED, "Combined GPRS/IMSI attach" }, - { 0, NULL } -}; - -const struct value_string gprs_upd_t_strs[] = { - { GPRS_UPD_T_RA, "RA updating" }, - { GPRS_UPD_T_RA_LA, "combined RA/LA updating" }, - { GPRS_UPD_T_RA_LA_IMSI_ATT, "combined RA/LA updating + IMSI attach" }, - { GPRS_UPD_T_PERIODIC, "periodic updating" }, - { 0, NULL } -}; - -/* 10.5.5.5 */ -const struct value_string gprs_det_t_mo_strs[] = { - { GPRS_DET_T_MO_GPRS, "GPRS detach" }, - { GPRS_DET_T_MO_IMSI, "IMSI detach" }, - { GPRS_DET_T_MO_COMBINED, "Combined GPRS/IMSI detach" }, - { 0, NULL } -}; - -static const struct tlv_definition gsm48_gmm_att_tlvdef = { - .def = { - [GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_FIXED, 1 }, - [GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 }, - [GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 }, - [GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 }, - [GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 }, - [GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 }, - [GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 }, - }, -}; - -static const struct tlv_definition gsm48_sm_att_tlvdef = { - .def = { - [GSM48_IE_GSM_APN] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GSM_PROTO_CONF_OPT] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GSM_PDP_ADDR] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GSM_AA_TMR] = { TLV_TYPE_TV, 1 }, - [GSM48_IE_GSM_NAME_FULL] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GSM_NAME_SHORT] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GSM_TIMEZONE] = { TLV_TYPE_FIXED, 1 }, - [GSM48_IE_GSM_UTC_AND_TZ] = { TLV_TYPE_FIXED, 7 }, - [GSM48_IE_GSM_LSA_ID] = { TLV_TYPE_TLV, 0 }, - }, -}; - -/* 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)) - LOGP(DMM, LOGL_ERROR, "Starting MM timer %u while old " - "timer %u pending\n", T, mm->T); - mm->T = T; - mm->num_T_exp = 0; - - /* FIXME: we should do this only once ? */ - mm->timer.data = mm; - mm->timer.cb = &mmctx_timer_cb; - - osmo_timer_schedule(&mm->timer, seconds, 0); -} - -static void mmctx_timer_stop(struct sgsn_mm_ctx *mm, unsigned int T) -{ - if (mm->T != T) - LOGP(DMM, LOGL_ERROR, "Stopping MM timer %u but " - "%u is running\n", T, mm->T); - osmo_timer_del(&mm->timer); -} - -/* Send a message through the underlying layer */ -static int gsm48_gmm_sendmsg(struct msgb *msg, int command, - struct sgsn_mm_ctx *mm) -{ - if (mm) - rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_SIG_OUT]); - - /* caller needs to provide TLLI, BVCI and NSEI */ - return gprs_llc_tx_ui(msg, GPRS_SAPI_GMM, command, mm); -} - -/* copy identifiers from old message to new message, this - * is required so lower layers can route it correctly */ -static void gmm_copy_id(struct msgb *msg, const struct msgb *old) -{ - msgb_tlli(msg) = msgb_tlli(old); - msgb_bvci(msg) = msgb_bvci(old); - msgb_nsei(msg) = msgb_nsei(old); -} - -/* Store BVCI/NSEI in MM context */ -static void msgid2mmctx(struct sgsn_mm_ctx *mm, const struct msgb *msg) -{ - mm->bvci = msgb_bvci(msg); - mm->nsei = msgb_nsei(msg); -} - -/* Store BVCI/NSEI in MM context */ -static void mmctx2msgid(struct msgb *msg, const struct sgsn_mm_ctx *mm) -{ - msgb_tlli(msg) = mm->tlli; - msgb_bvci(msg) = mm->bvci; - msgb_nsei(msg) = mm->nsei; -} - -/* 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(gmm_cause_names, cause)); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - if (sm) { - gh->proto_discr = GSM48_PDISC_SM_GPRS; - gh->msg_type = GSM48_MT_GSM_STATUS; - } else { - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_STATUS; - } - gh->data[0] = cause; - - return gsm48_gmm_sendmsg(msg, 0, mmctx); -} -static int gsm48_tx_gmm_status(struct sgsn_mm_ctx *mmctx, uint8_t cause) -{ - struct msgb *msg = gsm48_msgb_alloc(); - - mmctx2msgid(msg, mmctx); - return _tx_status(msg, cause, mmctx, 0); -}; -static int gsm48_tx_gmm_status_oldmsg(struct msgb *oldmsg, uint8_t cause) -{ - struct msgb *msg = gsm48_msgb_alloc(); - - gmm_copy_id(msg, oldmsg); - return _tx_status(msg, cause, NULL, 0); -} -static int gsm48_tx_sm_status(struct sgsn_mm_ctx *mmctx, uint8_t cause) -{ - struct msgb *msg = gsm48_msgb_alloc(); - - mmctx2msgid(msg, mmctx); - return _tx_status(msg, cause, mmctx, 1); -}; -static int gsm48_tx_sm_status_oldmsg(struct msgb *oldmsg, uint8_t cause) -{ - struct msgb *msg = gsm48_msgb_alloc(); - - gmm_copy_id(msg, oldmsg); - return _tx_status(msg, cause, NULL, 1); -} - - -static struct gsm48_qos default_qos = { - .delay_class = 4, /* best effort */ - .reliab_class = GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT, - .peak_tput = GSM48_QOS_PEAK_TPUT_32000bps, - .preced_class = GSM48_QOS_PC_NORMAL, - .mean_tput = GSM48_QOS_MEAN_TPUT_BEST_EFFORT, - .traf_class = GSM48_QOS_TC_INTERACTIVE, - .deliv_order = GSM48_QOS_DO_UNORDERED, - .deliv_err_sdu = GSM48_QOS_ERRSDU_YES, - .max_sdu_size = GSM48_QOS_MAXSDU_1520, - .max_bitrate_up = GSM48_QOS_MBRATE_63k, - .max_bitrate_down = GSM48_QOS_MBRATE_63k, - .resid_ber = GSM48_QOS_RBER_5e_2, - .sdu_err_ratio = GSM48_QOS_SERR_1e_2, - .handling_prio = 3, - .xfer_delay = 0x10, /* 200ms */ - .guar_bitrate_up = GSM48_QOS_MBRATE_0k, - .guar_bitrate_down = GSM48_QOS_MBRATE_0k, - .sig_ind = 0, /* not optimised for signalling */ - .max_bitrate_down_ext = 0, /* use octet 9 */ - .guar_bitrate_down_ext = 0, /* use octet 13 */ -}; - -/* Chapter 9.4.2: Attach accept */ -static int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm48_attach_ack *aa; - uint8_t *mid; -#if 0 - uint8_t *ptsig; -#endif - - DEBUGP(DMM, "<- GPRS ATTACH ACCEPT (new P-TMSI=0x%08x)\n", mm->p_tmsi); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_ATTACH_ACK; - - aa = (struct gsm48_attach_ack *) msgb_put(msg, sizeof(*aa)); - aa->force_stby = 0; /* not indicated */ - aa->att_result = 1; /* GPRS only */ - aa->ra_upd_timer = GPRS_TMR_MINUTE | 10; - 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; - - /* Optional: Negotiated Ready timer value */ -#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: MS-identity (combined attach) */ - /* Optional: GMM cause (partial attach result for combined attach) */ - - return gsm48_gmm_sendmsg(msg, 0, mm); -} - -/* Chapter 9.4.5: Attach reject */ -static int _tx_gmm_att_rej(struct msgb *msg, uint8_t gmm_cause) -{ - struct gsm48_hdr *gh; - - DEBUGP(DMM, "<- GPRS ATTACH REJECT\n"); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_ATTACH_REJ; - gh->data[0] = gmm_cause; - - return gsm48_gmm_sendmsg(msg, 0, NULL); -} -static int gsm48_tx_gmm_att_rej_oldmsg(const struct msgb *old_msg, - uint8_t gmm_cause) -{ - struct msgb *msg = gsm48_msgb_alloc(); - gmm_copy_id(msg, old_msg); - return _tx_gmm_att_rej(msg, gmm_cause); -} -static int gsm48_tx_gmm_att_rej(struct sgsn_mm_ctx *mm, - uint8_t gmm_cause) -{ - struct msgb *msg = gsm48_msgb_alloc(); - mmctx2msgid(msg, mm); - return _tx_gmm_att_rej(msg, gmm_cause); -} - -/* Chapter 9.4.6.2 Detach accept */ -static int gsm48_tx_gmm_det_ack(struct sgsn_mm_ctx *mm, uint8_t force_stby) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - DEBUGP(DMM, "<- GPRS DETACH ACCEPT\n"); - - 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_DETACH_ACK; - gh->data[0] = force_stby; - - return gsm48_gmm_sendmsg(msg, 0, mm); -} - -/* Transmit Chapter 9.4.12 Identity Request */ -static int gsm48_tx_gmm_id_req(struct sgsn_mm_ctx *mm, uint8_t id_type) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - DEBUGP(DMM, "<- GPRS IDENTITY REQUEST: mi_type=%02x\n", 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); -} - -#if 0 -/* Section 9.4.9: Authentication and Ciphering Request */ -static int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, uint8_t *rand, - uint8_t key_seq, uint8_t algo) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm48_auth_ciph_req *acreq; - uint8_t *m_rand, *m_cksn; - - DEBUGP(DMM, "<- GPRS AUTH AND CIPHERING REQ (rand = %s)\n", - osmo_hexdump(rand, 16)); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_AUTH_CIPH_REQ; - - acreq = (struct gsm48_auth_ciph_req *) msgb_put(msg, sizeof(*acreq)); - acreq->ciph_alg = algo & 0xf; - acreq->imeisv_req = 0x1; - acreq->force_stby = 0x0; - acreq->ac_ref_nr = 0x0; /* FIXME: increment this? */ - - /* Only if authentication is requested we need to set RAND + CKSN */ - if (rand) { - m_rand = msgb_put(msg, 16+1); - m_rand[0] = GSM48_IE_GMM_AUTH_RAND; - memcpy(m_rand+1, rand, 16); - - m_cksn = msgb_put(msg, 1+1); - m_cksn[0] = GSM48_IE_GMM_CIPH_CKSN; - m_cksn[1] = key_seq; - } - - /* Start T3360 */ - mmctx_timer_start(mm, 3360, GSM0408_T3360_SECS); - - /* FIXME: make sure we don't send any other messages to the MS */ - - return gsm48_gmm_sendmsg(msg, 1, mm); -} - -/* Section 9.4.11: Authentication and Ciphering Reject */ -static int gsm48_tx_gmm_auth_ciph_rej(struct sgsn_mm_ctx *mm) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - DEBUGP(DMM, "<- 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); -} -#endif - -/* 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; - int rc; - - /* FIXME: Stop T3360 */ - - rc = tlv_parse(&tp, &gsm48_gmm_att_tlvdef, acr->data, - (msg->data + msg->len) - acr->data, 0, 0); - - /* FIXME: compare ac_ref? */ - - if (!TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_SRES) || - !TLVP_PRESENT(&tp, GSM48_IE_GMM_IMEISV)) { - /* FIXME: missing mandatory IE */ - } - - /* FIXME: compare SRES with what we expected */ - /* FIXME: enable LLC cipheirng */ - return 0; -} - -/* Check if we can already authorize a subscriber */ -static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx, - enum gprs_t3350_mode t3350_mode) -{ - if (strlen(ctx->imei) && strlen(ctx->imsi)) { -#ifdef PTMSI_ALLOC - /* Start T3350 and re-transmit up to 5 times until ATTACH COMPLETE */ - ctx->t3350_mode = t3350_mode; - mmctx_timer_start(ctx, 3350, GSM0408_T3350_SECS); -#endif - ctx->mm_state = GMM_REGISTERED_NORMAL; - return gsm48_tx_gmm_att_ack(ctx); - } - if (!strlen(ctx->imei)) { - ctx->mm_state = GMM_COMMON_PROC_INIT; - ctx->t3370_id_type = GSM_MI_TYPE_IMEI; - mmctx_timer_start(ctx, 3370, GSM0408_T3370_SECS); - return gsm48_tx_gmm_id_req(ctx, GSM_MI_TYPE_IMEI); - } - - if (!strlen(ctx->imsi)) { - ctx->mm_state = GMM_COMMON_PROC_INIT; - ctx->t3370_id_type = GSM_MI_TYPE_IMSI; - mmctx_timer_start(ctx, 3370, GSM0408_T3370_SECS); - return gsm48_tx_gmm_id_req(ctx, GSM_MI_TYPE_IMSI); - } - - return 0; -} - -/* 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]); - DEBUGP(DMM, "-> GMM IDENTITY RESPONSE: mi_type=0x%02x MI(%s) ", - mi_type, mi_string); - - if (!ctx) { - DEBUGP(DMM, "from unknown TLLI 0x%08x?!?\n", msgb_tlli(msg)); - 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) { - DEBUGP(DMM, "Deleting old MM Context for same IMSI " - "p_tmsi_old=0x%08x, p_tmsi_new=0x%08x\n", - ictx->p_tmsi, ctx->p_tmsi); - gprs_llgmm_assign(ictx->llme, ictx->tlli, - 0xffffffff, GPRS_ALGO_GEA0, NULL); - /* FIXME: this is a hard free, we don't - * clean-up any PDP contexts on the - * libgtp side */ - sgsn_mm_ctx_free(ictx); - } - } - strncpy(ctx->imsi, mi_string, sizeof(ctx->imei)); - break; - case GSM_MI_TYPE_IMEI: - strncpy(ctx->imei, mi_string, sizeof(ctx->imei)); - break; - case GSM_MI_TYPE_IMEISV: - break; - } - - DEBUGPC(DMM, "\n"); - /* Check if we can let the mobile station enter */ - return gsm48_gmm_authorize(ctx, ctx->t3350_mode); -} - -/* 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, *old_ra_info, *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; - - DEBUGP(DMM, "-> GMM ATTACH REQUEST "); - - /* As per TS 04.08 Chapter 4.7.1.4, the attach request arrives either - * with a foreign TLLI (P-TMSI that was allocated to the MS before), - * or with random TLLI. */ - - cid = bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); - - /* MS network capability 10.5.5.12 */ - msnc_len = *cur++; - msnc = cur; - if (msnc_len > sizeof(ctx->ms_network_capa.buf)) - goto err_inval; - cur += msnc_len; - - /* aTTACH Type 10.5.5.2 */ - att_type = *cur++ & 0x0f; - - /* DRX parameter 10.5.5.6 */ - drx_par = *cur++ << 8; - drx_par |= *cur++; - - /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ - mi_len = *cur++; - mi = cur; - if (mi_len > 8) - goto err_inval; - mi_type = *mi & GSM_MI_TYPE_MASK; - cur += mi_len; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); - - DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, - get_value_string(gprs_att_t_strs, att_type)); - - /* Old routing area identification 10.5.5.15 */ - old_ra_info = cur; - 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; - - /* 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 - /* As a temorary hack, we simply assume that the IMSI exists, - * as long as it is part of 'our' network */ - char mccmnc[16]; - snprintf(mccmnc, sizeof(mccmnc), "%03d%02d", ra_id.mcc, ra_id.mnc); - if (strncmp(mccmnc, mi_string, 5) && - (sgsn->cfg.acl_enabled && - !sgsn_acl_lookup(mi_string))) { - LOGP(DMM, LOGL_INFO, "Rejecting ATTACH REQUESET IMSI=%s\n", - mi_string); - return gsm48_tx_gmm_att_rej_oldmsg(msg, - GMM_CAUSE_GPRS_NOTALLOWED); - } - ctx = sgsn_mm_ctx_alloc(0, &ra_id); - if (!ctx) - return gsm48_tx_gmm_att_rej_oldmsg(msg, GMM_CAUSE_NET_FAIL); - strncpy(ctx->imsi, mi_string, sizeof(ctx->imsi)); -#endif - } - ctx->tlli = msgb_tlli(msg); - ctx->llme = llme; - msgid2mmctx(ctx, msg); - break; - case GSM_MI_TYPE_TMSI: - memcpy(&tmsi, mi+1, 4); - tmsi = ntohl(tmsi); - /* Try to find MM context based on P-TMSI */ - if (!ctx) - ctx = sgsn_mm_ctx_by_ptmsi(tmsi); - if (!ctx) { - /* Allocate a context as most of our code expects one. - * Context will not have an IMSI ultil ID RESP is received */ - ctx = sgsn_mm_ctx_alloc(msgb_tlli(msg), &ra_id); - ctx->p_tmsi = tmsi; - } - ctx->tlli = msgb_tlli(msg); - ctx->llme = llme; - msgid2mmctx(ctx, msg); - break; - default: - LOGP(DMM, LOGL_NOTICE, "Rejecting ATTACH REQUEST with " - "MI type %u\n", mi_type); - return gsm48_tx_gmm_att_rej_oldmsg(msg, GMM_CAUSE_MS_ID_NOT_DERIVED); - } - /* Update MM Context with currient RA and Cell ID */ - ctx->ra = ra_id; - ctx->cell_id = cid; - /* Update MM Context with other data */ - ctx->drx_parms = drx_par; - ctx->ms_radio_access_capa.len = ms_ra_acc_cap_len; - memcpy(ctx->ms_radio_access_capa.buf, ms_ra_acc_cap, - ctx->ms_radio_access_capa.len); - ctx->ms_network_capa.len = msnc_len; - memcpy(ctx->ms_network_capa.buf, msnc, msnc_len); - -#ifdef PTMSI_ALLOC - /* Allocate a new P-TMSI (+ P-TMSI signature) and update TLLI */ - ctx->p_tmsi_old = ctx->p_tmsi; - ctx->p_tmsi = sgsn_alloc_ptmsi(); -#endif - /* Even if there is no P-TMSI allocated, the MS will switch from - * foreign TLLI to local TLLI */ - ctx->tlli_new = gprs_tmsi2tlli(ctx->p_tmsi, TLLI_LOCAL); - - /* Inform LLC layer about new TLLI but keep old active */ - gprs_llgmm_assign(ctx->llme, ctx->tlli, ctx->tlli_new, - GPRS_ALGO_GEA0, NULL); - - DEBUGPC(DMM, "\n"); - return gsm48_gmm_authorize(ctx, GMM_T3350_MODE_ATT); - -err_inval: - DEBUGPC(DMM, "\n"); - return gsm48_tx_gmm_att_rej_oldmsg(msg, GMM_CAUSE_SEM_INCORR_MSG); -} - -/* 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); - struct sgsn_pdp_ctx *pdp, *pdp2; - uint8_t detach_type, power_off; - int rc; - - 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 */ - - DEBUGP(DMM, "-> 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" : ""); - - /* Mark MM state as deregistered */ - ctx->mm_state = GMM_DEREGISTERED; - - /* delete all existing PDP contexts for this MS */ - llist_for_each_entry_safe(pdp, pdp2, &ctx->pdp_list, list) { - LOGP(DMM, LOGL_NOTICE, "Dropping PDP context for NSAPI=%u " - "due to GPRS DETACH REQUEST\n", pdp->nsapi); - sgsn_delete_pdp_ctx(pdp); - /* FIXME: the callback wants to transmit a DEACT PDP CTX ACK, - * which is quite stupid for a MS that has just detached.. */ - } - - /* force_stby = 0 */ - rc = gsm48_tx_gmm_det_ack(ctx, 0); - - /* TLLI unassignment */ - gprs_llgmm_assign(ctx->llme, ctx->tlli, 0xffffffff, - GPRS_ALGO_GEA0, NULL); - - return rc; -} - -/* Chapter 9.4.15: Routing area update accept */ -static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm48_ra_upd_ack *rua; - uint8_t *mid; - - DEBUGP(DMM, "<- 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_TMR_MINUTE | 10; - - 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 - - /* Option: MS ID, ... */ - return gsm48_gmm_sendmsg(msg, 0, mm); -} - -/* Chapter 9.4.17: Routing area update reject */ -static int gsm48_tx_gmm_ra_upd_rej(struct msgb *old_msg, uint8_t cause) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - DEBUGP(DMM, "<- ROUTING AREA UPDATE REJECT\n"); - - gmm_copy_id(msg, old_msg); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_RA_UPD_REJ; - gh->data[0] = cause; - gh->data[1] = 0; /* ? */ - - /* Option: P-TMSI signature, allocated P-TMSI, MS ID, ... */ - return gsm48_gmm_sendmsg(msg, 0, NULL); -} - -static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx, - const uint8_t *pdp_status) -{ - struct sgsn_pdp_ctx *pdp, *pdp2; - /* 24.008 4.7.5.1.3: If the PDP context status information element is - * included in ROUTING AREA UPDATE REQUEST message, then the network - * shall deactivate all those PDP contexts locally (without peer to - * peer signalling between the MS and the network), which are not in SM - * state PDP-INACTIVE on network side but are indicated by the MS as - * being in state PDP-INACTIVE. */ - - llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) { - if (pdp->nsapi < 8) { - if (!(pdp_status[0] & (1 << pdp->nsapi))) { - LOGP(DMM, LOGL_NOTICE, "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)))) { - LOGP(DMM, LOGL_NOTICE, "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) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t *cur = gh->data; - uint8_t *ms_ra_acc_cap; - uint8_t ms_ra_acc_cap_len; - struct gprs_ra_id old_ra_id; - struct tlv_parsed tp; - uint8_t upd_type; - int rc; - - /* Update Type 10.5.5.18 */ - upd_type = *cur++ & 0x0f; - - DEBUGP(DMM, "-> GMM RA UPDATE REQUEST type=\"%s\" ", - 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++; - ms_ra_acc_cap = cur; - if (ms_ra_acc_cap_len > 52) - return gsm48_tx_gmm_ra_upd_rej(msg, GMM_CAUSE_PROTO_ERR_UNSPEC); - cur += ms_ra_acc_cap_len; - - /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status, - * DRX parameter, MS network capability */ - rc = 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: - DEBUGPC(DMM, " unsupported in Mode III, is your SI13 corrupt?\n"); - return gsm48_tx_gmm_ra_upd_rej(msg, GMM_CAUSE_PROTO_ERR_UNSPEC); - break; - case GPRS_UPD_T_RA: - case GPRS_UPD_T_PERIODIC: - break; - } - - /* Look-up the MM context based on old RA-ID and TLLI */ - mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &old_ra_id); - if (!mmctx || mmctx->mm_state == GMM_DEREGISTERED) { - /* send a XID reset to re-set all LLC sequence numbers - * in the MS */ - DEBUGPC(DMM, " LLC XID RESET "); - gprs_llgmm_reset(llme); - /* The MS has to perform GPRS attach */ - DEBUGPC(DMM, " REJECT\n"); - /* Device is still IMSI atached for CS but initiate GPRS ATTACH */ - return gsm48_tx_gmm_ra_upd_rej(msg, GMM_CAUSE_MS_ID_NOT_DERIVED); - } - - /* Store new BVCI/NSEI in MM context (FIXME: delay until we ack?) */ - msgid2mmctx(mmctx, msg); - /* Bump the statistics of received signalling msgs for this MM context */ - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); - - /* Update the MM context with the new RA-ID */ - bssgp_parse_cell_id(&mmctx->ra, msgb_bcid(msg)); - /* Update the MM context with the new (i.e. foreign) TLLI */ - mmctx->tlli = msgb_tlli(msg); - /* FIXME: Update the MM context with the MS radio acc capabilities */ - /* FIXME: Update the MM context with the MS network capabilities */ - - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_RA_UPDATE]); - - DEBUGPC(DMM, " ACCEPT\n"); -#ifdef PTMSI_ALLOC - 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, GSM0408_T3350_SECS); -#endif - /* Even if there is no P-TMSI allocated, the MS will switch from - * foreign TLLI to local TLLI */ - mmctx->tlli_new = gprs_tmsi2tlli(mmctx->p_tmsi, TLLI_LOCAL); - - /* Inform LLC layer about new TLLI but keep old active */ - gprs_llgmm_assign(mmctx->llme, mmctx->tlli, mmctx->tlli_new, - GPRS_ALGO_GEA0, NULL); - - /* Look at PDP Context Status IE and see if MS's view of - * activated/deactivated NSAPIs agrees with our view */ - if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) { - const uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS); - process_ms_ctx_status(mmctx, pdp_status); - } - - /* Make sure we are NORMAL (i.e. not SUSPENDED anymore) */ - mmctx->mm_state = GMM_REGISTERED_NORMAL; - - /* Send RA UPDATE ACCEPT */ - return gsm48_tx_gmm_ra_upd_ack(mmctx); -} - -static int gsm48_rx_gmm_status(struct sgsn_mm_ctx *mmctx, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DMM, "-> GPRS MM STATUS (cause: %s)\n", - get_value_string(gmm_cause_names, gh->data[0])); - - return 0; -} - -/* GPRS Mobility Management */ -static int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg, - struct gprs_llc_llme *llme) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - int rc; - - /* MMCTX can be NULL when called */ - - if (!mmctx && - gh->msg_type != GSM48_MT_GMM_ATTACH_REQ && - gh->msg_type != GSM48_MT_GMM_RA_UPD_REQ) { - LOGP(DMM, LOGL_NOTICE, "Cannot handle GMM for unknown MM CTX\n"); - gprs_llgmm_reset(llme); - return gsm48_tx_gmm_status_oldmsg(msg, GMM_CAUSE_MS_ID_NOT_DERIVED); - } - - 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_ID_RESP: - rc = gsm48_rx_gmm_id_resp(mmctx, msg); - break; - case GSM48_MT_GMM_STATUS: - rc = gsm48_rx_gmm_status(mmctx, msg); - break; - case GSM48_MT_GMM_DETACH_REQ: - rc = gsm48_rx_gmm_det_req(mmctx, msg); - break; - case GSM48_MT_GMM_ATTACH_COMPL: - /* only in case SGSN offered new P-TMSI */ - DEBUGP(DMM, "-> ATTACH COMPLETE\n"); - mmctx_timer_stop(mmctx, 3350); - mmctx->p_tmsi_old = 0; - /* Unassign the old TLLI */ - mmctx->tlli = mmctx->tlli_new; - gprs_llgmm_assign(mmctx->llme, 0xffffffff, mmctx->tlli_new, - GPRS_ALGO_GEA0, NULL); - rc = 0; - break; - case GSM48_MT_GMM_RA_UPD_COMPL: - /* only in case SGSN offered new P-TMSI */ - DEBUGP(DMM, "-> ROUTEING AREA UPDATE COMPLETE\n"); - mmctx_timer_stop(mmctx, 3350); - mmctx->p_tmsi_old = 0; - /* Unassign the old TLLI */ - mmctx->tlli = mmctx->tlli_new; - gprs_llgmm_assign(mmctx->llme, 0xffffffff, mmctx->tlli_new, - GPRS_ALGO_GEA0, NULL); - rc = 0; - break; - case GSM48_MT_GMM_PTMSI_REALL_COMPL: - DEBUGP(DMM, "-> PTMSI REALLLICATION COMPLETE\n"); - mmctx_timer_stop(mmctx, 3350); - mmctx->p_tmsi_old = 0; - /* Unassign the old TLLI */ - mmctx->tlli = mmctx->tlli_new; - //gprs_llgmm_assign(mmctx->llme, 0xffffffff, mmctx->tlli_new, GPRS_ALGO_GEA0, NULL); - rc = 0; - break; - case GSM48_MT_GMM_AUTH_CIPH_RESP: - rc = gsm48_rx_gmm_auth_ciph_resp(mmctx, msg); - break; - default: - DEBUGP(DMM, "Unknown GSM 04.08 GMM msg type 0x%02x\n", - gh->msg_type); - rc = gsm48_tx_gmm_status(mmctx, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL); - break; - } - - return rc; -} - -static void mmctx_timer_cb(void *_mm) -{ - struct sgsn_mm_ctx *mm = _mm; - - mm->num_T_exp++; - - switch (mm->T) { - case 3350: /* waiting for ATTACH COMPLETE */ - if (mm->num_T_exp >= 5) { - LOGP(DMM, LOGL_NOTICE, "T3350 expired >= 5 times\n"); - mm->mm_state = GMM_DEREGISTERED; - /* 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; - } - osmo_timer_schedule(&mm->timer, GSM0408_T3350_SECS, 0); - break; - case 3360: /* waiting for AUTH AND CIPH RESP */ - if (mm->num_T_exp >= 5) { - LOGP(DMM, LOGL_NOTICE, "T3360 expired >= 5 times\n"); - mm->mm_state = GMM_DEREGISTERED; - break; - } - /* FIXME: re-transmit the respective msg and re-start timer */ - osmo_timer_schedule(&mm->timer, GSM0408_T3360_SECS, 0); - break; - case 3370: /* waiting for IDENTITY RESPONSE */ - if (mm->num_T_exp >= 5) { - LOGP(DMM, LOGL_NOTICE, "T3370 expired >= 5 times\n"); - gsm48_tx_gmm_att_rej(mm, GMM_CAUSE_MS_ID_NOT_DERIVED); - mm->mm_state = GMM_DEREGISTERED; - break; - } - /* re-tranmit IDENTITY REQUEST and re-start timer */ - gsm48_tx_gmm_id_req(mm, mm->t3370_id_type); - osmo_timer_schedule(&mm->timer, GSM0408_T3370_SECS, 0); - break; - default: - LOGP(DMM, LOGL_ERROR, "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)) - LOGP(DMM, LOGL_ERROR, "Starting MM timer %u while old " - "timer %u pending\n", T, pdp->T); - pdp->T = T; - pdp->num_T_exp = 0; - - /* FIXME: we should do this only once ? */ - pdp->timer.data = pdp; - pdp->timer.cb = &pdpctx_timer_cb; - - osmo_timer_schedule(&pdp->timer, seconds, 0); -} - - -#if 0 -static void msgb_put_pdp_addr_ipv4(struct msgb *msg, uint32_t ipaddr) -{ - uint8_t v[6]; - - v[0] = PDP_TYPE_ORG_IETF; - v[1] = PDP_TYPE_N_IETF_IPv4; - *(uint32_t *)(v+2) = htonl(ipaddr); - - msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v); -} - -static void msgb_put_pdp_addr_ppp(struct msgb *msg) -{ - uint8_t v[2]; - - v[0] = PDP_TYPE_ORG_ETSI; - v[1] = PDP_TYPE_N_ETSI_PPP; - - msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v); -} -#endif - -/* Section 9.5.2: Ativate PDP Context Accept */ -int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - uint8_t transaction_id = pdp->ti ^ 0x8; /* flip */ - - DEBUGP(DMM, "<- ACTIVATE PDP CONTEXT ACK\n"); - - mmctx2msgid(msg, pdp->mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); - gh->msg_type = GSM48_MT_GSM_ACT_PDP_ACK; - - /* Negotiated LLC SAPI */ - msgb_v_put(msg, pdp->sapi); - - /* FIXME: copy QoS parameters from original request */ - //msgb_lv_put(msg, pdp->lib->qos_neg.l, pdp->lib->qos_neg.v); - msgb_lv_put(msg, sizeof(default_qos), (uint8_t *)&default_qos); - - /* Radio priority 10.5.7.2 */ - msgb_v_put(msg, pdp->lib->radio_pri); - - /* PDP address */ - /* Highest 4 bits of first byte need to be set to 1, otherwise - * the IE is identical with the 04.08 PDP Address IE */ - pdp->lib->eua.v[0] &= ~0xf0; - msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, - pdp->lib->eua.l, pdp->lib->eua.v); - pdp->lib->eua.v[0] |= 0xf0; - - /* Optional: Protocol configuration options (FIXME: why 'req') */ - if (pdp->lib->pco_req.l && pdp->lib->pco_req.v) - msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT, - pdp->lib->pco_req.l, pdp->lib->pco_req.v); - - /* Optional: Packet Flow Identifier */ - - return gsm48_gmm_sendmsg(msg, 0, pdp->mm); -} - -/* Section 9.5.3: Activate PDP Context reject */ -int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid, - uint8_t cause, uint8_t pco_len, uint8_t *pco_v) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - uint8_t transaction_id = tid ^ 0x8; /* flip */ - - DEBUGP(DMM, "<- ACTIVATE PDP CONTEXT REJ(cause=%u)\n", cause); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); - gh->msg_type = GSM48_MT_GSM_ACT_PDP_REJ; - - msgb_v_put(msg, cause); - if (pco_len && pco_v) - msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT, pco_len, pco_v); - - return gsm48_gmm_sendmsg(msg, 0, mm); -} - -/* Section 9.5.8: Deactivate PDP Context Request */ -static int _gsm48_tx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, uint8_t tid, - uint8_t sm_cause) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - uint8_t transaction_id = tid ^ 0x8; /* flip */ - - DEBUGP(DMM, "<- DEACTIVATE PDP CONTEXT REQ\n"); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); - gh->msg_type = GSM48_MT_GSM_DEACT_PDP_REQ; - - msgb_v_put(msg, sm_cause); - - return gsm48_gmm_sendmsg(msg, 0, mm); -} -int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause) -{ - pdpctx_timer_start(pdp, 3395, GSM0408_T3395_SECS); - - return _gsm48_tx_gsm_deact_pdp_req(pdp->mm, pdp->ti, sm_cause); -} - -/* Section 9.5.9: Deactivate PDP Context Accept */ -static int _gsm48_tx_gsm_deact_pdp_acc(struct sgsn_mm_ctx *mm, uint8_t tid) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - uint8_t transaction_id = tid ^ 0x8; /* flip */ - - DEBUGP(DMM, "<- DEACTIVATE PDP CONTEXT ACK\n"); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); - gh->msg_type = GSM48_MT_GSM_DEACT_PDP_ACK; - - return gsm48_gmm_sendmsg(msg, 0, mm); -} -int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp) -{ - return _gsm48_tx_gsm_deact_pdp_acc(pdp->mm, pdp->ti); -} - -/* Section 9.5.1: Activate PDP Context Request */ -static int gsm48_rx_gsm_act_pdp_req(struct sgsn_mm_ctx *mmctx, - struct msgb *msg) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - struct gsm48_act_pdp_ctx_req *act_req = (struct gsm48_act_pdp_ctx_req *) gh->data; - uint8_t req_qos_len, req_pdpa_len; - uint8_t *req_qos, *req_pdpa; - struct tlv_parsed tp; - uint8_t transaction_id = (gh->proto_discr >> 4); - struct sgsn_ggsn_ctx *ggsn; - struct sgsn_pdp_ctx *pdp; - - DEBUGP(DMM, "-> 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 */ - - /* 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)); - - 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; - } - - DEBUGPC(DMM, "\n"); - - /* 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; - - /* FIXME: determine GGSN based on APN and subscription options */ - if (TLVP_PRESENT(&tp, GSM48_IE_GSM_APN)) {} - - /* 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); - } - - /* Check if NSAPI is already in use */ - pdp = sgsn_pdp_ctx_by_nsapi(mmctx, act_req->req_nsapi); - if (pdp) { - /* We already have a PDP context for this TLLI + NSAPI tuple */ - if (pdp->sapi == act_req->req_llc_sapi && - pdp->ti == transaction_id) { - /* This apparently is a re-transmission of a PDP CTX - * ACT REQ (our ACT ACK must have got dropped) */ - return gsm48_tx_gsm_act_pdp_acc(pdp); - } - - /* Send reject with GSM_CAUSE_NSAPI_IN_USE */ - return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, - GSM_CAUSE_NSAPI_IN_USE, - 0, NULL); - } - - /* Only increment counter for a real activation, after we checked - * for re-transmissions */ - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PDP_CTX_ACT]); - - ggsn = sgsn_ggsn_ctx_by_id(0); - if (!ggsn) { - LOGP(DGPRS, LOGL_ERROR, "No GGSN context 0 found!\n"); - return -EIO; - } - ggsn->gsn = sgsn->gsn; - pdp = sgsn_create_pdp_ctx(ggsn, mmctx, act_req->req_nsapi, &tp); - if (!pdp) - return -1; - - /* Store SAPI and Transaction Identifier */ - pdp->sapi = act_req->req_llc_sapi; - pdp->ti = transaction_id; - - return 0; -} - -/* Section 9.5.8: Deactivate PDP Context Request */ -static int gsm48_rx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, struct msgb *msg) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t transaction_id = (gh->proto_discr >> 4); - struct sgsn_pdp_ctx *pdp; - - DEBUGP(DMM, "-> DEACTIVATE PDP CONTEXT REQ (cause: %s)\n", - get_value_string(gsm_cause_names, gh->data[0])); - - pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id); - if (!pdp) { - LOGP(DMM, LOGL_NOTICE, "Deactivate PDP Context Request for " - "non-existing PDP Context (IMSI=%s, TI=%u)\n", - mm->imsi, transaction_id); - return _gsm48_tx_gsm_deact_pdp_acc(mm, transaction_id); - } - - return sgsn_delete_pdp_ctx(pdp); -} - -/* Section 9.5.9: Deactivate PDP Context Accept */ -static int gsm48_rx_gsm_deact_pdp_ack(struct sgsn_mm_ctx *mm, struct msgb *msg) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t transaction_id = (gh->proto_discr >> 4); - struct sgsn_pdp_ctx *pdp; - - DEBUGP(DMM, "-> DEACTIVATE PDP CONTEXT ACK\n"); - - pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id); - if (!pdp) { - LOGP(DMM, LOGL_NOTICE, "Deactivate PDP Context Accept for " - "non-existing PDP Context (IMSI=%s, TI=%u)\n", - mm->imsi, transaction_id); - return 0; - } - - return sgsn_delete_pdp_ctx(pdp); -} - -static int gsm48_rx_gsm_status(struct sgsn_mm_ctx *ctx, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DMM, "-> GPRS SM STATUS (cause: %s)\n", - get_value_string(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) { - LOGP(DMM, LOGL_NOTICE, "T3395 expired >= 5 times\n"); - pdp->state = PDP_STATE_INACTIVE; - sgsn_delete_pdp_ctx(pdp); - break; - } - gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL); - osmo_timer_schedule(&pdp->timer, GSM0408_T3395_SECS, 0); - break; - default: - LOGP(DMM, LOGL_ERROR, "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"); - gsm48_tx_gmm_status_oldmsg(msg, GMM_CAUSE_IMPL_DETACHED); - return gsm48_tx_sm_status_oldmsg(msg, GSM_CAUSE_PROTO_ERR_UNSPEC); - } - - 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: - DEBUGP(DMM, "Unimplemented GSM 04.08 GSM msg type 0x%02x\n", - gh->msg_type); - rc = gsm48_tx_sm_status(mmctx, GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL); - break; - default: - DEBUGP(DMM, "Unknown GSM 04.08 GSM msg type 0x%02x\n", - gh->msg_type); - rc = gsm48_tx_sm_status(mmctx, GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL); - break; - - } - - return rc; -} - -/* Main entry point for incoming 04.08 GPRS messages */ -int gsm0408_gprs_rcvmsg(struct msgb *msg, struct gprs_llc_llme *llme) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t pdisc = gh->proto_discr & 0x0f; - struct sgsn_mm_ctx *mmctx; - struct gprs_ra_id ra_id; - int rc = -EINVAL; - - bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); - mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id); - if (mmctx) { - msgid2mmctx(mmctx, msg); - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); - mmctx->llme = llme; - } - - /* MMCTX can be NULL */ - - switch (pdisc) { - case GSM48_PDISC_MM_GPRS: - rc = gsm0408_rcv_gmm(mmctx, msg, llme); - break; - case GSM48_PDISC_SM_GPRS: - rc = gsm0408_rcv_gsm(mmctx, msg, llme); - break; - default: - DEBUGP(DMM, "Unknown GSM 04.08 discriminator 0x%02x\n", - pdisc); - /* FIXME: return status message */ - break; - } - - return rc; -} - -int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli) -{ - struct sgsn_mm_ctx *mmctx; - - mmctx = sgsn_mm_ctx_by_tlli(tlli, raid); - if (!mmctx) { - LOGP(DMM, LOGL_NOTICE, "SUSPEND request for unknown " - "TLLI=%08x\n", tlli); - return -EINVAL; - } - - if (mmctx->mm_state != GMM_REGISTERED_NORMAL) { - LOGP(DMM, LOGL_NOTICE, "SUSPEND request while state " - "!= REGISTERED (TLLI=%08x)\n", tlli); - return -EINVAL; - } - - /* Transition from REGISTERED_NORMAL to REGISTERED_SUSPENDED */ - mmctx->mm_state = GMM_REGISTERED_SUSPENDED; - return 0; -} - -int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli, - uint8_t suspend_ref) -{ - struct sgsn_mm_ctx *mmctx; - - /* FIXME: make use of suspend reference? */ - - mmctx = sgsn_mm_ctx_by_tlli(tlli, raid); - if (!mmctx) { - LOGP(DMM, LOGL_NOTICE, "RESUME request for unknown " - "TLLI=%08x\n", tlli); - return -EINVAL; - } - - if (mmctx->mm_state != GMM_REGISTERED_SUSPENDED) { - LOGP(DMM, LOGL_NOTICE, "RESUME request while state " - "!= SUSPENDED (TLLI=%08x)\n", tlli); - /* FIXME: should we not simply ignore it? */ - return -EINVAL; - } - - /* Transition from SUSPENDED to NORMAL */ - mmctx->mm_state = GMM_REGISTERED_NORMAL; - return 0; -} diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c deleted file mode 100644 index 3075573e9..000000000 --- a/openbsc/src/gprs/gprs_llc.c +++ /dev/null @@ -1,957 +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 - -/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU - * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */ -static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx) -{ - struct bssgp_dl_ud_par dup; - const uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x20 }; - - memset(&dup, 0, sizeof(dup)); - /* before we have received some identity from the MS, we might - * not yet have a MMC context (e.g. XID negotiation of primarly - * LLC connection fro GMM sapi). */ - if (mmctx) { - dup.imsi = mmctx->imsi; - dup.drx_parms = mmctx->drx_parms; - dup.ms_ra_cap.len = mmctx->ms_radio_access_capa.len; - dup.ms_ra_cap.v = mmctx->ms_radio_access_capa.buf; - } - 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[] = { - [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; - -/* If the TLLI is foreign, return its local version */ -static inline uint32_t tlli_foreign2local(uint32_t tlli) -{ - uint32_t new_tlli; - - if (gprs_tlli_type(tlli) == TLLI_FOREIGN) { - new_tlli = tlli | 0x40000000; - DEBUGP(DLLC, "TLLI 0x%08x is foreign, converting to " - "local TLLI 0x%08x\n", tlli, new_tlli); - } else - new_tlli = tlli; - - return new_tlli; -} - -/* lookup LLC Entity based on DLCI (TLLI+SAPI tuple) */ -static struct gprs_llc_lle *lle_by_tlli_sapi(uint32_t tlli, uint8_t sapi) -{ - struct gprs_llc_llme *llme; - - tlli = tlli_foreign2local(tlli); - - llist_for_each_entry(llme, &gprs_llc_llmes, list) { - if (llme->tlli == tlli || llme->old_tlli == tlli) - return &llme->lle[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; - - for (i = 0; i < ARRAY_SIZE(llme->lle); i++) - lle_init(llme, i); - - llist_add(&llme->list, &gprs_llc_llmes); - - return llme; -} - -static void llme_free(struct gprs_llc_llme *llme) -{ - llist_del(&llme->list); - talloc_free(llme); -} - -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, -}; - -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 } -}; - -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; -}; - -#define LLC_ALLOC_SIZE 16384 -#define UI_HDR_LEN 3 -#define N202 4 -#define CRC24_LENGTH 3 - -static 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; -} - -#if 0 -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: - ; - } - -} - -static void t201_expired(void *data) -{ - struct gprs_llc_lle *lle = data; - - if (lle->retrans_ctr < lle->params.n200) { - /* FIXME: transmit apropriate supervisory frame (8.6.4.1) */ - /* FIXME: set timer T201 */ - lle->retrans_ctr++; - } -} -#endif - -int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command, - enum gprs_llc_u_cmd u_cmd, int pf_bit) -{ - uint8_t *fcs, *llch; - uint8_t addr, ctrl; - uint32_t fcs_calc; - - /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ - - /* Address Field */ - addr = sapi & 0xf; - if (command) - addr |= 0x40; - - /* 6.3 Figure 8 */ - ctrl = 0xe0 | u_cmd; - if (pf_bit) - ctrl |= 0x10; - - /* prepend LLC UI header */ - llch = msgb_push(msg, 2); - llch[0] = addr; - llch[1] = ctrl; - - /* append FCS to end of frame */ - fcs = msgb_put(msg, 3); - fcs_calc = gprs_llc_fcs(llch, fcs - llch); - fcs[0] = fcs_calc & 0xff; - fcs[1] = (fcs_calc >> 8) & 0xff; - fcs[2] = (fcs_calc >> 16) & 0xff; - - /* Identifiers passed down: (BVCI, NSEI) */ - - /* Send BSSGP-DL-UNITDATA.req */ - return _bssgp_tx_dl_ud(msg, NULL); -} - -/* Send XID response to LLE */ -static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, - int command) -{ - /* copy identifiers from LLE to ensure lower layers can route */ - msgb_tlli(msg) = lle->llme->tlli; - msgb_bvci(msg) = lle->llme->bvci; - msgb_nsei(msg) = lle->llme->nsei; - - return gprs_llc_tx_u(msg, lle->sapi, command, GPRS_LLC_U_XID, 1); -} - -/* Transmit a UI frame over the given SAPI */ -int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command, - void *mmctx) -{ - struct gprs_llc_lle *lle; - uint8_t *fcs, *llch; - uint8_t addr, ctrl[2]; - uint32_t fcs_calc; - uint16_t nu = 0; - uint32_t oc; - - /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ - - /* look-up or create the LL Entity for this (TLLI, SAPI) tuple */ - lle = lle_by_tlli_sapi(msgb_tlli(msg), sapi); - if (!lle) { - struct gprs_llc_llme *llme; - LOGP(DLLC, LOGL_ERROR, "LLC TX: unknown TLLI 0x%08x, " - "creating LLME on the fly\n", msgb_tlli(msg)); - llme = llme_alloc(msgb_tlli(msg)); - lle = &llme->lle[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); - return -EFBIG; - } - - /* Update LLE's (BVCI, NSEI) tuple */ - lle->llme->bvci = msgb_bvci(msg); - lle->llme->nsei = msgb_nsei(msg); - - /* Obtain current values for N(u) and OC */ - nu = lle->vu_send; - oc = lle->oc_ui_send; - /* Increment V(U) */ - lle->vu_send = (lle->vu_send + 1) % 512; - /* Increment Overflow Counter, if needed */ - if ((lle->vu_send + 1) / 512) - lle->oc_ui_send += 512; - - /* Address Field */ - addr = sapi & 0xf; - if (command) - addr |= 0x40; - - /* Control Field */ - ctrl[0] = 0xc0; - ctrl[0] |= nu >> 6; - ctrl[1] = (nu << 2) & 0xfc; - ctrl[1] |= 0x01; /* Protected Mode */ - - /* prepend LLC UI header */ - llch = msgb_push(msg, 3); - llch[0] = addr; - llch[1] = ctrl[0]; - llch[2] = ctrl[1]; - - /* append FCS to end of frame */ - fcs = msgb_put(msg, 3); - fcs_calc = gprs_llc_fcs(llch, fcs - llch); - fcs[0] = fcs_calc & 0xff; - fcs[1] = (fcs_calc >> 8) & 0xff; - fcs[2] = (fcs_calc >> 16) & 0xff; - - /* encrypt information field + FCS, if needed! */ - if (lle->llme->algo != GPRS_ALGO_GEA0) { - uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */ - uint16_t crypt_len = (fcs + 3) - (llch + 3); - uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK]; - uint32_t iv; - int rc, i; - uint64_t kc = *(uint64_t *)&lle->llme->kc; - - /* Compute the 'Input' Paraemeter */ - iv = gprs_cipher_gen_input_ui(iov_ui, sapi, nu, oc); - - /* Compute the keystream that we need to XOR with the data */ - rc = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo, - kc, iv, GPRS_CIPH_SGSN2MS); - if (rc < 0) { - LOGP(DLLC, LOGL_ERROR, "Error crypting UI frame: %d\n", rc); - return rc; - } - - /* XOR the cipher output with the information field + FCS */ - for (i = 0; i < crypt_len; i++) - *(llch + 3 + i) ^= cipher_out[i]; - - /* Mark frame as encrypted */ - ctrl[1] |= 0x02; - } - - /* Identifiers passed down: (BVCI, NSEI) */ - - /* Send BSSGP-DL-UNITDATA.req */ - return _bssgp_tx_dl_ud(msg, mmctx); -} - -/* According to 6.4.1.6 / Figure 11 */ -static int msgb_put_xid_par(struct msgb *msg, uint8_t type, uint8_t length, uint8_t *data) -{ - uint8_t header_len = 1; - uint8_t *cur; - - /* type is a 5-bit field... */ - if (type > 0x1f) - return -EINVAL; - - if (length > 3) - header_len = 2; - - cur = msgb_put(msg, length + header_len); - - /* build the header without or with XL bit */ - if (length <= 3) { - *cur++ = (type << 2) | (length & 3); - } else { - *cur++ = 0x80 | (type << 2) | (length >> 6); - *cur++ = (length << 2); - } - - /* copy over the payload of the parameter*/ - memcpy(cur, data, length); - - return length + header_len; -} - -static void rx_llc_xid(struct gprs_llc_lle *lle, - struct gprs_llc_hdr_parsed *gph) -{ - /* FIXME: 8.5.3.3: check if XID is invalid */ - if (gph->is_cmd) { - /* FIXME: implement XID negotiation using SNDCP */ - struct msgb *resp; - uint8_t *xid; - resp = msgb_alloc_headroom(4096, 1024, "LLC_XID"); - xid = msgb_put(resp, gph->data_len); - memcpy(xid, gph->data, gph->data_len); - gprs_llc_tx_xid(lle, resp, 0); - } else { - /* FIXME: if we had sent a XID reset, send - * LLGMM-RESET.conf to GMM */ - /* FIXME: implement XID negotiation using SNDCP */ - } -} - -static void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph) -{ - DEBUGP(DLLC, "LLC SAPI=%u %c %c FCS=0x%06x", - gph->sapi, gph->is_cmd ? 'C' : 'R', gph->ack_req ? 'A' : ' ', - gph->fcs); - - if (gph->cmd) - DEBUGPC(DLLC, "CMD=%s ", get_value_string(llc_cmd_strs, gph->cmd)); - - if (gph->data) - DEBUGPC(DLLC, "DATA "); - - DEBUGPC(DLLC, "\n"); -} -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: - ; - } - - return 0; -} - -/* parse a GPRS LLC header, also check for invalid frames */ -static 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; -} - -/* receive an incoming LLC PDU (BSSGP-UL-UNITDATA-IND, 7.2.4.2) */ -int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) -{ -#if 0 - struct bssgp_ud_hdr *udh = (struct bssgp_ud_hdr *) msgb_bssgph(msg); -#endif - struct gprs_llc_hdr *lh = (struct gprs_llc_hdr *) msgb_llch(msg); - struct gprs_llc_hdr_parsed llhp; - struct gprs_llc_lle *lle; - int rc = 0; - - /* Identifiers from DOWN: NSEI, BVCI, TLLI */ - - memset(&llhp, 0, sizeof(llhp)); - rc = gprs_llc_hdr_parse(&llhp, (uint8_t *) lh, TLVP_LEN(tv, BSSGP_IE_LLC_PDU)); - gprs_llc_hdr_dump(&llhp); - if (rc < 0) { - LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n"); - return rc; - } - - switch (gprs_tlli_type(msgb_tlli(msg))) { - case TLLI_LOCAL: - case TLLI_FOREIGN: - case TLLI_RANDOM: - case TLLI_AUXILIARY: - break; - default: - LOGP(DLLC, LOGL_ERROR, - "Discarding frame with strange TLLI type\n"); - break; - } - - /* find the LLC Entity for this TLLI+SAPI tuple */ - lle = lle_by_tlli_sapi(msgb_tlli(msg), llhp.sapi); - - /* 7.2.1.1 LLC belonging to unassigned TLLI+SAPI shall be discarded, - * except UID and XID frames with SAPI=1 */ - if (!lle) { - if (llhp.sapi == GPRS_SAPI_GMM && - (llhp.cmd == GPRS_LLC_XID || llhp.cmd == GPRS_LLC_UI)) { - struct gprs_llc_llme *llme; - /* FIXME: don't use the TLLI but the 0xFFFF unassigned? */ - llme = llme_alloc(msgb_tlli(msg)); - LOGP(DLLC, LOGL_DEBUG, "LLC RX: unknown TLLI 0x%08x, " - "creating LLME on the fly\n", msgb_tlli(msg)); - lle = &llme->lle[llhp.sapi]; - } else { - LOGP(DLLC, LOGL_NOTICE, - "unknown TLLI/SAPI: Silently dropping\n"); - return 0; - } - } - - /* decrypt information field + FCS, if needed! */ - if (llhp.is_encrypted) { - uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */ - uint16_t crypt_len = llhp.data_len + 3; - uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK]; - uint32_t iv; - uint64_t kc = *(uint64_t *)&lle->llme->kc; - int rc, i; - - if (lle->llme->algo == GPRS_ALGO_GEA0) { - LOGP(DLLC, LOGL_NOTICE, "encrypted frame for LLC that " - "has no KC/Algo! Dropping.\n"); - return 0; - } - - iv = gprs_cipher_gen_input_ui(iov_ui, lle->sapi, llhp.seq_tx, - lle->oc_ui_recv); - rc = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo, - kc, iv, GPRS_CIPH_MS2SGSN); - if (rc < 0) { - LOGP(DLLC, LOGL_ERROR, "Error decrypting frame: %d\n", - rc); - return rc; - } - - /* XOR the cipher output with the information field + FCS */ - for (i = 0; i < crypt_len; i++) - *(llhp.data + i) ^= cipher_out[i]; - } else { - if (lle->llme->algo != GPRS_ALGO_GEA0) { - LOGP(DLLC, LOGL_NOTICE, "unencrypted frame for LLC " - "that is supposed to be encrypted. Dropping.\n"); - return 0; - } - } - - /* We have to do the FCS check _after_ decryption */ - llhp.fcs_calc = gprs_llc_fcs((uint8_t *)lh, llhp.crc_length); - if (llhp.fcs != llhp.fcs_calc) { - LOGP(DLLC, LOGL_INFO, "Dropping frame with invalid FCS\n"); - return -EIO; - } - - /* Update LLE's (BVCI, NSEI) tuple */ - lle->llme->bvci = msgb_bvci(msg); - lle->llme->nsei = msgb_nsei(msg); - - /* Receive and Process the actual LLC frame */ - rc = gprs_llc_hdr_rx(&llhp, lle); - if (rc < 0) - return rc; - - /* llhp.data is only set when we need to send LL_[UNIT]DATA_IND up */ - if (llhp.data) { - msgb_gmmh(msg) = llhp.data; - switch (llhp.sapi) { - case GPRS_SAPI_GMM: - /* send LL_UNITDATA_IND to GMM */ - rc = gsm0408_gprs_rcvmsg(msg, lle->llme); - break; - case GPRS_SAPI_SNDCP3: - case GPRS_SAPI_SNDCP5: - case GPRS_SAPI_SNDCP9: - case GPRS_SAPI_SNDCP11: - /* send LL_DATA_IND/LL_UNITDATA_IND to SNDCP */ - rc = sndcp_llunitdata_ind(msg, lle, llhp.data, llhp.data_len); - break; - case GPRS_SAPI_SMS: - /* FIXME */ - case GPRS_SAPI_TOM2: - case GPRS_SAPI_TOM8: - /* FIXME: send LL_DATA_IND/LL_UNITDATA_IND to TOM */ - default: - LOGP(DLLC, LOGL_NOTICE, "Unsupported SAPI %u\n", llhp.sapi); - rc = -EINVAL; - break; - } - } - - return rc; -} - -/* 04.64 Chapter 7.2.1.1 LLGMM-ASSIGN */ -int gprs_llgmm_assign(struct gprs_llc_llme *llme, - uint32_t old_tlli, uint32_t new_tlli, - enum gprs_ciph_algo alg, const uint8_t *kc) -{ - unsigned int i; - - /* Update the crypto parameters */ - llme->algo = alg; - if (alg != GPRS_ALGO_GEA0) - memcpy(llme->kc, kc, sizeof(llme->kc)); - - if (old_tlli == 0xffffffff && new_tlli != 0xffffffff) { - /* TLLI Assignment 8.3.1 */ - /* New TLLI shall be assigned and used when (re)transmitting LLC frames */ - /* If old TLLI != 0xffffffff was assigned to LLME, then TLLI - * old is unassigned. Only TLLI new shall be accepted when - * received from peer. */ - if (llme->old_tlli != 0xffffffff) { - llme->old_tlli = 0xffffffff; - llme->tlli = new_tlli; - } else { - /* If TLLI old == 0xffffffff was assigned to LLME, then this is - * TLLI assignmemt according to 8.3.1 */ - llme->old_tlli = 0xffffffff; - llme->tlli = new_tlli; - llme->state = GPRS_LLMS_ASSIGNED; - /* 8.5.3.1 For all LLE's */ - for (i = 0; i < ARRAY_SIZE(llme->lle); i++) { - struct gprs_llc_lle *l = &llme->lle[i]; - l->vu_send = l->vu_recv = 0; - l->retrans_ctr = 0; - l->state = GPRS_LLES_ASSIGNED_ADM; - /* FIXME Set parameters according to table 9 */ - } - } - } else if (old_tlli != 0xffffffff && new_tlli != 0xffffffff) { - /* TLLI Change 8.3.2 */ - /* Both TLLI Old and TLLI New are assigned; use New when - * (re)transmitting. Accept both Old and New on Rx */ - llme->old_tlli = llme->tlli; - llme->tlli = new_tlli; - llme->state = GPRS_LLMS_ASSIGNED; - } else if (old_tlli != 0xffffffff && new_tlli == 0xffffffff) { - /* TLLI Unassignment 8.3.3) */ - llme->tlli = llme->old_tlli = 0; - llme->state = GPRS_LLMS_UNASSIGNED; - for (i = 0; i < ARRAY_SIZE(llme->lle); i++) { - struct gprs_llc_lle *l = &llme->lle[i]; - l->state = GPRS_LLES_UNASSIGNED; - } - llme_free(llme); - } else - return -EINVAL; - - return 0; -} - -/* Chapter 7.2.1.2 LLGMM-RESET.req */ -int gprs_llgmm_reset(struct gprs_llc_llme *llme) -{ - struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID"); - int random = rand(); - - /* First XID component must be RESET */ - msgb_put_xid_par(msg, GPRS_LLC_XID_T_RESET, 0, NULL); - /* randomly select new IOV-UI */ - msgb_put_xid_par(msg, GPRS_LLC_XID_T_IOV_UI, 4, (uint8_t *) &random); - - /* FIXME: Start T200, wait for XID response */ - return gprs_llc_tx_xid(&llme->lle[1], msg, 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_vty.c b/openbsc/src/gprs/gprs_llc_vty.c deleted file mode 100644 index ab5269922..000000000 --- a/openbsc/src/gprs/gprs_llc_vty.c +++ /dev/null @@ -1,108 +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 - -struct value_string gprs_llc_state_strs[] = { - { GPRS_LLES_UNASSIGNED, "TLLI Unassigned" }, - { GPRS_LLES_ASSIGNED_ADM, "TLLI Assigned" }, - { GPRS_LLES_LOCAL_EST, "Local Establishment" }, - { GPRS_LLES_REMOTE_EST, "Remote Establishment" }, - { GPRS_LLES_ABM, "Asynchronous Balanced Mode" }, - { GPRS_LLES_LOCAL_REL, "Local Release" }, - { GPRS_LLES_TIMER_REC, "Timer Recovery" }, -}; - -static void vty_dump_lle(struct vty *vty, struct gprs_llc_lle *lle) -{ - struct gprs_llc_params *par = &lle->params; - vty_out(vty, " SAPI %2u State %s VUsend=%u, VUrecv=%u", lle->sapi, - get_value_string(gprs_llc_state_strs, lle->state), - lle->vu_send, lle->vu_recv); - vty_out(vty, " Vsent=%u Vack=%u Vrecv=%u, RetransCtr=%u%s", - lle->v_sent, lle->v_ack, lle->v_recv, - lle->retrans_ctr, VTY_NEWLINE); - vty_out(vty, " T200=%u, N200=%u, N201-U=%u, N201-I=%u, mD=%u, " - "mU=%u, kD=%u, kU=%u%s", par->t200_201, par->n200, - par->n201_u, par->n201_i, par->mD, par->mU, par->kD, - par->kU, VTY_NEWLINE); -} - -static uint8_t valid_sapis[] = { 1, 2, 3, 5, 7, 8, 9, 11 }; - -static void vty_dump_llme(struct vty *vty, struct gprs_llc_llme *llme) -{ - unsigned int i; - - vty_out(vty, "TLLI %08x (Old TLLI %08x) BVCI=%u NSEI=%u: State %s%s", - llme->tlli, llme->old_tlli, llme->bvci, llme->nsei, - 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_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c deleted file mode 100644 index 84bf512ae..000000000 --- a/openbsc/src/gprs/gprs_sgsn.c +++ /dev/null @@ -1,404 +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 - -extern struct sgsn_instance *sgsn; - -LLIST_HEAD(sgsn_mm_ctxts); -LLIST_HEAD(sgsn_ggsn_ctxts); -LLIST_HEAD(sgsn_apn_ctxts); -LLIST_HEAD(sgsn_pdp_ctxts); - -static const struct rate_ctr_desc mmctx_ctr_description[] = { - { "sign.packets.in", "Signalling Messages ( In)" }, - { "sign.packets.out", "Signalling Messages (Out)" }, - { "udata.packets.in", "User Data Messages ( In)" }, - { "udata.packets.out", "User Data Messages (Out)" }, - { "udata.bytes.in", "User Data Bytes ( In)" }, - { "udata.bytes.out", "User Data Bytes (Out)" }, - { "pdp_ctx_act", "PDP Context Activations " }, - { "suspend", "SUSPEND Count " }, - { "paging.ps", "Paging Packet Switched " }, - { "paging.cs", "Paging Circuit Switched " }, - { "ra_update", "Routing Area Update " }, -}; - -static const struct rate_ctr_group_desc mmctx_ctrg_desc = { - .group_name_prefix = "sgsn.mmctx", - .group_description = "SGSN MM Context Statistics", - .num_ctr = ARRAY_SIZE(mmctx_ctr_description), - .ctr_desc = mmctx_ctr_description, -}; - -static const struct rate_ctr_desc pdpctx_ctr_description[] = { - { "udata.packets.in", "User Data Messages ( In)" }, - { "udata.packets.out", "User Data Messages (Out)" }, - { "udata.bytes.in", "User Data Bytes ( In)" }, - { "udata.bytes.out", "User Data Bytes (Out)" }, -}; - -static const struct rate_ctr_group_desc pdpctx_ctrg_desc = { - .group_name_prefix = "sgsn.pdpctx", - .group_description = "SGSN PDP Context Statistics", - .num_ctr = ARRAY_SIZE(pdpctx_ctr_description), - .ctr_desc = pdpctx_ctr_description, -}; - -static int ra_id_equals(const struct gprs_ra_id *id1, - const struct gprs_ra_id *id2) -{ - return (id1->mcc == id2->mcc && id1->mnc == id2->mnc && - id1->lac == id2->lac && id1->rac == id2->rac); -} - -/* See 03.02 Chapter 2.6 */ -static inline uint32_t tlli_foreign(uint32_t tlli) -{ - return ((tlli | 0x80000000) & ~0x40000000); -} - -/* look-up a SGSN MM context based on TLLI + RAI */ -struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli, - const struct gprs_ra_id *raid) -{ - struct sgsn_mm_ctx *ctx; - int tlli_type; - - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { - if (tlli == ctx->tlli && - ra_id_equals(raid, &ctx->ra)) - return ctx; - } - - tlli_type = gprs_tlli_type(tlli); - switch (tlli_type) { - case TLLI_LOCAL: - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { - if ((ctx->p_tmsi | 0xC0000000) == tlli || - (ctx->p_tmsi_old && (ctx->p_tmsi_old | 0xC0000000) == tlli)) { - ctx->tlli = tlli; - return ctx; - } - } - break; - case TLLI_FOREIGN: - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { - if (tlli == tlli_foreign(ctx->tlli) && - ra_id_equals(raid, &ctx->ra)) - return ctx; - } - break; - default: - break; - } - - return NULL; -} - -struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t p_tmsi) -{ - struct sgsn_mm_ctx *ctx; - - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { - if (p_tmsi == ctx->p_tmsi || - (ctx->p_tmsi_old && ctx->p_tmsi_old == p_tmsi)) - return ctx; - } - return NULL; -} - -struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi) -{ - struct sgsn_mm_ctx *ctx; - - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { - if (!strcmp(imsi, ctx->imsi)) - return ctx; - } - return NULL; - -} - -/* Allocate a new SGSN MM context */ -struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli, - const struct gprs_ra_id *raid) -{ - struct sgsn_mm_ctx *ctx; - - ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx); - if (!ctx) - return NULL; - - memcpy(&ctx->ra, raid, sizeof(ctx->ra)); - ctx->tlli = tlli; - ctx->mm_state = GMM_DEREGISTERED; - ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli); - INIT_LLIST_HEAD(&ctx->pdp_list); - - llist_add(&ctx->list, &sgsn_mm_ctxts); - - return ctx; -} - -/* this is a hard _free_ function, it doesn't clean up the PDP contexts - * in libgtp! */ -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); -} - -/* look up PDP context by MM context and NSAPI */ -struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm, - uint8_t nsapi) -{ - struct sgsn_pdp_ctx *pdp; - - llist_for_each_entry(pdp, &mm->pdp_list, list) { - if (pdp->nsapi == nsapi) - return pdp; - } - return NULL; -} - -/* look up PDP context by MM context and transaction ID */ -struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm, - uint8_t tid) -{ - struct sgsn_pdp_ctx *pdp; - - llist_for_each_entry(pdp, &mm->pdp_list, list) { - if (pdp->ti == tid) - return pdp; - } - return NULL; -} - -/* you don't want to use this directly, call sgsn_create_pdp_ctx() */ -struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm, - uint8_t nsapi) -{ - struct sgsn_pdp_ctx *pdp; - - pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi); - if (pdp) - return NULL; - - pdp = talloc_zero(tall_bsc_ctx, struct sgsn_pdp_ctx); - if (!pdp) - return NULL; - - pdp->mm = mm; - pdp->nsapi = nsapi; - pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi); - llist_add(&pdp->list, &mm->pdp_list); - llist_add(&pdp->g_list, &sgsn_pdp_ctxts); - - return pdp; -} - -#include -/* you probably want to call sgsn_delete_pdp_ctx() instead */ -void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp) -{ - rate_ctr_group_free(pdp->ctrg); - 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; - LOGP(DGPRS, LOGL_NOTICE, "freeing PDP context that still " - "has a libgtp handle attached to it, this shouldn't " - "happen!\n"); - osmo_generate_backtrace(); - lib->priv = NULL; - } - - 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; -} - -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 */ - -#if 0 -struct apn_ctx *apn_ctx_alloc(const char *ap_name) -{ - struct apn_ctx *actx; - - actx = talloc_zero(talloc_bsc_ctx, struct apn_ctx); - if (!actx) - return NULL; - actx->name = talloc_strdup(actx, ap_name); - - return actx; -} - -struct apn_ctx *apn_ctx_by_name(const char *name) -{ - struct apn_ctx *actx; - - llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { - if (!strcmp(name, actx->name)) - return actx; - } - return NULL; -} - -struct apn_ctx *apn_ctx_find_alloc(const char *name) -{ - struct apn_ctx *actx; - - actx = apn_ctx_by_name(name); - if (!actx) - actx = apn_ctx_alloc(name); - - return actx; -} -#endif - -uint32_t sgsn_alloc_ptmsi(void) -{ - struct sgsn_mm_ctx *mm; - uint32_t ptmsi; - -restart: - ptmsi = rand(); - llist_for_each_entry(mm, &sgsn_mm_ctxts, list) { - if (mm->p_tmsi == ptmsi) - goto restart; - } - - return ptmsi; -} - -static void drop_one_pdp(struct sgsn_pdp_ctx *pdp) -{ - if (pdp->mm->mm_state == GMM_REGISTERED_NORMAL) - gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL); - else { - /* FIXME: GPRS paging in case MS is SUSPENDED */ - LOGP(DGPRS, LOGL_NOTICE, "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 - * ottherwise 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; -} diff --git a/openbsc/src/gprs/gprs_sndcp.c b/openbsc/src/gprs/gprs_sndcp.c deleted file mode 100644 index e901ceb96..000000000 --- a/openbsc/src/gprs/gprs_sndcp.c +++ /dev/null @@ -1,620 +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 "gprs_sndcp.h" - -/* Chapter 7.2: SN-PDU Formats */ -struct sndcp_common_hdr { - /* octet 1 */ - uint8_t nsapi:4; - uint8_t more:1; - uint8_t type:1; - uint8_t first:1; - uint8_t spare:1; -} __attribute__((packed)); - -/* PCOMP / DCOMP only exist in first fragment */ -struct sndcp_comp_hdr { - /* octet 2 */ - uint8_t pcomp:4; - uint8_t dcomp:4; -} __attribute__((packed)); - -struct sndcp_udata_hdr { - /* octet 3 */ - uint8_t npdu_high:4; - uint8_t seg_nr:4; - /* octet 4 */ - uint8_t npdu_low; -} __attribute__((packed)); - - -static void *tall_sndcp_ctx; - -/* A fragment queue entry, containing one framgent of a N-PDU */ -struct defrag_queue_entry { - struct llist_head list; - /* segment number of this fragment */ - uint32_t seg_nr; - /* length of the data area of this fragment */ - uint32_t data_len; - /* pointer to the data of this fragment */ - uint8_t *data; -}; - -LLIST_HEAD(gprs_sndcp_entities); - -/* Enqueue a fragment into the defragment queue */ -static int defrag_enqueue(struct gprs_sndcp_entity *sne, uint8_t seg_nr, - uint8_t *data, uint32_t data_len) -{ - struct defrag_queue_entry *dqe; - - dqe = talloc_zero(tall_sndcp_ctx, struct defrag_queue_entry); - if (!dqe) - return -ENOMEM; - dqe->data = talloc_zero_size(dqe, data_len); - if (!dqe->data) { - talloc_free(dqe); - return -ENOMEM; - } - dqe->seg_nr = seg_nr; - dqe->data_len = data_len; - - llist_add(&dqe->list, &sne->defrag.frag_list); - - if (seg_nr > sne->defrag.highest_seg) - sne->defrag.highest_seg = seg_nr; - - sne->defrag.seg_have |= (1 << seg_nr); - sne->defrag.tot_len += data_len; - - memcpy(dqe->data, data, data_len); - - return 0; -} - -/* return if we have all segments of this N-PDU */ -static int defrag_have_all_segments(struct gprs_sndcp_entity *sne) -{ - uint32_t seg_needed = 0; - unsigned int i; - - /* create a bitmask of needed segments */ - for (i = 0; i <= sne->defrag.highest_seg; i++) - seg_needed |= (1 << i); - - if (seg_needed == sne->defrag.seg_have) - return 1; - - return 0; -} - -static struct defrag_queue_entry *defrag_get_seg(struct gprs_sndcp_entity *sne, - uint32_t seg_nr) -{ - struct defrag_queue_entry *dqe; - - llist_for_each_entry(dqe, &sne->defrag.frag_list, list) { - if (dqe->seg_nr == seg_nr) { - llist_del(&dqe->list); - return dqe; - } - } - return NULL; -} - -/* Perform actual defragmentation and create an output packet */ -static int defrag_segments(struct gprs_sndcp_entity *sne) -{ - struct msgb *msg; - unsigned int seg_nr; - uint8_t *npdu; - - LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Defragment output PDU %u " - "num_seg=%u tot_len=%u\n", sne->lle->llme->tlli, sne->nsapi, - sne->defrag.npdu, sne->defrag.highest_seg, sne->defrag.tot_len); - msg = msgb_alloc_headroom(sne->defrag.tot_len+256, 128, "SNDCP Defrag"); - if (!msg) - return -ENOMEM; - - /* FIXME: message headers + identifiers */ - - npdu = msg->data; - - for (seg_nr = 0; seg_nr <= sne->defrag.highest_seg; seg_nr++) { - struct defrag_queue_entry *dqe; - uint8_t *data; - - dqe = defrag_get_seg(sne, seg_nr); - if (!dqe) { - LOGP(DSNDCP, LOGL_ERROR, "Segment %u missing\n", seg_nr); - msgb_free(msg); - return -EIO; - } - /* actually append the segment to the N-PDU */ - data = msgb_put(msg, dqe->data_len); - memcpy(data, dqe->data, dqe->data_len); - - /* release memory for the fragment queue entry */ - talloc_free(dqe); - } - - /* FIXME: cancel timer */ - - /* actually send the N-PDU to the SGSN core code, which then - * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ - return sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli, - sne->nsapi, msg, sne->defrag.tot_len, npdu); -} - -static int defrag_input(struct gprs_sndcp_entity *sne, struct msgb *msg, uint8_t *hdr, - unsigned int len) -{ - struct sndcp_common_hdr *sch; - struct sndcp_comp_hdr *scomph = NULL; - struct sndcp_udata_hdr *suh; - uint16_t npdu_num; - uint8_t *data; - int rc; - - 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)); - - data = (uint8_t *)suh + sizeof(struct sndcp_udata_hdr); - - npdu_num = (suh->npdu_high << 8) | suh->npdu_low; - - LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Input PDU %u Segment %u " - "Length %u %s %s\n", sne->lle->llme->tlli, sne->nsapi, npdu_num, - suh->seg_nr, len, sch->first ? "F " : "", sch->more ? "M" : ""); - - if (sch->first) { - /* first segment of a new packet. Discard all leftover fragments of - * previous packet */ - if (!llist_empty(&sne->defrag.frag_list)) { - struct defrag_queue_entry *dqe, *dqe2; - LOGP(DSNDCP, LOGL_INFO, "TLLI=0x%08x NSAPI=%u: Dropping " - "SN-PDU %u due to insufficient segments (%04x)\n", - sne->lle->llme->tlli, sne->nsapi, sne->defrag.npdu, - sne->defrag.seg_have); - llist_for_each_entry_safe(dqe, dqe2, &sne->defrag.frag_list, list) { - llist_del(&dqe->list); - talloc_free(dqe); - } - } - /* store the currently de-fragmented PDU number */ - sne->defrag.npdu = npdu_num; - - /* Re-set fragmentation state */ - sne->defrag.no_more = sne->defrag.highest_seg = sne->defrag.seg_have = 0; - sne->defrag.tot_len = 0; - /* FIXME: (re)start timer */ - } - - if (sne->defrag.npdu != npdu_num) { - LOGP(DSNDCP, LOGL_INFO, "Segment for different SN-PDU " - "(%u != %u)\n", npdu_num, sne->defrag.npdu); - /* FIXME */ - } - - /* FIXME: check if seg_nr already exists */ - /* make sure to subtract length of SNDCP header from 'len' */ - rc = defrag_enqueue(sne, suh->seg_nr, data, len - (data - hdr)); - if (rc < 0) - return rc; - - if (!sch->more) { - /* this is suppsed to be the last segment of the N-PDU, but it - * might well be not the last to arrive */ - sne->defrag.no_more = 1; - } - - if (sne->defrag.no_more) { - /* we have already received the last segment before, let's check - * if all the previous segments exist */ - if (defrag_have_all_segments(sne)) - return defrag_segments(sne); - } - - return 0; -} - -static struct gprs_sndcp_entity *gprs_sndcp_entity_by_lle(const struct gprs_llc_lle *lle, - uint8_t nsapi) -{ - struct gprs_sndcp_entity *sne; - - llist_for_each_entry(sne, &gprs_sndcp_entities, list) { - if (sne->lle == lle && sne->nsapi == nsapi) - return sne; - } - return NULL; -} - -static struct gprs_sndcp_entity *gprs_sndcp_entity_alloc(struct gprs_llc_lle *lle, - uint8_t nsapi) -{ - struct gprs_sndcp_entity *sne; - - sne = talloc_zero(tall_sndcp_ctx, struct gprs_sndcp_entity); - if (!sne) - return NULL; - - sne->lle = lle; - sne->nsapi = nsapi; - sne->defrag.timer.data = sne; - //sne->fqueue.timer.cb = FIXME; - sne->rx_state = SNDCP_RX_S_FIRST; - INIT_LLIST_HEAD(&sne->defrag.frag_list); - - llist_add(&sne->list, &gprs_sndcp_entities); - - return sne; -} - -/* Entry point for the SNSM-ACTIVATE.indication */ -int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) -{ - LOGP(DSNDCP, LOGL_INFO, "SNSM-ACTIVATE.ind (lle=%p TLLI=%08x, " - "SAPI=%u, NSAPI=%u)\n", lle, lle->llme->tlli, lle->sapi, nsapi); - - if (gprs_sndcp_entity_by_lle(lle, nsapi)) { - LOGP(DSNDCP, LOGL_ERROR, "Trying to ACTIVATE " - "already-existing entity (TLLI=%08x, NSAPI=%u)\n", - lle->llme->tlli, nsapi); - return -EEXIST; - } - - if (!gprs_sndcp_entity_alloc(lle, nsapi)) { - LOGP(DSNDCP, LOGL_ERROR, "Out of memory during ACTIVATE\n"); - return -ENOMEM; - } - - return 0; -} - -/* Entry point for the SNSM-DEACTIVATE.indication */ -int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) -{ - struct gprs_sndcp_entity *sne; - - LOGP(DSNDCP, LOGL_INFO, "SNSM-DEACTIVATE.ind (lle=%p, TLLI=%08x, " - "SAPI=%u, NSAPI=%u)\n", lle, lle->llme->tlli, lle->sapi, nsapi); - - sne = gprs_sndcp_entity_by_lle(lle, nsapi); - if (!sne) { - LOGP(DSNDCP, LOGL_ERROR, "SNSM-DEACTIVATE.ind for non-" - "existing TLLI=%08x SAPI=%u NSAPI=%u\n", lle->llme->tlli, - lle->sapi, nsapi); - return -ENOENT; - } - llist_del(&sne->list); - /* frag queue entries are hierarchically allocated, so no need to - * free them explicitly here */ - talloc_free(sne); - - return 0; -} - -/* Fragmenter state */ -struct sndcp_frag_state { - uint8_t frag_nr; - struct msgb *msg; /* original message */ - uint8_t *next_byte; /* first byte of next fragment */ - - struct gprs_sndcp_entity *sne; - void *mmcontext; -}; - -/* returns '1' if there are more fragments to send, '0' if none */ -static int sndcp_send_ud_frag(struct sndcp_frag_state *fs) -{ - struct gprs_sndcp_entity *sne = fs->sne; - struct gprs_llc_lle *lle = sne->lle; - struct sndcp_common_hdr *sch; - struct sndcp_comp_hdr *scomph; - struct sndcp_udata_hdr *suh; - struct msgb *fmsg; - unsigned int max_payload_len; - unsigned int len; - uint8_t *data; - int rc, more; - - fmsg = msgb_alloc_headroom(fs->sne->lle->params.n201_u+256, 128, - "SNDCP Frag"); - if (!fmsg) - return -ENOMEM; - - /* make sure lower layers route the fragment like the original */ - msgb_tlli(fmsg) = msgb_tlli(fs->msg); - msgb_bvci(fmsg) = msgb_bvci(fs->msg); - msgb_nsei(fmsg) = msgb_nsei(fs->msg); - - /* prepend common SNDCP header */ - sch = (struct sndcp_common_hdr *) msgb_put(fmsg, sizeof(*sch)); - sch->nsapi = sne->nsapi; - /* Set FIRST bit if we are the first fragment in a series */ - if (fs->frag_nr == 0) - sch->first = 1; - sch->type = 1; - - /* append the compression header for first fragment */ - if (sch->first) { - scomph = (struct sndcp_comp_hdr *) - msgb_put(fmsg, sizeof(*scomph)); - scomph->pcomp = 0; - scomph->dcomp = 0; - } - - /* append the user-data header */ - suh = (struct sndcp_udata_hdr *) msgb_put(fmsg, sizeof(*suh)); - suh->npdu_low = sne->tx_npdu_nr & 0xff; - suh->npdu_high = (sne->tx_npdu_nr >> 8) & 0xf; - suh->seg_nr = fs->frag_nr % 0xf; - - /* calculate remaining length to be sent */ - len = (fs->msg->data + fs->msg->len) - fs->next_byte; - /* how much payload can we actually send via LLC? */ - max_payload_len = lle->params.n201_u - (sizeof(*sch) + sizeof(*suh)); - if (sch->first) - max_payload_len -= sizeof(*scomph); - /* check if we're exceeding the max */ - if (len > max_payload_len) - len = max_payload_len; - - /* copy the actual fragment data into our fmsg */ - data = msgb_put(fmsg, len); - memcpy(data, fs->next_byte, len); - - /* Increment fragment number and data pointer to next fragment */ - fs->frag_nr++; - fs->next_byte += len; - - /* determine if we have more fragemnts to send */ - if ((fs->msg->data + fs->msg->len) <= fs->next_byte) - more = 0; - else - more = 1; - - /* set the MORE bit of the SNDCP header accordingly */ - sch->more = more; - - rc = gprs_llc_tx_ui(fmsg, lle->sapi, 0, fs->mmcontext); - if (rc < 0) { - /* abort in case of error, do not advance frag_nr / next_byte */ - msgb_free(fmsg); - return rc; - } - - if (!more) { - /* we've sent all fragments */ - msgb_free(fs->msg); - memset(fs, 0, sizeof(*fs)); - /* increment NPDU number for next frame */ - sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff; - return 0; - } - - /* default: more fragments to send */ - return 1; -} - -/* Request transmission of a SN-PDU over specified LLC Entity + SAPI */ -int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi, - void *mmcontext) -{ - struct gprs_sndcp_entity *sne; - struct sndcp_common_hdr *sch; - struct sndcp_comp_hdr *scomph; - struct sndcp_udata_hdr *suh; - struct sndcp_frag_state fs; - - /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ - - sne = gprs_sndcp_entity_by_lle(lle, nsapi); - if (!sne) { - LOGP(DSNDCP, LOGL_ERROR, "Cannot find SNDCP Entity\n"); - return -EIO; - } - - /* Check if we need to fragment this N-PDU into multiple SN-PDUs */ - if (msg->len > lle->params.n201_u - - (sizeof(*sch) + sizeof(*suh) + sizeof(*scomph))) { - /* initialize the fragmenter state */ - fs.msg = msg; - fs.frag_nr = 0; - fs.next_byte = msg->data; - fs.sne = sne; - fs.mmcontext = mmcontext; - - /* call function to generate and send fragments until all - * of the N-PDU has been sent */ - while (1) { - int rc = sndcp_send_ud_frag(&fs); - if (rc == 0) - return 0; - if (rc < 0) - return rc; - } - /* not reached */ - return 0; - } - - /* this is the non-fragmenting case where we only build 1 SN-PDU */ - - /* prepend the user-data header */ - suh = (struct sndcp_udata_hdr *) msgb_push(msg, sizeof(*suh)); - suh->npdu_low = sne->tx_npdu_nr & 0xff; - suh->npdu_high = (sne->tx_npdu_nr >> 8) & 0xf; - suh->seg_nr = 0; - sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff; - - scomph = (struct sndcp_comp_hdr *) msgb_push(msg, sizeof(*scomph)); - scomph->pcomp = 0; - scomph->dcomp = 0; - - /* prepend common SNDCP header */ - sch = (struct sndcp_common_hdr *) msgb_push(msg, sizeof(*sch)); - sch->first = 1; - sch->type = 1; - sch->nsapi = nsapi; - - return gprs_llc_tx_ui(msg, lle->sapi, 0, mmcontext); -} - -/* Section 5.1.2.17 LL-UNITDATA.ind */ -int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, - uint8_t *hdr, uint16_t len) -{ - struct gprs_sndcp_entity *sne; - struct sndcp_common_hdr *sch = (struct sndcp_common_hdr *)hdr; - struct sndcp_comp_hdr *scomph = NULL; - struct sndcp_udata_hdr *suh; - uint8_t *npdu; - uint16_t npdu_num; - int npdu_len; - - sch = (struct sndcp_common_hdr *) hdr; - if (sch->first) { - scomph = (struct sndcp_comp_hdr *) (hdr + 1); - suh = (struct sndcp_udata_hdr *) (hdr + 1 + sizeof(struct sndcp_common_hdr)); - } else - suh = (struct sndcp_udata_hdr *) (hdr + sizeof(struct sndcp_common_hdr)); - - if (sch->type == 0) { - LOGP(DSNDCP, LOGL_ERROR, "SN-DATA PDU at unitdata_ind() function\n"); - return -EINVAL; - } - - if (len < sizeof(*sch) + sizeof(*suh)) { - LOGP(DSNDCP, LOGL_ERROR, "SN-UNITDATA PDU too short (%u)\n", len); - return -EIO; - } - - sne = gprs_sndcp_entity_by_lle(lle, sch->nsapi); - if (!sne) { - LOGP(DSNDCP, LOGL_ERROR, "Message for non-existing SNDCP Entity " - "(lle=%p, TLLI=%08x, SAPI=%u, NSAPI=%u)\n", lle, - lle->llme->tlli, lle->sapi, sch->nsapi); - return -EIO; - } - /* FIXME: move this RA_ID up to the LLME or even higher */ - bssgp_parse_cell_id(&sne->ra_id, msgb_bcid(msg)); - - /* any non-first segment is by definition something to defragment - * as is any segment that tells us there are more segments */ - if (!sch->first || sch->more) - return defrag_input(sne, msg, hdr, len); - - if (scomph && (scomph->pcomp || scomph->dcomp)) { - LOGP(DSNDCP, LOGL_ERROR, "We don't support compression yet\n"); - return -EIO; - } - - npdu_num = (suh->npdu_high << 8) | suh->npdu_low; - npdu = (uint8_t *)suh + sizeof(*suh); - npdu_len = (msg->data + msg->len) - npdu; - if (npdu_len <= 0) { - LOGP(DSNDCP, LOGL_ERROR, "Short SNDCP N-PDU: %d\n", npdu_len); - return -EIO; - } - /* actually send the N-PDU to the SGSN core code, which then - * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ - return sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli, sne->nsapi, msg, npdu_len, npdu); -} - -#if 0 -/* Section 5.1.2.1 LL-RESET.ind */ -static int sndcp_ll_reset_ind(struct gprs_sndcp_entity *se) -{ - /* treat all outstanding SNDCP-LLC request type primitives as not sent */ - /* reset all SNDCP XID parameters to default values */ - LOGP(DSNDCP, LOGL_NOTICE, "not implemented.\n"); - return 0; -} - -static int sndcp_ll_status_ind() -{ - /* inform the SM sub-layer by means of SNSM-STATUS.req */ - LOGP(DSNDCP, LOGL_NOTICE, "not implemented.\n"); - return 0; -} - -static struct sndcp_state_list {{ - uint32_t states; - unsigned int type; - int (*rout)(struct gprs_sndcp_entity *se, struct msgb *msg); -} sndcp_state_list[] = { - { ALL_STATES, - LL_RESET_IND, sndcp_ll_reset_ind }, - { ALL_STATES, - LL_ESTABLISH_IND, sndcp_ll_est_ind }, - { SBIT(SNDCP_S_EST_RQD), - LL_ESTABLISH_RESP, sndcp_ll_est_ind }, - { SBIT(SNDCP_S_EST_RQD), - LL_ESTABLISH_CONF, sndcp_ll_est_conf }, - { SBIT(SNDCP_S_ -}; - -static int sndcp_rx_llc_prim() -{ - case LL_ESTABLISH_REQ: - case LL_RELEASE_REQ: - case LL_XID_REQ: - case LL_DATA_REQ: - LL_UNITDATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ - - switch (prim) { - case LL_RESET_IND: - case LL_ESTABLISH_IND: - case LL_ESTABLISH_RESP: - case LL_ESTABLISH_CONF: - case LL_RELEASE_IND: - case LL_RELEASE_CONF: - case LL_XID_IND: - case LL_XID_RESP: - case LL_XID_CONF: - case LL_DATA_IND: - case LL_DATA_CONF: - case LL_UNITDATA_IND: - case LL_STATUS_IND: -} -#endif diff --git a/openbsc/src/gprs/gprs_sndcp.h b/openbsc/src/gprs/gprs_sndcp.h deleted file mode 100644 index fef871aa1..000000000 --- a/openbsc/src/gprs/gprs_sndcp.h +++ /dev/null @@ -1,53 +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; -}; - -/* See 6.7.1.2 Reassembly */ -enum sndcp_rx_state { - SNDCP_RX_S_FIRST, - SNDCP_RX_S_SUBSEQ, - SNDCP_RX_S_DISCARD, -}; - -struct gprs_sndcp_entity { - struct llist_head list; - - /* FIXME: move this RA_ID up to the LLME or even higher */ - struct gprs_ra_id ra_id; - /* reference to the LLC Entity below this SNDCP entity */ - struct gprs_llc_lle *lle; - /* The NSAPI we shall use on top of LLC */ - uint8_t nsapi; - - /* NPDU number for the GTP->SNDCP side */ - uint16_t tx_npdu_nr; - /* SNDCP eeceiver state */ - enum sndcp_rx_state rx_state; - /* The defragmentation queue */ - struct defrag_state defrag; -}; - -extern struct llist_head gprs_sndcp_entities; - -#endif /* INT_SNDCP_H */ diff --git a/openbsc/src/gprs/gprs_sndcp_vty.c b/openbsc/src/gprs/gprs_sndcp_vty.c deleted file mode 100644 index deeef071a..000000000 --- a/openbsc/src/gprs/gprs_sndcp_vty.c +++ /dev/null @@ -1,72 +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 "gprs_sndcp.h" - -#include -#include - -static void vty_dump_sne(struct vty *vty, struct gprs_sndcp_entity *sne) -{ - vty_out(vty, " TLLI %08x SAPI=%u NSAPI=%u:%s", - sne->lle->llme->tlli, sne->lle->sapi, sne->nsapi, VTY_NEWLINE); - vty_out(vty, " Defrag: npdu=%u highest_seg=%u seg_have=0x%08x tot_len=%u%s", - sne->defrag.npdu, sne->defrag.highest_seg, sne->defrag.seg_have, - sne->defrag.tot_len, VTY_NEWLINE); -} - - -DEFUN(show_sndcp, show_sndcp_cmd, - "show sndcp", - SHOW_STR "Display information about the SNDCP protocol") -{ - struct gprs_sndcp_entity *sne; - - vty_out(vty, "State of SNDCP Entities%s", VTY_NEWLINE); - llist_for_each_entry(sne, &gprs_sndcp_entities, list) - vty_dump_sne(vty, sne); - - return CMD_SUCCESS; -} - -int gprs_sndcp_vty_init(void) -{ - install_element_ve(&show_sndcp_cmd); - - return 0; -} diff --git a/openbsc/src/gprs/osmo_sgsn.cfg b/openbsc/src/gprs/osmo_sgsn.cfg deleted file mode 100644 index c4c9ec1cf..000000000 --- a/openbsc/src/gprs/osmo_sgsn.cfg +++ /dev/null @@ -1,23 +0,0 @@ -! -! Osmocom SGSN (0.9.0.474-0ede2) configuration saved from vty -!! -! -line vty - no login -! -sgsn - gtp local-ip 192.168.100.11 - ggsn 0 remote-ip 192.168.100.239 - ggsn 0 gtp-version 1 -ns - timer tns-block 3 - timer tns-block-retries 3 - timer tns-reset 3 - timer tns-reset-retries 3 - timer tns-test 30 - timer tns-alive 3 - timer tns-alive-retries 10 - encapsulation udp local-ip 192.168.100.11 - encapsulation udp local-port 23000 - encapsulation framerelay-gre enabled 0 -bssgp diff --git a/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c deleted file mode 100644 index f2eb35d2c..000000000 --- a/openbsc/src/gprs/sgsn_libgtp.c +++ /dev/null @@ -1,622 +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 - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a 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 - -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 sgsn_pdp_ctx *pctx; - struct pdp_t *pdp; - uint64_t imsi_ui64; - 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 not verified */ - pdp->selmode = 0x01; - - /* IMSI, TEID/TEIC, FLLU/FLLC, TID, NSAPI set in pdp_newpdp */ - - /* FIXME: MSISDN in BCD format from mmctx */ - //pdp->msisdn.l/.v - - /* 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 */ - pdp->qos_req.l = TLVP_LEN(tp, OSMO_IE_GSM_REQ_QOS); - if (pdp->qos_req.l > sizeof(pdp->qos_req.v)) - pdp->qos_req.l = sizeof(pdp->qos_req.v); - memcpy(pdp->qos_req.v, TLVP_VAL(tp, OSMO_IE_GSM_REQ_QOS), - pdp->qos_req.l); - - /* SGSN address for control plane */ - pdp->gsnlc.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr); - memcpy(pdp->gsnlc.v, &sgsn->cfg.gtp_listenaddr.sin_addr, - sizeof(sgsn->cfg.gtp_listenaddr.sin_addr)); - - /* SGSN address for user plane */ - pdp->gsnlu.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr); - memcpy(pdp->gsnlu.v, &sgsn->cfg.gtp_listenaddr.sin_addr, - sizeof(sgsn->cfg.gtp_listenaddr.sin_addr)); - - /* 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) -{ - LOGP(DGPRS, LOGL_ERROR, "Delete PDP Context\n"); - - /* FIXME: decide if we need teardown or not ! */ - return gtp_delete_context_req(pctx->ggsn->gsn, pctx->lib, pctx, 1); -} - -struct cause_map { - uint8_t cause_in; - uint8_t cause_out; -}; - -static uint8_t cause_map(const struct cause_map *map, uint8_t in, uint8_t deflt) -{ - const struct cause_map *m; - - for (m = map; m->cause_in && m->cause_out; m++) { - if (m->cause_in == in) - return m->cause_out; - } - return deflt; -} - -/* how do we map from gtp cause to SM cause */ -static const struct cause_map gtp2sm_cause_map[] = { - { GTPCAUSE_NO_RESOURCES, GSM_CAUSE_INSUFF_RSRC }, - { GTPCAUSE_NOT_SUPPORTED, GSM_CAUSE_SERV_OPT_NOTSUPP }, - { GTPCAUSE_MAN_IE_INCORRECT, GSM_CAUSE_INV_MAND_INFO }, - { GTPCAUSE_MAN_IE_MISSING, GSM_CAUSE_INV_MAND_INFO }, - { GTPCAUSE_OPT_IE_INCORRECT, GSM_CAUSE_PROTO_ERR_UNSPEC }, - { GTPCAUSE_SYS_FAIL, GSM_CAUSE_NET_FAIL }, - { GTPCAUSE_ROAMING_REST, GSM_CAUSE_REQ_SERV_OPT_NOTSUB }, - { GTPCAUSE_PTIMSI_MISMATCH, GSM_CAUSE_PROTO_ERR_UNSPEC }, - { GTPCAUSE_CONN_SUSP, GSM_CAUSE_PROTO_ERR_UNSPEC }, - { GTPCAUSE_AUTH_FAIL, GSM_CAUSE_AUTH_FAILED }, - { GTPCAUSE_USER_AUTH_FAIL, GSM_CAUSE_ACT_REJ_GGSN }, - { GTPCAUSE_CONTEXT_NOT_FOUND, GSM_CAUSE_PROTO_ERR_UNSPEC }, - { GTPCAUSE_ADDR_OCCUPIED, GSM_CAUSE_INSUFF_RSRC }, - { GTPCAUSE_NO_MEMORY, GSM_CAUSE_INSUFF_RSRC }, - { GTPCAUSE_RELOC_FAIL, GSM_CAUSE_PROTO_ERR_UNSPEC }, - { GTPCAUSE_UNKNOWN_MAN_EXTHEADER, GSM_CAUSE_PROTO_ERR_UNSPEC }, - { GTPCAUSE_MISSING_APN, GSM_CAUSE_MISSING_APN }, - { GTPCAUSE_UNKNOWN_PDP, GSM_CAUSE_UNKNOWN_PDP }, - { 0, 0 } -}; - -/* The GGSN has confirmed the creation of a PDP Context */ -static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) -{ - struct sgsn_pdp_ctx *pctx = cbp; - uint8_t reject_cause; - int rc; - - DEBUGP(DGPRS, "Received CREATE PDP CTX CONF, cause=%d(%s)\n", - cause, get_value_string(gtp_cause_strs, cause)); - - /* Check for cause value if it was really successful */ - if (cause < 0) { - LOGP(DGPRS, LOGL_NOTICE, "Create PDP ctx req timed out\n"); - if (pdp && pdp->version == 1) { - pdp->version = 0; - gtp_create_context_req(sgsn->gsn, pdp, cbp); - return 0; - } else { - reject_cause = GSM_CAUSE_NET_FAIL; - goto reject; - } - } - - /* Check for cause value if it was really successful */ - if (cause != GTPCAUSE_ACC_REQ) { - reject_cause = cause_map(gtp2sm_cause_map, cause, - GSM_CAUSE_ACT_REJ_GGSN); - goto reject; - } - - /* Activate the SNDCP layer */ - sndcp_sm_activate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi); - - /* Send PDP CTX ACT to MS */ - return gsm48_tx_gsm_act_pdp_acc(pctx); - -reject: - pctx->state = PDP_STATE_NONE; - if (pdp) - pdp_freepdp(pdp); - /* Send PDP CTX ACT REJ to MS */ - rc = gsm48_tx_gsm_act_pdp_rej(pctx->mm, pctx->ti, reject_cause, - 0, NULL); - sgsn_pdp_ctx_free(pctx); - - return EOF; -} - -/* Confirmation of a PDP Context Delete */ -static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) -{ - struct sgsn_pdp_ctx *pctx = cbp; - int rc; - - DEBUGP(DGPRS, "Received DELETE PDP CTX CONF, cause=%d(%s)\n", - cause, get_value_string(gtp_cause_strs, cause)); - - /* Deactivate the SNDCP layer */ - sndcp_sm_deactivate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi); - - /* Confirm deactivation of PDP context to MS */ - rc = gsm48_tx_gsm_deact_pdp_acc(pctx); - - /* 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) { - DEBUGP(DGPRS, "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) { - DEBUGP(DGPRS, "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; - int rc; - - DEBUGP(DGPRS, "GTP DATA IND from GGSN, length=%u\n", len); - - pdp = lib->priv; - if (!pdp) { - LOGP(DGPRS, LOGL_NOTICE, - "GTP DATA IND from GGSN for unknown PDP\n"); - return -EIO; - } - mm = pdp->mm; - if (!mm) { - LOGP(DGPRS, LOGL_ERROR, - "PDP context (imsi=%s) without MM context!\n", mm->imsi); - return -EIO; - } - - msg = msgb_alloc_headroom(len+256, 128, "GTP->SNDCP"); - ud = msgb_put(msg, len); - memcpy(ud, packet, len); - - msgb_tlli(msg) = mm->tlli; - msgb_bvci(msg) = mm->bvci; - msgb_nsei(msg) = mm->nsei; - - switch (mm->mm_state) { - case GMM_REGISTERED_SUSPENDED: - /* initiate PS PAGING procedure */ - memset(&pinfo, 0, sizeof(pinfo)); - pinfo.mode = BSSGP_PAGING_PS; - pinfo.scope = BSSGP_PAGING_BVCI; - pinfo.bvci = mm->bvci; - pinfo.imsi = mm->imsi; - pinfo.ptmsi = &mm->p_tmsi; - pinfo.drx_params = mm->drx_parms; - pinfo.qos[0] = 0; // FIXME - rc = bssgp_tx_paging(mm->nsei, 0, &pinfo); - rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PAGING_PS]); - /* FIXME: queue the packet we received from GTP */ - break; - case GMM_REGISTERED_NORMAL: - break; - default: - LOGP(DGPRS, LOGL_ERROR, "GTP DATA IND for TLLI %08X in state " - "%u\n", mm->tlli, mm->mm_state); - msgb_free(msg); - return -1; - } - - rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_OUT]); - rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_OUT], len); - rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_UDATA_OUT]); - rate_ctr_add(&mm->ctrg->ctr[GMM_CTR_BYTES_UDATA_OUT], len); - - return sndcp_unitdata_req(msg, &mm->llme->lle[pdp->sapi], - pdp->nsapi, mm); -} - -/* Called by SNDCP when it has received/re-assembled a N-PDU */ -int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi, - struct msgb *msg, uint32_t npdu_len, uint8_t *npdu) -{ - struct sgsn_mm_ctx *mmctx; - struct sgsn_pdp_ctx *pdp; - - /* look-up the MM context for this message */ - mmctx = sgsn_mm_ctx_by_tlli(tlli, ra_id); - if (!mmctx) { - LOGP(DGPRS, LOGL_ERROR, - "Cannot find MM CTX for TLLI %08x\n", tlli); - return -EIO; - } - /* look-up the PDP context for this message */ - pdp = sgsn_pdp_ctx_by_nsapi(mmctx, nsapi); - if (!pdp) { - LOGP(DGPRS, LOGL_ERROR, "Cannot find PDP CTX for " - "TLLI=%08x, NSAPI=%u\n", tlli, nsapi); - return -EIO; - } - if (!pdp->lib) { - LOGP(DGPRS, LOGL_ERROR, "PDP CTX without libgtp\n"); - return -EIO; - } - - rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_IN]); - rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_IN], npdu_len); - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_UDATA_IN]); - rate_ctr_add(&mmctx->ctrg->ctr[GMM_CTR_BYTES_UDATA_IN], npdu_len); - - return gtp_data_req(pdp->ggsn->gsn, pdp->lib, npdu, npdu_len); -} - -/* libgtp select loop integration */ -static int sgsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what) -{ - struct sgsn_instance *sgi = fd->data; - int rc; - - if (!(what & BSC_FD_READ)) - return 0; - - switch (fd->priv_nr) { - case 0: - rc = gtp_decaps0(sgi->gsn); - break; - case 1: - rc = gtp_decaps1c(sgi->gsn); - break; - case 2: - rc = gtp_decaps1u(sgi->gsn); - break; - default: - rc = -EINVAL; - break; - } - return rc; -} - -static void sgsn_gtp_tmr_start(struct sgsn_instance *sgi) -{ - struct timeval next; - - /* Retrieve next retransmission as struct timeval */ - gtp_retranstimeout(sgi->gsn, &next); - - /* re-schedule the timer */ - osmo_timer_schedule(&sgi->gtp_timer, next.tv_sec, next.tv_usec/1000); -} - -/* timer callback for libgtp retransmissions and ping */ -static void sgsn_gtp_tmr_cb(void *data) -{ - struct sgsn_instance *sgi = data; - - /* Do all the retransmissions as needed */ - gtp_retrans(sgi->gsn); - - sgsn_gtp_tmr_start(sgi); -} - -int sgsn_gtp_init(struct sgsn_instance *sgi) -{ - int rc; - struct gsn_t *gsn; - - rc = gtp_new(&sgi->gsn, sgi->cfg.gtp_statedir, - &sgi->cfg.gtp_listenaddr.sin_addr, GTP_MODE_SGSN); - if (rc) { - LOGP(DGPRS, LOGL_ERROR, "Failed to create GTP: %d\n", rc); - return rc; - } - gsn = sgi->gsn; - - sgi->gtp_fd0.fd = gsn->fd0; - sgi->gtp_fd0.priv_nr = 0; - sgi->gtp_fd0.data = sgi; - sgi->gtp_fd0.when = BSC_FD_READ; - sgi->gtp_fd0.cb = sgsn_gtp_fd_cb; - rc = osmo_fd_register(&sgi->gtp_fd0); - if (rc < 0) - return rc; - - sgi->gtp_fd1c.fd = gsn->fd1c; - sgi->gtp_fd1c.priv_nr = 1; - sgi->gtp_fd1c.data = sgi; - sgi->gtp_fd1c.when = BSC_FD_READ; - sgi->gtp_fd1c.cb = sgsn_gtp_fd_cb; - rc = osmo_fd_register(&sgi->gtp_fd1c); - if (rc < 0) { - osmo_fd_unregister(&sgi->gtp_fd0); - return rc; - } - - sgi->gtp_fd1u.fd = gsn->fd1u; - sgi->gtp_fd1u.priv_nr = 2; - sgi->gtp_fd1u.data = sgi; - sgi->gtp_fd1u.when = BSC_FD_READ; - sgi->gtp_fd1u.cb = sgsn_gtp_fd_cb; - rc = osmo_fd_register(&sgi->gtp_fd1u); - if (rc < 0) { - osmo_fd_unregister(&sgi->gtp_fd0); - osmo_fd_unregister(&sgi->gtp_fd1c); - return rc; - } - - /* Start GTP re-transmission timer */ - sgi->gtp_timer.cb = sgsn_gtp_tmr_cb; - sgi->gtp_timer.data = sgi; - sgsn_gtp_tmr_start(sgi); - - /* Register callbackcs with libgtp */ - gtp_set_cb_delete_context(gsn, cb_delete_context); - gtp_set_cb_conf(gsn, cb_conf); - gtp_set_cb_recovery(gsn, cb_recovery); - gtp_set_cb_data_ind(gsn, cb_data_ind); - gtp_set_cb_unsup_ind(gsn, cb_unsup_ind); - gtp_set_cb_extheader_ind(gsn, cb_extheader_ind); - - return 0; -} diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c deleted file mode 100644 index 040e1d713..000000000 --- a/openbsc/src/gprs/sgsn_main.c +++ /dev/null @@ -1,380 +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 "../../bscconfig.h" - -/* FIXME: put into seperate header file */ -int gprs_sndcp_vty_init(void); -int sgsn_gtp_init(struct sgsn_instance *sgi); - -/* this is here for the vty... it will never be called */ -void subscr_put() { abort(); } - -#define _GNU_SOURCE -#include - -void *tall_bsc_ctx; - -struct gprs_ns_inst *sgsn_nsi; -static int daemonize = 0; -const char *openbsc_copyright = - "Copyright (C) 2010 Harald Welte and On-Waves\r\n" - "License AGPLv3+: GNU AGPL version 2 or later \r\n" - "This is free software: you are free to change and redistribute it.\r\n" - "There is NO WARRANTY, to the extent permitted by law.\r\n"; - -static struct sgsn_instance sgsn_inst = { - .config_file = "osmo_sgsn.cfg", - .cfg = { - .gtp_statedir = "./", - .acl_enabled = 1, - }, -}; -struct sgsn_instance *sgsn = &sgsn_inst; - -/* call-back function for the NS protocol */ -static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, - struct msgb *msg, uint16_t bvci) -{ - int rc = 0; - - switch (event) { - case GPRS_NS_EVT_UNIT_DATA: - /* hand the message into the BSSGP implementation */ - rc = bssgp_rcvmsg(msg); - break; - default: - LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event); - if (msg) - msgb_free(msg); - rc = -EIO; - break; - } - return rc; -} - -/* call-back function for the BSSGP protocol */ -int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) -{ - struct osmo_bssgp_prim *bp; - bp = container_of(oph, struct osmo_bssgp_prim, oph); - - switch (oph->sap) { - case SAP_BSSGP_LL: - switch (oph->primitive) { - case PRIM_BSSGP_UL_UD: - return gprs_llc_rcvmsg(oph->msg, bp->tp); - } - break; - case SAP_BSSGP_GMM: - switch (oph->primitive) { - case PRIM_BSSGP_GMM_SUSPEND: - return gprs_gmm_rx_suspend(bp->ra_id, bp->tlli); - case PRIM_BSSGP_GMM_RESUME: - return gprs_gmm_rx_resume(bp->ra_id, bp->tlli, - bp->u.resume.suspend_ref); - } - break; - case SAP_BSSGP_NM: - break; - } - return 0; -} - -static void signal_handler(int signal) -{ - fprintf(stdout, "signal %u received\n", signal); - - switch (signal) { - case SIGINT: - osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); - sleep(1); - exit(0); - break; - case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report(tall_vty_ctx, stderr); - talloc_report_full(tall_bsc_ctx, stderr); - break; - case SIGUSR2: - talloc_report_full(tall_vty_ctx, stderr); - break; - default: - break; - } -} - -/* NSI that BSSGP uses when transmitting on NS */ -extern struct gprs_ns_inst *bssgp_nsi; -extern void *tall_msgb_ctx; - -extern enum node_type 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\n"); - printf(" -e --log-level number\tSet a global log level\n"); -} - -static void handle_options(int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"debug", 1, 0, 'd'}, - {"daemonize", 0, 0, 'D'}, - {"config-file", 1, 0, 'c'}, - {"disable-color", 0, 0, 's'}, - {"timestamp", 0, 0, 'T'}, - {"log-level", 1, 0, 'e'}, - {NULL, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hd:Dc:sTe:", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - //print_usage(); - print_help(); - exit(0); - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - daemonize = 1; - break; - case 'c': - sgsn_inst.config_file = strdup(optarg); - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - default: - /* ignore */ - break; - } - } -} - -/* default categories */ -static struct log_info_cat gprs_categories[] = { - [DMM] = { - .name = "DMM", - .description = "Layer3 Mobility Management (MM)", - .color = "\033[1;33m", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DPAG] = { - .name = "DPAG", - .description = "Paging Subsystem", - .color = "\033[1;38m", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DMEAS] = { - .name = "DMEAS", - .description = "Radio Measurement Processing", - .enabled = 0, .loglevel = LOGL_NOTICE, - }, - [DREF] = { - .name = "DREF", - .description = "Reference Counting", - .enabled = 0, .loglevel = LOGL_NOTICE, - }, - [DGPRS] = { - .name = "DGPRS", - .description = "GPRS Packet Service", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DNS] = { - .name = "DNS", - .description = "GPRS Network Service (NS)", - .enabled = 1, .loglevel = LOGL_INFO, - }, - [DBSSGP] = { - .name = "DBSSGP", - .description = "GPRS BSS Gateway Protocol (BSSGP)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DLLC] = { - .name = "DLLC", - .description = "GPRS Logical Link Control Protocol (LLC)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DSNDCP] = { - .name = "DSNDCP", - .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, -}; - -static const struct log_info gprs_log_info = { - .filter_fn = gprs_log_filter_fn, - .cat = gprs_categories, - .num_cat = ARRAY_SIZE(gprs_categories), -}; - - -int main(int argc, char **argv) -{ - struct gsm_network dummy_network; - int rc; - - tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn"); - tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); - - signal(SIGINT, &signal_handler); - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - signal(SIGUSR2, &signal_handler); - - osmo_init_ignore_signals(); - osmo_init_logging(&gprs_log_info); - - vty_info.copyright = openbsc_copyright; - vty_init(&vty_info); - logging_vty_add_cmds(&gprs_log_info); - sgsn_vty_init(); - - handle_options(argc, argv); - - rate_ctr_init(tall_bsc_ctx); - rc = telnet_init(tall_bsc_ctx, &dummy_network, 4245); - if (rc < 0) - exit(1); - - gprs_ns_set_log_ss(DNS); - bssgp_set_log_ss(DBSSGP); - - sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_bsc_ctx); - if (!sgsn_nsi) { - LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); - exit(1); - } - bssgp_nsi = sgsn_inst.cfg.nsi = sgsn_nsi; - - gprs_llc_init("/usr/local/lib/osmocom/crypt/"); - - gprs_ns_vty_init(bssgp_nsi); - bssgp_vty_init(); - gprs_llc_vty_init(); - gprs_sndcp_vty_init(); - /* FIXME: register signal handler for SS_L_NS */ - - rc = sgsn_parse_config(sgsn_inst.config_file, &sgsn_inst.cfg); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n"); - exit(2); - } - - rc = sgsn_gtp_init(&sgsn_inst); - if (rc) { - LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on GTP socket\n"); - exit(2); - } - - rc = gprs_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 (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 a4ba2802e..000000000 --- a/openbsc/src/gprs/sgsn_vty.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * (C) 2010-2013 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 sgsn_config *g_cfg = NULL; - -struct imsi_acl_entry { - struct llist_head list; - char imsi[16+1]; -}; - -#define GSM48_MAX_APN_LEN 102 /* 10.5.6.1 */ -static char *gprs_apn2str(uint8_t *apn, unsigned int len) -{ - static char apnbuf[GSM48_MAX_APN_LEN+1]; - unsigned int i = 0; - - if (!apn) - return ""; - - if (len > sizeof(apnbuf)-1) - len = sizeof(apnbuf)-1; - - memcpy(apnbuf, apn, len); - apnbuf[len] = '\0'; - - /* replace the domain name step sizes with dots */ - while (i < len) { - unsigned int step = apnbuf[i]; - apnbuf[i] = '.'; - i += step+1; - } - - return apnbuf+1; -} - -static 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; - - 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) { - 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); - } - - vty_out(vty, " auth-policy %s%s", - g_cfg->acl_enabled ? "closed" : "accept-all", VTY_NEWLINE); - llist_for_each_entry(acl, &g_cfg->imsi_acl, list) - vty_out(vty, " imsi-acl add %s%s", acl->imsi, 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; -} - -#if 0 -DEFUN(cfg_apn_ggsn, cfg_apn_ggsn_cmd, - "apn APNAME ggsn <0-255>", - "") -{ - struct apn_ctx ** -} -#endif - -const struct value_string gprs_mm_st_strs[] = { - { GMM_DEREGISTERED, "DEREGISTERED" }, - { GMM_COMMON_PROC_INIT, "COMMON PROCEDURE (INIT)" }, - { GMM_REGISTERED_NORMAL, "REGISTERED (NORMAL)" }, - { GMM_REGISTERED_SUSPENDED, "REGISTERED (SUSPENDED)" }, - { GMM_DEREGISTERED_INIT, "DEREGISTERED (INIT)" }, - { 0, NULL } -}; - -static void vty_dump_pdp(struct vty *vty, const char *pfx, - struct sgsn_pdp_ctx *pdp) -{ - vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u%s", - pfx, pdp->mm->imsi, pdp->sapi, pdp->nsapi, VTY_NEWLINE); - vty_out(vty, "%s APN: %s%s", pfx, - gprs_apn2str(pdp->lib->apn_use.v, pdp->lib->apn_use.l), - VTY_NEWLINE); - vty_out(vty, "%s PDP Address: %s%s", pfx, - gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l), - VTY_NEWLINE); - vty_out_rate_ctr_group(vty, " ", pdp->ctrg); -} - -static void vty_dump_mmctx(struct vty *vty, const char *pfx, - struct sgsn_mm_ctx *mm, int pdp) -{ - vty_out(vty, "%sMM Context for IMSI %s, IMEI %s, P-TMSI %08x%s", - pfx, mm->imsi, mm->imei, mm->p_tmsi, VTY_NEWLINE); - vty_out(vty, "%s MSISDN: %s, TLLI: %08x%s", pfx, mm->msisdn, - mm->tlli, VTY_NEWLINE); - vty_out(vty, "%s MM State: %s, Routeing Area: %u-%u-%u-%u, " - "Cell ID: %u%s", pfx, - get_value_string(gprs_mm_st_strs, mm->mm_state), - mm->ra.mcc, mm->ra.mnc, mm->ra.lac, mm->ra.rac, - mm->cell_id, VTY_NEWLINE); - - vty_out_rate_ctr_group(vty, " ", mm->ctrg); - - if (pdp) { - struct sgsn_pdp_ctx *pdp; - - llist_for_each_entry(pdp, &mm->pdp_list, list) - vty_dump_pdp(vty, " ", pdp); - } -} - -DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn", - SHOW_STR "Display information about the SGSN") -{ - /* 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; -} - -/* temporary IMSI ACL hack */ -struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi) -{ - struct imsi_acl_entry *acl; - llist_for_each_entry(acl, &g_cfg->imsi_acl, list) { - if (!strcmp(imsi, acl->imsi)) - return acl; - } - return NULL; -} - -int sgsn_acl_add(const char *imsi) -{ - struct imsi_acl_entry *acl; - - if (sgsn_acl_lookup(imsi)) - return -EEXIST; - - acl = talloc_zero(NULL, struct imsi_acl_entry); - if (!acl) - return -ENOMEM; - strncpy(acl->imsi, imsi, sizeof(acl->imsi)); - - llist_add(&acl->list, &g_cfg->imsi_acl); - - return 0; -} - -int sgsn_acl_del(const char *imsi) -{ - struct imsi_acl_entry *acl; - - acl = sgsn_acl_lookup(imsi); - if (!acl) - return -ENODEV; - - llist_del(&acl->list); - talloc_free(acl); - - return 0; -} - - -DEFUN(imsi_acl, cfg_imsi_acl_cmd, - "imsi-acl (add|del) IMSI", - "Access Control List of foreign IMSIs\n" - "Add IMSI to ACL\n" - "Remove IMSI from ACL\n" - "IMSI of subscriber\n") -{ - const char *op = argv[0]; - const char *imsi = argv[1]; - int rc; - - if (!strcmp(op, "add")) - rc = sgsn_acl_add(imsi); - else - rc = sgsn_acl_del(imsi); - - if (rc < 0) { - vty_out(vty, "%% unable to %s ACL\n", op); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_auth_policy, cfg_auth_policy_cmd, - "auth-policy (accept-all|closed)", - "Autorization Policy of SGSN\n" - "Accept all IMSIs (DANGEROUS\n" - "Accept only home network subscribers or those in ACL\n") -{ - if (!strcmp(argv[0], "accept-all")) - g_cfg->acl_enabled = 0; - else - g_cfg->acl_enabled = 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(CONFIG_NODE, &cfg_sgsn_cmd); - install_node(&sgsn_node, config_write_sgsn); - install_default(SGSN_NODE); - install_element(SGSN_NODE, &ournode_exit_cmd); - install_element(SGSN_NODE, &ournode_end_cmd); - 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); - - return 0; -} - -int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg) -{ - int rc; - - g_cfg = cfg; - INIT_LLIST_HEAD(&g_cfg->imsi_acl); - - 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/ipaccess/Makefile.am b/openbsc/src/ipaccess/Makefile.am deleted file mode 100644 index 308215b5d..000000000 --- a/openbsc/src/ipaccess/Makefile.am +++ /dev/null @@ -1,32 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(COVERAGE_LDFLAGS) -OSMO_LIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOABIS_LIBS) - -bin_PROGRAMS = ipaccess-find ipaccess-config ipaccess-proxy - -ipaccess_find_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libmsc/libmsc.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(OSMO_LIBS) -ipaccess_find_SOURCES = ipaccess-find.c - -ipaccess_config_SOURCES = ipaccess-config.c ipaccess-firmware.c network_listen.c - -# FIXME: resolve the bogus dependencies patched around here: -ipaccess_config_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libmsc/libmsc.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - -ldl -ldbi $(LIBCRYPT) $(OSMO_LIBS) - -ipaccess_proxy_SOURCES = ipaccess-proxy.c -ipaccess_proxy_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libmsc/libmsc.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(OSMO_LIBS) diff --git a/openbsc/src/ipaccess/ipaccess-config.c b/openbsc/src/ipaccess/ipaccess-config.c deleted file mode 100644 index e66771a1a..000000000 --- a/openbsc/src/ipaccess/ipaccess-config.c +++ /dev/null @@ -1,1024 +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 - -struct gsm_network *bsc_gsmnet; - -static int net_listen_testnr; -static int restart; -static char *prim_oml_ip; -static char *bts_ip_addr, *bts_ip_mask, *bts_ip_gw; -static char *unit_id; -static uint16_t nv_flags; -static uint16_t nv_mask; -static char *software = NULL; -static int sw_load_state = 0; -static int oml_state = 0; -static int dump_files = 0; -static char *firmware_analysis = NULL; -static int found_trx = 0; -static int loop_tests = 0; - -struct sw_load { - uint8_t file_id[255]; - uint8_t file_id_len; - - uint8_t file_version[255]; - uint8_t file_version_len; -}; - -static void *tall_ctx_config = NULL; -static struct sw_load *sw_load1 = NULL; -static struct sw_load *sw_load2 = NULL; - -/* -static uint8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 }; -static uint8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 }; -*/ - -extern int ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what); -extern struct e1inp_line_ops ipaccess_e1inp_line_ops; - -/* Actively connect to a BTS. Currently used by ipaccess-config.c */ -static int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa) -{ - struct e1inp_ts *e1i_ts = &line->ts[0]; - struct osmo_fd *bfd = &e1i_ts->driver.ipaccess.fd; - int ret, on = 1; - - bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - bfd->cb = ipaccess_fd_cb; - bfd->when = BSC_FD_READ | BSC_FD_WRITE; - bfd->data = line; - bfd->priv_nr = E1INP_SIGN_OML; - - if (bfd->fd < 0) { - LOGP(DLINP, LOGL_ERROR, "could not create TCP socket.\n"); - return -EIO; - } - - setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); - if (ret < 0) { - LOGP(DLINP, LOGL_ERROR, "could not connect socket\n"); - close(bfd->fd); - return ret; - } - - ret = osmo_fd_register(bfd); - if (ret < 0) { - close(bfd->fd); - return ret; - } - return ret; - //return e1inp_line_register(line); -} - -/* configure pseudo E1 line in ip.access style and connect to BTS */ -static int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin) -{ - struct e1inp_line *line; - struct e1inp_ts *sign_ts, *rsl_ts; - struct e1inp_sign_link *oml_link, *rsl_link; - - line = talloc_zero(tall_bsc_ctx, struct e1inp_line); - if (!line) - return -ENOMEM; - - line->driver = e1inp_driver_find("ipa"); - if (!line->driver) { - fprintf(stderr, "cannot `ipa' driver, giving up.\n"); - return -EINVAL; - } - line->ops = &ipaccess_e1inp_line_ops; - - /* create E1 timeslots for signalling and TRAU frames */ - e1inp_ts_config_sign(&line->ts[1-1], line); - e1inp_ts_config_sign(&line->ts[2-1], line); - - /* create signalling links for TS1 */ - sign_ts = &line->ts[1-1]; - rsl_ts = &line->ts[2-1]; - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, - bts->c0, 0xff, 0); - rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL, - bts->c0, 0, 0); - - /* create back-links from bts/trx */ - bts->oml_link = oml_link; - bts->c0->rsl_link = rsl_link; - - /* default port at BTS for incoming connections is 3006 */ - if (sin->sin_port == 0) - sin->sin_port = htons(3006); - - return ipaccess_connect(line, sin); -} - -/* - * Callback function for NACK on the OML NM - * - * Currently we send the config requests but don't check the - * result. The nanoBTS will send us a NACK when we did something the - * BTS didn't like. - */ -static int ipacc_msg_nack(uint8_t mt) -{ - fprintf(stderr, "Failure to set attribute. This seems fatal\n"); - exit(-1); - return 0; -} - -static void check_restart_or_exit(struct gsm_bts_trx *trx) -{ - if (restart) { - abis_nm_ipaccess_restart(trx); - } else { - exit(0); - } -} - -static int ipacc_msg_ack(uint8_t mt, struct gsm_bts_trx *trx) -{ - if (sw_load_state == 1) { - fprintf(stderr, "The new software is activaed.\n"); - check_restart_or_exit(trx); - } else if (oml_state == 1) { - fprintf(stderr, "Set the NV Attributes.\n"); - check_restart_or_exit(trx); - } - - return 0; -} - -static const uint8_t phys_conf_min[] = { 0x02 }; - -static uint16_t build_physconf(uint8_t *physconf_buf, const struct rxlev_stats *st) -{ - uint16_t *whitelist = (uint16_t *) (physconf_buf + 4); - int num_arfcn; - unsigned int arfcnlist_size; - - /* Create whitelist from rxlevels */ - physconf_buf[0] = phys_conf_min[0]; - physconf_buf[1] = NM_IPAC_EIE_ARFCN_WHITE; - num_arfcn = ipac_rxlevstat2whitelist(whitelist, st, 0, 100); - arfcnlist_size = num_arfcn * 2; - *((uint16_t *) (physconf_buf+2)) = htons(arfcnlist_size); - DEBUGP(DNM, "physconf_buf (%s)\n", osmo_hexdump(physconf_buf, arfcnlist_size+4)); - return arfcnlist_size+4; -} - -static int nwl_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_bts_trx *trx; - uint8_t physconf_buf[2*NUM_ARFCNS+16]; - uint16_t physconf_len; - - switch (signal) { - case S_IPAC_NWL_COMPLETE: - trx = signal_data; - DEBUGP(DNM, "received S_IPAC_NWL_COMPLETE signal\n"); - switch (trx->ipaccess.test_nr) { - case NM_IPACC_TESTNO_CHAN_USAGE: - /* Dump RxLev results */ - //rxlev_stat_dump(&trx->ipaccess.rxlev_stat); - /* Create whitelist from results */ - physconf_len = build_physconf(physconf_buf, - &trx->ipaccess.rxlev_stat); - /* Start next test abbout BCCH channel usage */ - ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_CHAN_USAGE, - physconf_buf, physconf_len); - break; - case NM_IPACC_TESTNO_BCCH_CHAN_USAGE: - /* Dump BCCH RxLev results */ - //rxlev_stat_dump(&trx->ipaccess.rxlev_stat); - /* Create whitelist from results */ - physconf_len = build_physconf(physconf_buf, - &trx->ipaccess.rxlev_stat); - /* Start next test about BCCH info */ - ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_INFO, - physconf_buf, physconf_len); - break; - case NM_IPACC_TESTNO_BCCH_INFO: - /* re-start full process with CHAN_USAGE */ - if (loop_tests) { - DEBUGP(DNM, "starting next test cycle\n"); - ipac_nwl_test_start(trx, net_listen_testnr, phys_conf_min, - sizeof(phys_conf_min)); - } else { - exit(0); - } - break; - } - break; - } - return 0; -} - -static int nm_state_event(int evt, uint8_t obj_class, void *obj, - struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, - struct abis_om_obj_inst *obj_inst); - -static int nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct ipacc_ack_signal_data *ipacc_data; - struct nm_statechg_signal_data *nsd; - - switch (signal) { - case S_NM_IPACC_NACK: - ipacc_data = signal_data; - return ipacc_msg_nack(ipacc_data->msg_type); - case S_NM_IPACC_ACK: - ipacc_data = signal_data; - return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->trx); - case S_NM_IPACC_RESTART_ACK: - printf("The BTS has acked the restart. Exiting.\n"); - exit(0); - break; - case S_NM_IPACC_RESTART_NACK: - printf("The BTS has nacked the restart. Exiting.\n"); - exit(0); - break; - case S_NM_STATECHG_OPER: - case S_NM_STATECHG_ADM: - nsd = signal_data; - nm_state_event(signal, nsd->obj_class, nsd->obj, nsd->old_state, - nsd->new_state, nsd->obj_inst); - break; - default: - break; - } - - return 0; -} - -/* callback function passed to the ABIS OML code */ -static int percent; -static int percent_old; -static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg, - void *data, void *param) -{ - struct msgb *msg; - struct gsm_bts_trx *trx; - - if (hook != GSM_HOOK_NM_SWLOAD) - return 0; - - trx = (struct gsm_bts_trx *) data; - - switch (event) { - case NM_MT_LOAD_INIT_ACK: - fprintf(stdout, "Software Load Initiate ACK\n"); - break; - case NM_MT_LOAD_INIT_NACK: - fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); - exit(5); - break; - case NM_MT_LOAD_END_ACK: - fprintf(stderr, "LOAD END ACK..."); - /* now make it the default */ - sw_load_state = 1; - - msg = msgb_alloc(1024, "sw: nvattr"); - msg->l2h = msgb_put(msg, 3); - msg->l3h = &msg->l2h[3]; - - /* activate software */ - if (sw_load1) { - msgb_v_put(msg, NM_ATT_SW_DESCR); - msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load1->file_id_len, sw_load1->file_id); - msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load1->file_version_len, - sw_load1->file_version); - } - - if (sw_load2) { - msgb_v_put(msg, NM_ATT_SW_DESCR); - msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load2->file_id_len, sw_load2->file_id); - msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load2->file_version_len, - sw_load2->file_version); - } - - /* fill in the data */ - msg->l2h[0] = NM_ATT_IPACC_CUR_SW_CFG; - msg->l2h[1] = msgb_l3len(msg) >> 8; - msg->l2h[2] = msgb_l3len(msg) & 0xff; - printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg)); - abis_nm_ipaccess_set_nvattr(trx, msg->l2h, msgb_l2len(msg)); - msgb_free(msg); - break; - case NM_MT_LOAD_END_NACK: - fprintf(stderr, "ERROR: Software Load End NACK\n"); - exit(3); - break; - case NM_MT_ACTIVATE_SW_NACK: - fprintf(stderr, "ERROR: Activate Software NACK\n"); - exit(4); - break; - case NM_MT_ACTIVATE_SW_ACK: - break; - case NM_MT_LOAD_SEG_ACK: - percent = abis_nm_software_load_status(trx->bts); - if (percent > percent_old) - printf("Software Download Progress: %d%%\n", percent); - percent_old = percent; - break; - case NM_MT_LOAD_ABORT: - fprintf(stderr, "ERROR: Load aborted by the BTS.\n"); - exit(6); - break; - } - return 0; -} - -static void nv_put_ip_if_cfg(struct msgb *nmsg, uint32_t ip, uint32_t mask) -{ - msgb_put_u8(nmsg, NM_ATT_IPACC_IP_IF_CFG); - - msgb_put_u32(nmsg, ip); - msgb_put_u32(nmsg, mask); -} - -static void nv_put_gw_cfg(struct msgb *nmsg, uint32_t addr, uint32_t mask, uint32_t gw) -{ - msgb_put_u8(nmsg, NM_ATT_IPACC_IP_GW_CFG); - msgb_put_u32(nmsg, addr); - msgb_put_u32(nmsg, mask); - msgb_put_u32(nmsg, gw); -} - -static void nv_put_unit_id(struct msgb *nmsg, const char *unit_id) -{ - msgb_tl16v_put(nmsg, NM_ATT_IPACC_UNIT_ID, strlen(unit_id)+1, - (const uint8_t *)unit_id); -} - -static void nv_put_prim_oml(struct msgb *nmsg, uint32_t ip, uint16_t port) -{ - int len; - - /* 0x88 + IP + port */ - len = 1 + sizeof(ip) + sizeof(port); - - msgb_put_u8(nmsg, NM_ATT_IPACC_PRIM_OML_CFG_LIST); - msgb_put_u16(nmsg, len); - - msgb_put_u8(nmsg, 0x88); - - /* IP address */ - msgb_put_u32(nmsg, ip); - - /* port number */ - msgb_put_u16(nmsg, port); -} - -static void nv_put_flags(struct msgb *nmsg, uint16_t nv_flags, uint16_t nv_mask) -{ - msgb_put_u8(nmsg, NM_ATT_IPACC_NV_FLAGS); - msgb_put_u16(nmsg, sizeof(nv_flags) + sizeof(nv_mask)); - msgb_put_u8(nmsg, nv_flags & 0xff); - msgb_put_u8(nmsg, nv_mask & 0xff); - msgb_put_u8(nmsg, nv_flags >> 8); - msgb_put_u8(nmsg, nv_mask >> 8); -} - -/* human-readable test names for the ip.access tests */ -static const struct value_string ipa_test_strs[] = { - { 64, "ccch-usage" }, - { 65, "bcch-usage" }, - { 66, "freq-sync" }, - { 67, "rtp-usage" }, - { 68, "rtp-perf" }, - { 69, "gprs-ccch" }, - { 70, "pccch-usage" }, - { 71, "gprs-usage" }, - { 72, "esta-mf" }, - { 73, "uplink-mf" }, - { 74, "dolink-mf" }, - { 75, "tbf-details" }, - { 76, "tbf-usage" }, - { 77, "llc-data" }, - { 78, "pdch-usage" }, - { 79, "power-control" }, - { 80, "link-adaption" }, - { 81, "tch-usage" }, - { 82, "amr-mf" }, - { 83, "rtp-multiplex-perf" }, - { 84, "rtp-multiplex-usage" }, - { 85, "srtp-multiplex-usage" }, - { 86, "abis-traffic" }, - { 89, "gprs-multiplex-perf" }, - { 90, "gprs-multiplex-usage" }, - { 0, NULL }, -}; - -/* human-readable names for the ip.access nanoBTS NVRAM Flags */ -static const struct value_string ipa_nvflag_strs[] = { - { 0x0001, "static-ip" }, - { 0x0002, "static-gw" }, - { 0x0004, "no-dhcp-vsi" }, - { 0x0008, "dhcp-enabled" }, - { 0x0040, "led-disabled" }, - { 0x0100, "secondary-oml-enabled" }, - { 0x0200, "diag-enabled" }, - { 0x0400, "cli-enabled" }, - { 0x0800, "http-enabled" }, - { 0x1000, "post-enabled" }, - { 0x2000, "snmp-enabled" }, - { 0, NULL } -}; - -/* set the flags in flags/mask according to a string-identified flag and 'enable' */ -static int ipa_nvflag_set(uint16_t *flags, uint16_t *mask, const char *name, int en) -{ - int rc; - rc = get_string_value(ipa_nvflag_strs, name); - if (rc < 0) - return rc; - - *mask |= rc; - if (en) - *flags |= rc; - else - *flags &= ~rc; - - return 0; -} - -static void bootstrap_om(struct gsm_bts_trx *trx) -{ - struct msgb *nmsg = msgb_alloc(1024, "nested msgb"); - int need_to_set_attr = 0; - int len; - - printf("OML link established using TRX %d\n", trx->nr); - - if (unit_id) { - len = strlen(unit_id); - if (len > nmsg->data_len-10) - goto out_err; - printf("setting Unit ID to '%s'\n", unit_id); - nv_put_unit_id(nmsg, unit_id); - need_to_set_attr = 1; - } - if (prim_oml_ip) { - struct in_addr ia; - - if (!inet_aton(prim_oml_ip, &ia)) { - fprintf(stderr, "invalid IP address: %s\n", - prim_oml_ip); - goto out_err; - } - - printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia)); - nv_put_prim_oml(nmsg, ntohl(ia.s_addr), 0); - need_to_set_attr = 1; - } - if (nv_mask) { - printf("setting NV Flags/Mask to 0x%04x/0x%04x\n", - nv_flags, nv_mask); - nv_put_flags(nmsg, nv_flags, nv_mask); - need_to_set_attr = 1; - } - if (bts_ip_addr && bts_ip_mask) { - struct in_addr ia_addr, ia_mask; - - if (!inet_aton(bts_ip_addr, &ia_addr)) { - fprintf(stderr, "invalid IP address: %s\n", - bts_ip_addr); - goto out_err; - } - - if (!inet_aton(bts_ip_mask, &ia_mask)) { - fprintf(stderr, "invalid IP address: %s\n", - bts_ip_mask); - goto out_err; - } - - printf("setting static IP Address/Mask\n"); - nv_put_ip_if_cfg(nmsg, ntohl(ia_addr.s_addr), ntohl(ia_mask.s_addr)); - need_to_set_attr = 1; - } - if (bts_ip_gw) { - struct in_addr ia_gw; - - if (!inet_aton(bts_ip_gw, &ia_gw)) { - fprintf(stderr, "invalid IP address: %s\n", - bts_ip_gw); - goto out_err; - } - - printf("setting static IP Gateway\n"); - /* we only set the default gateway with zero addr/mask */ - nv_put_gw_cfg(nmsg, 0, 0, ntohl(ia_gw.s_addr)); - need_to_set_attr = 1; - } - - if (need_to_set_attr) { - abis_nm_ipaccess_set_nvattr(trx, nmsg->head, nmsg->len); - oml_state = 1; - } - - if (restart && !prim_oml_ip && !software) { - printf("restarting BTS\n"); - abis_nm_ipaccess_restart(trx); - } - -out_err: - msgb_free(nmsg); -} - -static int nm_state_event(int evt, uint8_t obj_class, void *obj, - struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, - struct abis_om_obj_inst *obj_inst) -{ - if (obj_class == NM_OC_BASEB_TRANSC) { - if (!found_trx && obj_inst->trx_nr != 0xff) { - struct gsm_bts_trx *trx = container_of(obj, struct gsm_bts_trx, bb_transc); - bootstrap_om(trx); - found_trx = 1; - } - } else if (evt == S_NM_STATECHG_OPER && - obj_class == NM_OC_RADIO_CARRIER && - new_state->availability == 3) { - struct gsm_bts_trx *trx = obj; - - if (net_listen_testnr) - ipac_nwl_test_start(trx, net_listen_testnr, - phys_conf_min, sizeof(phys_conf_min)); - else if (software) { - int rc; - printf("Attempting software upload with '%s'\n", software); - rc = abis_nm_software_load(trx->bts, trx->nr, software, 19, 0, swload_cbfn, trx); - if (rc < 0) { - fprintf(stderr, "Failed to start software load\n"); - exit(-3); - } - } - } - return 0; -} - -static struct sw_load *create_swload(struct sdp_header *header) -{ - struct sw_load *load; - - load = talloc_zero(tall_ctx_config, struct sw_load); - - strncpy((char *)load->file_id, header->firmware_info.sw_part, 20); - load->file_id_len = strlen(header->firmware_info.sw_part) + 1; - - strncpy((char *)load->file_version, header->firmware_info.version, 20); - load->file_version_len = strlen(header->firmware_info.version) + 1; - - return load; -} - -static int find_sw_load_params(const char *filename) -{ - struct stat stat; - struct sdp_header *header; - struct llist_head *entry; - int fd; - void *tall_firm_ctx = 0; - - entry = talloc_zero(tall_firm_ctx, struct llist_head); - INIT_LLIST_HEAD(entry); - - fd = open(filename, O_RDONLY); - if (!fd) { - perror("nada"); - return -1; - } - - /* verify the file */ - if (fstat(fd, &stat) == -1) { - perror("Can not stat the file"); - close(fd); - return -1; - } - - ipaccess_analyze_file(fd, stat.st_size, 0, entry); - if (close(fd) != 0) { - perror("Close failed.\n"); - return -1; - } - - /* try to find what we are looking for */ - llist_for_each_entry(header, entry, entry) { - if (ntohs(header->firmware_info.more_more_magic) == 0x1000) { - sw_load1 = create_swload(header); - } else if (ntohs(header->firmware_info.more_more_magic) == 0x2001) { - sw_load2 = create_swload(header); - } - } - - if (!sw_load1 || !sw_load2) { - fprintf(stderr, "Did not find data.\n"); - talloc_free(tall_firm_ctx); - return -1; - } - - talloc_free(tall_firm_ctx); - return 0; -} - -static void dump_entry(struct sdp_header_item *sub_entry, int part, int fd) -{ - int out_fd; - int copied; - char filename[4096]; - off_t target; - - if (!dump_files) - return; - - if (sub_entry->header_entry.something1 == 0) - return; - - snprintf(filename, sizeof(filename), "part.%d", part++); - out_fd = open(filename, O_WRONLY | O_CREAT, 0660); - if (out_fd < 0) { - perror("Can not dump firmware"); - return; - } - - target = sub_entry->absolute_offset + ntohl(sub_entry->header_entry.start) + 4; - if (lseek(fd, target, SEEK_SET) != target) { - perror("seek failed"); - close(out_fd); - return; - } - - for (copied = 0; copied < ntohl(sub_entry->header_entry.length); ++copied) { - char c; - if (read(fd, &c, sizeof(c)) != sizeof(c)) { - perror("copy failed"); - break; - } - - if (write(out_fd, &c, sizeof(c)) != sizeof(c)) { - perror("write failed"); - break; - } - } - - close(out_fd); -} - -static void analyze_firmware(const char *filename) -{ - struct stat stat; - struct sdp_header *header; - struct sdp_header_item *sub_entry; - struct llist_head *entry; - int fd; - void *tall_firm_ctx = 0; - int part = 0; - - entry = talloc_zero(tall_firm_ctx, struct llist_head); - INIT_LLIST_HEAD(entry); - - printf("Opening possible firmware '%s'\n", filename); - fd = open(filename, O_RDONLY); - if (!fd) { - perror("nada"); - return; - } - - /* verify the file */ - if (fstat(fd, &stat) == -1) { - perror("Can not stat the file"); - close(fd); - return; - } - - ipaccess_analyze_file(fd, stat.st_size, 0, entry); - - llist_for_each_entry(header, entry, entry) { - printf("Printing header information:\n"); - printf("more_more_magic: 0x%x\n", ntohs(header->firmware_info.more_more_magic)); - printf("header_length: %u\n", ntohl(header->firmware_info.header_length)); - printf("file_length: %u\n", ntohl(header->firmware_info.file_length)); - printf("sw_part: %.20s\n", header->firmware_info.sw_part); - printf("text1: %.64s\n", header->firmware_info.text1); - printf("time: %.12s\n", header->firmware_info.time); - printf("date: %.14s\n", header->firmware_info.date); - printf("text2: %.10s\n", header->firmware_info.text2); - printf("version: %.20s\n", header->firmware_info.version); - printf("subitems...\n"); - - llist_for_each_entry(sub_entry, &header->header_list, entry) { - printf("\tsomething1: %u\n", sub_entry->header_entry.something1); - printf("\ttext1: %.64s\n", sub_entry->header_entry.text1); - printf("\ttime: %.12s\n", sub_entry->header_entry.time); - printf("\tdate: %.14s\n", sub_entry->header_entry.date); - printf("\ttext2: %.10s\n", sub_entry->header_entry.text2); - printf("\tversion: %.20s\n", sub_entry->header_entry.version); - printf("\tlength: %u\n", ntohl(sub_entry->header_entry.length)); - printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1)); - printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2)); - printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start)); - printf("\tabs. offset: 0x%lx\n", sub_entry->absolute_offset); - printf("\n\n"); - - dump_entry(sub_entry, part++, fd); - } - printf("\n\n"); - } - - if (close(fd) != 0) { - perror("Close failed.\n"); - return; - } - - talloc_free(tall_firm_ctx); -} - -static void print_usage(void) -{ - printf("Usage: ipaccess-config\n"); -} - -static void print_help(void) -{ -#if 0 - printf("Commmands for reading from the BTS:\n"); - printf(" -D --dump\t\t\tDump the BTS configuration\n"); - printf("\n"); -#endif - printf("Commmands for writing to the BTS:\n"); - printf(" -u --unit-id UNIT_ID\t\tSet the Unit ID of the BTS\n"); - printf(" -o --oml-ip IP\t\tSet primary OML IP (IP of your BSC)\n"); - printf(" -i --ip-address IP/MASK\tSet static IP address + netmask of BTS\n"); - printf(" -g --ip-gateway IP\t\tSet static IP gateway of BTS\n"); - printf(" -r --restart\t\t\tRestart the BTS (after other operations)\n"); - printf(" -n --nvram-flags FLAGS/MASK\tSet NVRAM attributes\n"); - printf(" -S --nvattr-set FLAG\tSet one additional NVRAM attribute\n"); - printf(" -U --nvattr-unset FLAG\tSet one additional NVRAM attribute\n"); - printf(" -l --listen TESTNR\t\tPerform specified test number\n"); - printf(" -L --Listen TEST_NAME\t\tPerform specified test\n"); - printf(" -s --stream-id ID\t\tSet the IPA Stream Identifier for OML\n"); - printf(" -d --software FIRMWARE\tDownload firmware into BTS\n"); - printf("\n"); - printf("Miscellaneous commands:\n"); - printf(" -h --help\t\t\tthis text\n"); - printf(" -H --HELP\t\t\tPrint parameter details.\n"); - printf(" -f --firmware FIRMWARE\tProvide firmware information\n"); - printf(" -w --write-firmware\t\tThis will dump the firmware parts to the filesystem. Use with -f.\n"); - printf(" -p --loop\t\t\tLoop the tests executed with the --listen command.\n"); -} - -static void print_value_string(const struct value_string *val, int size) -{ - int i; - - for (i = 0; i < size - 1; ++i) { - char sep = val[i + 1].str == NULL ? '.' : ','; - printf("%s%c ", val[i].str, sep); - } - printf("\n"); -} - -static void print_options(void) -{ - - printf("Options for NVRAM (-S,-U):\n "); - print_value_string(&ipa_nvflag_strs[0], ARRAY_SIZE(ipa_nvflag_strs)); - - printf("Options for Tests (-L):\n "); - print_value_string(&ipa_test_strs[0], ARRAY_SIZE(ipa_test_strs)); -} - -extern void bts_model_nanobts_init(); - -int main(int argc, char **argv) -{ - struct gsm_bts *bts; - struct sockaddr_in sin; - int rc, option_index = 0, stream_id = 0xff; - - osmo_init_logging(&log_info); - log_parse_category_mask(osmo_stderr_target, "DNM,0"); - bts_model_nanobts_init(); - - printf("ipaccess-config (C) 2009-2010 by Harald Welte and others\n"); - printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); - - while (1) { - int c; - unsigned long ul; - char *slash; - static struct option long_options[] = { - { "unit-id", 1, 0, 'u' }, - { "oml-ip", 1, 0, 'o' }, - { "ip-address", 1, 0, 'i' }, - { "ip-gateway", 1, 0, 'g' }, - { "restart", 0, 0, 'r' }, - { "nvram-flags", 1, 0, 'n' }, - { "nvattr-set", 1, 0, 'S' }, - { "nvattr-unset", 1, 0, 'U' }, - { "help", 0, 0, 'h' }, - { "HELP", 0, 0, 'H' }, - { "listen", 1, 0, 'l' }, - { "Listen", 1, 0, 'L' }, - { "stream-id", 1, 0, 's' }, - { "software", 1, 0, 'd' }, - { "firmware", 1, 0, 'f' }, - { "write-firmware", 0, 0, 'w' }, - { "disable-color", 0, 0, 'c'}, - { "loop", 0, 0, 'p' }, - { 0, 0, 0, 0 }, - }; - - c = getopt_long(argc, argv, "u:o:i:g:rn:S:U:l:L:hs:d:f:wcpH", long_options, - &option_index); - - if (c == -1) - break; - - switch (c) { - case 'u': - unit_id = optarg; - break; - case 'o': - prim_oml_ip = optarg; - break; - case 'i': - slash = strchr(optarg, '/'); - if (!slash) - exit(2); - bts_ip_addr = optarg; - *slash = 0; - bts_ip_mask = slash+1; - break; - case 'g': - bts_ip_gw = optarg; - break; - case 'r': - restart = 1; - break; - case 'n': - slash = strchr(optarg, '/'); - if (!slash) - exit(2); - ul = strtoul(optarg, NULL, 16); - nv_flags = ul & 0xffff; - ul = strtoul(slash+1, NULL, 16); - nv_mask = ul & 0xffff; - break; - case 'S': - if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 1) < 0) - exit(2); - break; - case 'U': - if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 0) < 0) - exit(2); - break; - case 'l': - net_listen_testnr = atoi(optarg); - break; - case 'L': - net_listen_testnr = get_string_value(ipa_test_strs, - optarg); - if (net_listen_testnr < 0) { - fprintf(stderr, - "The test '%s' is not known. Use -H to" - " see available tests.\n", optarg); - exit(2); - } - break; - case 's': - stream_id = atoi(optarg); - break; - case 'd': - software = strdup(optarg); - if (find_sw_load_params(optarg) != 0) - exit(0); - break; - case 'f': - firmware_analysis = optarg; - break; - case 'w': - dump_files = 1; - break; - case 'c': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'p': - loop_tests = 1; - break; - case 'h': - print_usage(); - print_help(); - exit(0); - case 'H': - print_options(); - exit(0); - } - }; - - if (firmware_analysis) - analyze_firmware(firmware_analysis); - - if (optind >= argc) { - /* only warn if we have not done anything else */ - if (!firmware_analysis) - fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n"); - exit(2); - } - libosmo_abis_init(tall_ctx_config); - - bsc_gsmnet = gsm_network_init(1, 1, NULL); - if (!bsc_gsmnet) - exit(1); - - bts = gsm_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_NANOBTS, HARDCODED_TSC, - HARDCODED_BSIC); - /* ip.access supports up to 4 chained TRX */ - gsm_bts_trx_alloc(bts); - gsm_bts_trx_alloc(bts); - gsm_bts_trx_alloc(bts); - bts->oml_tei = stream_id; - - osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); - osmo_signal_register_handler(SS_IPAC_NWL, nwl_sig_cb, NULL); - - ipac_nwl_init(); - - printf("Trying to connect to ip.access BTS ...\n"); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - inet_aton(argv[optind], &sin.sin_addr); - rc = ia_config_connect(bts, &sin); - if (rc < 0) { - perror("Error connecting to the BTS"); - exit(1); - } - - bts->oml_link->ts->sign.delay = 10; - bts->c0->rsl_link->ts->sign.delay = 10; - while (1) { - rc = osmo_select_main(0); - if (rc < 0) - exit(3); - } - - exit(0); -} - diff --git a/openbsc/src/ipaccess/ipaccess-find.c b/openbsc/src/ipaccess/ipaccess-find.c deleted file mode 100644 index bb9819ed1..000000000 --- a/openbsc/src/ipaccess/ipaccess-find.c +++ /dev/null @@ -1,208 +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 - -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) { - rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, - strlen(ifname)); - 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' ", ipaccess_idtag_name(t_tag), cur); - - cur += t_len; - } - printf("\n"); - return 0; -} - -static int read_response(int fd) -{ - unsigned char buf[255]; - struct sockaddr_in sa; - int len; - socklen_t sa_len = sizeof(sa); - - len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len); - if (len < 0) - return len; - - /* 2 bytes length, 1 byte protocol (0xfe) */ - if (buf[2] != 0xfe) - return 0; - - if (buf[4] != IPAC_MSGT_ID_RESP) - return 0; - - return parse_response(buf+6, len-6); -} - -static int bfd_cb(struct osmo_fd *bfd, unsigned int flags) -{ - if (flags & BSC_FD_READ) - return read_response(bfd->fd); - if (flags & BSC_FD_WRITE) { - bfd->when &= ~BSC_FD_WRITE; - return bcast_find(bfd->fd); - } - return 0; -} - -static struct osmo_timer_list timer; - -static void timer_cb(void *_data) -{ - struct osmo_fd *bfd = _data; - - bfd->when |= BSC_FD_WRITE; - - osmo_timer_schedule(&timer, 5, 0); -} - -int main(int argc, char **argv) -{ - struct osmo_fd bfd; - char *ifname = NULL; - int rc; - - printf("ipaccess-find (C) 2009 by Harald Welte\n"); - printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); - - if (argc < 2) { - fprintf(stdout, "you might need to specify the outgoing\n" - " network interface, e.g. ``%s eth0''\n", argv[0]); - } else { - ifname = argv[1]; - } - - bfd.cb = bfd_cb; - bfd.when = BSC_FD_READ | BSC_FD_WRITE; - bfd.fd = udp_sock(ifname); - if (bfd.fd < 0) { - perror("Cannot create local socket for broadcast udp"); - exit(1); - } - - osmo_fd_register(&bfd); - - timer.cb = timer_cb; - timer.data = &bfd; - - osmo_timer_schedule(&timer, 5, 0); - - printf("Trying to find ip.access BTS by broadcast UDP...\n"); - - while (1) { - rc = osmo_select_main(0); - if (rc < 0) - exit(3); - } - - exit(0); -} - diff --git a/openbsc/src/ipaccess/ipaccess-firmware.c b/openbsc/src/ipaccess/ipaccess-firmware.c deleted file mode 100644 index 5f55bb526..000000000 --- a/openbsc/src/ipaccess/ipaccess-firmware.c +++ /dev/null @@ -1,135 +0,0 @@ -/* Routines for parsing an ipacces SDP firmware file */ - -/* (C) 2009 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have 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 9ab4ebbf4..000000000 --- a/openbsc/src/ipaccess/ipaccess-proxy.c +++ /dev/null @@ -1,1202 +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 - -/* 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); - 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); - uint16_t site_id, bts_id, trx_id; - struct ipa_bts_conn *ipbc; - int ret = 0; - - switch (msg_type) { - case IPAC_MSGT_PING: - ret = ipaccess_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 */ - ipaccess_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, ... */ - site_id = bts_id = trx_id = 0; - ipaccess_parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT), - &site_id, &bts_id, &trx_id); - ipbc = find_bts_by_unitid(ipp, site_id, 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, site_id, bts_id, - 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", - site_id, bts_id, trx_id); - - if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) { - LOGP(DLINP, LOGL_ERROR, "Second OML connection from " - "same BTS ?!?\n"); - return 0; - } - - if (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 |= trx_id << 8; - ipbc->rsl_conn[trx_id] = ipc; - - /* Create RSL TCP connection towards BSC */ - sin.sin_port = htons(IPA_TCP_PORT_RSL); - ipbc->bsc_rsl_conn[trx_id] = - connect_bsc(&sin, RSL_TO_BSC | (trx_id << 8), ipbc); - if (!ipbc->bsc_oml_conn) - return -EIO; - DEBUGP(DLINP, "(%u/%u/%u) Connected RSL to BSC\n", - site_id, bts_id, trx_id); - } - break; - case IPAC_MSGT_ID_GET: - DEBUGP(DLMI, "ID_GET\n"); - if ((bfd->priv_nr & 0xff) != OML_TO_BSC && - (bfd->priv_nr & 0xff) != RSL_TO_BSC) { - DEBUGP(DLINP, "IDentity REQuest from BTS ?!?\n"); - return -EIO; - } - ipbc = ipc->bts_conn; - if (!ipbc) { - DEBUGP(DLINP, "ID_GET from BSC before we have ID_RESP from BTS\n"); - return -EIO; - } - ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len); - break; - case IPAC_MSGT_ID_ACK: - DEBUGP(DLMI, "ID_ACK? -> ACK!\n"); - ret = ipaccess_send_id_ack(bfd->fd); - break; - default: - LOGP(DLMI, LOGL_ERROR, "Unhandled IPA type; %d\n", msg_type); - return 1; - break; - } - return 0; -} - -struct msgb *ipaccess_proxy_read_msg(struct osmo_fd *bfd, int *error) -{ - struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP"); - struct ipaccess_head *hh; - int len, ret = 0; - - if (!msg) { - *error = -ENOMEM; - return NULL; - } - - /* first read our 3-byte header */ - hh = (struct ipaccess_head *) msg->data; - ret = recv(bfd->fd, msg->data, 3, 0); - if (ret < 0) { - if (errno != EAGAIN) - LOGP(DLINP, LOGL_ERROR, "recv error: %s\n", strerror(errno)); - msgb_free(msg); - *error = ret; - return NULL; - } else if (ret == 0) { - msgb_free(msg); - *error = ret; - return NULL; - } - - msgb_put(msg, ret); - - /* then read te length as specified in header */ - msg->l2h = msg->data + sizeof(*hh); - len = ntohs(hh->len); - ret = recv(bfd->fd, msg->l2h, len, 0); - if (ret < len) { - LOGP(DLINP, LOGL_ERROR, "short read!\n"); - msgb_free(msg); - *error = -EIO; - return NULL; - } - msgb_put(msg, ret); - - return msg; -} - -static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc, - unsigned int priv_nr) -{ - struct ipa_proxy_conn *bsc_conn; - unsigned int trx_id = priv_nr >> 8; - - switch (priv_nr & 0xff) { - case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ - bsc_conn = ipbc->bsc_oml_conn; - break; - case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ - bsc_conn = ipbc->bsc_rsl_conn[trx_id]; - break; - case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ - bsc_conn = ipbc->oml_conn; - break; - case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ - bsc_conn = ipbc->rsl_conn[trx_id]; - break; - default: - bsc_conn = NULL; - break; - } - return bsc_conn; -} - -static void reconn_tmr_cb(void *data) -{ - struct ipa_proxy *ipp = data; - struct ipa_bts_conn *ipbc; - struct sockaddr_in sin; - int i; - - DEBUGP(DLINP, "Running reconnect timer\n"); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - inet_aton(bsc_ipaddr, &sin.sin_addr); - - llist_for_each_entry(ipbc, &ipp->bts_list, list) { - /* if OML to BSC is dead, try to restore it */ - if (ipbc->oml_conn && !ipbc->bsc_oml_conn) { - sin.sin_port = htons(IPA_TCP_PORT_OML); - logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0); - LOGPC(DLINP, LOGL_NOTICE, "OML Trying to reconnect\n"); - ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); - if (!ipbc->bsc_oml_conn) - goto reschedule; - logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0); - LOGPC(DLINP, LOGL_NOTICE, "OML Reconnected\n"); - } - /* if we (still) don't have a OML connection, skip RSL */ - if (!ipbc->oml_conn || !ipbc->bsc_oml_conn) - continue; - - for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) { - unsigned int priv_nr; - /* don't establish RSL links which we don't have */ - if (!ipbc->rsl_conn[i]) - continue; - if (ipbc->bsc_rsl_conn[i]) - continue; - priv_nr = ipbc->rsl_conn[i]->fd.priv_nr; - priv_nr &= ~0xff; - priv_nr |= RSL_TO_BSC; - sin.sin_port = htons(IPA_TCP_PORT_RSL); - logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8); - LOGPC(DLINP, LOGL_NOTICE, "RSL Trying to reconnect\n"); - ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc); - if (!ipbc->bsc_rsl_conn[i]) - goto reschedule; - logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8); - LOGPC(DLINP, LOGL_NOTICE, "RSL Reconnected\n"); - } - } - return; - -reschedule: - osmo_timer_schedule(&ipp->reconn_timer, 5, 0); -} - -static void handle_dead_socket(struct osmo_fd *bfd) -{ - struct ipa_proxy_conn *ipc = bfd->data; /* local conn */ - struct ipa_proxy_conn *bsc_conn; /* remote conn */ - struct ipa_bts_conn *ipbc = ipc->bts_conn; - unsigned int trx_id = bfd->priv_nr >> 8; - struct msgb *msg, *msg2; - - osmo_fd_unregister(bfd); - close(bfd->fd); - bfd->fd = -1; - - /* FIXME: clear tx_queue, remove all references, etc. */ - llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list) - msgb_free(msg); - - switch (bfd->priv_nr & 0xff) { - case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ - /* The BTS started a connection with us but we got no - * IPAC_MSGT_ID_RESP message yet, in that scenario we did not - * allocate the ipa_bts_conn structure. */ - if (ipbc == NULL) - break; - ipbc->oml_conn = NULL; - bsc_conn = ipbc->bsc_oml_conn; - /* close the connection to the BSC */ - osmo_fd_unregister(&bsc_conn->fd); - close(bsc_conn->fd.fd); - llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) - msgb_free(msg); - talloc_free(bsc_conn); - ipbc->bsc_oml_conn = NULL; - /* FIXME: do we need to delete the entire ipbc ? */ - break; - case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ - ipbc->rsl_conn[trx_id] = NULL; - bsc_conn = ipbc->bsc_rsl_conn[trx_id]; - /* close the connection to the BSC */ - osmo_fd_unregister(&bsc_conn->fd); - close(bsc_conn->fd.fd); - llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) - msgb_free(msg); - talloc_free(bsc_conn); - ipbc->bsc_rsl_conn[trx_id] = NULL; - break; - case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ - ipbc->bsc_oml_conn = NULL; - bsc_conn = ipbc->oml_conn; - /* start reconnect timer */ - osmo_timer_schedule(&ipp->reconn_timer, 5, 0); - break; - case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ - ipbc->bsc_rsl_conn[trx_id] = NULL; - bsc_conn = ipbc->rsl_conn[trx_id]; - /* start reconnect timer */ - osmo_timer_schedule(&ipp->reconn_timer, 5, 0); - break; - default: - bsc_conn = NULL; - break; - } - - talloc_free(ipc); -} - -static void patch_gprs_msg(struct ipa_bts_conn *ipbc, int priv_nr, struct msgb *msg) -{ - uint8_t *nsvci; - - if ((priv_nr & 0xff) != OML_FROM_BTS && (priv_nr & 0xff) != OML_TO_BSC) - return; - - if (msgb_l2len(msg) != 39) - return; - - /* - * Check if this is a IPA Set Attribute or IPA Set Attribute ACK - * and if the FOM Class is GPRS NSVC0 and then we will patch it. - * - * The patch assumes the message looks like the one from the trace - * but we only match messages with a specific size anyway... So - * this hack should work just fine. - */ - - if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 && - msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 && - msg->l2h[18] == 0xf5 && msg->l2h[19] == 0xf2) { - nsvci = &msg->l2h[23]; - ipbc->gprs_orig_port = *(uint16_t *)(nsvci+8); - ipbc->gprs_orig_ip = *(uint32_t *)(nsvci+10); - *(uint16_t *)(nsvci+8) = htons(ipbc->gprs_local_port); - *(uint32_t *)(nsvci+10) = ipbc->ipp->listen_addr.s_addr; - } else if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 && - msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 && - msg->l2h[18] == 0xf6 && msg->l2h[19] == 0xf2) { - nsvci = &msg->l2h[23]; - *(uint16_t *)(nsvci+8) = ipbc->gprs_orig_port; - *(uint32_t *)(nsvci+10) = ipbc->gprs_orig_ip; - } -} - -static int handle_tcp_read(struct osmo_fd *bfd) -{ - struct ipa_proxy_conn *ipc = bfd->data; - struct ipa_bts_conn *ipbc = ipc->bts_conn; - struct ipa_proxy_conn *bsc_conn; - struct msgb *msg; - struct ipaccess_head *hh; - int ret = 0; - char *btsbsc; - - if ((bfd->priv_nr & 0xff) <= 2) - btsbsc = "BTS"; - else - btsbsc = "BSC"; - - msg = ipaccess_proxy_read_msg(bfd, &ret); - if (!msg) { - if (ret == 0) { - logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); - LOGPC(DLINP, LOGL_NOTICE, "%s disappeared, " - "dead socket\n", btsbsc); - handle_dead_socket(bfd); - } - return ret; - } - - msgb_put(msg, ret); - logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); - DEBUGPC(DLMI, "RX<-%s: %s\n", btsbsc, osmo_hexdump(msg->data, msg->len)); - - hh = (struct ipaccess_head *) msg->data; - if (hh->proto == IPAC_PROTO_IPACCESS) { - ret = ipaccess_rcvmsg(ipc, msg, bfd); - if (ret < 0) { - osmo_fd_unregister(bfd); - close(bfd->fd); - bfd->fd = -1; - talloc_free(bfd); - msgb_free(msg); - return ret; - } else if (ret == 0) { - /* we do not forward parts of the CCM protocol - * through the proxy but rather terminate it ourselves. */ - msgb_free(msg); - return ret; - } - } - - if (!ipbc) { - LOGP(DLINP, LOGL_ERROR, - "received %s packet but no ipc->bts_conn?!?\n", btsbsc); - msgb_free(msg); - return -EIO; - } - - bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr); - if (bsc_conn) { - if (gprs_ns_ipaddr) - patch_gprs_msg(ipbc, bfd->priv_nr, msg); - /* enqueue packet towards BSC */ - msgb_enqueue(&bsc_conn->tx_queue, msg); - /* mark respective filedescriptor as 'we want to write' */ - bsc_conn->fd.when |= BSC_FD_WRITE; - } else { - logp_ipbc_uid(DLINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8); - LOGPC(DLINP, LOGL_INFO, "Dropping packet from %s, " - "since remote connection is dead\n", btsbsc); - msgb_free(msg); - } - - return ret; -} - -/* a TCP socket is ready to be written to */ -static int handle_tcp_write(struct osmo_fd *bfd) -{ - struct ipa_proxy_conn *ipc = bfd->data; - struct ipa_bts_conn *ipbc = ipc->bts_conn; - struct llist_head *lh; - struct msgb *msg; - char *btsbsc; - int ret; - - if ((bfd->priv_nr & 0xff) <= 2) - btsbsc = "BTS"; - else - btsbsc = "BSC"; - - - /* get the next msg for this timeslot */ - if (llist_empty(&ipc->tx_queue)) { - bfd->when &= ~BSC_FD_WRITE; - return 0; - } - lh = ipc->tx_queue.next; - llist_del(lh); - msg = llist_entry(lh, struct msgb, list); - - logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); - DEBUGPC(DLMI, "TX %04x: %s\n", bfd->priv_nr, - osmo_hexdump(msg->data, msg->len)); - - ret = send(bfd->fd, msg->data, msg->len, 0); - msgb_free(msg); - - if (ret == 0) { - logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); - LOGP(DLINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc); - handle_dead_socket(bfd); - } - - return ret; -} - -/* callback from select.c in case one of the fd's can be read/written */ -static int 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 = 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 = ipaccess_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; - - setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); - if (ret < 0) { - LOGP(DLINP, LOGL_ERROR, "Could not connect socket: %s\n", - inet_ntoa(sa->sin_addr)); - close(bfd->fd); - talloc_free(ipc); - return NULL; - } - - /* pre-fill tx_queue with identity request */ - ret = osmo_fd_register(bfd); - if (ret < 0) { - close(bfd->fd); - talloc_free(ipc); - return NULL; - } - - return ipc; -} - -static int ipaccess_proxy_setup(void) -{ - int ret; - - ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy); - if (!ipp) - return -ENOMEM; - INIT_LLIST_HEAD(&ipp->bts_list); - ipp->reconn_timer.cb = reconn_tmr_cb; - ipp->reconn_timer.data = ipp; - - /* Listen for OML connections */ - ret = make_sock(&ipp->oml_listen_fd, IPPROTO_TCP, INADDR_ANY, - IPA_TCP_PORT_OML, OML_FROM_BTS, listen_fd_cb, NULL); - if (ret < 0) - return ret; - - /* Listen for RSL connections */ - ret = make_sock(&ipp->rsl_listen_fd, IPPROTO_TCP, INADDR_ANY, - IPA_TCP_PORT_RSL, RSL_FROM_BTS, listen_fd_cb, NULL); - - if (ret < 0) - return ret; - - /* Connect the GPRS NS Socket */ - if (gprs_ns_ipaddr) { - inet_aton(gprs_ns_ipaddr, &ipp->gprs_addr); - inet_aton(listen_ipaddr, &ipp->listen_addr); - } - - return ret; -} - -static void signal_handler(int signal) -{ - fprintf(stdout, "signal %u received\n", signal); - - switch (signal) { - case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report_full(tall_bsc_ctx, stderr); - break; - default: - break; - } -} - -static void print_help(void) -{ - printf(" ipaccess-proxy is a proxy BTS.\n"); - printf(" -h --help. This help text.\n"); - printf(" -l --listen IP. The ip to listen to.\n"); - printf(" -b --bsc IP. The BSC IP address.\n"); - printf(" -g --gprs IP. Take GPRS NS from that IP.\n"); - printf("\n"); - printf(" -s --disable-color. Disable the color inside the logging message.\n"); - printf(" -e --log-level number. Set the global loglevel.\n"); - printf(" -T --timestamp. Prefix every log message with a timestamp.\n"); - printf(" -V --version. Print the version of OpenBSC.\n"); -} - -static void print_usage(void) -{ - printf("Usage: ipaccess-proxy [options]\n"); -} - -enum { - IPA_PROXY_OPT_LISTEN_NONE = 0, - IPA_PROXY_OPT_LISTEN_IP = (1 << 0), - IPA_PROXY_OPT_BSC_IP = (1 << 1), -}; - -static void handle_options(int argc, char** argv) -{ - int options_mask = 0; - - /* disable explicit missing arguments error output from getopt_long */ - opterr = 0; - - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"disable-color", 0, 0, 's'}, - {"timestamp", 0, 0, 'T'}, - {"log-level", 1, 0, 'e'}, - {"listen", 1, 0, 'l'}, - {"bsc", 1, 0, 'b'}, - {0, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hsTe:l:b:g:", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(); - print_help(); - exit(0); - case 'l': - listen_ipaddr = optarg; - options_mask |= IPA_PROXY_OPT_LISTEN_IP; - break; - case 'b': - bsc_ipaddr = optarg; - options_mask |= IPA_PROXY_OPT_BSC_IP; - break; - case 'g': - gprs_ns_ipaddr = optarg; - break; - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - case '?': - if (optopt) { - printf("ERROR: missing mandatory argument " - "for `%s' option\n", argv[optind-1]); - } else { - printf("ERROR: unknown option `%s'\n", - argv[optind-1]); - } - print_usage(); - print_help(); - exit(EXIT_FAILURE); - break; - default: - /* ignore */ - break; - } - } - if ((options_mask & (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP)) - != (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP)) { - printf("ERROR: You have to specify `--listen' and `--bsc' " - "options at least.\n"); - print_usage(); - print_help(); - exit(EXIT_FAILURE); - } -} - -int main(int argc, char **argv) -{ - int rc; - - tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy"); - - osmo_init_logging(&log_info); - log_parse_category_mask(osmo_stderr_target, "DLINP:DLMI"); - - handle_options(argc, argv); - - rc = ipaccess_proxy_setup(); - if (rc < 0) - exit(1); - - signal(SIGUSR1, &signal_handler); - signal(SIGABRT, &signal_handler); - osmo_init_ignore_signals(); - - while (1) { - osmo_select_main(0); - } -} diff --git a/openbsc/src/ipaccess/network_listen.c b/openbsc/src/ipaccess/network_listen.c deleted file mode 100644 index 6749c4aa3..000000000 --- a/openbsc/src/ipaccess/network_listen.c +++ /dev/null @@ -1,254 +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 */ - test_rep_len = ntohs(*(uint16_t *) &foh->data[3]); - /* 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 */ - ferr_list_len = ntohs(*(uint16_t *) &foh->data[7]); - - /* 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 */ - ferr_list_len = ntohs(*(uint16_t *) &foh->data[7]); - - /* 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 42fabab6d..000000000 --- a/openbsc/src/libbsc/Makefile.am +++ /dev/null @@ -1,26 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) - -noinst_LIBRARIES = libbsc.a - -libbsc_a_SOURCES = abis_nm.c abis_nm_vty.c \ - abis_om2000.c abis_om2000_vty.c \ - abis_rsl.c bsc_rll.c \ - paging.c \ - bts_ericsson_rbs2000.c \ - bts_ipaccess_nanobts.c \ - bts_siemens_bs11.c \ - bts_nokia_site.c \ - bts_unknown.c \ - bts_sysmobts.c \ - chan_alloc.c \ - gsm_subscriber_base.c \ - handover_decision.c handover_logic.c meas_rep.c \ - rest_octets.c system_information.c \ - e1_config.c \ - bsc_api.c bsc_msc.c bsc_vty.c \ - gsm_04_08_utils.c \ - bsc_init.c bts_init.c bsc_rf_ctrl.c \ - arfcn_range_encode.c - diff --git a/openbsc/src/libbsc/abis_nm.c b/openbsc/src/libbsc/abis_nm.c deleted file mode 100644 index a2e9f0d5d..000000000 --- a/openbsc/src/libbsc/abis_nm.c +++ /dev/null @@ -1,2741 +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 - -#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 void 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; -} - -static struct msgb *nm_msgb_alloc(void) -{ - return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, - "OML"); -} - -int _abis_nm_sendmsg(struct msgb *msg) -{ - msg->l2h = msg->data; - - if (!msg->dst) { - LOGP(DNM, LOGL_ERROR, "%s: msg->dst == NULL\n", __func__); - return -EINVAL; - } - - return abis_sendmsg(msg); -} - -/* Send a OML NM Message from BSC to BTS */ -static int abis_nm_queue_msg(struct gsm_bts *bts, struct msgb *msg) -{ - msg->dst = bts->oml_link; - - /* queue OML messages */ - if (llist_empty(&bts->abis_queue) && !bts->abis_nm_pend) { - bts->abis_nm_pend = OBSC_NM_W_ACK_CB(msg); - return _abis_nm_sendmsg(msg); - } else { - msgb_enqueue(&bts->abis_queue, msg); - return 0; - } - -} - -int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg) -{ - OBSC_NM_W_ACK_CB(msg) = 1; - return abis_nm_queue_msg(bts, msg); -} - -static int abis_nm_sendmsg_direct(struct gsm_bts *bts, struct msgb *msg) -{ - OBSC_NM_W_ACK_CB(msg) = 0; - return abis_nm_queue_msg(bts, msg); -} - -static int abis_nm_rcvmsg_sw(struct msgb *mb); - -int nm_is_running(struct gsm_nm_state *s) { - return (s->operational == NM_OPSTATE_ENABLED) && ( - (s->availability == NM_AVSTATE_OK) || - (s->availability == 0xff) - ); -} - -/* Update the administrative state of a given object in our in-memory data - * structures and send an event to the higher layer */ -static int update_admstate(struct gsm_bts *bts, uint8_t obj_class, - struct abis_om_obj_inst *obj_inst, uint8_t adm_state) -{ - struct gsm_nm_state *nm_state, new_state; - struct nm_statechg_signal_data nsd; - - memset(&nsd, 0, sizeof(nsd)); - - nsd.obj = gsm_objclass2obj(bts, obj_class, obj_inst); - if (!nsd.obj) - return -EINVAL; - nm_state = gsm_objclass2nmstate(bts, obj_class, obj_inst); - if (!nm_state) - return -1; - - new_state = *nm_state; - new_state.administrative = adm_state; - - nsd.bts = bts; - nsd.obj_class = obj_class; - nsd.old_state = nm_state; - nsd.new_state = &new_state; - nsd.obj_inst = obj_inst; - osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); - - nm_state->administrative = adm_state; - - return 0; -} - -static int abis_nm_rx_statechg_rep(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct tlv_parsed tp; - struct gsm_nm_state *nm_state, new_state; - - DEBUGPC(DNM, "STATE CHG: "); - - memset(&new_state, 0, sizeof(new_state)); - - nm_state = gsm_objclass2nmstate(bts, foh->obj_class, &foh->obj_inst); - if (!nm_state) { - DEBUGPC(DNM, "unknown object class\n"); - return -EINVAL; - } - - new_state = *nm_state; - - abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); - if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) { - new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE); - DEBUGPC(DNM, "OP_STATE=%s ", - abis_nm_opstate_name(new_state.operational)); - } - if (TLVP_PRESENT(&tp, NM_ATT_AVAIL_STATUS)) { - if (TLVP_LEN(&tp, NM_ATT_AVAIL_STATUS) == 0) - new_state.availability = 0xff; - else - new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS); - DEBUGPC(DNM, "AVAIL=%s(%02x) ", - abis_nm_avail_name(new_state.availability), - new_state.availability); - } else - new_state.availability = 0xff; - if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) { - new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); - DEBUGPC(DNM, "ADM=%2s ", - get_value_string(abis_nm_adm_state_names, - new_state.administrative)); - } - DEBUGPC(DNM, "\n"); - - if ((new_state.administrative != 0 && nm_state->administrative == 0) || - new_state.operational != nm_state->operational || - new_state.availability != nm_state->availability) { - /* Update the operational state of a given object in our in-memory data - * structures and send an event to the higher layer */ - struct nm_statechg_signal_data nsd; - nsd.obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst); - nsd.obj_class = foh->obj_class; - nsd.old_state = nm_state; - nsd.new_state = &new_state; - nsd.obj_inst = &foh->obj_inst; - nsd.bts = bts; - osmo_signal_dispatch(SS_NM, S_NM_STATECHG_OPER, &nsd); - nm_state->operational = new_state.operational; - nm_state->availability = new_state.availability; - if (nm_state->administrative == 0) - nm_state->administrative = new_state.administrative; - } -#if 0 - if (op_state == 1) { - /* try to enable objects that are disabled */ - abis_nm_opstart(bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr); - } -#endif - return 0; -} - -static int rx_fail_evt_rep(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct tlv_parsed tp; - const uint8_t *p_val; - char *p_text; - - LOGPC(DNM, LOGL_ERROR, "Failure Event Report "); - - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - - if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE)) - LOGPC(DNM, LOGL_ERROR, "Type=%s ", - abis_nm_event_type_name(*TLVP_VAL(&tp, NM_ATT_EVENT_TYPE))); - if (TLVP_PRESENT(&tp, NM_ATT_SEVERITY)) - LOGPC(DNM, LOGL_ERROR, "Severity=%s ", - abis_nm_severity_name(*TLVP_VAL(&tp, NM_ATT_SEVERITY))); - if (TLVP_PRESENT(&tp, NM_ATT_PROB_CAUSE)) { - p_val = TLVP_VAL(&tp, NM_ATT_PROB_CAUSE); - LOGPC(DNM, LOGL_ERROR, "Probable cause= %02X %02X %02X ", p_val[0], p_val[1], p_val[2]); - } - if (TLVP_PRESENT(&tp, NM_ATT_ADD_TEXT)) { - p_val = TLVP_VAL(&tp, NM_ATT_ADD_TEXT); - p_text = talloc_strndup(tall_bsc_ctx, (const char *) p_val, TLVP_LEN(&tp, NM_ATT_ADD_TEXT)); - if (p_text) { - LOGPC(DNM, LOGL_ERROR, "Additional Text=%s ", p_text); - talloc_free(p_text); - } - } - - LOGPC(DNM, LOGL_ERROR, "\n"); - - return 0; -} - -static int abis_nm_rcvmsg_report(struct msgb *mb) -{ - struct abis_om_fom_hdr *foh = msgb_l3(mb); - uint8_t mt = foh->msg_type; - - abis_nm_debugp_foh(DNM, foh); - - //nmh->cfg->report_cb(mb, foh); - - switch (mt) { - case NM_MT_STATECHG_EVENT_REP: - return abis_nm_rx_statechg_rep(mb); - break; - case NM_MT_SW_ACTIVATED_REP: - DEBUGPC(DNM, "Software Activated Report\n"); - osmo_signal_dispatch(SS_NM, S_NM_SW_ACTIV_REP, mb); - break; - case NM_MT_FAILURE_EVENT_REP: - rx_fail_evt_rep(mb); - osmo_signal_dispatch(SS_NM, S_NM_FAIL_REP, mb); - break; - case NM_MT_TEST_REP: - DEBUGPC(DNM, "Test Report\n"); - osmo_signal_dispatch(SS_NM, S_NM_TEST_REP, mb); - break; - default: - DEBUGPC(DNM, "reporting NM MT 0x%02x\n", mt); - break; - - }; - - return 0; -} - -/* Activate the specified software into the BTS */ -static int ipacc_sw_activate(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, - uint8_t i2, const uint8_t *sw_desc, uint8_t swdesc_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = swdesc_len; - uint8_t *trailer; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2); - - trailer = msgb_put(msg, swdesc_len); - memcpy(trailer, sw_desc, swdesc_len); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_parse_sw_config(const uint8_t *sw_descr, const size_t sw_descr_len, - struct abis_nm_sw_descr *desc, const int res_len) -{ - static const struct tlv_definition sw_descr_def = { - .def = { - [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V, }, - [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V, }, - }, - }; - - size_t pos = 0; - int desc_pos = 0; - - for (pos = 0; pos < sw_descr_len && desc_pos < res_len; ++desc_pos) { - uint8_t tag; - uint16_t tag_len; - const uint8_t *val; - int len; - - memset(&desc[desc_pos], 0, sizeof(desc[desc_pos])); - desc[desc_pos].start = &sw_descr[pos]; - - /* Classic TLV parsing doesn't work well with SW_DESCR because of it's - * nested nature and the fact you have to assume it contains only two sub - * tags NM_ATT_FILE_VERSION & NM_ATT_FILE_ID to parse it */ - if (sw_descr[pos] != NM_ATT_SW_DESCR) { - LOGP(DNM, LOGL_ERROR, - "SW_DESCR attribute identifier not found!\n"); - return -1; - } - - pos += 1; - len = tlv_parse_one(&tag, &tag_len, &val, - &sw_descr_def, &sw_descr[pos], sw_descr_len - pos); - if (len < 0 || (tag != NM_ATT_FILE_ID)) { - LOGP(DNM, LOGL_ERROR, - "FILE_ID attribute identifier not found!\n"); - return -2; - } - desc[desc_pos].file_id = val; - desc[desc_pos].file_id_len = tag_len; - pos += len; - - - len = tlv_parse_one(&tag, &tag_len, &val, - &sw_descr_def, &sw_descr[pos], sw_descr_len - pos); - if (len < 0 || (tag != NM_ATT_FILE_VERSION)) { - LOGP(DNM, LOGL_ERROR, - "FILE_VERSION attribute identifier not found!\n"); - return -3; - } - desc[desc_pos].file_ver = val; - desc[desc_pos].file_ver_len = tag_len; - pos += len; - - /* final size */ - desc[desc_pos].len = &sw_descr[pos] - desc[desc_pos].start; - } - - return desc_pos; -} - -int abis_nm_select_newest_sw(const struct abis_nm_sw_descr *sw_descr, - const size_t size) -{ - int res = 0; - int i; - - for (i = 1; i < size; ++i) { - if (memcmp(sw_descr[res].file_ver, sw_descr[i].file_ver, - OSMO_MIN(sw_descr[i].file_ver_len, sw_descr[res].file_ver_len)) < 0) { - res = i; - } - } - - return res; -} - -static int abis_nm_rx_sw_act_req(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct tlv_parsed tp; - const uint8_t *sw_config; - int ret, sw_config_len, len; - struct abis_nm_sw_descr sw_descr[5]; - - abis_nm_debugp_foh(DNM, foh); - - DEBUGPC(DNM, "SW Activate Request: "); - - DEBUGP(DNM, "Software Activate Request, ACKing and Activating\n"); - - ret = abis_nm_sw_act_req_ack(sign_link->trx->bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr, 0, - foh->data, oh->length-sizeof(*foh)); - if (ret != 0) { - LOGP(DNM, LOGL_ERROR, - "Sending SW ActReq ACK failed: %d\n", ret); - return ret; - } - - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG); - sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG); - if (!TLVP_PRESENT(&tp, NM_ATT_SW_CONFIG)) { - LOGP(DNM, LOGL_ERROR, - "SW config not found! Can't continue.\n"); - return -EINVAL; - } else { - DEBUGP(DNM, "Found SW config: %s\n", osmo_hexdump(sw_config, sw_config_len)); - } - - /* Parse up to two sw descriptions from the data */ - len = abis_nm_parse_sw_config(sw_config, sw_config_len, - &sw_descr[0], ARRAY_SIZE(sw_descr)); - if (len <= 0) { - LOGP(DNM, LOGL_ERROR, "Failed to parse SW Config.\n"); - return -EINVAL; - } - - ret = abis_nm_select_newest_sw(&sw_descr[0], len); - DEBUGP(DNM, "Selected sw description %d of %d\n", ret, len); - - return ipacc_sw_activate(sign_link->trx->bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr, - sw_descr[ret].start, sw_descr[ret].len); -} - -/* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */ -static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct tlv_parsed tp; - uint8_t adm_state; - - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) - return -EINVAL; - - adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); - - return update_admstate(sign_link->trx->bts, foh->obj_class, &foh->obj_inst, adm_state); -} - -static int abis_nm_rx_lmt_event(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct tlv_parsed tp; - - DEBUGP(DNM, "LMT Event "); - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) && - TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) { - uint8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION); - DEBUGPC(DNM, "LOG%s ", onoff ? "ON" : "OFF"); - } - if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) && - TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) >= 1) { - uint8_t level = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV); - DEBUGPC(DNM, "Level=%u ", level); - } - if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_NAME) && - TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_NAME) >= 1) { - char *name = (char *) TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_NAME); - DEBUGPC(DNM, "Username=%s ", name); - } - DEBUGPC(DNM, "\n"); - /* FIXME: parse LMT LOGON TIME */ - return 0; -} - -void abis_nm_queue_send_next(struct gsm_bts *bts) -{ - int wait = 0; - struct msgb *msg; - /* the queue is empty */ - while (!llist_empty(&bts->abis_queue)) { - msg = msgb_dequeue(&bts->abis_queue); - wait = OBSC_NM_W_ACK_CB(msg); - _abis_nm_sendmsg(msg); - - if (wait) - break; - } - - bts->abis_nm_pend = wait; -} - -/* Receive a OML NM Message from BTS */ -static int abis_nm_rcvmsg_fom(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - uint8_t mt = foh->msg_type; - int ret = 0; - - /* check for unsolicited message */ - if (is_report(mt)) - return abis_nm_rcvmsg_report(mb); - - if (is_in_arr(mt, abis_nm_sw_load_msgs, ARRAY_SIZE(abis_nm_sw_load_msgs))) - return abis_nm_rcvmsg_sw(mb); - - if (is_in_arr(mt, abis_nm_nacks, ARRAY_SIZE(abis_nm_nacks))) { - struct nm_nack_signal_data nack_data; - struct tlv_parsed tp; - - abis_nm_debugp_foh(DNM, foh); - - DEBUGPC(DNM, "%s NACK ", abis_nm_nack_name(mt)); - - abis_nm_tlv_parse(&tp, sign_link->trx->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 = sign_link->trx->bts; - osmo_signal_dispatch(SS_NM, S_NM_NACK, &nack_data); - abis_nm_queue_send_next(sign_link->trx->bts); - return 0; - } -#if 0 - /* check if last message is to be acked */ - if (is_ack_nack(nmh->last_msgtype)) { - if (mt == MT_ACK(nmh->last_msgtype)) { - DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type); - /* we got our ACK, continue sending the next msg */ - } else if (mt == MT_NACK(nmh->last_msgtype)) { - /* we got a NACK, signal this to the caller */ - DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type); - /* FIXME: somehow signal this to the caller */ - } else { - /* really strange things happen */ - return -EINVAL; - } - } -#endif - - switch (mt) { - case NM_MT_CHG_ADM_STATE_ACK: - ret = abis_nm_rx_chg_adm_state_ack(mb); - break; - case NM_MT_SW_ACT_REQ: - ret = abis_nm_rx_sw_act_req(mb); - break; - case NM_MT_BS11_LMT_SESSION: - ret = abis_nm_rx_lmt_event(mb); - break; - case NM_MT_CONN_MDROP_LINK_ACK: - DEBUGP(DNM, "CONN MDROP LINK ACK\n"); - break; - case NM_MT_IPACC_RESTART_ACK: - osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_ACK, NULL); - break; - case NM_MT_IPACC_RESTART_NACK: - osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_NACK, NULL); - break; - case NM_MT_SET_BTS_ATTR_ACK: - break; - } - - abis_nm_queue_send_next(sign_link->trx->bts); - return ret; -} - -static int abis_nm_rx_ipacc(struct msgb *mb); - -static int abis_nm_rcvmsg_manuf(struct msgb *mb) -{ - int rc; - struct e1inp_sign_link *sign_link = mb->dst; - int bts_type = sign_link->trx->bts->type; - - switch (bts_type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMO_SYSMO: - rc = abis_nm_rx_ipacc(mb); - abis_nm_queue_send_next(sign_link->trx->bts); - break; - default: - LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this " - "BTS type (%u)\n", bts_type); - rc = 0; - break; - } - - return rc; -} - -/* High-Level API */ -/* Entry-point where L2 OML from BTS enters the NM code */ -int abis_nm_rcvmsg(struct msgb *msg) -{ - struct abis_om_hdr *oh = msgb_l2(msg); - int rc = 0; - - /* Various consistency checks */ - if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { - LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", - oh->placement); - if (oh->placement != ABIS_OM_PLACEMENT_FIRST) { - rc = -EINVAL; - goto err; - } - } - if (oh->sequence != 0) { - LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", - oh->sequence); - rc = -EINVAL; - goto err; - } -#if 0 - unsigned int l2_len = msg->tail - (uint8_t *)msgb_l2(msg); - unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr); - if (oh->length + hlen > l2_len) { - LOGP(DNM, LOGL_ERROR, "ABIS OML truncated message (%u > %u)\n", - oh->length + sizeof(*oh), l2_len); - return -EINVAL; - } - if (oh->length + hlen < l2_len) - LOGP(DNM, LOGL_ERROR, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len); -#endif - msg->l3h = (unsigned char *)oh + sizeof(*oh); - - switch (oh->mdisc) { - case ABIS_OM_MDISC_FOM: - rc = abis_nm_rcvmsg_fom(msg); - break; - case ABIS_OM_MDISC_MANUF: - rc = abis_nm_rcvmsg_manuf(msg); - break; - case ABIS_OM_MDISC_MMI: - case ABIS_OM_MDISC_TRAU: - LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n", - oh->mdisc); - break; - default: - LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n", - oh->mdisc); - rc = -EINVAL; - break; - } -err: - msgb_free(msg); - return rc; -} - -#if 0 -/* initialized all resources */ -struct abis_nm_h *abis_nm_init(struct abis_nm_cfg *cfg) -{ - struct abis_nm_h *nmh; - - nmh = malloc(sizeof(*nmh)); - if (!nmh) - return NULL; - - nmh->cfg = cfg; - - return nmh; -} - -/* free all resources */ -void abis_nm_fini(struct abis_nm_h *nmh) -{ - free(nmh); -} -#endif - -/* Here we are trying to define a high-level API that can be used by - * the actual BSC implementation. However, the architecture is currently - * still under design. Ideally the calls to this API would be synchronous, - * while the underlying stack behind the APi runs in a traditional select - * based state machine. - */ - -/* 6.2 Software Load: */ -enum sw_state { - SW_STATE_NONE, - SW_STATE_WAIT_INITACK, - SW_STATE_WAIT_SEGACK, - SW_STATE_WAIT_ENDACK, - SW_STATE_WAIT_ACTACK, - SW_STATE_ERROR, -}; - -struct abis_nm_sw { - struct gsm_bts *bts; - int trx_nr; - gsm_cbfn *cbfn; - void *cb_data; - int forced; - - /* this will become part of the SW LOAD INITIATE */ - uint8_t obj_class; - uint8_t obj_instance[3]; - - uint8_t file_id[255]; - uint8_t file_id_len; - - uint8_t file_version[255]; - uint8_t file_version_len; - - uint8_t window_size; - uint8_t seg_in_window; - - int fd; - FILE *stream; - enum sw_state state; - int last_seg; -}; - -static struct abis_nm_sw g_sw; - -static void sw_add_file_id_and_ver(struct abis_nm_sw *sw, struct msgb *msg) -{ - if (sw->bts->type == GSM_BTS_TYPE_NANOBTS) { - msgb_v_put(msg, NM_ATT_SW_DESCR); - msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - } else if (sw->bts->type == GSM_BTS_TYPE_BS11) { - msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - } else { - LOGP(DNM, LOGL_ERROR, "Please implement this for the BTS.\n"); - } -} - -/* 6.2.1 / 8.3.1: Load Data Initiate */ -static int sw_load_init(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = 3*2 + sw->file_id_len + sw->file_version_len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - sw_add_file_id_and_ver(sw, msg); - msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size); - - return abis_nm_sendmsg(sw->bts, msg); -} - -static int is_last_line(FILE *stream) -{ - char next_seg_buf[256]; - long pos; - - /* check if we're sending the last line */ - pos = ftell(stream); - if (!fgets(next_seg_buf, sizeof(next_seg_buf)-2, stream)) { - fseek(stream, pos, SEEK_SET); - return 1; - } - - fseek(stream, pos, SEEK_SET); - return 0; -} - -/* 6.2.2 / 8.3.2 Load Data Segment */ -static int sw_load_segment(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - char seg_buf[256]; - char *line_buf = seg_buf+2; - unsigned char *tlv; - int len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - - switch (sw->bts->type) { - case GSM_BTS_TYPE_BS11: - if (fgets(line_buf, sizeof(seg_buf)-2, sw->stream) == NULL) { - perror("fgets reading segment"); - return -EINVAL; - } - seg_buf[0] = 0x00; - - /* check if we're sending the last line */ - sw->last_seg = is_last_line(sw->stream); - if (sw->last_seg) - seg_buf[1] = 0; - else - seg_buf[1] = 1 + sw->seg_in_window++; - - len = strlen(line_buf) + 2; - tlv = msgb_put(msg, TLV_GROSS_LEN(len)); - tlv_put(tlv, NM_ATT_BS11_FILE_DATA, len, (uint8_t *)seg_buf); - /* BS11 wants CR + LF in excess of the TLV length !?! */ - tlv[1] -= 2; - - /* we only now know the exact length for the OM hdr */ - len = strlen(line_buf)+2; - break; - case GSM_BTS_TYPE_NANOBTS: { - osmo_static_assert(sizeof(seg_buf) >= IPACC_SEGMENT_SIZE, buffer_big_enough); - len = read(sw->fd, &seg_buf, IPACC_SEGMENT_SIZE); - if (len < 0) { - perror("read failed"); - return -EINVAL; - } - - if (len != IPACC_SEGMENT_SIZE) - sw->last_seg = 1; - - ++sw->seg_in_window; - msgb_tl16v_put(msg, NM_ATT_IPACC_FILE_DATA, len, (const uint8_t *) seg_buf); - len += 3; - break; - } - default: - LOGP(DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n"); - /* FIXME: Other BTS types */ - return -1; - } - - fill_om_fom_hdr(oh, len, NM_MT_LOAD_SEG, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - return abis_nm_sendmsg_direct(sw->bts, msg); -} - -/* 6.2.4 / 8.3.4 Load Data End */ -static int sw_load_end(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_LOAD_END, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - sw_add_file_id_and_ver(sw, msg); - return abis_nm_sendmsg(sw->bts, msg); -} - -/* Activate the specified software into the BTS */ -static int sw_activate(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - /* FIXME: this is BS11 specific format */ - msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - - return abis_nm_sendmsg(sw->bts, msg); -} - -struct sdp_firmware { - char magic[4]; - char more_magic[4]; - unsigned int header_length; - unsigned int file_length; -} __attribute__ ((packed)); - -static int parse_sdp_header(struct abis_nm_sw *sw) -{ - struct sdp_firmware firmware_header; - int rc; - struct stat stat; - - rc = read(sw->fd, &firmware_header, sizeof(firmware_header)); - if (rc != sizeof(firmware_header)) { - LOGP(DNM, LOGL_ERROR, "Could not read SDP file header.\n"); - return -1; - } - - if (strncmp(firmware_header.magic, " SDP", 4) != 0) { - LOGP(DNM, LOGL_ERROR, "The magic number1 is wrong.\n"); - return -1; - } - - if (firmware_header.more_magic[0] != 0x10 || - firmware_header.more_magic[1] != 0x02 || - firmware_header.more_magic[2] != 0x00 || - firmware_header.more_magic[3] != 0x00) { - LOGP(DNM, LOGL_ERROR, "The more magic number is wrong.\n"); - return -1; - } - - - if (fstat(sw->fd, &stat) == -1) { - LOGP(DNM, LOGL_ERROR, "Could not stat the file.\n"); - return -1; - } - - if (ntohl(firmware_header.file_length) != stat.st_size) { - LOGP(DNM, LOGL_ERROR, "The filesizes do not match.\n"); - return -1; - } - - /* go back to the start as we checked the whole filesize.. */ - lseek(sw->fd, 0l, SEEK_SET); - LOGP(DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood.\n" - "There might be checksums in the file that are not\n" - "verified and incomplete firmware might be flashed.\n" - "There is absolutely no WARRANTY that flashing will\n" - "work.\n"); - return 0; -} - -static int sw_open_file(struct abis_nm_sw *sw, const char *fname) -{ - char file_id[12+1]; - char file_version[80+1]; - int rc; - - sw->fd = open(fname, O_RDONLY); - if (sw->fd < 0) - return sw->fd; - - switch (sw->bts->type) { - case GSM_BTS_TYPE_BS11: - sw->stream = fdopen(sw->fd, "r"); - if (!sw->stream) { - perror("fdopen"); - return -1; - } - /* read first line and parse file ID and VERSION */ - rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n", - file_id, file_version); - if (rc != 2) { - perror("parsing header line of software file"); - return -1; - } - strcpy((char *)sw->file_id, file_id); - sw->file_id_len = strlen(file_id); - strcpy((char *)sw->file_version, file_version); - sw->file_version_len = strlen(file_version); - /* rewind to start of file */ - rewind(sw->stream); - break; - case GSM_BTS_TYPE_NANOBTS: - /* TODO: extract that from the filename or content */ - rc = parse_sdp_header(sw); - if (rc < 0) { - fprintf(stderr, "Could not parse the ipaccess SDP header\n"); - return -1; - } - - strcpy((char *)sw->file_id, "id"); - sw->file_id_len = 3; - strcpy((char *)sw->file_version, "version"); - sw->file_version_len = 8; - break; - default: - /* We don't know how to treat them yet */ - close(sw->fd); - return -EINVAL; - } - - return 0; -} - -static void sw_close_file(struct abis_nm_sw *sw) -{ - switch (sw->bts->type) { - case GSM_BTS_TYPE_BS11: - fclose(sw->stream); - break; - default: - close(sw->fd); - break; - } -} - -/* Fill the window */ -static int sw_fill_window(struct abis_nm_sw *sw) -{ - int rc; - - while (sw->seg_in_window < sw->window_size) { - rc = sw_load_segment(sw); - if (rc < 0) - return rc; - if (sw->last_seg) - break; - } - return 0; -} - -/* callback function from abis_nm_rcvmsg() handler */ -static int abis_nm_rcvmsg_sw(struct msgb *mb) -{ - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - int rc = -1; - struct abis_nm_sw *sw = &g_sw; - enum sw_state old_state = sw->state; - - //DEBUGP(DNM, "state %u, NM MT 0x%02x\n", sw->state, foh->msg_type); - - switch (sw->state) { - case SW_STATE_WAIT_INITACK: - switch (foh->msg_type) { - case NM_MT_LOAD_INIT_ACK: - /* fill window with segments */ - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_INIT_ACK, mb, - sw->cb_data, NULL); - rc = sw_fill_window(sw); - sw->state = SW_STATE_WAIT_SEGACK; - abis_nm_queue_send_next(sign_link->trx->bts); - break; - case NM_MT_LOAD_INIT_NACK: - if (sw->forced) { - DEBUGP(DNM, "FORCED: Ignoring Software Load " - "Init NACK\n"); - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_INIT_ACK, mb, - sw->cb_data, NULL); - rc = sw_fill_window(sw); - sw->state = SW_STATE_WAIT_SEGACK; - } else { - DEBUGP(DNM, "Software Load Init NACK\n"); - /* FIXME: cause */ - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_INIT_NACK, mb, - sw->cb_data, NULL); - sw->state = SW_STATE_ERROR; - } - abis_nm_queue_send_next(sign_link->trx->bts); - break; - } - break; - case SW_STATE_WAIT_SEGACK: - switch (foh->msg_type) { - case NM_MT_LOAD_SEG_ACK: - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_SEG_ACK, mb, - sw->cb_data, NULL); - sw->seg_in_window = 0; - if (!sw->last_seg) { - /* fill window with more segments */ - rc = sw_fill_window(sw); - sw->state = SW_STATE_WAIT_SEGACK; - } else { - /* end the transfer */ - sw->state = SW_STATE_WAIT_ENDACK; - rc = sw_load_end(sw); - } - abis_nm_queue_send_next(sign_link->trx->bts); - break; - case NM_MT_LOAD_ABORT: - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_ABORT, mb, - sw->cb_data, NULL); - break; - } - break; - case SW_STATE_WAIT_ENDACK: - switch (foh->msg_type) { - case NM_MT_LOAD_END_ACK: - sw_close_file(sw); - DEBUGP(DNM, "Software Load End (BTS %u)\n", - sw->bts->nr); - sw->state = SW_STATE_NONE; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_END_ACK, mb, - sw->cb_data, NULL); - rc = 0; - abis_nm_queue_send_next(sign_link->trx->bts); - break; - case NM_MT_LOAD_END_NACK: - if (sw->forced) { - DEBUGP(DNM, "FORCED: Ignoring Software Load" - "End NACK\n"); - sw->state = SW_STATE_NONE; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_END_ACK, mb, - sw->cb_data, NULL); - } else { - DEBUGP(DNM, "Software Load End NACK\n"); - /* FIXME: cause */ - sw->state = SW_STATE_ERROR; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_END_NACK, mb, - sw->cb_data, NULL); - } - abis_nm_queue_send_next(sign_link->trx->bts); - break; - } - case SW_STATE_WAIT_ACTACK: - switch (foh->msg_type) { - case NM_MT_ACTIVATE_SW_ACK: - /* we're done */ - DEBUGP(DNM, "Activate Software DONE!\n"); - sw->state = SW_STATE_NONE; - rc = 0; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_ACTIVATE_SW_ACK, mb, - sw->cb_data, NULL); - abis_nm_queue_send_next(sign_link->trx->bts); - break; - case NM_MT_ACTIVATE_SW_NACK: - DEBUGP(DNM, "Activate Software NACK\n"); - /* FIXME: cause */ - sw->state = SW_STATE_ERROR; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_ACTIVATE_SW_NACK, mb, - sw->cb_data, NULL); - abis_nm_queue_send_next(sign_link->trx->bts); - break; - } - case SW_STATE_NONE: - switch (foh->msg_type) { - case NM_MT_ACTIVATE_SW_ACK: - rc = 0; - break; - } - break; - case SW_STATE_ERROR: - break; - } - - if (rc) - DEBUGP(DNM, "unexpected NM MT 0x%02x in state %u -> %u\n", - foh->msg_type, old_state, sw->state); - - return rc; -} - -/* Load the specified software into the BTS */ -int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, - uint8_t win_size, int forced, - gsm_cbfn *cbfn, void *cb_data) -{ - struct abis_nm_sw *sw = &g_sw; - int rc; - - DEBUGP(DNM, "Software Load (BTS %u, File \"%s\")\n", - bts->nr, fname); - - if (sw->state != SW_STATE_NONE) - return -EBUSY; - - sw->bts = bts; - sw->trx_nr = trx_nr; - - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - sw->obj_class = NM_OC_SITE_MANAGER; - sw->obj_instance[0] = 0xff; - sw->obj_instance[1] = 0xff; - sw->obj_instance[2] = 0xff; - break; - case GSM_BTS_TYPE_NANOBTS: - sw->obj_class = NM_OC_BASEB_TRANSC; - sw->obj_instance[0] = sw->bts->nr; - sw->obj_instance[1] = sw->trx_nr; - sw->obj_instance[2] = 0xff; - break; - case GSM_BTS_TYPE_UNKNOWN: - default: - LOGPC(DNM, LOGL_ERROR, "Software Load not properly implemented.\n"); - return -1; - break; - } - sw->window_size = win_size; - sw->state = SW_STATE_WAIT_INITACK; - sw->cbfn = cbfn; - sw->cb_data = cb_data; - sw->forced = forced; - - rc = sw_open_file(sw, fname); - if (rc < 0) { - sw->state = SW_STATE_NONE; - return rc; - } - - return sw_load_init(sw); -} - -int abis_nm_software_load_status(struct gsm_bts *bts) -{ - struct abis_nm_sw *sw = &g_sw; - struct stat st; - int rc, percent; - - rc = fstat(sw->fd, &st); - if (rc < 0) { - perror("ERROR during stat"); - return rc; - } - - if (sw->stream) - percent = (ftell(sw->stream) * 100) / st.st_size; - else - percent = (lseek(sw->fd, 0, SEEK_CUR) * 100) / st.st_size; - return percent; -} - -/* Activate the specified software into the BTS */ -int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, - gsm_cbfn *cbfn, void *cb_data) -{ - struct abis_nm_sw *sw = &g_sw; - int rc; - - DEBUGP(DNM, "Activating Software (BTS %u, File \"%s\")\n", - bts->nr, fname); - - if (sw->state != SW_STATE_NONE) - return -EBUSY; - - sw->bts = bts; - sw->obj_class = NM_OC_SITE_MANAGER; - sw->obj_instance[0] = 0xff; - sw->obj_instance[1] = 0xff; - sw->obj_instance[2] = 0xff; - sw->state = SW_STATE_WAIT_ACTACK; - sw->cbfn = cbfn; - sw->cb_data = cb_data; - - /* Open the file in order to fill some sw struct members */ - rc = sw_open_file(sw, fname); - if (rc < 0) { - sw->state = SW_STATE_NONE; - return rc; - } - sw_close_file(sw); - - return sw_activate(sw); -} - -static void fill_nm_channel(struct abis_nm_channel *ch, uint8_t bts_port, - uint8_t ts_nr, uint8_t subslot_nr) -{ - ch->attrib = NM_ATT_ABIS_CHANNEL; - ch->bts_port = bts_port; - ch->timeslot = ts_nr; - ch->subslot = subslot_nr; -} - -int abis_nm_establish_tei(struct gsm_bts *bts, uint8_t trx_nr, - uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, - uint8_t tei) -{ - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - uint8_t len = sizeof(*ch) + 2; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ESTABLISH_TEI, NM_OC_RADIO_CARRIER, - bts->bts_nr, trx_nr, 0xff); - - msgb_tv_put(msg, NM_ATT_TEI, tei); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - - return abis_nm_sendmsg(bts, msg); -} - -/* connect signalling of one (BTS,TRX) to a particular timeslot on the E1 */ -int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, - uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot) -{ - struct gsm_bts *bts = trx->bts; - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_SIGN, - NM_OC_RADIO_CARRIER, bts->bts_nr, trx->nr, 0xff); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - - return abis_nm_sendmsg(bts, msg); -} - -#if 0 -int abis_nm_disc_terr_sign(struct abis_nm_h *h, struct abis_om_obj_inst *inst, - struct abis_nm_abis_channel *chan) -{ -} -#endif - -int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, - uint8_t e1_port, uint8_t e1_timeslot, - uint8_t e1_subslot) -{ - struct gsm_bts *bts = ts->trx->bts; - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_TRAF, - NM_OC_CHANNEL, bts->bts_nr, ts->trx->nr, ts->nr); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - - DEBUGP(DNM, "CONNECT TERR TRAF Um=%s E1=(%u,%u,%u)\n", - gsm_ts_name(ts), - e1_port, e1_timeslot, e1_subslot); - - return abis_nm_sendmsg(bts, msg); -} - -#if 0 -int abis_nm_disc_terr_traf(struct abis_nm_h *h, struct abis_om_obj_inst *inst, - struct abis_nm_abis_channel *chan, - uint8_t subchan) -{ -} -#endif - -/* Chapter 8.11.1 */ -int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - uint8_t *attr, uint8_t attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - DEBUGP(DNM, "Get Attr (bts=%d)\n", bts->nr); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_GET_ATTR, obj_class, - bts_nr, trx_nr, ts_nr); - msgb_tl16v_put(msg, NM_ATT_LIST_REQ_ATTR, attr_len, attr); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.6.1 */ -int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t *cur; - - DEBUGP(DNM, "Set BTS Attr (bts=%d)\n", bts->nr); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_SET_BTS_ATTR, NM_OC_BTS, bts->bts_nr, 0xff, 0xff); - cur = msgb_put(msg, attr_len); - memcpy(cur, attr, attr_len); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.6.2 */ -int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t *cur; - - DEBUGP(DNM, "Set TRX Attr (bts=%d,trx=%d)\n", trx->bts->nr, trx->nr); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_SET_RADIO_ATTR, NM_OC_RADIO_CARRIER, - trx->bts->bts_nr, trx->nr, 0xff); - cur = msgb_put(msg, attr_len); - memcpy(cur, attr, attr_len); - - return abis_nm_sendmsg(trx->bts, msg); -} - -static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb, - const char **reason) -{ - int i; - - *reason = "Reason unknown"; - - /* As it turns out, the BS-11 has some very peculiar restrictions - * on the channel combinations it allows */ - switch (ts->trx->bts->type) { - case GSM_BTS_TYPE_BS11: - switch (chan_comb) { - case NM_CHANC_TCHHalf: - case NM_CHANC_TCHHalf2: - /* not supported */ - *reason = "TCH/H is not supported."; - return -EINVAL; - case NM_CHANC_SDCCH: - /* only one SDCCH/8 per TRX */ - for (i = 0; i < TRX_NR_TS; i++) { - if (i == ts->nr) - continue; - if (ts->trx->ts[i].nm_chan_comb == - NM_CHANC_SDCCH) { - *reason = "Only one SDCCH/8 per TRX allowed."; - return -EINVAL; - } - } - /* not allowed for TS0 of BCCH-TRX */ - if (ts->trx == ts->trx->bts->c0 && - ts->nr == 0) { - *reason = "SDCCH/8 must be on TS0."; - return -EINVAL; - } - - /* not on the same TRX that has a BCCH+SDCCH4 - * combination */ - if (ts->trx != ts->trx->bts->c0 && - (ts->trx->ts[0].nm_chan_comb == 5 || - ts->trx->ts[0].nm_chan_comb == 8)) { - *reason = "SDCCH/8 and BCCH must be on the same TRX."; - return -EINVAL; - } - break; - case NM_CHANC_mainBCCH: - case NM_CHANC_BCCHComb: - /* allowed only for TS0 of C0 */ - if (ts->trx != ts->trx->bts->c0 || ts->nr != 0) { - *reason = "Main BCCH must be on TS0."; - return -EINVAL; - } - break; - case NM_CHANC_BCCH: - /* allowed only for TS 2/4/6 of C0 */ - if (ts->trx != ts->trx->bts->c0) { - *reason = "BCCH must be on C0."; - return -EINVAL; - } - if (ts->nr != 2 && ts->nr != 4 && ts->nr != 6) { - *reason = "BCCH must be on TS 2/4/6."; - return -EINVAL; - } - break; - case 8: /* this is not like 08.58, but in fact - * FCCH+SCH+BCCH+CCCH+SDCCH/4+SACCH/C4+CBCH */ - /* FIXME: only one CBCH allowed per cell */ - break; - } - break; - case GSM_BTS_TYPE_NANOBTS: - switch (ts->nr) { - case 0: - if (ts->trx->nr == 0) { - /* only on TRX0 */ - switch (chan_comb) { - case NM_CHANC_BCCH: - case NM_CHANC_mainBCCH: - case NM_CHANC_BCCHComb: - return 0; - break; - default: - *reason = "TS0 of TRX0 must carry a BCCH."; - return -EINVAL; - } - } else { - switch (chan_comb) { - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - return 0; - default: - *reason = "TS0 must carry a TCH/F or TCH/H."; - return -EINVAL; - } - } - break; - case 1: - if (ts->trx->nr == 0) { - switch (chan_comb) { - case NM_CHANC_SDCCH_CBCH: - if (ts->trx->ts[0].nm_chan_comb == - NM_CHANC_mainBCCH) - return 0; - *reason = "TS0 must be the main BCCH for CBCH."; - return -EINVAL; - case NM_CHANC_SDCCH: - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - case NM_CHANC_IPAC_TCHFull_PDCH: - return 0; - default: - *reason = "TS1 must carry a CBCH, SDCCH or TCH."; - return -EINVAL; - } - } else { - switch (chan_comb) { - case NM_CHANC_SDCCH: - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - return 0; - default: - *reason = "TS1 must carry a SDCCH or TCH."; - return -EINVAL; - } - } - break; - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - switch (chan_comb) { - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - return 0; - case NM_CHANC_IPAC_PDCH: - case NM_CHANC_IPAC_TCHFull_PDCH: - if (ts->trx->nr == 0) - return 0; - else { - *reason = "PDCH must be on TRX0."; - return -EINVAL; - } - } - break; - } - *reason = "Unknown combination"; - return -EINVAL; - case GSM_BTS_TYPE_OSMO_SYSMO: - /* no known restrictions */ - return 0; - default: - /* unknown BTS type */ - return 0; - } - return 0; -} - -/* Chapter 8.6.3 */ -int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb) -{ - struct gsm_bts *bts = ts->trx->bts; - struct abis_om_hdr *oh; - uint8_t zero = 0x00; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = 2 + 2; - const char *reason = NULL; - - if (bts->type == GSM_BTS_TYPE_BS11) - len += 4 + 2 + 2 + 3; - - DEBUGP(DNM, "Set Chan Attr %s\n", gsm_ts_name(ts)); - if (verify_chan_comb(ts, chan_comb, &reason) < 0) { - msgb_free(msg); - LOGP(DNM, LOGL_ERROR, - "Invalid Channel Combination %d on %s. Reason: %s\n", - chan_comb, gsm_ts_name(ts), reason); - return -EINVAL; - } - ts->nm_chan_comb = chan_comb; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_SET_CHAN_ATTR, - NM_OC_CHANNEL, bts->bts_nr, - ts->trx->nr, ts->nr); - msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb); - if (ts->hopping.enabled) { - unsigned int i; - uint8_t *len; - - msgb_tv_put(msg, NM_ATT_HSN, ts->hopping.hsn); - msgb_tv_put(msg, NM_ATT_MAIO, ts->hopping.maio); - - /* build the ARFCN list */ - msgb_put_u8(msg, NM_ATT_ARFCN_LIST); - len = msgb_put(msg, 1); - *len = 0; - for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { - if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) { - msgb_put_u16(msg, i); - /* At least BS-11 wants a TLV16 here */ - if (bts->type == GSM_BTS_TYPE_BS11) - *len += 1; - else - *len += sizeof(uint16_t); - } - } - } - if (ts->tsc == -1) - msgb_tv_put(msg, NM_ATT_TSC, bts->tsc); /* training sequence */ - else - msgb_tv_put(msg, NM_ATT_TSC, ts->tsc); /* training sequence */ - if (bts->type == GSM_BTS_TYPE_BS11) - msgb_tlv_put(msg, 0x59, 1, &zero); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_sw_act_req_ack(struct gsm_bts *bts, 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 = (struct abis_om_fom_hdr *) oh->data; - fill_om_fom_hdr(oh, 0, NM_MT_OPSTART, obj_class, i0, i1, i2); - - abis_nm_debugp_foh(DNM, foh); - DEBUGPC(DNM, "Sending OPSTART\n"); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.8.5 */ -int abis_nm_chg_adm_state(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, - uint8_t i1, uint8_t i2, enum abis_nm_adm_state adm_state) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2, NM_MT_CHG_ADM_STATE, obj_class, i0, i1, i2); - msgb_tv_put(msg, NM_ATT_ADM_STATE, adm_state); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_conn_mdrop_link(struct gsm_bts *bts, uint8_t e1_port0, uint8_t ts0, - uint8_t e1_port1, uint8_t ts1) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t *attr; - - DEBUGP(DNM, "CONNECT MDROP LINK E1=(%u,%u) -> E1=(%u, %u)\n", - e1_port0, ts0, e1_port1, ts1); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 6, NM_MT_CONN_MDROP_LINK, - NM_OC_SITE_MANAGER, 0x00, 0x00, 0x00); - - attr = msgb_put(msg, 3); - attr[0] = NM_ATT_MDROP_LINK; - attr[1] = e1_port0; - attr[2] = ts0; - - attr = msgb_put(msg, 3); - attr[0] = NM_ATT_MDROP_NEXT; - attr[1] = e1_port1; - attr[2] = ts1; - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.7.1 */ -int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - uint8_t test_nr, uint8_t auton_report, struct msgb *msg) -{ - struct abis_om_hdr *oh; - - DEBUGP(DNM, "PEFORM TEST %s\n", abis_nm_test_name(test_nr)); - - if (!msg) - msg = nm_msgb_alloc(); - - msgb_tv_push(msg, NM_ATT_AUTON_REPORT, auton_report); - msgb_tv_push(msg, NM_ATT_TEST_NO, test_nr); - oh = (struct abis_om_hdr *) msgb_push(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, msgb_l3len(msg), NM_MT_PERF_TEST, - obj_class, bts_nr, trx_nr, ts_nr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_event_reports(struct gsm_bts *bts, int on) -{ - if (on == 0) - return __simple_cmd(bts, NM_MT_STOP_EVENT_REP); - else - return __simple_cmd(bts, NM_MT_REST_EVENT_REP); -} - -/* Siemens (or BS-11) specific commands */ - -int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect) -{ - if (reconnect == 0) - return __simple_cmd(bts, NM_MT_BS11_DISCONNECT); - else - return __simple_cmd(bts, NM_MT_BS11_RECONNECT); -} - -int abis_nm_bs11_restart(struct gsm_bts *bts) -{ - return __simple_cmd(bts, NM_MT_BS11_RESTART); -} - - -struct bs11_date_time { - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t min; - uint8_t sec; -} __attribute__((packed)); - - -void get_bs11_date_time(struct bs11_date_time *aet) -{ - time_t t; - struct tm *tm; - - t = time(NULL); - tm = localtime(&t); - aet->sec = tm->tm_sec; - aet->min = tm->tm_min; - aet->hour = tm->tm_hour; - aet->day = tm->tm_mday; - aet->month = tm->tm_mon; - aet->year = htons(1900 + tm->tm_year); -} - -int abis_nm_bs11_reset_resource(struct gsm_bts *bts) -{ - return __simple_cmd(bts, NM_MT_BS11_RESET_RESOURCE); -} - -int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin) -{ - if (begin) - return __simple_cmd(bts, NM_MT_BS11_BEGIN_DB_TX); - else - return __simple_cmd(bts, NM_MT_BS11_END_DB_TX); -} - -int abis_nm_bs11_create_object(struct gsm_bts *bts, - enum abis_bs11_objtype type, uint8_t idx, - uint8_t attr_len, const uint8_t *attr) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t *cur; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_BS11_CREATE_OBJ, - NM_OC_BS11, type, 0, idx); - cur = msgb_put(msg, attr_len); - memcpy(cur, attr, attr_len); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_delete_object(struct gsm_bts *bts, - enum abis_bs11_objtype type, uint8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, - NM_OC_BS11, type, 0, idx); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, uint8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t zero = 0x00; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_CREATE_OBJ, - NM_OC_BS11_ENVABTSE, 0, idx, 0xff); - msgb_tlv_put(msg, 0x99, 1, &zero); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_create_bport(struct gsm_bts *bts, uint8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11_BPORT, - idx, 0xff, 0xff); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_delete_bport(struct gsm_bts *bts, uint8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, NM_OC_BS11_BPORT, - idx, 0xff, 0xff); - - return abis_nm_sendmsg(bts, msg); -} - -static const uint8_t sm_attr[] = { NM_ATT_TEI, NM_ATT_ABIS_CHANNEL }; -int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(sm_attr), NM_MT_GET_ATTR, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(sm_attr), sm_attr); - - return abis_nm_sendmsg(bts, msg); -} - -/* like abis_nm_conn_terr_traf + set_tei */ -int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, uint8_t e1_port, - uint8_t e1_timeslot, uint8_t e1_subslot, - uint8_t tei) -{ - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, sizeof(*ch)+2, NM_MT_BS11_SET_ATTR, - NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - msgb_tv_put(msg, NM_ATT_TEI, tei); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, uint8_t level) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, - NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); - msgb_tlv_put(msg, NM_ATT_BS11_TXPWR, 1, &level); - - return abis_nm_sendmsg(trx->bts, msg); -} - -int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t attr = NM_ATT_BS11_TXPWR; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); - - return abis_nm_sendmsg(trx->bts, msg); -} - -int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t attr[] = { NM_ATT_BS11_PLL_MODE }; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11, BS11_OBJ_LI, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_get_cclk(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t attr[] = { NM_ATT_BS11_CCLK_ACCURACY, - NM_ATT_BS11_CCLK_TYPE }; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11, BS11_OBJ_CCLK, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); - - return abis_nm_sendmsg(bts, msg); - -} - -//static const uint8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 }; - -int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on) -{ - return abis_nm_bs11_logon(bts, 0x02, "FACTORY", on); -} - -int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on) -{ - return abis_nm_bs11_logon(bts, 0x03, "FIELD ", on); -} - -int abis_nm_bs11_logon(struct gsm_bts *bts, uint8_t level, const char *name, int on) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - struct bs11_date_time bdt; - - get_bs11_date_time(&bdt); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - if (on) { - uint8_t len = 3*2 + sizeof(bdt) - + 1 + strlen(name); - fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON, - NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); - msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME, - sizeof(bdt), (uint8_t *) &bdt); - msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV, - 1, &level); - msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME, - strlen(name), (uint8_t *)name); - } else { - fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF, - NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); - } - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password) -{ - struct abis_om_hdr *oh; - struct msgb *msg; - - if (strlen(password) != 10) - return -EINVAL; - - msg = nm_msgb_alloc(); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+strlen(password), NM_MT_BS11_SET_ATTR, - NM_OC_BS11, BS11_OBJ_TRX1, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_BS11_PASSWORD, 10, (const uint8_t *)password); - - return abis_nm_sendmsg(bts, msg); -} - -/* change the BS-11 PLL Mode to either locked (E1 derived) or standalone */ -int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked) -{ - struct abis_om_hdr *oh; - struct msgb *msg; - uint8_t tlv_value; - - msg = nm_msgb_alloc(); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, - BS11_OBJ_LI, 0x00, 0x00); - - if (locked) - tlv_value = BS11_LI_PLL_LOCKED; - else - tlv_value = BS11_LI_PLL_STANDALONE; - - msgb_tlv_put(msg, NM_ATT_BS11_PLL_MODE, 1, &tlv_value); - - return abis_nm_sendmsg(bts, msg); -} - -/* Set the calibration value of the PLL (work value/set value) - * It depends on the login which one is changed */ -int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value) -{ - struct abis_om_hdr *oh; - struct msgb *msg; - uint8_t tlv_value[2]; - - msg = nm_msgb_alloc(); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, - BS11_OBJ_TRX1, 0x00, 0x00); - - tlv_value[0] = value>>8; - tlv_value[1] = value&0xff; - - msgb_tlv_put(msg, NM_ATT_BS11_PLL, 2, tlv_value); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_get_state(struct gsm_bts *bts) -{ - return __simple_cmd(bts, NM_MT_BS11_GET_STATE); -} - -/* BS11 SWL */ - -void *tall_fle_ctx; - -struct abis_nm_bs11_sw { - struct gsm_bts *bts; - char swl_fname[PATH_MAX]; - uint8_t win_size; - int forced; - struct llist_head file_list; - gsm_cbfn *user_cb; /* specified by the user */ -}; -static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw; - -struct file_list_entry { - struct llist_head list; - char fname[PATH_MAX]; -}; - -struct file_list_entry *fl_dequeue(struct llist_head *queue) -{ - struct llist_head *lh; - - if (llist_empty(queue)) - return NULL; - - lh = queue->next; - llist_del(lh); - - return llist_entry(lh, struct file_list_entry, list); -} - -static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw) -{ - char linebuf[255]; - struct llist_head *lh, *lh2; - FILE *swl; - int rc = 0; - - swl = fopen(bs11_sw->swl_fname, "r"); - if (!swl) - return -ENODEV; - - /* zero the stale file list, if any */ - llist_for_each_safe(lh, lh2, &bs11_sw->file_list) { - llist_del(lh); - talloc_free(lh); - } - - while (fgets(linebuf, sizeof(linebuf), swl)) { - char file_id[12+1]; - char file_version[80+1]; - struct file_list_entry *fle; - static char dir[PATH_MAX]; - - if (strlen(linebuf) < 4) - continue; - - rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version); - if (rc < 0) { - perror("ERR parsing SWL file"); - rc = -EINVAL; - goto out; - } - if (rc < 2) - continue; - - fle = talloc_zero(tall_fle_ctx, struct file_list_entry); - if (!fle) { - rc = -ENOMEM; - goto out; - } - - /* construct new filename */ - strncpy(dir, bs11_sw->swl_fname, sizeof(dir)); - strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1); - strcat(fle->fname, "/"); - strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname)); - - llist_add_tail(&fle->list, &bs11_sw->file_list); - } - -out: - fclose(swl); - return rc; -} - -/* bs11 swload specific callback, passed to abis_nm core swload */ -static int bs11_swload_cbfn(unsigned int hook, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct abis_nm_bs11_sw *bs11_sw = data; - struct file_list_entry *fle; - int rc = 0; - - switch (event) { - case NM_MT_LOAD_END_ACK: - fle = fl_dequeue(&bs11_sw->file_list); - if (fle) { - /* start download the next file of our file list */ - rc = abis_nm_software_load(bs11_sw->bts, 0xff, fle->fname, - bs11_sw->win_size, - bs11_sw->forced, - &bs11_swload_cbfn, bs11_sw); - talloc_free(fle); - } else { - /* activate the SWL */ - rc = abis_nm_software_activate(bs11_sw->bts, - bs11_sw->swl_fname, - bs11_swload_cbfn, - bs11_sw); - } - break; - case NM_MT_LOAD_SEG_ACK: - case NM_MT_LOAD_END_NACK: - case NM_MT_LOAD_INIT_ACK: - case NM_MT_LOAD_INIT_NACK: - case NM_MT_ACTIVATE_SW_NACK: - case NM_MT_ACTIVATE_SW_ACK: - default: - /* fallthrough to the user callback */ - if (bs11_sw->user_cb) - rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL); - break; - } - - return rc; -} - -/* Siemens provides a SWL file that is a mere listing of all the other - * files that are part of a software release. We need to upload first - * the list file, and then each file that is listed in the list file */ -int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, - uint8_t win_size, int forced, gsm_cbfn *cbfn) -{ - struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw; - struct file_list_entry *fle; - int rc = 0; - - INIT_LLIST_HEAD(&bs11_sw->file_list); - bs11_sw->bts = bts; - bs11_sw->win_size = win_size; - bs11_sw->user_cb = cbfn; - bs11_sw->forced = forced; - - strncpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname)); - rc = bs11_read_swl_file(bs11_sw); - if (rc < 0) - return rc; - - /* dequeue next item in file list */ - fle = fl_dequeue(&bs11_sw->file_list); - if (!fle) - return -EINVAL; - - /* start download the next file of our file list */ - rc = abis_nm_software_load(bts, 0xff, fle->fname, win_size, forced, - bs11_swload_cbfn, bs11_sw); - talloc_free(fle); - return rc; -} - -#if 0 -static uint8_t req_attr_btse[] = { - NM_ATT_ADM_STATE, NM_ATT_BS11_LMT_LOGON_SESSION, - NM_ATT_BS11_LMT_LOGIN_TIME, NM_ATT_BS11_LMT_USER_ACC_LEV, - NM_ATT_BS11_LMT_USER_NAME, - - 0xaf, NM_ATT_BS11_RX_OFFSET, NM_ATT_BS11_VENDOR_NAME, - - NM_ATT_BS11_SW_LOAD_INTENDED, NM_ATT_BS11_SW_LOAD_SAFETY, - - NM_ATT_BS11_SW_LOAD_STORED }; - -static uint8_t req_attr_btsm[] = { - NM_ATT_ABIS_CHANNEL, NM_ATT_TEI, NM_ATT_BS11_ABIS_EXT_TIME, - NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xce, NM_ATT_FILE_ID, - NM_ATT_FILE_VERSION, NM_ATT_OPER_STATE, 0xe8, NM_ATT_BS11_ALL_TEST_CATG, - NM_ATT_SW_DESCR, NM_ATT_GET_ARI }; -#endif - -static uint8_t req_attr[] = { - NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE, - 0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO, - 0x42, NM_ATT_BS11_ESN_PCB_SERIAL, NM_ATT_BS11_PLL }; - -int abis_nm_bs11_get_serno(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - /* SiemensHW CCTRL object */ - fill_om_fom_hdr(oh, 2+sizeof(req_attr), NM_MT_GET_ATTR, NM_OC_BS11, - 0x03, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(req_attr), req_attr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_ext_time(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - struct bs11_date_time aet; - - get_bs11_date_time(&aet); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - /* SiemensHW CCTRL object */ - fill_om_fom_hdr(oh, 2+sizeof(aet), NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - msgb_tlv_put(msg, NM_ATT_BS11_ABIS_EXT_TIME, sizeof(aet), (uint8_t *) &aet); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, uint8_t bport) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t attr = NM_ATT_BS11_LINE_CFG; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11_BPORT, bport, 0xff, 0x02); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, uint8_t bport, enum abis_bs11_line_cfg line_cfg) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - struct bs11_date_time aet; - - get_bs11_date_time(&aet); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2, NM_MT_BS11_SET_ATTR, NM_OC_BS11_BPORT, - bport, 0xff, 0x02); - msgb_tv_put(msg, NM_ATT_BS11_LINE_CFG, line_cfg); - - return abis_nm_sendmsg(bts, msg); -} - -/* ip.access nanoBTS specific commands */ -static const char ipaccess_magic[] = "com.ipaccess"; - - -static int abis_nm_rx_ipacc(struct msgb *msg) -{ - struct in_addr addr; - struct abis_om_hdr *oh = msgb_l2(msg); - struct abis_om_fom_hdr *foh; - uint8_t idstrlen = oh->data[0]; - struct tlv_parsed tp; - struct ipacc_ack_signal_data signal; - struct e1inp_sign_link *sign_link = msg->dst; - - if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) { - LOGP(DNM, LOGL_ERROR, "id string is not com.ipaccess !?!\n"); - return -EINVAL; - } - - foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen); - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - - abis_nm_debugp_foh(DNM, foh); - - DEBUGPC(DNM, "IPACCESS(0x%02x): ", foh->msg_type); - - switch (foh->msg_type) { - case NM_MT_IPACC_RSL_CONNECT_ACK: - DEBUGPC(DNM, "RSL CONNECT ACK "); - if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP)) { - memcpy(&addr, - TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP), sizeof(addr)); - - DEBUGPC(DNM, "IP=%s ", inet_ntoa(addr)); - } - if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP_PORT)) - DEBUGPC(DNM, "PORT=%u ", - ntohs(*((uint16_t *) - TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP_PORT)))); - if (TLVP_PRESENT(&tp, NM_ATT_IPACC_STREAM_ID)) - DEBUGPC(DNM, "STREAM=0x%02x ", - *TLVP_VAL(&tp, NM_ATT_IPACC_STREAM_ID)); - DEBUGPC(DNM, "\n"); - break; - case NM_MT_IPACC_RSL_CONNECT_NACK: - LOGP(DNM, LOGL_ERROR, "RSL CONNECT NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - DEBUGPC(DNM, " CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - DEBUGPC(DNM, "\n"); - break; - case NM_MT_IPACC_SET_NVATTR_ACK: - DEBUGPC(DNM, "SET NVATTR ACK\n"); - /* FIXME: decode and show the actual attributes */ - break; - case NM_MT_IPACC_SET_NVATTR_NACK: - LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - LOGPC(DNM, LOGL_ERROR, "\n"); - break; - case NM_MT_IPACC_GET_NVATTR_ACK: - DEBUGPC(DNM, "GET NVATTR ACK\n"); - /* FIXME: decode and show the actual attributes */ - break; - case NM_MT_IPACC_GET_NVATTR_NACK: - LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - LOGPC(DNM, LOGL_ERROR, "\n"); - break; - case NM_MT_IPACC_SET_ATTR_ACK: - DEBUGPC(DNM, "SET ATTR ACK\n"); - break; - case NM_MT_IPACC_SET_ATTR_NACK: - LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - LOGPC(DNM, LOGL_ERROR, "\n"); - break; - default: - DEBUGPC(DNM, "unknown\n"); - break; - } - - /* signal handling */ - switch (foh->msg_type) { - case NM_MT_IPACC_RSL_CONNECT_NACK: - case NM_MT_IPACC_SET_NVATTR_NACK: - case NM_MT_IPACC_GET_NVATTR_NACK: - signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr); - signal.msg_type = foh->msg_type; - osmo_signal_dispatch(SS_NM, S_NM_IPACC_NACK, &signal); - break; - case NM_MT_IPACC_SET_NVATTR_ACK: - signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr); - signal.msg_type = foh->msg_type; - osmo_signal_dispatch(SS_NM, S_NM_IPACC_ACK, &signal); - break; - default: - break; - } - - return 0; -} - -/* send an ip-access manufacturer specific message */ -int abis_nm_ipaccess_msg(struct gsm_bts *bts, uint8_t msg_type, - uint8_t obj_class, uint8_t bts_nr, - uint8_t trx_nr, uint8_t ts_nr, - uint8_t *attr, int attr_len) -{ - struct msgb *msg = nm_msgb_alloc(); - struct abis_om_hdr *oh; - struct abis_om_fom_hdr *foh; - uint8_t *data; - - /* construct the 12.21 OM header, observe the erroneous length */ - oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); - fill_om_hdr(oh, sizeof(*foh) + attr_len); - oh->mdisc = ABIS_OM_MDISC_MANUF; - - /* add the ip.access magic */ - data = msgb_put(msg, sizeof(ipaccess_magic)+1); - *data++ = sizeof(ipaccess_magic); - memcpy(data, ipaccess_magic, sizeof(ipaccess_magic)); - - /* fill the 12.21 FOM header */ - foh = (struct abis_om_fom_hdr *) msgb_put(msg, sizeof(*foh)); - foh->msg_type = msg_type; - foh->obj_class = obj_class; - foh->obj_inst.bts_nr = bts_nr; - foh->obj_inst.trx_nr = trx_nr; - foh->obj_inst.ts_nr = ts_nr; - - if (attr && attr_len) { - data = msgb_put(msg, attr_len); - memcpy(data, attr, attr_len); - } - - return abis_nm_sendmsg(bts, msg); -} - -/* set some attributes in NVRAM */ -int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, uint8_t *attr, - int attr_len) -{ - return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_SET_NVATTR, - NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff, attr, - attr_len); -} - -int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, - uint32_t ip, uint16_t port, uint8_t stream) -{ - struct in_addr ia; - uint8_t attr[] = { NM_ATT_IPACC_STREAM_ID, 0, - NM_ATT_IPACC_DST_IP_PORT, 0, 0, - NM_ATT_IPACC_DST_IP, 0, 0, 0, 0 }; - - int attr_len = sizeof(attr); - - ia.s_addr = htonl(ip); - attr[1] = stream; - attr[3] = port >> 8; - attr[4] = port & 0xff; - *(uint32_t *)(attr+6) = ia.s_addr; - - /* if ip == 0, we use the default IP */ - if (ip == 0) - attr_len -= 5; - - DEBUGP(DNM, "ip.access RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n", - inet_ntoa(ia), port, stream); - - return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT, - NM_OC_BASEB_TRANSC, trx->bts->bts_nr, - trx->nr, 0xff, attr, attr_len); -} - -/* restart / reboot an ip.access nanoBTS */ -int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC, - trx->bts->nr, trx->nr, 0xff); - - return abis_nm_sendmsg(trx->bts, msg); -} - -int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - uint8_t *attr, uint8_t attr_len) -{ - return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_ATTR, - obj_class, bts_nr, trx_nr, ts_nr, - attr, attr_len); -} - -void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts) -{ - /* we simply reuse the GSM48 function and overwrite the RAC - * with the Cell ID */ - gsm48_ra_id_by_bts(buf, bts); - *((uint16_t *)(buf + 5)) = htons(bts->cell_identity); -} - -void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked) -{ - int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED; - - trx->mo.nm_state.administrative = new_state; - if (!trx->bts || !trx->bts->oml_link) - return; - - abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER, - trx->bts->bts_nr, trx->nr, 0xff, - new_state); -} - -static const struct value_string ipacc_testres_names[] = { - { NM_IPACC_TESTRES_SUCCESS, "SUCCESS" }, - { NM_IPACC_TESTRES_TIMEOUT, "TIMEOUT" }, - { NM_IPACC_TESTRES_NO_CHANS, "NO CHANNELS" }, - { NM_IPACC_TESTRES_PARTIAL, "PARTIAL" }, - { NM_IPACC_TESTRES_STOPPED, "STOPPED" }, - { 0, NULL } -}; - -const char *ipacc_testres_name(uint8_t res) -{ - return get_value_string(ipacc_testres_names, res); -} - -void ipac_parse_cgi(struct cell_global_id *cid, const uint8_t *buf) -{ - cid->mcc = (buf[0] & 0xf) * 100; - cid->mcc += (buf[0] >> 4) * 10; - cid->mcc += (buf[1] & 0xf) * 1; - - if (buf[1] >> 4 == 0xf) { - cid->mnc = (buf[2] & 0xf) * 10; - cid->mnc += (buf[2] >> 4) * 1; - } else { - cid->mnc = (buf[2] & 0xf) * 100; - cid->mnc += (buf[2] >> 4) * 10; - cid->mnc += (buf[1] >> 4) * 1; - } - - cid->lac = ntohs(*((uint16_t *)&buf[3])); - cid->ci = ntohs(*((uint16_t *)&buf[5])); -} - -/* parse BCCH information IEI from wire format to struct ipac_bcch_info */ -int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf) -{ - uint8_t *cur = buf; - uint16_t len __attribute__((unused)); - - memset(binf, 0, sizeof(*binf)); - - if (cur[0] != NM_IPAC_EIE_BCCH_INFO) - return -EINVAL; - cur++; - - len = ntohs(*(uint16_t *)cur); - cur += 2; - - binf->info_type = ntohs(*(uint16_t *)cur); - cur += 2; - - if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) - binf->freq_qual = *cur >> 2; - - binf->arfcn = (*cur++ & 3) << 8; - binf->arfcn |= *cur++; - - if (binf->info_type & IPAC_BINF_RXLEV) - binf->rx_lev = *cur & 0x3f; - cur++; - - if (binf->info_type & IPAC_BINF_RXQUAL) - binf->rx_qual = *cur & 0x7; - cur++; - - if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) - binf->freq_err = ntohs(*(uint16_t *)cur); - cur += 2; - - if (binf->info_type & IPAC_BINF_FRAME_OFFSET) - binf->frame_offset = ntohs(*(uint16_t *)cur); - cur += 2; - - if (binf->info_type & IPAC_BINF_FRAME_NR_OFFSET) - binf->frame_nr_offset = ntohl(*(uint32_t *)cur); - cur += 4; - -#if 0 - /* Somehow this is not set correctly */ - if (binf->info_type & IPAC_BINF_BSIC) -#endif - binf->bsic = *cur & 0x3f; - cur++; - - ipac_parse_cgi(&binf->cgi, cur); - cur += 7; - - if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2) { - memcpy(binf->ba_list_si2, cur, sizeof(binf->ba_list_si2)); - cur += sizeof(binf->ba_list_si2); - } - - if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2bis) { - memcpy(binf->ba_list_si2bis, cur, - sizeof(binf->ba_list_si2bis)); - cur += sizeof(binf->ba_list_si2bis); - } - - if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2ter) { - memcpy(binf->ba_list_si2ter, cur, - sizeof(binf->ba_list_si2ter)); - cur += sizeof(binf->ba_list_si2ter); - } - - return 0; -} - -void abis_nm_clear_queue(struct gsm_bts *bts) -{ - struct msgb *msg; - - while (!llist_empty(&bts->abis_queue)) { - msg = msgb_dequeue(&bts->abis_queue); - msgb_free(msg); - } - - bts->abis_nm_pend = 0; -} diff --git a/openbsc/src/libbsc/abis_nm_ipaccess.c b/openbsc/src/libbsc/abis_nm_ipaccess.c deleted file mode 100644 index b8225383a..000000000 --- a/openbsc/src/libbsc/abis_nm_ipaccess.c +++ /dev/null @@ -1,87 +0,0 @@ -/* GSM Network Management (OML) messages on the A-bis interface - * Extensions for the ip.access A-bis over IP protocol*/ - -/* (C) 2008-2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 fd6021008..000000000 --- a/openbsc/src/libbsc/abis_nm_vty.c +++ /dev/null @@ -1,192 +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(bsc_gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->obj_class = get_string_value(abis_nm_obj_class_names, argv[1]); - oms->obj_inst[0] = atoi(argv[2]); - oms->obj_inst[1] = atoi(argv[3]); - oms->obj_inst[2] = atoi(argv[4]); - - vty->index = oms; - vty->node = OML_NODE; - - return CMD_SUCCESS; - -} - -DEFUN(oml_classnum_inst, oml_classnum_inst_cmd, - "bts <0-255> oml class <0-255> instance <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OML managed objects\n" - "Object Class\n" "Object Class\n" - "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(bsc_gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->obj_class = atoi(argv[1]); - oms->obj_inst[0] = atoi(argv[2]); - oms->obj_inst[1] = atoi(argv[3]); - oms->obj_inst[2] = atoi(argv[4]); - - vty->index = oms; - vty->node = OML_NODE; - - return CMD_SUCCESS; -} - -DEFUN(oml_chg_adm_state, oml_chg_adm_state_cmd, - "change-adm-state (locked|unlocked|shutdown|null)", - "Change the Administrative State\n" - "Locked\n" "Unlocked\n" "Shutdown\n" "NULL\n") -{ - struct oml_node_state *oms = vty->index; - enum abis_nm_adm_state state; - - state = get_string_value(abis_nm_adm_state_names, argv[0]); - - abis_nm_chg_adm_state(oms->bts, oms->obj_class, oms->obj_inst[0], - oms->obj_inst[1], oms->obj_inst[2], state); - - return CMD_SUCCESS; -} - -DEFUN(oml_opstart, oml_opstart_cmd, - "opstart", "Send an OPSTART message to the object") -{ - struct oml_node_state *oms = vty->index; - - abis_nm_opstart(oms->bts, oms->obj_class, oms->obj_inst[0], - oms->obj_inst[1], oms->obj_inst[2]); - - return CMD_SUCCESS; -} - -int abis_nm_vty_init(void) -{ - install_element(ENABLE_NODE, &oml_class_inst_cmd); - install_element(ENABLE_NODE, &oml_classnum_inst_cmd); - install_node(&oml_node, dummy_config_write); - - install_default(OML_NODE); - install_element(OML_NODE, &ournode_exit_cmd); - install_element(OML_NODE, &oml_chg_adm_state_cmd); - install_element(OML_NODE, &oml_opstart_cmd); - - return 0; -} diff --git a/openbsc/src/libbsc/abis_om2000.c b/openbsc/src/libbsc/abis_om2000.c deleted file mode 100644 index d8350e5c1..000000000 --- a/openbsc/src/libbsc/abis_om2000.c +++ /dev/null @@ -1,1609 +0,0 @@ -/* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface - * implemented based on protocol trace analysis, no formal documentation */ - -/* (C) 2010-2011 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define OM_ALLOC_SIZE 1024 -#define OM_HEADROOM_SIZE 128 - -/* use following functions from abis_nm.c: - * om2k_msgb_alloc() - * abis_om2k_sendmsg() - */ - -struct abis_om2k_hdr { - struct abis_om_hdr om; - uint16_t msg_type; - struct abis_om2k_mo mo; - uint8_t data[0]; -} __attribute__ ((packed)); - -enum abis_om2k_msgtype { - OM2K_MSGT_ABORT_SP_CMD = 0x0000, - OM2K_MSGT_ABORT_SP_COMPL = 0x0002, - OM2K_MSGT_ALARM_REP_ACK = 0x0004, - OM2K_MSGT_ALARM_REP_NACK = 0x0005, - OM2K_MSGT_ALARM_REP = 0x0006, - OM2K_MSGT_ALARM_STATUS_REQ = 0x0008, - OM2K_MSGT_ALARM_STATUS_REQ_ACK = 0x000a, - OM2K_MSGT_ALARM_STATUS_REQ_REJ = 0x000b, - OM2K_MSGT_ALARM_STATUS_RES_ACK = 0x000c, - OM2K_MSGT_ALARM_STATUS_RES_NACK = 0x000d, - OM2K_MSGT_ALARM_STATUS_RES = 0x000e, - OM2K_MSGT_CAL_TIME_RESP = 0x0010, - OM2K_MSGT_CAL_TIME_REJ = 0x0011, - OM2K_MSGT_CAL_TIME_REQ = 0x0012, - - OM2K_MSGT_CON_CONF_REQ = 0x0014, - OM2K_MSGT_CON_CONF_REQ_ACK = 0x0016, - OM2K_MSGT_CON_CONF_REQ_REJ = 0x0017, - OM2K_MSGT_CON_CONF_RES_ACK = 0x0018, - OM2K_MSGT_CON_CONF_RES_NACK = 0x0019, - OM2K_MSGT_CON_CONF_RES = 0x001a, - - OM2K_MSGT_CONNECT_CMD = 0x001c, - OM2K_MSGT_CONNECT_COMPL = 0x001e, - OM2K_MSGT_CONNECT_REJ = 0x001f, - - OM2K_MSGT_DISABLE_REQ = 0x0028, - OM2K_MSGT_DISABLE_REQ_ACK = 0x002a, - OM2K_MSGT_DISABLE_REQ_REJ = 0x002b, - OM2K_MSGT_DISABLE_RES_ACK = 0x002c, - OM2K_MSGT_DISABLE_RES_NACK = 0x002d, - OM2K_MSGT_DISABLE_RES = 0x002e, - OM2K_MSGT_DISCONNECT_CMD = 0x0030, - OM2K_MSGT_DISCONNECT_COMPL = 0x0032, - OM2K_MSGT_DISCONNECT_REJ = 0x0033, - OM2K_MSGT_ENABLE_REQ = 0x0034, - OM2K_MSGT_ENABLE_REQ_ACK = 0x0036, - OM2K_MSGT_ENABLE_REQ_REJ = 0x0037, - OM2K_MSGT_ENABLE_RES_ACK = 0x0038, - OM2K_MSGT_ENABLE_RES_NACK = 0x0039, - OM2K_MSGT_ENABLE_RES = 0x003a, - - OM2K_MSGT_FAULT_REP_ACK = 0x0040, - OM2K_MSGT_FAULT_REP_NACK = 0x0041, - OM2K_MSGT_FAULT_REP = 0x0042, - - OM2K_MSGT_IS_CONF_REQ = 0x0060, - OM2K_MSGT_IS_CONF_REQ_ACK = 0x0062, - OM2K_MSGT_IS_CONF_REQ_REJ = 0x0063, - OM2K_MSGT_IS_CONF_RES_ACK = 0x0064, - OM2K_MSGT_IS_CONF_RES_NACK = 0x0065, - OM2K_MSGT_IS_CONF_RES = 0x0066, - - OM2K_MSGT_OP_INFO = 0x0074, - OM2K_MSGT_OP_INFO_ACK = 0x0076, - OM2K_MSGT_OP_INFO_REJ = 0x0077, - OM2K_MSGT_RESET_CMD = 0x0078, - OM2K_MSGT_RESET_COMPL = 0x007a, - OM2K_MSGT_RESET_REJ = 0x007b, - OM2K_MSGT_RX_CONF_REQ = 0x007c, - OM2K_MSGT_RX_CONF_REQ_ACK = 0x007e, - OM2K_MSGT_RX_CONF_REQ_REJ = 0x007f, - OM2K_MSGT_RX_CONF_RES_ACK = 0x0080, - OM2K_MSGT_RX_CONF_RES_NACK = 0x0081, - OM2K_MSGT_RX_CONF_RES = 0x0082, - OM2K_MSGT_START_REQ = 0x0084, - OM2K_MSGT_START_REQ_ACK = 0x0086, - OM2K_MSGT_START_REQ_REJ = 0x0087, - OM2K_MSGT_START_RES_ACK = 0x0088, - OM2K_MSGT_START_RES_NACK = 0x0089, - OM2K_MSGT_START_RES = 0x008a, - OM2K_MSGT_STATUS_REQ = 0x008c, - OM2K_MSGT_STATUS_RESP = 0x008e, - OM2K_MSGT_STATUS_REJ = 0x008f, - - OM2K_MSGT_TEST_REQ = 0x0094, - OM2K_MSGT_TEST_REQ_ACK = 0x0096, - OM2K_MSGT_TEST_REQ_REJ = 0x0097, - OM2K_MSGT_TEST_RES_ACK = 0x0098, - OM2K_MSGT_TEST_RES_NACK = 0x0099, - OM2K_MSGT_TEST_RES = 0x009a, - - OM2K_MSGT_TF_CONF_REQ = 0x00a0, - OM2K_MSGT_TF_CONF_REQ_ACK = 0x00a2, - OM2K_MSGT_TF_CONF_REQ_REJ = 0x00a3, - OM2K_MSGT_TF_CONF_RES_ACK = 0x00a4, - OM2K_MSGT_TF_CONF_RES_NACK = 0x00a5, - OM2K_MSGT_TF_CONF_RES = 0x00a6, - OM2K_MSGT_TS_CONF_REQ = 0x00a8, - OM2K_MSGT_TS_CONF_REQ_ACK = 0x00aa, - OM2K_MSGT_TS_CONF_REQ_REJ = 0x00ab, - OM2K_MSGT_TS_CONF_RES_ACK = 0x00ac, - OM2K_MSGT_TS_CONF_RES_NACK = 0x00ad, - OM2K_MSGT_TS_CONF_RES = 0x00ae, - OM2K_MSGT_TX_CONF_REQ = 0x00b0, - OM2K_MSGT_TX_CONF_REQ_ACK = 0x00b2, - OM2K_MSGT_TX_CONF_REQ_REJ = 0x00b3, - OM2K_MSGT_TX_CONF_RES_ACK = 0x00b4, - OM2K_MSGT_TX_CONF_RES_NACK = 0x00b5, - OM2K_MSGT_TX_CONF_RES = 0x00b6, - - OM2K_MSGT_NEGOT_REQ_ACK = 0x0104, - OM2K_MSGT_NEGOT_REQ_NACK = 0x0105, - OM2K_MSGT_NEGOT_REQ = 0x0106, -}; - -enum abis_om2k_dei { - OM2K_DEI_ACCORDANCE_IND = 0x00, - OM2K_DEI_BCC = 0x06, - OM2K_DEI_BS_AG_BKS_RES = 0x07, - OM2K_DEI_BSIC = 0x09, - OM2K_DEI_BA_PA_MFRMS = 0x0a, - OM2K_DEI_CBCH_INDICATOR = 0x0b, - OM2K_DEI_CCCH_OPTIONS = 0x0c, - OM2K_DEI_CAL_TIME = 0x0d, - OM2K_DEI_COMBINATION = 0x0f, - OM2K_DEI_CON_CONN_LIST = 0x10, - OM2K_DEI_DRX_DEV_MAX = 0x12, - OM2K_DEI_END_LIST_NR = 0x13, - OM2K_DEI_EXT_COND_MAP_1 = 0x14, - OM2K_DEI_EXT_COND_MAP_2 = 0x15, - OM2K_DEI_FILLING_MARKER = 0x1c, - OM2K_DEI_FN_OFFSET = 0x1d, - OM2K_DEI_FREQ_LIST = 0x1e, - OM2K_DEI_FREQ_SPEC_RX = 0x1f, - OM2K_DEI_FREQ_SPEC_TX = 0x20, - OM2K_DEI_HSN = 0x21, - OM2K_DEI_ICM_INDICATOR = 0x22, - OM2K_DEI_INT_FAULT_MAP_1A = 0x23, - OM2K_DEI_INT_FAULT_MAP_1B = 0x24, - OM2K_DEI_INT_FAULT_MAP_2A = 0x25, - OM2K_DEI_INT_FAULT_MAP_2A_EXT = 0x26, - OM2K_DEI_IS_CONN_LIST = 0x27, - OM2K_DEI_LIST_NR = 0x28, - OM2K_DEI_LOCAL_ACCESS = 0x2a, - OM2K_DEI_MAIO = 0x2b, - OM2K_DEI_MO_STATE = 0x2c, - OM2K_DEI_NY1 = 0x2d, - OM2K_DEI_OP_INFO = 0x2e, - OM2K_DEI_POWER = 0x2f, - OM2K_DEI_REASON_CODE = 0x32, - OM2K_DEI_RX_DIVERSITY = 0x33, - OM2K_DEI_RESULT_CODE = 0x35, - OM2K_DEI_T3105 = 0x38, - OM2K_DEI_TF_MODE = 0x3a, - OM2K_DEI_TS_NR = 0x3c, - OM2K_DEI_TSC = 0x3d, - OM2K_DEI_BTS_VERSION = 0x40, - OM2K_DEI_OML_IWD_VERSION = 0x41, - OM2K_DEI_RSL_IWD_VERSION = 0x42, - OM2K_DEI_OML_FUNC_MAP_1 = 0x43, - OM2K_DEI_OML_FUNC_MAP_2 = 0x44, - OM2K_DEI_RSL_FUNC_MAP_1 = 0x45, - OM2K_DEI_RSL_FUNC_MAP_2 = 0x46, - OM2K_DEI_EXT_RANGE = 0x47, - OM2K_DEI_REQ_IND = 0x48, - OM2K_DEI_REPL_UNIT_MAP = 0x50, - OM2K_DEI_ICM_BOUND_PARAMS = 0x74, - OM2K_DEI_LSC = 0x79, - OM2K_DEI_LSC_FILT_TIME = 0x7a, - OM2K_DEI_CALL_SUPV_TIME = 0x7b, - OM2K_DEI_ICM_CHAN_RATE = 0x7e, - OM2K_DEI_HW_INFO_SIG = 0x84, - OM2K_DEI_TF_SYNC_SRC = 0x86, - OM2K_DEI_TTA = 0x87, - OM2K_DEI_CAPA_SIG = 0x8a, - OM2K_DEI_NEGOT_REC1 = 0x90, - OM2K_DEI_NEGOT_REC2 = 0x91, - OM2K_DEI_ENCR_ALG = 0x92, - OM2K_DEI_INTERF_REJ_COMB = 0x94, - OM2K_DEI_FS_OFFSET = 0x98, - OM2K_DEI_EXT_COND_MAP_2_EXT = 0x9c, -}; - -const struct tlv_definition om2k_att_tlvdef = { - .def = { - [OM2K_DEI_ACCORDANCE_IND] = { TLV_TYPE_TV }, - [OM2K_DEI_BCC] = { TLV_TYPE_TV }, - [OM2K_DEI_BS_AG_BKS_RES] = { TLV_TYPE_TV }, - [OM2K_DEI_BSIC] = { TLV_TYPE_TV }, - [OM2K_DEI_BA_PA_MFRMS] = { TLV_TYPE_TV }, - [OM2K_DEI_CBCH_INDICATOR] = { TLV_TYPE_TV }, - [OM2K_DEI_INT_FAULT_MAP_1A] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_INT_FAULT_MAP_1B] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_INT_FAULT_MAP_2A] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_INT_FAULT_MAP_2A_EXT]={ TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_CCCH_OPTIONS] = { TLV_TYPE_TV }, - [OM2K_DEI_CAL_TIME] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_COMBINATION] = { TLV_TYPE_TV }, - [OM2K_DEI_CON_CONN_LIST] = { TLV_TYPE_TLV }, - [OM2K_DEI_DRX_DEV_MAX] = { TLV_TYPE_TV }, - [OM2K_DEI_END_LIST_NR] = { TLV_TYPE_TV }, - [OM2K_DEI_EXT_COND_MAP_1] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_EXT_COND_MAP_2] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_FILLING_MARKER] = { TLV_TYPE_TV }, - [OM2K_DEI_FN_OFFSET] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_FREQ_LIST] = { TLV_TYPE_TLV }, - [OM2K_DEI_FREQ_SPEC_RX] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_FREQ_SPEC_TX] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_HSN] = { TLV_TYPE_TV }, - [OM2K_DEI_ICM_INDICATOR] = { TLV_TYPE_TV }, - [OM2K_DEI_IS_CONN_LIST] = { TLV_TYPE_TLV }, - [OM2K_DEI_LIST_NR] = { TLV_TYPE_TV }, - [OM2K_DEI_LOCAL_ACCESS] = { TLV_TYPE_TV }, - [OM2K_DEI_MAIO] = { TLV_TYPE_TV }, - [OM2K_DEI_MO_STATE] = { TLV_TYPE_TV }, - [OM2K_DEI_NY1] = { TLV_TYPE_TV }, - [OM2K_DEI_OP_INFO] = { TLV_TYPE_TV }, - [OM2K_DEI_POWER] = { TLV_TYPE_TV }, - [OM2K_DEI_REASON_CODE] = { TLV_TYPE_TV }, - [OM2K_DEI_RX_DIVERSITY] = { TLV_TYPE_TV }, - [OM2K_DEI_RESULT_CODE] = { TLV_TYPE_TV }, - [OM2K_DEI_T3105] = { TLV_TYPE_TV }, - [OM2K_DEI_TF_MODE] = { TLV_TYPE_TV }, - [OM2K_DEI_TS_NR] = { TLV_TYPE_TV }, - [OM2K_DEI_TSC] = { TLV_TYPE_TV }, - [OM2K_DEI_BTS_VERSION] = { TLV_TYPE_FIXED, 12 }, - [OM2K_DEI_OML_IWD_VERSION] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_RSL_IWD_VERSION] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_OML_FUNC_MAP_1] = { TLV_TYPE_TLV }, - [OM2K_DEI_OML_FUNC_MAP_2] = { TLV_TYPE_TLV }, - [OM2K_DEI_RSL_FUNC_MAP_1] = { TLV_TYPE_TLV }, - [OM2K_DEI_RSL_FUNC_MAP_2] = { TLV_TYPE_TLV }, - [OM2K_DEI_EXT_RANGE] = { TLV_TYPE_TV }, - [OM2K_DEI_REQ_IND] = { TLV_TYPE_TV }, - [OM2K_DEI_REPL_UNIT_MAP] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_ICM_BOUND_PARAMS] = { TLV_TYPE_FIXED, 5 }, - [OM2K_DEI_LSC] = { TLV_TYPE_TV }, - [OM2K_DEI_LSC_FILT_TIME] = { TLV_TYPE_TV }, - [OM2K_DEI_CALL_SUPV_TIME] = { TLV_TYPE_TV }, - [OM2K_DEI_ICM_CHAN_RATE] = { TLV_TYPE_TV }, - [OM2K_DEI_HW_INFO_SIG] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_TF_SYNC_SRC] = { TLV_TYPE_TV }, - [OM2K_DEI_TTA] = { TLV_TYPE_TV }, - [OM2K_DEI_CAPA_SIG] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_NEGOT_REC1] = { TLV_TYPE_TLV }, - [OM2K_DEI_NEGOT_REC2] = { TLV_TYPE_TLV }, - [OM2K_DEI_ENCR_ALG] = { TLV_TYPE_TV }, - [OM2K_DEI_INTERF_REJ_COMB] = { TLV_TYPE_TV }, - [OM2K_DEI_FS_OFFSET] = { TLV_TYPE_FIXED, 5 }, - [OM2K_DEI_EXT_COND_MAP_2_EXT] = { TLV_TYPE_FIXED, 4 }, - }, -}; - -static const struct value_string om2k_msgcode_vals[] = { - { 0x0000, "Abort SP Command" }, - { 0x0002, "Abort SP Complete" }, - { 0x0004, "Alarm Report ACK" }, - { 0x0005, "Alarm Report NACK" }, - { 0x0006, "Alarm Report" }, - { 0x0008, "Alarm Status Request" }, - { 0x000a, "Alarm Status Request Accept" }, - { 0x000b, "Alarm Status Request Reject" }, - { 0x000c, "Alarm Status Result ACK" }, - { 0x000d, "Alarm Status Result NACK" }, - { 0x000e, "Alarm Status Result" }, - { 0x0010, "Calendar Time Response" }, - { 0x0011, "Calendar Time Reject" }, - { 0x0012, "Calendar Time Request" }, - { 0x0014, "CON Configuration Request" }, - { 0x0016, "CON Configuration Request Accept" }, - { 0x0017, "CON Configuration Request Reject" }, - { 0x0018, "CON Configuration Result ACK" }, - { 0x0019, "CON Configuration Result NACK" }, - { 0x001a, "CON Configuration Result" }, - { 0x001c, "Connect Command" }, - { 0x001e, "Connect Complete" }, - { 0x001f, "Connect Reject" }, - { 0x0028, "Disable Request" }, - { 0x002a, "Disable Request Accept" }, - { 0x002b, "Disable Request Reject" }, - { 0x002c, "Disable Result ACK" }, - { 0x002d, "Disable Result NACK" }, - { 0x002e, "Disable Result" }, - { 0x0030, "Disconnect Command" }, - { 0x0032, "Disconnect Complete" }, - { 0x0033, "Disconnect Reject" }, - { 0x0034, "Enable Request" }, - { 0x0036, "Enable Request Accept" }, - { 0x0037, "Enable Request Reject" }, - { 0x0038, "Enable Result ACK" }, - { 0x0039, "Enable Result NACK" }, - { 0x003a, "Enable Result" }, - { 0x003c, "Escape Downlink Normal" }, - { 0x003d, "Escape Downlink NACK" }, - { 0x003e, "Escape Uplink Normal" }, - { 0x003f, "Escape Uplink NACK" }, - { 0x0040, "Fault Report ACK" }, - { 0x0041, "Fault Report NACK" }, - { 0x0042, "Fault Report" }, - { 0x0044, "File Package End Command" }, - { 0x0046, "File Package End Result" }, - { 0x0047, "File Package End Reject" }, - { 0x0048, "File Relation Request" }, - { 0x004a, "File Relation Response" }, - { 0x004b, "File Relation Request Reject" }, - { 0x004c, "File Segment Transfer" }, - { 0x004e, "File Segment Transfer Complete" }, - { 0x004f, "File Segment Transfer Reject" }, - { 0x0050, "HW Information Request" }, - { 0x0052, "HW Information Request Accept" }, - { 0x0053, "HW Information Request Reject" }, - { 0x0054, "HW Information Result ACK" }, - { 0x0055, "HW Information Result NACK" }, - { 0x0056, "HW Information Result" }, - { 0x0060, "IS Configuration Request" }, - { 0x0062, "IS Configuration Request Accept" }, - { 0x0063, "IS Configuration Request Reject" }, - { 0x0064, "IS Configuration Result ACK" }, - { 0x0065, "IS Configuration Result NACK" }, - { 0x0066, "IS Configuration Result" }, - { 0x0068, "Load Data End" }, - { 0x006a, "Load Data End Result" }, - { 0x006b, "Load Data End Reject" }, - { 0x006c, "Load Data Init" }, - { 0x006e, "Load Data Init Accept" }, - { 0x006f, "Load Data Init Reject" }, - { 0x0070, "Loop Control Command" }, - { 0x0072, "Loop Control Complete" }, - { 0x0073, "Loop Control Reject" }, - { 0x0074, "Operational Information" }, - { 0x0076, "Operational Information Accept" }, - { 0x0077, "Operational Information Reject" }, - { 0x0078, "Reset Command" }, - { 0x007a, "Reset Complete" }, - { 0x007b, "Reset Reject" }, - { 0x007c, "RX Configuration Request" }, - { 0x007e, "RX Configuration Request Accept" }, - { 0x007f, "RX Configuration Request Reject" }, - { 0x0080, "RX Configuration Result ACK" }, - { 0x0081, "RX Configuration Result NACK" }, - { 0x0082, "RX Configuration Result" }, - { 0x0084, "Start Request" }, - { 0x0086, "Start Request Accept" }, - { 0x0087, "Start Request Reject" }, - { 0x0088, "Start Result ACK" }, - { 0x0089, "Start Result NACK" }, - { 0x008a, "Start Result" }, - { 0x008c, "Status Request" }, - { 0x008e, "Status Response" }, - { 0x008f, "Status Reject" }, - { 0x0094, "Test Request" }, - { 0x0096, "Test Request Accept" }, - { 0x0097, "Test Request Reject" }, - { 0x0098, "Test Result ACK" }, - { 0x0099, "Test Result NACK" }, - { 0x009a, "Test Result" }, - { 0x00a0, "TF Configuration Request" }, - { 0x00a2, "TF Configuration Request Accept" }, - { 0x00a3, "TF Configuration Request Reject" }, - { 0x00a4, "TF Configuration Result ACK" }, - { 0x00a5, "TF Configuration Result NACK" }, - { 0x00a6, "TF Configuration Result" }, - { 0x00a8, "TS Configuration Request" }, - { 0x00aa, "TS Configuration Request Accept" }, - { 0x00ab, "TS Configuration Request Reject" }, - { 0x00ac, "TS Configuration Result ACK" }, - { 0x00ad, "TS Configuration Result NACK" }, - { 0x00ae, "TS Configuration Result" }, - { 0x00b0, "TX Configuration Request" }, - { 0x00b2, "TX Configuration Request Accept" }, - { 0x00b3, "TX Configuration Request Reject" }, - { 0x00b4, "TX Configuration Result ACK" }, - { 0x00b5, "TX Configuration Result NACK" }, - { 0x00b6, "TX Configuration Result" }, - { 0x00bc, "DIP Alarm Report ACK" }, - { 0x00bd, "DIP Alarm Report NACK" }, - { 0x00be, "DIP Alarm Report" }, - { 0x00c0, "DIP Alarm Status Request" }, - { 0x00c2, "DIP Alarm Status Response" }, - { 0x00c3, "DIP Alarm Status Reject" }, - { 0x00c4, "DIP Quality Report I ACK" }, - { 0x00c5, "DIP Quality Report I NACK" }, - { 0x00c6, "DIP Quality Report I" }, - { 0x00c8, "DIP Quality Report II ACK" }, - { 0x00c9, "DIP Quality Report II NACK" }, - { 0x00ca, "DIP Quality Report II" }, - { 0x00dc, "DP Configuration Request" }, - { 0x00de, "DP Configuration Request Accept" }, - { 0x00df, "DP Configuration Request Reject" }, - { 0x00e0, "DP Configuration Result ACK" }, - { 0x00e1, "DP Configuration Result NACK" }, - { 0x00e2, "DP Configuration Result" }, - { 0x00e4, "Capabilities HW Info Report ACK" }, - { 0x00e5, "Capabilities HW Info Report NACK" }, - { 0x00e6, "Capabilities HW Info Report" }, - { 0x00e8, "Capabilities Request" }, - { 0x00ea, "Capabilities Request Accept" }, - { 0x00eb, "Capabilities Request Reject" }, - { 0x00ec, "Capabilities Result ACK" }, - { 0x00ed, "Capabilities Result NACK" }, - { 0x00ee, "Capabilities Result" }, - { 0x00f0, "FM Configuration Request" }, - { 0x00f2, "FM Configuration Request Accept" }, - { 0x00f3, "FM Configuration Request Reject" }, - { 0x00f4, "FM Configuration Result ACK" }, - { 0x00f5, "FM Configuration Result NACK" }, - { 0x00f6, "FM Configuration Result" }, - { 0x00f8, "FM Report Request" }, - { 0x00fa, "FM Report Response" }, - { 0x00fb, "FM Report Reject" }, - { 0x00fc, "FM Start Command" }, - { 0x00fe, "FM Start Complete" }, - { 0x00ff, "FM Start Reject" }, - { 0x0100, "FM Stop Command" }, - { 0x0102, "FM Stop Complete" }, - { 0x0103, "FM Stop Reject" }, - { 0x0104, "Negotiation Request ACK" }, - { 0x0105, "Negotiation Request NACK" }, - { 0x0106, "Negotiation Request" }, - { 0x0108, "BTS Initiated Request ACK" }, - { 0x0109, "BTS Initiated Request NACK" }, - { 0x010a, "BTS Initiated Request" }, - { 0x010c, "Radio Channels Release Command" }, - { 0x010e, "Radio Channels Release Complete" }, - { 0x010f, "Radio Channels Release Reject" }, - { 0x0118, "Feature Control Command" }, - { 0x011a, "Feature Control Complete" }, - { 0x011b, "Feature Control Reject" }, - - { 0, NULL } -}; - -/* TS 12.21 Section 9.4: Attributes */ -static const struct value_string om2k_attr_vals[] = { - { 0x00, "Accordance indication" }, - { 0x01, "Alarm Id" }, - { 0x02, "Alarm Data" }, - { 0x03, "Alarm Severity" }, - { 0x04, "Alarm Status" }, - { 0x05, "Alarm Status Type" }, - { 0x06, "BCC" }, - { 0x07, "BS_AG_BKS_RES" }, - { 0x09, "BSIC" }, - { 0x0a, "BA_PA_MFRMS" }, - { 0x0b, "CBCH Indicator" }, - { 0x0c, "CCCH Options" }, - { 0x0d, "Calendar Time" }, - { 0x0f, "Channel Combination" }, - { 0x10, "CON Connection List" }, - { 0x11, "Data End Indication" }, - { 0x12, "DRX_DEV_MAX" }, - { 0x13, "End List Number" }, - { 0x14, "External Condition Map Class 1" }, - { 0x15, "External Condition Map Class 2" }, - { 0x16, "File Relation Indication" }, - { 0x17, "File Revision" }, - { 0x18, "File Segment Data" }, - { 0x19, "File Segment Length" }, - { 0x1a, "File Segment Sequence Number" }, - { 0x1b, "File Size" }, - { 0x1c, "Filling Marker" }, - { 0x1d, "FN Offset" }, - { 0x1e, "Frequency List" }, - { 0x1f, "Frequency Specifier RX" }, - { 0x20, "Frequency Specifier TX" }, - { 0x21, "HSN" }, - { 0x22, "ICM Indicator" }, - { 0x23, "Internal Fault Map Class 1A" }, - { 0x24, "Internal Fault Map Class 1B" }, - { 0x25, "Internal Fault Map Class 2A" }, - { 0x26, "Internal Fault Map Class 2A Extension" }, - { 0x27, "IS Connection List" }, - { 0x28, "List Number" }, - { 0x29, "File Package State Indication" }, - { 0x2a, "Local Access State" }, - { 0x2b, "MAIO" }, - { 0x2c, "MO State" }, - { 0x2d, "Ny1" }, - { 0x2e, "Operational Information" }, - { 0x2f, "Power" }, - { 0x30, "RU Position Data" }, - { 0x31, "Protocol Error" }, - { 0x32, "Reason Code" }, - { 0x33, "Receiver Diversity" }, - { 0x34, "Replacement Unit Map" }, - { 0x35, "Result Code" }, - { 0x36, "RU Revision Data" }, - { 0x38, "T3105" }, - { 0x39, "Test Loop Setting" }, - { 0x3a, "TF Mode" }, - { 0x3b, "TF Compensation Value" }, - { 0x3c, "Time Slot Number" }, - { 0x3d, "TSC" }, - { 0x3e, "RU Logical Id" }, - { 0x3f, "RU Serial Number Data" }, - { 0x40, "BTS Version" }, - { 0x41, "OML IWD Version" }, - { 0x42, "RWL IWD Version" }, - { 0x43, "OML Function Map 1" }, - { 0x44, "OML Function Map 2" }, - { 0x45, "RSL Function Map 1" }, - { 0x46, "RSL Function Map 2" }, - { 0x47, "Extended Range Indicator" }, - { 0x48, "Request Indicators" }, - { 0x49, "DIP Alarm Condition Map" }, - { 0x4a, "ES Incoming" }, - { 0x4b, "ES Outgoing" }, - { 0x4e, "SES Incoming" }, - { 0x4f, "SES Outgoing" }, - { 0x50, "Replacement Unit Map Extension" }, - { 0x52, "UAS Incoming" }, - { 0x53, "UAS Outgoing" }, - { 0x58, "DF Incoming" }, - { 0x5a, "DF Outgoing" }, - { 0x5c, "SF" }, - { 0x60, "S Bits Setting" }, - { 0x61, "CRC-4 Use Option" }, - { 0x62, "T Parameter" }, - { 0x63, "N Parameter" }, - { 0x64, "N1 Parameter" }, - { 0x65, "N3 Parameter" }, - { 0x66, "N4 Parameter" }, - { 0x67, "P Parameter" }, - { 0x68, "Q Parameter" }, - { 0x69, "BI_Q1" }, - { 0x6a, "BI_Q2" }, - { 0x74, "ICM Boundary Parameters" }, - { 0x77, "AFT" }, - { 0x78, "AFT RAI" }, - { 0x79, "Link Supervision Control" }, - { 0x7a, "Link Supervision Filtering Time" }, - { 0x7b, "Call Supervision Time" }, - { 0x7c, "Interval Length UAS Incoming" }, - { 0x7d, "Interval Length UAS Outgoing" }, - { 0x7e, "ICM Channel Rate" }, - { 0x7f, "Attribute Identifier" }, - { 0x80, "FM Frequency List" }, - { 0x81, "FM Frequency Report" }, - { 0x82, "FM Percentile" }, - { 0x83, "FM Clear Indication" }, - { 0x84, "HW Info Signature" }, - { 0x85, "MO Record" }, - { 0x86, "TF Synchronisation Source" }, - { 0x87, "TTA" }, - { 0x88, "End Segment Number" }, - { 0x89, "Segment Number" }, - { 0x8a, "Capabilities Signature" }, - { 0x8c, "File Relation List" }, - { 0x90, "Negotiation Record I" }, - { 0x91, "Negotiation Record II" }, - { 0x92, "Encryption Algorithm" }, - { 0x94, "Interference Rejection Combining" }, - { 0x95, "Dedication Information" }, - { 0x97, "Feature Code" }, - { 0x98, "FS Offset" }, - { 0x99, "ESB Timeslot" }, - { 0x9a, "Master TG Instance" }, - { 0x9b, "Master TX Chain Delay" }, - { 0x9c, "External Condition Class 2 Extension" }, - { 0x9d, "TSs MO State" }, - { 0, NULL } -}; - -const struct value_string om2k_mo_class_short_vals[] = { - { 0x01, "TRXC" }, - { 0x03, "TS" }, - { 0x04, "TF" }, - { 0x05, "IS" }, - { 0x06, "CON" }, - { 0x07, "DP" }, - { 0x0a, "CF" }, - { 0x0b, "TX" }, - { 0x0c, "RX" }, - { 0, NULL } -}; - -static struct msgb *om2k_msgb_alloc(void) -{ - return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, - "OM2000"); -} - -static int abis_om2k_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len) -{ - return tlv_parse(tp, &om2k_att_tlvdef, buf, len, 0, 0); -} - -static int abis_om2k_msg_tlv_parse(struct tlv_parsed *tp, struct abis_om2k_hdr *oh) -{ - return abis_om2k_tlv_parse(tp, oh->data, oh->om.length - 6); -} - -static char *om2k_mo_name(const struct abis_om2k_mo *mo) -{ - static char mo_buf[64]; - - memset(mo_buf, 0, sizeof(mo_buf)); - snprintf(mo_buf, sizeof(mo_buf), "%s/%02x/%02x/%02x", - get_value_string(om2k_mo_class_short_vals, mo->class), - mo->bts, mo->assoc_so, mo->inst); - return mo_buf; -} - -/* resolve the gsm_nm_state data structure for a given MO */ -static struct gsm_nm_state * -mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - struct gsm_bts_trx *trx; - struct gsm_nm_state *nm_state = NULL; - - switch (mo->class) { - case OM2K_MO_CLS_TRXC: - trx = gsm_bts_trx_num(bts, mo->assoc_so); - if (!trx) - return NULL; - nm_state = &trx->mo.nm_state; - break; - case OM2K_MO_CLS_TS: - trx = gsm_bts_trx_num(bts, mo->assoc_so); - if (!trx) - return NULL; - if (mo->inst >= ARRAY_SIZE(trx->ts)) - return NULL; - nm_state = &trx->ts[mo->inst].mo.nm_state; - break; - case OM2K_MO_CLS_TF: - nm_state = &bts->rbs2000.tf.mo.nm_state; - break; - case OM2K_MO_CLS_IS: - nm_state = &bts->rbs2000.is.mo.nm_state; - break; - case OM2K_MO_CLS_CON: - nm_state = &bts->rbs2000.con.mo.nm_state; - break; - case OM2K_MO_CLS_DP: - nm_state = &bts->rbs2000.con.mo.nm_state; - break; - case OM2K_MO_CLS_CF: - nm_state = &bts->mo.nm_state; - break; - case OM2K_MO_CLS_TX: - trx = gsm_bts_trx_num(bts, mo->assoc_so); - if (!trx) - return NULL; - break; - case OM2K_MO_CLS_RX: - trx = gsm_bts_trx_num(bts, mo->assoc_so); - if (!trx) - return NULL; - break; - } - - return nm_state; -} - -static void *mo2obj(struct gsm_bts *bts, struct abis_om2k_mo *mo) -{ - struct gsm_bts_trx *trx; - - switch (mo->class) { - case OM2K_MO_CLS_TX: - case OM2K_MO_CLS_RX: - case OM2K_MO_CLS_TRXC: - return gsm_bts_trx_num(bts, mo->assoc_so); - case OM2K_MO_CLS_TS: - trx = gsm_bts_trx_num(bts, mo->assoc_so); - if (!trx) - return NULL; - if (mo->inst >= ARRAY_SIZE(trx->ts)) - return NULL; - return &trx->ts[mo->inst]; - case OM2K_MO_CLS_TF: - case OM2K_MO_CLS_IS: - case OM2K_MO_CLS_CON: - case OM2K_MO_CLS_DP: - case OM2K_MO_CLS_CF: - return bts; - } - - return NULL; -} - -static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, - uint8_t mo_state) -{ - struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); - struct gsm_nm_state new_state; - struct nm_statechg_signal_data nsd; - - if (!nm_state) - return; - - new_state = *nm_state; - /* NOTICE: 12.21 Availability state values != OM2000 */ - new_state.availability = mo_state; - - memset(&nsd, 0, sizeof(nsd)); - - nsd.bts = bts; - nsd.obj = mo2obj(bts, mo); - nsd.old_state = nm_state; - nsd.new_state = &new_state; - nsd.om2k_mo = mo; - - osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); - - nm_state->availability = new_state.availability; -} - -static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t op_state) -{ - struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); - struct gsm_nm_state new_state; - - if (!nm_state) - return; - - new_state = *nm_state; - switch (op_state) { - case 1: - new_state.operational = NM_OPSTATE_ENABLED; - break; - case 0: - new_state.operational = NM_OPSTATE_DISABLED; - break; - default: - new_state.operational = NM_OPSTATE_NULL; - break; - } - - nm_state->operational = new_state.operational; -} - -static void signal_op_state(struct gsm_bts *bts, struct abis_om2k_mo *mo) -{ - struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); - struct nm_statechg_signal_data nsd; - - if (!nm_state) - return; - - nsd.bts = bts; - nsd.obj = mo2obj(bts, mo); - nsd.old_state = nm_state; - nsd.new_state = nm_state; - nsd.om2k_mo = mo; - - osmo_signal_dispatch(SS_NM, S_NM_STATECHG_OPER, &nsd); -} - -static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) -{ - struct abis_om2k_hdr *o2h; - struct gsm_bts_trx *trx; - - msg->l2h = msg->data; - o2h = (struct abis_om2k_hdr *) msg->l2h; - - /* Compute the length in the OML header */ - o2h->om.length = 6 + msgb_l2len(msg)-sizeof(*o2h); - - switch (o2h->mo.class) { - case OM2K_MO_CLS_TRXC: - case OM2K_MO_CLS_TX: - case OM2K_MO_CLS_RX: - /* Route through per-TRX OML Link to the appropriate TRX */ - trx = gsm_bts_trx_by_nr(bts, o2h->mo.inst); - if (!trx) { - LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " - "non-existing TRX\n", om2k_mo_name(&o2h->mo)); - return -ENODEV; - } - msg->dst = trx->oml_link; - break; - case OM2K_MO_CLS_TS: - /* Route through per-TRX OML Link to the appropriate TRX */ - trx = gsm_bts_trx_by_nr(bts, o2h->mo.assoc_so); - if (!trx) { - LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " - "non-existing TRX\n", om2k_mo_name(&o2h->mo)); - return -ENODEV; - } - msg->dst = trx->oml_link; - break; - default: - /* Route through the IXU/DXU OML Link */ - msg->dst = bts->oml_link; - break; - } - - return _abis_nm_sendmsg(msg); -} - -static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo, - uint16_t msg_type) -{ - o2h->om.mdisc = ABIS_OM_MDISC_FOM; - o2h->om.placement = ABIS_OM_PLACEMENT_ONLY; - o2h->om.sequence = 0; - /* We fill o2h->om.length later during om2k_sendmsg() */ - o2h->msg_type = htons(msg_type); - memcpy(&o2h->mo, mo, sizeof(o2h->mo)); -} - -const struct abis_om2k_mo om2k_mo_cf = { OM2K_MO_CLS_CF, 0, 0xFF, 0 }; -const struct abis_om2k_mo om2k_mo_is = { OM2K_MO_CLS_IS, 0, 0xFF, 0 }; -const struct abis_om2k_mo om2k_mo_con = { OM2K_MO_CLS_CON, 0, 0xFF, 0 }; -const struct abis_om2k_mo om2k_mo_tf = { OM2K_MO_CLS_TF, 0, 0xFF, 0 }; - -static int abis_om2k_cal_time_resp(struct gsm_bts *bts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - time_t tm_t; - struct tm *tm; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &om2k_mo_cf, OM2K_MSGT_CAL_TIME_RESP); - - tm_t = time(NULL); - tm = localtime(&tm_t); - - msgb_put_u8(msg, OM2K_DEI_CAL_TIME); - msgb_put_u8(msg, tm->tm_year % 100); - msgb_put_u8(msg, tm->tm_mon + 1); - msgb_put_u8(msg, tm->tm_mday); - msgb_put_u8(msg, tm->tm_hour); - msgb_put_u8(msg, tm->tm_min); - msgb_put_u8(msg, tm->tm_sec); - - return abis_om2k_sendmsg(bts, msg); -} - -static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t msg_type) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, mo, msg_type); - - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), - get_value_string(om2k_msgcode_vals, msg_type)); - - return abis_om2k_sendmsg(bts, msg); -} - -int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_RESET_CMD); -} - -int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_START_REQ); -} - -int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_STATUS_REQ); -} - -int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_CONNECT_CMD); -} - -int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISCONNECT_CMD); -} - -int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_TEST_REQ); -} - -int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_ENABLE_REQ); -} - -int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISABLE_REQ); -} - -int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t operational) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, mo, OM2K_MSGT_OP_INFO); - - msgb_tv_put(msg, OM2K_DEI_OP_INFO, operational); - - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_OP_INFO)); - - /* we update the state here... and send the signal at ACK */ - update_op_state(bts, mo, operational); - - return abis_om2k_sendmsg(bts, msg); -} - -static void om2k_fill_is_conn_grp(struct om2k_is_conn_grp *grp, uint16_t icp1, - uint16_t icp2, uint8_t cont_idx) -{ - grp->icp1 = htons(icp1); - grp->icp2 = htons(icp2); - grp->cont_idx = cont_idx; -} - -int abis_om2k_tx_is_conf_req(struct gsm_bts *bts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct is_conn_group *grp; - unsigned int num_grps = 0, i = 0; - struct om2k_is_conn_grp *cg; - - /* count number of groups in linked list */ - llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) - num_grps++; - - if (!num_grps) - return -EINVAL; - - /* allocate buffer for oml group array */ - cg = talloc_zero_array(bts, struct om2k_is_conn_grp, num_grps); - - /* fill array with data from linked list */ - llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) - om2k_fill_is_conn_grp(&cg[i++], grp->icp1, grp->icp2, grp->ci); - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &om2k_mo_is, OM2K_MSGT_IS_CONF_REQ); - - msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); - msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); - - msgb_tlv_put(msg, OM2K_DEI_IS_CONN_LIST, - num_grps * sizeof(*cg), (uint8_t *)cg); - - talloc_free(cg); - - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&om2k_mo_is), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_IS_CONF_REQ)); - - return abis_om2k_sendmsg(bts, msg); -} - -int abis_om2k_tx_con_conf_req(struct gsm_bts *bts, uint8_t *data, - unsigned int len) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &om2k_mo_con, OM2K_MSGT_CON_CONF_REQ); - - msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); - msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); - - msgb_tlv_put(msg, OM2K_DEI_CON_CONN_LIST, len, data); - - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&om2k_mo_con), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_CON_CONF_REQ)); - - return abis_om2k_sendmsg(bts, msg); -} - -static void om2k_trx_to_mo(struct abis_om2k_mo *mo, - const struct gsm_bts_trx *trx, - enum abis_om2k_mo_cls cls) -{ - mo->class = cls; - mo->bts = 0; - mo->inst = trx->nr; - mo->assoc_so = 255; -} - -static void om2k_ts_to_mo(struct abis_om2k_mo *mo, - const struct gsm_bts_trx_ts *ts) -{ - mo->class = OM2K_MO_CLS_TS; - mo->bts = 0; - mo->inst = ts->nr; - mo->assoc_so = ts->trx->nr; -} - -/* Configure a Receiver MO */ -int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct abis_om2k_mo mo; - - om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_RX); - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &mo, OM2K_MSGT_RX_CONF_REQ); - - msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_RX, trx->arfcn); - msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ - - return abis_om2k_sendmsg(trx->bts, msg); -} - -/* Configure a Transmitter MO */ -int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct abis_om2k_mo mo; - - om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_TX); - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TX_CONF_REQ); - - msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_TX, trx->arfcn); - msgb_tv_put(msg, OM2K_DEI_POWER, trx->nominal_power-trx->max_power_red); - msgb_tv_put(msg, OM2K_DEI_FILLING_MARKER, 0); /* Filling enabled */ - msgb_tv_put(msg, OM2K_DEI_BCC, trx->bts->bsic & 0x7); - /* Dedication Information is optional */ - - return abis_om2k_sendmsg(trx->bts, msg); -} - -enum abis_om2k_tf_mode { - OM2K_TF_MODE_MASTER = 0x00, - OM2K_TF_MODE_STANDALONE = 0x01, - OM2K_TF_MODE_SLAVE = 0x02, - OM2K_TF_MODE_UNDEFINED = 0xff, -}; - -static const uint8_t fs_offset_undef[5] = { 0xff, 0xff, 0xff, 0xff, 0xff }; - -int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &om2k_mo_tf, OM2K_MSGT_TF_CONF_REQ); - - msgb_tv_put(msg, OM2K_DEI_TF_MODE, OM2K_TF_MODE_STANDALONE); - msgb_tv_put(msg, OM2K_DEI_TF_SYNC_SRC, 0x00); - msgb_tv_fixed_put(msg, OM2K_DEI_FS_OFFSET, - sizeof(fs_offset_undef), fs_offset_undef); - - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&om2k_mo_tf), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_TF_CONF_REQ)); - - return abis_om2k_sendmsg(bts, msg); -} - -static uint8_t pchan2comb(enum gsm_phys_chan_config pchan) -{ - switch (pchan) { - case GSM_PCHAN_CCCH: - return 4; - case GSM_PCHAN_CCCH_SDCCH4: - return 5; - case GSM_PCHAN_SDCCH8_SACCH8C: - return 3; - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - case GSM_PCHAN_PDCH: - case GSM_PCHAN_TCH_F_PDCH: - return 8; - default: - return 0; - } -} - -static int put_freq_list(uint8_t *buf, uint16_t arfcn) -{ - buf[0] = 0x00; /* TX/RX address */ - buf[1] = (arfcn >> 8); - buf[2] = (arfcn & 0xff); - - return 3; -} - -/* Compute a frequency list in OM2000 fomrmat */ -static int om2k_gen_freq_list(uint8_t *list, struct gsm_bts_trx_ts *ts) -{ - uint8_t *cur = list; - int len; - - if (ts->hopping.enabled) { - unsigned int i; - for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { - if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) - cur += put_freq_list(cur, i); - } - } else - cur += put_freq_list(cur, ts->trx->arfcn); - - len = cur - list; - - return len; -} - -const uint8_t icm_bound_params[] = { 0x02, 0x06, 0x0c, 0x16, 0x06 }; - -int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct abis_om2k_mo mo; - uint8_t freq_list[64*3]; /* BA max size: 64 ARFCN */ - int freq_list_len; - - om2k_ts_to_mo(&mo, ts); - - memset(freq_list, 0, sizeof(freq_list)); - freq_list_len = om2k_gen_freq_list(freq_list, ts); - if (freq_list_len < 0) - return freq_list_len; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TS_CONF_REQ); - - msgb_tv_put(msg, OM2K_DEI_COMBINATION, pchan2comb(ts->pchan)); - msgb_tv_put(msg, OM2K_DEI_TS_NR, ts->nr); - msgb_tlv_put(msg, OM2K_DEI_FREQ_LIST, freq_list_len, freq_list); - msgb_tv_put(msg, OM2K_DEI_HSN, ts->hopping.hsn); - msgb_tv_put(msg, OM2K_DEI_MAIO, ts->hopping.maio); - msgb_tv_put(msg, OM2K_DEI_BSIC, ts->trx->bts->bsic); - msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ - msgb_tv16_put(msg, OM2K_DEI_FN_OFFSET, 0); - msgb_tv_put(msg, OM2K_DEI_EXT_RANGE, 0); /* Off */ - /* Optional: Interference Rejection Combining */ - msgb_tv_put(msg, OM2K_DEI_INTERF_REJ_COMB, 0x00); - switch (ts->pchan) { - case GSM_PCHAN_CCCH: - msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); - msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01); - msgb_tv_put(msg, OM2K_DEI_DRX_DEV_MAX, 0x05); - /* Repeat Paging/IMM.ASS: True, Allow Paging Type 3: Yes, Page for 5 seconds (default) */ - msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01); - break; - case GSM_PCHAN_CCCH_SDCCH4: - msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); - msgb_tv_put(msg, OM2K_DEI_NY1, 35); - msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); - msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0); - msgb_tv_put(msg, OM2K_DEI_TSC, ts->trx->bts->tsc); - 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, ts->trx->bts->tsc); - /* 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, ts->trx->bts->tsc); - /* Disable RF RESOURCE INDICATION on idle channels */ - msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); - msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, - sizeof(icm_bound_params), icm_bound_params); - msgb_tv_put(msg, OM2K_DEI_TTA, 10); /* Timer for Time Alignment */ - if (ts->pchan == GSM_PCHAN_TCH_H) - msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 1); /* TCH/H */ - else - msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 0); /* TCH/F */ - msgb_tv_put(msg, OM2K_DEI_LSC, 1); /* enabled */ - msgb_tv_put(msg, OM2K_DEI_LSC_FILT_TIME, 10); /* units of 100ms */ - msgb_tv_put(msg, OM2K_DEI_CALL_SUPV_TIME, 8); - msgb_tv_put(msg, OM2K_DEI_ENCR_ALG, 0x00); - break; - } - - return abis_om2k_sendmsg(ts->trx->bts, msg); -} - -static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t *data, unsigned int len) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, mo, OM2K_MSGT_NEGOT_REQ_ACK); - - msgb_tlv_put(msg, OM2K_DEI_NEGOT_REC2, len, data); - - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_NEGOT_REQ_ACK)); - - return abis_om2k_sendmsg(bts, msg); -} - -struct iwd_version { - uint8_t gen_char[3+1]; - uint8_t rev_char[3+1]; -}; - -struct iwd_type { - uint8_t num_vers; - struct iwd_version v[8]; -}; - -static int om2k_rx_negot_req(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; - struct abis_om2k_hdr *o2h = msgb_l2(msg); - struct iwd_type iwd_types[16]; - uint8_t num_iwd_types = o2h->data[2]; - uint8_t *cur = o2h->data+3; - unsigned int i, v; - - uint8_t out_buf[1024]; - uint8_t *out_cur = out_buf+1; - uint8_t out_num_types = 0; - - memset(iwd_types, 0, sizeof(iwd_types)); - - /* Parse the RBS-supported IWD versions into iwd_types array */ - for (i = 0; i < num_iwd_types; i++) { - uint8_t num_versions = *cur++; - uint8_t iwd_type = *cur++; - - iwd_types[iwd_type].num_vers = num_versions; - - for (v = 0; v < num_versions; v++) { - struct iwd_version *iwd_v = &iwd_types[iwd_type].v[v]; - - memcpy(iwd_v->gen_char, cur, 3); - cur += 3; - memcpy(iwd_v->rev_char, cur, 3); - cur += 3; - - DEBUGP(DNM, "\tIWD Type %u Gen %s Rev %s\n", iwd_type, - iwd_v->gen_char, iwd_v->rev_char); - } - } - - /* Select the last version for each IWD type */ - for (i = 0; i < ARRAY_SIZE(iwd_types); i++) { - struct iwd_type *type = &iwd_types[i]; - struct iwd_version *last_v; - - if (type->num_vers == 0) - continue; - - out_num_types++; - - last_v = &type->v[type->num_vers-1]; - - *out_cur++ = i; - memcpy(out_cur, last_v->gen_char, 3); - out_cur += 3; - memcpy(out_cur, last_v->rev_char, 3); - out_cur += 3; - } - - out_buf[0] = out_num_types; - - return abis_om2k_tx_negot_req_ack(sign_link->trx->bts, &o2h->mo, out_buf, out_cur - out_buf); -} - -static int om2k_rx_start_res(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; - struct abis_om2k_hdr *o2h = msgb_l2(msg); - int rc; - - rc = abis_om2k_tx_simple(sign_link->trx->bts, &o2h->mo, OM2K_MSGT_START_RES_ACK); - rc = abis_om2k_tx_op_info(sign_link->trx->bts, &o2h->mo, 1); - - return rc; -} - -static int om2k_rx_op_info_ack(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; - struct abis_om2k_hdr *o2h = msgb_l2(msg); - - /* This Acknowledgement does not contain the actual operational state, - * so we signal whatever state we saved when we sent the Op Info - * request */ - - signal_op_state(sign_link->trx->bts, &o2h->mo); - - return 0; -} - -const struct value_string om2k_result_strings[] = { - { 0x02, "Wrong state or out of sequence" }, - { 0x03, "File error" }, - { 0x04, "Fault, unspecified" }, - { 0x05, "Tuning fault" }, - { 0x06, "Protocol error" }, - { 0x07, "MO not connected" }, - { 0x08, "Parameter error" }, - { 0x09, "Optional function not supported" }, - { 0x0a, "Local access state LOCALLY DISCONNECTED" }, - { 0, NULL } -}; - -const struct value_string om2k_accordance_strings[] = { - { 0x00, "Data according to request" }, - { 0x01, "Data not according to request" }, - { 0x02, "Inconsistent MO data" }, - { 0x03, "Capability constraint violation" }, - { 0, NULL } -}; - -const struct value_string om2k_mostate_vals[] = { - { 0x00, "RESET" }, - { 0x01, "STARTED" }, - { 0x02, "ENABLED" }, - { 0x03, "DISABLED" }, - { 0, NULL } -}; - -static int om2k_rx_nack(struct msgb *msg) -{ - struct abis_om2k_hdr *o2h = msgb_l2(msg); - uint16_t msg_type = ntohs(o2h->msg_type); - struct tlv_parsed tp; - - LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s", om2k_mo_name(&o2h->mo), - get_value_string(om2k_msgcode_vals, msg_type)); - - abis_om2k_msg_tlv_parse(&tp, o2h); - if (TLVP_PRESENT(&tp, OM2K_DEI_REASON_CODE)) - LOGPC(DNM, LOGL_ERROR, ", Reason 0x%02x", - *TLVP_VAL(&tp, OM2K_DEI_REASON_CODE)); - - if (TLVP_PRESENT(&tp, OM2K_DEI_RESULT_CODE)) - LOGPC(DNM, LOGL_ERROR, ", Result %s", - get_value_string(om2k_result_strings, - *TLVP_VAL(&tp, OM2K_DEI_RESULT_CODE))); - LOGPC(DNM, LOGL_ERROR, "\n"); - - return 0; -} - -/* Process a Configuration Result message */ -static int process_conf_res(struct gsm_bts *bts, struct msgb *msg) -{ - struct abis_om2k_hdr *o2h = msgb_l2(msg); - uint16_t msg_type = ntohs(o2h->msg_type); - struct nm_om2k_signal_data nsd; - struct tlv_parsed tp; - uint8_t acc; - unsigned int log_level; - int ret; - - memset(&nsd, 0, sizeof(nsd)); - - abis_om2k_msg_tlv_parse(&tp, o2h); - if (!TLVP_PRESENT(&tp, OM2K_DEI_ACCORDANCE_IND)) - return -EIO; - - acc = *TLVP_VAL(&tp, OM2K_DEI_ACCORDANCE_IND); - - switch (acc) { - case 0: - log_level = LOGL_DEBUG; - ret = 0; - break; - default: - log_level = LOGL_ERROR; - ret = -EINVAL; - break; - } - - LOGP(DNM, log_level, "Rx MO=%s %s, Accordance: %s\n", - om2k_mo_name(&o2h->mo), - get_value_string(om2k_msgcode_vals, msg_type), - get_value_string(om2k_accordance_strings, acc)); - - nsd.bts = bts; - nsd.obj = mo2obj(bts, &o2h->mo); - nsd.om2k_mo = &o2h->mo; - nsd.accordance_ind = acc; - osmo_signal_dispatch(SS_NM, S_NM_OM2K_CONF_RES, &nsd); - - return ret; -} - -static int process_mo_state(struct gsm_bts *bts, struct msgb *msg) -{ - struct abis_om2k_hdr *o2h = msgb_l2(msg); - uint16_t msg_type = ntohs(o2h->msg_type); - struct tlv_parsed tp; - uint8_t mo_state; - - abis_om2k_msg_tlv_parse(&tp, o2h); - if (!TLVP_PRESENT(&tp, OM2K_DEI_MO_STATE)) - return -EIO; - mo_state = *TLVP_VAL(&tp, OM2K_DEI_MO_STATE); - - LOGP(DNM, LOGL_DEBUG, "Rx MO=%s %s, MO State: %s\n", - om2k_mo_name(&o2h->mo), - get_value_string(om2k_msgcode_vals, msg_type), - get_value_string(om2k_mostate_vals, mo_state)); - - update_mo_state(bts, &o2h->mo, mo_state); - - return 0; -} - -int abis_om2k_rcvmsg(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct abis_om2k_hdr *o2h = msgb_l2(msg); - struct abis_om_hdr *oh = &o2h->om; - uint16_t msg_type = ntohs(o2h->msg_type); - int rc = 0; - - /* Various consistency checks */ - if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { - LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", - oh->placement); - if (oh->placement != ABIS_OM_PLACEMENT_FIRST) - return -EINVAL; - } - if (oh->sequence != 0) { - LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", - oh->sequence); - return -EINVAL; - } - - msg->l3h = (unsigned char *)o2h + sizeof(*o2h); - - if (oh->mdisc != ABIS_OM_MDISC_FOM) { - LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n", - oh->mdisc); - return -EINVAL; - } - - DEBUGP(DNM, "Rx MO=%s %s (%s)\n", om2k_mo_name(&o2h->mo), - get_value_string(om2k_msgcode_vals, msg_type), - osmo_hexdump(msg->l2h, msgb_l2len(msg))); - - switch (msg_type) { - case OM2K_MSGT_CAL_TIME_REQ: - rc = abis_om2k_cal_time_resp(bts); - break; - case OM2K_MSGT_FAULT_REP: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK); - process_mo_state(bts, msg); - break; - case OM2K_MSGT_NEGOT_REQ: - rc = om2k_rx_negot_req(msg); - break; - case OM2K_MSGT_START_RES: - rc = om2k_rx_start_res(msg); - process_mo_state(bts, msg); - break; - case OM2K_MSGT_OP_INFO_ACK: - rc = om2k_rx_op_info_ack(msg); - break; - case OM2K_MSGT_IS_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_IS_CONF_RES_ACK); - process_conf_res(bts, msg); - break; - case OM2K_MSGT_CON_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CON_CONF_RES_ACK); - process_conf_res(bts, msg); - break; - case OM2K_MSGT_TX_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TX_CONF_RES_ACK); - process_conf_res(bts, msg); - break; - case OM2K_MSGT_RX_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RX_CONF_RES_ACK); - process_conf_res(bts, msg); - break; - case OM2K_MSGT_TS_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TS_CONF_RES_ACK); - process_conf_res(bts, msg); - break; - case OM2K_MSGT_TF_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TF_CONF_RES_ACK); - process_conf_res(bts, msg); - break; - case OM2K_MSGT_CONNECT_COMPL: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RESET_CMD); - break; - case OM2K_MSGT_RESET_COMPL: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_REQ); - break; - case OM2K_MSGT_ENABLE_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_ENABLE_RES_ACK); - process_mo_state(bts, msg); - break; - case OM2K_MSGT_DISABLE_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_DISABLE_RES_ACK); - process_mo_state(bts, msg); - break; - case OM2K_MSGT_TEST_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TEST_RES_ACK); - process_mo_state(bts, msg); - break; - case OM2K_MSGT_STATUS_RESP: - process_mo_state(bts, msg); - break; - case OM2K_MSGT_START_REQ_ACK: - case OM2K_MSGT_CON_CONF_REQ_ACK: - case OM2K_MSGT_IS_CONF_REQ_ACK: - case OM2K_MSGT_TX_CONF_REQ_ACK: - case OM2K_MSGT_RX_CONF_REQ_ACK: - case OM2K_MSGT_TS_CONF_REQ_ACK: - case OM2K_MSGT_TF_CONF_REQ_ACK: - case OM2K_MSGT_ENABLE_REQ_ACK: - case OM2K_MSGT_ALARM_STATUS_REQ_ACK: - case OM2K_MSGT_DISABLE_REQ_ACK: - break; - case OM2K_MSGT_START_REQ_REJ: - case OM2K_MSGT_CONNECT_REJ: - case OM2K_MSGT_OP_INFO_REJ: - case OM2K_MSGT_DISCONNECT_REJ: - case OM2K_MSGT_TEST_REQ_REJ: - case OM2K_MSGT_CON_CONF_REQ_REJ: - case OM2K_MSGT_IS_CONF_REQ_REJ: - case OM2K_MSGT_TX_CONF_REQ_REJ: - case OM2K_MSGT_RX_CONF_REQ_REJ: - case OM2K_MSGT_TS_CONF_REQ_REJ: - case OM2K_MSGT_TF_CONF_REQ_REJ: - case OM2K_MSGT_ENABLE_REQ_REJ: - case OM2K_MSGT_ALARM_STATUS_REQ_REJ: - case OM2K_MSGT_DISABLE_REQ_REJ: - rc = om2k_rx_nack(msg); - break; - default: - LOGP(DNM, LOGL_NOTICE, "Rx unhandled OM2000 msg %s\n", - get_value_string(om2k_msgcode_vals, msg_type)); - } - - msgb_free(msg); - return rc; -} diff --git a/openbsc/src/libbsc/abis_om2000_vty.c b/openbsc/src/libbsc/abis_om2000_vty.c deleted file mode 100644 index 3df005ba0..000000000 --- a/openbsc/src/libbsc/abis_om2000_vty.c +++ /dev/null @@ -1,466 +0,0 @@ -/* VTY interface for A-bis OM2000 */ - -/* (C) 2010-2011 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -extern struct gsm_network *bsc_gsmnet; - -static struct cmd_node om2k_node = { - OM2K_NODE, - "%s(om2k)# ", - 1, -}; - -struct oml_node_state { - struct gsm_bts *bts; - struct abis_om2k_mo mo; -}; - -static int dummy_config_write(struct vty *v) -{ - return CMD_SUCCESS; -} - -/* FIXME: auto-generate those strings from the value_string lists */ -#define OM2K_OBJCLASS_VTY "(trxc|ts|tf|is|con|dp|cf|tx|rx)" -#define OM2K_OBJCLASS_VTY_HELP "TRX Controller\n" \ - "Timeslot\n" \ - "Timing Function\n" \ - "Interface Switch\n" \ - "Abis Concentrator\n" \ - "Digital Path\n" \ - "Central Function\n" \ - "Transmitter\n" \ - "Receiver\n" - -DEFUN(om2k_class_inst, om2k_class_inst_cmd, - "bts <0-255> om2000 class " OM2K_OBJCLASS_VTY - " <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OM2000 managed objects\n" - "Object Class\n" OM2K_OBJCLASS_VTY_HELP - "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(bsc_gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (bts->type != GSM_BTS_TYPE_RBS2000) { - vty_out(vty, "%% BTS %d not an Ericsson RBS%s", - bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->mo.class = get_string_value(om2k_mo_class_short_vals, argv[1]); - oms->mo.bts = atoi(argv[2]); - oms->mo.assoc_so = atoi(argv[3]); - oms->mo.inst = atoi(argv[4]); - - vty->index = oms; - vty->node = OM2K_NODE; - - return CMD_SUCCESS; - -} - -DEFUN(om2k_classnum_inst, om2k_classnum_inst_cmd, - "bts <0-255> om2000 class <0-255> <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OML managed objects\n" - "Object Class\n" "Object Class\n" - "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(bsc_gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->mo.class = atoi(argv[1]); - oms->mo.bts = atoi(argv[2]); - oms->mo.assoc_so = atoi(argv[3]); - oms->mo.inst = atoi(argv[4]); - - vty->index = oms; - vty->node = OM2K_NODE; - - return CMD_SUCCESS; -} - -DEFUN(om2k_reset, om2k_reset_cmd, - "reset-command", - "Reset the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_reset_cmd(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_start, om2k_start_cmd, - "start-request", - "Start the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_start_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_status, om2k_status_cmd, - "status-request", - "Get the MO Status\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_status_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_connect, om2k_connect_cmd, - "connect-command", - "Connect the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_connect_cmd(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_disconnect, om2k_disconnect_cmd, - "disconnect-command", - "Disconnect the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_disconnect_cmd(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_enable, om2k_enable_cmd, - "enable-request", - "Enable the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_enable_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_disable, om2k_disable_cmd, - "disable-request", - "Disable the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_disable_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_op_info, om2k_op_info_cmd, - "operational-info <0-1>", - "Set operational information\n" - "Set operational info to 0 or 1\n") -{ - struct oml_node_state *oms = vty->index; - int oper = atoi(argv[0]); - - abis_om2k_tx_op_info(oms->bts, &oms->mo, oper); - return CMD_SUCCESS; -} - -DEFUN(om2k_test, om2k_test_cmd, - "test-request", - "Test the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_test_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -struct con_conn_group { - struct llist_head list; - - uint8_t cg; - uint16_t ccp; - uint8_t tag; - uint8_t tei; -}; - -static void add_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp, - uint8_t tag, uint8_t tei) -{ - struct con_conn_group *ent = talloc_zero(bts, struct con_conn_group); - - ent->cg = cg; - ent->ccp = ccp; - ent->tag = tag; - ent->tei = tei; - - llist_add_tail(&ent->list, &bts->rbs2000.con.conn_groups); -} - -static int del_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp, - uint8_t tag, uint8_t tei) -{ - struct con_conn_group *grp, *grp2; - - llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.con.conn_groups, list) { - if (grp->cg == cg && grp->ccp == ccp && grp->tag == tag - && grp->tei == tei) { - llist_del(&grp->list); - talloc_free(grp); - return 0; - } - } - return -ENOENT; -} - -#define CON_LIST_HELP "CON connetiton list\n" \ - "Add entry to CON list\n" \ - "Delete entry from CON list\n" \ - "Connection Group Number\n" \ - "CON Connection Point\n" \ - -DEFUN(om2k_con_list_dec, om2k_con_list_dec_cmd, - "con-connection-list (add|del) <1-255> <0-1023> deconcentrated", - CON_LIST_HELP "De-concentrated in/outlet\n") -{ - struct oml_node_state *oms = vty->index; - struct gsm_bts *bts = oms->bts; - uint8_t cg = atoi(argv[1]); - uint16_t ccp = atoi(argv[2]); - - if (!strcmp(argv[0], "add")) - add_con_list(bts, cg, ccp, 0, 0xff); - else { - if (del_con_list(bts, cg, ccp, 0, 0xff) < 0) { - vty_out(vty, "%% No matching CON list entry%s", - VTY_NEWLINE); - return CMD_WARNING; - } - } - - return CMD_SUCCESS; -} - -DEFUN(om2k_con_list_tei, om2k_con_list_tei_cmd, - "con-connection-list (add|del) <1-255> <0-1023> tei <0-63>", - CON_LIST_HELP "Concentrated in/outlet with TEI\n" "TEI Number\n") -{ - struct oml_node_state *oms = vty->index; - struct gsm_bts *bts = oms->bts; - uint8_t cg = atoi(argv[1]); - uint16_t ccp = atoi(argv[2]); - uint8_t tei = atoi(argv[3]); - - if (!strcmp(argv[0], "add")) - add_con_list(bts, cg, ccp, cg, tei); - else { - if (del_con_list(bts, cg, ccp, cg, tei) < 0) { - vty_out(vty, "%% No matching CON list entry%s", - VTY_NEWLINE); - return CMD_WARNING; - } - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd, - "is-connection-list (add|del) <0-2047> <0-2047> <0-255>", - "Interface Switch Connnection List\n" - "Add to IS list\n" "Delete from IS list\n" - "ICP1\n" "ICP2\n" "Contiguity Index\n") -{ - struct gsm_bts *bts = vty->index; - uint16_t icp1 = atoi(argv[1]); - uint16_t icp2 = atoi(argv[2]); - uint8_t ci = atoi(argv[3]); - struct is_conn_group *grp, *grp2; - - if (!strcmp(argv[0], "add")) { - grp = talloc_zero(bts, struct is_conn_group); - grp->icp1 = icp1; - grp->icp2 = icp2; - grp->ci = ci; - llist_add_tail(&grp->list, &bts->rbs2000.is.conn_groups); - } else { - llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.is.conn_groups, list) { - if (grp->icp1 == icp1 && grp->icp2 == icp2 - && grp->ci == ci) { - llist_del(&grp->list); - talloc_free(grp); - return CMD_SUCCESS; - } - } - vty_out(vty, "%% No matching IS Conn Group found!%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - - -DEFUN(om2k_conf_req, om2k_conf_req_cmd, - "configuration-request", - "Send the configuration request for current MO\n") -{ - struct oml_node_state *oms = vty->index; - struct gsm_bts *bts = oms->bts; - struct gsm_bts_trx *trx = NULL; - struct gsm_bts_trx_ts *ts = NULL; - - switch (oms->mo.class) { - case OM2K_MO_CLS_IS: - abis_om2k_tx_is_conf_req(bts); - break; - case OM2K_MO_CLS_TS: - trx = gsm_bts_trx_by_nr(bts, oms->mo.assoc_so); - if (!trx) { - vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr, - oms->mo.assoc_so, VTY_NEWLINE); - return CMD_WARNING; - } - if (oms->mo.inst >= ARRAY_SIZE(trx->ts)) { - vty_out(vty, "%% Timeslot %u out of range%s", - oms->mo.inst, VTY_NEWLINE); - return CMD_WARNING; - } - ts = &trx->ts[oms->mo.inst]; - abis_om2k_tx_ts_conf_req(ts); - break; - case OM2K_MO_CLS_RX: - case OM2K_MO_CLS_TX: - case OM2K_MO_CLS_TRXC: - trx = gsm_bts_trx_by_nr(bts, oms->mo.inst); - if (!trx) { - vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr, - oms->mo.inst, VTY_NEWLINE); - return CMD_WARNING; - } - switch (oms->mo.class) { - case OM2K_MO_CLS_RX: - abis_om2k_tx_rx_conf_req(trx); - break; - case OM2K_MO_CLS_TX: - abis_om2k_tx_tx_conf_req(trx); - break; - default: - break; - } - break; - case OM2K_MO_CLS_TF: - abis_om2k_tx_tf_conf_req(bts); - break; - default: - vty_out(vty, "%% Don't know how to configure MO%s", - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts) -{ - struct is_conn_group *igrp; - struct con_conn_group *cgrp; - - llist_for_each_entry(igrp, &bts->rbs2000.is.conn_groups, list) - vty_out(vty, " is-connection-list add %u %u %u%s", - igrp->icp1, igrp->icp2, igrp->ci, VTY_NEWLINE); - - llist_for_each_entry(cgrp, &bts->rbs2000.con.conn_groups, list) { - vty_out(vty, " con-connection-list add %u %u ", - cgrp->cg, cgrp->ccp); - if (cgrp->tei == 0xff) - vty_out(vty, "deconcentrated%s", VTY_NEWLINE); - else - vty_out(vty, "tei %u%s", cgrp->tei, VTY_NEWLINE); - } -} - -int abis_om2k_vty_init(void) -{ - install_element(ENABLE_NODE, &om2k_class_inst_cmd); - install_element(ENABLE_NODE, &om2k_classnum_inst_cmd); - install_node(&om2k_node, dummy_config_write); - - install_default(OM2K_NODE); - install_element(OM2K_NODE, &ournode_exit_cmd); - install_element(OM2K_NODE, &om2k_reset_cmd); - install_element(OM2K_NODE, &om2k_start_cmd); - install_element(OM2K_NODE, &om2k_status_cmd); - install_element(OM2K_NODE, &om2k_connect_cmd); - install_element(OM2K_NODE, &om2k_disconnect_cmd); - install_element(OM2K_NODE, &om2k_enable_cmd); - install_element(OM2K_NODE, &om2k_disable_cmd); - install_element(OM2K_NODE, &om2k_op_info_cmd); - install_element(OM2K_NODE, &om2k_test_cmd); - install_element(OM2K_NODE, &om2k_conf_req_cmd); - install_element(OM2K_NODE, &om2k_con_list_dec_cmd); - install_element(OM2K_NODE, &om2k_con_list_tei_cmd); - - install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd); - - return 0; -} diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c deleted file mode 100644 index 41bfcdc92..000000000 --- a/openbsc/src/libbsc/abis_rsl.c +++ /dev/null @@ -1,2204 +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 - -#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 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 have an error timer pending to release that */ - if (lchan->state != LCHAN_S_REL_ERR) - rsl_lchan_set_state(lchan, LCHAN_S_NONE); - lchan_free(lchan); -} - -static uint8_t mdisc_by_msgtype(uint8_t msg_type) -{ - /* mask off the transparent bit ? */ - msg_type &= 0xfe; - - if ((msg_type & 0xf0) == 0x00) - return ABIS_RSL_MDISC_RLL; - if ((msg_type & 0xf0) == 0x10) { - if (msg_type >= 0x19 && msg_type <= 0x22) - return ABIS_RSL_MDISC_TRX; - else - return ABIS_RSL_MDISC_COM_CHAN; - } - if ((msg_type & 0xe0) == 0x20) - return ABIS_RSL_MDISC_DED_CHAN; - - return ABIS_RSL_MDISC_LOC; -} - -static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh, - uint8_t msg_type) -{ - dh->c.msg_discr = mdisc_by_msgtype(msg_type); - dh->c.msg_type = msg_type; - dh->ie_chan = RSL_IE_CHAN_NR; -} - -/* determine logical channel based on TRX and channel number IE */ -struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr) -{ - struct gsm_lchan *lchan; - uint8_t ts_nr = chan_nr & 0x07; - uint8_t cbits = chan_nr >> 3; - uint8_t lch_idx; - struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; - - if (cbits == 0x01) { - lch_idx = 0; /* TCH/F */ - if (ts->pchan != GSM_PCHAN_TCH_F && - ts->pchan != GSM_PCHAN_PDCH && - ts->pchan != GSM_PCHAN_TCH_F_PDCH) - LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - } else if ((cbits & 0x1e) == 0x02) { - lch_idx = cbits & 0x1; /* TCH/H */ - if (ts->pchan != GSM_PCHAN_TCH_H) - LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - } else if ((cbits & 0x1c) == 0x04) { - lch_idx = cbits & 0x3; /* SDCCH/4 */ - if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4) - LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - } else if ((cbits & 0x18) == 0x08) { - lch_idx = cbits & 0x7; /* SDCCH/8 */ - if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C) - LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { - lch_idx = 0; - if (ts->pchan != GSM_PCHAN_CCCH && - ts->pchan != GSM_PCHAN_CCCH_SDCCH4) - LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - /* FIXME: we should not return first sdcch4 !!! */ - } else { - LOGP(DRSL, LOGL_ERROR, "unknown chan_nr=0x%02x\n", chan_nr); - return NULL; - } - - lchan = &ts->lchan[lch_idx]; - log_set_context(BSC_CTX_LCHAN, lchan); - if (lchan->conn) - log_set_context(BSC_CTX_SUBSCR, lchan->conn->subscr); - - return lchan; -} - -/* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */ -uint64_t str_to_imsi(const char *imsi_str) -{ - uint64_t ret; - - ret = strtoull(imsi_str, NULL, 10); - - return ret; -} - -static struct msgb *rsl_msgb_alloc(void) -{ - return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, - "RSL"); -} - -#define MACBLOCK_SIZE 23 -static void pad_macblock(uint8_t *out, const uint8_t *in, int len) -{ - memcpy(out, in, len); - - if (len < MACBLOCK_SIZE) - memset(out+len, 0x2b, MACBLOCK_SIZE-len); -} - -/* Chapter 9.3.7: Encryption Information */ -static int build_encr_info(uint8_t *out, struct gsm_lchan *lchan) -{ - *out++ = lchan->encr.alg_id & 0xff; - if (lchan->encr.key_len) - memcpy(out, lchan->encr.key, lchan->encr.key_len); - return lchan->encr.key_len + 1; -} - -static void print_rsl_cause(int lvl, const uint8_t *cause_v, uint8_t cause_len) -{ - int i; - - LOGPC(DRSL, lvl, "CAUSE=0x%02x(%s) ", - cause_v[0], rsl_err_name(cause_v[0])); - for (i = 1; i < cause_len-1; i++) - LOGPC(DRSL, lvl, "%02x ", cause_v[i]); -} - -static void lchan_act_tmr_cb(void *data) -{ - struct gsm_lchan *lchan = data; - - LOGP(DRSL, LOGL_ERROR, - "%s Timeout during activation. Marked as broken.\n", - gsm_lchan_name(lchan)); - - rsl_lchan_set_state(lchan, LCHAN_S_BROKEN); - lchan_free(lchan); -} - -static void lchan_deact_tmr_cb(void *data) -{ - struct gsm_lchan *lchan = data; - - LOGP(DRSL, LOGL_ERROR, - "%s Timeout during deactivation! Marked as broken.\n", - gsm_lchan_name(lchan)); - - rsl_lchan_set_state(lchan, LCHAN_S_BROKEN); - lchan_free(lchan); -} - - -/* Send a BCCH_INFO message as per Chapter 8.5.1 */ -int rsl_bcch_info(struct gsm_bts_trx *trx, uint8_t type, - const uint8_t *data, int len) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh); - init_dchan_hdr(dh, RSL_MT_BCCH_INFO); - dh->chan_nr = RSL_CHAN_BCCH; - - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); - msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, - const uint8_t *data, int len) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_TRX; - ch->msg_type = RSL_MT_SACCH_FILL; - - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); - msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_sacch_info_modify(struct gsm_lchan *lchan, uint8_t type, - const uint8_t *data, int len) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_SACCH_INFO_MODIFY); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); - msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - - db = abs(db); - if (db > 30) - return -EINVAL; - - msg = rsl_msgb_alloc(); - - lchan->bs_power = db/2; - if (fpc) - lchan->bs_power |= 0x10; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_BS_POWER_CONTROL); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - int ctl_lvl; - - ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, dbm); - if (ctl_lvl < 0) - return ctl_lvl; - - msg = rsl_msgb_alloc(); - - lchan->ms_power = ctl_lvl; - - if (fpc) - lchan->ms_power |= 0x20; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_MS_POWER_CONTROL); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm, - struct gsm_lchan *lchan) -{ - memset(cm, 0, sizeof(*cm)); - - /* FIXME: what to do with data calls ? */ - if (lchan->ts->trx->bts->network->dtx_enabled) - cm->dtx_dtu = 0x03; - else - cm->dtx_dtu = 0x00; - - /* set TCH Speech/Data */ - cm->spd_ind = lchan->rsl_cmode; - - if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && - lchan->tch_mode != GSM48_CMODE_SIGN) - LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, " - "but tch_mode != signalling\n"); - - switch (lchan->type) { - case GSM_LCHAN_SDCCH: - cm->chan_rt = RSL_CMOD_CRT_SDCCH; - break; - case GSM_LCHAN_TCH_F: - cm->chan_rt = RSL_CMOD_CRT_TCH_Bm; - break; - case GSM_LCHAN_TCH_H: - cm->chan_rt = RSL_CMOD_CRT_TCH_Lm; - break; - case GSM_LCHAN_NONE: - case GSM_LCHAN_UNKNOWN: - default: - return -EINVAL; - } - - switch (lchan->tch_mode) { - case GSM48_CMODE_SIGN: - cm->chan_rate = 0; - break; - case GSM48_CMODE_SPEECH_V1: - cm->chan_rate = RSL_CMOD_SP_GSM1; - break; - case GSM48_CMODE_SPEECH_EFR: - cm->chan_rate = RSL_CMOD_SP_GSM2; - break; - case GSM48_CMODE_SPEECH_AMR: - cm->chan_rate = RSL_CMOD_SP_GSM3; - break; - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_6k0: - switch (lchan->csd_mode) { - case LCHAN_CSD_M_NT: - /* non-transparent CSD with RLP */ - switch (lchan->tch_mode) { - case GSM48_CMODE_DATA_14k5: - cm->chan_rate = RSL_CMOD_SP_NT_14k5; - break; - case GSM48_CMODE_DATA_12k0: - cm->chan_rate = RSL_CMOD_SP_NT_12k0; - break; - case GSM48_CMODE_DATA_6k0: - cm->chan_rate = RSL_CMOD_SP_NT_6k0; - break; - default: - return -EINVAL; - } - break; - /* transparent data services below */ - case LCHAN_CSD_M_T_1200_75: - cm->chan_rate = RSL_CMOD_CSD_T_1200_75; - break; - case LCHAN_CSD_M_T_600: - cm->chan_rate = RSL_CMOD_CSD_T_600; - break; - case LCHAN_CSD_M_T_1200: - cm->chan_rate = RSL_CMOD_CSD_T_1200; - break; - case LCHAN_CSD_M_T_2400: - cm->chan_rate = RSL_CMOD_CSD_T_2400; - break; - case LCHAN_CSD_M_T_9600: - cm->chan_rate = RSL_CMOD_CSD_T_9600; - break; - case LCHAN_CSD_M_T_14400: - cm->chan_rate = RSL_CMOD_CSD_T_14400; - break; - case LCHAN_CSD_M_T_29000: - cm->chan_rate = RSL_CMOD_CSD_T_29000; - break; - case LCHAN_CSD_M_T_32000: - cm->chan_rate = RSL_CMOD_CSD_T_32000; - break; - default: - return -EINVAL; - } - default: - return -EINVAL; - } - - return 0; -} - -/* Chapter 8.4.1 */ -#if 0 -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) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); - /* For compatibility with Phase 1 */ - msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(*chan_mode), - (uint8_t *) chan_mode); - msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4, - (uint8_t *) chan_ident); -#if 0 - msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1, - (uint8_t *) &encr_info); -#endif - msgb_tv_put(msg, RSL_IE_BS_POWER, bs_power); - msgb_tv_put(msg, RSL_IE_MS_POWER, ms_power); - msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} -#endif - -int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type, - uint8_t ta, uint8_t ho_ref) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - int rc; - uint8_t *len; - - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - struct rsl_ie_chan_mode cm; - struct gsm48_chan_desc cd; - - rc = channel_mode_from_lchan(&cm, lchan); - if (rc < 0) - return rc; - - memset(&cd, 0, sizeof(cd)); - gsm48_lchan2chan_desc(&cd, lchan); - - msg = rsl_msgb_alloc(); - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); - msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), - (uint8_t *) &cm); - - /* - * The Channel Identification is needed for Phase1 phones - * and it contains the GSM48 Channel Description and the - * Mobile Allocation. The GSM 08.58 asks for the Mobile - * Allocation to have a length of zero. We are using the - * msgb_l3len to calculate the length of both messages. - */ - msgb_v_put(msg, RSL_IE_CHAN_IDENT); - len = msgb_put(msg, 1); - msgb_tv_fixed_put(msg, GSM48_IE_CHANDESC_2, sizeof(cd), (const uint8_t *) &cd); - - if (lchan->ts->hopping.enabled) - msgb_tlv_put(msg, GSM48_IE_MA_AFTER, lchan->ts->hopping.ma_len, - lchan->ts->hopping.ma_data); - else - msgb_tlv_put(msg, GSM48_IE_MA_AFTER, 0, NULL); - - /* update the calculated size */ - msg->l3h = len + 1; - *len = msgb_l3len(msg); - - if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { - uint8_t encr_info[MAX_A5_KEY_LEN+2]; - rc = build_encr_info(encr_info, lchan); - if (rc > 0) - msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); - } - - switch (act_type) { - case RSL_ACT_INTER_ASYNC: - case RSL_ACT_INTER_SYNC: - msgb_tv_put(msg, RSL_IE_HANDO_REF, ho_ref); - break; - default: - break; - } - - msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); - msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); - msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); - - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) - msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf), - (uint8_t *) &lchan->mr_conf); - - 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); - } - - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { - msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf), - (uint8_t *) &lchan->mr_conf); - } - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.6: Send the encryption command with given L3 info */ -int rsl_encryption_cmd(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh; - struct gsm_lchan *lchan = msg->lchan; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - uint8_t encr_info[MAX_A5_KEY_LEN+2]; - uint8_t l3_len = msg->len; - int rc; - - /* First push the L3 IE tag and length */ - msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); - - /* then the link identifier (SAPI0, main sign link) */ - msgb_tv_push(msg, RSL_IE_LINK_IDENT, 0); - - /* then encryption information */ - rc = build_encr_info(encr_info, lchan); - if (rc <= 0) - return rc; - msgb_tlv_push(msg, RSL_IE_ENCR_INFO, rc, encr_info); - - /* and finally the DCHAN header */ - dh = (struct abis_rsl_dchan_hdr *) msgb_push(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_ENCR_CMD); - dh->chan_nr = chan_nr; - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.5 / 4.6: Deactivate the SACCH after 04.08 RR CHAN RELEASE */ -int rsl_deact_sacch(struct gsm_lchan *lchan) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_DEACTIVATE_SACCH); - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - msg->lchan = lchan; - msg->dst = lchan->ts->trx->rsl_link; - - DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan)); - - return abis_rsl_sendmsg(msg); -} - -static void error_timeout_cb(void *data) -{ - struct gsm_lchan *lchan = data; - if (lchan->state != LCHAN_S_REL_ERR) { - LOGP(DRSL, LOGL_ERROR, "%s error timeout but not in error state: %d\n", - gsm_lchan_name(lchan), lchan->state); - return; - } - - /* go back to the none state */ - LOGP(DRSL, LOGL_NOTICE, "%s is back in operation.\n", gsm_lchan_name(lchan)); - rsl_lchan_set_state(lchan, LCHAN_S_NONE); -} - -static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan); - -/* Chapter 8.4.14 / 4.7: Tell BTS to release the radio channel */ -static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error, - enum sacch_deact deact_sacch) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - int rc; - - /* Stop timers that should lead to a channel release */ - osmo_timer_del(&lchan->T3109); - - if (lchan->state == LCHAN_S_REL_ERR) { - LOGP(DRSL, LOGL_NOTICE, "%s is in error state not sending release.\n", - gsm_lchan_name(lchan)); - return -1; - } - - msg = rsl_msgb_alloc(); - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL); - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - msg->lchan = lchan; - msg->dst = lchan->ts->trx->rsl_link; - - DEBUGP(DRSL, "%s RF Channel Release CMD due error %d\n", gsm_lchan_name(lchan), error); - - if (error) { - struct e1inp_sign_link *sign_link = msg->dst; - - /* - * 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); - lchan->error_timer.data = lchan; - lchan->error_timer.cb = error_timeout_cb; - osmo_timer_schedule(&lchan->error_timer, - sign_link->trx->bts->network->T3111 + 2, 0); - } - - /* Start another timer or assume the BTS sends a ACK/NACK? */ - lchan->act_timer.cb = lchan_deact_tmr_cb; - lchan->act_timer.data = lchan; - osmo_timer_schedule(&lchan->act_timer, 4, 0); - - rc = abis_rsl_sendmsg(msg); - - /* BTS will respond by RF CHAN REL ACK */ - return rc; -} - -static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan) -{ - - DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", gsm_lchan_name(lchan)); - - /* Stop all pending timers */ - osmo_timer_del(&lchan->act_timer); - osmo_timer_del(&lchan->T3111); - - if (lchan->state == LCHAN_S_BROKEN) { - /* we are leaving this channel broken for now */ - LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK for broken channel.\n", - gsm_lchan_name(lchan)); - return 0; - } - - if (lchan->state != LCHAN_S_REL_REQ && lchan->state != LCHAN_S_REL_ERR) - LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n", - gsm_lchan_name(lchan), - gsm_lchans_name(lchan->state)); - do_lchan_free(lchan); - - return 0; -} - -int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len, - uint8_t *ms_ident, uint8_t chan_needed) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_PAGING_CMD); - dh->chan_nr = RSL_CHAN_PCH_AGCH; - - msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group); - msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len-2, ms_ident+2); - msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed); - - msg->dst = bts->c0->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int imsi_str2bcd(uint8_t *bcd_out, const char *str_in) -{ - int i, len = strlen(str_in); - - for (i = 0; i < len; i++) { - int num = str_in[i] - 0x30; - if (num < 0 || num > 9) - return -1; - if (i % 2 == 0) - bcd_out[i/2] = num; - else - bcd_out[i/2] |= (num << 4); - } - - return 0; -} - -/* Chapter 8.5.6 */ -int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - uint8_t buf[MACBLOCK_SIZE]; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD); - dh->chan_nr = RSL_CHAN_PCH_AGCH; - - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val); - break; - default: - /* If phase 2, construct a FULL_IMM_ASS_INFO */ - pad_macblock(buf, val, len); - msgb_tlv_put(msg, RSL_IE_FULL_IMM_ASS_INFO, MACBLOCK_SIZE, buf); - break; - } - - msg->dst = bts->c0->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Send Siemens specific MS RF Power Capability Indication */ -int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_SIEMENS_MRPCI); - dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; - dh->chan_nr = gsm_lchan2chan_nr(lchan); - msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(uint8_t *)mrpci); - - DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n", - gsm_lchan_name(lchan), *(uint8_t *)mrpci); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - - -/* Send "DATA REQUEST" message with given L3 Info payload */ -/* Chapter 8.3.1 */ -int rsl_data_request(struct msgb *msg, uint8_t link_id) -{ - if (msg->lchan == NULL) { - LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n"); - return -EINVAL; - } - - rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, gsm_lchan2chan_nr(msg->lchan), - link_id, 1); - - msg->dst = msg->lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Send "ESTABLISH REQUEST" message with given L3 Info payload */ -/* Chapter 8.3.1 */ -int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id) -{ - struct msgb *msg; - - msg = rsl_rll_simple(RSL_MT_EST_REQ, gsm_lchan2chan_nr(lchan), - link_id, 0); - msg->dst = lchan->ts->trx->rsl_link; - - DEBUGP(DRLL, "%s RSL RLL ESTABLISH REQ (link_id=0x%02x)\n", - gsm_lchan_name(lchan), link_id); - - return abis_rsl_sendmsg(msg); -} - -/* 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); - - return abis_rsl_sendmsg(msg); -} - -int rsl_lchan_set_state(struct gsm_lchan *lchan, int state) -{ - lchan->state = state; - return 0; -} - -/* Chapter 8.4.2: Channel Activate Acknowledge */ -static int rsl_rx_chan_act_ack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - - /* BTS has confirmed channel activation, we now need - * to assign the activated channel to the MS */ - if (rslh->ie_chan != RSL_IE_CHAN_NR) - return -EINVAL; - - osmo_timer_del(&msg->lchan->act_timer); - - if (msg->lchan->state == LCHAN_S_BROKEN) { - LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK for broken channel.\n", - gsm_lchan_name(msg->lchan)); - return 0; - } - - if (msg->lchan->state != LCHAN_S_ACT_REQ) - LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n", - gsm_lchan_name(msg->lchan), - gsm_lchans_name(msg->lchan->state)); - rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE); - - if (msg->lchan->rqd_ref) { - rsl_send_imm_assignment(msg->lchan); - talloc_free(msg->lchan->rqd_ref); - msg->lchan->rqd_ref = NULL; - msg->lchan->rqd_ta = 0; - } - - send_lchan_signal(S_LCHAN_ACTIVATE_ACK, msg->lchan, NULL); - - return 0; -} - -/* Chapter 8.4.3: Channel Activate NACK */ -static int rsl_rx_chan_act_nack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - osmo_timer_del(&msg->lchan->act_timer); - - if (msg->lchan->state == LCHAN_S_BROKEN) { - LOGP(DRSL, LOGL_ERROR, - "%s CHANNEL ACTIVATE NACK for broken channel.\n", - gsm_lchan_name(msg->lchan)); - return -1; - } - - LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK ", - gsm_lchan_name(msg->lchan)); - - /* BTS has rejected channel activation ?!? */ - if (dh->ie_chan != RSL_IE_CHAN_NR) - return -EINVAL; - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) { - const uint8_t *cause = TLVP_VAL(&tp, RSL_IE_CAUSE); - print_rsl_cause(LOGL_ERROR, cause, - TLVP_LEN(&tp, RSL_IE_CAUSE)); - msg->lchan->error_cause = *cause; - if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC) - rsl_lchan_set_state(msg->lchan, LCHAN_S_BROKEN); - else - rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE); - - } else - rsl_lchan_set_state(msg->lchan, LCHAN_S_BROKEN); - - 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; - - /* FIXME: print which channel */ - LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL: RELEASING ", - gsm_lchan_name(msg->lchan)); - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) - print_rsl_cause(LOGL_NOTICE, TLVP_VAL(&tp, RSL_IE_CAUSE), - TLVP_LEN(&tp, RSL_IE_CAUSE)); - - LOGPC(DRSL, LOGL_NOTICE, "\n"); - /* FIXME: only free it after channel release ACK */ - osmo_counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rf_fail); - return rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE); -} - -static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru, - const char *prefix) -{ - DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ", - prefix, rxlev2dbm(mru->full.rx_lev), - prefix, rxlev2dbm(mru->sub.rx_lev)); - DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ", - prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual); -} - -static void print_meas_rep(struct gsm_lchan *lchan, struct gsm_meas_rep *mr) -{ - int i; - char *name = ""; - - if (lchan && lchan->conn && lchan->conn->subscr) - name = subscr_name(lchan->conn->subscr); - - DEBUGP(DMEAS, "[%s] MEASUREMENT RESULT NR=%d ", name, mr->nr); - - if (mr->flags & MEAS_REP_F_DL_DTX) - DEBUGPC(DMEAS, "DTXd "); - - print_meas_rep_uni(&mr->ul, "ul"); - DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power); - if (mr->flags & MEAS_REP_F_MS_TO) - DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset); - - if (mr->flags & MEAS_REP_F_MS_L1) { - DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr); - DEBUGPC(DMEAS, "L1_FPC=%u ", - mr->flags & MEAS_REP_F_FPC ? 1 : 0); - DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta); - } - - if (mr->flags & MEAS_REP_F_UL_DTX) - DEBUGPC(DMEAS, "DTXu "); - if (mr->flags & MEAS_REP_F_BA1) - DEBUGPC(DMEAS, "BA1 "); - if (!(mr->flags & MEAS_REP_F_DL_VALID)) - DEBUGPC(DMEAS, "NOT VALID "); - else - print_meas_rep_uni(&mr->dl, "dl"); - - DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell); - if (mr->num_cell == 7) - return; - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - DEBUGP(DMEAS, "IDX=%u ARFCN=%u BSIC=%u => %d dBm\n", - mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev)); - } -} - -static int rsl_rx_meas_res(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - struct gsm_meas_rep *mr = lchan_next_meas_rep(msg->lchan); - uint8_t len; - const uint8_t *val; - int rc; - - /* check if this channel is actually active */ - /* FIXME: maybe this check should be way more generic/centralized */ - if (msg->lchan->state != LCHAN_S_ACTIVE) { - LOGP(DRSL, LOGL_DEBUG, "%s: MEAS RES for inactive channel\n", - gsm_lchan_name(msg->lchan)); - return 0; - } - - memset(mr, 0, sizeof(*mr)); - mr->lchan = msg->lchan; - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) || - !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) || - !TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) - return -EIO; - - /* Mandatory Parts */ - mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR); - - len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS); - val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS); - if (len >= 3) { - if (val[0] & 0x40) - mr->flags |= MEAS_REP_F_DL_DTX; - mr->ul.full.rx_lev = val[0] & 0x3f; - mr->ul.sub.rx_lev = val[1] & 0x3f; - mr->ul.full.rx_qual = val[2]>>3 & 0x7; - mr->ul.sub.rx_qual = val[2] & 0x7; - } - - mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); - - /* Optional Parts */ - if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) - mr->ms_timing_offset = - *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET); - - if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) { - struct e1inp_sign_link *sign_link = msg->dst; - - val = TLVP_VAL(&tp, RSL_IE_L1_INFO); - mr->flags |= MEAS_REP_F_MS_L1; - mr->ms_l1.pwr = ms_pwr_dbm(sign_link->trx->bts->band, val[0] >> 3); - if (val[0] & 0x04) - mr->flags |= MEAS_REP_F_FPC; - mr->ms_l1.ta = val[1]; - /* BS11 and Nokia reports TA shifted by 2 bits */ - if (msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11 - || msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) - mr->ms_l1.ta >>= 2; - } - if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { - msg->l3h = (uint8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO); - rc = gsm48_parse_meas_rep(mr, msg); - if (rc < 0) - return rc; - } - - print_meas_rep(msg->lchan, mr); - - send_lchan_signal(S_LCHAN_MEAS_REP, msg->lchan, mr); - - return 0; -} - -/* Chapter 8.4.7 */ -static int rsl_rx_hando_det(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - DEBUGP(DRSL, "%s HANDOVER DETECT ", gsm_lchan_name(msg->lchan)); - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY)) - DEBUGPC(DRSL, "access delay = %u\n", - *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY)); - else - DEBUGPC(DRSL, "\n"); - - send_lchan_signal(S_LCHAN_HANDOVER_DETECT, msg->lchan, NULL); - - return 0; -} - -static int abis_rsl_rx_dchan(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - int rc = 0; - char *ts_name; - struct e1inp_sign_link *sign_link = msg->dst; - - msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr); - ts_name = gsm_lchan_name(msg->lchan); - - switch (rslh->c.msg_type) { - case RSL_MT_CHAN_ACTIV_ACK: - DEBUGP(DRSL, "%s CHANNEL ACTIVATE ACK\n", ts_name); - rc = rsl_rx_chan_act_ack(msg); - break; - case RSL_MT_CHAN_ACTIV_NACK: - rc = rsl_rx_chan_act_nack(msg); - break; - case RSL_MT_CONN_FAIL: - rc = rsl_rx_conn_fail(msg); - break; - case RSL_MT_MEAS_RES: - rc = rsl_rx_meas_res(msg); - break; - case RSL_MT_HANDO_DET: - rc = rsl_rx_hando_det(msg); - break; - case RSL_MT_RF_CHAN_REL_ACK: - rc = rsl_rx_rf_chan_rel_ack(msg->lchan); - break; - case RSL_MT_MODE_MODIFY_ACK: - DEBUGP(DRSL, "%s CHANNEL MODE MODIFY ACK\n", ts_name); - break; - case RSL_MT_MODE_MODIFY_NACK: - LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name); - break; - case RSL_MT_IPAC_PDCH_ACT_ACK: - DEBUGPC(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name); - msg->lchan->ts->flags |= TS_F_PDCH_MODE; - break; - case RSL_MT_IPAC_PDCH_ACT_NACK: - LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name); - break; - case RSL_MT_IPAC_PDCH_DEACT_ACK: - DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name); - msg->lchan->ts->flags &= ~TS_F_PDCH_MODE; - break; - case RSL_MT_IPAC_PDCH_DEACT_NACK: - LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name); - break; - case RSL_MT_PHY_CONTEXT_CONF: - case RSL_MT_PREPROC_MEAS_RES: - case RSL_MT_TALKER_DET: - case RSL_MT_LISTENER_DET: - case RSL_MT_REMOTE_CODEC_CONF_REP: - case RSL_MT_MR_CODEC_MOD_ACK: - case RSL_MT_MR_CODEC_MOD_NACK: - case RSL_MT_MR_CODEC_MOD_PER: - LOGP(DRSL, LOGL_NOTICE, "%s Unimplemented Abis RSL DChan " - "msg 0x%02x\n", ts_name, rslh->c.msg_type); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "%s unknown Abis RSL DChan msg 0x%02x\n", - ts_name, rslh->c.msg_type); - return -EINVAL; - } - - return rc; -} - -static int rsl_rx_error_rep(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh = msgb_l2(msg); - struct tlv_parsed tp; - struct e1inp_sign_link *sign_link = msg->dst; - - LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT ", gsm_trx_name(sign_link->trx)); - - rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh)); - - if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) - print_rsl_cause(LOGL_ERROR, TLVP_VAL(&tp, RSL_IE_CAUSE), - TLVP_LEN(&tp, RSL_IE_CAUSE)); - - LOGPC(DRSL, LOGL_ERROR, "\n"); - - return 0; -} - -static int abis_rsl_rx_trx(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh = msgb_l2(msg); - struct e1inp_sign_link *sign_link = msg->dst; - int rc = 0; - - switch (rslh->msg_type) { - case RSL_MT_ERROR_REPORT: - rc = rsl_rx_error_rep(msg); - break; - case RSL_MT_RF_RES_IND: - /* interference on idle channels of TRX */ - //DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(sign_link->trx)); - break; - case RSL_MT_OVERLOAD: - /* indicate CCCH / ACCH / processor overload */ - LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n", - gsm_trx_name(sign_link->trx)); - break; - case 0x42: /* Nokia specific: SI End ACK */ - LOGP(DRSL, LOGL_INFO, "Nokia SI End ACK\n"); - break; - case 0x43: /* Nokia specific: SI End NACK */ - LOGP(DRSL, LOGL_INFO, "Nokia SI End NACK\n"); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message " - "type 0x%02x\n", gsm_trx_name(sign_link->trx), rslh->msg_type); - return -EINVAL; - } - return rc; -} - -/* If T3101 expires, we never received a response to IMMEDIATE ASSIGN */ -static void t3101_expired(void *data) -{ - struct gsm_lchan *lchan = data; - - rsl_rf_chan_release(lchan, 1, SACCH_DEACTIVATE); -} - -/* If T3111 expires, we will send the RF Channel Request */ -static void t3111_expired(void *data) -{ - struct gsm_lchan *lchan = data; - - rsl_rf_chan_release(lchan, 0, SACCH_NONE); -} - -/* If T3109 expires the MS has not send a UA/UM do the error release */ -static void t3109_expired(void *data) -{ - struct gsm_lchan *lchan = data; - - LOGP(DRSL, LOGL_ERROR, - "%s SACCH deactivation timeout.\n", gsm_lchan_name(lchan)); - rsl_rf_chan_release(lchan, 1, SACCH_NONE); -} - -#define GSM48_LEN2PLEN(a) (((a) << 2) | 1) - -/* Format an IMM ASS REJ according to 04.08 Chapter 9.1.20 */ -static int rsl_send_imm_ass_rej(struct gsm_bts *bts, - unsigned int num_req_refs, - struct gsm48_req_ref *rqd_refs, - uint8_t wait_ind) -{ - uint8_t buf[GSM_MACBLOCK_LEN]; - struct gsm48_imm_ass_rej *iar = (struct gsm48_imm_ass_rej *)buf; - - /* create IMMEDIATE ASSIGN REJECT 04.08 message */ - memset(iar, 0, sizeof(*iar)); - iar->proto_discr = GSM48_PDISC_RR; - iar->msg_type = GSM48_MT_RR_IMM_ASS_REJ; - iar->page_mode = GSM48_PM_SAME; - - memcpy(&iar->req_ref1, &rqd_refs[0], sizeof(iar->req_ref1)); - iar->wait_ind1 = wait_ind; - - if (num_req_refs >= 2) - memcpy(&iar->req_ref2, &rqd_refs[1], sizeof(iar->req_ref2)); - else - memcpy(&iar->req_ref2, &rqd_refs[0], sizeof(iar->req_ref2)); - iar->wait_ind2 = wait_ind; - - if (num_req_refs >= 3) - memcpy(&iar->req_ref3, &rqd_refs[2], sizeof(iar->req_ref3)); - else - memcpy(&iar->req_ref3, &rqd_refs[0], sizeof(iar->req_ref3)); - iar->wait_ind3 = wait_ind; - - if (num_req_refs >= 4) - memcpy(&iar->req_ref4, &rqd_refs[3], sizeof(iar->req_ref4)); - else - memcpy(&iar->req_ref4, &rqd_refs[0], sizeof(iar->req_ref4)); - iar->wait_ind4 = wait_ind; - - /* we need to subtract 1 byte from sizeof(*iar) since ia includes the l2_plen field */ - iar->l2_plen = GSM48_LEN2PLEN((sizeof(*iar)-1)); - - return rsl_imm_assign_cmd(bts, sizeof(*iar), (uint8_t *) iar); -} - -/* MS has requested a channel on the RACH */ -static int rsl_rx_chan_rqd(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); - struct gsm48_req_ref *rqd_ref; - enum gsm_chan_t lctype; - enum gsm_chreq_reason_t chreq_reason; - struct gsm_lchan *lchan; - uint8_t rqd_ta; - int is_lu; - - uint16_t arfcn; - uint8_t subch; - - /* parse request reference to be used in immediate assign */ - if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) - return -EINVAL; - - rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; - - /* parse access delay and use as TA */ - if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) - return -EINVAL; - rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; - - /* determine channel type (SDCCH/TCH_F/TCH_H) based on - * request reference RA */ - lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra); - chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci); - - osmo_counter_inc(bts->network->stats.chreq.total); - - /* - * We want LOCATION UPDATES to succeed and will assign a TCH - * if we have no SDCCH available. - */ - is_lu = !!(chreq_reason == GSM_CHREQ_REASON_LOCATION_UPD); - - /* check availability / allocate channel */ - lchan = lchan_alloc(bts, lctype, is_lu); - if (!lchan) { - LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n", - msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra); - osmo_counter_inc(bts->network->stats.chreq.no_channel); - /* FIXME gather multiple CHAN RQD and reject up to 4 at the same time */ - if (bts->network->T3122) - rsl_send_imm_ass_rej(bts, 1, rqd_ref, bts->network->T3122 & 0xff); - return -ENOMEM; - } - - if (lchan->state != LCHAN_S_NONE) - LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel " - "in state %s\n", gsm_lchan_name(lchan), - gsm_lchans_name(lchan->state)); - - /* save the RACH data as we need it after the CHAN ACT ACK */ - lchan->rqd_ref = talloc_zero(bts, struct gsm48_req_ref); - if (!lchan->rqd_ref) { - LOGP(DRSL, LOGL_ERROR, "Failed to allocate gsm48_req_ref.\n"); - lchan_free(lchan); - return -ENOMEM; - } - - rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); - memcpy(lchan->rqd_ref, rqd_ref, sizeof(*rqd_ref)); - lchan->rqd_ta = rqd_ta; - - arfcn = lchan->ts->trx->arfcn; - subch = lchan->nr; - - lchan->encr.alg_id = RSL_ENC_ALG_A5(0); /* no encryption */ - lchan->ms_power = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); - lchan->bs_power = 0; /* 0dB reduction, output power = Pn */ - lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; - lchan->tch_mode = GSM48_CMODE_SIGN; - - /* Start another timer or assume the BTS sends a ACK/NACK? */ - lchan->act_timer.cb = lchan_act_tmr_cb; - lchan->act_timer.data = lchan; - osmo_timer_schedule(&lchan->act_timer, 4, 0); - - DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s " - "r=%s ra=0x%02x ta=%d\n", gsm_lchan_name(lchan), arfcn, subch, - gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason), - rqd_ref->ra, rqd_ta); - - /* BS11 requires TA shifted by 2 bits */ - if (bts->type == GSM_BTS_TYPE_BS11) - rqd_ta <<= 2; - rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, 0); - - return 0; -} - -static int rsl_send_imm_assignment(struct gsm_lchan *lchan) -{ - struct gsm_bts *bts = lchan->ts->trx->bts; - uint8_t buf[GSM_MACBLOCK_LEN]; - struct gsm48_imm_ass *ia = (struct gsm48_imm_ass *) buf; - - /* create IMMEDIATE ASSIGN 04.08 messge */ - memset(ia, 0, sizeof(*ia)); - /* we set ia->l2_plen once we know the length of the MA below */ - ia->proto_discr = GSM48_PDISC_RR; - ia->msg_type = GSM48_MT_RR_IMM_ASS; - ia->page_mode = GSM48_PM_SAME; - gsm48_lchan2chan_desc(&ia->chan_desc, lchan); - - /* use request reference extracted from CHAN_RQD */ - memcpy(&ia->req_ref, lchan->rqd_ref, sizeof(ia->req_ref)); - ia->timing_advance = lchan->rqd_ta; - if (!lchan->ts->hopping.enabled) { - ia->mob_alloc_len = 0; - } else { - ia->mob_alloc_len = lchan->ts->hopping.ma_len; - memcpy(ia->mob_alloc, lchan->ts->hopping.ma_data, ia->mob_alloc_len); - } - /* we need to subtract 1 byte from sizeof(*ia) since ia includes the l2_plen field */ - ia->l2_plen = GSM48_LEN2PLEN((sizeof(*ia)-1) + ia->mob_alloc_len); - - /* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */ - lchan->T3101.cb = t3101_expired; - lchan->T3101.data = lchan; - osmo_timer_schedule(&lchan->T3101, bts->network->T3101, 0); - - /* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */ - return rsl_imm_assign_cmd(bts, sizeof(*ia)+ia->mob_alloc_len, (uint8_t *) ia); -} - -/* current load on the CCCH */ -static int rsl_rx_ccch_load(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - struct ccch_signal_data sd; - - sd.bts = sign_link->trx->bts; - sd.rach_slot_count = -1; - sd.rach_busy_count = -1; - sd.rach_access_count = -1; - - switch (rslh->data[0]) { - case RSL_IE_PAGING_LOAD: - sd.pg_buf_space = rslh->data[1] << 8 | rslh->data[2]; - if (is_ipaccess_bts(sign_link->trx->bts) && sd.pg_buf_space == 0xffff) { - /* paging load below configured threshold, use 50 as default */ - sd.pg_buf_space = 50; - } - paging_update_buffer_space(sign_link->trx->bts, sd.pg_buf_space); - osmo_signal_dispatch(SS_CCCH, S_CCCH_PAGING_LOAD, &sd); - break; - case RSL_IE_RACH_LOAD: - if (msg->data_len >= 7) { - sd.rach_slot_count = rslh->data[2] << 8 | rslh->data[3]; - sd.rach_busy_count = rslh->data[4] << 8 | rslh->data[5]; - sd.rach_access_count = rslh->data[6] << 8 | rslh->data[7]; - osmo_signal_dispatch(SS_CCCH, S_CCCH_RACH_LOAD, &sd); - } - break; - default: - break; - } - - return 0; -} - -static int abis_rsl_rx_cchan(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - int rc = 0; - - msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr); - - switch (rslh->c.msg_type) { - case RSL_MT_CHAN_RQD: - /* MS has requested a channel on the RACH */ - rc = rsl_rx_chan_rqd(msg); - break; - case RSL_MT_CCCH_LOAD_IND: - /* current load on the CCCH */ - rc = rsl_rx_ccch_load(msg); - break; - case RSL_MT_DELETE_IND: - /* CCCH overloaded, IMM_ASSIGN was dropped */ - case RSL_MT_CBCH_LOAD_IND: - /* current load on the CBCH */ - LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message " - "type 0x%02x\n", rslh->c.msg_type); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type " - "0x%02x\n", rslh->c.msg_type); - return -EINVAL; - } - - return rc; -} - -static int rsl_rx_rll_err_ind(struct msgb *msg) -{ - struct tlv_parsed tp; - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - uint8_t rlm_cause; - - rsl_tlv_parse(&tp, rllh->data, msgb_l2len(msg) - sizeof(*rllh)); - if (!TLVP_PRESENT(&tp, RSL_IE_RLM_CAUSE)) { - LOGP(DRLL, LOGL_ERROR, - "%s ERROR INDICATION without mandantory cause.\n", - gsm_lchan_name(msg->lchan)); - return -1; - } - - rlm_cause = *TLVP_VAL(&tp, RSL_IE_RLM_CAUSE); - LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s\n", - gsm_lchan_name(msg->lchan), - rsl_rlm_cause_name(rlm_cause)); - - rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND); - - if (rlm_cause == RLL_CAUSE_T200_EXPIRED) { - osmo_counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rll_err); - return rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE); - } - - return 0; -} - -static void rsl_handle_release(struct gsm_lchan *lchan) -{ - int sapi; - struct gsm_bts *bts; - - /* - * Maybe only one link/SAPI was releasd or the error handling - * was activated. Just return now and let the other code handle - * it. - */ - if (lchan->state != LCHAN_S_REL_REQ) - return; - - for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { - if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) - continue; - LOGP(DRSL, LOGL_DEBUG, "%s waiting for SAPI=%d to be released.\n", - gsm_lchan_name(lchan), sapi); - return; - } - - - /* Stop T3109 and wait for T3111 before re-using the channel */ - osmo_timer_del(&lchan->T3109); - lchan->T3111.cb = t3111_expired; - lchan->T3111.data = lchan; - bts = lchan->ts->trx->bts; - osmo_timer_schedule(&lchan->T3111, bts->network->T3111, 0); -} - -/* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST - 0x02, 0x06, - 0x01, 0x20, - 0x02, 0x00, - 0x0b, 0x00, 0x0f, 0x05, 0x08, ... */ - -static int abis_rsl_rx_rll(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - int rc = 0; - char *ts_name; - uint8_t sapi = rllh->link_id & 7; - - msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr); - ts_name = gsm_lchan_name(msg->lchan); - DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi); - - switch (rllh->c.msg_type) { - case RSL_MT_DATA_IND: - DEBUGPC(DRLL, "DATA INDICATION\n"); - if (msgb_l2len(msg) > - sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && - rllh->data[0] == RSL_IE_L3_INFO) { - msg->l3h = &rllh->data[3]; - return gsm0408_rcvmsg(msg, rllh->link_id); - } - break; - case RSL_MT_EST_IND: - DEBUGPC(DRLL, "ESTABLISH INDICATION\n"); - /* lchan is established, stop T3101 */ - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS; - osmo_timer_del(&msg->lchan->T3101); - if (msgb_l2len(msg) > - sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && - rllh->data[0] == RSL_IE_L3_INFO) { - msg->l3h = &rllh->data[3]; - return gsm0408_rcvmsg(msg, rllh->link_id); - } - break; - case RSL_MT_EST_CONF: - DEBUGPC(DRLL, "ESTABLISH CONFIRM\n"); - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_NET; - rll_indication(msg->lchan, rllh->link_id, - BSC_RLLR_IND_EST_CONF); - break; - case RSL_MT_REL_IND: - /* BTS informs us of having received DISC from MS */ - DEBUGPC(DRLL, "RELEASE INDICATION\n"); - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; - rll_indication(msg->lchan, rllh->link_id, - BSC_RLLR_IND_REL_IND); - rsl_handle_release(msg->lchan); - break; - case RSL_MT_REL_CONF: - /* BTS informs us of having received UA from MS, - * in response to DISC that we've sent earlier */ - DEBUGPC(DRLL, "RELEASE CONFIRMATION\n"); - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; - rsl_handle_release(msg->lchan); - break; - case RSL_MT_ERROR_IND: - rc = rsl_rx_rll_err_ind(msg); - break; - case RSL_MT_UNIT_DATA_IND: - LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message " - "type 0x%02x\n", rllh->c.msg_type); - break; - default: - LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message " - "type 0x%02x\n", rllh->c.msg_type); - } - return rc; -} - -static uint8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan) -{ - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 0x00; - case GSM_LCHAN_TCH_H: - return 0x03; - default: - break; - } - case GSM48_CMODE_SPEECH_EFR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 0x01; - /* there's no half-rate EFR */ - default: - break; - } - case GSM48_CMODE_SPEECH_AMR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 0x02; - case GSM_LCHAN_TCH_H: - return 0x05; - default: - break; - } - default: - break; - } - LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for " - "tch_mode == 0x%02x\n", lchan->tch_mode); - return 0; -} - -static 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; - } - case GSM48_CMODE_SPEECH_EFR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return RTP_PT_GSM_EFR; - /* there's no half-rate EFR */ - default: - break; - } - case GSM48_CMODE_SPEECH_AMR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - case GSM_LCHAN_TCH_H: - return RTP_PT_AMR; - default: - break; - } - default: - break; - } - LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access rtp payload type for " - "tch_mode == 0x%02x\n & lchan_type == %d", - lchan->tch_mode, lchan->type); - return 0; -} - -/* ip.access specific RSL extensions */ -static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv) -{ - struct in_addr ip; - uint16_t port, conn_id; - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) { - ip.s_addr = *((uint32_t *) TLVP_VAL(tv, RSL_IE_IPAC_LOCAL_IP)); - DEBUGPC(DRSL, "LOCAL_IP=%s ", inet_ntoa(ip)); - lchan->abis_ip.bound_ip = ntohl(ip.s_addr); - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_PORT)) { - port = *((uint16_t *) TLVP_VAL(tv, RSL_IE_IPAC_LOCAL_PORT)); - port = ntohs(port); - DEBUGPC(DRSL, "LOCAL_PORT=%u ", port); - lchan->abis_ip.bound_port = port; - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_CONN_ID)) { - conn_id = *((uint16_t *) TLVP_VAL(tv, RSL_IE_IPAC_CONN_ID)); - conn_id = ntohs(conn_id); - DEBUGPC(DRSL, "CON_ID=%u ", conn_id); - lchan->abis_ip.conn_id = conn_id; - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_RTP_PAYLOAD2)) { - lchan->abis_ip.rtp_payload2 = - *TLVP_VAL(tv, RSL_IE_IPAC_RTP_PAYLOAD2); - DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ", - lchan->abis_ip.rtp_payload2); - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_SPEECH_MODE)) { - lchan->abis_ip.speech_mode = - *TLVP_VAL(tv, RSL_IE_IPAC_SPEECH_MODE); - DEBUGPC(DRSL, "speech_mode=0x%02x ", - lchan->abis_ip.speech_mode); - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_IP)) { - ip.s_addr = *((uint32_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_IP)); - DEBUGPC(DRSL, "REMOTE_IP=%s ", inet_ntoa(ip)); - lchan->abis_ip.connect_ip = ntohl(ip.s_addr); - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_PORT)) { - port = *((uint16_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_PORT)); - port = ntohs(port); - DEBUGPC(DRSL, "REMOTE_PORT=%u ", port); - lchan->abis_ip.connect_port = port; - } -} - -/*! \brief Issue IPA RSL CRCX to configure RTP on BTS side - * \param[in] lchan Logical Channel for which we issue CRCX - */ -int rsl_ipacc_crcx(struct gsm_lchan *lchan) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_IPAC_CRCX); - dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - /* 0x1- == receive-only, 0x-1 == EFR codec */ - lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan); - lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); - msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); - msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); - - DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x RTP_PAYLOAD=%d\n", - gsm_lchan_name(lchan), lchan->abis_ip.speech_mode, - lchan->abis_ip.rtp_payload); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/*! \brief Issue IPA RSL MDCX to configure MGW-side of RTP - * \param[in] lchan Logical Channel for which we issue MDCX - * \param[in] ip Remote (MGW) IP address for RTP - * \param[in] port Remote (MGW) UDP port number for RTP - * \param[in] rtp_payload2 Contents of RTP PAYLOAD 2 IE - */ -int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip, uint16_t port, - uint8_t rtp_payload2) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - uint32_t *att_ip; - struct in_addr ia; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_IPAC_MDCX); - dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - /* we need to store these now as MDCX_ACK does not return them :( */ - lchan->abis_ip.rtp_payload2 = rtp_payload2; - lchan->abis_ip.connect_port = port; - lchan->abis_ip.connect_ip = ip; - - /* 0x0- == both directions, 0x-1 == EFR codec */ - lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan); - lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); - - ia.s_addr = htonl(ip); - DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD=%d RTP_PAYLOAD2=%d " - "CONN_ID=%d speech_mode=0x%02x\n", gsm_lchan_name(lchan), - inet_ntoa(ia), port, lchan->abis_ip.rtp_payload, rtp_payload2, - lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode); - - msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id); - msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP); - att_ip = (uint32_t *) msgb_put(msg, sizeof(ip)); - *att_ip = ia.s_addr; - msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port); - msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); - msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); - if (rtp_payload2) - msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* tell BTS to connect RTP stream to our local RTP socket */ -int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan) -{ - struct rtp_socket *rs = lchan->abis_ip.rtp_socket; - int rc; - - rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr), - ntohs(rs->rtp.sin_local.sin_port), - /* FIXME: use RTP payload of bound socket, not BTS*/ - lchan->abis_ip.rtp_payload2); - - return rc; -} - -int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - uint8_t msg_type; - - if (act) - msg_type = RSL_MT_IPAC_PDCH_ACT; - else - msg_type = RSL_MT_IPAC_PDCH_DEACT; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, msg_type); - dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; - dh->chan_nr = gsm_ts2chan_nr(ts, 0); - - DEBUGP(DRSL, "%s IPAC_PDCH_%sACT\n", gsm_ts_name(ts), - act ? "" : "DE"); - - msg->dst = ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tv; - struct gsm_lchan *lchan = msg->lchan; - - /* the BTS has acknowledged a local bind, it now tells us the IP - * address and port number to which it has bound the given logical - * channel */ - - rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); - if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) || - !TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) || - !TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) { - LOGP(DRSL, LOGL_NOTICE, "mandatory IE missing"); - return -EINVAL; - } - - ipac_parse_rtp(lchan, &tv); - - osmo_signal_dispatch(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan); - - return 0; -} - -static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tv; - struct gsm_lchan *lchan = msg->lchan; - - /* the BTS has acknowledged a remote connect request and - * it now tells us the IP address and port number to which it has - * connected the given logical channel */ - - rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); - ipac_parse_rtp(lchan, &tv); - osmo_signal_dispatch(SS_ABISIP, S_ABISIP_MDCX_ACK, msg->lchan); - - return 0; -} - -static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tv; - - rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (TLVP_PRESENT(&tv, RSL_IE_CAUSE)) - print_rsl_cause(LOGL_DEBUG, TLVP_VAL(&tv, RSL_IE_CAUSE), - TLVP_LEN(&tv, RSL_IE_CAUSE)); - - osmo_signal_dispatch(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan); - - return 0; -} - -static int abis_rsl_rx_ipacc(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - char *ts_name; - int rc = 0; - - msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr); - ts_name = gsm_lchan_name(msg->lchan); - - switch (rllh->c.msg_type) { - case RSL_MT_IPAC_CRCX_ACK: - DEBUGP(DRSL, "%s IPAC_CRCX_ACK ", ts_name); - rc = abis_rsl_rx_ipacc_crcx_ack(msg); - break; - case RSL_MT_IPAC_CRCX_NACK: - /* somehow the BTS was unable to bind the lchan to its local - * port?!? */ - LOGP(DRSL, LOGL_ERROR, "%s IPAC_CRCX_NACK\n", ts_name); - break; - case RSL_MT_IPAC_MDCX_ACK: - /* the BTS tells us that a connect operation was successful */ - DEBUGP(DRSL, "%s IPAC_MDCX_ACK ", ts_name); - rc = abis_rsl_rx_ipacc_mdcx_ack(msg); - break; - case RSL_MT_IPAC_MDCX_NACK: - /* somehow the BTS was unable to connect the lchan to a remote - * port */ - LOGP(DRSL, LOGL_ERROR, "%s IPAC_MDCX_NACK\n", ts_name); - break; - case RSL_MT_IPAC_DLCX_IND: - DEBUGP(DRSL, "%s IPAC_DLCX_IND ", ts_name); - rc = abis_rsl_rx_ipacc_dlcx_ind(msg); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n", - rllh->c.msg_type); - break; - } - DEBUGPC(DRSL, "\n"); - - return rc; -} - - -/* Entry-point where L2 RSL from BTS enters */ -int abis_rsl_rcvmsg(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh; - int rc = 0; - - if (!msg) { - DEBUGP(DRSL, "Empty RSL msg?..\n"); - return -1; - } - - if (msgb_l2len(msg) < sizeof(*rslh)) { - DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg)); - msgb_free(msg); - return -1; - } - - rslh = msgb_l2(msg); - - switch (rslh->msg_discr & 0xfe) { - case ABIS_RSL_MDISC_RLL: - rc = abis_rsl_rx_rll(msg); - break; - case ABIS_RSL_MDISC_DED_CHAN: - rc = abis_rsl_rx_dchan(msg); - break; - case ABIS_RSL_MDISC_COM_CHAN: - rc = abis_rsl_rx_cchan(msg); - break; - case ABIS_RSL_MDISC_TRX: - rc = abis_rsl_rx_trx(msg); - break; - case ABIS_RSL_MDISC_LOC: - LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL msg disc 0x%02x\n", - rslh->msg_discr); - break; - case ABIS_RSL_MDISC_IPACCESS: - rc = abis_rsl_rx_ipacc(msg); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator " - "0x%02x\n", rslh->msg_discr); - rc = -EINVAL; - } - msgb_free(msg); - return rc; -} - -int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number, - uint8_t cb_command, const uint8_t *data, int len) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *cb_cmd; - - cb_cmd = rsl_msgb_alloc(); - if (!cb_cmd) - return -1; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(cb_cmd, sizeof*dh); - init_dchan_hdr(dh, RSL_MT_SMS_BC_CMD); - dh->chan_nr = RSL_CHAN_SDCCH4_ACCH; /* TODO: check the chan config */ - - msgb_tv_put(cb_cmd, RSL_IE_CB_CMD_TYPE, cb_command); - msgb_tlv_put(cb_cmd, RSL_IE_SMSCB_MSG, len, data); - - cb_cmd->trx = bts->c0; - - return abis_rsl_sendmsg(cb_cmd); -} - -int rsl_nokia_si_begin(struct gsm_bts_trx *trx) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_TRX; - ch->msg_type = 0x40; /* Nokia SI Begin */ - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_nokia_si_end(struct gsm_bts_trx *trx) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_TRX; - ch->msg_type = 0x41; /* Nokia SI End */ - - msgb_tv_put(msg, 0xFD, 0x00); /* Nokia Pagemode Info, No paging reorganisation required */ - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_DED_CHAN; - ch->msg_type = RSL_MT_BS_POWER_CONTROL; - - msgb_tv_put(msg, RSL_IE_CHAN_NR, channel); - msgb_tv_put(msg, RSL_IE_BS_POWER, reduction); /* reduction in 2dB steps */ - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/** - * Release all allocated SAPIs starting from @param start and - * release them with the given release mode. Once the release - * confirmation arrives it will be attempted to release the - * the RF channel. - */ -int rsl_release_sapis_from(struct gsm_lchan *lchan, int start, - enum rsl_rel_mode release_mode) -{ - int no_sapi = 1; - int sapi; - - for (sapi = start; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { - uint8_t link_id; - if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) - continue; - - link_id = sapi; - if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H) - link_id |= 0x40; - rsl_release_request(lchan, link_id, release_mode); - no_sapi = 0; - } - - return no_sapi; -} - -int rsl_start_t3109(struct gsm_lchan *lchan) -{ - struct gsm_bts *bts = lchan->ts->trx->bts; - - /* Disabled, mostly legacy code */ - if (bts->network->T3109 == 0) - return -1; - - lchan->T3109.cb = t3109_expired; - lchan->T3109.data = lchan; - osmo_timer_schedule(&lchan->T3109, bts->network->T3109, 0); - return 0; -} - -/** - * \brief directly RF Channel Release the lchan - * - * When no SAPI was allocated, directly release the logical channel. This - * should only be called from chan_alloc.c on channel release handling. In - * case no SAPI was established the RF Channel can be directly released, - */ -int rsl_direct_rf_release(struct gsm_lchan *lchan) -{ - int i; - for (i = 0; i < ARRAY_SIZE(lchan->sapis); ++i) { - if (lchan->sapis[i] != LCHAN_SAPI_UNUSED) { - LOGP(DRSL, LOGL_ERROR, "%s SAPI(%d) still allocated.\n", - gsm_lchan_name(lchan), i); - return -1; - } - } - - /* Now release it */ - return rsl_rf_chan_release(lchan, 0, SACCH_NONE); -} diff --git a/openbsc/src/libbsc/arfcn_range_encode.c b/openbsc/src/libbsc/arfcn_range_encode.c deleted file mode 100644 index 02a75a53c..000000000 --- a/openbsc/src/libbsc/arfcn_range_encode.c +++ /dev/null @@ -1,305 +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 - -int greatest_power_of_2_lesser_or_equal_to(int index) -{ - int power_of_2 = 1; - - do { - power_of_2 *= 2; - } while (power_of_2 <= index); - - /* now go back one step */ - return power_of_2 / 2; -} - -static inline int mod(int data, int range) -{ - int res = data % range; - while (res < 0) - res += range; - return res; -} - -/** - * Determine at which index to split the ARFCNs to create an - * equally size partition for the given range. Return -1 if - * no such partition exists. - */ -int range_enc_find_index(const int range, const int *freqs, const int size) -{ - int i, j, n; - - const int RANGE_DELTA = (range - 1) / 2; - - for (i = 0; i < size; ++i) { - n = 0; - for (j = 0; j < size; ++j) { - if (mod(freqs[j] - freqs[i], range) <= RANGE_DELTA) - n += 1; - } - - if (n - 1 == (size - 1) / 2) - return i; - } - - return -1; -} - -/** - * Range encode the ARFCN list. - * \param range The range to use. - * \param arfcns The list of ARFCNs - * \param size The size of the list of ARFCNs - * \param out Place to store the W(i) output. - */ -int range_enc_arfcns(const int range, - const int *arfcns, int size, int *out, - const int index) -{ - int split_at; - int i; - - /* - * The below is a GNU extension and we can remove it when - * we move to a quicksort like in-situ swap with the pivot. - */ - int arfcns_left[size / 2]; - int arfcns_right[size / 2]; - int l_size; - int r_size; - int l_origin; - int r_origin; - - - /* Test the two recursion anchors and stop processing */ - if (size == 0) - return 0; - - if (size == 1) { - out[index] = 1 + arfcns[0]; - return 0; - } - - /* Now do the processing */ - split_at = range_enc_find_index(range, arfcns, size); - - /* we now know where to split */ - out[index] = 1 + arfcns[split_at]; - - /* calculate the work that needs to be done for the leafs */ - l_origin = mod(arfcns[split_at] + ((range - 1) / 2) + 1, range); - r_origin = mod(arfcns[split_at] + 1, range); - for (i = 0, l_size = 0, r_size = 0; i < size; ++i) { - if (mod(arfcns[i] - l_origin, range) < range / 2) - arfcns_left[l_size++] = mod(arfcns[i] - l_origin, range); - if (mod(arfcns[i] - r_origin, range) < range / 2) - arfcns_right[r_size++] = mod(arfcns[i] - r_origin, range); - } - - /* - * Now recurse and we need to make this iterative... but as the - * tree is balanced the stack will not be too deep. - */ - range_enc_arfcns(range / 2, arfcns_left, l_size, - out, index + greatest_power_of_2_lesser_or_equal_to(index + 1)); - range_enc_arfcns((range -1 ) / 2, arfcns_right, r_size, - out, index + (2 * greatest_power_of_2_lesser_or_equal_to(index + 1))); - return 0; -} - -/* - * The easiest is to use f0 == arfcns[0]. This means that under certain - * circumstances we can encode less ARFCNs than possible with an optimal f0. - * - * TODO: Solve the optimisation problem and pick f0 so that the max distance - * is the smallest. Taking into account the modulo operation. I think picking - * size/2 will be the optimal arfcn. - */ -/** - * This implements the range determination as described in GSM 04.08 J4. The - * result will be a base frequency f0 and the range to use. - * - * \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) - return ARFCN_RANGE_1024; - - return ARFCN_RANGE_INVALID; -} - -/* - * The below is easier is to write in four methods than - * to use the max_bits. The encoding is so screwed.. as - * the bits need to be put in place in the wrong order.. - */ -#define HIGH_BITS(w, index, bits, offset) \ - (w[index - 1] >> (bits - offset)) -#define LOW_BITS(w, index, bits, offset) \ - (w[index - 1]) - -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; -} - -int range_enc_range128(uint8_t *chan_list, int f0, int *w) -{ - chan_list[0] = 0x8C; - write_orig_arfcn(chan_list, f0); - - LOGP(DRR, LOGL_ERROR, "Range128 encoding is not implemented.\n"); - return -1; -} - -int range_enc_range256(uint8_t *chan_list, int f0, int *w) -{ - chan_list[0] = 0x8A; - write_orig_arfcn(chan_list, f0); - - LOGP(DRR, LOGL_ERROR, "Range256 encoding is not implemented.\n"); - return -1; -} - -int range_enc_range512(uint8_t *chan_list, int f0, int *w) -{ - struct gsm48_range_512 *range512; - write_orig_arfcn(chan_list, f0); - - range512 = (struct gsm48_range_512 *) &chan_list[0]; - range512->form_id = chan_list[0] = 0x44; - - /* W(1) */ - range512->w1_hi = HIGH_BITS(w, 1, 9, 7); - range512->w1_lo = LOW_BITS (w, 1, 9, 2); - /* W(2) */ - range512->w2_hi = HIGH_BITS(w, 2, 8, 6); - range512->w2_lo = LOW_BITS (w, 2, 8, 2); - /* W(3) */ - range512->w3_hi = HIGH_BITS(w, 3, 8, 6); - range512->w3_lo = LOW_BITS (w, 3, 8, 2); - /* W(4) */ - range512->w4_hi = HIGH_BITS(w, 4, 7, 6); - range512->w4_lo = LOW_BITS (w, 4, 7, 1); - /* W(5) */ - range512->w5 = HIGH_BITS(w, 5, 7, 7); - /* W(6) */ - range512->w6 = HIGH_BITS(w, 6, 7, 7); - /* W(7) */ - range512->w7_hi = HIGH_BITS(w, 7, 7, 1); - range512->w7_lo = LOW_BITS (w, 7, 7, 6); - /* W(8) */ - range512->w8_hi = HIGH_BITS(w, 8, 6, 2); - range512->w8_lo = LOW_BITS (w, 8, 6, 4); - /* W(9) */ - range512->w9_hi = HIGH_BITS(w, 9, 6, 4); - range512->w9_lo = LOW_BITS(w, 9, 6, 2); - /* W(10) */ - range512->w10 = HIGH_BITS(w, 10, 6, 6); - /* W(11) */ - range512->w11 = HIGH_BITS(w, 11, 6, 6); - /* W(12) */ - range512->w12_hi = HIGH_BITS(w, 12, 6, 2); - range512->w12_lo = LOW_BITS (w, 12, 6, 4); - /* W(13) */ - range512->w13_hi = HIGH_BITS(w, 13, 6, 4); - range512->w13_lo = LOW_BITS(w, 13, 6, 2); - /* W(14) */ - range512->w14 = HIGH_BITS(w, 14, 6, 6); - /* W(15) */ - range512->w15 = HIGH_BITS(w, 15, 6, 6); - /* W(16) */ - range512->w16_hi = HIGH_BITS(w, 16, 5, 2); - range512->w16_lo = HIGH_BITS(w, 16, 5, 3); - /* W(17) */ - range512->w17 = HIGH_BITS(w, 17, 5, 5); - - return 0; -} - -int range_enc_range1024(uint8_t *chan_list, int f0, int f0_included, int *w) -{ - chan_list[0] = 0x80 | (f0_included << 2); - - LOGP(DRR, LOGL_ERROR, "Range1024 encoding is not implemented.\n"); - return -1; -} - -int range_enc_filter_arfcns(const int range, int *arfcns, - const int size, const int f0, int *f0_included) -{ - int i, j = 0; - *f0_included = 0; - - if (range == ARFCN_RANGE_1024) { - for (i = 0; i < size; ++i) { - if (arfcns[i] == f0) { - *f0_included = 1; - continue; - } - - /* copy and subtract */ - arfcns[j++] = mod(arfcns[i] - 1, 1024); - } - } else { - 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) - 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 bde13b929..000000000 --- a/openbsc/src/libbsc/bsc_api.c +++ /dev/null @@ -1,854 +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 - -#define GSM0808_T10_VALUE 6, 0 - -static LLIST_HEAD(sub_connections); - -static void rll_ind_cb(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind); -static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id); -static void handle_release(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); -static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); -static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); - -/* GSM 08.08 3.2.2.33 */ -static uint8_t lchan_to_chosen_channel(struct gsm_lchan *lchan) -{ - uint8_t channel_mode = 0, channel = 0; - - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - case GSM48_CMODE_SPEECH_EFR: - case GSM48_CMODE_SPEECH_AMR: - channel_mode = 0x9; - break; - case GSM48_CMODE_SIGN: - channel_mode = 0x8; - break; - case GSM48_CMODE_DATA_14k5: - channel_mode = 0xe; - break; - case GSM48_CMODE_DATA_12k0: - channel_mode = 0xb; - break; - case GSM48_CMODE_DATA_6k0: - channel_mode = 0xc; - break; - case GSM48_CMODE_DATA_3k6: - channel_mode = 0xd; - break; - } - - switch (lchan->type) { - case GSM_LCHAN_NONE: - channel = 0x0; - break; - case GSM_LCHAN_SDCCH: - channel = 0x1; - break; - case GSM_LCHAN_TCH_F: - channel = 0x8; - break; - case GSM_LCHAN_TCH_H: - channel = 0x9; - break; - case GSM_LCHAN_UNKNOWN: - default: - LOGP(DMSC, LOGL_ERROR, "Unknown lchan type: %u\n", lchan->type); - break; - } - - return channel_mode << 4 | channel; -} - -static uint8_t chan_mode_to_speech(struct gsm_lchan *lchan) -{ - int mode = 0; - - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - mode = 1; - break; - case GSM48_CMODE_SPEECH_EFR: - mode = 0x11; - break; - case GSM48_CMODE_SPEECH_AMR: - mode = 0x21; - break; - case GSM48_CMODE_SIGN: - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_6k0: - case GSM48_CMODE_DATA_3k6: - default: - LOGP(DMSC, LOGL_ERROR, "Using non speech mode: %d\n", mode); - return 0; - break; - } - - /* assume to always do AMR HR on any TCH type */ - if (lchan->type == GSM_LCHAN_TCH_H || - lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) - mode |= 0x4; - - return mode; -} - -static void assignment_t10_timeout(void *_conn) -{ - struct bsc_api *api; - struct gsm_subscriber_connection *conn = - (struct gsm_subscriber_connection *) _conn; - - LOGP(DMSC, LOGL_ERROR, "Assigment T10 timeout on %p\n", conn); - - /* - * normal release on the secondary channel but only if the - * secondary_channel has not been released by the handle_chan_nack. - */ - if (conn->secondary_lchan) - lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); - conn->secondary_lchan = NULL; - - /* inform them about the failure */ - api = conn->bts->network->bsc_api; - api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); -} - -/** - * Handle the multirate config - */ -static void handle_mr_config(struct gsm_subscriber_connection *conn, - struct gsm_lchan *lchan) -{ - struct bsc_api *api; - api = conn->bts->network->bsc_api; - - if (api->mr_config) - return api->mr_config(conn, &lchan->mr_conf); - - lchan->mr_conf.ver = 1; - lchan->mr_conf.icmi = 1; - lchan->mr_conf.m5_90 = 1; -} - -/* - * Start a new assignment and make sure that it is completed within T10 either - * positively, negatively or by the timeout. - * - * 1.) allocate a new lchan - * 2.) copy the encryption key and other data from the - * old to the new channel. - * 3.) RSL Channel Activate this channel and wait - * - * -> Signal handler for the LCHAN - * 4.) Send GSM 04.08 assignment command to the MS - * - * -> Assignment Complete/Assignment Failure - * 5.) Release the SDCCH, continue signalling on the new link - */ -static int handle_new_assignment(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) -{ - struct gsm_lchan *new_lchan; - int chan_type; - - chan_type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H; - - new_lchan = lchan_alloc(conn->bts, chan_type, 0); - - if (!new_lchan) { - LOGP(DMSC, LOGL_NOTICE, "No free channel.\n"); - return -1; - } - - /* copy old data to the new channel */ - memcpy(&new_lchan->encr, &conn->lchan->encr, sizeof(new_lchan->encr)); - new_lchan->ms_power = conn->lchan->ms_power; - new_lchan->bs_power = conn->lchan->bs_power; - - /* copy new data to it */ - new_lchan->tch_mode = chan_mode; - new_lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; - - /* handle AMR correctly */ - if (chan_mode == GSM48_CMODE_SPEECH_AMR) - handle_mr_config(conn, new_lchan); - - if (rsl_chan_activate_lchan(new_lchan, 0x1, 0, 0) < 0) { - LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); - lchan_free(new_lchan); - return -1; - } - - /* remember that we have the channel */ - conn->secondary_lchan = new_lchan; - new_lchan->conn = conn; - - rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); - return 0; -} - -struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan) -{ - struct gsm_subscriber_connection *conn; - - conn = talloc_zero(lchan->ts->trx->bts->network, struct gsm_subscriber_connection); - if (!conn) - return NULL; - - /* Configure the time and start it so it will be closed */ - conn->lchan = lchan; - conn->bts = lchan->ts->trx->bts; - lchan->conn = conn; - llist_add_tail(&conn->entry, &sub_connections); - return conn; -} - -/* TODO: move subscriber put here... */ -void subscr_con_free(struct gsm_subscriber_connection *conn) -{ - if (!conn) - return; - - - if (conn->subscr) { - subscr_put(conn->subscr); - conn->subscr = NULL; - } - - - if (conn->ho_lchan) { - LOGP(DNM, LOGL_ERROR, "The ho_lchan should have been cleared.\n"); - conn->ho_lchan->conn = NULL; - } - - if (conn->lchan) { - LOGP(DNM, LOGL_ERROR, "The lchan should have been cleared.\n"); - conn->lchan->conn = NULL; - } - - if (conn->secondary_lchan) { - LOGP(DNM, LOGL_ERROR, "The secondary_lchan should have been cleared.\n"); - conn->secondary_lchan->conn = NULL; - } - - llist_del(&conn->entry); - talloc_free(conn); -} - -int bsc_api_init(struct gsm_network *network, struct bsc_api *api) -{ - network->bsc_api = api; - return 0; -} - -/*! \brief process incoming 08.08 DTAP from MSC (send via BTS to MS) */ -int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, - struct msgb *msg, int link_id, int allow_sacch) -{ - uint8_t sapi; - - - if (!conn->lchan) { - LOGP(DMSC, LOGL_ERROR, - "Called submit dtap without an lchan.\n"); - msgb_free(msg); - return -1; - } - - sapi = link_id & 0x7; - msg->lchan = conn->lchan; - msg->dst = msg->lchan->ts->trx->rsl_link; - - /* If we are on a TCH and need to submit a SMS (on SAPI=3) we need to use the SACH */ - if (allow_sacch && sapi != 0) { - if (conn->lchan->type == GSM_LCHAN_TCH_F || conn->lchan->type == GSM_LCHAN_TCH_H) - link_id |= 0x40; - } - - msg->l3h = msg->data; - /* is requested SAPI already up? */ - if (conn->lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) { - /* Establish L2 for additional SAPI */ - OBSC_LINKID_CB(msg) = link_id; - if (rll_establish(msg->lchan, sapi, rll_ind_cb, msg) != 0) { - msgb_free(msg); - send_sapi_reject(conn, link_id); - return -1; - } - return 0; - } else { - /* Directly forward via RLL/RSL to BTS */ - return rsl_data_request(msg, link_id); - } -} - -/* - * \brief Check if the given channel is compatible with the mode/fullrate - */ -static int chan_compat_with_mode(struct gsm_lchan *lchan, int chan_mode, int full_rate) -{ - switch (chan_mode) { - case GSM48_CMODE_SIGN: - /* signalling is always possible */ - return 1; - case GSM48_CMODE_SPEECH_V1: - case GSM48_CMODE_SPEECH_AMR: - case GSM48_CMODE_DATA_3k6: - case GSM48_CMODE_DATA_6k0: - /* these services can all run on TCH/H, but we may have - * an explicit override by the 'full_rate' argument */ - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 1; - case GSM_LCHAN_TCH_H: - if (full_rate) - return 0; - else - return 1; - break; - default: - return 0; - } - break; - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_SPEECH_EFR: - /* these services all explicitly require a TCH/F */ - if (lchan->type == GSM_LCHAN_TCH_F) - return 1; - else - return 0; - break; - } - - return 0; -} - -/** - * Send a GSM08.08 Assignment Request. Right now this does not contain the - * audio codec type or the allowed rates for the config. It is assumed that - * this is for audio handling only. In case the current channel does not allow - * the selected mode a new one will be allocated. - * - * TODO: Add multirate configuration, make it work for more than audio. - */ -int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) -{ - struct bsc_api *api; - api = conn->bts->network->bsc_api; - - if (!chan_compat_with_mode(conn->lchan, chan_mode, full_rate)) { - if (handle_new_assignment(conn, chan_mode, full_rate) != 0) - goto error; - } else { - LOGP(DMSC, LOGL_NOTICE, - "Sending ChanModify for speech %d %d\n", chan_mode, full_rate); - if (chan_mode == GSM48_CMODE_SPEECH_AMR) - handle_mr_config(conn, conn->lchan); - - gsm48_lchan_modify(conn->lchan, chan_mode); - } - - /* we will now start the timer to complete the assignment */ - conn->T10.cb = assignment_t10_timeout; - conn->T10.data = conn; - osmo_timer_schedule(&conn->T10, GSM0808_T10_VALUE); - return 0; - -error: - api->assign_fail(conn, 0, NULL); - return -1; -} - -int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len, - uint8_t *mi, int chan_type) -{ - return rsl_paging_cmd(bts, page_group, mi_len, mi, chan_type); -} - -static void handle_ass_compl(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh; - struct bsc_api *api = conn->bts->network->bsc_api; - - if (conn->secondary_lchan != msg->lchan) { - LOGP(DMSC, LOGL_ERROR, "Assignment Compl should occur on second lchan.\n"); - return; - } - - gh = msgb_l3(msg); - if (msgb_l3len(msg) - sizeof(*gh) != 1) { - LOGP(DMSC, LOGL_ERROR, "Assignment Compl invalid: %u\n", - msgb_l3len(msg) - sizeof(*gh)); - return; - } - - /* swap channels */ - osmo_timer_del(&conn->T10); - - lchan_release(conn->lchan, 0, RSL_REL_LOCAL_END); - conn->lchan = conn->secondary_lchan; - conn->secondary_lchan = NULL; - - if (is_ipaccess_bts(conn->bts) && conn->lchan->tch_mode != GSM48_CMODE_SIGN) - rsl_ipacc_crcx(conn->lchan); - - api->assign_compl(conn, gh->data[0], - lchan_to_chosen_channel(conn->lchan), - conn->lchan->encr.alg_id, - chan_mode_to_speech(conn->lchan)); -} - -static void handle_ass_fail(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct bsc_api *api = conn->bts->network->bsc_api; - uint8_t *rr_failure; - struct gsm48_hdr *gh; - - - if (conn->lchan != msg->lchan) { - LOGP(DMSC, LOGL_ERROR, "Assignment failure should occur on primary lchan.\n"); - return; - } - - /* stop the timer and release it */ - osmo_timer_del(&conn->T10); - lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); - conn->secondary_lchan = NULL; - - gh = msgb_l3(msg); - if (msgb_l3len(msg) - sizeof(*gh) != 1) { - LOGP(DMSC, LOGL_ERROR, "assignemnt failure unhandled: %u\n", - msgb_l3len(msg) - sizeof(*gh)); - rr_failure = NULL; - } else { - rr_failure = &gh->data[0]; - } - - api->assign_fail(conn, - GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE, - rr_failure); -} - -static void handle_classmark_chg(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - uint8_t cm2_len, cm3_len = 0; - uint8_t *cm2, *cm3 = NULL; - - DEBUGP(DRR, "CLASSMARK CHANGE "); - - /* classmark 2 */ - cm2_len = gh->data[0]; - cm2 = &gh->data[1]; - DEBUGPC(DRR, "CM2(len=%u) ", cm2_len); - - if (payload_len > cm2_len + 1) { - /* we must have a classmark3 */ - if (gh->data[cm2_len+1] != 0x20) { - DEBUGPC(DRR, "ERR CM3 TAG\n"); - return; - } - if (cm2_len > 3) { - DEBUGPC(DRR, "CM2 too long!\n"); - return; - } - - cm3_len = gh->data[cm2_len+2]; - cm3 = &gh->data[cm2_len+3]; - if (cm3_len > 14) { - DEBUGPC(DRR, "CM3 len %u too long!\n", cm3_len); - return; - } - DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len); - } - api->classmark_chg(conn, cm2, cm2_len, cm3, cm3_len); -} - -/* Chapter 9.1.16 Handover complete */ -static void handle_rr_ho_compl(struct msgb *msg) -{ - struct lchan_signal_data sig; - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DRR, "HANDOVER COMPLETE cause = %s\n", - rr_cause_name(gh->data[0])); - - sig.lchan = msg->lchan; - sig.mr = NULL; - osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, &sig); - /* FIXME: release old channel */ -} - -/* Chapter 9.1.17 Handover Failure */ -static void handle_rr_ho_fail(struct msgb *msg) -{ - struct lchan_signal_data sig; - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DRR, "HANDOVER FAILED cause = %s\n", - rr_cause_name(gh->data[0])); - - sig.lchan = msg->lchan; - sig.mr = NULL; - osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, &sig); - /* FIXME: release allocated new channel */ -} - - -static void dispatch_dtap(struct gsm_subscriber_connection *conn, - uint8_t link_id, struct msgb *msg) -{ - struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; - struct gsm48_hdr *gh; - uint8_t pdisc; - int rc; - - if (msgb_l3len(msg) < sizeof(*gh)) { - LOGP(DMSC, LOGL_ERROR, "Message too short for a GSM48 header.\n"); - return; - } - - gh = msgb_l3(msg); - pdisc = gh->proto_discr & 0x0f; - - /* the idea is to handle all RR messages here, and only hand - * MM/CC/SMS-CP/LCS up to the MSC. Some messages like PAGING - * RESPONSE or CM SERVICE REQUEST will not be covered here, as - * they are only possible in the first L3 message of each L2 - * channel, i.e. 'conn' will not exist and gsm0408_rcvmsg() - * will call api->compl_l3() for it */ - switch (pdisc) { - case GSM48_PDISC_RR: - switch (gh->msg_type) { - case GSM48_MT_RR_GPRS_SUSP_REQ: - DEBUGP(DRR, "GRPS SUSPEND REQUEST\n"); - break; - case GSM48_MT_RR_STATUS: - LOGP(DRR, LOGL_NOTICE, "RR STATUS (cause: %s)\n", - rr_cause_name(gh->data[0])); - break; - case GSM48_MT_RR_MEAS_REP: - /* This shouldn't actually end up here, as RSL treats - * L3 Info of 08.58 MEASUREMENT REPORT different by calling - * directly into gsm48_parse_meas_rep */ - LOGP(DMEAS, LOGL_ERROR, "DIRECT GSM48 MEASUREMENT REPORT ?!? "); - break; - case GSM48_MT_RR_HANDO_COMPL: - handle_rr_ho_compl(msg); - break; - case GSM48_MT_RR_HANDO_FAIL: - handle_rr_ho_fail(msg); - break; - case GSM48_MT_RR_CIPH_M_COMPL: - if (api->cipher_mode_compl) - api->cipher_mode_compl(conn, msg, - conn->lchan->encr.alg_id); - break; - case GSM48_MT_RR_ASS_COMPL: - handle_ass_compl(conn, msg); - break; - case GSM48_MT_RR_ASS_FAIL: - handle_ass_fail(conn, msg); - break; - case GSM48_MT_RR_CHAN_MODE_MODIF_ACK: - osmo_timer_del(&conn->T10); - rc = gsm48_rx_rr_modif_ack(msg); - if (rc < 0) { - api->assign_fail(conn, - GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, - NULL); - } else if (rc >= 0) { - api->assign_compl(conn, 0, - lchan_to_chosen_channel(conn->lchan), - conn->lchan->encr.alg_id, - chan_mode_to_speech(conn->lchan)); - } - break; - case GSM48_MT_RR_CLSM_CHG: - handle_classmark_chg(conn, msg); - break; - case GSM48_MT_RR_APP_INFO: - /* Passing RR APP INFO to MSC, not quite - * according to spec */ - if (api->dtap) - api->dtap(conn, link_id, msg); - break; - default: - /* Normally, a MSC should never receive RR - * messages, but we'd rather forward what we - * don't know than drop it... */ - LOGP(DRR, LOGL_NOTICE, "BSC: Passing unknown 04.08 " - "RR message type 0x%02x to MSC\n", gh->msg_type); - if (api->dtap) - api->dtap(conn, link_id, msg); - } - break; - default: - if (api->dtap) - api->dtap(conn, link_id, msg); - break; - } -} - -/*! \brief RSL has received a DATA INDICATION with L3 from MS */ -int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id) -{ - int rc; - struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; - struct gsm_lchan *lchan; - - lchan = msg->lchan; - if (lchan->state != LCHAN_S_ACTIVE) { - LOGP(DRSL, LOGL_INFO, "Got data in non active state(%s), " - "discarding.\n", gsm_lchans_name(lchan->state)); - return -1; - } - - - if (lchan->conn) { - /* if we already have a connection, forward via DTAP to - * MSC */ - dispatch_dtap(lchan->conn, link_id, msg); - } else { - /* allocate a new connection */ - rc = BSC_API_CONN_POL_REJECT; - lchan->conn = subscr_con_allocate(msg->lchan); - if (!lchan->conn) { - lchan_release(lchan, 1, RSL_REL_NORMAL); - return -1; - } - - /* fwd via bsc_api to send COMPLETE L3 INFO to MSC */ - rc = api->compl_l3(lchan->conn, msg, 0); - - if (rc != BSC_API_CONN_POL_ACCEPT) { - lchan->conn->lchan = NULL; - subscr_con_free(lchan->conn); - lchan_release(lchan, 1, RSL_REL_NORMAL); - } - } - - return 0; -} - -/*! \brief We received a GSM 08.08 CIPHER MODE from the MSC */ -int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher, - const uint8_t *key, int len, int include_imeisv) -{ - if (cipher > 0 && key == NULL) { - LOGP(DRSL, LOGL_ERROR, "Need to have an encrytpion key.\n"); - return -1; - } - - if (len > MAX_A5_KEY_LEN) { - LOGP(DRSL, LOGL_ERROR, "The key is too long: %d\n", len); - return -1; - } - - conn->lchan->encr.alg_id = RSL_ENC_ALG_A5(cipher); - if (key) { - conn->lchan->encr.key_len = len; - memcpy(conn->lchan->encr.key, key, len); - } - - return gsm48_send_rr_ciph_mode(conn->lchan, include_imeisv); -} - -/* - * Release all occupied RF Channels but stay around for more. - */ -int gsm0808_clear(struct gsm_subscriber_connection *conn) -{ - if (conn->ho_lchan) - bsc_clear_handover(conn, 1); - - if (conn->secondary_lchan) - lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); - - if (conn->lchan) - lchan_release(conn->lchan, 1, RSL_REL_NORMAL); - - conn->lchan = NULL; - conn->secondary_lchan = NULL; - conn->ho_lchan = NULL; - conn->bts = NULL; - - osmo_timer_del(&conn->T10); - - return 0; -} - -static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id) -{ - struct bsc_api *api; - - if (!conn) - return; - - api = conn->bts->network->bsc_api; - if (!api || !api->sapi_n_reject) - return; - - api->sapi_n_reject(conn, link_id); -} - -static void rll_ind_cb(struct gsm_lchan *lchan, uint8_t link_id, void *_data, enum bsc_rllr_ind rllr_ind) -{ - struct msgb *msg = _data; - - /* - * There seems to be a small window that the RLL timer can - * fire after a lchan_release call and before the S_CHALLOC_FREED - * is called. Check if a conn is set before proceeding. - */ - if (!lchan->conn) - return; - - switch (rllr_ind) { - case BSC_RLLR_IND_EST_CONF: - rsl_data_request(msg, OBSC_LINKID_CB(msg)); - break; - case BSC_RLLR_IND_REL_IND: - case BSC_RLLR_IND_ERR_IND: - case BSC_RLLR_IND_TIMEOUT: - send_sapi_reject(lchan->conn, OBSC_LINKID_CB(msg)); - msgb_free(msg); - break; - } -} - -static int bsc_handle_lchan_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct bsc_api *bsc; - struct gsm_lchan *lchan; - struct lchan_signal_data *lchan_data; - - if (subsys != SS_LCHAN) - return 0; - - - lchan_data = signal_data; - if (!lchan_data->lchan || !lchan_data->lchan->conn) - return 0; - - lchan = lchan_data->lchan; - bsc = lchan->ts->trx->bts->network->bsc_api; - if (!bsc) - return 0; - - switch (signal) { - case S_LCHAN_UNEXPECTED_RELEASE: - handle_release(lchan->conn, bsc, lchan); - break; - case S_LCHAN_ACTIVATE_ACK: - handle_chan_ack(lchan->conn, bsc, lchan); - break; - case S_LCHAN_ACTIVATE_NACK: - handle_chan_nack(lchan->conn, bsc, lchan); - break; - } - - return 0; -} - -static void handle_release(struct gsm_subscriber_connection *conn, - struct bsc_api *bsc, struct gsm_lchan *lchan) -{ - int destruct = 1; - - if (conn->secondary_lchan == lchan) { - osmo_timer_del(&conn->T10); - conn->secondary_lchan = NULL; - - bsc->assign_fail(conn, - GSM0808_CAUSE_RADIO_INTERFACE_FAILURE, - NULL); - } - - /* clear the connection now */ - if (bsc->clear_request) - destruct = bsc->clear_request(conn, 0); - - /* now give up all channels */ - if (conn->lchan == lchan) - conn->lchan = NULL; - if (conn->ho_lchan == lchan) { - bsc_clear_handover(conn, 0); - conn->ho_lchan = NULL; - } - lchan->conn = NULL; - - gsm0808_clear(conn); - - if (destruct) - subscr_con_free(conn); -} - -static void handle_chan_ack(struct gsm_subscriber_connection *conn, - struct bsc_api *api, struct gsm_lchan *lchan) -{ - if (conn->secondary_lchan != lchan) - return; - - LOGP(DMSC, LOGL_NOTICE, "Sending assignment on chan: %p\n", lchan); - gsm48_send_rr_ass_cmd(conn->lchan, lchan, lchan->ms_power); -} - -static void handle_chan_nack(struct gsm_subscriber_connection *conn, - struct bsc_api *api, struct gsm_lchan *lchan) -{ - if (conn->secondary_lchan != lchan) - return; - - LOGP(DMSC, LOGL_ERROR, "Channel activation failed. Waiting for timeout now\n"); - conn->secondary_lchan->conn = NULL; - conn->secondary_lchan = NULL; -} - -static __attribute__((constructor)) void on_dso_load_bsc(void) -{ - osmo_signal_register_handler(SS_LCHAN, bsc_handle_lchan_signal, NULL); -} diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c deleted file mode 100644 index 8fd72cf46..000000000 --- a/openbsc/src/libbsc/bsc_init.c +++ /dev/null @@ -1,501 +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 - -/* global pointer to the gsm network data structure */ -extern struct gsm_network *bsc_gsmnet; - -/* Callback function for NACK on the OML NM */ -static int oml_msg_nack(struct nm_nack_signal_data *nack) -{ - if (nack->mt == NM_MT_SET_BTS_ATTR_NACK) { - - LOGP(DNM, LOGL_ERROR, "Failed to set BTS attributes. That is fatal. " - "Was the bts type and frequency properly specified?\n"); - goto drop_bts; - } else { - LOGP(DNM, LOGL_ERROR, "Got a NACK going to drop the OML links.\n"); - goto drop_bts; - } - - return 0; - -drop_bts: - if (!nack->bts) { - LOGP(DNM, LOGL_ERROR, "Unknown bts. Can not drop it.\n"); - return 0; - } - - if (is_ipaccess_bts(nack->bts)) - ipaccess_drop_oml(nack->bts); - - return 0; -} - -/* Callback function to be called every time we receive a signal from NM */ -static int nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct nm_nack_signal_data *nack; - - switch (signal) { - case S_NM_NACK: - nack = signal_data; - return oml_msg_nack(nack); - default: - break; - } - return 0; -} - -int bsc_shutdown_net(struct gsm_network *net) -{ - struct gsm_bts *bts; - - llist_for_each_entry(bts, &net->bts_list, list) { - LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr); - osmo_signal_dispatch(SS_L_GLOBAL, S_GLOBAL_BTS_CLOSE_OM, bts); - } - - return 0; -} - -static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len) -{ - struct gsm_bts *bts = trx->bts; - int rc; - - DEBUGP(DRR, "SI%s: %s\n", get_value_string(osmo_sitype_strs, i), - osmo_hexdump(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN)); - - switch (i) { - case SYSINFO_TYPE_5: - case SYSINFO_TYPE_5bis: - case SYSINFO_TYPE_5ter: - case SYSINFO_TYPE_6: - rc = rsl_sacch_filling(trx, osmo_sitype2rsl(i), - GSM_BTS_SI(bts, i), si_len); - break; - default: - rc = rsl_bcch_info(trx, osmo_sitype2rsl(i), - GSM_BTS_SI(bts, i), si_len); - break; - } - - return rc; -} - -/* set all system information types */ -static int set_system_infos(struct gsm_bts_trx *trx) -{ - int i, rc; - struct gsm_bts *bts = trx->bts; - uint8_t gen_si[_MAX_SYSINFO_TYPE], n_si = 0, n; - int si_len[_MAX_SYSINFO_TYPE]; - - bts->si_common.cell_sel_par.ms_txpwr_max_ccch = - ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); - bts->si_common.cell_sel_par.neci = bts->network->neci; - - /* First, we determine which of the SI messages we actually need */ - - if (trx == bts->c0) { - /* 1...4 are always present on a C0 TRX */ - gen_si[n_si++] = SYSINFO_TYPE_1; - gen_si[n_si++] = SYSINFO_TYPE_2; - gen_si[n_si++] = SYSINFO_TYPE_2bis; - gen_si[n_si++] = SYSINFO_TYPE_2ter; - gen_si[n_si++] = SYSINFO_TYPE_3; - gen_si[n_si++] = SYSINFO_TYPE_4; - - /* 13 is always present on a C0 TRX of a GPRS BTS */ - if (bts->gprs.mode != BTS_GPRS_NONE) - gen_si[n_si++] = SYSINFO_TYPE_13; - } - - /* 5 and 6 are always present on every TRX */ - gen_si[n_si++] = SYSINFO_TYPE_5; - gen_si[n_si++] = SYSINFO_TYPE_5bis; - gen_si[n_si++] = SYSINFO_TYPE_5ter; - gen_si[n_si++] = SYSINFO_TYPE_6; - - /* Second, we generate the selected SI via RSL */ - - for (n = 0; n < n_si; n++) { - i = gen_si[n]; - /* Only generate SI if this SI is not in "static" (user-defined) mode */ - if (!(bts->si_mode_static & (1 << i))) { - /* Set SI as being valid. gsm_generate_si() might unset - * it, if SI is not required. */ - bts->si_valid |= (1 << i); - rc = gsm_generate_si(bts, i); - if (rc < 0) - goto err_out; - si_len[i] = rc; - } else { - if (i == SYSINFO_TYPE_5 || i == SYSINFO_TYPE_5bis - || i == SYSINFO_TYPE_5ter) - si_len[i] = 18; - else if (i == SYSINFO_TYPE_6) - si_len[i] = 11; - else - si_len[i] = 23; - } - } - - /* Third, we send the selected SI via RSL */ - - for (n = 0; n < n_si; n++) { - i = gen_si[n]; - if (!(bts->si_valid & (1 << i))) - continue; - rc = rsl_si(trx, i, si_len[i]); - if (rc < 0) - return rc; - } - - return 0; -err_out: - LOGP(DRR, LOGL_ERROR, "Cannot generate SI%s for BTS %u, most likely " - "a problem with neighbor cell list generation\n", - get_value_string(osmo_sitype_strs, i), bts->nr); - return rc; -} - -/* Produce a MA as specified in 10.5.2.21 */ -static int generate_ma_for_ts(struct gsm_bts_trx_ts *ts) -{ - /* we have three bitvecs: the per-timeslot ARFCNs, the cell chan ARFCNs - * and the MA */ - struct bitvec *cell_chan = &ts->trx->bts->si_common.cell_alloc; - struct bitvec *ts_arfcn = &ts->hopping.arfcns; - struct bitvec *ma = &ts->hopping.ma; - unsigned int num_cell_arfcns, bitnum, n_chan; - int i; - - /* re-set the MA to all-zero */ - ma->cur_bit = 0; - ts->hopping.ma_len = 0; - memset(ma->data, 0, ma->data_len); - - if (!ts->hopping.enabled) - return 0; - - /* count the number of ARFCNs in the cell channel allocation */ - num_cell_arfcns = 0; - for (i = 0; i < 1024; i++) { - if (bitvec_get_bit_pos(cell_chan, i)) - num_cell_arfcns++; - } - - /* pad it to octet-aligned number of bits */ - ts->hopping.ma_len = num_cell_arfcns / 8; - if (num_cell_arfcns % 8) - ts->hopping.ma_len++; - - n_chan = 0; - for (i = 0; i < 1024; i++) { - if (!bitvec_get_bit_pos(cell_chan, i)) - continue; - /* set the corresponding bit in the MA */ - bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; - if (bitvec_get_bit_pos(ts_arfcn, i)) - bitvec_set_bit_pos(ma, bitnum, 1); - else - bitvec_set_bit_pos(ma, bitnum, 0); - n_chan++; - } - - /* ARFCN 0 is special: It is coded last in the bitmask */ - if (bitvec_get_bit_pos(cell_chan, 0)) { - n_chan++; - /* set the corresponding bit in the MA */ - bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; - if (bitvec_get_bit_pos(ts_arfcn, 0)) - bitvec_set_bit_pos(ma, bitnum, 1); - else - bitvec_set_bit_pos(ma, bitnum, 0); - } - - return 0; -} - -static void bootstrap_rsl(struct gsm_bts_trx *trx) -{ - unsigned int i; - - LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) " - "on ARFCN %u using MCC=%u MNC=%u LAC=%u CID=%u BSIC=%u TSC=%u\n", - trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code, - bsc_gsmnet->network_code, trx->bts->location_area_code, - trx->bts->cell_identity, trx->bts->bsic, trx->bts->tsc); - - if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) { - rsl_nokia_si_begin(trx); - } - - set_system_infos(trx); - - if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) { - /* channel unspecific, power reduction in 2 dB steps */ - rsl_bs_power_control(trx, 0xFF, trx->max_power_red / 2); - rsl_nokia_si_end(trx); - } - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) - generate_ma_for_ts(&trx->ts[i]); -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - struct gsm_bts_trx *trx = isd->trx; - int ts_no, lchan_no; - - if (subsys != SS_L_INPUT) - return -EINVAL; - - switch (signal) { - case S_L_INP_TEI_UP: - if (isd->link_type == E1INP_SIGN_OML) { - /* TODO: this is required for the Nokia BTS, hopping is configured - during OML, other MA is not set. */ - struct gsm_bts_trx *cur_trx; - /* was static in system_information.c */ - extern int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts); - uint8_t ca[20]; - /* has to be called before generate_ma_for_ts to - set bts->si_common.cell_alloc */ - generate_cell_chan_list(ca, trx->bts); - - llist_for_each_entry(cur_trx, &trx->bts->trx_list, list) { - int i; - - for (i = 0; i < ARRAY_SIZE(cur_trx->ts); i++) - generate_ma_for_ts(&cur_trx->ts[i]); - } - } - if (isd->link_type == E1INP_SIGN_RSL) - bootstrap_rsl(trx); - break; - case S_L_INP_TEI_DN: - LOGP(DLMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx); - - if (isd->link_type == E1INP_SIGN_OML) - osmo_counter_inc(trx->bts->network->stats.bts.oml_fail); - else if (isd->link_type == E1INP_SIGN_RSL) - osmo_counter_inc(trx->bts->network->stats.bts.rsl_fail); - - /* - * free all allocated channels. change the nm_state so the - * trx and trx_ts becomes unusable and chan_alloc.c can not - * allocate from it. - */ - for (ts_no = 0; ts_no < ARRAY_SIZE(trx->ts); ++ts_no) { - struct gsm_bts_trx_ts *ts = &trx->ts[ts_no]; - - for (lchan_no = 0; lchan_no < ARRAY_SIZE(ts->lchan); ++lchan_no) { - if (ts->lchan[lchan_no].state != LCHAN_S_NONE) - lchan_free(&ts->lchan[lchan_no]); - lchan_reset(&ts->lchan[lchan_no]); - } - } - - gsm_bts_mo_reset(trx->bts); - - abis_nm_clear_queue(trx->bts); - break; - default: - break; - } - - return 0; -} - -static int bootstrap_bts(struct gsm_bts *bts) -{ - int i, n; - - if (bts->model->start && !bts->model->started) { - int ret = bts->model->start(bts->network); - if (ret < 0) - return ret; - - bts->model->started = true; - } - - /* FIXME: What about secondary TRX of a BTS? What about a BTS that has TRX - * in different bands? Why is 'band' a parameter of the BTS and not of the TRX? */ - switch (bts->band) { - case GSM_BAND_1800: - if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) { - LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n"); - return -EINVAL; - } - break; - case GSM_BAND_1900: - if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) { - LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n"); - return -EINVAL; - } - break; - case GSM_BAND_900: - if (bts->c0->arfcn < 1 || - (bts->c0->arfcn > 124 && bts->c0->arfcn < 955) || - bts->c0->arfcn > 1023) { - LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 1-124, 955-1023.\n"); - return -EINVAL; - } - break; - case GSM_BAND_850: - if (bts->c0->arfcn < 128 || bts->c0->arfcn > 251) { - LOGP(DNM, LOGL_ERROR, "GSM850 channel must be between 128-251.\n"); - return -EINVAL; - } - break; - default: - LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n"); - return -EINVAL; - } - - if (bts->network->auth_policy == GSM_AUTH_POLICY_ACCEPT_ALL && - !bts->si_common.rach_control.cell_bar) - LOGP(DNM, LOGL_ERROR, "\nWARNING: You are running an 'accept-all' " - "network on a BTS that is not barred. This " - "configuration is likely to interfere with production " - "GSM networks and should only be used in a RF " - "shielded environment such as a faraday cage!\n\n"); - - /* Control Channel Description is set from vty/config */ - - /* T3212 is set from vty/config */ - - /* Set ccch config by looking at ts config */ - for (n=0, i=0; i<8; i++) - n += bts->c0->ts[i].pchan == GSM_PCHAN_CCCH ? 1 : 0; - - switch (n) { - case 0: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C; - /* Limit reserved block to 2 on combined channel */ - if (bts->si_common.chan_desc.bs_ag_blks_res > 2) - bts->si_common.chan_desc.bs_ag_blks_res = 2; - break; - case 1: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_NC; - break; - case 2: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_2_NC; - break; - case 3: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_3_NC; - break; - case 4: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_4_NC; - break; - default: - LOGP(DNM, LOGL_ERROR, "Unsupported CCCH timeslot configuration\n"); - return -EINVAL; - } - - /* some defaults for our system information */ - bts->si_common.cell_options.radio_link_timeout = 7; /* 12 */ - - /* allow/disallow DTXu */ - if (bts->network->dtx_enabled) - bts->si_common.cell_options.dtx = 0; - else - bts->si_common.cell_options.dtx = 2; - - bts->si_common.cell_options.pwrc = 0; /* PWRC not set */ - - bts->si_common.cell_sel_par.acs = 0; - - bts->si_common.ncc_permitted = 0xff; - - /* Initialize the BTS state */ - gsm_bts_mo_reset(bts); - - return 0; -} - -int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *), - const char *config_file) -{ - struct telnet_connection dummy_conn; - struct gsm_bts *bts; - int rc; - - /* initialize our data structures */ - bsc_gsmnet = gsm_network_init(1, 1, mncc_recv); - if (!bsc_gsmnet) - return -ENOMEM; - - bsc_gsmnet->name_long = talloc_strdup(bsc_gsmnet, "OpenBSC"); - bsc_gsmnet->name_short = talloc_strdup(bsc_gsmnet, "OpenBSC"); - - /* our vty command code expects vty->priv to point to a telnet_connection */ - dummy_conn.priv = bsc_gsmnet; - rc = vty_read_config_file(config_file, &dummy_conn); - if (rc < 0) { - LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file); - return rc; - } - - rc = telnet_init(tall_bsc_ctx, bsc_gsmnet, 4242); - 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 66288a316..000000000 --- a/openbsc/src/libbsc/bsc_msc.c +++ /dev/null @@ -1,273 +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; - - 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 Connection timeout.\n"); - 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); - - if ((what & BSC_FD_WRITE) == 0) { - LOGP(DMSC, LOGL_ERROR, "Callback but not writable.\n"); - return -1; - } - - queue = container_of(fd, struct osmo_wqueue, bfd); - con = container_of(queue, struct bsc_msc_connection, write_queue); - - /* 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 socket failed.\n"); - goto error; - } - if (val != 0) { - LOGP(DMSC, LOGL_ERROR, "Not connected to the MSC: %d\n", val); - goto error; - } - - - /* go to full operation */ - fd->cb = 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.\n"); - 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 connections configured.\n"); - connection_loss(con); - return -1; - } - - /* move to the next connection */ - dest = (struct bsc_msc_dest *) con->dests->next; - llist_del(&dest->list); - llist_add_tail(&dest->list, con->dests); - - LOGP(DMSC, LOGL_NOTICE, "Attempting to connect MSC at %s:%d\n", - dest->ip, dest->port); - - con->is_connected = 0; - - fd = &con->write_queue.bfd; - fd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - fd->priv_nr = 1; - - if (fd->fd < 0) { - perror("Creating TCP socket failed"); - return fd->fd; - } - - /* make it non blocking */ - setnonblocking(fd); - - /* set the socket priority */ - ret = setsockopt(fd->fd, IPPROTO_IP, IP_TOS, - &dest->dscp, sizeof(dest->dscp)); - if (ret != 0) - LOGP(DMSC, LOGL_ERROR, "Failed to set DSCP to %d. %s\n", - dest->dscp, strerror(errno)); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(dest->port); - inet_aton(dest->ip, &sin.sin_addr); - - setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin)); - - if (ret == -1 && errno == EINPROGRESS) { - LOGP(DMSC, LOGL_ERROR, "MSC Connection in progress\n"); - fd->when = BSC_FD_WRITE; - fd->cb = msc_connection_connect; - con->timeout_timer.cb = msc_con_timeout; - con->timeout_timer.data = con; - 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; - 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); - - 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.\n"); - bsc_msc_connect(con); -} - -void bsc_msc_schedule_connect(struct bsc_msc_connection *con) -{ - LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC.\n"); - con->reconnect_timer.cb = reconnect_msc; - con->reconnect_timer.data = con; - osmo_timer_schedule(&con->reconnect_timer, 5, 0); -} - -struct msgb *bsc_msc_id_get_resp(const char *token) -{ - struct msgb *msg; - - if (!token) { - LOGP(DMSC, LOGL_ERROR, "No token specified.\n"); - return NULL; - } - - msg = msgb_alloc_headroom(4096, 128, "id resp"); - if (!msg) { - LOGP(DMSC, LOGL_ERROR, "Failed to create the message.\n"); - return NULL; - } - - msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP); - msgb_l16tv_put(msg, strlen(token) + 1, - IPAC_IDTAG_UNITNAME, (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 bd36e1884..000000000 --- a/openbsc/src/libbsc/bsc_rf_ctrl.c +++ /dev/null @@ -1,530 +0,0 @@ -/* RF Ctl handling socket */ - -/* (C) 2010 by Harald Welte - * (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 - -#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 || !bsc_data->rf_ctrl) - return OSMO_BSC_RF_POLICY_UNKNOWN; - - switch (bsc_data->rf_ctrl->policy) { - case S_RF_ON: - return OSMO_BSC_RF_POLICY_ON; - case S_RF_OFF: - return OSMO_BSC_RF_POLICY_OFF; - case S_RF_GRACE: - return OSMO_BSC_RF_POLICY_GRACE; - default: - return OSMO_BSC_RF_POLICY_UNKNOWN; - } -} - -static int lock_each_trx(struct gsm_network *net, int lock) -{ - struct gsm_bts *bts; - - llist_for_each_entry(bts, &net->bts_list, list) { - struct gsm_bts_trx *trx; - - /* Exclude the BTS from the global lock */ - if (bts->excl_from_rf_lock) { - LOGP(DLINP, LOGL_DEBUG, - "Excluding BTS(%d) from trx lock.\n", bts->nr); - continue; - } - - llist_for_each_entry(trx, &bts->trx_list, list) { - gsm_trx_lock_rf(trx, lock); - } - } - - return 0; -} - -static void send_resp(struct osmo_bsc_rf_conn *conn, char send) -{ - struct msgb *msg; - - msg = msgb_alloc(10, "RF Query"); - if (!msg) { - LOGP(DLINP, LOGL_ERROR, "Failed to allocate response msg.\n"); - return; - } - - msg->l2h = msgb_put(msg, 1); - msg->l2h[0] = send; - - if (osmo_wqueue_enqueue(&conn->queue, msg) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the answer.\n"); - msgb_free(msg); - return; - } - - return; -} - - -/* - * Send a - * 'g' when we are in grace mode - * '1' when one TRX is online, - * '0' otherwise - */ -static void handle_query(struct osmo_bsc_rf_conn *conn) -{ - struct gsm_bts *bts; - char send = RF_CMD_OFF; - - if (conn->rf->policy == S_RF_GRACE) - return send_resp(conn, RF_CMD_ON_G); - - llist_for_each_entry(bts, &conn->rf->gsm_network->bts_list, list) { - struct gsm_bts_trx *trx; - - /* Exclude the BTS from the global lock */ - if (bts->excl_from_rf_lock) { - LOGP(DLINP, LOGL_DEBUG, - "Excluding BTS(%d) from query.\n", bts->nr); - continue; - } - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->mo.nm_state.availability == NM_AVSTATE_OK && - trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) { - send = RF_CMD_ON; - break; - } - } - } - - send_resp(conn, send); -} - -static void rf_check_cb(void *_data) -{ - struct gsm_bts *bts; - struct osmo_bsc_rf *rf = _data; - - llist_for_each_entry(bts, &rf->gsm_network->bts_list, list) { - struct gsm_bts_trx *trx; - - /* don't bother to check a booting or missing BTS */ - if (!bts->oml_link || !is_ipaccess_bts(bts)) - continue; - - /* Exclude the BTS from the global lock */ - if (bts->excl_from_rf_lock) { - LOGP(DLINP, LOGL_DEBUG, - "Excluding BTS(%d) from query.\n", bts->nr); - continue; - } - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->mo.nm_state.availability != NM_AVSTATE_OK || - trx->mo.nm_state.operational != NM_OPSTATE_ENABLED || - trx->mo.nm_state.administrative != NM_STATE_UNLOCKED) { - LOGP(DNM, LOGL_ERROR, "RF activation failed. Starting again.\n"); - ipaccess_drop_oml(bts); - break; - } - } - } -} - -static void send_signal(struct osmo_bsc_rf *rf, int val) -{ - struct rf_signal_data sig; - sig.net = rf->gsm_network; - - rf->policy = val; - osmo_signal_dispatch(SS_RF, val, &sig); -} - -static int switch_rf_off(struct osmo_bsc_rf *rf) -{ - lock_each_trx(rf->gsm_network, 1); - send_signal(rf, S_RF_OFF); - - return 0; -} - -static void grace_timeout(void *_data) -{ - struct osmo_bsc_rf *rf = (struct osmo_bsc_rf *) _data; - - LOGP(DLINP, LOGL_NOTICE, "Grace timeout. Going to disable all BTS/TRX.\n"); - switch_rf_off(rf); -} - -static int enter_grace(struct osmo_bsc_rf *rf) -{ - if (osmo_timer_pending(&rf->grace_timeout)) { - LOGP(DLINP, LOGL_NOTICE, "RF Grace timer is pending. Not restarting.\n"); - return 0; - } - - rf->grace_timeout.cb = grace_timeout; - rf->grace_timeout.data = rf; - osmo_timer_schedule(&rf->grace_timeout, rf->gsm_network->bsc_data->mid_call_timeout, 0); - LOGP(DLINP, LOGL_NOTICE, "Going to switch RF off in %d seconds.\n", - rf->gsm_network->bsc_data->mid_call_timeout); - - send_signal(rf, S_RF_GRACE); - return 0; -} - -static void rf_delay_cmd_cb(void *data) -{ - struct osmo_bsc_rf *rf = data; - - switch (rf->last_request) { - case RF_CMD_D_OFF: - rf->last_state_command = "RF Direct Off"; - osmo_timer_del(&rf->rf_check); - osmo_timer_del(&rf->grace_timeout); - switch_rf_off(rf); - break; - case RF_CMD_ON: - rf->last_state_command = "RF Direct On"; - osmo_timer_del(&rf->grace_timeout); - lock_each_trx(rf->gsm_network, 0); - send_signal(rf, S_RF_ON); - osmo_timer_schedule(&rf->rf_check, 3, 0); - break; - case RF_CMD_OFF: - rf->last_state_command = "RF Scheduled Off"; - osmo_timer_del(&rf->rf_check); - enter_grace(rf); - break; - } -} - -static int rf_read_cmd(struct osmo_fd *fd) -{ - struct osmo_bsc_rf_conn *conn = fd->data; - char buf[1]; - int rc; - - rc = read(fd->fd, buf, sizeof(buf)); - if (rc != sizeof(buf)) { - LOGP(DLINP, LOGL_ERROR, "Short read %d/%s\n", errno, strerror(errno)); - osmo_fd_unregister(fd); - close(fd->fd); - osmo_wqueue_clear(&conn->queue); - talloc_free(conn); - return -1; - } - - switch (buf[0]) { - case RF_CMD_QUERY: - handle_query(conn); - break; - case RF_CMD_D_OFF: - case RF_CMD_ON: - case RF_CMD_OFF: - osmo_bsc_rf_schedule_lock(conn->rf, buf[0]); - break; - default: - conn->rf->last_state_command = "Unknown command"; - LOGP(DLINP, LOGL_ERROR, "Unknown command %d\n", buf[0]); - break; - } - - return 0; -} - -static int rf_write_cmd(struct osmo_fd *fd, struct msgb *msg) -{ - int rc; - - rc = write(fd->fd, msg->data, msg->len); - if (rc != msg->len) { - LOGP(DLINP, LOGL_ERROR, "Short write %d/%s\n", errno, strerror(errno)); - return -1; - } - - return 0; -} - -static int rf_ctrl_accept(struct osmo_fd *bfd, unsigned int what) -{ - struct osmo_bsc_rf_conn *conn; - struct osmo_bsc_rf *rf = bfd->data; - struct sockaddr_un addr; - socklen_t len = sizeof(addr); - int fd; - - fd = accept(bfd->fd, (struct sockaddr *) &addr, &len); - if (fd < 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to accept. errno: %d/%s\n", - errno, strerror(errno)); - return -1; - } - - conn = talloc_zero(rf, struct osmo_bsc_rf_conn); - if (!conn) { - LOGP(DLINP, LOGL_ERROR, "Failed to allocate mem.\n"); - close(fd); - return -1; - } - - osmo_wqueue_init(&conn->queue, 10); - conn->queue.bfd.data = conn; - conn->queue.bfd.fd = fd; - conn->queue.bfd.when = BSC_FD_READ | BSC_FD_WRITE; - conn->queue.read_cb = rf_read_cmd; - conn->queue.write_cb = rf_write_cmd; - conn->rf = rf; - - if (osmo_fd_register(&conn->queue.bfd) != 0) { - close(fd); - talloc_free(conn); - return -1; - } - - return 0; -} - -static void rf_auto_off_cb(void *_timer) -{ - struct osmo_bsc_rf *rf = _timer; - - LOGP(DLINP, LOGL_NOTICE, - "Going to switch off RF due lack of a MSC connection.\n"); - osmo_bsc_rf_schedule_lock(rf, RF_CMD_D_OFF); -} - -static int msc_signal_handler(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_network *net; - struct msc_signal_data *msc; - struct osmo_bsc_rf *rf; - - /* check if we want to handle this signal */ - if (subsys != SS_MSC) - return 0; - - net = handler_data; - msc = signal_data; - - /* check if we have the needed information */ - if (!net->bsc_data || !net->bsc_data->rf_ctrl) - 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; -} - -struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net) -{ - unsigned int namelen; - struct sockaddr_un local; - struct osmo_fd *bfd; - struct osmo_bsc_rf *rf; - int rc; - - rf = talloc_zero(NULL, struct osmo_bsc_rf); - if (!rf) { - LOGP(DLINP, LOGL_ERROR, "Failed to create osmo_bsc_rf.\n"); - return NULL; - } - - 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 NULL; - } - - local.sun_family = AF_UNIX; - strncpy(local.sun_path, path, sizeof(local.sun_path)); - local.sun_path[sizeof(local.sun_path) - 1] = '\0'; - unlink(local.sun_path); - - /* we use the same magic that X11 uses in Xtranssock.c for - * calculating the proper length of the sockaddr */ -#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) - local.sun_len = strlen(local.sun_path); -#endif -#if defined(BSD44SOCKETS) || defined(SUN_LEN) - namelen = SUN_LEN(&local); -#else - namelen = strlen(local.sun_path) + - offsetof(struct sockaddr_un, sun_path); -#endif - - rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); - if (rc != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to bind '%s' errno: %d/%s\n", - local.sun_path, errno, strerror(errno)); - close(bfd->fd); - talloc_free(rf); - return NULL; - } - - if (listen(bfd->fd, 0) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to listen: %d/%s\n", errno, strerror(errno)); - close(bfd->fd); - talloc_free(rf); - return NULL; - } - - 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); - talloc_free(rf); - return NULL; - } - - rf->gsm_network = net; - rf->policy = S_RF_ON; - rf->last_state_command = ""; - - /* check the rf state */ - rf->rf_check.data = rf; - rf->rf_check.cb = rf_check_cb; - - /* delay cmd handling */ - rf->delay_cmd.data = rf; - rf->delay_cmd.cb = rf_delay_cmd_cb; - - rf->auto_off_timer.data = rf; - rf->auto_off_timer.cb = rf_auto_off_cb; - - /* listen to RF signals */ - osmo_signal_register_handler(SS_MSC, msc_signal_handler, net); - - return rf; -} - -void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd) -{ - rf->last_request = cmd; - if (!osmo_timer_pending(&rf->delay_cmd)) - osmo_timer_schedule(&rf->delay_cmd, 1, 0); -} diff --git a/openbsc/src/libbsc/bsc_rll.c b/openbsc/src/libbsc/bsc_rll.c deleted file mode 100644 index d12aeeec9..000000000 --- a/openbsc/src/libbsc/bsc_rll.c +++ /dev/null @@ -1,141 +0,0 @@ -/* GSM BSC Radio Link Layer API - * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bsc_rll_req { - struct llist_head list; - struct osmo_timer_list timer; - - struct gsm_lchan *lchan; - uint8_t link_id; - - void (*cb)(struct gsm_lchan *lchan, uint8_t link_id, - void *data, enum bsc_rllr_ind); - void *data; -}; - -/* we only compare C1, C2 and SAPI */ -#define LINKID_MASK 0xC7 - -static LLIST_HEAD(bsc_rll_reqs); - -static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type) -{ - llist_del(&rllr->list); - rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type); - talloc_free(rllr); -} - -static void timer_cb(void *_rllr) -{ - struct bsc_rll_req *rllr = _rllr; - - complete_rllr(rllr, BSC_RLLR_IND_TIMEOUT); -} - -/* establish a RLL connection with given SAPI / priority */ -int rll_establish(struct gsm_lchan *lchan, uint8_t sapi, - void (*cb)(struct gsm_lchan *, uint8_t, void *, - enum bsc_rllr_ind), - void *data) -{ - struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req); - uint8_t link_id; - if (!rllr) - return -ENOMEM; - - link_id = sapi; - - /* If we are a TCH and not in signalling mode, we need to - * indicate that the new RLL connection is to be made on the SACCH */ - if ((lchan->type == GSM_LCHAN_TCH_F || - lchan->type == GSM_LCHAN_TCH_H) && sapi != 0) - link_id |= 0x40; - - rllr->lchan = lchan; - rllr->link_id = link_id; - rllr->cb = cb; - rllr->data = data; - - llist_add(&rllr->list, &bsc_rll_reqs); - - rllr->timer.cb = &timer_cb; - rllr->timer.data = rllr; - - osmo_timer_schedule(&rllr->timer, 10, 0); - - /* send the RSL RLL ESTablish REQuest */ - return rsl_establish_request(rllr->lchan, rllr->link_id); -} - -/* Called from RSL code in case we have received an indication regarding - * any RLL link */ -void rll_indication(struct gsm_lchan *lchan, 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_vty.c b/openbsc/src/libbsc/bsc_vty.c deleted file mode 100644 index 45df90f5f..000000000 --- a/openbsc/src/libbsc/bsc_vty.c +++ /dev/null @@ -1,3142 +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 "../../bscconfig.h" - - -#define NETWORK_STR "Configure the GSM network\n" -#define CODE_CMD_STR "Code commands\n" -#define NAME_CMD_STR "Name Commands\n" -#define NAME_STR "Name to use\n" -#define LCHAN_NR_STR "Logical Channel Number\n" - - -/* FIXME: this should go to some common file */ -static const struct value_string gprs_ns_timer_strs[] = { - { 0, "tns-block" }, - { 1, "tns-block-retries" }, - { 2, "tns-reset" }, - { 3, "tns-reset-retries" }, - { 4, "tns-test" }, - { 5, "tns-alive" }, - { 6, "tns-alive-retries" }, - { 0, NULL } -}; - -static const struct value_string gprs_bssgp_cfg_strs[] = { - { 0, "blocking-timer" }, - { 1, "blocking-retries" }, - { 2, "unblocking-retries" }, - { 3, "reset-timer" }, - { 4, "reset-retries" }, - { 5, "suspend-timer" }, - { 6, "suspend-retries" }, - { 7, "resume-timer" }, - { 8, "resume-retries" }, - { 9, "capability-update-timer" }, - { 10, "capability-update-retries" }, - { 0, NULL } -}; - -static const struct value_string bts_neigh_mode_strs[] = { - { NL_MODE_AUTOMATIC, "automatic" }, - { NL_MODE_MANUAL, "manual" }, - { NL_MODE_MANUAL_SI5SEP, "manual-si5" }, - { 0, NULL } -}; - -const struct value_string bts_loc_fix_names[] = { - { BTS_LOC_FIX_INVALID, "invalid" }, - { BTS_LOC_FIX_2D, "fix2d" }, - { BTS_LOC_FIX_3D, "fix3d" }, - { 0, NULL } -}; - -struct cmd_node net_node = { - GSMNET_NODE, - "%s(config-net)# ", - 1, -}; - -struct cmd_node bts_node = { - BTS_NODE, - "%s(config-net-bts)# ", - 1, -}; - -struct cmd_node trx_node = { - TRX_NODE, - "%s(config-net-bts-trx)# ", - 1, -}; - -struct cmd_node ts_node = { - TS_NODE, - "%s(config-net-bts-trx-ts)# ", - 1, -}; - -extern struct gsm_network *bsc_gsmnet; - -struct gsm_network *gsmnet_from_vty(struct vty *v) -{ - /* In case we read from the config file, the vty->priv cannot - * point to a struct telnet_connection, and thus conn->priv - * will not point to the gsm_network structure */ -#if 0 - struct telnet_connection *conn = v->priv; - return (struct gsm_network *) conn->priv; -#else - return bsc_gsmnet; -#endif -} - -static int dummy_config_write(struct vty *v) -{ - return CMD_SUCCESS; -} - -static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms) -{ - vty_out(vty,"Oper '%s', Admin '%s', Avail '%s'%s", - abis_nm_opstate_name(nms->operational), - get_value_string(abis_nm_adm_state_names, nms->administrative), - abis_nm_avail_name(nms->availability), VTY_NEWLINE); -} - -static void dump_pchan_load_vty(struct vty *vty, char *prefix, - const struct pchan_load *pl) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) { - const struct load_counter *lc = &pl->pchan[i]; - unsigned int percent; - - if (lc->total == 0) - continue; - - percent = (lc->used * 100) / lc->total; - - vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix, - gsm_pchan_name(i), percent, lc->used, lc->total, - VTY_NEWLINE); - } -} - -static void net_dump_vty(struct vty *vty, struct gsm_network *net) -{ - struct pchan_load pl; - - vty_out(vty, "BSC is on Country Code %u, Network Code %u " - "and has %u BTS%s", net->country_code, net->network_code, - net->num_bts, VTY_NEWLINE); - vty_out(vty, " Long network name: '%s'%s", - net->name_long, VTY_NEWLINE); - vty_out(vty, " Short network name: '%s'%s", - net->name_short, VTY_NEWLINE); - vty_out(vty, " Authentication policy: %s%s", - gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE); - vty_out(vty, " Location updating reject cause: %u%s", - net->reject_cause, VTY_NEWLINE); - vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption, - VTY_NEWLINE); - vty_out(vty, " NECI (TCH/H): %u%s", net->neci, - VTY_NEWLINE); - vty_out(vty, " Use TCH for Paging any: %d%s", net->pag_any_tch, - VTY_NEWLINE); - vty_out(vty, " RRLP Mode: %s%s", rrlp_mode_name(net->rrlp.mode), - VTY_NEWLINE); - vty_out(vty, " MM Info: %s%s", net->send_mm_info ? "On" : "Off", - VTY_NEWLINE); - vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off", - VTY_NEWLINE); - network_chan_load(&pl, net); - vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); - dump_pchan_load_vty(vty, " ", &pl); - - /* show rf */ - if (net->bsc_data && net->bsc_data->rf_ctrl) - vty_out(vty, " Last RF Command: %s%s", - net->bsc_data->rf_ctrl->last_state_command, - VTY_NEWLINE); -} - -DEFUN(show_net, show_net_cmd, "show network", - SHOW_STR "Display information about a GSM NETWORK\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - net_dump_vty(vty, net); - - return CMD_SUCCESS; -} - -static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l) -{ - struct e1inp_line *line; - - if (!e1l) { - vty_out(vty, " None%s", VTY_NEWLINE); - return; - } - - line = e1l->ts->line; - - vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s", - line->num, line->driver->name, e1l->ts->num, - e1inp_signtype_name(e1l->type), VTY_NEWLINE); - vty_out(vty, " E1 TEI %u, SAPI %u%s", - e1l->tei, e1l->sapi, VTY_NEWLINE); -} - -static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) -{ - struct pchan_load pl; - - vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, " - "BSIC %u, TSC %u and %u TRX%s", - bts->nr, btstype2str(bts->type), gsm_band_name(bts->band), - bts->cell_identity, - bts->location_area_code, bts->bsic, bts->tsc, - bts->num_trx, VTY_NEWLINE); - vty_out(vty, "Description: %s%s", - bts->description ? bts->description : "(null)", VTY_NEWLINE); - vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE); - vty_out(vty, "Minimum Rx Level for Access: %i dBm%s", - rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min), - VTY_NEWLINE); - vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s", - bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); - vty_out(vty, "RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer, - VTY_NEWLINE); - vty_out(vty, "RACH Max transmissions: %u%s", - rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), - VTY_NEWLINE); - if (bts->si_common.rach_control.cell_bar) - vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE); - vty_out(vty, "Channel Description Attachment: %s%s", - (bts->si_common.chan_desc.att) ? "yes" : "no", VTY_NEWLINE); - vty_out(vty, "Channel Description BS-PA-MFRMS: %u%s", - bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE); - vty_out(vty, "Channel Description BS-AG_BLKS-RES: %u%s", - bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE); - vty_out(vty, "System Information present: 0x%08x, static: 0x%08x%s", - bts->si_valid, bts->si_mode_static, VTY_NEWLINE); - if (is_ipaccess_bts(bts)) - vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s", - bts->ip_access.site_id, bts->ip_access.bts_id, - bts->oml_tei, VTY_NEWLINE); - else if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) - vty_out(vty, " Skip Reset: %d%s", - bts->nokia.skip_reset, VTY_NEWLINE); - vty_out(vty, " NM State: "); - net_dump_nmstate(vty, &bts->mo.nm_state); - vty_out(vty, " Site Mgr NM State: "); - net_dump_nmstate(vty, &bts->site_mgr.mo.nm_state); - vty_out(vty, " GPRS NSE: "); - net_dump_nmstate(vty, &bts->gprs.nse.mo.nm_state); - vty_out(vty, " GPRS CELL: "); - net_dump_nmstate(vty, &bts->gprs.cell.mo.nm_state); - vty_out(vty, " GPRS NSVC0: "); - net_dump_nmstate(vty, &bts->gprs.nsvc[0].mo.nm_state); - vty_out(vty, " GPRS NSVC1: "); - net_dump_nmstate(vty, &bts->gprs.nsvc[1].mo.nm_state); - vty_out(vty, " Paging: %u pending requests, %u free slots%s", - paging_pending_requests_nr(bts), - bts->paging.available_slots, VTY_NEWLINE); - if (is_ipaccess_bts(bts)) { - vty_out(vty, " OML Link state: %s.%s", - bts->oml_link ? "connected" : "disconnected", VTY_NEWLINE); - } else { - vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); - e1isl_dump_vty(vty, bts->oml_link); - } - - /* FIXME: chan_desc */ - memset(&pl, 0, sizeof(pl)); - bts_chan_load(&pl, bts); - vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); - dump_pchan_load_vty(vty, " ", &pl); -} - -DEFUN(show_bts, show_bts_cmd, "show bts [<0-255>]", - SHOW_STR "Display information about a BTS\n" - "BTS number") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - int bts_nr; - - if (argc != 0) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS '%s'%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); - return CMD_SUCCESS; - } - /* print all BTS's */ - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) - bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); - - return CMD_SUCCESS; -} - -/* utility functions */ -static void parse_e1_link(struct gsm_e1_subslot *e1_link, const char *line, - const char *ts, const char *ss) -{ - e1_link->e1_nr = atoi(line); - e1_link->e1_ts = atoi(ts); - if (!strcmp(ss, "full")) - e1_link->e1_ts_ss = 255; - else - e1_link->e1_ts_ss = atoi(ss); -} - -static void config_write_e1_link(struct vty *vty, struct gsm_e1_subslot *e1_link, - const char *prefix) -{ - if (!e1_link->e1_ts) - return; - - if (e1_link->e1_ts_ss == 255) - vty_out(vty, "%se1 line %u timeslot %u sub-slot full%s", - prefix, e1_link->e1_nr, e1_link->e1_ts, VTY_NEWLINE); - else - vty_out(vty, "%se1 line %u timeslot %u sub-slot %u%s", - prefix, e1_link->e1_nr, e1_link->e1_ts, - e1_link->e1_ts_ss, VTY_NEWLINE); -} - - -static void config_write_ts_single(struct vty *vty, struct gsm_bts_trx_ts *ts) -{ - vty_out(vty, " timeslot %u%s", ts->nr, VTY_NEWLINE); - if (ts->tsc != -1 && ts->tsc != ts->trx->bts->tsc) - vty_out(vty, " training_sequence_code %u%s", ts->tsc, VTY_NEWLINE); - if (ts->pchan != GSM_PCHAN_NONE) - vty_out(vty, " phys_chan_config %s%s", - gsm_pchan_name(ts->pchan), VTY_NEWLINE); - vty_out(vty, " hopping enabled %u%s", - ts->hopping.enabled, VTY_NEWLINE); - if (ts->hopping.enabled) { - unsigned int i; - vty_out(vty, " hopping sequence-number %u%s", - ts->hopping.hsn, VTY_NEWLINE); - vty_out(vty, " hopping maio %u%s", - ts->hopping.maio, VTY_NEWLINE); - for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { - if (!bitvec_get_bit_pos(&ts->hopping.arfcns, i)) - continue; - vty_out(vty, " hopping arfcn add %u%s", - i, VTY_NEWLINE); - } - } - config_write_e1_link(vty, &ts->e1_link, " "); - - if (ts->trx->bts->model->config_write_ts) - ts->trx->bts->model->config_write_ts(vty, ts); -} - -static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx) -{ - int i; - - vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE); - if (trx->description) - vty_out(vty, " description %s%s", trx->description, - VTY_NEWLINE); - vty_out(vty, " rf_locked %u%s", - trx->mo.nm_state.administrative == NM_STATE_LOCKED ? 1 : 0, - VTY_NEWLINE); - vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE); - vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE); - vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE); - config_write_e1_link(vty, &trx->rsl_e1_link, " rsl "); - vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE); - - if (trx->bts->model->config_write_trx) - trx->bts->model->config_write_trx(vty, trx); - - for (i = 0; i < TRX_NR_TS; i++) - config_write_ts_single(vty, &trx->ts[i]); -} - -static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts) -{ - unsigned int i; - vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode), - VTY_NEWLINE); - if (bts->gprs.mode == BTS_GPRS_NONE) - return; - - vty_out(vty, " gprs routing area %u%s", bts->gprs.rac, - VTY_NEWLINE); - vty_out(vty, " gprs network-control-order nc%u%s", - bts->gprs.net_ctrl_ord, VTY_NEWLINE); - vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci, - VTY_NEWLINE); - for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++) - vty_out(vty, " gprs cell timer %s %u%s", - get_value_string(gprs_bssgp_cfg_strs, i), - bts->gprs.cell.timer[i], VTY_NEWLINE); - vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei, - VTY_NEWLINE); - for (i = 0; i < ARRAY_SIZE(bts->gprs.nse.timer); i++) - vty_out(vty, " gprs ns timer %s %u%s", - get_value_string(gprs_ns_timer_strs, i), - bts->gprs.nse.timer[i], VTY_NEWLINE); - for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { - struct gsm_bts_gprs_nsvc *nsvc = - &bts->gprs.nsvc[i]; - struct in_addr ia; - - ia.s_addr = htonl(nsvc->remote_ip); - vty_out(vty, " gprs nsvc %u nsvci %u%s", i, - nsvc->nsvci, VTY_NEWLINE); - vty_out(vty, " gprs nsvc %u local udp port %u%s", i, - nsvc->local_port, VTY_NEWLINE); - vty_out(vty, " gprs nsvc %u remote udp port %u%s", i, - nsvc->remote_port, VTY_NEWLINE); - vty_out(vty, " gprs nsvc %u remote ip %s%s", i, - inet_ntoa(ia), VTY_NEWLINE); - } -} - -static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - int i; - - vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE); - vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE); - if (bts->description) - vty_out(vty, " description %s%s", bts->description, VTY_NEWLINE); - vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE); - vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE); - vty_out(vty, " location_area_code %u%s", bts->location_area_code, - VTY_NEWLINE); - vty_out(vty, " training_sequence_code %u%s", bts->tsc, VTY_NEWLINE); - vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE); - if (bts->tz.override != 0) - vty_out(vty, " timezone %d %d%s", bts->tz.hr, bts->tz.mn, VTY_NEWLINE); - vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE); - vty_out(vty, " cell reselection hysteresis %u%s", - bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); - vty_out(vty, " rxlev access min %u%s", - bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE); - - if (bts->si_common.cell_ro_sel_par.present) { - struct gsm48_si_selection_params *sp; - sp = &bts->si_common.cell_ro_sel_par; - - if (sp->cbq) - vty_out(vty, " cell bar qualify %u%s", - sp->cbq, VTY_NEWLINE); - - if (sp->cell_resel_off) - vty_out(vty, " cell reselection offset %u%s", - sp->cell_resel_off*2, VTY_NEWLINE); - - if (sp->temp_offs == 7) - vty_out(vty, " temporary offset infinite%s", - VTY_NEWLINE); - else if (sp->temp_offs) - vty_out(vty, " temporary offset %u%s", - sp->temp_offs*10, VTY_NEWLINE); - - if (sp->penalty_time == 31) - vty_out(vty, " penalty time reserved%s", - VTY_NEWLINE); - else if (sp->penalty_time) - vty_out(vty, " penalty time %u%s", - (sp->penalty_time*20)+20, VTY_NEWLINE); - } - - /* Is periodic LU enabled or disabled? */ - if (bts->si_common.chan_desc.t3212 == 0) - vty_out(vty, " no periodic location update%s", VTY_NEWLINE); - else - vty_out(vty, " periodic location update %u%s", - bts->si_common.chan_desc.t3212 * 6, VTY_NEWLINE); - - vty_out(vty, " 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); - for (i = SYSINFO_TYPE_1; i < _MAX_SYSINFO_TYPE; i++) { - if (bts->si_mode_static & (1 << i)) { - vty_out(vty, " system-information %s mode static%s", - get_value_string(osmo_sitype_strs, i), VTY_NEWLINE); - vty_out(vty, " system-information %s static %s%s", - get_value_string(osmo_sitype_strs, i), - osmo_hexdump_nospc(bts->si_buf[i], sizeof(bts->si_buf[i])), - VTY_NEWLINE); - } - } - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMO_SYSMO: - vty_out(vty, " ip.access unit_id %u %u%s", - bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE); - if (bts->ip_access.rsl_ip) { - struct in_addr ia; - ia.s_addr = htonl(bts->ip_access.rsl_ip); - vty_out(vty, " ip.access rsl-ip %s%s", inet_ntoa(ia), - VTY_NEWLINE); - } - vty_out(vty, " oml ip.access stream_id %u line %u%s", - bts->oml_tei, bts->oml_e1_link.e1_nr, VTY_NEWLINE); - break; - case GSM_BTS_TYPE_NOKIA_SITE: - vty_out(vty, " nokia_site skip-reset %d%s", bts->nokia.skip_reset, VTY_NEWLINE); - break; - 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); - } - } - - config_write_bts_gprs(vty, bts); - - if (bts->excl_from_rf_lock) - vty_out(vty, " rf-lock-exclude%s", VTY_NEWLINE); - - if (bts->model->config_write_bts) - bts->model->config_write_bts(vty, bts); - - llist_for_each_entry(trx, &bts->trx_list, list) - config_write_trx_single(vty, trx); -} - -static int config_write_bts(struct vty *v) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(v); - struct gsm_bts *bts; - - llist_for_each_entry(bts, &gsmnet->bts_list, list) - config_write_bts_single(v, bts); - - return CMD_SUCCESS; -} - -static int config_write_net(struct vty *vty) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - vty_out(vty, "network%s", VTY_NEWLINE); - vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE); - vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE); - vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE); - vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE); - vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE); - vty_out(vty, " location updating reject cause %u%s", - gsmnet->reject_cause, VTY_NEWLINE); - vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE); - vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE); - vty_out(vty, " paging any use tch %d%s", gsmnet->pag_any_tch, VTY_NEWLINE); - vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode), - VTY_NEWLINE); - vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE); - vty_out(vty, " handover %u%s", gsmnet->handover.active, VTY_NEWLINE); - vty_out(vty, " handover window rxlev averaging %u%s", - gsmnet->handover.win_rxlev_avg, VTY_NEWLINE); - vty_out(vty, " handover window rxqual averaging %u%s", - gsmnet->handover.win_rxqual_avg, VTY_NEWLINE); - vty_out(vty, " handover window rxlev neighbor averaging %u%s", - gsmnet->handover.win_rxlev_avg_neigh, VTY_NEWLINE); - vty_out(vty, " handover power budget interval %u%s", - gsmnet->handover.pwr_interval, VTY_NEWLINE); - vty_out(vty, " handover power budget hysteresis %u%s", - gsmnet->handover.pwr_hysteresis, VTY_NEWLINE); - vty_out(vty, " handover maximum distance %u%s", - gsmnet->handover.max_distance, VTY_NEWLINE); - vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE); - vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE); - vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE); - vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE); - vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE); - vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE); - vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE); - vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE); - vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE); - vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE); - vty_out(vty, " timer t3122 %u%s", gsmnet->T3122, VTY_NEWLINE); - vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE); - vty_out(vty, " dtx-used %u%s", gsmnet->dtx_enabled, VTY_NEWLINE); - vty_out(vty, " subscriber-keep-in-ram %d%s", - gsmnet->keep_subscr, VTY_NEWLINE); - - return CMD_SUCCESS; -} - -static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx) -{ - vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s", - trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE); - vty_out(vty, "Description: %s%s", - trx->description ? trx->description : "(null)", VTY_NEWLINE); - vty_out(vty, " RF Nominal Power: %d dBm, reduced by %u dB, " - "resulting BS power: %d dBm%s", - trx->nominal_power, trx->max_power_red, - trx->nominal_power - trx->max_power_red, VTY_NEWLINE); - vty_out(vty, " NM State: "); - net_dump_nmstate(vty, &trx->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), - ts->tsc == -1 ? ts->trx->bts->tsc : ts->tsc); - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) - vty_out(vty, " (%s mode)", - ts->flags & TS_F_PDCH_MODE ? "PDCH" : "TCH/F"); - vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, " NM State: "); - net_dump_nmstate(vty, &ts->mo.nm_state); - if (!is_ipaccess_bts(ts->trx->bts)) - vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s", - ts->e1_link.e1_nr, ts->e1_link.e1_ts, - ts->e1_link.e1_ts_ss, VTY_NEWLINE); -} - -DEFUN(show_ts, - show_ts_cmd, - "show timeslot [<0-255>] [<0-255>] [<0-7>]", - SHOW_STR "Display information about a TS\n" - "BTS Number\n" "TRX Number\n" "Timeslot Number\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts = NULL; - struct gsm_bts_trx *trx = NULL; - struct gsm_bts_trx_ts *ts = NULL; - int bts_nr, trx_nr, ts_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS '%s'%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = gsm_bts_num(net, bts_nr); - } - if (argc >= 2) { - trx_nr = atoi(argv[1]); - if (trx_nr >= bts->num_trx) { - vty_out(vty, "%% can't find TRX '%s'%s", argv[1], - VTY_NEWLINE); - return CMD_WARNING; - } - trx = gsm_bts_trx_num(bts, trx_nr); - } - if (argc >= 3) { - ts_nr = atoi(argv[2]); - if (ts_nr >= TRX_NR_TS) { - vty_out(vty, "%% can't find TS '%s'%s", argv[2], - VTY_NEWLINE); - return CMD_WARNING; - } - /* Fully Specified: print and exit */ - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - return CMD_SUCCESS; - } - - if (bts && trx) { - /* Iterate over all TS in this TRX */ - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - } - } else if (bts) { - /* Iterate over all TRX in this BTS, TS in each TRX */ - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = gsm_bts_trx_num(bts, trx_nr); - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - } - } - } else { - /* Iterate over all BTS, TRX in each BTS, TS in each TRX */ - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = gsm_bts_num(net, bts_nr); - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = gsm_bts_trx_num(bts, trx_nr); - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - } - } - } - } - - return CMD_SUCCESS; -} - -static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr) -{ - vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, - subscr->authorized, VTY_NEWLINE); - if (subscr->name) - vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); - if (subscr->extension) - vty_out(vty, " Extension: %s%s", subscr->extension, - VTY_NEWLINE); - vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); - if (subscr->tmsi != GSM_RESERVED_TMSI) - vty_out(vty, " TMSI: %08X%s", subscr->tmsi, - VTY_NEWLINE); - - vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); -} - -static void meas_rep_dump_uni_vty(struct vty *vty, - struct gsm_meas_rep_unidir *mru, - const char *prefix, - const char *dir) -{ - vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ", - prefix, dir, rxlev2dbm(mru->full.rx_lev), - dir, rxlev2dbm(mru->sub.rx_lev)); - vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s", - dir, mru->full.rx_qual, dir, mru->sub.rx_qual, - VTY_NEWLINE); -} - -static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr, - const char *prefix) -{ - vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE); - vty_out(vty, "%s Flags: %s%s%s%s%s", prefix, - mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "", - mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "", - mr->flags & MEAS_REP_F_FPC ? "FPC " : "", - mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ", - VTY_NEWLINE); - if (mr->flags & MEAS_REP_F_MS_TO) - vty_out(vty, "%s MS Timing Offset: %u%s", prefix, - mr->ms_timing_offset, VTY_NEWLINE); - if (mr->flags & MEAS_REP_F_MS_L1) - vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s", - prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE); - if (mr->flags & MEAS_REP_F_DL_VALID) - meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl"); - meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul"); -} - -static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan) -{ - int idx; - - vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u: Type %s%s", - lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, - lchan->nr, gsm_lchant_name(lchan->type), VTY_NEWLINE); - vty_out(vty, " Connection: %u, State: %s%s", - lchan->conn ? 1: 0, - gsm_lchans_name(lchan->state), VTY_NEWLINE); - vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s", - lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red - - lchan->bs_power*2, - ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power), - VTY_NEWLINE); - if (lchan->conn && lchan->conn->subscr) { - vty_out(vty, " Subscriber:%s", VTY_NEWLINE); - subscr_dump_vty(vty, lchan->conn->subscr); - } else - vty_out(vty, " No Subscriber%s", VTY_NEWLINE); - if (is_ipaccess_bts(lchan->ts->trx->bts)) { - struct in_addr ia; - ia.s_addr = htonl(lchan->abis_ip.bound_ip); - vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s", - inet_ntoa(ia), lchan->abis_ip.bound_port, - lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id, - VTY_NEWLINE); - } - - /* we want to report the last measurement report */ - idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, 1); - meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " "); -} - -static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan) -{ - struct gsm_meas_rep *mr; - int idx; - - /* we want to report the last measurement report */ - idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, 1); - mr = &lchan->meas_rep[idx]; - - vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u, Type %s - " - "L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s", - lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, - lchan->nr, gsm_lchant_name(lchan->type), mr->ms_l1.pwr, - rxlev2dbm(mr->dl.full.rx_lev), - rxlev2dbm(mr->ul.full.rx_lev), - VTY_NEWLINE); -} - - -static int dump_lchan_trx_ts(struct gsm_bts_trx_ts *ts, struct vty *vty, - void (*dump_cb)(struct vty *, struct gsm_lchan *)) -{ - int lchan_nr; - for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; lchan_nr++) { - struct gsm_lchan *lchan = &ts->lchan[lchan_nr]; - if ((lchan->type == GSM_LCHAN_NONE) && (lchan->state == LCHAN_S_NONE)) - continue; - dump_cb(vty, lchan); - } - - return CMD_SUCCESS; -} - -static int dump_lchan_trx(struct gsm_bts_trx *trx, struct vty *vty, - void (*dump_cb)(struct vty *, struct gsm_lchan *)) -{ - int ts_nr; - - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; - dump_lchan_trx_ts(ts, vty, dump_cb); - } - - return CMD_SUCCESS; -} - -static int dump_lchan_bts(struct gsm_bts *bts, struct vty *vty, - void (*dump_cb)(struct vty *, struct gsm_lchan *)) -{ - int trx_nr; - - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_nr); - dump_lchan_trx(trx, vty, dump_cb); - } - - return CMD_SUCCESS; -} - -static int lchan_summary(struct vty *vty, int argc, const char **argv, - void (*dump_cb)(struct vty *, struct gsm_lchan *)) -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - struct gsm_lchan *lchan; - int bts_nr, trx_nr, ts_nr, lchan_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = gsm_bts_num(net, bts_nr); - - if (argc == 1) - return dump_lchan_bts(bts, vty, dump_cb); - } - if (argc >= 2) { - trx_nr = atoi(argv[1]); - if (trx_nr >= bts->num_trx) { - vty_out(vty, "%% can't find TRX %s%s", argv[1], - VTY_NEWLINE); - return CMD_WARNING; - } - trx = gsm_bts_trx_num(bts, trx_nr); - - if (argc == 2) - return dump_lchan_trx(trx, vty, dump_cb); - } - if (argc >= 3) { - ts_nr = atoi(argv[2]); - if (ts_nr >= TRX_NR_TS) { - vty_out(vty, "%% can't find TS %s%s", argv[2], - VTY_NEWLINE); - return CMD_WARNING; - } - ts = &trx->ts[ts_nr]; - - if (argc == 3) - return dump_lchan_trx_ts(ts, vty, dump_cb); - } - if (argc >= 4) { - lchan_nr = atoi(argv[3]); - if (lchan_nr >= TS_MAX_LCHAN) { - vty_out(vty, "%% can't find LCHAN %s%s", argv[3], - VTY_NEWLINE); - return CMD_WARNING; - } - lchan = &ts->lchan[lchan_nr]; - dump_cb(vty, lchan); - return CMD_SUCCESS; - } - - - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = gsm_bts_num(net, bts_nr); - dump_lchan_bts(bts, vty, dump_cb); - } - - return CMD_SUCCESS; -} - - -DEFUN(show_lchan, - show_lchan_cmd, - "show lchan [<0-255>] [<0-255>] [<0-7>] [lchan_nr]", - SHOW_STR "Display information about a logical channel\n" - "BTS Number\n" "TRX Number\n" "Timeslot Number\n" - LCHAN_NR_STR) - -{ - return lchan_summary(vty, argc, argv, lchan_dump_full_vty); -} - -DEFUN(show_lchan_summary, - show_lchan_summary_cmd, - "show lchan summary [<0-255>] [<0-255>] [<0-7>] [lchan_nr]", - SHOW_STR "Display information about a logical channel\n" - "Short summary\n" - "BTS Number\n" "TRX Number\n" "Timeslot Number\n" - LCHAN_NR_STR) -{ - return lchan_summary(vty, argc, argv, lchan_dump_short_vty); -} - -static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag) -{ - vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE); - subscr_dump_vty(vty, pag->subscr); -} - -static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts) -{ - struct gsm_paging_request *pag; - - if (!bts->paging.bts) - return; - - llist_for_each_entry(pag, &bts->paging.pending_requests, entry) - paging_dump_vty(vty, pag); -} - -DEFUN(show_paging, - show_paging_cmd, - "show paging [<0-255>]", - SHOW_STR "Display information about paging reuqests of a BTS\n" - "BTS Number\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts; - int bts_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = gsm_bts_num(net, bts_nr); - bts_paging_dump_vty(vty, bts); - - return CMD_SUCCESS; - } - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = gsm_bts_num(net, bts_nr); - bts_paging_dump_vty(vty, bts); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_net, - cfg_net_cmd, - "network", NETWORK_STR) -{ - vty->index = gsmnet_from_vty(vty); - vty->node = GSMNET_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ncc, - cfg_net_ncc_cmd, - "network country code <1-999>", - "Set the GSM network country code\n" - "Country commands\n" - CODE_CMD_STR - "Network Country Code to use\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->country_code = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_mnc, - cfg_net_mnc_cmd, - "mobile network code <0-999>", - "Set the GSM mobile network code\n" - "Network Commands\n" - CODE_CMD_STR - "Mobile Network Code to use\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->network_code = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_name_short, - cfg_net_name_short_cmd, - "short name NAME", - "Set the short GSM network name\n" NAME_CMD_STR NAME_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - bsc_replace_string(gsmnet, &gsmnet->name_short, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_name_long, - cfg_net_name_long_cmd, - "long name NAME", - "Set the long GSM network name\n" NAME_CMD_STR NAME_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - bsc_replace_string(gsmnet, &gsmnet->name_long, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_auth_policy, - cfg_net_auth_policy_cmd, - "auth policy (closed|accept-all|token)", - "Authentication (not cryptographic)\n" - "Set the GSM network authentication policy\n" - "Require the MS to be activated in HLR\n" - "Accept all MS, whether in HLR or not\n" - "Use SMS-token based authentication\n") -{ - enum gsm_auth_policy policy = gsm_auth_policy_parse(argv[0]); - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->auth_policy = policy; - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_reject_cause, - cfg_net_reject_cause_cmd, - "location updating reject cause <2-111>", - "Set the reject cause of location updating reject\n" - "Set the reject cause of location updating reject\n" - "Set the reject cause of location updating reject\n" - "Set the reject cause of location updating reject\n" - "Cause Value as Per GSM TS 04.08\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->reject_cause = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_encryption, - cfg_net_encryption_cmd, - "encryption a5 (0|1|2|3)", - "Encryption options\n" - "A5 encryption\n" "A5/0: No encryption\n" - "A5/1: Encryption\n" "A5/2: Export-grade Encryption\n" - "A5/3: 'New' Secure Encryption\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->a5_encryption= atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_neci, - cfg_net_neci_cmd, - "neci (0|1)", - "New Establish Cause Indication\n" - "Don't set the NECI bit\n" "Set the NECI bit\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->neci = atoi(argv[0]); - gsm_net_update_ctype(gsmnet); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd, - "rrlp mode (none|ms-based|ms-preferred|ass-preferred)", - "Radio Resource Location Protocol\n" - "Set the Radio Resource Location Protocol Mode\n" - "Don't send RRLP request\n" - "Request MS-based location\n" - "Request any location, prefer MS-based\n" - "Request any location, prefer MS-assisted\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd, - "mm info (0|1)", - "Mobility Management\n" - "Send MM INFO after LOC UPD ACCEPT\n" - "Disable\n" "Enable\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->send_mm_info = atoi(argv[0]); - - return CMD_SUCCESS; -} - -#define HANDOVER_STR "Handover Options\n" - -DEFUN(cfg_net_handover, cfg_net_handover_cmd, - "handover (0|1)", - HANDOVER_STR - "Don't perform in-call handover\n" - "Perform in-call handover\n") -{ - int enable = atoi(argv[0]); - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - if (enable && ipacc_rtp_direct) { - vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode " - "is enabled by using the -P command line option%s", - VTY_NEWLINE); - return CMD_WARNING; - } - gsmnet->handover.active = enable; - - return CMD_SUCCESS; -} - -#define HO_WIN_STR HANDOVER_STR "Measurement Window\n" -#define HO_WIN_RXLEV_STR HO_WIN_STR "Received Level Averaging\n" -#define HO_WIN_RXQUAL_STR HO_WIN_STR "Received Quality Averaging\n" -#define HO_PBUDGET_STR HANDOVER_STR "Power Budget\n" -#define HO_AVG_COUNT_STR "Amount to use for Averaging\n" - -DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd, - "handover window rxlev averaging <1-10>", - HO_WIN_RXLEV_STR - "How many RxLev measurements are used for averaging\n" - HO_AVG_COUNT_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.win_rxlev_avg = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd, - "handover window rxqual averaging <1-10>", - HO_WIN_RXQUAL_STR - "How many RxQual measurements are used for averaging\n" - HO_AVG_COUNT_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.win_rxqual_avg = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd, - "handover window rxlev neighbor averaging <1-10>", - HO_WIN_RXLEV_STR "Neighbor\n" - "How many RxQual measurements are used for averaging\n" - HO_AVG_COUNT_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd, - "handover power budget interval <1-99>", - HO_PBUDGET_STR - "How often to check if we have a better cell (SACCH frames)\n" - "Interval\n" "Number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.pwr_interval = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd, - "handover power budget hysteresis <0-999>", - HO_PBUDGET_STR - "How many dB does a neighbor to be stronger to become a HO candidate\n" - "Hysteresis\n" "Number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.pwr_hysteresis = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd, - "handover maximum distance <0-9999>", - HANDOVER_STR - "How big is the maximum timing advance before HO is forced\n" - "Distance\n" "Number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.max_distance = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_pag_any_tch, - cfg_net_pag_any_tch_cmd, - "paging any use tch (0|1)", - "Assign a TCH when receiving a Paging Any request\n" - "Any Channel\n" "Use\n" "TCH\n" - "Do not use TCH for Paging Request Any\n" - "Do use TCH for Paging Request Any\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->pag_any_tch = atoi(argv[0]); - gsm_net_update_ctype(gsmnet); - return CMD_SUCCESS; -} - -#define DECLARE_TIMER(number, doc) \ - DEFUN(cfg_net_T##number, \ - cfg_net_T##number##_cmd, \ - "timer t" #number " <0-65535>", \ - "Configure GSM Timers\n" \ - doc "Timer Value\n") \ -{ \ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); \ - int value = atoi(argv[0]); \ - \ - if (value < 0 || value > 65535) { \ - vty_out(vty, "Timer value %s out of range.%s", \ - argv[0], VTY_NEWLINE); \ - return CMD_WARNING; \ - } \ - \ - gsmnet->T##number = value; \ - return CMD_SUCCESS; \ -} - -DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT.\n") -DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.\n") -DECLARE_TIMER(3105, "Set the timer for repetition of PHYSICAL INFORMATION.\n") -DECLARE_TIMER(3107, "Currently not used.\n") -DECLARE_TIMER(3109, "Set the RSL SACCH deactivation timeout.\n") -DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF Channel.\n") -DECLARE_TIMER(3113, "Set the time to try paging a subscriber.\n") -DECLARE_TIMER(3115, "Currently not used.\n") -DECLARE_TIMER(3117, "Currently not used.\n") -DECLARE_TIMER(3119, "Currently not used.\n") -DECLARE_TIMER(3122, "Waiting time (seconds) after IMM ASS REJECT\n") -DECLARE_TIMER(3141, "Currently not used.\n") - -DEFUN(cfg_net_dtx, - cfg_net_dtx_cmd, - "dtx-used (0|1)", - "Enable the usage of DTX.\n" - "DTX is disabled\n" "DTX is enabled\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->dtx_enabled = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_subscr_keep, - cfg_net_subscr_keep_cmd, - "subscriber-keep-in-ram (0|1)", - "Keep unused subscribers in RAM.\n" - "Delete unused subscribers\n" "Keep unused subscribers\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->keep_subscr = atoi(argv[0]); - return CMD_SUCCESS; -} - -/* per-BTS configuration */ -DEFUN(cfg_bts, - cfg_bts_cmd, - "bts <0-255>", - "Select a BTS to configure\n" - "BTS Number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - int bts_nr = atoi(argv[0]); - struct gsm_bts *bts; - - if (bts_nr > gsmnet->num_bts) { - vty_out(vty, "%% The next unused BTS number is %u%s", - gsmnet->num_bts, VTY_NEWLINE); - return CMD_WARNING; - } else if (bts_nr == gsmnet->num_bts) { - /* allocate a new one */ - bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_UNKNOWN, - HARDCODED_TSC, HARDCODED_BSIC); - } else - bts = gsm_bts_num(gsmnet, bts_nr); - - if (!bts) { - vty_out(vty, "%% Unable to allocate BTS %u%s", - gsmnet->num_bts, VTY_NEWLINE); - return CMD_WARNING; - } - - vty->index = bts; - vty->index_sub = &bts->description; - vty->node = BTS_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_type, - cfg_bts_type_cmd, - "type TYPE", /* dynamically created */ - "Set the BTS type\n" "Type\n") -{ - struct gsm_bts *bts = vty->index; - int rc; - - rc = gsm_set_bts_type(bts, parse_btstype(argv[0])); - if (rc < 0) - return CMD_WARNING; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_band, - cfg_bts_band_cmd, - "band BAND", - "Set the frequency band of this BTS\n" "Frequency band\n") -{ - struct gsm_bts *bts = vty->index; - int band = gsm_band_parse(argv[0]); - - if (band < 0) { - vty_out(vty, "%% BAND %d is not a valid GSM band%s", - band, VTY_NEWLINE); - return CMD_WARNING; - } - - bts->band = band; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ci, - cfg_bts_ci_cmd, - "cell_identity <0-65535>", - "Set the Cell identity of this BTS\n" "Cell Identity\n") -{ - struct gsm_bts *bts = vty->index; - int ci = atoi(argv[0]); - - if (ci < 0 || ci > 0xffff) { - vty_out(vty, "%% CI %d is not in the valid range (0-65535)%s", - ci, VTY_NEWLINE); - return CMD_WARNING; - } - bts->cell_identity = ci; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_lac, - cfg_bts_lac_cmd, - "location_area_code <0-65535>", - "Set the Location Area Code (LAC) of this BTS\n" "LAC\n") -{ - struct gsm_bts *bts = vty->index; - int lac = atoi(argv[0]); - - if (lac < 0 || lac > 0xffff) { - vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { - vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - bts->location_area_code = lac; - - return CMD_SUCCESS; -} - - -DEFUN(cfg_bts_tsc, - cfg_bts_tsc_cmd, - "training_sequence_code <0-7>", - "Set the Training Sequence Code (TSC) of this BTS\n" "TSC\n") -{ - struct gsm_bts *bts = vty->index; - int tsc = atoi(argv[0]); - - bts->tsc = tsc; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_bsic, - cfg_bts_bsic_cmd, - "base_station_id_code <0-63>", - "Set the Base Station Identity Code (BSIC) of this BTS\n" - "BSIC of this BTS\n") -{ - struct gsm_bts *bts = vty->index; - int bsic = atoi(argv[0]); - - if (bsic < 0 || bsic > 0x3f) { - vty_out(vty, "%% BSIC %d is not in the valid range (0-255)%s", - bsic, VTY_NEWLINE); - return CMD_WARNING; - } - bts->bsic = bsic; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_timezone, - cfg_bts_timezone_cmd, - "timezone <-19-19> (0|15|30|45)", - "Set the Timezone Offset of this BTS\n" - "Timezone offset (hours)\n" - "Timezone offset (00 minutes)\n" - "Timezone offset (15 minutes)\n" - "Timezone offset (30 minutes)\n" - "Timezone offset (45 minutes)\n" - ) -{ - struct gsm_bts *bts = vty->index; - int tzhr = atoi(argv[0]); - int tzmn = atoi(argv[1]); - - bts->tz.hr = tzhr; - bts->tz.mn = tzmn; - bts->tz.override = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_timezone, - cfg_bts_no_timezone_cmd, - "no timezone", - NO_STR - "Disable BTS specific timezone\n") -{ - struct gsm_bts *bts = vty->index; - - bts->tz.override = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_unit_id, - cfg_bts_unit_id_cmd, - "ip.access unit_id <0-65534> <0-255>", - "Abis/IP specific options\n" - "Set the IPA BTS Unit ID\n" - "Unit ID (Site)\n" - "Unit ID (BTS)\n") -{ - struct gsm_bts *bts = vty->index; - int site_id = atoi(argv[0]); - int bts_id = atoi(argv[1]); - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->ip_access.site_id = site_id; - bts->ip_access.bts_id = bts_id; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rsl_ip, - cfg_bts_rsl_ip_cmd, - "ip.access rsl-ip A.B.C.D", - "Abis/IP specific options\n" - "Set the IPA RSL IP Address of the BSC\n" - "Destination IP address for RSL connection\n") -{ - struct gsm_bts *bts = vty->index; - struct in_addr ia; - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - inet_aton(argv[0], &ia); - bts->ip_access.rsl_ip = ntohl(ia.s_addr); - - return CMD_SUCCESS; -} - - -DEFUN(cfg_bts_nokia_site_skip_reset, - cfg_bts_nokia_site_skip_reset_cmd, - "nokia_site skip-reset (0|1)", - "Nokia *Site related commands\n" - "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; -} - -#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_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; -} - -#define GPRS_TEXT "GPRS Packet Network\n" - -DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd, - "gprs cell bvci <2-65535>", - GPRS_TEXT - "GPRS Cell Settings\n" - "GPRS BSSGP VC Identifier\n" - "GPRS BSSGP VC Identifier") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.cell.bvci = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd, - "gprs nsei <0-65535>", - GPRS_TEXT - "GPRS NS Entity Identifier\n" - "GPRS NS Entity Identifier") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.nse.nsei = atoi(argv[0]); - - return CMD_SUCCESS; -} - -#define NSVC_TEXT "Network Service Virtual Connection (NS-VC)\n" \ - "NSVC Logical Number\n" - -DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd, - "gprs nsvc <0-1> nsvci <0-65535>", - GPRS_TEXT NSVC_TEXT - "NS Virtual Connection Identifier\n" - "GPRS NS VC Identifier") -{ - struct gsm_bts *bts = vty->index; - int idx = atoi(argv[0]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.nsvc[idx].nsvci = atoi(argv[1]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd, - "gprs nsvc <0-1> local udp port <0-65535>", - GPRS_TEXT NSVC_TEXT - "GPRS NS Local UDP Port\n" - "GPRS NS Local UDP Port\n" - "GPRS NS Local UDP Port\n" - "GPRS NS Local UDP Port Number\n") -{ - struct gsm_bts *bts = vty->index; - int idx = atoi(argv[0]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.nsvc[idx].local_port = atoi(argv[1]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd, - "gprs nsvc <0-1> remote udp port <0-65535>", - GPRS_TEXT NSVC_TEXT - "GPRS NS Remote UDP Port\n" - "GPRS NS Remote UDP Port\n" - "GPRS NS Remote UDP Port\n" - "GPRS NS Remote UDP Port Number\n") -{ - struct gsm_bts *bts = vty->index; - int idx = atoi(argv[0]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.nsvc[idx].remote_port = atoi(argv[1]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd, - "gprs nsvc <0-1> remote ip A.B.C.D", - GPRS_TEXT NSVC_TEXT - "GPRS NS Remote IP Address\n" - "GPRS NS Remote IP Address\n" - "GPRS NS Remote IP Address\n") -{ - struct gsm_bts *bts = vty->index; - int idx = atoi(argv[0]); - struct in_addr ia; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - inet_aton(argv[1], &ia); - bts->gprs.nsvc[idx].remote_ip = ntohl(ia.s_addr); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_pag_free, cfg_bts_pag_free_cmd, - "paging free <-1-1024>", - "Paging options\n" - "Only page when having a certain amount of free slots\n" - "amount of required free paging slots. -1 to disable\n") -{ - struct gsm_bts *bts = vty->index; - - bts->paging.free_chans_need = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_ns_timer, cfg_bts_gprs_ns_timer_cmd, - "gprs ns timer " NS_TIMERS " <0-255>", - GPRS_TEXT "Network Service\n" - "Network Service Timer\n" - NS_TIMERS_HELP "Timer Value\n") -{ - struct gsm_bts *bts = vty->index; - int idx = get_string_value(gprs_ns_timer_strs, argv[0]); - int val = atoi(argv[1]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.nse.timer)) - return CMD_WARNING; - - bts->gprs.nse.timer[idx] = val; - - return CMD_SUCCESS; -} - -#define BSSGP_TIMERS "(blocking-timer|blocking-retries|unblocking-retries|reset-timer|reset-retries|suspend-timer|suspend-retries|resume-timer|resume-retries|capability-update-timer|capability-update-retries)" -#define BSSGP_TIMERS_HELP \ - "Tbvc-block timeout\n" \ - "Tbvc-block retries\n" \ - "Tbvc-unblock retries\n" \ - "Tbvcc-reset timeout\n" \ - "Tbvc-reset retries\n" \ - "Tbvc-suspend timeout\n" \ - "Tbvc-suspend retries\n" \ - "Tbvc-resume timeout\n" \ - "Tbvc-resume retries\n" \ - "Tbvc-capa-update timeout\n" \ - "Tbvc-capa-update retries\n" - -DEFUN(cfg_bts_gprs_cell_timer, cfg_bts_gprs_cell_timer_cmd, - "gprs cell timer " BSSGP_TIMERS " <0-255>", - GPRS_TEXT "Cell / BSSGP\n" - "Cell/BSSGP Timer\n" - BSSGP_TIMERS_HELP "Timer Value\n") -{ - struct gsm_bts *bts = vty->index; - int idx = get_string_value(gprs_bssgp_cfg_strs, argv[0]); - int val = atoi(argv[1]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.cell.timer)) - return CMD_WARNING; - - bts->gprs.cell.timer[idx] = val; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd, - "gprs routing area <0-255>", - GPRS_TEXT - "GPRS Routing Area Code\n" - "GPRS Routing Area Code\n" - "GPRS Routing Area Code\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.rac = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_net_ctrl_ord, cfg_bts_gprs_net_ctrl_ord_cmd, - "gprs network-control-order (nc0|nc1|nc2)", - GPRS_TEXT - "GPRS Network Control Order\n" - "MS controlled cell re-selection, no measurement reporting\n" - "MS controlled cell re-selection, MS sends measurement reports\n" - "Network controlled cell re-selection, MS sends measurement reports\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.net_ctrl_ord = atoi(argv[0] + 2); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_mode, cfg_bts_gprs_mode_cmd, - "gprs mode (none|gprs|egprs)", - GPRS_TEXT - "GPRS Mode for this BTS\n" - "GPRS Disabled on this BTS\n" - "GPRS Enabled on this BTS\n" - "EGPRS (EDGE) Enabled on this BTS\n") -{ - struct gsm_bts *bts = vty->index; - enum bts_gprs_mode mode = bts_gprs_mode_parse(argv[0]); - - if (mode != BTS_GPRS_NONE && - !gsm_bts_has_feature(bts, BTS_FEAT_GPRS)) { - vty_out(vty, "This BTS type does not support %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - if (mode == BTS_GPRS_EGPRS && - !gsm_bts_has_feature(bts, BTS_FEAT_EGPRS)) { - vty_out(vty, "This BTS type does not support %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.mode = mode; - - return CMD_SUCCESS; -} - -#define SI_TEXT "System Information Messages\n" -#define SI_TYPE_TEXT "(1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter)" -#define SI_TYPE_HELP "System Information Type 1\n" \ - "System Information Type 2\n" \ - "System Information Type 3\n" \ - "System Information Type 4\n" \ - "System Information Type 5\n" \ - "System Information Type 6\n" \ - "System Information Type 7\n" \ - "System Information Type 8\n" \ - "System Information Type 9\n" \ - "System Information Type 10\n" \ - "System Information Type 13\n" \ - "System Information Type 16\n" \ - "System Information Type 17\n" \ - "System Information Type 18\n" \ - "System Information Type 19\n" \ - "System Information Type 20\n" \ - "System Information Type 2bis\n" \ - "System Information Type 2ter\n" \ - "System Information Type 2quater\n" \ - "System Information Type 5bis\n" \ - "System Information Type 5ter\n" - -DEFUN(cfg_bts_si_mode, cfg_bts_si_mode_cmd, - "system-information " SI_TYPE_TEXT " mode (static|computed)", - SI_TEXT SI_TYPE_HELP - "System Information Mode\n" - "Static user-specified\n" - "Dynamic, BSC-computed\n") -{ - struct gsm_bts *bts = vty->index; - int type; - - type = get_string_value(osmo_sitype_strs, argv[0]); - if (type < 0) { - vty_out(vty, "Error SI Type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[1], "static")) - bts->si_mode_static |= (1 << type); - else - bts->si_mode_static &= ~(1 << type); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_si_static, cfg_bts_si_static_cmd, - "system-information " SI_TYPE_TEXT " static HEXSTRING", - SI_TEXT SI_TYPE_HELP - "Static System Information filling\n" - "Static user-specified SI content in HEX notation\n") -{ - struct gsm_bts *bts = vty->index; - int rc, type; - - type = get_string_value(osmo_sitype_strs, argv[0]); - if (type < 0) { - vty_out(vty, "Error SI Type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!(bts->si_mode_static & (1 << type))) { - vty_out(vty, "SI Type %s is not configured in static mode%s", - get_value_string(osmo_sitype_strs, type), VTY_NEWLINE); - return CMD_WARNING; - } - - /* Fill buffer with padding pattern */ - memset(bts->si_buf[type], 0x2b, sizeof(bts->si_buf[type])); - - /* Parse the user-specified SI in hex format, [partially] overwriting padding */ - rc = osmo_hexparse(argv[1], bts->si_buf[type], sizeof(bts->si_buf[0])); - if (rc < 0 || rc > sizeof(bts->si_buf[0])) { - vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE); - return CMD_WARNING; - } - - /* Mark this SI as present */ - bts->si_valid |= (1 << type); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_neigh_mode, cfg_bts_neigh_mode_cmd, - "neighbor-list mode (automatic|manual|manual-si5)", - "Neighbor List\n" "Mode of Neighbor List generation\n" - "Automatically from all BTS in this OpenBSC\n" "Manual\n" - "Manual with different lists for SI2 and SI5\n") -{ - struct gsm_bts *bts = vty->index; - int mode = get_string_value(bts_neigh_mode_strs, argv[0]); - - switch (mode) { - case NL_MODE_MANUAL_SI5SEP: - case NL_MODE_MANUAL: - /* make sure we clear the current list when switching to - * manual mode */ - if (bts->neigh_list_manual_mode == 0) - memset(&bts->si_common.data.neigh_list, 0, - sizeof(bts->si_common.data.neigh_list)); - break; - default: - break; - } - - bts->neigh_list_manual_mode = mode; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_neigh, cfg_bts_neigh_cmd, - "neighbor-list (add|del) arfcn <0-1023>", - "Neighbor List\n" "Add to manual neighbor list\n" - "Delete from manual neighbor list\n" "ARFCN of neighbor\n" - "ARFCN of neighbor\n") -{ - struct gsm_bts *bts = vty->index; - struct bitvec *bv = &bts->si_common.neigh_list; - uint16_t arfcn = atoi(argv[1]); - - if (!bts->neigh_list_manual_mode) { - vty_out(vty, "%% Cannot configure neighbor list in " - "automatic mode%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "add")) - bitvec_set_bit_pos(bv, arfcn, 1); - else - bitvec_set_bit_pos(bv, arfcn, 0); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_si5_neigh, cfg_bts_si5_neigh_cmd, - "si5 neighbor-list (add|del) arfcn <0-1023>", - "SI5 Neighbor List\n" - "SI5 Neighbor List\n" "Add to manual SI5 neighbor list\n" - "Delete from SI5 manual neighbor list\n" "ARFCN of neighbor\n" - "ARFCN of neighbor\n") -{ - struct gsm_bts *bts = vty->index; - struct bitvec *bv = &bts->si_common.si5_neigh_list; - uint16_t arfcn = atoi(argv[1]); - - if (!bts->neigh_list_manual_mode) { - vty_out(vty, "%% Cannot configure neighbor list in " - "automatic mode%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "add")) - bitvec_set_bit_pos(bv, arfcn, 1); - else - bitvec_set_bit_pos(bv, arfcn, 0); - - return CMD_SUCCESS; -} - -#define EXCL_RFLOCK_STR "Exclude this BTS from the global RF Lock\n" - -DEFUN(cfg_bts_excl_rf_lock, - cfg_bts_excl_rf_lock_cmd, - "rf-lock-exclude", - EXCL_RFLOCK_STR) -{ - struct gsm_bts *bts = vty->index; - bts->excl_from_rf_lock = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_excl_rf_lock, - cfg_bts_no_excl_rf_lock_cmd, - "no rf-lock-exclude", - NO_STR EXCL_RFLOCK_STR) -{ - struct gsm_bts *bts = vty->index; - bts->excl_from_rf_lock = 0; - return CMD_SUCCESS; -} - -#define 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; - - ts->tsc = atoi(argv[0]); - - return CMD_SUCCESS; -} - -#define HOPPING_STR "Configure frequency hopping\n" - -DEFUN(cfg_ts_hopping, - cfg_ts_hopping_cmd, - "hopping enabled (0|1)", - HOPPING_STR "Enable or disable frequency hopping\n" - "Disable frequency hopping\n" "Enable frequency hopping\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - int enabled = atoi(argv[0]); - - if (enabled && !gsm_bts_has_feature(ts->trx->bts, BTS_FEAT_HOPPING)) { - vty_out(vty, "BTS model does not support hopping%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - ts->hopping.enabled = enabled; - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_hsn, - cfg_ts_hsn_cmd, - "hopping sequence-number <0-63>", - HOPPING_STR - "Which hopping sequence to use for this channel\n" - "Hopping Sequence Number (HSN)\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - - ts->hopping.hsn = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_maio, - cfg_ts_maio_cmd, - "hopping maio <0-63>", - HOPPING_STR - "Which hopping MAIO to use for this channel\n" - "Mobile Allocation Index Offset (MAIO)\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - - ts->hopping.maio = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_arfcn_add, - cfg_ts_arfcn_add_cmd, - "hopping arfcn add <0-1023>", - HOPPING_STR "Configure hopping ARFCN list\n" - "Add an entry to the hopping ARFCN list\n" "ARFCN\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - int arfcn = atoi(argv[0]); - - bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 1); - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_arfcn_del, - cfg_ts_arfcn_del_cmd, - "hopping arfcn del <0-1023>", - HOPPING_STR "Configure hopping ARFCN list\n" - "Delete an entry to the hopping ARFCN list\n" "ARFCN\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - int arfcn = atoi(argv[0]); - - bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 0); - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_e1_subslot, - cfg_ts_e1_subslot_cmd, - "e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", - "E1/T1 channel connected to this on-air timeslot\n" - "E1/T1 channel connected to this on-air timeslot\n" - "E1/T1 line connected to this on-air timeslot\n" - "E1/T1 timeslot connected to this on-air timeslot\n" - "E1/T1 timeslot connected to this on-air timeslot\n" - "E1/T1 sub-slot connected to this on-air timeslot\n" - "E1/T1 sub-slot 0 connected to this on-air timeslot\n" - "E1/T1 sub-slot 1 connected to this on-air timeslot\n" - "E1/T1 sub-slot 2 connected to this on-air timeslot\n" - "E1/T1 sub-slot 3 connected to this on-air timeslot\n" - "Full E1/T1 timeslot connected to this on-air timeslot\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - - parse_e1_link(&ts->e1_link, argv[0], argv[1], argv[2]); - - return CMD_SUCCESS; -} - -void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net) -{ - vty_out(vty, "Channel Requests : %lu total, %lu no channel%s", - osmo_counter_get(net->stats.chreq.total), - osmo_counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); - vty_out(vty, "Channel Failures : %lu rf_failures, %lu rll failures%s", - osmo_counter_get(net->stats.chan.rf_fail), - osmo_counter_get(net->stats.chan.rll_err), VTY_NEWLINE); - vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s", - osmo_counter_get(net->stats.paging.attempted), - osmo_counter_get(net->stats.paging.completed), - osmo_counter_get(net->stats.paging.expired), VTY_NEWLINE); - vty_out(vty, "BTS failures : %lu OML, %lu RSL%s", - osmo_counter_get(net->stats.bts.oml_fail), - osmo_counter_get(net->stats.bts.rsl_fail), VTY_NEWLINE); -} - -DEFUN(logging_fltr_imsi, - logging_fltr_imsi_cmd, - "logging filter imsi IMSI", - LOGGING_STR FILTER_STR - "Filter log messages by IMSI\n" "IMSI to be used as filter\n") -{ - struct log_target *tgt = osmo_log_vty2tgt(vty); - - if (!tgt) - return CMD_WARNING; - - log_set_imsi_filter(tgt, argv[0]); - return CMD_SUCCESS; -} - - -DEFUN(drop_bts, - drop_bts_cmd, - "drop bts connection <0-65535> (oml|rsl)", - "Debug/Simulation command to drop 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(pdch_act, pdch_act_cmd, - "bts <0-255> trx <0-255> timeslot <0-7> pdch (activate|deactivate)", - "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n" - "TRX Timeslot\n" "Timeslot Number\n" "Packet Data Channel\n" - "Activate Dynamic PDCH/TCH (-> PDCH mode)\n" - "Deactivate Dynamic PDCH/TCH (-> TCH mode)\n") -{ - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - int bts_nr = atoi(argv[0]); - int trx_nr = atoi(argv[1]); - int ts_nr = atoi(argv[2]); - int activate; - - bts = gsm_bts_num(bsc_gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "%% This command only works for ipaccess BTS%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - trx = gsm_bts_trx_num(bts, trx_nr); - if (!trx) { - vty_out(vty, "%% No such TRX (%d)%s", trx_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - ts = &trx->ts[ts_nr]; - if (ts->pchan != GSM_PCHAN_TCH_F_PDCH) { - vty_out(vty, "%% Timeslot %u is not in dynamic TCH_F/PDCH " - "mode%s", ts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[3], "activate")) - activate = 1; - else - activate = 0; - - rsl_ipacc_pdch_activate(ts, activate); - - return CMD_SUCCESS; - -} - -extern int bsc_vty_init_extra(void); -extern const char *openbsc_copyright; - -int bsc_vty_init(const struct log_info *cat) -{ - cfg_ts_pchan_cmd.string = - vty_cmd_string_from_valstr(tall_bsc_ctx, - gsm_pchant_names, - "phys_chan_config (", "|", ")", - VTY_DO_LOWER); - cfg_ts_pchan_cmd.doc = - vty_cmd_string_from_valstr(tall_bsc_ctx, - gsm_pchant_descs, - "Physical Channel Combination\n", - "\n", "", 0); - - cfg_bts_type_cmd.string = - vty_cmd_string_from_valstr(tall_bsc_ctx, - bts_type_names, - "type (", "|", ")", - VTY_DO_LOWER); - cfg_bts_type_cmd.doc = - vty_cmd_string_from_valstr(tall_bsc_ctx, - bts_type_descs, - "BTS Vendor/Type\n", - "\n", "", 0); - - - install_element_ve(&show_net_cmd); - install_element_ve(&show_bts_cmd); - install_element_ve(&show_trx_cmd); - install_element_ve(&show_ts_cmd); - install_element_ve(&show_lchan_cmd); - install_element_ve(&show_lchan_summary_cmd); - install_element_ve(&logging_fltr_imsi_cmd); - - install_element_ve(&show_paging_cmd); - - logging_vty_add_cmds(cat); - install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd); - - install_element(CONFIG_NODE, &cfg_net_cmd); - install_node(&net_node, config_write_net); - install_default(GSMNET_NODE); - install_element(GSMNET_NODE, &ournode_exit_cmd); - install_element(GSMNET_NODE, &ournode_end_cmd); - install_element(GSMNET_NODE, &cfg_net_ncc_cmd); - install_element(GSMNET_NODE, &cfg_net_mnc_cmd); - install_element(GSMNET_NODE, &cfg_net_name_short_cmd); - install_element(GSMNET_NODE, &cfg_net_name_long_cmd); - install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd); - install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd); - install_element(GSMNET_NODE, &cfg_net_encryption_cmd); - install_element(GSMNET_NODE, &cfg_net_neci_cmd); - install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd); - install_element(GSMNET_NODE, &cfg_net_mm_info_cmd); - install_element(GSMNET_NODE, &cfg_net_handover_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd); - install_element(GSMNET_NODE, &cfg_net_T3101_cmd); - install_element(GSMNET_NODE, &cfg_net_T3103_cmd); - install_element(GSMNET_NODE, &cfg_net_T3105_cmd); - install_element(GSMNET_NODE, &cfg_net_T3107_cmd); - install_element(GSMNET_NODE, &cfg_net_T3109_cmd); - install_element(GSMNET_NODE, &cfg_net_T3111_cmd); - install_element(GSMNET_NODE, &cfg_net_T3113_cmd); - install_element(GSMNET_NODE, &cfg_net_T3115_cmd); - install_element(GSMNET_NODE, &cfg_net_T3117_cmd); - install_element(GSMNET_NODE, &cfg_net_T3119_cmd); - install_element(GSMNET_NODE, &cfg_net_T3122_cmd); - install_element(GSMNET_NODE, &cfg_net_T3141_cmd); - install_element(GSMNET_NODE, &cfg_net_dtx_cmd); - install_element(GSMNET_NODE, &cfg_net_subscr_keep_cmd); - install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd); - - install_element(GSMNET_NODE, &cfg_bts_cmd); - install_node(&bts_node, config_write_bts); - install_default(BTS_NODE); - install_element(BTS_NODE, &ournode_exit_cmd); - install_element(BTS_NODE, &ournode_end_cmd); - install_element(BTS_NODE, &cfg_bts_type_cmd); - install_element(BTS_NODE, &cfg_description_cmd); - install_element(BTS_NODE, &cfg_no_description_cmd); - install_element(BTS_NODE, &cfg_bts_band_cmd); - install_element(BTS_NODE, &cfg_bts_ci_cmd); - install_element(BTS_NODE, &cfg_bts_lac_cmd); - install_element(BTS_NODE, &cfg_bts_tsc_cmd); - install_element(BTS_NODE, &cfg_bts_bsic_cmd); - install_element(BTS_NODE, &cfg_bts_unit_id_cmd); - install_element(BTS_NODE, &cfg_bts_rsl_ip_cmd); - install_element(BTS_NODE, &cfg_bts_timezone_cmd); - install_element(BTS_NODE, &cfg_bts_no_timezone_cmd); - install_element(BTS_NODE, &cfg_bts_nokia_site_skip_reset_cmd); - install_element(BTS_NODE, &cfg_bts_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_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_gprs_mode_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_net_ctrl_ord_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd); - install_element(BTS_NODE, &cfg_bts_pag_free_cmd); - install_element(BTS_NODE, &cfg_bts_si_mode_cmd); - install_element(BTS_NODE, &cfg_bts_si_static_cmd); - install_element(BTS_NODE, &cfg_bts_neigh_mode_cmd); - install_element(BTS_NODE, &cfg_bts_neigh_cmd); - install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd); - install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd); - install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd); - - install_element(BTS_NODE, &cfg_trx_cmd); - install_node(&trx_node, dummy_config_write); - install_default(TRX_NODE); - install_element(TRX_NODE, &ournode_exit_cmd); - install_element(TRX_NODE, &ournode_end_cmd); - install_element(TRX_NODE, &cfg_trx_arfcn_cmd); - install_element(TRX_NODE, &cfg_description_cmd); - install_element(TRX_NODE, &cfg_no_description_cmd); - install_element(TRX_NODE, &cfg_trx_nominal_power_cmd); - install_element(TRX_NODE, &cfg_trx_max_power_red_cmd); - install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd); - install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd); - install_element(TRX_NODE, &cfg_trx_rf_locked_cmd); - - install_element(TRX_NODE, &cfg_ts_cmd); - install_node(&ts_node, dummy_config_write); - install_default(TS_NODE); - install_element(TS_NODE, &ournode_exit_cmd); - install_element(TS_NODE, &ournode_end_cmd); - install_element(TS_NODE, &cfg_ts_pchan_cmd); - install_element(TS_NODE, &cfg_ts_pchan_compat_cmd); - install_element(TS_NODE, &cfg_ts_tsc_cmd); - install_element(TS_NODE, &cfg_ts_hopping_cmd); - install_element(TS_NODE, &cfg_ts_hsn_cmd); - install_element(TS_NODE, &cfg_ts_maio_cmd); - install_element(TS_NODE, &cfg_ts_arfcn_add_cmd); - install_element(TS_NODE, &cfg_ts_arfcn_del_cmd); - install_element(TS_NODE, &cfg_ts_e1_subslot_cmd); - - install_element(ENABLE_NODE, &drop_bts_cmd); - install_element(ENABLE_NODE, &pdch_act_cmd); - - abis_nm_vty_init(); - abis_om2k_vty_init(); - e1inp_vty_init(); - - bsc_vty_init_extra(); - - return 0; -} diff --git a/openbsc/src/libbsc/bts_ericsson_rbs2000.c b/openbsc/src/libbsc/bts_ericsson_rbs2000.c deleted file mode 100644 index ba5ebe576..000000000 --- a/openbsc/src/libbsc/bts_ericsson_rbs2000.c +++ /dev/null @@ -1,276 +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); - abis_om2k_tx_start_req(bts, &om2k_mo_cf); - /* FIXME */ -} - -static void bootstrap_om_trx(struct gsm_bts_trx *trx) -{ - struct abis_om2k_mo trx_mo = { OM2K_MO_CLS_TRXC, 0, 255, trx->nr }; - - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", - trx->bts->nr, trx->nr); - - abis_om2k_tx_reset_cmd(trx->bts, &trx_mo); -} - -static int shutdown_om(struct gsm_bts *bts) -{ - /* FIXME */ - return 0; -} - - -/* Tell LAPD to start start the SAP (send SABM requests) for all signalling - * timeslots in this line */ -static void start_sabm_in_line(struct e1inp_line *line, int start) -{ - struct e1inp_sign_link *link; - int i; - - for (i = 0; i < ARRAY_SIZE(line->ts); i++) { - struct e1inp_ts *ts = &line->ts[i]; - - if (ts->type != E1INP_TS_TYPE_SIGN) - continue; - - llist_for_each_entry(link, &ts->sign.sign_links, list) { - if (start) - lapd_sap_start(ts->lapd, link->tei, link->sapi); - else - lapd_sap_stop(ts->lapd, link->tei, link->sapi); - } - } -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int gbl_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_bts *bts; - - if (subsys != SS_L_GLOBAL) - return 0; - - switch (signal) { - case S_GLOBAL_BTS_CLOSE_OM: - bts = signal_data; - if (bts->type == GSM_BTS_TYPE_RBS2000) - shutdown_om(signal_data); - break; - } - - return 0; -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - - if (subsys != SS_L_INPUT) - return 0; - - switch (signal) { - case S_L_INP_TEI_UP: - switch (isd->link_type) { - case E1INP_SIGN_OML: - if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000) - break; - if (isd->tei == isd->trx->bts->oml_tei) - bootstrap_om_bts(isd->trx->bts); - else - bootstrap_om_trx(isd->trx); - break; - } - break; - case S_L_INP_LINE_INIT: - case S_L_INP_LINE_NOALARM: - if (strcasecmp(isd->line->driver->name, "DAHDI") - && strcasecmp(isd->line->driver->name, "MISDN_LAPD")) - break; - start_sabm_in_line(isd->line, 1); - break; - case S_L_INP_LINE_ALARM: - if (strcasecmp(isd->line->driver->name, "DAHDI") - && strcasecmp(isd->line->driver->name, "MISDN_LAPD")) - break; - start_sabm_in_line(isd->line, 0); - break; - } - - return 0; -} - -static void nm_statechg_evt(unsigned int signal, - struct nm_statechg_signal_data *nsd) -{ - struct abis_om2k_mo mo; - - if (nsd->bts->type != GSM_BTS_TYPE_RBS2000) - return; - - switch (nsd->om2k_mo->class) { - case OM2K_MO_CLS_CF: - if (nsd->new_state->operational != NM_OPSTATE_ENABLED || - nsd->new_state->availability != OM2K_MO_S_STARTED) - break; - /* CF has started, we can trigger IS and TF start */ - abis_om2k_tx_connect_cmd(nsd->bts, &om2k_mo_is); - abis_om2k_tx_connect_cmd(nsd->bts, &om2k_mo_tf); - break; - case OM2K_MO_CLS_IS: - if (nsd->new_state->availability == OM2K_MO_S_ENABLED) { - /* IS is enabled, we can proceed with TRXC/RX/TX/TS */ - break; - } - if (nsd->new_state->operational != NM_OPSTATE_ENABLED) - break; - /* IS has started, we can configure + enable it */ - abis_om2k_tx_is_conf_req(nsd->bts); - break; - case OM2K_MO_CLS_TF: - if (nsd->new_state->operational != NM_OPSTATE_ENABLED || - nsd->new_state->availability == OM2K_MO_S_DISABLED) - break; - if (nsd->new_state->availability == OM2K_MO_S_STARTED) { - /* TF has started, configure + enable it */ - abis_om2k_tx_tf_conf_req(nsd->bts); - } - break; - case OM2K_MO_CLS_TRXC: - if (nsd->new_state->availability != OM2K_MO_S_STARTED) - break; - /* TRXC is started, connect the TX and RX objects */ - memcpy(&mo, nsd->om2k_mo, sizeof(mo)); - mo.class = OM2K_MO_CLS_TX; - abis_om2k_tx_connect_cmd(nsd->bts, &mo); - mo.class = OM2K_MO_CLS_RX; - abis_om2k_tx_connect_cmd(nsd->bts, &mo); - break; - case OM2K_MO_CLS_RX: - if (nsd->new_state->operational != NM_OPSTATE_ENABLED || - nsd->new_state->availability != OM2K_MO_S_STARTED) - break; - /* RX is started, configure + enable it */ - abis_om2k_tx_rx_conf_req(nsd->obj); - break; - case OM2K_MO_CLS_TX: - if (nsd->new_state->operational != NM_OPSTATE_ENABLED || - nsd->new_state->availability != OM2K_MO_S_STARTED) - break; - /* RX is started, configure + enable it */ - abis_om2k_tx_tx_conf_req(nsd->obj); - break; - } -} - -static void nm_conf_res(struct nm_om2k_signal_data *nsd) -{ - switch (nsd->om2k_mo->class) { - case OM2K_MO_CLS_IS: - case OM2K_MO_CLS_TF: - case OM2K_MO_CLS_RX: - case OM2K_MO_CLS_TX: - /* If configuration was a success, enable it */ - abis_om2k_tx_enable_req(nsd->bts, nsd->om2k_mo); - break; - } -} - -static int nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - if (subsys != SS_NM) - return 0; - - switch (signal) { - case S_NM_STATECHG_OPER: - case S_NM_STATECHG_ADM: - nm_statechg_evt(signal, signal_data); - break; - case S_NM_OM2K_CONF_RES: - nm_conf_res(signal_data); - break; - default: - break; - } - - return 0; -} - -static void config_write_bts(struct vty *vty, struct gsm_bts *bts) -{ - abis_om2k_config_write_bts(vty, bts); -} - -static int bts_model_rbs2k_start(struct gsm_network *net); - -static void bts_model_rbs2k_e1line_bind_ops(struct e1inp_line *line) -{ - e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); -} - -static struct gsm_bts_model model_rbs2k = { - .type = GSM_BTS_TYPE_RBS2000, - .name = "rbs2000", - .start = bts_model_rbs2k_start, - .oml_rcvmsg = &abis_om2k_rcvmsg, - .config_write_bts = &config_write_bts, - .e1line_bind_ops = &bts_model_rbs2k_e1line_bind_ops, -}; - -static int bts_model_rbs2k_start(struct gsm_network *net) -{ - model_rbs2k.features.data = &model_rbs2k._features_data[0]; - model_rbs2k.features.data_len = sizeof(model_rbs2k._features_data); - - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HOPPING); - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HSCSD); - - osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); - osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); - osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); - - return 0; -} - -int bts_model_rbs2k_init(void) -{ - return gsm_bts_model_register(&model_rbs2k); -} diff --git a/openbsc/src/libbsc/bts_init.c b/openbsc/src/libbsc/bts_init.c deleted file mode 100644 index d6b152a79..000000000 --- a/openbsc/src/libbsc/bts_init.c +++ /dev/null @@ -1,30 +0,0 @@ -/* (C) 2011 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License 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 c8b235d56..000000000 --- a/openbsc/src/libbsc/bts_ipaccess_nanobts.c +++ /dev/null @@ -1,650 +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 - -extern struct gsm_network *bsc_gsmnet; - -static int bts_model_nanobts_start(struct gsm_network *net); -static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line); - -struct gsm_bts_model bts_model_nanobts = { - .type = GSM_BTS_TYPE_NANOBTS, - .name = "nanobts", - .start = bts_model_nanobts_start, - .oml_rcvmsg = &abis_nm_rcvmsg, - .e1line_bind_ops = bts_model_nanobts_e1line_bind_ops, - .nm_att_tlvdef = { - .def = { - /* ip.access specifics */ - [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, - [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, }, - [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, - [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, - [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, - [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 }, - [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V }, - }, - }, -}; - -static unsigned char nanobts_attr_bts[] = { - NM_ATT_INTERF_BOUND, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73, - /* interference avg. period in numbers of SACCH multifr */ - NM_ATT_INTAVE_PARAM, 0x06, - /* conn fail based on SACCH error rate */ - NM_ATT_CONN_FAIL_CRIT, 0x00, 0x02, 0x01, 0x10, - NM_ATT_T200, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, 0xa8, - NM_ATT_MAX_TA, 0x3f, - NM_ATT_OVERL_PERIOD, 0x00, 0x01, 10, /* seconds */ - NM_ATT_CCCH_L_T, 10, /* percent */ - NM_ATT_CCCH_L_I_P, 1, /* seconds */ - NM_ATT_RACH_B_THRESH, 10, /* busy threshold in - dBm */ - NM_ATT_LDAVG_SLOTS, 0x03, 0xe8, /* rach load averaging 1000 slots */ - NM_ATT_BTS_AIR_TIMER, 128, /* miliseconds */ - NM_ATT_NY1, 10, /* 10 retransmissions of physical config */ - NM_ATT_BCCH_ARFCN, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, - NM_ATT_BSIC, HARDCODED_BSIC, - NM_ATT_IPACC_CGI, 0, 7, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x00, -}; - -static unsigned char nanobts_attr_radio[] = { - NM_ATT_RF_MAXPOWR_R, 0x0c, /* number of -2dB reduction steps / Pn */ - NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, -}; - -static unsigned char nanobts_attr_nse[] = { - NM_ATT_IPACC_NSEI, 0, 2, 0x03, 0x9d, /* NSEI 925 */ - /* all timers in seconds */ - NM_ATT_IPACC_NS_CFG, 0, 7, 3, /* (un)blocking timer (Tns-block) */ - 3, /* (un)blocking retries */ - 3, /* reset timer (Tns-reset) */ - 3, /* reset retries */ - 30, /* test timer (Tns-test) */ - 3, /* alive timer (Tns-alive) */ - 10, /* alive retrires */ - /* all timers in seconds, unless otherwise stated */ - NM_ATT_IPACC_BSSGP_CFG, 0, 11, - 3, /* blockimg timer (T1) */ - 3, /* blocking retries */ - 3, /* unblocking retries */ - 3, /* reset timer (T2) */ - 3, /* reset retries */ - 10, /* suspend timer (T3) in 100ms */ - 3, /* suspend retries */ - 10, /* resume timer (T4) in 100ms */ - 3, /* resume retries */ - 10, /* capability update timer (T5) */ - 3, /* capability update retries */ -}; - -static unsigned char nanobts_attr_cell[] = { - NM_ATT_IPACC_RAC, 0, 1, 1, /* routing area code */ - NM_ATT_IPACC_GPRS_PAGING_CFG, 0, 2, - 5, /* repeat time (50ms) */ - 3, /* repeat count */ - NM_ATT_IPACC_BVCI, 0, 2, 0x03, 0x9d, /* BVCI 925 */ - /* all timers in seconds, unless otherwise stated */ - NM_ATT_IPACC_RLC_CFG, 0, 9, - 20, /* T3142 */ - 5, /* T3169 */ - 5, /* T3191 */ - 160, /* T3193 (units of 10ms) */ - 5, /* T3195 */ - 10, /* N3101 */ - 4, /* N3103 */ - 8, /* N3105 */ - 15, /* RLC CV countdown */ - NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00, /* CS1..CS4 */ - NM_ATT_IPACC_RLC_CFG_2, 0, 5, - 0x00, 250, /* T downlink TBF extension (0..500) */ - 0x00, 250, /* T uplink TBF extension (0..500) */ - 2, /* CS2 */ -#if 0 - /* EDGE model only, breaks older models. - * Should inquire the BTS capabilities */ - NM_ATT_IPACC_RLC_CFG_3, 0, 1, - 2, /* MCS2 */ -#endif -}; - -static unsigned char nanobts_attr_nsvc0[] = { - NM_ATT_IPACC_NSVCI, 0, 2, 0x03, 0x9d, /* 925 */ - NM_ATT_IPACC_NS_LINK_CFG, 0, 8, - 0x59, 0xd8, /* remote udp port (23000) */ - 192, 168, 100, 11, /* remote ip address */ - 0x59, 0xd8, /* local udp port (23000) */ -}; - -static void patch_16(uint8_t *data, const uint16_t val) -{ - memcpy(data, &val, sizeof(val)); -} - -static void patch_32(uint8_t *data, const uint32_t val) -{ - memcpy(data, &val, sizeof(val)); -} - -/* - * Patch the various SYSTEM INFORMATION tables to update - * the LAI - */ -static void patch_nm_tables(struct gsm_bts *bts) -{ - uint8_t arfcn_low = bts->c0->arfcn & 0xff; - uint8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; - - /* patch ARFCN into BTS Attributes */ - nanobts_attr_bts[42] &= 0xf0; - nanobts_attr_bts[42] |= arfcn_high; - nanobts_attr_bts[43] = arfcn_low; - - /* patch the RACH attributes */ - if (bts->rach_b_thresh != -1) { - nanobts_attr_bts[33] = bts->rach_b_thresh & 0xff; - } - - if (bts->rach_ldavg_slots != -1) { - uint8_t avg_high = bts->rach_ldavg_slots & 0xff; - uint8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; - - nanobts_attr_bts[35] = avg_high; - nanobts_attr_bts[36] = avg_low; - } - - /* patch BSIC */ - nanobts_attr_bts[sizeof(nanobts_attr_bts)-11] = bts->bsic; - - /* patch CGI */ - abis_nm_ipaccess_cgi(nanobts_attr_bts+sizeof(nanobts_attr_bts)-7, bts); - - /* patch the power reduction */ - nanobts_attr_radio[1] = bts->c0->max_power_red / 2; - - /* patch NSEI */ - nanobts_attr_nse[3] = bts->gprs.nse.nsei >> 8; - nanobts_attr_nse[4] = bts->gprs.nse.nsei & 0xff; - memcpy(nanobts_attr_nse+8, bts->gprs.nse.timer, - ARRAY_SIZE(bts->gprs.nse.timer)); - memcpy(nanobts_attr_nse+18, bts->gprs.cell.timer, - ARRAY_SIZE(bts->gprs.cell.timer)); - - /* patch NSVCI */ - nanobts_attr_nsvc0[3] = bts->gprs.nsvc[0].nsvci >> 8; - nanobts_attr_nsvc0[4] = bts->gprs.nsvc[0].nsvci & 0xff; - - /* patch IP address as SGSN IP */ - patch_16(nanobts_attr_nsvc0 + 8, - htons(bts->gprs.nsvc[0].remote_port)); - patch_32(nanobts_attr_nsvc0 + 10, - htonl(bts->gprs.nsvc[0].remote_ip)); - patch_16(nanobts_attr_nsvc0 + 14, - htons(bts->gprs.nsvc[0].local_port)); - - /* patch BVCI */ - nanobts_attr_cell[12] = bts->gprs.cell.bvci >> 8; - nanobts_attr_cell[13] = bts->gprs.cell.bvci & 0xff; - /* patch RAC */ - nanobts_attr_cell[3] = bts->gprs.rac; - - if (bts->gprs.mode == BTS_GPRS_EGPRS) { - /* patch EGPRS coding schemes MCS 1..9 */ - nanobts_attr_cell[29] = 0x8f; - nanobts_attr_cell[30] = 0xff; - } -} - - -/* Callback function to be called whenever we get a GSM 12.21 state change event */ -static int nm_statechg_event(int evt, struct nm_statechg_signal_data *nsd) -{ - 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; - - 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) { - patch_nm_tables(bts); - abis_nm_set_bts_attr(bts, nanobts_attr_bts, - sizeof(nanobts_attr_bts)); - abis_nm_chg_adm_state(bts, obj_class, - bts->bts_nr, 0xff, 0xff, - NM_STATE_UNLOCKED); - abis_nm_opstart(bts, obj_class, - bts->bts_nr, 0xff, 0xff); - } - break; - case NM_OC_CHANNEL: - ts = obj; - trx = ts->trx; - if (new_state->operational == NM_OPSTATE_DISABLED && - new_state->availability == NM_AVSTATE_DEPENDENCY) { - patch_nm_tables(trx->bts); - enum abis_nm_chan_comb ccomb = - abis_nm_chcomb4pchan(ts->pchan); - abis_nm_set_channel_attr(ts, ccomb); - abis_nm_chg_adm_state(trx->bts, obj_class, - trx->bts->bts_nr, trx->nr, ts->nr, - NM_STATE_UNLOCKED); - abis_nm_opstart(trx->bts, obj_class, - trx->bts->bts_nr, trx->nr, ts->nr); - } - break; - case NM_OC_RADIO_CARRIER: - trx = obj; - if (new_state->operational == NM_OPSTATE_DISABLED && - new_state->availability == NM_AVSTATE_OK) - abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, - trx->nr, 0xff); - break; - case NM_OC_GPRS_NSE: - bts = container_of(obj, struct gsm_bts, gprs.nse); - if (bts->gprs.mode == BTS_GPRS_NONE) - break; - if (new_state->availability == NM_AVSTATE_DEPENDENCY) { - abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, - 0xff, 0xff, nanobts_attr_nse, - sizeof(nanobts_attr_nse)); - abis_nm_opstart(bts, obj_class, bts->bts_nr, - 0xff, 0xff); - } - break; - case NM_OC_GPRS_CELL: - bts = container_of(obj, struct gsm_bts, gprs.cell); - if (bts->gprs.mode == BTS_GPRS_NONE) - break; - if (new_state->availability == NM_AVSTATE_DEPENDENCY) { - abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, - 0, 0xff, nanobts_attr_cell, - sizeof(nanobts_attr_cell)); - abis_nm_opstart(bts, obj_class, bts->bts_nr, - 0, 0xff); - abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, - 0, 0xff, NM_STATE_UNLOCKED); - abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE, bts->bts_nr, - 0xff, 0xff, NM_STATE_UNLOCKED); - } - break; - case NM_OC_GPRS_NSVC: - nsvc = obj; - bts = nsvc->bts; - if (bts->gprs.mode == BTS_GPRS_NONE) - break; - /* We skip NSVC1 since we only use NSVC0 */ - if (nsvc->id == 1) - break; - if ((new_state->availability == NM_AVSTATE_OFF_LINE) || - (new_state->availability == NM_AVSTATE_DEPENDENCY)) { - abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, - nsvc->id, 0xff, - nanobts_attr_nsvc0, - sizeof(nanobts_attr_nsvc0)); - abis_nm_opstart(bts, obj_class, bts->bts_nr, - nsvc->id, 0xff); - abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, - nsvc->id, 0xff, - NM_STATE_UNLOCKED); - } - default: - break; - } - return 0; -} - -/* Callback function to be called every time we receive a 12.21 SW activated report */ -static int sw_activ_rep(struct msgb *mb) -{ - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct 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 */ - nanobts_attr_radio[5] &= 0xf0; - nanobts_attr_radio[5] |= trx->arfcn >> 8; - nanobts_attr_radio[6] = trx->arfcn & 0xff; - abis_nm_set_radio_attr(trx, nanobts_attr_radio, - sizeof(nanobts_attr_radio)); - abis_nm_chg_adm_state(trx->bts, foh->obj_class, - trx->bts->bts_nr, trx->nr, 0xff, - rc_state); - abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr, - trx->nr, 0xff); - break; - } - } - return 0; -} - -/* Callback function to be called every time we receive a signal from NM */ -static int 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); - - 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_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; -} - -/* 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); - - /* 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_nokia_site.c b/openbsc/src/libbsc/bts_nokia_site.c deleted file mode 100644 index 36e3fac4f..000000000 --- a/openbsc/src/libbsc/bts_nokia_site.c +++ /dev/null @@ -1,1740 +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 ? */ - -#define RESET_INTERVAL 15, 0 /* 15 seconds */ - -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); - - return abis_nm_send(bts, NOKIA_MSG_RESET_REQ, ref, data, len_data); -} - -/* TODO: put in a separate file ? */ - -static int abis_nm_send_multi_segments(struct gsm_bts *bts, uint8_t msg_type, - uint16_t ref, uint8_t * data, int len) -{ - int len_remain, len_to_send, max_send; - int seq = 0; - int ret; - - len_remain = len; - - while (len_remain) { - struct abis_om_hdr *oh; - struct abis_om_nokia_hdr *noh; - struct msgb *msg = nm_msgb_alloc(); - - if (seq == 0) - max_send = 256 - sizeof(struct abis_om_nokia_hdr); - else - max_send = 256; - - if (len_remain > max_send) { - len_to_send = max_send; - - if (seq == 0) { - /* first segment */ - oh = (struct abis_om_hdr *)msgb_put(msg, - ABIS_OM_NOKIA_HDR_SIZE - + - len_to_send); - - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_FIRST; /* first segment of multi-segment message */ - oh->sequence = seq; - oh->length = 0; /* 256 bytes */ - - noh = (struct abis_om_nokia_hdr *)oh->data; - - noh->msg_type = msg_type; - noh->spare = 0; - noh->reference = htons(ref); - memcpy(noh->data, data, len_to_send); - } else { - /* segment in between */ - oh = (struct abis_om_hdr *)msgb_put(msg, - sizeof - (struct - abis_om_hdr) - + - len_to_send); - - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_MIDDLE; /* segment of multi-segment message */ - oh->sequence = seq; - oh->length = 0; /* 256 bytes */ - - memcpy(oh->data, data, len_to_send); - } - } else { - - len_to_send = len_remain; - - /* check if message fits in a single segment */ - - if (seq == 0) - return abis_nm_send(bts, msg_type, ref, data, - len_to_send); - - /* last segment */ - - oh = (struct abis_om_hdr *)msgb_put(msg, - sizeof(struct - abis_om_hdr) - + len_to_send); - - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_LAST; /* last segment of multi-segment message */ - oh->sequence = seq; - oh->length = len_to_send; - - memcpy(oh->data, data, len_to_send); - } - - DEBUGPC(DNM, "Sending multi-segment %d\n", seq); - - ret = abis_nm_sendmsg(bts, msg); - if (ret < 0) - return ret; - - nokia_abis_nm_queue_send_next(bts); - - /* next segment */ - len_remain -= len_to_send; - data += len_to_send; - seq++; - } - return ret; -} - -/* TODO: put in a separate file ? */ - -static int abis_nm_send_config(struct gsm_bts *bts, uint8_t bts_type) -{ - struct gsm_bts_trx *trx; - uint8_t config[2048]; /* TODO: might be too small if lots of TRX are used */ - int len = 0; - int idx = 0; - int ret; - int hopping = 0; - int need_hopping = 0; - - memset(config, 0, sizeof(config)); - - llist_for_each_entry(trx, &bts->trx_list, list) { -#if 0 /* debugging */ - printf("TRX\n"); - printf(" arfcn: %d\n", trx->arfcn); - printf(" bsic: %d\n", trx->bts->bsic); - uint8_t ca[20]; - memset(ca, 0xFF, sizeof(ca)); - ret = generate_cell_chan_list(ca, trx->bts); - printf(" ca (%d): %s\n", ret, osmo_hexdump(ca, sizeof(ca))); - int i; - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - - printf(" pchan %d: %d\n", i, ts->pchan); - } -#endif - ret = make_fu_config(trx, idx + 1, config + len, &hopping); - need_hopping |= hopping; - len += ret; - - idx++; - } - - ret = make_bts_config(bts_type, idx, config + len, need_hopping); - len += ret; - -#if 0 /* debugging */ - dump_elements(config, len); -#endif - - return abis_nm_send_multi_segments(bts, NOKIA_MSG_CONF_DATA, 1, config, - len); -} - -#define GET_NEXT_BYTE if(idx >= len) return 0; \ - ub = data[idx++]; - -static int find_element(uint8_t * data, int len, uint16_t id, uint8_t * value, - int max_value) -{ - uint8_t ub; - int idx = 0; - int found = 0; - int constructed __attribute__((unused)); - uint16_t id_value; - - for (;;) { - - GET_NEXT_BYTE; - - /* encoding bit, construced means that other elements are contained */ - constructed = ((ub & 0x20) ? 1 : 0); - - if ((ub & 0x1F) == 0x1F) { - /* fixed pattern, ID follows */ - GET_NEXT_BYTE; /* ID */ - id_value = ub & 0x7F; - if (ub & 0x80) { - /* extension bit */ - GET_NEXT_BYTE; /* ID low part */ - id_value = (id_value << 7) | (ub & 0x7F); - } - if (id_value == id) - found = 1; - } else { - id_value = (ub & 0x3F); - if (id_value == id) - found = 1; - } - - GET_NEXT_BYTE; /* length */ - - if (found) { - /* get data */ - uint8_t n = ub; - uint8_t i; - for (i = 0; i < n; i++) { - GET_NEXT_BYTE; - if (max_value <= 0) - return -1; /* buffer too small */ - *value = ub; - value++; - max_value--; - } - return n; /* length */ - } else { - /* skip data */ - uint8_t n = ub; - uint8_t i; - for (i = 0; i < n; i++) { - GET_NEXT_BYTE; - } - } - } - return 0; /* not found */ -} - -static int dump_elements(uint8_t * data, int len) -{ - uint8_t ub; - int idx = 0; - int constructed; - uint16_t id_value; - static char indent[100] = ""; /* TODO: move static to BTS context */ - - for (;;) { - - GET_NEXT_BYTE; - - /* encoding bit, construced means that other elements are contained */ - constructed = ((ub & 0x20) ? 1 : 0); - - if ((ub & 0x1F) == 0x1F) { - /* fixed pattern, ID follows */ - GET_NEXT_BYTE; /* ID */ - id_value = ub & 0x7F; - if (ub & 0x80) { - /* extension bit */ - GET_NEXT_BYTE; /* ID low part */ - id_value = (id_value << 7) | (ub & 0x7F); - } - - } else { - id_value = (ub & 0x3F); - } - - GET_NEXT_BYTE; /* length */ - - printf("%s--ID = 0x%02X (%s) %s\n", indent, id_value, - get_element_name_string(id_value), - constructed ? "** constructed **" : ""); - printf("%s length = %d\n", indent, ub); - printf("%s %s\n", indent, osmo_hexdump(data + idx, ub)); - - if (constructed) { - int indent_len = strlen(indent); - strcat(indent, " "); - - dump_elements(data + idx, ub); - - indent[indent_len] = 0; - } - /* skip data */ - uint8_t n = ub; - uint8_t i; - for (i = 0; i < n; i++) { - GET_NEXT_BYTE; - } - } - return 0; -} - -/* TODO: put in a separate file ? */ - -/* taken from abis_nm.c */ - -static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts) -{ - int wait = 0; - struct msgb *msg; - /* the queue is empty */ - while (!llist_empty(&bts->abis_queue)) { - msg = msgb_dequeue(&bts->abis_queue); - wait = OBSC_NM_W_ACK_CB(msg); - abis_sendmsg(msg); - - if (wait) - break; - } - - bts->abis_nm_pend = wait; -} - -/* TODO: put in a separate file ? */ - -/* timer for restarting OML after BTS reset */ - -static void reset_timer_cb(void *_bts) -{ - struct gsm_bts *bts = _bts; - struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; - struct e1inp_line *line; - - bts->nokia.wait_reset = 0; - - /* OML link */ - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " - "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); - return; - } - - start_sabm_in_line(line, 0, -1); /* stop all first */ - start_sabm_in_line(line, 1, SAPI_OML); /* start only OML */ -} - -/* TODO: put in a separate file ? */ - -/* - This is how the configuration is done: - - start OML link - - reset BTS - - receive ACK, wait some time and restart OML link - - receive OMU STARTED message, send START DOWNLOAD REQ - - receive CNF REQ message, send CONF DATA - - receive ACK, start RSL link(s) - ACK some other messages received from the BTS. - - Probably its also possible to configure the BTS without a reset, this - has not been tested yet. -*/ - -static int abis_nm_rcvmsg_fom(struct msgb *mb) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)mb->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_nokia_hdr *noh = msgb_l3(mb); - uint8_t mt = noh->msg_type; - int ret = 0; - uint16_t ref = ntohs(noh->reference); - uint8_t info[256]; - uint8_t ack = 0xFF; - uint8_t severity = 0xFF; - int str_len; - int len_data; - - if (bts->nokia.wait_reset) { - LOGP(DNM, LOGL_INFO, - "Ignore message while waiting for reset\n"); - return ret; - } - - if (oh->length < sizeof(struct abis_om_nokia_hdr)) { - LOGP(DNM, LOGL_ERROR, "Message too short\n"); - return -EINVAL; - } - - len_data = oh->length - sizeof(struct abis_om_nokia_hdr); - LOGP(DNM, LOGL_INFO, "(0x%02X) %s\n", mt, get_msg_type_name_string(mt)); -#if 0 /* debugging */ - dump_elements(noh->data, len_data); -#endif - - switch (mt) { - case NOKIA_MSG_OMU_STARTED: - if (find_element(noh->data, len_data, - NOKIA_EI_BTS_TYPE, &bts->nokia.bts_type, - sizeof(uint8_t)) == sizeof(uint8_t)) - LOGP(DNM, LOGL_INFO, "BTS type = %d (%s)\n", - bts->nokia.bts_type, - get_bts_type_string(bts->nokia.bts_type)); - else - LOGP(DNM, LOGL_ERROR, "BTS type not found\n"); - /* send START_DOWNLOAD_REQ */ - abis_nm_download_req(bts, ref); - break; - case NOKIA_MSG_MF_REQ: - break; - case NOKIA_MSG_CONF_REQ: - /* send ACK */ - abis_nm_ack(bts, ref); - nokia_abis_nm_queue_send_next(bts); - /* send CONF_DATA */ - abis_nm_send_config(bts, bts->nokia.bts_type); - bts->nokia.configured = 1; - break; - case NOKIA_MSG_ACK: - if (find_element - (noh->data, len_data, NOKIA_EI_ACK, &ack, - sizeof(uint8_t)) == sizeof(uint8_t)) { - LOGP(DNM, LOGL_INFO, "ACK = %d\n", ack); - if (ack != 1) { - LOGP(DNM, LOGL_ERROR, "No ACK received (%d)\n", - ack); - /* TODO: properly handle failures (NACK) */ - } - } else - LOGP(DNM, LOGL_ERROR, "ACK not found\n"); - - /* TODO: the assumption for the following is that no NACK was received */ - - /* ACK for reset message ? */ - if (!bts->nokia.did_reset) { - bts->nokia.did_reset = 1; - - /* - TODO: For the InSite processing the received data is - blocked in the driver during reset. - Otherwise the LAPD module might assert because the InSite - sends garbage on the E1 line during reset. - This is done by looking at "wait_reset" in the driver - (function handle_ts1_read()) and ignoring the received data. - It seems to be necessary for the MetroSite too. - */ - bts->nokia.wait_reset = 1; - - bts->nokia.reset_timer.cb = &reset_timer_cb; - bts->nokia.reset_timer.data = bts; - osmo_timer_schedule(&bts->nokia.reset_timer, RESET_INTERVAL); - - 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); - - 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 101e996dd..000000000 --- a/openbsc/src/libbsc/bts_siemens_bs11.c +++ /dev/null @@ -1,607 +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; - - switch (ts->pchan) { - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts, - e1l->e1_ts_ss); - break; - default: - break; - } -} - -static void nm_reconfig_trx(struct gsm_bts_trx *trx) -{ - struct gsm_e1_subslot *e1l = &trx->rsl_e1_link; - int i; - - patch_nm_tables(trx->bts); - - switch (trx->bts->type) { - case GSM_BTS_TYPE_BS11: - /* FIXME: discover this by fetching an attribute */ -#if 0 - trx->nominal_power = 15; /* 15dBm == 30mW PA configuration */ -#else - trx->nominal_power = 24; /* 24dBm == 250mW PA configuration */ -#endif - abis_nm_conn_terr_sign(trx, e1l->e1_nr, e1l->e1_ts, - e1l->e1_ts_ss); - abis_nm_establish_tei(trx->bts, trx->nr, e1l->e1_nr, - e1l->e1_ts, e1l->e1_ts_ss, trx->rsl_tei); - - /* Set Radio Attributes */ - if (trx == trx->bts->c0) - abis_nm_set_radio_attr(trx, bs11_attr_radio, - sizeof(bs11_attr_radio)); - else { - uint8_t trx1_attr_radio[sizeof(bs11_attr_radio)]; - uint8_t arfcn_low = trx->arfcn & 0xff; - uint8_t arfcn_high = (trx->arfcn >> 8) & 0x0f; - memcpy(trx1_attr_radio, bs11_attr_radio, - sizeof(trx1_attr_radio)); - - /* patch ARFCN into TRX Attributes */ - trx1_attr_radio[2] &= 0xf0; - trx1_attr_radio[2] |= arfcn_high; - trx1_attr_radio[3] = arfcn_low; - - abis_nm_set_radio_attr(trx, trx1_attr_radio, - sizeof(trx1_attr_radio)); - } - break; - case GSM_BTS_TYPE_NANOBTS: - switch (trx->bts->band) { - case GSM_BAND_850: - case GSM_BAND_900: - trx->nominal_power = 20; - break; - case GSM_BAND_1800: - case GSM_BAND_1900: - trx->nominal_power = 23; - break; - default: - LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n", - gsm_band_name(trx->bts->band)); - break; - } - break; - default: - break; - } - - for (i = 0; i < TRX_NR_TS; i++) - nm_reconfig_ts(&trx->ts[i]); -} - -static void nm_reconfig_bts(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - patch_nm_tables(bts); - abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/ - abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts)); - abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */ - abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */ - break; - default: - break; - } - - llist_for_each_entry(trx, &bts->trx_list, list) - nm_reconfig_trx(trx); -} - - -static void bootstrap_om_bs11(struct gsm_bts *bts) -{ - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); - - /* stop sending event reports */ - abis_nm_event_reports(bts, 0); - - /* begin DB transmission */ - abis_nm_bs11_db_transmission(bts, 1); - - /* end DB transmission */ - abis_nm_bs11_db_transmission(bts, 0); - - /* Reset BTS Site manager resource */ - abis_nm_bs11_reset_resource(bts); - - /* begin DB transmission */ - abis_nm_bs11_db_transmission(bts, 1); - - /* reconfigure BTS with all TRX and all TS */ - nm_reconfig_bts(bts); - - /* end DB transmission */ - abis_nm_bs11_db_transmission(bts, 0); - - /* Reset BTS Site manager resource */ - abis_nm_bs11_reset_resource(bts); - - /* restart sending event reports */ - abis_nm_event_reports(bts, 1); -} - -static int shutdown_om(struct gsm_bts *bts) -{ - /* stop sending event reports */ - abis_nm_event_reports(bts, 0); - - /* begin DB transmission */ - abis_nm_bs11_db_transmission(bts, 1); - - /* end DB transmission */ - abis_nm_bs11_db_transmission(bts, 0); - - /* Reset BTS Site manager resource */ - abis_nm_bs11_reset_resource(bts); - - return 0; -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int gbl_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_bts *bts; - - if (subsys != SS_L_GLOBAL) - return 0; - - switch (signal) { - case S_GLOBAL_BTS_CLOSE_OM: - bts = signal_data; - if (bts->type == GSM_BTS_TYPE_BS11) - shutdown_om(signal_data); - break; - } - - return 0; -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - - if (subsys != SS_L_INPUT) - return 0; - - switch (signal) { - case S_L_INP_TEI_UP: - switch (isd->link_type) { - case E1INP_SIGN_OML: - if (isd->trx->bts->type == GSM_BTS_TYPE_BS11) - bootstrap_om_bs11(isd->trx->bts); - break; - } - } - - return 0; -} - -static int bts_model_bs11_start(struct gsm_network *net) -{ - model_bs11.features.data = &model_bs11._features_data[0]; - model_bs11.features.data_len = sizeof(model_bs11._features_data); - - gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HOPPING); - gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HSCSD); - - 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 754e277e5..000000000 --- a/openbsc/src/libbsc/bts_sysmobts.c +++ /dev/null @@ -1,59 +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_OSMO_SYSMO; - - model_sysmobts.features.data = &model_sysmobts._features_data[0]; - model_sysmobts.features.data_len = - sizeof(model_sysmobts._features_data); - - gsm_btsmodel_set_feature(&model_sysmobts, BTS_FEAT_GPRS); - gsm_btsmodel_set_feature(&model_sysmobts, BTS_FEAT_EGPRS); - - return gsm_bts_model_register(&model_sysmobts); -} diff --git a/openbsc/src/libbsc/bts_unknown.c b/openbsc/src/libbsc/bts_unknown.c deleted file mode 100644 index f1135294f..000000000 --- a/openbsc/src/libbsc/bts_unknown.c +++ /dev/null @@ -1,40 +0,0 @@ -/* Generic BTS - VTY code tries to allocate this BTS before type is known */ - -/* (C) 2010 by Daniel Willmann - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more 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 9b74329f4..000000000 --- a/openbsc/src/libbsc/chan_alloc.c +++ /dev/null @@ -1,493 +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; - } - - 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; -} - -struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts, - enum gsm_phys_chan_config pchan) -{ - struct gsm_bts_trx *trx = bts->c0; - struct gsm_bts_trx_ts *ts = &trx->ts[0]; - - if (pchan != GSM_PCHAN_CCCH && - pchan != GSM_PCHAN_CCCH_SDCCH4) - return NULL; - - if (ts->pchan != GSM_PCHAN_NONE) - return NULL; - - ts->pchan = pchan; - - return ts; -} - -/* Allocate a physical channel (TS) */ -struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts, - enum gsm_phys_chan_config pchan) -{ - int j; - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - int from, to; - - if (!trx_is_usable(trx)) - continue; - - /* the following constraints are pure policy, - * no requirement to put this restriction in place */ - if (trx == bts->c0) { - /* On the first TRX we run one CCCH and one SDCCH8 */ - switch (pchan) { - case GSM_PCHAN_CCCH: - case GSM_PCHAN_CCCH_SDCCH4: - from = 0; to = 0; - break; - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - from = 1; to = 7; - break; - case GSM_PCHAN_SDCCH8_SACCH8C: - default: - return NULL; - } - } else { - /* Every secondary TRX is configured for TCH/F - * and TCH/H only */ - switch (pchan) { - case GSM_PCHAN_SDCCH8_SACCH8C: - from = 1; to = 1; - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - from = 1; to = 7; - break; - default: - return NULL; - } - } - - for (j = from; j <= to; j++) { - struct gsm_bts_trx_ts *ts = &trx->ts[j]; - - if (!ts_is_usable(ts)) - continue; - - if (ts->pchan == GSM_PCHAN_NONE) { - ts->pchan = pchan; - /* set channel attribute on OML */ - abis_nm_set_channel_attr(ts, abis_nm_chcomb4pchan(pchan)); - return ts; - } - } - } - return NULL; -} - -/* Free a physical channel (TS) */ -void ts_free(struct gsm_bts_trx_ts *ts) -{ - ts->pchan = GSM_PCHAN_NONE; -} - -static const uint8_t subslots_per_pchan[] = { - [GSM_PCHAN_NONE] = 0, - [GSM_PCHAN_CCCH] = 0, - [GSM_PCHAN_CCCH_SDCCH4] = 4, - [GSM_PCHAN_TCH_F] = 1, - [GSM_PCHAN_TCH_H] = 2, - [GSM_PCHAN_SDCCH8_SACCH8C] = 8, - /* FIXME: what about dynamic TCH_F_TCH_H ? */ - [GSM_PCHAN_TCH_F_PDCH] = 1, -}; - -static struct gsm_lchan * -_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan) -{ - struct gsm_bts_trx_ts *ts; - int j, ss; - - if (!trx_is_usable(trx)) - return NULL; - - for (j = 0; j < 8; j++) { - ts = &trx->ts[j]; - if (!ts_is_usable(ts)) - continue; - /* ip.access dynamic TCH/F + PDCH combination */ - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH && - pchan == GSM_PCHAN_TCH_F) { - /* we can only consider such a dynamic channel - * if the PDCH is currently inactive */ - if (ts->flags & TS_F_PDCH_MODE) - continue; - } else if (ts->pchan != pchan) - continue; - /* check if all sub-slots are allocated yet */ - for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) { - struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->type == GSM_LCHAN_NONE && - lc->state == LCHAN_S_NONE) - return lc; - } - } - - return NULL; -} - -static struct gsm_lchan * -_lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) -{ - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - struct gsm_lchan *lc; - - if (bts->chan_alloc_reverse) { - llist_for_each_entry_reverse(trx, &bts->trx_list, list) { - lc = _lc_find_trx(trx, pchan); - if (lc) - return lc; - } - } else { - llist_for_each_entry(trx, &bts->trx_list, list) { - lc = _lc_find_trx(trx, pchan); - if (lc) - return lc; - } - } - - /* we cannot allocate more of these */ - if (pchan == GSM_PCHAN_CCCH_SDCCH4) - return NULL; - - /* if we've reached here, we need to allocate a new physical - * channel for the logical channel type requested */ - ts = ts_alloc(bts, pchan); - if (!ts) { - /* no more radio resources */ - return NULL; - } - return &ts->lchan[0]; -} - -/* Allocate a logical channel */ -struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, - int allow_bigger) -{ - struct gsm_lchan *lchan = NULL; - enum gsm_phys_chan_config first, second; - - switch (type) { - case GSM_LCHAN_SDCCH: - if (bts->chan_alloc_reverse) { - first = GSM_PCHAN_SDCCH8_SACCH8C; - second = GSM_PCHAN_CCCH_SDCCH4; - } else { - first = GSM_PCHAN_CCCH_SDCCH4; - second = GSM_PCHAN_SDCCH8_SACCH8C; - } - - lchan = _lc_find_bts(bts, first); - if (lchan == NULL) - lchan = _lc_find_bts(bts, second); - - /* allow to assign bigger channels */ - if (allow_bigger) { - if (lchan == NULL) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); - type = GSM_LCHAN_TCH_H; - } - - if (lchan == NULL) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - type = GSM_LCHAN_TCH_F; - } - } - break; - case GSM_LCHAN_TCH_F: - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - break; - case GSM_LCHAN_TCH_H: - lchan =_lc_find_bts(bts, GSM_PCHAN_TCH_H); - /* If we don't have TCH/H available, fall-back to TCH/F */ - if (!lchan) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - type = GSM_LCHAN_TCH_F; - } - break; - default: - LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type); - } - - if (lchan) { - lchan->type = type; - - /* clear sapis */ - memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis)); - - /* clear multi rate config */ - memset(&lchan->mr_conf, 0, sizeof(lchan->mr_conf)); - } else { - struct challoc_signal_data sig; - sig.bts = bts; - sig.type = type; - osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_ALLOC_FAIL, &sig); - } - - return lchan; -} - -/* Free a logical channel */ -void lchan_free(struct gsm_lchan *lchan) -{ - struct challoc_signal_data sig; - int i; - - sig.type = lchan->type; - lchan->type = GSM_LCHAN_NONE; - - - if (lchan->conn) { - struct lchan_signal_data sig; - - /* We might kill an active channel... */ - sig.lchan = lchan; - sig.mr = NULL; - osmo_signal_dispatch(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, &sig); - } - - if (lchan->abis_ip.rtp_socket) { - LOGP(DRLL, LOGL_ERROR, "%s RTP Proxy Socket remained open.\n", - gsm_lchan_name(lchan)); - rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - } - - /* stop the timer */ - osmo_timer_del(&lchan->T3101); - - /* clear cached measuement reports */ - lchan->meas_rep_idx = 0; - for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) { - lchan->meas_rep[i].flags = 0; - lchan->meas_rep[i].nr = 0; - } - for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++) - lchan->neigh_meas[i].arfcn = 0; - - if (lchan->rqd_ref) { - talloc_free(lchan->rqd_ref); - lchan->rqd_ref = NULL; - lchan->rqd_ta = 0; - } - - sig.lchan = lchan; - sig.bts = lchan->ts->trx->bts; - osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_FREED, &sig); - - if (lchan->conn) { - LOGP(DRLL, LOGL_ERROR, "the subscriber connection should be gone.\n"); - lchan->conn = NULL; - } - - /* FIXME: ts_free() the timeslot, if we're the last logical - * channel using it */ -} - -/* - * There was an error with the TRX and we need to forget - * any state so that a lchan can be allocated again after - * the trx is fully usable. - * - * This should be called after lchan_free to force a channel - * be available for allocation again. This means that this - * method will stop the "delay after error"-timer and set the - * state to LCHAN_S_NONE. - */ -void lchan_reset(struct gsm_lchan *lchan) -{ - osmo_timer_del(&lchan->T3101); - osmo_timer_del(&lchan->T3109); - osmo_timer_del(&lchan->T3111); - osmo_timer_del(&lchan->error_timer); - - lchan->type = GSM_LCHAN_NONE; - lchan->state = LCHAN_S_NONE; - - if (lchan->abis_ip.rtp_socket) { - rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - } -} - -/* Drive the release process of the lchan */ -static void _lchan_handle_release(struct gsm_lchan *lchan, - int sacch_deact, int mode) -{ - /* Release all SAPIs on the local end and continue */ - rsl_release_sapis_from(lchan, 1, RSL_REL_LOCAL_END); - - /* - * Shall we send a RR Release, start T3109 and wait for the - * release indication from the BTS or just take it down (e.g. - * on assignment requests) - */ - if (sacch_deact) { - gsm48_send_rr_release(lchan); - - /* Deactivate the SACCH on the BTS side */ - rsl_deact_sacch(lchan); - rsl_start_t3109(lchan); - } else if (lchan->sapis[0] == LCHAN_SAPI_UNUSED) { - rsl_direct_rf_release(lchan); - } else { - rsl_release_request(lchan, 0, mode); - } -} - -/* Consider releasing the channel now */ -int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode mode) -{ - DEBUGP(DRLL, "%s starting release sequence\n", gsm_lchan_name(lchan)); - rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ); - - lchan->conn = NULL; - _lchan_handle_release(lchan, sacch_deact, mode); - return 1; -} - -static struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) { - struct gsm_bts_trx *trx; - int ts_no, lchan_no; - - llist_for_each_entry(trx, &bts->trx_list, list) { - for (ts_no = 0; ts_no < 8; ++ts_no) { - for (lchan_no = 0; lchan_no < TS_MAX_LCHAN; ++lchan_no) { - struct gsm_lchan *lchan = - &trx->ts[ts_no].lchan[lchan_no]; - if (lchan->conn && subscr == lchan->conn->subscr) - return lchan; - } - } - } - - return NULL; -} - -struct gsm_subscriber_connection *connection_for_subscr(struct gsm_subscriber *subscr) -{ - struct gsm_bts *bts; - struct gsm_network *net = subscr->net; - struct gsm_lchan *lchan; - - llist_for_each_entry(bts, &net->bts_list, list) { - lchan = lchan_find(bts, subscr); - if (lchan) - return lchan->conn; - } - - return NULL; -} - -void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - int i; - - /* skip administratively deactivated tranxsceivers */ - if (!nm_is_running(&trx->mo.nm_state) || - !nm_is_running(&trx->bb_transc.mo.nm_state)) - continue; - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - struct load_counter *pl = &cl->pchan[ts->pchan]; - int j; - - /* skip administratively deactivated timeslots */ - if (!nm_is_running(&ts->mo.nm_state)) - continue; - - for (j = 0; j < subslots_per_pchan[ts->pchan]; j++) { - struct gsm_lchan *lchan = &ts->lchan[j]; - - pl->total++; - - switch (lchan->state) { - case LCHAN_S_NONE: - break; - default: - pl->used++; - break; - } - } - } - } -} - -void network_chan_load(struct pchan_load *pl, struct gsm_network *net) -{ - struct gsm_bts *bts; - - memset(pl, 0, sizeof(*pl)); - - llist_for_each_entry(bts, &net->bts_list, list) - bts_chan_load(pl, bts); -} - diff --git a/openbsc/src/libbsc/e1_config.c b/openbsc/src/libbsc/e1_config.c deleted file mode 100644 index d82b009c7..000000000 --- a/openbsc/src/libbsc/e1_config.c +++ /dev/null @@ -1,303 +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 tale the configuration present in the - * bts/trx/ts data structures and ensure the E1 configuration reflects the - * timeslot/subslot/TEI configuration */ - -int e1_reconfig_ts(struct gsm_bts_trx_ts *ts) -{ - struct gsm_e1_subslot *e1_link = &ts->e1_link; - struct e1inp_line *line; - struct e1inp_ts *e1_ts; - - DEBUGP(DLMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr); - - if (!e1_link->e1_ts) { - LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) without E1 timeslot?\n", - ts->nr, ts->trx->nr, ts->trx->bts->nr); - return 0; - } - - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) referring to " - "non-existing E1 line %u\n", ts->nr, ts->trx->nr, - ts->trx->bts->nr, e1_link->e1_nr); - return -ENOMEM; - } - - switch (ts->pchan) { - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - e1_ts = &line->ts[e1_link->e1_ts-1]; - e1inp_ts_config_trau(e1_ts, line, subch_cb); - subch_demux_activate(&e1_ts->trau.demux, e1_link->e1_ts_ss); - break; - default: - break; - } - - return 0; -} - -int e1_reconfig_trx(struct gsm_bts_trx *trx) -{ - struct gsm_e1_subslot *e1_link = &trx->rsl_e1_link; - struct e1inp_ts *sign_ts; - struct e1inp_line *line; - struct e1inp_sign_link *rsl_link; - int i; - - if (!e1_link->e1_ts) { - LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link without " - "timeslot?\n", trx->bts->nr, trx->nr); - return -EINVAL; - } - - /* RSL Link */ - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link referring " - "to non-existing E1 line %u\n", trx->bts->nr, - trx->nr, e1_link->e1_nr); - return -ENOMEM; - } - sign_ts = &line->ts[e1_link->e1_ts-1]; - e1inp_ts_config_sign(sign_ts, line); - /* Ericsson RBS have a per-TRX OML link in parallel to RSL */ - if (trx->bts->type == GSM_BTS_TYPE_RBS2000) { - struct e1inp_sign_link *oml_link; - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, trx, - trx->rsl_tei, SAPI_OML); - if (!oml_link) { - LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) OML link creation " - "failed\n", trx->bts->nr, trx->nr); - return -ENOMEM; - } - if (trx->oml_link) - e1inp_sign_link_destroy(trx->oml_link); - trx->oml_link = oml_link; - } - rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, - trx, trx->rsl_tei, SAPI_RSL); - if (!rsl_link) { - LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link creation " - "failed\n", trx->bts->nr, trx->nr); - return -ENOMEM; - } - if (trx->rsl_link) - e1inp_sign_link_destroy(trx->rsl_link); - trx->rsl_link = rsl_link; - - for (i = 0; i < TRX_NR_TS; i++) - e1_reconfig_ts(&trx->ts[i]); - - return 0; -} - -/* this is the generic callback for all ISDN-based BTS. */ -static int bts_isdn_sign_link(struct msgb *msg) -{ - int ret = -EINVAL; - struct e1inp_sign_link *link = msg->dst; - struct gsm_bts *bts; - - log_set_context(BSC_CTX_BTS, link->trx->bts); - switch (link->type) { - case E1INP_SIGN_OML: - bts = link->trx->bts; - ret = bts->model->oml_rcvmsg(msg); - break; - case E1INP_SIGN_RSL: - ret = abis_rsl_rcvmsg(msg); - break; - default: - LOGP(DLMI, LOGL_ERROR, "unknown link type %u\n", link->type); - break; - } - return ret; -} - -struct e1inp_line_ops bts_isdn_e1inp_line_ops = { - .sign_link = bts_isdn_sign_link, -}; - -int e1_reconfig_bts(struct gsm_bts *bts) -{ - struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; - struct e1inp_ts *sign_ts; - struct e1inp_line *line; - struct e1inp_sign_link *oml_link; - struct gsm_bts_trx *trx; - - DEBUGP(DLMI, "e1_reconfig_bts(%u)\n", bts->nr); - - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " - "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); - return -ENOMEM; - } - - if (!bts->model->e1line_bind_ops) { - LOGP(DLINP, LOGL_ERROR, "no callback to bind E1 line operations\n"); - return -EINVAL; - } - if (!line->ops) - bts->model->e1line_bind_ops(line); - - /* skip signal link initialization, this is done later for these BTS. */ - if (bts->type == GSM_BTS_TYPE_NANOBTS || - bts->type == GSM_BTS_TYPE_OSMO_SYSMO) - return e1inp_line_update(line); - - /* OML link */ - if (!e1_link->e1_ts) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link without timeslot?\n", - bts->nr); - return -EINVAL; - } - - sign_ts = &line->ts[e1_link->e1_ts-1]; - e1inp_ts_config_sign(sign_ts, line); - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, - bts->c0, bts->oml_tei, SAPI_OML); - if (!oml_link) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link creation failed\n", - bts->nr); - return -ENOMEM; - } - if (bts->oml_link) - e1inp_sign_link_destroy(bts->oml_link); - bts->oml_link = oml_link; - - llist_for_each_entry(trx, &bts->trx_list, list) - e1_reconfig_trx(trx); - - /* notify E1 input something has changed */ - return e1inp_line_update(line); -} - -#if 0 -/* do some compiled-in configuration for our BTS/E1 setup */ -int e1_config(struct gsm_bts *bts, int cardnr, int release_l2) -{ - struct e1inp_line *line; - struct e1inp_ts *sign_ts; - struct e1inp_sign_link *oml_link, *rsl_link; - struct gsm_bts_trx *trx = bts->c0; - int base_ts; - - switch (bts->nr) { - case 0: - /* First BTS uses E1 TS 01,02,03,04,05 */ - base_ts = HARDCODED_BTS0_TS - 1; - break; - case 1: - /* Second BTS uses E1 TS 06,07,08,09,10 */ - base_ts = HARDCODED_BTS1_TS - 1; - break; - case 2: - /* Third BTS uses E1 TS 11,12,13,14,15 */ - base_ts = HARDCODED_BTS2_TS - 1; - default: - return -EINVAL; - } - - line = talloc_zero(tall_bsc_ctx, struct e1inp_line); - if (!line) - return -ENOMEM; - - /* create E1 timeslots for signalling and TRAU frames */ - e1inp_ts_config(&line->ts[base_ts+1-1], line, E1INP_TS_TYPE_SIGN); - e1inp_ts_config(&line->ts[base_ts+2-1], line, E1INP_TS_TYPE_TRAU); - e1inp_ts_config(&line->ts[base_ts+3-1], line, E1INP_TS_TYPE_TRAU); - - /* create signalling links for TS1 */ - sign_ts = &line->ts[base_ts+1-1]; - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, - trx, TEI_OML, SAPI_OML); - rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, - trx, TEI_RSL, SAPI_RSL); - - /* create back-links from bts/trx */ - bts->oml_link = oml_link; - trx->rsl_link = rsl_link; - - /* enable subchannel demuxer on TS2 */ - subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 1); - subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 2); - subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 3); - - /* enable subchannel demuxer on TS3 */ - subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 0); - subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 1); - subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 2); - subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 3); - - trx = gsm_bts_trx_num(bts, 1); - if (trx) { - /* create E1 timeslots for TRAU frames of TRX1 */ - e1inp_ts_config(&line->ts[base_ts+4-1], line, E1INP_TS_TYPE_TRAU); - e1inp_ts_config(&line->ts[base_ts+5-1], line, E1INP_TS_TYPE_TRAU); - - /* create RSL signalling link for TRX1 */ - sign_ts = &line->ts[base_ts+1-1]; - rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, - trx, TEI_RSL+1, SAPI_RSL); - /* create back-links from trx */ - trx->rsl_link = rsl_link; - - /* enable subchannel demuxer on TS2 */ - subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 0); - subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 1); - subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 2); - subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 3); - - /* enable subchannel demuxer on TS3 */ - subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 0); - subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 1); - subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 2); - subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 3); - } - - return mi_setup(cardnr, line, release_l2); -} -#endif 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 8ccefd740..000000000 --- a/openbsc/src/libbsc/gsm_04_08_utils.c +++ /dev/null @@ -1,651 +0,0 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 - * utility functions - */ - -/* (C) 2008-2009 by Harald Welte - * (C) 2008, 2009 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -/* should ip.access BTS use direct RTP streams between each other (1), - * or should OpenBSC always act as RTP relay/proxy in between (0) ? */ -int ipacc_rtp_direct = 1; - -static int gsm48_sendmsg(struct msgb *msg) -{ - if (msg->lchan) - msg->dst = msg->lchan->ts->trx->rsl_link; - - msg->l3h = msg->data; - return rsl_data_request(msg, 0); -} - -/* Section 9.1.8 / Table 9.9 */ -struct chreq { - uint8_t val; - uint8_t mask; - enum chreq_type type; -}; - -/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */ -static const struct chreq chreq_type_neci1[] = { - { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, - { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F }, - { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H }, - { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL }, - { 0xe0, 0xe0, CHREQ_T_TCH_F }, - { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H }, - { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, - { 0x00, 0xf0, CHREQ_T_LOCATION_UPD }, - { 0x10, 0xf0, CHREQ_T_SDCCH }, - { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 }, - { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, - { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, - { 0x67, 0xff, CHREQ_T_LMU }, - { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, - { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, - { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, - { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, -}; - -/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */ -static const struct chreq chreq_type_neci0[] = { - { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, - { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H }, - { 0xe0, 0xe0, CHREQ_T_TCH_F }, - { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, - { 0x00, 0xe0, CHREQ_T_LOCATION_UPD }, - { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 }, - { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, - { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, - { 0x67, 0xff, CHREQ_T_LMU }, - { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, - { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, - { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, - { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, -}; - -static const enum gsm_chan_t ctype_by_chreq[] = { - [CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F, - [CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H, - [CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH, - [CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F, - [CHREQ_T_LMU] = GSM_LCHAN_SDCCH, - [CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH, - [CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN, -}; - -static const enum gsm_chreq_reason_t reason_by_chreq[] = { - [CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG, - [CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD, - [CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER, -}; - -/* verify that the two tables match */ -osmo_static_assert(sizeof(ctype_by_chreq) == - sizeof(((struct gsm_network *) NULL)->ctype_by_chreq), assert_size); - -/* - * Update channel types for request based on policy. E.g. in the - * case of a TCH/H network/bsc use TCH/H for the emergency calls, - * for early assignment assign a SDCCH and some other options. - */ -void gsm_net_update_ctype(struct gsm_network *network) -{ - /* copy over the data */ - memcpy(network->ctype_by_chreq, ctype_by_chreq, sizeof(ctype_by_chreq)); - - /* - * Use TCH/H for emergency calls when this cell allows TCH/H. Maybe it - * is better to iterate over the BTS/TRX and check if no TCH/F is available - * and then set it to TCH/H. - */ - if (network->neci) - network->ctype_by_chreq[CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_H; - - if (network->pag_any_tch) { - if (network->neci) { - network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_H; - network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_H; - } else { - network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_F; - network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_F; - } - } -} - -enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, uint8_t ra) -{ - int i; - int length; - const struct chreq *chreq; - - if (network->neci) { - chreq = chreq_type_neci1; - length = ARRAY_SIZE(chreq_type_neci1); - } else { - chreq = chreq_type_neci0; - length = ARRAY_SIZE(chreq_type_neci0); - } - - - for (i = 0; i < length; i++) { - const struct chreq *chr = &chreq[i]; - if ((ra & chr->mask) == chr->val) - return network->ctype_by_chreq[chr->type]; - } - LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra); - return GSM_LCHAN_SDCCH; -} - -int get_reason_by_chreq(uint8_t ra, int neci) -{ - int i; - int length; - const struct chreq *chreq; - - if (neci) { - chreq = chreq_type_neci1; - length = ARRAY_SIZE(chreq_type_neci1); - } else { - chreq = chreq_type_neci0; - length = ARRAY_SIZE(chreq_type_neci0); - } - - for (i = 0; i < length; i++) { - const struct chreq *chr = &chreq[i]; - if ((ra & chr->mask) == chr->val) - return reason_by_chreq[chr->type]; - } - LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra); - return GSM_CHREQ_REASON_OTHER; -} - -/* 7.1.7 and 9.1.7: RR CHANnel RELease */ -int gsm48_send_rr_release(struct gsm_lchan *lchan) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - uint8_t *cause; - - msg->lchan = lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_CHAN_REL; - - cause = msgb_put(msg, 1); - cause[0] = GSM48_RR_CAUSE_NORMAL; - - DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n", - lchan->nr, lchan->type); - - /* Send actual release request to MS */ - return gsm48_sendmsg(msg); -} - -int send_siemens_mrpci(struct gsm_lchan *lchan, - uint8_t *classmark2_lv) -{ - struct rsl_mrpci mrpci; - - if (classmark2_lv[0] < 2) - return -EINVAL; - - mrpci.power_class = classmark2_lv[1] & 0x7; - mrpci.vgcs_capable = classmark2_lv[2] & (1 << 1); - mrpci.vbs_capable = classmark2_lv[2] & (1 <<2); - mrpci.gsm_phase = (classmark2_lv[1]) >> 5 & 0x3; - - return rsl_siemens_mrpci(lchan, &mrpci); -} - -int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type) -{ - /* Check the size for the classmark */ - if (length < 1 + *classmark2_lv) - return -1; - - uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1; - if (length < 2 + *classmark2_lv + mi_lv[0]) - return -2; - - *mi_type = mi_lv[1] & GSM_MI_TYPE_MASK; - return gsm48_mi_to_string(mi_string, GSM48_MI_SIZE, mi_lv+1, *mi_lv); -} - -int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length, - char *mi_string, uint8_t *mi_type) -{ - static const uint32_t classmark_offset = - offsetof(struct gsm48_pag_resp, classmark2); - uint8_t *classmark2_lv = (uint8_t *) &resp->classmark2; - return gsm48_extract_mi(classmark2_lv, length - classmark_offset, - mi_string, mi_type); -} - -int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, - struct msgb *msg, struct gsm_subscriber *subscr) -{ - struct gsm_bts *bts = msg->lchan->ts->trx->bts; - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t *classmark2_lv = gh->data + 1; - - if (is_siemens_bts(bts)) - send_siemens_mrpci(msg->lchan, classmark2_lv); - - if (!conn->subscr) { - conn->subscr = subscr; - } else if (conn->subscr != subscr) { - LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n"); - subscr_put(subscr); - return -EINVAL; - } else { - DEBUGP(DRR, "<- Channel already owned by us\n"); - subscr_put(subscr); - subscr = conn->subscr; - } - - osmo_counter_inc(bts->network->stats.paging.completed); - - /* Stop paging on the bts we received the paging response */ - paging_request_stop(conn->bts, subscr, conn, msg); - return 0; -} - -/* Chapter 9.1.9: Ciphering Mode Command */ -int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - uint8_t ciph_mod_set; - - msg->lchan = lchan; - - DEBUGP(DRR, "TX CIPHERING MODE CMD\n"); - - if (lchan->encr.alg_id <= RSL_ENC_ALG_A5(0)) - ciph_mod_set = 0; - else - ciph_mod_set = (lchan->encr.alg_id-2)<<1 | 1; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_CIPH_M_CMD; - gh->data[0] = (want_imeisv & 0x1) << 4 | (ciph_mod_set & 0xf); - - return rsl_encryption_cmd(msg); -} - -static void gsm48_cell_desc(struct gsm48_cell_desc *cd, - const struct gsm_bts *bts) -{ - cd->ncc = (bts->bsic >> 3 & 0x7); - cd->bcc = (bts->bsic & 0x7); - cd->arfcn_hi = bts->c0->arfcn >> 8; - cd->arfcn_lo = bts->c0->arfcn & 0xff; -} - -void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, - const struct gsm_lchan *lchan) -{ - uint16_t arfcn = lchan->ts->trx->arfcn & 0x3ff; - - cd->chan_nr = gsm_lchan2chan_nr(lchan); - if (!lchan->ts->hopping.enabled) { - cd->h0.tsc = lchan->ts->trx->bts->tsc; - cd->h0.h = 0; - cd->h0.arfcn_high = arfcn >> 8; - cd->h0.arfcn_low = arfcn & 0xff; - } else { - cd->h1.tsc = lchan->ts->trx->bts->tsc; - cd->h1.h = 1; - cd->h1.maio_high = lchan->ts->hopping.maio >> 2; - cd->h1.maio_low = lchan->ts->hopping.maio & 0x03; - cd->h1.hsn = lchan->ts->hopping.hsn; - } -} - -#define GSM48_HOCMD_CCHDESC_LEN 16 - -/* Chapter 9.1.15: Handover Command */ -int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, - uint8_t power_command, uint8_t ho_ref) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_ho_cmd *ho = - (struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho)); - - msg->lchan = old_lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_HANDO_CMD; - - /* mandatory bits */ - gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts); - gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan); - ho->ho_ref = ho_ref; - ho->power_command = power_command; - - if (new_lchan->ts->hopping.enabled) { - struct gsm_bts *bts = new_lchan->ts->trx->bts; - struct gsm48_system_information_type_1 *si1; - uint8_t *cur; - - si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1); - /* Copy the Cell Chan Desc (ARFCNS in this cell) */ - msgb_put_u8(msg, GSM48_IE_CELL_CH_DESC); - cur = msgb_put(msg, GSM48_HOCMD_CCHDESC_LEN); - memcpy(cur, si1->cell_channel_description, - GSM48_HOCMD_CCHDESC_LEN); - /* Copy the Mobile Allocation */ - msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, - new_lchan->ts->hopping.ma_len, - new_lchan->ts->hopping.ma_data); - } - /* FIXME: optional bits for type of synchronization? */ - - return gsm48_sendmsg(msg); -} - -/* Chapter 9.1.2: Assignment Command */ -int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_ass_cmd *ass = - (struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass)); - - DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode); - - msg->lchan = dest_lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_ASS_CMD; - - /* - * fill the channel information element, this code - * should probably be shared with rsl_rx_chan_rqd(), - * gsm48_tx_chan_mode_modify. But beware that 10.5.2.5 - * 10.5.2.5.a have slightly different semantic for - * the chan_desc. But as long as multi-slot configurations - * are not used we seem to be fine. - */ - gsm48_lchan2chan_desc(&ass->chan_desc, lchan); - ass->power_command = power_command; - - /* optional: cell channel description */ - - msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode); - - /* mobile allocation in case of hopping */ - if (lchan->ts->hopping.enabled) { - msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, lchan->ts->hopping.ma_len, - lchan->ts->hopping.ma_data); - } - - /* in case of multi rate we need to attach a config */ - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { - if (lchan->mr_conf.ver == 0) { - LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " - "without multirate config.\n"); - } else { - uint8_t *data = msgb_put(msg, 4); - data[0] = GSM48_IE_MUL_RATE_CFG; - data[1] = 0x2; - memcpy(&data[2], &lchan->mr_conf, 2); - } - } - - return gsm48_sendmsg(msg); -} - -/* 9.1.5 Channel mode modify: Modify the mode on the MS side */ -int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, uint8_t mode) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_chan_mode_modify *cmm = - (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm)); - - DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode); - - lchan->tch_mode = mode; - msg->lchan = lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF; - - /* fill the channel information element, this code - * should probably be shared with rsl_rx_chan_rqd() */ - gsm48_lchan2chan_desc(&cmm->chan_desc, lchan); - cmm->mode = mode; - - /* in case of multi rate we need to attach a config */ - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { - if (lchan->mr_conf.ver == 0) { - LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " - "without multirate config.\n"); - } else { - uint8_t *data = msgb_put(msg, 4); - data[0] = GSM48_IE_MUL_RATE_CFG; - data[1] = 0x2; - memcpy(&data[2], &lchan->mr_conf, 2); - } - } - - return gsm48_sendmsg(msg); -} - -int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t lchan_mode) -{ - int rc; - - rc = gsm48_tx_chan_mode_modify(lchan, lchan_mode); - if (rc < 0) - return rc; - - return rc; -} - -int gsm48_rx_rr_modif_ack(struct msgb *msg) -{ - int rc; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_chan_mode_modify *mod = - (struct gsm48_chan_mode_modify *) gh->data; - - DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n"); - - if (mod->mode != msg->lchan->tch_mode) { - LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n", - msg->lchan->tch_mode, mod->mode); - return -1; - } - - /* update the channel type */ - switch (mod->mode) { - case GSM48_CMODE_SIGN: - msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; - break; - case GSM48_CMODE_SPEECH_V1: - case GSM48_CMODE_SPEECH_EFR: - case GSM48_CMODE_SPEECH_AMR: - msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; - break; - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_6k0: - case GSM48_CMODE_DATA_3k6: - msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA; - break; - } - - /* We've successfully modified the MS side of the channel, - * now go on to modify the BTS side of the channel */ - rc = rsl_chan_mode_modify_req(msg->lchan); - - /* FIXME: we not only need to do this after mode modify, but - * also after channel activation */ - if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN) - rsl_ipacc_crcx(msg->lchan); - return rc; -} - -int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t *data = gh->data; - struct gsm_bts *bts = msg->lchan->ts->trx->bts; - struct bitvec *nbv = &bts->si_common.neigh_list; - struct gsm_meas_rep_cell *mrc; - - if (gh->msg_type != GSM48_MT_RR_MEAS_REP) - return -EINVAL; - - if (data[0] & 0x80) - rep->flags |= MEAS_REP_F_BA1; - if (data[0] & 0x40) - rep->flags |= MEAS_REP_F_UL_DTX; - if ((data[1] & 0x40) == 0x00) - rep->flags |= MEAS_REP_F_DL_VALID; - - rep->dl.full.rx_lev = data[0] & 0x3f; - rep->dl.sub.rx_lev = data[1] & 0x3f; - rep->dl.full.rx_qual = (data[2] >> 4) & 0x7; - rep->dl.sub.rx_qual = (data[2] >> 1) & 0x7; - - rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2); - if (rep->num_cell < 1 || rep->num_cell > 6) - return 0; - - /* an encoding nightmare in perfection */ - mrc = &rep->cell[0]; - mrc->rxlev = data[3] & 0x3f; - mrc->neigh_idx = data[4] >> 3; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5); - if (rep->num_cell < 2) - return 0; - - mrc = &rep->cell[1]; - mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7); - mrc->neigh_idx = (data[6] >> 2) & 0x1f; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4); - if (rep->num_cell < 3) - return 0; - - mrc = &rep->cell[2]; - mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6); - mrc->neigh_idx = (data[8] >> 1) & 0x1f; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3); - if (rep->num_cell < 4) - return 0; - - mrc = &rep->cell[3]; - mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5); - mrc->neigh_idx = data[10] & 0x1f; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = data[11] >> 2; - if (rep->num_cell < 5) - return 0; - - mrc = &rep->cell[4]; - mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4); - mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7); - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = (data[13] >> 1) & 0x3f; - if (rep->num_cell < 6) - return 0; - - mrc = &rep->cell[5]; - mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3); - mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6); - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = data[15] & 0x3f; - - return 0; -} - -struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value) -{ - struct msgb *msg; - struct gsm48_hdr *gh; - - msg = gsm48_msgb_alloc(); - if (!msg) - return NULL; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_CM_SERV_REJ; - gh->data[0] = value; - - return msg; -} - -struct msgb *gsm48_create_loc_upd_rej(uint8_t cause) -{ - struct gsm48_hdr *gh; - struct msgb *msg; - - msg = gsm48_msgb_alloc(); - if (!msg) - return NULL; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT; - gh->data[0] = cause; - return msg; -} diff --git a/openbsc/src/libbsc/gsm_subscriber_base.c b/openbsc/src/libbsc/gsm_subscriber_base.c deleted file mode 100644 index 747a699f9..000000000 --- a/openbsc/src/libbsc/gsm_subscriber_base.c +++ /dev/null @@ -1,149 +0,0 @@ -/* The concept of a subscriber as seen by the BSC */ - -/* (C) 2008 by Harald Welte - * (C) 2009-2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -LLIST_HEAD(active_subscribers); -void *tall_subscr_ctx; - -/* for the gsm_subscriber.c */ -struct llist_head *subscr_bsc_active_subscribers(void) -{ - return &active_subscribers; -} - - -char *subscr_name(struct gsm_subscriber *subscr) -{ - if (strlen(subscr->name)) - return subscr->name; - - return subscr->imsi; -} - -struct gsm_subscriber *subscr_alloc(void) -{ - struct gsm_subscriber *s; - - s = talloc_zero(tall_subscr_ctx, struct gsm_subscriber); - if (!s) - return NULL; - - llist_add_tail(&s->entry, &active_subscribers); - s->use_count = 1; - s->tmsi = GSM_RESERVED_TMSI; - - INIT_LLIST_HEAD(&s->requests); - - return s; -} - -static void subscr_free(struct gsm_subscriber *subscr) -{ - llist_del(&subscr->entry); - talloc_free(subscr); -} - -struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr) -{ - subscr->use_count++; - DEBUGP(DREF, "subscr %s usage increases usage to: %d\n", - subscr->extension, subscr->use_count); - return subscr; -} - -struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr) -{ - subscr->use_count--; - DEBUGP(DREF, "subscr %s usage decreased usage to: %d\n", - subscr->extension, subscr->use_count); - if (subscr->use_count <= 0 && !subscr->net->keep_subscr) - subscr_free(subscr); - return NULL; -} - -struct gsm_subscriber *subscr_get_or_create(struct gsm_network *net, - const char *imsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (strcmp(subscr->imsi, imsi) == 0 && subscr->net == net) - return subscr_get(subscr); - } - - subscr = subscr_alloc(); - if (!subscr) - return NULL; - - strcpy(subscr->imsi, imsi); - subscr->net = net; - return subscr; -} - -struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_network *net, uint32_t tmsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (subscr->tmsi == tmsi && subscr->net == net) - return subscr_get(subscr); - } - - return NULL; -} - -struct gsm_subscriber *subscr_active_by_imsi(struct gsm_network *net, const char *imsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (strcmp(subscr->imsi, imsi) == 0 && subscr->net == net) - return subscr_get(subscr); - } - - return NULL; -} - -int subscr_purge_inactive(struct gsm_network *net) -{ - struct gsm_subscriber *subscr, *tmp; - int purged = 0; - - llist_for_each_entry_safe(subscr, tmp, subscr_bsc_active_subscribers(), entry) { - if (subscr->net == net && subscr->use_count <= 0) { - subscr_free(subscr); - purged += 1; - } - } - - return purged; -} diff --git a/openbsc/src/libbsc/handover_decision.c b/openbsc/src/libbsc/handover_decision.c deleted file mode 100644 index 24c0f79cb..000000000 --- a/openbsc/src/libbsc/handover_decision.c +++ /dev/null @@ -1,297 +0,0 @@ -/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This - * only implements the handover algorithm/decision, but not execution - * of it */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/* issue handover to a cell identified by ARFCN and BSIC */ -static int handover_to_arfcn_bsic(struct gsm_lchan *lchan, - uint16_t arfcn, uint8_t bsic) -{ - struct gsm_bts *new_bts; - - /* resolve the gsm_bts structure for the best neighbor */ - new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic); - if (!new_bts) { - LOGP(DHO, LOGL_NOTICE, "unable to determine neighbor BTS " - "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic); - return -EINVAL; - } - - /* and actually try to handover to that cell */ - return bsc_handover_start(lchan, new_bts); -} - -/* did we get a RXLEV for a given cell in the given report? */ -static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr, - uint16_t arfcn, uint8_t bsic) -{ - int i; - - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - - /* search for matching report */ - if (!(mrc->arfcn == arfcn && mrc->bsic == bsic)) - continue; - - mrc->flags |= MRC_F_PROCESSED; - return mrc->rxlev; - } - return -ENODEV; -} - -/* obtain averaged rxlev for given neighbor */ -static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window) -{ - unsigned int i, idx; - int avg = 0; - - idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev), - nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev), - window); - - for (i = 0; i < window; i++) { - int j = (idx+i) % ARRAY_SIZE(nmp->rxlev); - - avg += nmp->rxlev[j]; - } - - return avg / window; -} - -/* find empty or evict bad neighbor */ -static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan) -{ - int j, worst = 999999; - struct neigh_meas_proc *nmp_worst = NULL; - - /* first try to find an empty/unused slot */ - for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; - if (!nmp->arfcn) - return nmp; - } - - /* no empty slot found. evict worst neighbor from list */ - for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; - int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG); - if (!nmp_worst || avg < worst) { - worst = avg; - nmp_worst = nmp; - } - } - - return nmp_worst; -} - -/* process neighbor cell measurement reports */ -static void process_meas_neigh(struct gsm_meas_rep *mr) -{ - int i, j, idx; - - /* for each reported cell, try to update global state */ - for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j]; - unsigned int idx; - int rxlev; - - /* skip unused entries */ - if (!nmp->arfcn) - continue; - - rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic); - idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); - if (rxlev >= 0) { - nmp->rxlev[idx] = rxlev; - nmp->last_seen_nr = mr->nr; - } else - nmp->rxlev[idx] = 0; - nmp->rxlev_cnt++; - } - - /* iterate over list of reported cells, check if we did not - * process all of them */ - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - struct neigh_meas_proc *nmp; - - if (mrc->flags & MRC_F_PROCESSED) - continue; - - nmp = find_evict_neigh(mr->lchan); - - nmp->arfcn = mrc->arfcn; - nmp->bsic = mrc->bsic; - - idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); - nmp->rxlev[idx] = mrc->rxlev; - nmp->rxlev_cnt++; - nmp->last_seen_nr = mr->nr; - - mrc->flags |= MRC_F_PROCESSED; - } -} - -/* attempt to do a handover */ -static int attempt_handover(struct gsm_meas_rep *mr) -{ - struct gsm_network *net = mr->lchan->ts->trx->bts->network; - struct neigh_meas_proc *best_cell = NULL; - unsigned int best_better_db = 0; - int i, rc; - - /* find the best cell in this report that is at least RXLEV_HYST - * better than the current serving cell */ - - for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) { - struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i]; - int avg, better; - - /* skip empty slots */ - if (nmp->arfcn == 0) - continue; - - /* caculate average rxlev for this cell over the window */ - avg = neigh_meas_avg(nmp, net->handover.win_rxlev_avg_neigh); - - /* check if hysteresis is fulfilled */ - if (avg < mr->dl.full.rx_lev + net->handover.pwr_hysteresis) - continue; - - better = avg - mr->dl.full.rx_lev; - if (better > best_better_db) { - best_cell = nmp; - best_better_db = better; - } - } - - if (!best_cell) - return 0; - - LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ", - gsm_ts_name(mr->lchan->ts), best_cell->arfcn); - if (!net->handover.active) { - LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n"); - return 0; - } - - rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic); - switch (rc) { - case 0: - LOGPC(DHO, LOGL_INFO, "Starting handover\n"); - break; - case -ENOSPC: - LOGPC(DHO, LOGL_INFO, "No channel available\n"); - break; - case -EBUSY: - LOGPC(DHO, LOGL_INFO, "Handover already active\n"); - break; - default: - LOGPC(DHO, LOGL_ERROR, "Unknown error\n"); - } - return rc; -} - -/* process an already parsed measurement report and decide if we want to - * attempt a handover */ -static int process_meas_rep(struct gsm_meas_rep *mr) -{ - struct gsm_network *net = mr->lchan->ts->trx->bts->network; - int av_rxlev; - - /* we currently only do handover for TCH channels */ - switch (mr->lchan->type) { - case GSM_LCHAN_TCH_F: - case GSM_LCHAN_TCH_H: - break; - default: - return 0; - } - - /* parse actual neighbor cell info */ - if (mr->num_cell > 0 && mr->num_cell < 7) - process_meas_neigh(mr); - - av_rxlev = get_meas_rep_avg(mr->lchan, MEAS_REP_DL_RXLEV_FULL, - net->handover.win_rxlev_avg); - - /* Interference HO */ - if (rxlev2dbm(av_rxlev) > -85 && - meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, - 3, 4, 5)) - return attempt_handover(mr); - - /* Bad Quality */ - if (meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, - 3, 4, 5)) - return attempt_handover(mr); - - /* Low Level */ - if (rxlev2dbm(av_rxlev) <= -110) - return attempt_handover(mr); - - /* Distance */ - if (mr->ms_l1.ta > net->handover.max_distance) - return attempt_handover(mr); - - /* Power Budget AKA Better Cell */ - if ((mr->nr % net->handover.pwr_interval) == 0) - return attempt_handover(mr); - - return 0; - -} - -static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct lchan_signal_data *lchan_data; - - if (subsys != SS_LCHAN) - return 0; - - lchan_data = signal_data; - switch (signal) { - case S_LCHAN_MEAS_REP: - process_meas_rep(lchan_data->mr); - break; - } - - return 0; -} - -void on_dso_load_ho_dec(void) -{ - osmo_signal_register_handler(SS_LCHAN, ho_dec_sig_cb, NULL); -} diff --git a/openbsc/src/libbsc/handover_logic.c b/openbsc/src/libbsc/handover_logic.c deleted file mode 100644 index 9cf26af9d..000000000 --- a/openbsc/src/libbsc/handover_logic.c +++ /dev/null @@ -1,374 +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 - -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 struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - llist_for_each_entry(ho, &bsc_handovers, list) { - if (ho->new_lchan == new_lchan) - return ho; - } - - return NULL; -} - -static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan) -{ - struct bsc_handover *ho; - - llist_for_each_entry(ho, &bsc_handovers, list) { - if (ho->old_lchan == old_lchan) - return ho; - } - - return NULL; -} - -/* Hand over the specified logical channel to the specified new BTS. - * This is the main entry point for the actual handover algorithm, - * after it has decided it wants to initiate HO to a specific BTS */ -int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) -{ - struct gsm_lchan *new_lchan; - struct bsc_handover *ho; - static uint8_t ho_ref; - int rc; - - /* don't attempt multiple handovers for the same lchan at - * the same time */ - if (bsc_ho_by_old_lchan(old_lchan)) - return -EBUSY; - - DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n", - old_lchan->ts->trx->bts->nr, bts->nr); - - osmo_counter_inc(bts->network->stats.handover.attempted); - - if (!old_lchan->conn) { - LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n"); - return -ENOSPC; - } - - new_lchan = lchan_alloc(bts, old_lchan->type, 0); - if (!new_lchan) { - LOGP(DHO, LOGL_NOTICE, "No free channel\n"); - osmo_counter_inc(bts->network->stats.handover.no_channel); - return -ENOSPC; - } - - ho = talloc_zero(tall_bsc_ctx, struct bsc_handover); - if (!ho) { - LOGP(DHO, LOGL_FATAL, "Out of Memory\n"); - lchan_free(new_lchan); - return -ENOMEM; - } - ho->old_lchan = old_lchan; - ho->new_lchan = new_lchan; - ho->ho_ref = ho_ref++; - - /* copy some parameters from old lchan */ - memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr)); - new_lchan->ms_power = old_lchan->ms_power; - new_lchan->bs_power = old_lchan->bs_power; - new_lchan->rsl_cmode = old_lchan->rsl_cmode; - new_lchan->tch_mode = old_lchan->tch_mode; - - new_lchan->conn = old_lchan->conn; - new_lchan->conn->ho_lchan = new_lchan; - - /* FIXME: do we have a better idea of the timing advance? */ - rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0, - ho->ho_ref); - if (rc < 0) { - LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); - new_lchan->conn->ho_lchan = NULL; - new_lchan->conn = NULL; - talloc_free(ho); - lchan_free(new_lchan); - return rc; - } - - rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); - llist_add(&ho->list, &bsc_handovers); - /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */ - - return 0; -} - -void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan) -{ - struct bsc_handover *ho; - - ho = bsc_ho_by_new_lchan(conn->ho_lchan); - - - if (!ho && conn->ho_lchan) - LOGP(DHO, LOGL_ERROR, "BUG: We lost some state.\n"); - - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return; - } - - conn->ho_lchan->conn = NULL; - conn->ho_lchan = NULL; - - if (free_lchan) - lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); - - osmo_timer_del(&ho->T3103); - llist_del(&ho->list); - talloc_free(ho); -} - -/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */ -static void ho_T3103_cb(void *_ho) -{ - struct bsc_handover *ho = _ho; - struct gsm_network *net = ho->new_lchan->ts->trx->bts->network; - - DEBUGP(DHO, "HO T3103 expired\n"); - osmo_counter_inc(net->stats.handover.timeout); - - ho->new_lchan->conn->ho_lchan = NULL; - ho->new_lchan->conn = NULL; - lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); - llist_del(&ho->list); - talloc_free(ho); -} - -/* RSL has acknowledged activation of the new lchan */ -static int ho_chan_activ_ack(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - /* we need to check if this channel activation is related to - * a handover at all (and if, which particular handover) */ - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) - return -ENODEV; - - DEBUGP(DHO, "handover activate ack, send HO Command\n"); - - /* we can now send the 04.08 HANDOVER COMMAND to the MS - * using the old lchan */ - - gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref); - - /* start T3103. We can continue either with T3103 expiration, - * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */ - ho->T3103.cb = ho_T3103_cb; - ho->T3103.data = ho; - osmo_timer_schedule(&ho->T3103, 10, 0); - - /* create a RTP connection */ - if (is_ipaccess_bts(new_lchan->ts->trx->bts)) - rsl_ipacc_crcx(new_lchan); - - return 0; -} - -/* RSL has not acknowledged activation of the new lchan */ -static int ho_chan_activ_nack(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) { - LOGP(DHO, LOGL_INFO, "ACT NACK: unable to find HO record\n"); - return -ENODEV; - } - - new_lchan->conn->ho_lchan = NULL; - new_lchan->conn = NULL; - llist_del(&ho->list); - talloc_free(ho); - - /* FIXME: maybe we should try to allocate a new LCHAN here? */ - - return 0; -} - -/* GSM 04.08 HANDOVER COMPLETE has been received on new channel */ -static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) -{ - struct gsm_network *net; - struct bsc_handover *ho; - - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return -ENODEV; - } - - net = new_lchan->ts->trx->bts->network; - LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN " - "%u->%u\n", subscr_name(ho->old_lchan->conn->subscr), - ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr, - ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn); - - osmo_counter_inc(net->stats.handover.completed); - - osmo_timer_del(&ho->T3103); - - /* Replace the ho lchan with the primary one */ - if (ho->old_lchan != new_lchan->conn->lchan) - LOGP(DHO, LOGL_ERROR, "Primary lchan changed during handover.\n"); - - if (new_lchan != new_lchan->conn->ho_lchan) - LOGP(DHO, LOGL_ERROR, "Handover channel changed during this handover.\n"); - - new_lchan->conn->ho_lchan = NULL; - new_lchan->conn->lchan = new_lchan; - ho->old_lchan->conn = NULL; - - rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE); - lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END); - - /* do something to re-route the actual speech frames ! */ - - llist_del(&ho->list); - talloc_free(ho); - - return 0; -} - -/* GSM 04.08 HANDOVER FAIL has been received */ -static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan) -{ - struct gsm_network *net = old_lchan->ts->trx->bts->network; - struct bsc_handover *ho; - - ho = bsc_ho_by_old_lchan(old_lchan); - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return -ENODEV; - } - - osmo_counter_inc(net->stats.handover.failed); - - osmo_timer_del(&ho->T3103); - llist_del(&ho->list); - - /* release the channel and forget about it */ - ho->new_lchan->conn->ho_lchan = NULL; - ho->new_lchan->conn = NULL; - lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); - - talloc_free(ho); - - return 0; -} - -/* GSM 08.58 HANDOVER DETECT has been received */ -static int ho_rsl_detect(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return -ENODEV; - } - - /* FIXME: do we actually want to do something here ? */ - - return 0; -} - -static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct lchan_signal_data *lchan_data; - struct gsm_lchan *lchan; - - lchan_data = signal_data; - switch (subsys) { - case SS_LCHAN: - lchan = lchan_data->lchan; - switch (signal) { - case S_LCHAN_ACTIVATE_ACK: - return ho_chan_activ_ack(lchan); - case S_LCHAN_ACTIVATE_NACK: - return ho_chan_activ_nack(lchan); - case S_LCHAN_HANDOVER_DETECT: - return ho_rsl_detect(lchan); - case S_LCHAN_HANDOVER_COMPL: - return ho_gsm48_ho_compl(lchan); - case S_LCHAN_HANDOVER_FAIL: - return ho_gsm48_ho_fail(lchan); - } - break; - default: - break; - } - - return 0; -} - -struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) - return NULL; - return ho->old_lchan; -} - -static __attribute__((constructor)) void on_dso_load_ho_logic(void) -{ - osmo_signal_register_handler(SS_LCHAN, ho_logic_sig_cb, NULL); -} diff --git a/openbsc/src/libbsc/meas_proc.c b/openbsc/src/libbsc/meas_proc.c deleted file mode 100644 index 5b97e74ee..000000000 --- a/openbsc/src/libbsc/meas_proc.c +++ /dev/null @@ -1,84 +0,0 @@ -/* Measurement Processing */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received 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 808103d28..000000000 --- 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/paging.c b/openbsc/src/libbsc/paging.c deleted file mode 100644 index 286c57ba9..000000000 --- a/openbsc/src/libbsc/paging.c +++ /dev/null @@ -1,436 +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 - -void *tall_paging_ctx; - -#define PAGING_TIMER 0, 500000 - -/* - * Kill one paging request update the internal list... - */ -static void paging_remove_request(struct gsm_bts_paging_state *paging_bts, - struct gsm_paging_request *to_be_deleted) -{ - osmo_timer_del(&to_be_deleted->T3113); - llist_del(&to_be_deleted->entry); - subscr_put(to_be_deleted->subscr); - talloc_free(to_be_deleted); -} - -static void page_ms(struct gsm_paging_request *request) -{ - uint8_t mi[128]; - unsigned int mi_len; - unsigned int page_group; - struct gsm_bts *bts = request->bts; - - /* the bts is down.. we will just wait for the paging to expire */ - if (!bts->oml_link) - return; - - LOGP(DPAG, LOGL_INFO, "Going to send paging commands: imsi: '%s' tmsi: '0x%x'\n", - request->subscr->imsi, request->subscr->tmsi); - - if (request->subscr->tmsi == GSM_RESERVED_TMSI) - mi_len = gsm48_generate_mid_from_imsi(mi, request->subscr->imsi); - else - mi_len = gsm48_generate_mid_from_tmsi(mi, request->subscr->tmsi); - - page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc, - str_to_imsi(request->subscr->imsi)); - gsm0808_page(bts, page_group, mi_len, mi, request->chan_type); -} - -static void paging_schedule_if_needed(struct gsm_bts_paging_state *paging_bts) -{ - if (llist_empty(&paging_bts->pending_requests)) - return; - - if (!osmo_timer_pending(&paging_bts->work_timer)) - osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER); -} - - -static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts); -static void paging_give_credit(void *data) -{ - struct gsm_bts_paging_state *paging_bts = data; - - LOGP(DPAG, LOGL_NOTICE, "No slots available on bts nr %d\n", paging_bts->bts->nr); - paging_bts->available_slots = 20; - paging_handle_pending_requests(paging_bts); -} - -static int can_send_pag_req(struct gsm_bts *bts, int rsl_type) -{ - struct pchan_load pl; - int count; - - memset(&pl, 0, sizeof(pl)); - bts_chan_load(&pl, bts); - - switch (rsl_type) { - case RSL_CHANNEED_TCH_F: - case RSL_CHANNEED_TCH_ForH: - goto count_tch; - break; - case RSL_CHANNEED_SDCCH: - goto count_sdcch; - break; - case RSL_CHANNEED_ANY: - default: - if (bts->network->pag_any_tch) - goto count_tch; - else - goto count_sdcch; - break; - } - - return 0; - - /* could available SDCCH */ -count_sdcch: - count = 0; - count += pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].total - - pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].used; - count += pl.pchan[GSM_PCHAN_CCCH_SDCCH4].total - - pl.pchan[GSM_PCHAN_CCCH_SDCCH4].used; - return bts->paging.free_chans_need > count; - -count_tch: - count = 0; - count += pl.pchan[GSM_PCHAN_TCH_F].total - - pl.pchan[GSM_PCHAN_TCH_F].used; - if (bts->network->neci) - count += pl.pchan[GSM_PCHAN_TCH_H].total - - pl.pchan[GSM_PCHAN_TCH_H].used; - return bts->paging.free_chans_need > count; -} - -/* - * This is kicked by the periodic PAGING LOAD Indicator - * coming from abis_rsl.c - * - * We attempt to iterate once over the list of items but - * only upto available_slots. - */ -static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts) -{ - struct gsm_paging_request *request = NULL; - - /* - * Determine if the pending_requests list is empty and - * return then. - */ - if (llist_empty(&paging_bts->pending_requests)) { - /* since the list is empty, no need to reschedule the timer */ - return; - } - - /* - * In case the BTS does not provide us with load indication and we - * ran out of slots, call an autofill routine. It might be that the - * BTS did not like our paging messages and then we have counted down - * to zero and we do not get any messages. - */ - if (paging_bts->available_slots == 0) { - paging_bts->credit_timer.cb = paging_give_credit; - paging_bts->credit_timer.data = paging_bts; - osmo_timer_schedule(&paging_bts->credit_timer, 5, 0); - return; - } - - request = llist_entry(paging_bts->pending_requests.next, - struct gsm_paging_request, entry); - - /* we need to determine the number of free channels */ - if (paging_bts->free_chans_need != -1) { - if (can_send_pag_req(request->bts, request->chan_type) != 0) - goto skip_paging; - } - - /* handle the paging request now */ - page_ms(request); - paging_bts->available_slots--; - request->attempts++; - - /* take the current and add it to the back */ - llist_del(&request->entry); - llist_add_tail(&request->entry, &paging_bts->pending_requests); - -skip_paging: - osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER); -} - -static void paging_worker(void *data) -{ - struct gsm_bts_paging_state *paging_bts = data; - - paging_handle_pending_requests(paging_bts); -} - -static void paging_init_if_needed(struct gsm_bts *bts) -{ - if (bts->paging.bts) - return; - - bts->paging.bts = bts; - INIT_LLIST_HEAD(&bts->paging.pending_requests); - bts->paging.work_timer.cb = paging_worker; - bts->paging.work_timer.data = &bts->paging; - - /* Large number, until we get a proper message */ - bts->paging.available_slots = 20; -} - -static int paging_pending_request(struct gsm_bts_paging_state *bts, - struct gsm_subscriber *subscr) { - struct gsm_paging_request *req; - - llist_for_each_entry(req, &bts->pending_requests, entry) { - if (subscr == req->subscr) - return 1; - } - - return 0; -} - -static void paging_T3113_expired(void *data) -{ - struct gsm_paging_request *req = (struct gsm_paging_request *)data; - void *cbfn_param; - gsm_cbfn *cbfn; - int msg; - - LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n", - req, req->subscr->imsi); - - /* must be destroyed before calling cbfn, to prevent double free */ - osmo_counter_inc(req->bts->network->stats.paging.expired); - cbfn_param = req->cbfn_param; - cbfn = req->cbfn; - - /* did we ever manage to page the subscriber */ - msg = req->attempts > 0 ? GSM_PAGING_EXPIRED : GSM_PAGING_BUSY; - - /* destroy it now. Do not access req afterwards */ - paging_remove_request(&req->bts->paging, req); - - if (cbfn) - cbfn(GSM_HOOK_RR_PAGING, msg, NULL, NULL, - cbfn_param); -} - -static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr, - int type, gsm_cbfn *cbfn, void *data) -{ - struct gsm_bts_paging_state *bts_entry = &bts->paging; - struct gsm_paging_request *req; - - if (paging_pending_request(bts_entry, subscr)) { - LOGP(DPAG, LOGL_INFO, "Paging request already pending for %s\n", subscr->imsi); - return -EEXIST; - } - - LOGP(DPAG, LOGL_DEBUG, "Start paging of subscriber %llu on bts %d.\n", - subscr->id, bts->nr); - req = talloc_zero(tall_paging_ctx, struct gsm_paging_request); - req->subscr = subscr_get(subscr); - req->bts = bts; - req->chan_type = type; - req->cbfn = cbfn; - req->cbfn_param = data; - req->T3113.cb = paging_T3113_expired; - req->T3113.data = req; - osmo_timer_schedule(&req->T3113, bts->network->T3113, 0); - llist_add_tail(&req->entry, &bts_entry->pending_requests); - paging_schedule_if_needed(bts_entry); - - return 0; -} - -int paging_request_bts(struct gsm_bts *bts, struct gsm_subscriber *subscr, - int type, gsm_cbfn *cbfn, void *data) -{ - int rc; - - /* skip all currently inactive TRX */ - if (!trx_is_usable(bts->c0)) - return 0; - - /* maybe it is the first time we use it */ - paging_init_if_needed(bts); - - - /* Trigger paging, pass any error to the caller */ - rc = _paging_request(bts, subscr, type, cbfn, data); - if (rc < 0) - return rc; - return 1; -} - -int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr, - int type, gsm_cbfn *cbfn, void *data) -{ - struct gsm_bts *bts = NULL; - int num_pages = 0; - - osmo_counter_inc(network->stats.paging.attempted); - - /* start paging subscriber on all BTS within Location Area */ - do { - int rc; - - bts = gsm_bts_by_lac(network, subscr->lac, bts); - if (!bts) - break; - - rc = paging_request_bts(bts, subscr, type, cbfn, data); - if (rc < 0) - return rc; - num_pages += rc; - } while (1); - - if (num_pages == 0) - osmo_counter_inc(network->stats.paging.detached); - - return num_pages; -} - - -/* we consciously ignore the type of the request here */ -static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr, - struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm_bts_paging_state *bts_entry = &bts->paging; - struct gsm_paging_request *req, *req2; - - paging_init_if_needed(bts); - - llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests, - entry) { - if (req->subscr == subscr) { - if (conn && req->cbfn) { - LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d, calling cbfn.\n", bts->nr); - req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, - msg, conn, req->cbfn_param); - } else - LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d silently.\n", bts->nr); - paging_remove_request(&bts->paging, req); - break; - } - } -} - -/* Stop paging on all other bts' */ -void paging_request_stop(struct gsm_bts *_bts, struct gsm_subscriber *subscr, - struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm_bts *bts = NULL; - - if (_bts) - _paging_request_stop(_bts, subscr, conn, msg); - - do { - /* - * FIXME: Don't use the lac of the subscriber... - * as it might have magically changed the lac.. use the - * location area of the _bts as reconfiguration of the - * network is probably happening less often. - */ - bts = gsm_bts_by_lac(subscr->net, subscr->lac, bts); - if (!bts) - break; - - /* Stop paging */ - if (bts != _bts) - _paging_request_stop(bts, subscr, NULL, NULL); - } while (1); -} - -void paging_update_buffer_space(struct gsm_bts *bts, uint16_t free_slots) -{ - paging_init_if_needed(bts); - - osmo_timer_del(&bts->paging.credit_timer); - bts->paging.available_slots = free_slots; - paging_schedule_if_needed(&bts->paging); -} - -unsigned int paging_pending_requests_nr(struct gsm_bts *bts) -{ - unsigned int requests = 0; - struct gsm_paging_request *req; - - paging_init_if_needed(bts); - - llist_for_each_entry(req, &bts->paging.pending_requests, entry) - ++requests; - - return requests; -} - -/** - * Find any paging data for the given subscriber at the given BTS. - */ -void *paging_get_data(struct gsm_bts *bts, struct gsm_subscriber *subscr) -{ - struct gsm_paging_request *req; - - llist_for_each_entry(req, &bts->paging.pending_requests, entry) - if (req->subscr == subscr) - return req->cbfn_param; - - return NULL; -} diff --git a/openbsc/src/libbsc/rest_octets.c b/openbsc/src/libbsc/rest_octets.c deleted file mode 100644 index 41bf5050b..000000000 --- a/openbsc/src/libbsc/rest_octets.c +++ /dev/null @@ -1,437 +0,0 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface, - * rest octet handling according to - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include -#include -#include - -/* generate SI1 rest octets */ -int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 1; - - if (nch_pos) { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, *nch_pos, 5); - } else - bitvec_set_bit(&bv, L); - - if (is1800_net) - bitvec_set_bit(&bv, L); - else - bitvec_set_bit(&bv, H); - - bitvec_spare_padding(&bv, 6); - return bv.data_len; -} - -/* Append selection parameters to bitvec */ -static void append_selection_params(struct bitvec *bv, - const struct gsm48_si_selection_params *sp) -{ - if (sp->present) { - bitvec_set_bit(bv, H); - bitvec_set_bit(bv, sp->cbq); - bitvec_set_uint(bv, sp->cell_resel_off, 6); - bitvec_set_uint(bv, sp->temp_offs, 3); - bitvec_set_uint(bv, sp->penalty_time, 5); - } else - bitvec_set_bit(bv, L); -} - -/* Append power offset to bitvec */ -static void append_power_offset(struct bitvec *bv, - const struct gsm48_si_power_offset *po) -{ - if (po->present) { - bitvec_set_bit(bv, H); - bitvec_set_uint(bv, po->power_offset, 2); - } else - bitvec_set_bit(bv, L); -} - -/* Append GPRS indicator to bitvec */ -static void append_gprs_ind(struct bitvec *bv, - const struct gsm48_si3_gprs_ind *gi) -{ - if (gi->present) { - bitvec_set_bit(bv, H); - bitvec_set_uint(bv, gi->ra_colour, 3); - /* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */ - bitvec_set_bit(bv, gi->si13_position); - } else - bitvec_set_bit(bv, L); -} - - -/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ -int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 4; - - /* Optional Selection Parameters */ - append_selection_params(&bv, &si3->selection_params); - - /* Optional Power Offset */ - append_power_offset(&bv, &si3->power_offset); - - /* Do we have a SI2ter on the BCCH? */ - if (si3->si2ter_indicator) - bitvec_set_bit(&bv, H); - else - bitvec_set_bit(&bv, L); - - /* Early Classmark Sending Control */ - if (si3->early_cm_ctrl) - bitvec_set_bit(&bv, H); - else - bitvec_set_bit(&bv, L); - - /* Do we have a SI Type 9 on the BCCH? */ - if (si3->scheduling.present) { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, si3->scheduling.where, 3); - } else - bitvec_set_bit(&bv, L); - - /* GPRS Indicator */ - append_gprs_ind(&bv, &si3->gprs_ind); - - bitvec_spare_padding(&bv, (bv.data_len*8)-1); - return bv.data_len; -} - -static int append_lsa_params(struct bitvec *bv, - const struct gsm48_lsa_params *lsa_params) -{ - /* FIXME */ - return -1; -} - -/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ -int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 10; /* FIXME: up to ? */ - - /* SI4 Rest Octets O */ - append_selection_params(&bv, &si4->selection_params); - append_power_offset(&bv, &si4->power_offset); - append_gprs_ind(&bv, &si4->gprs_ind); - - if (0 /* FIXME */) { - /* H and SI4 Rest Octets S */ - bitvec_set_bit(&bv, H); - - /* LSA Parameters */ - if (si4->lsa_params.present) { - bitvec_set_bit(&bv, H); - append_lsa_params(&bv, &si4->lsa_params); - } else - bitvec_set_bit(&bv, L); - - /* Cell Identity */ - if (1) { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, si4->cell_id, 16); - } else - bitvec_set_bit(&bv, L); - - /* LSA ID Information */ - if (0) { - bitvec_set_bit(&bv, H); - /* FIXME */ - } else - bitvec_set_bit(&bv, L); - } else { - /* L and break indicator */ - bitvec_set_bit(&bv, L); - bitvec_set_bit(&bv, si4->break_ind ? H : L); - } - - return bv.data_len; -} - -/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a: - < GPRS Mobile Allocation IE > ::= - < HSN : bit (6) > - { 0 | 1 < RFL number list : < RFL number list struct > > } - { 0 < MA_LENGTH : bit (6) > - < MA_BITMAP: bit (val(MA_LENGTH) + 1) > - | 1 { 0 | 1 > } } ; - - < RFL number list struct > :: = - < RFL_NUMBER : bit (4) > - { 0 | 1 < RFL number list struct > } ; - < ARFCN index list struct > ::= - < ARFCN_INDEX : bit(6) > - { 0 | 1 < ARFCN index list struct > } ; - */ -static int append_gprs_mobile_alloc(struct bitvec *bv) -{ - /* Hopping Sequence Number */ - bitvec_set_uint(bv, 0, 6); - - if (0) { - /* We want to use a RFL number list */ - bitvec_set_bit(bv, 1); - /* FIXME: RFL number list */ - } else - bitvec_set_bit(bv, 0); - - if (0) { - /* We want to use a MA_BITMAP */ - bitvec_set_bit(bv, 0); - /* FIXME: MA_LENGTH, MA_BITMAP, ... */ - } else { - bitvec_set_bit(bv, 1); - if (0) { - /* We want to provide an ARFCN index list */ - bitvec_set_bit(bv, 1); - /* FIXME */ - } else - bitvec_set_bit(bv, 0); - } - return 0; -} - -static int encode_t3192(unsigned int t3192) -{ - if (t3192 == 0) - return 3; - else if (t3192 <= 80) - return 4; - else if (t3192 <= 120) - return 5; - else if (t3192 <= 160) - return 6; - else if (t3192 <= 200) - return 7; - else if (t3192 <= 500) - return 0; - else if (t3192 <= 1000) - return 1; - else if (t3192 <= 1500) - return 2; - else - return -EINVAL; -} - -static int encode_drx_timer(unsigned int drx) -{ - if (drx == 0) - return 0; - else if (drx == 1) - return 1; - else if (drx == 2) - return 2; - else if (drx <= 4) - return 3; - else if (drx <= 8) - return 4; - else if (drx <= 16) - return 5; - else if (drx <= 32) - return 6; - else if (drx <= 64) - return 7; - else - return -EINVAL; -} - -/* GPRS Cell Options as per TS 04.60 Chapter 12.24 - < GPRS Cell Options IE > ::= - < NMO : bit(2) > - < T3168 : bit(3) > - < T3192 : bit(3) > - < DRX_TIMER_MAX: bit(3) > - < ACCESS_BURST_TYPE: bit > - < CONTROL_ACK_TYPE : bit > - < BS_CV_MAX: bit(4) > - { 0 | 1 < PAN_DEC : bit(3) > - < PAN_INC : bit(3) > - < PAN_MAX : bit(3) > - { 0 | 1 < Extension Length : bit(6) > - < bit (val(Extension Length) + 1 - & { < Extension Information > ! { bit ** = } } ; - < Extension Information > ::= - { 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit > - < BEP_PERIOD : bit(4) > } - < PFC_FEATURE_MODE : bit > - < DTM_SUPPORT : bit > - - ** ; - */ -static int append_gprs_cell_opt(struct bitvec *bv, - const struct gprs_cell_options *gco) -{ - int t3192, drx_timer_max; - - t3192 = encode_t3192(gco->t3192); - if (t3192 < 0) - return t3192; - - drx_timer_max = encode_drx_timer(gco->drx_timer_max); - if (drx_timer_max < 0) - return drx_timer_max; - - bitvec_set_uint(bv, gco->nmo, 2); - bitvec_set_uint(bv, gco->t3168 / 500, 3); - bitvec_set_uint(bv, t3192, 3); - bitvec_set_uint(bv, drx_timer_max, 3); - /* ACCESS_BURST_TYPE: Hard-code 8bit */ - bitvec_set_bit(bv, 0); - /* CONTROL_ACK_TYPE: Hard-code to RLC/MAC control block */ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, gco->bs_cv_max, 4); - - if (0) { - /* hard-code no PAN_{DEC,INC,MAX} */ - bitvec_set_bit(bv, 0); - } else { - /* copied from ip.access BSC protocol trace */ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, 1, 3); /* DEC */ - bitvec_set_uint(bv, 1, 3); /* INC */ - bitvec_set_uint(bv, 15, 3); /* MAX */ - } - - if (!gco->ext_info_present) { - /* no extension information */ - bitvec_set_bit(bv, 0); - } else { - /* extension information */ - bitvec_set_bit(bv, 1); - if (!gco->ext_info.egprs_supported) { - /* 6bit length of extension */ - bitvec_set_uint(bv, (1 + 3)-1, 6); - /* EGPRS supported in the cell */ - bitvec_set_bit(bv, 0); - } else { - /* 6bit length of extension */ - bitvec_set_uint(bv, (1 + 5 + 3)-1, 6); - /* EGPRS supported in the cell */ - bitvec_set_bit(bv, 1); - /* 1bit EGPRS PACKET CHANNEL REQUEST */ - bitvec_set_bit(bv, gco->ext_info.use_egprs_p_ch_req); - /* 4bit BEP PERIOD */ - bitvec_set_uint(bv, gco->ext_info.bep_period, 4); - } - bitvec_set_bit(bv, gco->ext_info.pfc_supported); - bitvec_set_bit(bv, gco->ext_info.dtm_supported); - bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination); - } - - return 0; -} - -static void append_gprs_pwr_ctrl_pars(struct bitvec *bv, - const struct gprs_power_ctrl_pars *pcp) -{ - bitvec_set_uint(bv, pcp->alpha, 4); - bitvec_set_uint(bv, pcp->t_avg_w, 5); - bitvec_set_uint(bv, pcp->t_avg_t, 5); - bitvec_set_uint(bv, pcp->pc_meas_chan, 1); - bitvec_set_uint(bv, pcp->n_avg_i, 4); -} - -/* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */ -int rest_octets_si13(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 c901a4ae6..000000000 --- a/openbsc/src/libbsc/system_information.c +++ /dev/null @@ -1,844 +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 - -/* - * DCS1800 and PCS1900 have overlapping ARFCNs. We would need to set the - * ARFCN_PCS flag on the 1900 ARFCNs but this would increase cell_alloc - * and other arrays to make sure (ARFCN_PCS + 1024)/8 ARFCNs fit into the - * array. DCS1800 and PCS1900 can not be used at the same time so conserve - * memory and do the below. - */ -static int band_compatible(const struct gsm_bts *bts, int arfcn) -{ - enum gsm_band band = gsm_arfcn2band(arfcn); - - /* normal case */ - if (band == bts->band) - return 1; - /* deal with ARFCN_PCS not set */ - if (band == GSM_BAND_1800 && bts->band == GSM_BAND_1900) - return 1; - - return 0; -} - -static int is_dcs_net(const struct gsm_bts *bts) -{ - if (bts->band == GSM_BAND_850) - return 0; - if (bts->band == GSM_BAND_1900) - return 0; - return 1; -} - -static int use_arfcn(const struct gsm_bts *bts, const int bis, const int ter, - const int pgsm, const int arfcn) -{ - if (!bis && !ter && band_compatible(bts, arfcn)) - return 1; - if (bis && pgsm && band_compatible(bts, arfcn) && (arfcn < 1 || arfcn > 124)) - return 1; - if (ter && !band_compatible(bts, arfcn)) - return 1; - return 0; -} - -/* Frequency Lists as per TS 04.08 10.5.2.13 */ - -/* 10.5.2.13.2: Bit map 0 format */ -static int freq_list_bm0_set_arfcn(uint8_t *chan_list, unsigned int arfcn) -{ - unsigned int byte, bit; - - if (arfcn > 124 || arfcn < 1) { - LOGP(DRR, LOGL_ERROR, "Bitmap 0 only supports ARFCN 1...124\n"); - return -EINVAL; - } - - /* the bitmask is from 1..124, not from 0..123 */ - arfcn--; - - byte = arfcn / 8; - bit = arfcn % 8; - - chan_list[GSM48_CELL_CHAN_DESC_SIZE-1-byte] |= (1 << bit); - - return 0; -} - -/* 10.5.2.13.7: Variable bit map format */ -static int freq_list_bmrel_set_arfcn(uint8_t *chan_list, unsigned int arfcn) -{ - unsigned int byte, bit; - unsigned int min_arfcn; - unsigned int bitno; - - min_arfcn = (chan_list[0] & 1) << 9; - min_arfcn |= chan_list[1] << 1; - min_arfcn |= (chan_list[2] >> 7) & 1; - - /* The lower end of our bitmaks is always implicitly included */ - if (arfcn == min_arfcn) - return 0; - - if (((arfcn - min_arfcn) & 1023) > 111) { - LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn); - return -EINVAL; - } - - bitno = (arfcn - min_arfcn) & 1023; - byte = bitno / 8; - bit = bitno % 8; - - chan_list[2 + byte] |= 1 << (7 - bit); - - return 0; -} - -/* generate a variable bitmap */ -static int enc_freq_lst_var_bitmap(uint8_t *chan_list, - struct bitvec *bv, const struct gsm_bts *bts, - int bis, int ter, int min, int pgsm) -{ - int i; - - /* set it to 'Variable bitmap format' */ - chan_list[0] = 0x8e; - - chan_list[0] |= (min >> 9) & 1; - chan_list[1] = (min >> 1); - chan_list[2] = (min & 1) << 7; - - for (i = 0; i < bv->data_len*8; i++) { - /* see notes in bitvec2freq_list */ - if (bitvec_get_bit_pos(bv, i) - && ((!bis && !ter && gsm_arfcn2band(i) == bts->band) - || (bis && pgsm && gsm_arfcn2band(i) == bts->band && (i < 1 || i > 124)) - || (ter && gsm_arfcn2band(i) != bts->band))) { - int rc = freq_list_bmrel_set_arfcn(chan_list, i); - if (rc < 0) - return rc; - } - } - - return 0; -} - -/* generate a frequency list with the range 512 format */ -static int enc_freq_lst_range(uint8_t *chan_list, - struct bitvec *bv, const struct gsm_bts *bts, - int bis, int ter, int pgsm) -{ - int arfcns[RANGE_ENC_MAX_ARFCNS]; - int w[RANGE_ENC_MAX_ARFCNS]; - int f0_included = 0; - int arfcns_used = 0; - int i, rc, range, f0; - - /* - * Select ARFCNs according to the rules in bitvec2freq_list - */ - for (i = 0; i < bv->data_len * 8; ++i) { - /* More ARFCNs than the maximum */ - if (arfcns_used > ARRAY_SIZE(arfcns)) - return -1; - /* Check if we can select it? */ - if (bitvec_get_bit_pos(bv, i) && use_arfcn(bts, bis, ter, pgsm, i)) - arfcns[arfcns_used++] = i; - } - - /* - * Check if the given list of ARFCNs can be encoded. - */ - range = range_enc_determine_range(arfcns, arfcns_used, &f0); - if (range == ARFCN_RANGE_INVALID) - return -2; - - /* - * Manipulate the ARFCN list according to the rules in J4 depending - * on the selected range. - */ - arfcns_used = range_enc_filter_arfcns(range, arfcns, arfcns_used, - f0, &f0_included); - - memset(w, 0, sizeof(w)); - rc = range_enc_arfcns(range, arfcns, arfcns_used, w, 0); - if (rc != 0) - return -3; - - /* Select the range and the amount of bits needed */ - switch (range) { - case ARFCN_RANGE_128: - return range_enc_range128(chan_list, f0, w); - break; - case ARFCN_RANGE_256: - return range_enc_range256(chan_list, f0, w); - break; - case ARFCN_RANGE_512: - return range_enc_range512(chan_list, f0, w); - break; - case ARFCN_RANGE_1024: - return range_enc_range1024(chan_list, f0, f0_included, w); - break; - default: - return -4; - }; -} - -/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ -static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv, - const struct gsm_bts *bts, int bis, int ter) -{ - int i, rc, min = -1, max = -1, pgsm = 0, arfcns = 0; - - memset(chan_list, 0, 16); - - if (bts->band == GSM_BAND_900 - && bts->c0->arfcn >= 1 && bts->c0->arfcn <= 124) - pgsm = 1; - /* P-GSM-only handsets only support 'bit map 0 format' */ - if (!bis && !ter && pgsm) { - chan_list[0] = 0; - - for (i = 0; i < bv->data_len*8; i++) { - if (i >= 1 && i <= 124 - && bitvec_get_bit_pos(bv, i)) { - rc = freq_list_bm0_set_arfcn(chan_list, i); - if (rc < 0) - return rc; - } - } - return 0; - } - - for (i = 0; i < bv->data_len*8; i++) { - /* in case of SI2 or SI5 allow all neighbours in same band - * in case of SI*bis, allow neighbours in same band ouside pgsm - * in case of SI*ter, allow neighbours in different bands - */ - if (!bitvec_get_bit_pos(bv, i)) - continue; - if (!use_arfcn(bts, bis, ter, pgsm, i)) - continue; - /* count the arfcns we want to carry */ - arfcns += 1; - - /* 955..1023 < 0..885 */ - if (min < 0) - min = i; - if (i >= 955 && min < 955) - min = i; - if (i >= 955 && min >= 955 && i < min) - min = i; - if (i < 955 && min < 955 && i < min) - min = i; - if (max < 0) - max = i; - if (i < 955 && max >= 955) - max = i; - if (i >= 955 && max >= 955 && i > max) - max = i; - if (i < 955 && max < 955 && i > max) - max = i; - } - - if (max == -1) { - /* Empty set, use 'bit map 0 format' */ - chan_list[0] = 0; - return 0; - } - - /* Now find the best encoding */ - if (((max - min) & 1023) <= 111) - return enc_freq_lst_var_bitmap(chan_list, bv, bts, bis, - ter, min, pgsm); - - /* Attempt to do the range encoding */ - rc = enc_freq_lst_range(chan_list, bv, bts, bis, ter, pgsm); - if (rc == 0) - return 0; - - LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, arfcns=%d " - "can not generate ARFCN list", min, max, arfcns); - return -EINVAL; -} - -/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ -/* static*/ int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - struct bitvec *bv = &bts->si_common.cell_alloc; - - /* Zero-initialize the bit-vector */ - memset(bv->data, 0, bv->data_len); - - /* first we generate a bitvec of all TRX ARFCN's in our BTS */ - llist_for_each_entry(trx, &bts->trx_list, list) { - unsigned int i, j; - /* Always add the TRX's ARFCN */ - bitvec_set_bit_pos(bv, trx->arfcn, 1); - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - /* Add any ARFCNs present in hopping channels */ - for (j = 0; j < 1024; j++) { - if (bitvec_get_bit_pos(&ts->hopping.arfcns, j)) - bitvec_set_bit_pos(bv, j, 1); - } - } - } - - /* then we generate a GSM 04.08 frequency list from the bitvec */ - return bitvec2freq_list(chan_list, bv, bts, 0, 0); -} - -/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ -static int generate_bcch_chan_list(uint8_t *chan_list, struct gsm_bts *bts, - int si5, int bis, int ter) -{ - struct gsm_bts *cur_bts; - struct bitvec *bv; - - if (si5 && bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) - bv = &bts->si_common.si5_neigh_list; - else - bv = &bts->si_common.neigh_list; - - /* Generate list of neighbor cells if we are in automatic mode */ - if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) { - /* Zero-initialize the bit-vector */ - memset(bv->data, 0, bv->data_len); - - /* first we generate a bitvec of the BCCH ARFCN's in our BSC */ - llist_for_each_entry(cur_bts, &bts->network->bts_list, list) { - if (cur_bts == bts) - continue; - bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1); - } - } - - /* then we generate a GSM 04.08 frequency list from the bitvec */ - return bitvec2freq_list(chan_list, bv, bts, bis, ter); -} - -static int list_arfcn(uint8_t *chan_list, uint8_t mask, char *text) -{ - int n = 0, i; - struct gsm_sysinfo_freq freq[1024]; - - memset(freq, 0, sizeof(freq)); - gsm48_decode_freq_list(freq, chan_list, 16, 0xce, 1); - for (i = 0; i < 1024; i++) { - if (freq[i].mask) { - if (!n) - LOGP(DRR, LOGL_INFO, "%s", text); - LOGPC(DRR, LOGL_INFO, " %d", i); - n++; - } - } - if (n) - LOGPC(DRR, LOGL_INFO, "\n"); - - return n; -} - -static int generate_si1(uint8_t *output, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_1 *si1 = - (struct gsm48_system_information_type_1 *) output; - - memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si1->header.l2_plen = (21 << 2) | 1; - si1->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si1->header.skip_indicator = 0; - si1->header.system_information = GSM48_MT_RR_SYSINFO_1; - - rc = generate_cell_chan_list(si1->cell_channel_description, bts); - if (rc < 0) - return rc; - list_arfcn(si1->cell_channel_description, 0xce, "Serving cell:"); - - si1->rach_control = bts->si_common.rach_control; - - /* - * SI1 Rest Octets (10.5.2.32), contains NCH position and band - * indicator but that is not in the 04.08. - */ - rc = rest_octets_si1(si1->rest_octets, NULL, is_dcs_net(bts)); - - return sizeof(*si1) + rc; -} - -static int generate_si2(uint8_t *output, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_2 *si2 = - (struct gsm48_system_information_type_2 *) output; - - memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si2->header.l2_plen = (22 << 2) | 1; - si2->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si2->header.skip_indicator = 0; - si2->header.system_information = GSM48_MT_RR_SYSINFO_2; - - rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts, 0, 0, 0); - if (rc < 0) - return rc; - list_arfcn(si2->bcch_frequency_list, 0xce, - "Neighbour cells in same band:"); - - si2->ncc_permitted = bts->si_common.ncc_permitted; - si2->rach_control = bts->si_common.rach_control; - - return sizeof(*si2); -} - -static int generate_si2bis(uint8_t *output, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_2bis *si2b = - (struct gsm48_system_information_type_2bis *) output; - int n; - - memset(si2b, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si2b->header.l2_plen = (22 << 2) | 1; - si2b->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si2b->header.skip_indicator = 0; - si2b->header.system_information = GSM48_MT_RR_SYSINFO_2bis; - - rc = generate_bcch_chan_list(si2b->bcch_frequency_list, bts, 0, 1, 0); - if (rc < 0) - return rc; - n = list_arfcn(si2b->bcch_frequency_list, 0xce, - "Neighbour cells in same band, but outside P-GSM:"); - if (n) { - /* indicate in SI2 and SI2bis: there is an extension */ - struct gsm48_system_information_type_2 *si2 = - (struct gsm48_system_information_type_2 *) - bts->si_buf[SYSINFO_TYPE_2]; - si2->bcch_frequency_list[0] |= 0x20; - si2b->bcch_frequency_list[0] |= 0x20; - } else - bts->si_valid &= ~(1 << SYSINFO_TYPE_2bis); - - si2b->rach_control = bts->si_common.rach_control; - - return sizeof(*si2b); -} - -static int generate_si2ter(uint8_t *output, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_2ter *si2t = - (struct gsm48_system_information_type_2ter *) output; - int n; - - memset(si2t, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si2t->header.l2_plen = (22 << 2) | 1; - si2t->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si2t->header.skip_indicator = 0; - si2t->header.system_information = GSM48_MT_RR_SYSINFO_2ter; - - rc = generate_bcch_chan_list(si2t->ext_bcch_frequency_list, bts, 0, 0, 1); - if (rc < 0) - return rc; - n = list_arfcn(si2t->ext_bcch_frequency_list, 0x8e, - "Neighbour cells in different band:"); - if (!n) - bts->si_valid &= ~(1 << SYSINFO_TYPE_2ter); - - return sizeof(*si2t); -} - -static struct gsm48_si_ro_info si_info = { - .selection_params = { - .present = 0, - }, - .power_offset = { - .present = 0, - }, - .si2ter_indicator = 0, - .early_cm_ctrl = 1, - .scheduling = { - .present = 0, - }, - .gprs_ind = { - .si13_position = 0, - .ra_colour = 0, - .present = 1, - }, - .lsa_params = { - .present = 0, - }, - .cell_id = 0, /* FIXME: doesn't the bts have this? */ - .break_ind = 0, -}; - -static int generate_si3(uint8_t *output, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_3 *si3 = - (struct gsm48_system_information_type_3 *) output; - - memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si3->header.l2_plen = (18 << 2) | 1; - si3->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si3->header.skip_indicator = 0; - si3->header.system_information = GSM48_MT_RR_SYSINFO_3; - - si3->cell_identity = htons(bts->cell_identity); - gsm48_generate_lai(&si3->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); - si3->control_channel_desc = bts->si_common.chan_desc; - si3->cell_options = bts->si_common.cell_options; - si3->cell_sel_par = bts->si_common.cell_sel_par; - si3->rach_control = bts->si_common.rach_control; - - if ((bts->si_valid & (1 << SYSINFO_TYPE_2ter))) { - LOGP(DRR, LOGL_INFO, "SI 2ter is included.\n"); - si_info.si2ter_indicator = 1; - } else { - si_info.si2ter_indicator = 0; - } - - /* SI3 Rest Octets (10.5.2.34), containing - CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME - Power Offset, 2ter Indicator, Early Classmark Sending, - Scheduling if and WHERE, GPRS Indicator, SI13 position */ - rc = rest_octets_si3(si3->rest_octets, &si_info); - - return sizeof(*si3) + rc; -} - -static int generate_si4(uint8_t *output, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_4 *si4 = - (struct gsm48_system_information_type_4 *) output; - - /* length of all IEs present except SI4 rest octets and l2_plen */ - int l2_plen = sizeof(*si4) - 1; - - memset(si4, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si4->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si4->header.skip_indicator = 0; - si4->header.system_information = GSM48_MT_RR_SYSINFO_4; - - gsm48_generate_lai(&si4->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); - si4->cell_sel_par = bts->si_common.cell_sel_par; - si4->rach_control = bts->si_common.rach_control; - - /* Optional: CBCH Channel Description + CBCH Mobile Allocation */ - - si4->header.l2_plen = (l2_plen << 2) | 1; - - /* SI4 Rest Octets (10.5.2.35), containing - Optional Power offset, GPRS Indicator, - Cell Identity, LSA ID, Selection Parameter */ - rc = rest_octets_si4(si4->data, &si_info); - - return sizeof(*si4) + rc; -} - -static int generate_si5(uint8_t *output, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_5 *si5; - int rc, l2_plen = 18; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMO_SYSMO: - *output++ = (l2_plen << 2) | 1; - l2_plen++; - break; - default: - break; - } - - si5 = (struct gsm48_system_information_type_5 *) output; - - /* l2 pseudo length, not part of msg: 18 */ - si5->rr_protocol_discriminator = GSM48_PDISC_RR; - si5->skip_indicator = 0; - si5->system_information = GSM48_MT_RR_SYSINFO_5; - rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts, 1, 0, 0); - if (rc < 0) - return rc; - list_arfcn(si5->bcch_frequency_list, 0xce, - "Neighbour cells in same band:"); - - /* 04.08 9.1.37: L2 Pseudo Length of 18 */ - return l2_plen; -} - -static int generate_si5bis(uint8_t *output, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_5bis *si5b; - int rc, l2_plen = 18; - int n; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMO_SYSMO: - *output++ = (l2_plen << 2) | 1; - l2_plen++; - break; - default: - break; - } - - si5b = (struct gsm48_system_information_type_5bis *) output; - - /* l2 pseudo length, not part of msg: 18 */ - si5b->rr_protocol_discriminator = GSM48_PDISC_RR; - si5b->skip_indicator = 0; - si5b->system_information = GSM48_MT_RR_SYSINFO_5bis; - rc = generate_bcch_chan_list(si5b->bcch_frequency_list, bts, 1, 1, 0); - if (rc < 0) - return rc; - n = list_arfcn(si5b->bcch_frequency_list, 0xce, - "Neighbour cells in same band, but outside P-GSM:"); - if (n) { - /* indicate in SI5 and SI5bis: there is an extension */ - struct gsm48_system_information_type_5 *si5 = - (struct gsm48_system_information_type_5 *) - bts->si_buf[SYSINFO_TYPE_5]; - si5->bcch_frequency_list[0] |= 0x20; - si5b->bcch_frequency_list[0] |= 0x20; - } else - bts->si_valid &= ~(1 << SYSINFO_TYPE_5bis); - - /* 04.08 9.1.37: L2 Pseudo Length of 18 */ - return l2_plen; -} - -static int generate_si5ter(uint8_t *output, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_5ter *si5t; - int rc, l2_plen = 18; - int n; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMO_SYSMO: - *output++ = (l2_plen << 2) | 1; - l2_plen++; - break; - default: - break; - } - - si5t = (struct gsm48_system_information_type_5ter *) output; - - /* l2 pseudo length, not part of msg: 18 */ - si5t->rr_protocol_discriminator = GSM48_PDISC_RR; - si5t->skip_indicator = 0; - si5t->system_information = GSM48_MT_RR_SYSINFO_5ter; - rc = generate_bcch_chan_list(si5t->bcch_frequency_list, bts, 1, 0, 1); - if (rc < 0) - return rc; - n = list_arfcn(si5t->bcch_frequency_list, 0x8e, - "Neighbour cells in different band:"); - if (!n) - bts->si_valid &= ~(1 << SYSINFO_TYPE_5ter); - - /* 04.08 9.1.37: L2 Pseudo Length of 18 */ - return l2_plen; -} - -static int generate_si6(uint8_t *output, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_6 *si6; - int l2_plen = 11; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMO_SYSMO: - *output++ = (l2_plen << 2) | 1; - l2_plen++; - break; - default: - break; - } - - si6 = (struct gsm48_system_information_type_6 *) output; - - /* l2 pseudo length, not part of msg: 11 */ - si6->rr_protocol_discriminator = GSM48_PDISC_RR; - si6->skip_indicator = 0; - si6->system_information = GSM48_MT_RR_SYSINFO_6; - si6->cell_identity = htons(bts->cell_identity); - gsm48_generate_lai(&si6->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); - si6->cell_options = bts->si_common.cell_options; - si6->ncc_permitted = bts->si_common.ncc_permitted; - - /* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */ - - return l2_plen; -} - -static struct gsm48_si13_info si13_default = { - .cell_opts = { - .nmo = GPRS_NMO_II, - .t3168 = 2000, - .t3192 = 1500, - .drx_timer_max = 3, - .bs_cv_max = 15, - .ext_info_present = 0, - .ext_info = { - /* The values below are just guesses ! */ - .egprs_supported = 0, - .use_egprs_p_ch_req = 1, - .bep_period = 5, - .pfc_supported = 0, - .dtm_supported = 0, - .bss_paging_coordination = 0, - }, - }, - .pwr_ctrl_pars = { - .alpha = 0, /* a = 0.0 */ - .t_avg_w = 16, - .t_avg_t = 16, - .pc_meas_chan = 0, /* downling measured on CCCH */ - .n_avg_i = 8, - }, - .bcch_change_mark = 1, - .si_change_field = 0, - .pbcch_present = 0, - { - .no_pbcch = { - .rac = 0, /* needs to be patched */ - .spgc_ccch_sup = 0, - .net_ctrl_ord = 0, - .prio_acc_thr = 6, - }, - }, -}; - -static int generate_si13(uint8_t *output, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_13 *si13 = - (struct gsm48_system_information_type_13 *) output; - int ret; - - memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si13->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si13->header.skip_indicator = 0; - si13->header.system_information = GSM48_MT_RR_SYSINFO_13; - - si13_default.no_pbcch.rac = bts->gprs.rac; - si13_default.no_pbcch.net_ctrl_ord = bts->gprs.net_ctrl_ord; - - ret = rest_octets_si13(si13->rest_octets, &si13_default); - if (ret < 0) - return ret; - - /* length is coded in bit 2 an up */ - si13->header.l2_plen = 0x01; - - return sizeof (*si13) + ret; -} - -typedef int (*gen_si_fn_t)(uint8_t *output, struct gsm_bts *bts); - -static const gen_si_fn_t gen_si_fn[_MAX_SYSINFO_TYPE] = { - [SYSINFO_TYPE_1] = &generate_si1, - [SYSINFO_TYPE_2] = &generate_si2, - [SYSINFO_TYPE_2bis] = &generate_si2bis, - [SYSINFO_TYPE_2ter] = &generate_si2ter, - [SYSINFO_TYPE_3] = &generate_si3, - [SYSINFO_TYPE_4] = &generate_si4, - [SYSINFO_TYPE_5] = &generate_si5, - [SYSINFO_TYPE_5bis] = &generate_si5bis, - [SYSINFO_TYPE_5ter] = &generate_si5ter, - [SYSINFO_TYPE_6] = &generate_si6, - [SYSINFO_TYPE_13] = &generate_si13, -}; - -int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) -{ - gen_si_fn_t gen_si; - - switch (bts->gprs.mode) { - case BTS_GPRS_EGPRS: - si13_default.cell_opts.ext_info_present = 1; - si13_default.cell_opts.ext_info.egprs_supported = 1; - /* fallthrough */ - case BTS_GPRS_GPRS: - si_info.gprs_ind.present = 1; - break; - case BTS_GPRS_NONE: - si_info.gprs_ind.present = 0; - break; - } - - memcpy(&si_info.selection_params, - &bts->si_common.cell_ro_sel_par, - sizeof(struct gsm48_si_selection_params)); - - gen_si = gen_si_fn[si_type]; - if (!gen_si) - return -EINVAL; - - return gen_si(bts->si_buf[si_type], bts); -} diff --git a/openbsc/src/libcommon/Makefile.am b/openbsc/src/libcommon/Makefile.am deleted file mode 100644 index e41326f12..000000000 --- a/openbsc/src/libcommon/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) - -noinst_LIBRARIES = libcommon.a - -libcommon_a_SOURCES = bsc_version.c common_vty.c debug.c gsm_data.c gsm_data_shared.c socket.c talloc_ctx.c diff --git a/openbsc/src/libcommon/bsc_version.c b/openbsc/src/libcommon/bsc_version.c deleted file mode 100644 index 04acf0412..000000000 --- a/openbsc/src/libcommon/bsc_version.c +++ /dev/null @@ -1,30 +0,0 @@ -/* Hold the copyright and version string */ -/* (C) 2010 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include "../../bscconfig.h" - -const char *openbsc_copyright = - "Copyright (C) 2008-2012 Harald Welte, Holger Freyther\r\n" - "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" - "Dieter Spaar, Andreas Eversberg, Sylvain Munaut\r\n\r\n" - "License AGPLv3+: GNU AGPL version 3 or later \r\n" - "This is free software: you are free to change and redistribute it.\r\n" - "There is NO WARRANTY, to the extent permitted by law.\r\n"; - - diff --git a/openbsc/src/libcommon/common_vty.c b/openbsc/src/libcommon/common_vty.c deleted file mode 100644 index 7342f14c7..000000000 --- a/openbsc/src/libcommon/common_vty.c +++ /dev/null @@ -1,244 +0,0 @@ -/* OpenBSC VTY common helpers */ -/* (C) 2009-2010 by Harald Welte - * (C) 2009-2010 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -enum node_type bsc_vty_go_parent(struct vty *vty) -{ - switch (vty->node) { - case GSMNET_NODE: - vty->node = CONFIG_NODE; - vty->index = NULL; - break; - case BTS_NODE: - vty->node = GSMNET_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts *bts = vty->index; - vty->index = bts->network; - } - break; - case TRX_NODE: - vty->node = BTS_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts_trx *trx = vty->index; - vty->index = trx->bts; - } - break; - case TS_NODE: - vty->node = TRX_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts_trx_ts *ts = vty->index; - vty->index = ts->trx; - } - break; - case OML_NODE: - case OM2K_NODE: - vty->node = ENABLE_NODE; - talloc_free(vty->index); - vty->index = NULL; - break; - case NAT_NODE: - vty->node = CONFIG_NODE; - vty->index = NULL; - break; - case NAT_BSC_NODE: - vty->node = NAT_NODE; - { - struct bsc_config *bsc_config = vty->index; - vty->index = bsc_config->nat; - } - break; - case PGROUP_NODE: - vty->node = NAT_NODE; - break; - case TRUNK_NODE: - vty->node = MGCP_NODE; - break; - case SMPP_ESME_NODE: - vty->node = SMPP_NODE; - vty->index = NULL; - break; - case SMPP_NODE: - case MSC_NODE: - case MNCC_INT_NODE: - default: - vty->node = CONFIG_NODE; - } - - return vty->node; -} - -/* Down vty node level. */ -gDEFUN(ournode_exit, - ournode_exit_cmd, "exit", "Exit current mode and down to previous mode\n") -{ - switch (vty->node) { - case GSMNET_NODE: - vty->node = CONFIG_NODE; - vty->index = NULL; - break; - case BTS_NODE: - vty->node = GSMNET_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts *bts = vty->index; - vty->index = bts->network; - vty->index_sub = NULL; - } - break; - case TRX_NODE: - vty->node = BTS_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts_trx *trx = vty->index; - vty->index = trx->bts; - vty->index_sub = &trx->bts->description; - } - break; - case TS_NODE: - vty->node = TRX_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts_trx_ts *ts = vty->index; - vty->index = ts->trx; - vty->index_sub = &ts->trx->description; - } - break; - case NAT_BSC_NODE: - vty->node = NAT_NODE; - { - struct bsc_config *bsc_config = vty->index; - vty->index = bsc_config->nat; - } - break; - case PGROUP_NODE: - vty->node = NAT_NODE; - 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: - vty->node = CONFIG_NODE; - vty->index = NULL; - break; - case OML_NODE: - case OM2K_NODE: - vty->node = ENABLE_NODE; - talloc_free(vty->index); - vty->index = NULL; - break; - case MSC_NODE: - case MNCC_INT_NODE: - vty->node = CONFIG_NODE; - break; - case TRUNK_NODE: - vty->node = MGCP_NODE; - vty->index = NULL; - break; - default: - break; - } - return CMD_SUCCESS; -} - -/* End of configuration. */ -gDEFUN(ournode_end, - ournode_end_cmd, "end", "End current mode and change to enable mode.") -{ - switch (vty->node) { - case VIEW_NODE: - case ENABLE_NODE: - /* Nothing to do. */ - break; - case CONFIG_NODE: - case GSMNET_NODE: - case BTS_NODE: - case TRX_NODE: - case TS_NODE: - case MGCP_NODE: - case TRUNK_NODE: - case GBPROXY_NODE: - case SGSN_NODE: - case VTY_NODE: - case NAT_NODE: - case NAT_BSC_NODE: - case PGROUP_NODE: - case MSC_NODE: - case MNCC_INT_NODE: - case SMPP_NODE: - case SMPP_ESME_NODE: - case BSC_NODE: - vty_config_unlock(vty); - vty->node = ENABLE_NODE; - vty->index = NULL; - vty->index_sub = NULL; - break; - default: - break; - } - return CMD_SUCCESS; -} - -int bsc_vty_is_config_node(struct vty *vty, int node) -{ - switch (node) { - /* add items that are not config */ - case OML_NODE: - case OM2K_NODE: - case SUBSCR_NODE: - case CONFIG_NODE: - return 0; - - default: - return 1; - } -} - -/* a talloc string replace routine */ -void bsc_replace_string(void *ctx, char **dst, const char *newstr) -{ - if (*dst) - talloc_free(*dst); - *dst = talloc_strdup(ctx, newstr); -} diff --git a/openbsc/src/libcommon/debug.c b/openbsc/src/libcommon/debug.c deleted file mode 100644 index b3685e6d2..000000000 --- a/openbsc/src/libcommon/debug.c +++ /dev/null @@ -1,212 +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, - }, -}; - -enum log_filter { - _FLT_ALL = LOG_FILTER_ALL, /* libosmocore */ - FLT_IMSI = 1, - FLT_NSVC = 2, - FLT_BVC = 3, -}; - -static int filter_fn(const struct log_context *ctx, - struct log_target *tar) -{ - struct gsm_subscriber *subscr = ctx->ctx[BSC_CTX_SUBSCR]; - const struct gprs_nsvc *nsvc = ctx->ctx[GPRS_CTX_NSVC]; - const struct gprs_nsvc *bvc = ctx->ctx[GPRS_CTX_BVC]; - - if ((tar->filter_map & (1 << FLT_IMSI)) != 0 - && subscr && strcmp(subscr->imsi, tar->filter_data[FLT_IMSI]) == 0) - return 1; - - /* Filter on the NS Virtual Connection */ - if ((tar->filter_map & (1 << FLT_NSVC)) != 0 - && nsvc && (nsvc == tar->filter_data[FLT_NSVC])) - return 1; - - /* Filter on the NS Virtual Connection */ - if ((tar->filter_map & (1 << FLT_BVC)) != 0 - && bvc && (bvc == tar->filter_data[FLT_BVC])) - return 1; - - return 0; -} - -const struct log_info log_info = { - .filter_fn = filter_fn, - .cat = default_categories, - .num_cat = ARRAY_SIZE(default_categories), -}; - -void log_set_imsi_filter(struct log_target *target, const char *imsi) -{ - if (imsi) { - target->filter_map |= (1 << FLT_IMSI); - target->filter_data[FLT_IMSI] = talloc_strdup(target, imsi); - } else if (target->filter_data[FLT_IMSI]) { - target->filter_map &= ~(1 << FLT_IMSI); - talloc_free(target->filter_data[FLT_IMSI]); - target->filter_data[FLT_IMSI] = NULL; - } -} diff --git a/openbsc/src/libcommon/gsm_data.c b/openbsc/src/libcommon/gsm_data.c deleted file mode 100644 index 5f7e32e73..000000000 --- a/openbsc/src/libcommon/gsm_data.c +++ /dev/null @@ -1,461 +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 - -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; -} - -struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_code, - int (*mncc_recv)(struct gsm_network *, struct msgb *)) -{ - struct gsm_network *net; - - net = talloc_zero(tall_bsc_ctx, struct gsm_network); - if (!net) - return NULL; - - net->bsc_data = talloc_zero(net, struct osmo_bsc_data); - if (!net->bsc_data) { - talloc_free(net); - return NULL; - } - - /* Init back pointer */ - net->bsc_data->auto_off_timeout = -1; - net->bsc_data->network = net; - INIT_LLIST_HEAD(&net->bsc_data->mscs); - - net->country_code = country_code; - net->network_code = network_code; - net->num_bts = 0; - net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED; - net->T3101 = GSM_T3101_DEFAULT; - net->T3105 = GSM_T3105_DEFAULT; - net->T3113 = GSM_T3113_DEFAULT; - net->T3122 = GSM_T3122_DEFAULT; - /* FIXME: initialize all other timers! */ - - /* default set of handover parameters */ - net->handover.win_rxlev_avg = 10; - net->handover.win_rxqual_avg = 1; - net->handover.win_rxlev_avg_neigh = 10; - net->handover.pwr_interval = 6; - net->handover.pwr_hysteresis = 3; - net->handover.max_distance = 9999; - - INIT_LLIST_HEAD(&net->trans_list); - INIT_LLIST_HEAD(&net->upqueue); - INIT_LLIST_HEAD(&net->bts_list); - - net->stats.chreq.total = osmo_counter_alloc("net.chreq.total"); - net->stats.chreq.no_channel = osmo_counter_alloc("net.chreq.no_channel"); - net->stats.handover.attempted = osmo_counter_alloc("net.handover.attempted"); - net->stats.handover.no_channel = osmo_counter_alloc("net.handover.no_channel"); - net->stats.handover.timeout = osmo_counter_alloc("net.handover.timeout"); - net->stats.handover.completed = osmo_counter_alloc("net.handover.completed"); - net->stats.handover.failed = osmo_counter_alloc("net.handover.failed"); - net->stats.loc_upd_type.attach = osmo_counter_alloc("net.loc_upd_type.attach"); - net->stats.loc_upd_type.normal = osmo_counter_alloc("net.loc_upd_type.normal"); - net->stats.loc_upd_type.periodic = osmo_counter_alloc("net.loc_upd_type.periodic"); - net->stats.loc_upd_type.detach = osmo_counter_alloc("net.imsi_detach.count"); - net->stats.loc_upd_resp.reject = osmo_counter_alloc("net.loc_upd_resp.reject"); - net->stats.loc_upd_resp.accept = osmo_counter_alloc("net.loc_upd_resp.accept"); - net->stats.paging.attempted = osmo_counter_alloc("net.paging.attempted"); - net->stats.paging.detached = osmo_counter_alloc("net.paging.detached"); - net->stats.paging.completed = osmo_counter_alloc("net.paging.completed"); - net->stats.paging.expired = osmo_counter_alloc("net.paging.expired"); - net->stats.sms.submitted = osmo_counter_alloc("net.sms.submitted"); - net->stats.sms.no_receiver = osmo_counter_alloc("net.sms.no_receiver"); - net->stats.sms.delivered = osmo_counter_alloc("net.sms.delivered"); - net->stats.sms.rp_err_mem = osmo_counter_alloc("net.sms.rp_err_mem"); - net->stats.sms.rp_err_other = osmo_counter_alloc("net.sms.rp_err_other"); - net->stats.call.mo_setup = osmo_counter_alloc("net.call.mo_setup"); - net->stats.call.mo_connect_ack = osmo_counter_alloc("net.call.mo_connect_ack"); - net->stats.call.mt_setup = osmo_counter_alloc("net.call.mt_setup"); - net->stats.call.mt_connect = osmo_counter_alloc("net.call.mt_connect"); - net->stats.chan.rf_fail = osmo_counter_alloc("net.chan.rf_fail"); - net->stats.chan.rll_err = osmo_counter_alloc("net.chan.rll_err"); - net->stats.bts.oml_fail = osmo_counter_alloc("net.bts.oml_fail"); - net->stats.bts.rsl_fail = osmo_counter_alloc("net.bts.rsl_fail"); - - net->mncc_recv = mncc_recv; - - gsm_net_update_ctype(net); - - return net; -} - -struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num) -{ - struct gsm_bts *bts; - - if (num >= net->num_bts) - return NULL; - - llist_for_each_entry(bts, &net->bts_list, list) { - if (bts->nr == num) - return bts; - } - - return NULL; -} - -/* Get reference to a neighbor cell on a given BCCH ARFCN */ -struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, - uint16_t arfcn, uint8_t bsic) -{ - struct gsm_bts *neigh; - /* FIXME: use some better heuristics here to determine which cell - * using this ARFCN really is closest to the target cell. For - * now we simply assume that each ARFCN will only be used by one - * cell */ - - llist_for_each_entry(neigh, &bts->network->bts_list, list) { - if (neigh->c0->arfcn == arfcn && - neigh->bsic == bsic) - return neigh; - } - - return NULL; -} - -const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE+1] = { - { GSM_BTS_TYPE_UNKNOWN, "unknown" }, - { GSM_BTS_TYPE_BS11, "bs11" }, - { GSM_BTS_TYPE_NANOBTS, "nanobts" }, - { GSM_BTS_TYPE_RBS2000, "rbs2000" }, - { GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" }, - { GSM_BTS_TYPE_OSMO_SYSMO, "sysmobts" }, - { 0, NULL } -}; - -const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = { - { GSM_BTS_TYPE_UNKNOWN, "Unknown BTS Type" }, - { GSM_BTS_TYPE_BS11, "Siemens BTS (BS-11 or compatible)" }, - { GSM_BTS_TYPE_NANOBTS, "ip.access nanoBTS or compatible" }, - { GSM_BTS_TYPE_RBS2000, "Ericsson RBS2000 Series" }, - { GSM_BTS_TYPE_NOKIA_SITE, "Nokia {Metro,Ultra,In}Site" }, - { GSM_BTS_TYPE_OSMO_SYSMO, "sysmocom sysmoBTS" }, - { 0, NULL } -}; - -enum gsm_bts_type parse_btstype(const char *arg) -{ - return get_string_value(bts_type_names, arg); -} - -const char *btstype2str(enum gsm_bts_type type) -{ - return get_value_string(bts_type_names, type); -} - -struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr) -{ - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->nr == nr) - return trx; - } - return NULL; -} - -/* Search for a BTS in the given Location Area; optionally start searching - * with start_bts (for continuing to search after the first result) */ -struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, - struct gsm_bts *start_bts) -{ - int i; - struct gsm_bts *bts; - int skip = 0; - - if (start_bts) - skip = 1; - - for (i = 0; i < net->num_bts; i++) { - bts = gsm_bts_num(net, i); - - if (skip) { - if (start_bts == bts) - skip = 0; - continue; - } - - if (lac == GSM_LAC_RESERVED_ALL_BTS || bts->location_area_code == lac) - return bts; - } - return NULL; -} - -static const struct value_string auth_policy_names[] = { - { GSM_AUTH_POLICY_CLOSED, "closed" }, - { GSM_AUTH_POLICY_ACCEPT_ALL, "accept-all" }, - { GSM_AUTH_POLICY_TOKEN, "token" }, - { 0, NULL } -}; - -enum gsm_auth_policy gsm_auth_policy_parse(const char *arg) -{ - return get_string_value(auth_policy_names, arg); -} - -const char *gsm_auth_policy_name(enum gsm_auth_policy policy) -{ - return get_value_string(auth_policy_names, policy); -} - -static const struct value_string rrlp_mode_names[] = { - { RRLP_MODE_NONE, "none" }, - { RRLP_MODE_MS_BASED, "ms-based" }, - { RRLP_MODE_MS_PREF, "ms-preferred" }, - { RRLP_MODE_ASS_PREF, "ass-preferred" }, - { 0, NULL } -}; - -enum rrlp_mode rrlp_mode_parse(const char *arg) -{ - return get_string_value(rrlp_mode_names, arg); -} - -const char *rrlp_mode_name(enum rrlp_mode mode) -{ - return get_value_string(rrlp_mode_names, mode); -} - -static const struct value_string bts_gprs_mode_names[] = { - { BTS_GPRS_NONE, "none" }, - { BTS_GPRS_GPRS, "gprs" }, - { BTS_GPRS_EGPRS, "egprs" }, - { 0, NULL } -}; - -enum bts_gprs_mode bts_gprs_mode_parse(const char *arg) -{ - return get_string_value(bts_gprs_mode_names, arg); -} - -const char *bts_gprs_mode_name(enum bts_gprs_mode mode) -{ - return get_value_string(bts_gprs_mode_names, mode); -} - -struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan) -{ - struct gsm_meas_rep *meas_rep; - - meas_rep = &lchan->meas_rep[lchan->meas_rep_idx]; - memset(meas_rep, 0, sizeof(*meas_rep)); - meas_rep->lchan = lchan; - lchan->meas_rep_idx = (lchan->meas_rep_idx + 1) - % ARRAY_SIZE(lchan->meas_rep); - - return meas_rep; -} - -int gsm_btsmodel_set_feature(struct gsm_bts_model *bts, enum gsm_bts_features feat) -{ - return bitvec_set_bit_pos(&bts->features, feat, 1); -} - -int gsm_bts_has_feature(struct gsm_bts *bts, enum gsm_bts_features feat) -{ - return bitvec_get_bit_pos(&bts->model->features, feat); -} - -int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) -{ - struct gsm_bts_model *model; - - model = bts_model_find(type); - if (!model) - return -EINVAL; - - bts->type = type; - bts->model = model; - - if (model->start && !model->started) { - int ret = model->start(bts->network); - if (ret < 0) - return ret; - - model->started = true; - } - - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMO_SYSMO: - /* Set the default OML Stream ID to 0xff */ - bts->oml_tei = 0xff; - bts->c0->nominal_power = 23; - break; - case GSM_BTS_TYPE_RBS2000: - INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups); - INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups); - break; - case GSM_BTS_TYPE_BS11: - case GSM_BTS_TYPE_UNKNOWN: - case GSM_BTS_TYPE_NOKIA_SITE: - case _NUM_GSM_BTS_TYPE: - break; - } - - return 0; -} - -struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type, - uint8_t tsc, uint8_t bsic) -{ - struct gsm_bts_model *model = bts_model_find(type); - struct gsm_bts *bts; - - if (!model && type != GSM_BTS_TYPE_UNKNOWN) - return NULL; - - bts = gsm_bts_alloc(net); - if (!bts) - return NULL; - - bts->network = net; - bts->nr = net->num_bts++; - bts->type = type; - bts->model = model; - bts->tsc = tsc; - bts->bsic = bsic; - - bts->neigh_list_manual_mode = 0; - bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */ - bts->si_common.cell_sel_par.rxlev_acc_min = 0; - bts->si_common.neigh_list.data = bts->si_common.data.neigh_list; - bts->si_common.neigh_list.data_len = - sizeof(bts->si_common.data.neigh_list); - bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list; - bts->si_common.si5_neigh_list.data_len = - sizeof(bts->si_common.data.si5_neigh_list); - bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc; - bts->si_common.cell_alloc.data_len = - sizeof(bts->si_common.data.cell_alloc); - bts->si_common.rach_control.re = 1; /* no re-establishment */ - bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */ - bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */ - bts->si_common.rach_control.t2 = 4; /* no emergency calls */ - bts->si_common.chan_desc.att = 1; /* attachment required */ - bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */ - bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */ - bts->si_common.chan_desc.t3212 = 5; /* Use 30 min periodic update interval as sane default */ - - 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; -} - diff --git a/openbsc/src/libcommon/gsm_data_shared.c b/openbsc/src/libcommon/gsm_data_shared.c deleted file mode 100644 index 1b0814c49..000000000 --- a/openbsc/src/libcommon/gsm_data_shared.c +++ /dev/null @@ -1,521 +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 gsm_pchant_names[10] = { - { GSM_PCHAN_NONE, "NONE" }, - { GSM_PCHAN_CCCH, "CCCH" }, - { GSM_PCHAN_CCCH_SDCCH4,"CCCH+SDCCH4" }, - { GSM_PCHAN_TCH_F, "TCH/F" }, - { GSM_PCHAN_TCH_H, "TCH/H" }, - { GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH8" }, - { GSM_PCHAN_PDCH, "PDCH" }, - { GSM_PCHAN_TCH_F_PDCH, "TCH/F_PDCH" }, - { GSM_PCHAN_UNKNOWN, "UNKNOWN" }, - { 0, NULL } -}; - -const struct value_string gsm_pchant_descs[10] = { - { 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" }, - { 0, NULL } -}; - -const char *gsm_pchan_name(enum gsm_phys_chan_config c) -{ - return get_value_string(gsm_pchant_names, c); -} - -enum gsm_phys_chan_config gsm_pchan_parse(const char *name) -{ - return get_string_value(gsm_pchant_names, name); -} - -const struct value_string gsm_lchant_names[6] = { - { GSM_LCHAN_NONE, "NONE" }, - { GSM_LCHAN_SDCCH, "SDCCH" }, - { GSM_LCHAN_TCH_F, "TCH/F" }, - { GSM_LCHAN_TCH_H, "TCH/H" }, - { GSM_LCHAN_UNKNOWN, "UNKNOWN" }, - { 0, NULL } -}; - -const char *gsm_lchant_name(enum gsm_chan_t c) -{ - return get_value_string(gsm_lchant_names, c); -} - -static const struct value_string lchan_s_names[] = { - { LCHAN_S_NONE, "NONE" }, - { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" }, - { LCHAN_S_ACTIVE, "ACTIVE" }, - { LCHAN_S_INACTIVE, "INACTIVE" }, - { LCHAN_S_REL_REQ, "RELEASE REQUESTED" }, - { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" }, - { LCHAN_S_BROKEN, "BROKEN UNUSABLE" }, - { 0, NULL } -}; - -const char *gsm_lchans_name(enum gsm_lchan_state s) -{ - return get_value_string(lchan_s_names, s); -} - -static const struct value_string chreq_names[] = { - { GSM_CHREQ_REASON_EMERG, "EMERGENCY" }, - { GSM_CHREQ_REASON_PAG, "PAGING" }, - { GSM_CHREQ_REASON_CALL, "CALL" }, - { GSM_CHREQ_REASON_LOCATION_UPD,"LOCATION_UPDATE" }, - { GSM_CHREQ_REASON_OTHER, "OTHER" }, - { 0, NULL } -}; - -const char *gsm_chreq_name(enum gsm_chreq_reason_t c) -{ - return get_value_string(chreq_names, c); -} - -struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx); - int k; - - if (!trx) - return NULL; - - trx->bts = bts; - trx->nr = bts->num_trx++; - trx->mo.nm_state.administrative = NM_STATE_UNLOCKED; - - gsm_mo_init(&trx->mo, bts, NM_OC_RADIO_CARRIER, - bts->nr, trx->nr, 0xff); - gsm_mo_init(&trx->bb_transc.mo, bts, NM_OC_BASEB_TRANSC, - bts->nr, trx->nr, 0xff); - - for (k = 0; k < TRX_NR_TS; k++) { - struct gsm_bts_trx_ts *ts = &trx->ts[k]; - int l; - - ts->trx = trx; - ts->nr = k; - ts->pchan = GSM_PCHAN_NONE; - ts->tsc = -1; - - gsm_mo_init(&ts->mo, bts, NM_OC_CHANNEL, - bts->nr, trx->nr, ts->nr); - - ts->hopping.arfcns.data_len = sizeof(ts->hopping.arfcns_data); - ts->hopping.arfcns.data = ts->hopping.arfcns_data; - ts->hopping.ma.data_len = sizeof(ts->hopping.ma_data); - ts->hopping.ma.data = ts->hopping.ma_data; - - for (l = 0; l < TS_MAX_LCHAN; l++) { - struct gsm_lchan *lchan; - lchan = &ts->lchan[l]; - - lchan->ts = ts; - lchan->nr = l; - lchan->type = GSM_LCHAN_NONE; - -#ifndef ROLE_BSC - INIT_LLIST_HEAD(&lchan->sapi_cmds); -#endif - } - } - - if (trx->nr != 0) - trx->nominal_power = bts->c0->nominal_power; - - llist_add_tail(&trx->list, &bts->trx_list); - - return trx; -} - - -static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 }; -static const uint8_t bts_cell_timer_default[] = - { 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 }; -static const struct gprs_rlc_cfg rlc_cfg_default = { - .parameter = { - [RLC_T3142] = 20, - [RLC_T3169] = 5, - [RLC_T3191] = 5, - [RLC_T3193] = 160, /* 10ms */ - [RLC_T3195] = 5, - [RLC_N3101] = 10, - [RLC_N3103] = 4, - [RLC_N3105] = 8, - [CV_COUNTDOWN] = 15, - [T_DL_TBF_EXT] = 250 * 10, /* ms */ - [T_UL_TBF_EXT] = 250 * 10, /* ms */ - }, - .paging = { - .repeat_time = 5 * 50, /* ms */ - .repeat_count = 3, - }, - .cs_mask = 0x1fff, - .initial_cs = 2, - .initial_mcs = 6, -}; - -struct gsm_bts *gsm_bts_alloc(void *ctx) -{ - struct gsm_bts *bts = talloc_zero(ctx, struct gsm_bts); - int i; - - if (!bts) - return NULL; - - bts->num_trx = 0; - INIT_LLIST_HEAD(&bts->trx_list); - bts->ms_max_power = 15; /* dBm */ - - gsm_mo_init(&bts->mo, bts, NM_OC_BTS, - bts->nr, 0xff, 0xff); - gsm_mo_init(&bts->site_mgr.mo, bts, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - - for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { - bts->gprs.nsvc[i].bts = bts; - bts->gprs.nsvc[i].id = i; - gsm_mo_init(&bts->gprs.nsvc[i].mo, bts, NM_OC_GPRS_NSVC, - bts->nr, i, 0xff); - } - memcpy(&bts->gprs.nse.timer, bts_nse_timer_default, - sizeof(bts->gprs.nse.timer)); - gsm_mo_init(&bts->gprs.nse.mo, bts, NM_OC_GPRS_NSE, - bts->nr, 0xff, 0xff); - memcpy(&bts->gprs.cell.timer, bts_cell_timer_default, - sizeof(bts->gprs.cell.timer)); - gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL, - bts->nr, 0xff, 0xff); - memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default, - sizeof(bts->gprs.cell.rlc_cfg)); - - /* create our primary TRX */ - bts->c0 = gsm_bts_trx_alloc(bts); - if (!bts->c0) { - talloc_free(bts); - return NULL; - } - bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4; - - bts->rach_b_thresh = -1; - bts->rach_ldavg_slots = -1; - bts->paging.free_chans_need = -1; - - return bts; -} - -/* reset the state of all MO in the BTS */ -void gsm_bts_mo_reset(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - unsigned int i; - - gsm_abis_mo_reset(&bts->mo); - gsm_abis_mo_reset(&bts->site_mgr.mo); - for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) - gsm_abis_mo_reset(&bts->gprs.nsvc[i].mo); - gsm_abis_mo_reset(&bts->gprs.nse.mo); - gsm_abis_mo_reset(&bts->gprs.cell.mo); - - llist_for_each_entry(trx, &bts->trx_list, list) { - gsm_abis_mo_reset(&trx->mo); - gsm_abis_mo_reset(&trx->bb_transc.mo); - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - gsm_abis_mo_reset(&ts->mo); - } - } -} - -struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num) -{ - struct gsm_bts_trx *trx; - - if (num >= bts->num_trx) - return NULL; - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->nr == num) - return trx; - } - - return NULL; -} - -static char ts2str[255]; - -char *gsm_trx_name(const struct gsm_bts_trx *trx) -{ - snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)", - trx->bts->nr, trx->nr); - - return ts2str; -} - - -char *gsm_ts_name(const struct gsm_bts_trx_ts *ts) -{ - snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)", - ts->trx->bts->nr, ts->trx->nr, ts->nr); - - return ts2str; -} - -char *gsm_lchan_name(const struct gsm_lchan *lchan) -{ - struct gsm_bts_trx_ts *ts = lchan->ts; - - snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,ss=%d)", - ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr); - - return ts2str; -} - -/* obtain the MO structure for a given object instance */ -struct gsm_abis_mo * -gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class, - const struct abis_om_obj_inst *obj_inst) -{ - struct gsm_bts_trx *trx; - struct gsm_abis_mo *mo = NULL; - - switch (obj_class) { - case NM_OC_BTS: - mo = &bts->mo; - break; - case NM_OC_RADIO_CARRIER: - if (obj_inst->trx_nr >= bts->num_trx) { - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - mo = &trx->mo; - break; - case NM_OC_BASEB_TRANSC: - if (obj_inst->trx_nr >= bts->num_trx) { - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - mo = &trx->bb_transc.mo; - break; - case NM_OC_CHANNEL: - if (obj_inst->trx_nr >= bts->num_trx) { - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - if (obj_inst->ts_nr >= TRX_NR_TS) - return NULL; - mo = &trx->ts[obj_inst->ts_nr].mo; - break; - case NM_OC_SITE_MANAGER: - mo = &bts->site_mgr.mo; - break; - case NM_OC_BS11: - switch (obj_inst->bts_nr) { - case BS11_OBJ_CCLK: - mo = &bts->bs11.cclk.mo; - break; - case BS11_OBJ_BBSIG: - if (obj_inst->ts_nr > bts->num_trx) - return NULL; - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - mo = &trx->bs11.bbsig.mo; - break; - case BS11_OBJ_PA: - if (obj_inst->ts_nr > bts->num_trx) - return NULL; - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - mo = &trx->bs11.pa.mo; - break; - default: - return NULL; - } - break; - case NM_OC_BS11_RACK: - mo = &bts->bs11.rack.mo; - break; - case NM_OC_BS11_ENVABTSE: - if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse)) - return NULL; - mo = &bts->bs11.envabtse[obj_inst->trx_nr].mo; - break; - case NM_OC_GPRS_NSE: - mo = &bts->gprs.nse.mo; - break; - case NM_OC_GPRS_CELL: - mo = &bts->gprs.cell.mo; - break; - case NM_OC_GPRS_NSVC: - if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) - return NULL; - mo = &bts->gprs.nsvc[obj_inst->trx_nr].mo; - break; - } - return mo; -} - -/* obtain the gsm_nm_state data structure for a given object instance */ -struct gsm_nm_state * -gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class, - const struct abis_om_obj_inst *obj_inst) -{ - struct gsm_abis_mo *mo; - - mo = gsm_objclass2mo(bts, obj_class, obj_inst); - if (!mo) - return NULL; - - return &mo->nm_state; -} - -/* obtain the in-memory data structure of a given object instance */ -void * -gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class, - const struct abis_om_obj_inst *obj_inst) -{ - struct gsm_bts_trx *trx; - void *obj = NULL; - - switch (obj_class) { - case NM_OC_BTS: - obj = bts; - break; - case NM_OC_RADIO_CARRIER: - if (obj_inst->trx_nr >= bts->num_trx) { - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - obj = trx; - break; - case NM_OC_BASEB_TRANSC: - if (obj_inst->trx_nr >= bts->num_trx) { - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - obj = &trx->bb_transc; - break; - case NM_OC_CHANNEL: - if (obj_inst->trx_nr >= bts->num_trx) { - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - if (obj_inst->ts_nr >= TRX_NR_TS) - return NULL; - obj = &trx->ts[obj_inst->ts_nr]; - break; - case NM_OC_SITE_MANAGER: - obj = &bts->site_mgr; - break; - case NM_OC_GPRS_NSE: - obj = &bts->gprs.nse; - break; - case NM_OC_GPRS_CELL: - obj = &bts->gprs.cell; - break; - case NM_OC_GPRS_NSVC: - if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) - return NULL; - obj = &bts->gprs.nsvc[obj_inst->trx_nr]; - break; - } - return obj; -} - -/* See Table 10.5.25 of GSM04.08 */ -uint8_t gsm_ts2chan_nr(const struct gsm_bts_trx_ts *ts, uint8_t lchan_nr) -{ - uint8_t cbits, chan_nr; - - switch (ts->pchan) { - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_PDCH: - case GSM_PCHAN_TCH_F_PDCH: - cbits = 0x01; - break; - case GSM_PCHAN_TCH_H: - cbits = 0x02; - cbits += lchan_nr; - break; - case GSM_PCHAN_CCCH_SDCCH4: - cbits = 0x04; - cbits += lchan_nr; - break; - case GSM_PCHAN_SDCCH8_SACCH8C: - cbits = 0x08; - cbits += lchan_nr; - break; - default: - case GSM_PCHAN_CCCH: - cbits = 0x10; - break; - } - - chan_nr = (cbits << 3) | (ts->nr & 0x7); - - return chan_nr; -} - -uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan) -{ - return gsm_ts2chan_nr(lchan->ts, lchan->nr); -} diff --git a/openbsc/src/libcommon/socket.c b/openbsc/src/libcommon/socket.c deleted file mode 100644 index 2a64767f8..000000000 --- a/openbsc/src/libcommon/socket.c +++ /dev/null @@ -1,111 +0,0 @@ -/* OpenBSC sokcet code, taken from Abis input driver for ip.access */ - -/* (C) 2009 by Harald Welte - * (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 ae6a15636..000000000 --- a/openbsc/src/libcommon/talloc_ctx.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include - -extern void *tall_msgb_ctx; -extern void *tall_fle_ctx; -extern void *tall_locop_ctx; -extern void *tall_authciphop_ctx; -extern void *tall_gsms_ctx; -extern void *tall_subscr_ctx; -extern void *tall_sub_req_ctx; -extern void *tall_call_ctx; -extern void *tall_paging_ctx; -extern void *tall_sigh_ctx; -extern void *tall_tqe_ctx; -extern void *tall_trans_ctx; -extern void *tall_map_ctx; -extern void *tall_upq_ctx; -extern void *tall_ctr_ctx; - -void talloc_ctx_init(void) -{ - tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); - tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0, - "bs11_file_list_entry"); - tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 0, "loc_updating_oper"); - tall_authciphop_ctx = talloc_named_const(tall_bsc_ctx, 0, "auth_ciph_oper"); - tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 0, "sms"); - tall_subscr_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscriber"); - tall_sub_req_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscr_request"); - tall_call_ctx = talloc_named_const(tall_bsc_ctx, 0, "gsm_call"); - tall_paging_ctx = talloc_named_const(tall_bsc_ctx, 0, "paging_request"); - tall_sigh_ctx = talloc_named_const(tall_bsc_ctx, 0, "signal_handler"); - tall_tqe_ctx = talloc_named_const(tall_bsc_ctx, 0, "subch_txq_entry"); - tall_trans_ctx = talloc_named_const(tall_bsc_ctx, 0, "transaction"); - tall_map_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_map_entry"); - tall_upq_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_upq_entry"); - tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); -} diff --git a/openbsc/src/libctrl/Makefile.am b/openbsc/src/libctrl/Makefile.am deleted file mode 100644 index 4f039c826..000000000 --- a/openbsc/src/libctrl/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) $(COVERAGE_LDFLAGS) - -noinst_LIBRARIES = libctrl.a - -libctrl_a_SOURCES = control_if.c control_cmd.c diff --git a/openbsc/src/libctrl/control_cmd.c b/openbsc/src/libctrl/control_cmd.c deleted file mode 100644 index 3c4efc06d..000000000 --- a/openbsc/src/libctrl/control_cmd.c +++ /dev/null @@ -1,493 +0,0 @@ -/* SNMP-like status interface - * - * (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 -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -extern vector ctrl_node_vec; - -static struct ctrl_cmd_map ccm[] = { - {"GET", CTRL_TYPE_GET}, - {"SET", CTRL_TYPE_SET}, - {"GET_REPLY", CTRL_TYPE_GET_REPLY}, - {"SET_REPLY", CTRL_TYPE_SET_REPLY}, - {"TRAP", CTRL_TYPE_TRAP}, - {"ERROR", CTRL_TYPE_ERROR}, - {NULL} -}; - -int ctrl_cmd_str2type(char *s) -{ - int i; - for (i=0; ccm[i].cmd != NULL; i++) { - if (strcasecmp(s, ccm[i].cmd) == 0) - return ccm[i].type; - } - return CTRL_TYPE_UNKNOWN; -} - -char *ctrl_cmd_type2str(int type) -{ - int i; - for (i=0; ccm[i].cmd != NULL; i++) { - if (ccm[i].type == type) - return ccm[i].cmd; - } - return NULL; -} - -/* Functions from libosmocom */ -extern vector cmd_make_descvec(const char *string, const char *descstr); - -/* Get the ctrl_cmd_element that matches this command */ -static struct ctrl_cmd_element *ctrl_cmd_get_element_match(vector vline, vector node) -{ - int index, j; - const char *desc; - struct ctrl_cmd_element *cmd_el; - struct ctrl_cmd_struct *cmd_desc; - char *str; - - for (index = 0; index < vector_active(node); index++) { - if ((cmd_el = vector_slot(node, index))) { - cmd_desc = &cmd_el->strcmd; - if (cmd_desc->nr_commands > vector_active(vline)) - continue; - for (j =0; j < vector_active(vline); j++) { - str = vector_slot(vline, j); - desc = cmd_desc->command[j]; - if (desc[0] == '*') - return cmd_el; /* Partial match */ - if (strcmp(desc, str) != 0) - break; - } - /* We went through all the elements and all matched */ - if (j == cmd_desc->nr_commands) - return cmd_el; - } - } - - return NULL; -} - -int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data) -{ - int ret = CTRL_CMD_ERROR; - struct ctrl_cmd_element *cmd_el; - - if ((command->type != CTRL_TYPE_GET) && (command->type != CTRL_TYPE_SET)) { - command->reply = "Trying to execute something not GET or SET"; - goto out; - } - if ((command->type == CTRL_TYPE_SET) && (!command->value)) { - command->reply = "SET without a value"; - goto out; - } - - if (!vline) - goto out; - - cmd_el = ctrl_cmd_get_element_match(vline, node); - - if (!cmd_el) { - command->reply = "Command not found"; - goto out; - } - - if (command->type == CTRL_TYPE_SET) { - if (!cmd_el->set) { - command->reply = "SET not implemented"; - goto out; - } - if (cmd_el->verify) { - if ((ret = cmd_el->verify(command, command->value, data))) { - ret = CTRL_CMD_ERROR; - /* If verify() set an appropriate error message, don't change it. */ - if (!command->reply) - command->reply = "Value failed verification."; - goto out; - } - } else if (cmd_el->param) { - LOGP(DCTRL, LOGL_NOTICE, "Parameter verification unimplemented, continuing without\n"); - } - ret = cmd_el->set(command, data); - goto out; - } else if (command->type == CTRL_TYPE_GET) { - if (!cmd_el->get) { - command->reply = "GET not implemented"; - goto out; - } - ret = cmd_el->get(command, data); - goto out; - } -out: - if (ret == CTRL_CMD_REPLY) { - if (command->type == CTRL_TYPE_SET) { - command->type = CTRL_TYPE_SET_REPLY; - } else if (command->type == CTRL_TYPE_GET) { - command->type = CTRL_TYPE_GET_REPLY; - } - } else if (ret == CTRL_CMD_ERROR) { - command->type = CTRL_TYPE_ERROR; - } - return ret; -} - -static void add_word(struct ctrl_cmd_struct *cmd, - const char *start, const char *end) -{ - if (!cmd->command) { - cmd->command = talloc_zero_array(tall_vty_vec_ctx, - char*, 1); - cmd->nr_commands = 0; - } else { - cmd->command = talloc_realloc(tall_vty_vec_ctx, - cmd->command, char*, - cmd->nr_commands + 1); - } - - cmd->command[cmd->nr_commands++] = talloc_strndup(cmd->command, - start, end - start); -} - -static void create_cmd_struct(struct ctrl_cmd_struct *cmd, const char *name) -{ - const char *cur, *word; - - for (cur = name, word = NULL; cur[0] != '\0'; ++cur) { - /* warn about optionals */ - if (cur[0] == '(' || cur[0] == ')' || cur[0] == '|') { - LOGP(DCTRL, LOGL_ERROR, - "Optionals are not supported in '%s'\n", name); - goto failure; - } - - if (isspace(cur[0])) { - if (word) { - add_word(cmd, word, cur); - word = NULL; - } - continue; - } - - if (!word) - word = cur; - } - - if (word) - add_word(cmd, word, cur); - - return; -failure: - cmd->nr_commands = 0; - talloc_free(cmd->command); -} - -int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd) -{ - vector cmds_vec; - - cmds_vec = vector_lookup_ensure(ctrl_node_vec, node); - - if (!cmds_vec) { - cmds_vec = vector_init(5); - if (!cmds_vec) { - LOGP(DCTRL, LOGL_ERROR, "vector_init failed.\n"); - return -ENOMEM; - } - vector_set_index(ctrl_node_vec, node, cmds_vec); - } - - vector_set(cmds_vec, cmd); - - create_cmd_struct(&cmd->strcmd, cmd->name); - return 0; -} - -struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type type) -{ - struct ctrl_cmd *cmd; - - cmd = talloc_zero(ctx, struct ctrl_cmd); - if (!cmd) - return NULL; - - cmd->type = type; - return cmd; -} - -struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd) -{ - struct ctrl_cmd *cmd2; - - cmd2 = talloc_zero(ctx, struct ctrl_cmd); - if (!cmd2) - return NULL; - - cmd2->type = cmd->type; - if (cmd->id) { - cmd2->id = talloc_strdup(cmd2, cmd->id); - if (!cmd2->id) - goto err; - } - if (cmd->variable) { - cmd2->variable = talloc_strdup(cmd2, cmd->variable); - if (!cmd2->variable) - goto err; - } - if (cmd->value) { - cmd2->value = talloc_strdup(cmd2, cmd->value); - if (!cmd2->value) - goto err; - } - if (cmd->reply) { - cmd2->reply = talloc_strdup(cmd2, cmd->reply); - if (!cmd2->reply) - goto err; - } - - return cmd2; -err: - talloc_free(cmd2); - return NULL; -} - -struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg) -{ - char *str, *tmp, *saveptr = NULL; - char *var, *val; - struct ctrl_cmd *cmd; - - cmd = talloc_zero(ctx, struct ctrl_cmd); - if (!cmd) { - LOGP(DCTRL, LOGL_ERROR, "Failed to allocate.\n"); - return NULL; - } - - /* Make sure input is NULL terminated */ - msgb_put_u8(msg, 0); - str = (char *) msg->l2h; - - tmp = strtok_r(str, " ", &saveptr); - if (!tmp) { - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "Request malformed"; - goto err; - } - - cmd->type = ctrl_cmd_str2type(tmp); - if (cmd->type == CTRL_TYPE_UNKNOWN) { - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "Request type unknown"; - goto err; - } - - tmp = strtok_r(NULL, " ", &saveptr); - - if (!tmp) { - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "Missing ID"; - goto err; - } - cmd->id = talloc_strdup(cmd, tmp); - if (!cmd->id) - goto oom; - - switch (cmd->type) { - case CTRL_TYPE_GET: - var = strtok_r(NULL, " ", &saveptr); - if (!var) { - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "GET incomplete"; - LOGP(DCTRL, LOGL_NOTICE, "GET Command incomplete\n"); - goto err; - } - cmd->variable = talloc_strdup(cmd, var); - LOGP(DCTRL, LOGL_DEBUG, "Command: GET %s\n", cmd->variable); - break; - case CTRL_TYPE_SET: - var = strtok_r(NULL, " ", &saveptr); - val = strtok_r(NULL, " ", &saveptr); - if (!var || !val) { - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "SET incomplete"; - LOGP(DCTRL, LOGL_NOTICE, "SET Command incomplete\n"); - goto err; - } - cmd->variable = talloc_strdup(cmd, var); - cmd->value = talloc_strdup(cmd, val); - if (!cmd->variable || !cmd->value) - goto oom; - LOGP(DCTRL, LOGL_DEBUG, "Command: SET %s = %s\n", cmd->variable, cmd->value); - break; - case CTRL_TYPE_GET_REPLY: - case CTRL_TYPE_SET_REPLY: - case CTRL_TYPE_TRAP: - var = strtok_r(NULL, " ", &saveptr); - val = strtok_r(NULL, " ", &saveptr); - if (!var || !val) { - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "Trap/Reply incomplete"; - LOGP(DCTRL, LOGL_NOTICE, "Trap/Reply incomplete\n"); - goto err; - } - cmd->variable = talloc_strdup(cmd, var); - cmd->reply = talloc_strdup(cmd, val); - if (!cmd->variable || !cmd->reply) - goto oom; - LOGP(DCTRL, LOGL_DEBUG, "Command: TRAP/REPLY %s: %s\n", cmd->variable, cmd->reply); - break; - case CTRL_TYPE_ERROR: - var = strtok_r(NULL, "\0", &saveptr); - if (!var) { - cmd->reply = ""; - goto err; - } - cmd->reply = talloc_strdup(cmd, var); - if (!cmd->reply) - goto oom; - LOGP(DCTRL, LOGL_DEBUG, "Command: ERROR %s\n", cmd->reply); - break; - case CTRL_TYPE_UNKNOWN: - default: - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "Unknown type"; - goto err; - } - - return cmd; -oom: - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "OOM"; -err: - talloc_free(cmd); - return NULL; -} - -struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd) -{ - struct msgb *msg; - char *type, *tmp; - - if (!cmd->id) - return NULL; - - msg = msgb_alloc_headroom(4096, 128, "ctrl command make"); - if (!msg) - return NULL; - - type = ctrl_cmd_type2str(cmd->type); - - switch (cmd->type) { - case CTRL_TYPE_GET: - if (!cmd->variable) - goto err; - - tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable); - if (!tmp) { - LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); - goto err; - } - - msg->l2h = msgb_put(msg, strlen(tmp)); - memcpy(msg->l2h, tmp, strlen(tmp)); - talloc_free(tmp); - break; - case CTRL_TYPE_SET: - if (!cmd->variable || !cmd->value) - goto err; - - tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable, - cmd->value); - if (!tmp) { - LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); - goto err; - } - - msg->l2h = msgb_put(msg, strlen(tmp)); - memcpy(msg->l2h, tmp, strlen(tmp)); - talloc_free(tmp); - break; - case CTRL_TYPE_GET_REPLY: - case CTRL_TYPE_SET_REPLY: - case CTRL_TYPE_TRAP: - if (!cmd->variable || !cmd->reply) - goto err; - - tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable, - cmd->reply); - if (!tmp) { - LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); - goto err; - } - - msg->l2h = msgb_put(msg, strlen(tmp)); - memcpy(msg->l2h, tmp, strlen(tmp)); - talloc_free(tmp); - break; - case CTRL_TYPE_ERROR: - if (!cmd->reply) - goto err; - - tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, - cmd->reply); - if (!tmp) { - LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); - goto err; - } - - msg->l2h = msgb_put(msg, strlen(tmp)); - memcpy(msg->l2h, tmp, strlen(tmp)); - talloc_free(tmp); - break; - default: - LOGP(DCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type); - goto err; - break; - } - - return msg; - -err: - msgb_free(msg); - return NULL; -} diff --git a/openbsc/src/libctrl/control_if.c b/openbsc/src/libctrl/control_if.c deleted file mode 100644 index b31f34fc6..000000000 --- a/openbsc/src/libctrl/control_if.c +++ /dev/null @@ -1,707 +0,0 @@ -/* SNMP-like status interface - * - * (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 -#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 - -vector ctrl_node_vec; - -/* Send command to all */ -int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd) -{ - struct ctrl_connection *ccon; - int ret = 0; - - llist_for_each_entry(ccon, &ctrl->ccon_list, list_entry) { - if (ccon == cmd->ccon) - continue; - if (ctrl_cmd_send(&ccon->write_queue, cmd)) - ret++; - } - return ret; -} - -int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd) -{ - int ret; - struct msgb *msg; - - msg = ctrl_cmd_make(cmd); - if (!msg) { - LOGP(DCTRL, LOGL_ERROR, "Could not generate msg\n"); - return -1; - } - - ipaccess_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL); - ipaccess_prepend_header(msg, IPAC_PROTO_OSMO); - - ret = osmo_wqueue_enqueue(queue, msg); - if (ret != 0) { - LOGP(DCTRL, LOGL_ERROR, "Failed to enqueue the command.\n"); - msgb_free(msg); - } - return ret; -} - -struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd) -{ - struct ctrl_cmd *trap; - - trap = ctrl_cmd_cpy(tall_bsc_ctx, cmd); - if (!trap) - return NULL; - - trap->ccon = cmd->ccon; - trap->type = CTRL_TYPE_TRAP; - return trap; -} - -static int get_num(vector vline, int i, long *num) -{ - char *token, *tmp; - - if (i >= vector_active(vline)) - return 0; - token = vector_slot(vline, i); - - errno = 0; - if (token[0] == '\0') - return 0; - - *num = strtol(token, &tmp, 10); - if (tmp[0] != '\0' || errno != 0) - return 0; - - return 1; -} - -int ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data) -{ - char *token, *request; - long num; - int i, j, ret, node; - - struct gsm_network *net = data; - struct gsm_bts *bts = NULL; - struct gsm_bts_trx *trx = NULL; - struct gsm_bts_trx_ts *ts = NULL; - vector vline, cmdvec, cmds_vec; - - ret = CTRL_CMD_ERROR; - cmd->reply = "Someone forgot to fill in the reply."; - node = CTRL_NODE_ROOT; - cmd->node = net; - - request = talloc_strdup(tall_bsc_ctx, cmd->variable); - if (!request) - goto err; - - for (i=0;ireply = "cmd_make_strvec failed."; - goto err; - } - - for (i=0;inode = bts; - node = CTRL_NODE_BTS; - } else if (!strcmp(token, "trx")) { - if (!bts) - goto err_missing; - i++; - if (!get_num(vline, i, &num)) - goto err_index; - - trx = gsm_bts_trx_num(bts, num); - if (!trx) - goto err_missing; - cmd->node = trx; - node = CTRL_NODE_TRX; - } else if (!strcmp(token, "ts")) { - if (!trx) - goto err_missing; - i++; - if (!get_num(vline, i, &num)) - goto err_index; - - if ((num >= 0) && (num < TRX_NR_TS)) - ts = &trx->ts[num]; - if (!ts) - goto err_missing; - cmd->node = ts; - node = CTRL_NODE_TS; - } else { - /* If we're here the rest must be the command */ - cmdvec = vector_init(vector_active(vline)-i); - for (j=i; jreply = "Command not found."; - vector_free(cmdvec); - break; - } - - ret = ctrl_cmd_exec(cmdvec, cmd, cmds_vec, data); - - vector_free(cmdvec); - break; - } - - if (i+1 == vector_active(vline)) - cmd->reply = "Command not present."; - } - - cmd_free_strvec(vline); - -err: - if (ret == CTRL_CMD_ERROR) - cmd->type = CTRL_TYPE_ERROR; - return ret; - -err_missing: - cmd_free_strvec(vline); - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "Error while resolving object"; - return ret; -err_index: - cmd_free_strvec(vline); - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "Error while parsing the index."; - return ret; -} - -static void control_close_conn(struct ctrl_connection *ccon) -{ - osmo_wqueue_clear(&ccon->write_queue); - close(ccon->write_queue.bfd.fd); - osmo_fd_unregister(&ccon->write_queue.bfd); - llist_del(&ccon->list_entry); - if (ccon->closed_cb) - ccon->closed_cb(ccon); - talloc_free(ccon); -} - -static int handle_control_read(struct osmo_fd * bfd) -{ - int ret = -1; - struct osmo_wqueue *queue; - struct ctrl_connection *ccon; - struct ipaccess_head *iph; - struct ipaccess_head_ext *iph_ext; - struct msgb *msg = NULL; - struct ctrl_cmd *cmd; - struct ctrl_handle *ctrl = bfd->data; - - queue = container_of(bfd, struct osmo_wqueue, bfd); - ccon = container_of(queue, struct ctrl_connection, write_queue); - - ret = ipa_msg_recv(bfd->fd, &msg); - if (ret <= 0) { - if (ret == 0) - LOGP(DCTRL, LOGL_INFO, "The control connection was closed\n"); - else - LOGP(DCTRL, LOGL_ERROR, "Failed to parse ip access message: %d\n", ret); - - goto err; - } - - if (msg->len < sizeof(*iph) + sizeof(*iph_ext)) { - LOGP(DCTRL, LOGL_ERROR, "The message is too short.\n"); - goto err; - } - - iph = (struct ipaccess_head *) msg->data; - if (iph->proto != IPAC_PROTO_OSMO) { - LOGP(DCTRL, LOGL_ERROR, "Protocol mismatch. We got 0x%x\n", iph->proto); - goto err; - } - - iph_ext = (struct ipaccess_head_ext *) iph->data; - if (iph_ext->proto != IPAC_PROTO_EXT_CTRL) { - LOGP(DCTRL, LOGL_ERROR, "Extended protocol mismatch. We got 0x%x\n", iph_ext->proto); - goto err; - } - - msg->l2h = iph_ext->data; - - cmd = ctrl_cmd_parse(ccon, msg); - - if (cmd) { - cmd->ccon = ccon; - if (ctrl_cmd_handle(cmd, ctrl->gsmnet) != CTRL_CMD_HANDLED) { - ctrl_cmd_send(queue, cmd); - talloc_free(cmd); - } - } else { - cmd = talloc_zero(ccon, struct ctrl_cmd); - if (!cmd) - goto err; - LOGP(DCTRL, LOGL_ERROR, "Command parser error.\n"); - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "Command parser error."; - ctrl_cmd_send(queue, cmd); - talloc_free(cmd); - } - - msgb_free(msg); - return 0; - -err: - control_close_conn(ccon); - msgb_free(msg); - return ret; -} - -static int control_write_cb(struct osmo_fd *bfd, struct msgb *msg) -{ - int rc; - - rc = write(bfd->fd, msg->data, msg->len); - if (rc != msg->len) - LOGP(DCTRL, LOGL_ERROR, "Failed to write message to the control connection.\n"); - - return rc; -} - -static struct ctrl_connection *ctrl_connection_alloc(void *ctx) -{ - struct ctrl_connection *ccon = talloc_zero(ctx, struct ctrl_connection); - if (!ccon) - return NULL; - - osmo_wqueue_init(&ccon->write_queue, 100); - /* Error handling here? */ - - INIT_LLIST_HEAD(&ccon->cmds); - return ccon; -} - -static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what) -{ - int ret, fd, on; - struct ctrl_handle *ctrl; - struct ctrl_connection *ccon; - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - - - if (!(what & BSC_FD_READ)) - return 0; - - fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); - if (fd < 0) { - perror("accept"); - return fd; - } - LOGP(DCTRL, LOGL_INFO, "accept()ed new control connection from %s\n", - inet_ntoa(sa.sin_addr)); - - on = 1; - ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); - if (ret != 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); - close(fd); - return ret; - } - ccon = ctrl_connection_alloc(listen_bfd->data); - if (!ccon) { - LOGP(DCTRL, LOGL_ERROR, "Failed to allocate.\n"); - close(fd); - return -1; - } - - ctrl = listen_bfd->data; - ccon->write_queue.bfd.data = ctrl; - ccon->write_queue.bfd.fd = fd; - ccon->write_queue.bfd.when = BSC_FD_READ; - ccon->write_queue.read_cb = handle_control_read; - ccon->write_queue.write_cb = control_write_cb; - - ret = osmo_fd_register(&ccon->write_queue.bfd); - if (ret < 0) { - LOGP(DCTRL, LOGL_ERROR, "Could not register FD.\n"); - close(ccon->write_queue.bfd.fd); - talloc_free(ccon); - } - - llist_add(&ccon->list_entry, &ctrl->ccon_list); - - return ret; -} - -static uint64_t get_rate_ctr_value(const struct rate_ctr *ctr, int intv) -{ - if (intv >= RATE_CTR_INTV_NUM) - return 0; - - /* Absolute value */ - if (intv == -1) { - return ctr->current; - } else { - return ctr->intv[intv].rate; - } -} - -static char *get_all_rate_ctr_in_group(const struct rate_ctr_group *ctrg, int intv) -{ - int i; - char *counters = talloc_strdup(tall_bsc_ctx, ""); - if (!counters) - return NULL; - - for (i=0;idesc->num_ctr;i++) { - counters = talloc_asprintf_append(counters, "\n%s.%u.%s %"PRIu64, - ctrg->desc->group_name_prefix, ctrg->idx, - ctrg->desc->ctr_desc[i].name, - get_rate_ctr_value(&ctrg->ctr[i], intv)); - if (!counters) - return NULL; - } - return counters; -} - -static int get_rate_ctr_group(const char *ctr_group, int intv, struct ctrl_cmd *cmd) -{ - int i; - char *counters; - struct rate_ctr_group *ctrg; - - cmd->reply = talloc_asprintf(cmd, "All counters in group %s", ctr_group); - if (!cmd->reply) - goto oom; - - for (i=0;;i++) { - ctrg = rate_ctr_get_group_by_name_idx(ctr_group, i); - if (!ctrg) - break; - - counters = get_all_rate_ctr_in_group(ctrg, intv); - if (!counters) - goto oom; - - cmd->reply = talloc_asprintf_append(cmd->reply, "%s", counters); - talloc_free(counters); - if (!cmd->reply) - goto oom; - } - - /* We found no counter group by that name */ - if (i == 0) { - cmd->reply = talloc_asprintf(cmd, "No counter group with name %s.", ctr_group); - return CTRL_CMD_ERROR; - } - - return CTRL_CMD_REPLY; -oom: - cmd->reply = "OOM."; - return CTRL_CMD_ERROR; -} - -static int get_rate_ctr_group_idx(const struct rate_ctr_group *ctrg, int intv, struct ctrl_cmd *cmd) -{ - char *counters; - - counters = get_all_rate_ctr_in_group(ctrg, intv); - if (!counters) - goto oom; - - cmd->reply = talloc_asprintf(cmd, "All counters in %s.%u%s", - ctrg->desc->group_name_prefix, ctrg->idx, counters); - talloc_free(counters); - if (!cmd->reply) - goto oom; - - return CTRL_CMD_REPLY; -oom: - cmd->reply = "OOM."; - return CTRL_CMD_ERROR; -} - -/* rate_ctr */ -CTRL_CMD_DEFINE(rate_ctr, "rate_ctr *"); -static int get_rate_ctr(struct ctrl_cmd *cmd, void *data) -{ - int intv; - unsigned int idx; - char *ctr_group, *ctr_idx, *ctr_name, *tmp, *dup, *saveptr, *interval; - struct rate_ctr_group *ctrg; - const struct rate_ctr *ctr; - - dup = talloc_strdup(cmd, cmd->variable); - if (!dup) - goto oom; - - /* Skip over possible prefixes (net.) */ - tmp = strstr(dup, "rate_ctr"); - if (!tmp) { - talloc_free(dup); - cmd->reply = "rate_ctr not a token in rate_ctr command!"; - goto err; - } - - strtok_r(tmp, ".", &saveptr); - interval = strtok_r(NULL, ".", &saveptr); - if (!interval) { - talloc_free(dup); - cmd->reply = "Missing interval."; - goto err; - } - - if (!strcmp(interval, "abs")) { - intv = -1; - } else if (!strcmp(interval, "per_sec")) { - intv = RATE_CTR_INTV_SEC; - } else if (!strcmp(interval, "per_min")) { - intv = RATE_CTR_INTV_MIN; - } else if (!strcmp(interval, "per_hour")) { - intv = RATE_CTR_INTV_HOUR; - } else if (!strcmp(interval, "per_day")) { - intv = RATE_CTR_INTV_DAY; - } else { - talloc_free(dup); - cmd->reply = "Wrong interval."; - goto err; - } - - ctr_group = strtok_r(NULL, ".", &saveptr); - tmp = strtok_r(NULL, ".", &saveptr); - if (!ctr_group || !tmp) { - talloc_free(dup); - cmd->reply = "Counter group must be of form a.b"; - goto err; - } - ctr_group[strlen(ctr_group)] = '.'; - - ctr_idx = strtok_r(NULL, ".", &saveptr); - if (!ctr_idx) { - talloc_free(dup); - return get_rate_ctr_group(ctr_group, intv, cmd); - } - idx = atoi(ctr_idx); - - ctrg = rate_ctr_get_group_by_name_idx(ctr_group, idx); - if (!ctrg) { - talloc_free(dup); - cmd->reply = "Counter group not found."; - goto err; - } - - ctr_name = strtok_r(NULL, "\0", &saveptr); - if (!ctr_name) { - talloc_free(dup); - return get_rate_ctr_group_idx(ctrg, intv, cmd); - } - - ctr = rate_ctr_get_by_name(ctrg, ctr_name); - if (!ctr) { - cmd->reply = "Counter name not found."; - talloc_free(dup); - goto err; - } - - talloc_free(dup); - - cmd->reply = talloc_asprintf(cmd, "%"PRIu64, get_rate_ctr_value(ctr, intv)); - if (!cmd->reply) - goto oom; - - return CTRL_CMD_REPLY; -oom: - cmd->reply = "OOM"; -err: - return CTRL_CMD_ERROR; -} - -static int set_rate_ctr(struct ctrl_cmd *cmd, void *data) -{ - cmd->reply = "Can't set rate counter."; - - return CTRL_CMD_ERROR; -} - -static int verify_rate_ctr(struct ctrl_cmd *cmd, const char *value, void *data) -{ - return 0; -} - -/* counter */ -CTRL_CMD_DEFINE(counter, "counter *"); -static int get_counter(struct ctrl_cmd *cmd, void *data) -{ - char *ctr_name, *tmp, *dup, *saveptr; - struct osmo_counter *counter; - - cmd->reply = "OOM"; - dup = talloc_strdup(cmd, cmd->variable); - if (!dup) - goto err; - - - tmp = strstr(dup, "counter"); - if (!tmp) { - talloc_free(dup); - goto err; - } - - strtok_r(tmp, ".", &saveptr); - ctr_name = strtok_r(NULL, "\0", &saveptr); - - if (!ctr_name) - goto err; - - counter = osmo_counter_get_by_name(ctr_name); - if (!counter) { - cmd->reply = "Counter name not found."; - talloc_free(dup); - goto err; - } - - talloc_free(dup); - - cmd->reply = talloc_asprintf(cmd, "%lu", counter->value); - if (!cmd->reply) { - cmd->reply = "OOM"; - goto err; - } - - return CTRL_CMD_REPLY; -err: - return CTRL_CMD_ERROR; -} - -static int set_counter(struct ctrl_cmd *cmd, void *data) -{ - - cmd->reply = "Can't set counter."; - - return CTRL_CMD_ERROR; -} - -static int verify_counter(struct ctrl_cmd *cmd, const char *value, void *data) -{ - return 0; -} - -struct ctrl_handle *controlif_setup(struct gsm_network *gsmnet, uint16_t port) -{ - int ret; - struct ctrl_handle *ctrl; - - ctrl = talloc_zero(tall_bsc_ctx, struct ctrl_handle); - if (!ctrl) - return NULL; - - INIT_LLIST_HEAD(&ctrl->ccon_list); - - ctrl->gsmnet = gsmnet; - - ctrl_node_vec = vector_init(5); - if (!ctrl_node_vec) - goto err; - - /* Listen for control connections */ - ret = make_sock(&ctrl->listen_fd, IPPROTO_TCP, INADDR_LOOPBACK, port, - 0, listen_fd_cb, ctrl); - if (ret < 0) - goto err_vec; - - ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rate_ctr); - if (ret) - goto err_vec; - ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_counter); - if (ret) - goto err_vec; - - return ctrl; -err_vec: - vector_free(ctrl_node_vec); - ctrl_node_vec = NULL; -err: - talloc_free(ctrl); - return NULL; -} diff --git a/openbsc/src/libgb/Makefile.am b/openbsc/src/libgb/Makefile.am deleted file mode 100644 index 159243111..000000000 --- a/openbsc/src/libgb/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) - -noinst_LIBRARIES = libgb.a - -libgb_a_SOURCES = gprs_ns.c gprs_ns_frgre.c gprs_ns_vty.c \ - gprs_bssgp.c gprs_bssgp_util.c gprs_bssgp_vty.c \ - gprs_bssgp_bss.c common_vty.c diff --git a/openbsc/src/libmgcp/Makefile.am b/openbsc/src/libmgcp/Makefile.am deleted file mode 100644 index 72f625dd4..000000000 --- a/openbsc/src/libmgcp/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) - -noinst_LIBRARIES = libmgcp.a - -libmgcp_a_SOURCES = mgcp_protocol.c mgcp_network.c mgcp_vty.c diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c deleted file mode 100644 index e9b58b24e..000000000 --- a/openbsc/src/libmgcp/mgcp_network.c +++ /dev/null @@ -1,706 +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 - -#warning "Make use of the rtp proxy code" - -/* attempt to determine byte order */ -#include -#include -#include - -#ifndef __BYTE_ORDER -# ifdef __APPLE__ -# define __BYTE_ORDER __DARWIN_BYTE_ORDER -# define __LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN -# define __BIG_ENDIAN __DARWIN_BIG_ENDIAN -# else -# error "__BYTE_ORDER should be defined by someone" -# endif -#endif - -/* according to rtp_proxy.c RFC 3550 */ -struct rtp_hdr { -#if __BYTE_ORDER == __LITTLE_ENDIAN - uint8_t csrc_count:4, - extension:1, - padding:1, - version:2; - uint8_t payload_type:7, - marker:1; -#elif __BYTE_ORDER == __BIG_ENDIAN - uint8_t version:2, - padding:1, - extension:1, - csrc_count:4; - uint8_t marker:1, - payload_type:7; -#endif - uint16_t sequence; - uint32_t timestamp; - uint32_t ssrc; -} __attribute__((packed)); - -#define RTP_SEQ_MOD (1 << 16) -#define RTP_MAX_DROPOUT 3000 -#define RTP_MAX_MISORDER 100 - - -enum { - DEST_NETWORK = 0, - DEST_BTS = 1, -}; - -enum { - PROTO_RTP, - PROTO_RTCP, -}; - -#define DUMMY_LOAD 0x23 - - -/** - * This does not need to be a precision timestamp and - * is allowed to wrap quite fast. The returned value is - * milli seconds now. - */ -uint32_t get_current_ts(void) -{ - struct timespec tp; - uint64_t ret; - - memset(&tp, 0, sizeof(tp)); - if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) - LOGP(DMGCP, LOGL_NOTICE, - "Getting the clock failed.\n"); - - /* convert it to useconds */ - ret = tp.tv_sec; - ret *= 1000; - ret += tp.tv_nsec / 1000 / 1000; - - return ret; -} - -static int udp_send(int fd, struct in_addr *addr, int port, char *buf, int len) -{ - struct sockaddr_in out; - out.sin_family = AF_INET; - out.sin_port = port; - memcpy(&out.sin_addr, addr, sizeof(*addr)); - - return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out)); -} - -int mgcp_send_dummy(struct mgcp_endpoint *endp) -{ - static char buf[] = { DUMMY_LOAD }; - - return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr, - endp->net_end.rtp_port, buf, 1); -} - -/** - * 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 packages to appear as if there - * is only one source. - * There is also no probation period for new sources. Every package - * we receive will be seen as a switch in streams. - */ -static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, - int payload, struct sockaddr_in *addr, char *data, int len) -{ - uint32_t arrival_time; - int32_t transit, d; - uint16_t seq, udelta; - uint32_t timestamp; - struct rtp_hdr *rtp_hdr; - - if (len < sizeof(*rtp_hdr)) - return; - - rtp_hdr = (struct rtp_hdr *) data; - seq = ntohs(rtp_hdr->sequence); - timestamp = ntohl(rtp_hdr->timestamp); - arrival_time = get_current_ts(); - - if (!state->initialized) { - state->base_seq = seq; - state->max_seq = seq - 1; - state->ssrc = state->orig_ssrc = rtp_hdr->ssrc; - state->initialized = 1; - state->last_timestamp = timestamp; - state->jitter = 0; - state->transit = arrival_time - timestamp; - } else if (state->ssrc != rtp_hdr->ssrc) { - state->ssrc = rtp_hdr->ssrc; - state->seq_offset = (state->max_seq + 1) - seq; - state->timestamp_offset = state->last_timestamp - timestamp; - state->patch = endp->allow_patch; - LOGP(DMGCP, LOGL_NOTICE, - "The SSRC changed on 0x%x SSRC: %u offset: %d from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), state->ssrc, state->seq_offset, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); - } - - /* apply the offset and store it back to the packet */ - if (state->patch) { - seq += state->seq_offset; - rtp_hdr->sequence = htons(seq); - rtp_hdr->ssrc = state->orig_ssrc; - - timestamp += state->timestamp_offset; - rtp_hdr->timestamp = htonl(timestamp); - } - - /* - * The below takes the shape of the validation from Appendix A. Check - * if there is something weird with the sequence number, otherwise check - * for a wrap around in the sequence number. - */ - udelta = seq - state->max_seq; - if (udelta < RTP_MAX_DROPOUT) { - if (seq < state->max_seq) - state->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. The local timestamp has a usec resolution. - */ - transit = arrival_time - timestamp; - d = transit - state->transit; - state->transit = transit; - if (d < 0) - d = -d; - state->jitter += d - ((state->jitter + 8) >> 4); - - - state->max_seq = seq; - state->last_timestamp = timestamp; - - if (payload < 0) - return; - - rtp_hdr->payload_type = payload; -} - -/* - * The below code is for dispatching. We have a dedicated port for - * the data coming from the net and one to discover the BTS. - */ -static int forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf, int len) -{ - if (!tap->enabled) - return 0; - - return sendto(fd, buf, len, 0, - (struct sockaddr *)&tap->forward, sizeof(tap->forward)); -} - -static int send_transcoder(struct mgcp_rtp_end *end, struct mgcp_config *cfg, - int is_rtp, const char *buf, int len) -{ - int rc; - int port; - struct sockaddr_in addr; - - port = is_rtp ? end->rtp_port : end->rtcp_port; - - addr.sin_family = AF_INET; - addr.sin_addr = cfg->transcoder_in; - addr.sin_port = port; - - rc = sendto(is_rtp ? - end->rtp.fd : - end->rtcp.fd, buf, len, 0, - (struct sockaddr *) &addr, sizeof(addr)); - - if (rc != len) - LOGP(DMGCP, LOGL_ERROR, - "Failed to send data to the transcoder: %s\n", - strerror(errno)); - - return rc; -} - -static int send_to(struct mgcp_endpoint *endp, int dest, int is_rtp, - struct sockaddr_in *addr, char *buf, int rc) -{ - struct mgcp_trunk_config *tcfg = endp->tcfg; - /* For loop toggle the destination and then dispatch. */ - if (tcfg->audio_loop) - dest = !dest; - - /* Loop based on the conn_mode, maybe undoing the above */ - if (endp->conn_mode == MGCP_CONN_LOOPBACK) - dest = !dest; - - if (dest == DEST_NETWORK) { - if (is_rtp) { - patch_and_count(endp, &endp->bts_state, - endp->net_end.payload_type, - addr, buf, rc); - forward_data(endp->net_end.rtp.fd, - &endp->taps[MGCP_TAP_NET_OUT], buf, rc); - return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr, - endp->net_end.rtp_port, buf, rc); - } else if (!tcfg->omit_rtcp) { - return udp_send(endp->net_end.rtcp.fd, &endp->net_end.addr, - endp->net_end.rtcp_port, buf, rc); - } - } else { - if (is_rtp) { - patch_and_count(endp, &endp->net_state, - endp->bts_end.payload_type, - addr, buf, rc); - forward_data(endp->bts_end.rtp.fd, - &endp->taps[MGCP_TAP_BTS_OUT], buf, rc); - return udp_send(endp->bts_end.rtp.fd, &endp->bts_end.addr, - endp->bts_end.rtp_port, buf, rc); - } else if (!tcfg->omit_rtcp) { - return udp_send(endp->bts_end.rtcp.fd, &endp->bts_end.addr, - endp->bts_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[4096]; - 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; - } - - if (endp->net_end.rtp_port != addr.sin_port && - endp->net_end.rtcp_port != addr.sin_port) { - LOGP(DMGCP, LOGL_ERROR, - "Data from wrong source port %d on 0x%x\n", - ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); - return -1; - } - - /* throw away the dummy message */ - if (rc == 1 && buf[0] == DUMMY_LOAD) { - LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from network on 0x%x\n", - ENDPOINT_NUMBER(endp)); - return 0; - } - - proto = fd == &endp->net_end.rtp ? PROTO_RTP : PROTO_RTCP; - endp->net_end.packets += 1; - 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 send_to(endp, DEST_BTS, proto == PROTO_RTP, &addr, - buf, rc); - case MGCP_RTP_TRANSCODED: - return send_transcoder(&endp->trans_net, endp->cfg, - proto == PROTO_RTP, buf, rc); - } - - 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 == PROTO_RTP && endp->bts_end.rtp_port == 0) { - if (!cfg->bts_ip || - memcmp(&addr->sin_addr, - &cfg->bts_in, sizeof(cfg->bts_in)) == 0 || - memcmp(&addr->sin_addr, - &endp->bts_end.addr, sizeof(endp->bts_end.addr)) == 0) { - - endp->bts_end.rtp_port = addr->sin_port; - endp->bts_end.addr = addr->sin_addr; - - LOGP(DMGCP, LOGL_NOTICE, - "Found BTS for endpoint: 0x%x on port: %d/%d of %s\n", - ENDPOINT_NUMBER(endp), ntohs(endp->bts_end.rtp_port), - ntohs(endp->bts_end.rtcp_port), inet_ntoa(addr->sin_addr)); - } - } else if (proto == PROTO_RTCP && endp->bts_end.rtcp_port == 0) { - if (memcmp(&endp->bts_end.addr, &addr->sin_addr, - sizeof(endp->bts_end.addr)) == 0) { - endp->bts_end.rtcp_port = addr->sin_port; - } - } -} - -static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) -{ - char buf[4096]; - struct sockaddr_in addr; - struct mgcp_endpoint *endp; - int rc, proto; - - endp = (struct mgcp_endpoint *) fd->data; - - rc = receive_from(endp, fd->fd, &addr, buf, sizeof(buf)); - if (rc <= 0) - return -1; - - proto = fd == &endp->bts_end.rtp ? PROTO_RTP : PROTO_RTCP; - - /* We have no idea who called us, maybe it is the BTS. */ - /* it was the BTS... */ - discover_bts(endp, proto, &addr); - - if (memcmp(&endp->bts_end.addr, &addr.sin_addr, sizeof(addr.sin_addr)) != 0) { - LOGP(DMGCP, LOGL_ERROR, - "Data from wrong bts %s on 0x%x\n", - inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp)); - return -1; - } - - if (endp->bts_end.rtp_port != addr.sin_port && - endp->bts_end.rtcp_port != addr.sin_port) { - LOGP(DMGCP, LOGL_ERROR, - "Data from wrong bts source port %d on 0x%x\n", - ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); - return -1; - } - - /* throw away the dummy message */ - if (rc == 1 && buf[0] == DUMMY_LOAD) { - LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from bts on 0x%x\n", - ENDPOINT_NUMBER(endp)); - return 0; - } - - /* do this before the loop handling */ - endp->bts_end.packets += 1; - 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 send_to(endp, DEST_NETWORK, proto == PROTO_RTP, &addr, - buf, rc); - case MGCP_RTP_TRANSCODED: - return send_transcoder(&endp->trans_bts, endp->cfg, - proto == PROTO_RTP, buf, rc); - } - - 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[4096]; - 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 ? PROTO_RTP : PROTO_RTCP; - - if (memcmp(&addr.sin_addr, &cfg->transcoder_in, sizeof(addr.sin_addr)) != 0) { - LOGP(DMGCP, LOGL_ERROR, - "Data not coming from transcoder dest: %d %s on 0x%x\n", - dest, inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(_endp)); - return -1; - } - - if (end->rtp_port != addr.sin_port && - end->rtcp_port != addr.sin_port) { - LOGP(DMGCP, LOGL_ERROR, - "Data from wrong transcoder dest %d source port %d on 0x%x\n", - dest, ntohs(addr.sin_port), ENDPOINT_NUMBER(_endp)); - return -1; - } - - /* throw away the dummy message */ - if (rc == 1 && buf[0] == DUMMY_LOAD) { - LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from transcoder dest %d on 0x%x\n", - dest, ENDPOINT_NUMBER(_endp)); - return 0; - } - - end->packets += 1; - return send_to(_endp, dest, proto == PROTO_RTP, &addr, &buf[0], rc); -} - -static int rtp_data_trans_net(struct osmo_fd *fd, unsigned int what) -{ - struct mgcp_endpoint *endp; - endp = (struct mgcp_endpoint *) fd->data; - - return rtp_data_transcoder(&endp->trans_net, endp, DEST_NETWORK, fd); -} - -static int rtp_data_trans_bts(struct osmo_fd *fd, unsigned int what) -{ - struct mgcp_endpoint *endp; - endp = (struct mgcp_endpoint *) fd->data; - - return rtp_data_transcoder(&endp->trans_bts, endp, DEST_BTS, fd); -} - -static int create_bind(const char *source_addr, struct 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; -} - -static int set_ip_tos(int fd, int tos) -{ - int ret; - ret = setsockopt(fd, IPPROTO_IP, IP_TOS, - &tos, sizeof(tos)); - return ret != 0; -} - -static int bind_rtp(struct mgcp_config *cfg, struct mgcp_rtp_end *rtp_end, int endpno) -{ - if (create_bind(cfg->source_addr, &rtp_end->rtp, rtp_end->local_port) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to create RTP port: %s:%d on 0x%x\n", - cfg->source_addr, rtp_end->local_port, endpno); - goto cleanup0; - } - - if (create_bind(cfg->source_addr, &rtp_end->rtcp, rtp_end->local_port + 1) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to create RTCP port: %s:%d on 0x%x\n", - cfg->source_addr, rtp_end->local_port + 1, endpno); - goto cleanup1; - } - - set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp); - set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp); - - rtp_end->rtp.when = BSC_FD_READ; - if (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, int rtp_port) -{ - if (end->rtp.fd != -1 || end->rtcp.fd != -1) { - LOGP(DMGCP, LOGL_ERROR, "Previous %s was still bound on %d\n", - port, ENDPOINT_NUMBER(_endp)); - mgcp_free_rtp_port(end); - } - - end->local_port = rtp_port; - end->rtp.cb = cb; - end->rtp.data = _endp; - end->rtcp.data = _endp; - end->rtcp.cb = cb; - return bind_rtp(_endp->cfg, end, ENDPOINT_NUMBER(_endp)); -} - - -int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port) -{ - return int_bind("bts-port", &endp->bts_end, - rtp_data_bts, endp, rtp_port); -} - -int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port) -{ - return int_bind("net-port", &endp->net_end, - rtp_data_net, endp, rtp_port); -} - -int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port) -{ - return int_bind("trans-net", &endp->trans_net, - rtp_data_trans_net, endp, rtp_port); -} - -int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port) -{ - return int_bind("trans-bts", &endp->trans_bts, - rtp_data_trans_bts, endp, rtp_port); -} - -int mgcp_free_rtp_port(struct mgcp_rtp_end *end) -{ - if (end->rtp.fd != -1) { - close(end->rtp.fd); - end->rtp.fd = -1; - 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->cycles + state->max_seq; - *expected = *expected - state->base_seq + 1; - - if (!state->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->initialized) - return 0; - return state->jitter >> 4; -} diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c deleted file mode 100644 index 616e0a94c..000000000 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ /dev/null @@ -1,1203 +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_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_parse_data { - struct mgcp_config *cfg; - struct mgcp_endpoint *endp; - char *trans; - char *save; - int found; -}; - -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 mgcp_analyze_header(struct mgcp_parse_data *parse, char *data); - -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 struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, - const char *msg, const char *trans_id) -{ - const char *addr = endp->cfg->local_ip; - const char *fmtp_extra = endp->bts_end.fmtp_extra; - char sdp_record[4096]; - - if (!addr) - addr = endp->cfg->source_addr; - - snprintf(sdp_record, sizeof(sdp_record) - 1, - "I: %u\n\n" - "v=0\r\n" - "o=- %u 23 IN IP4 %s\r\n" - "c=IN IP4 %s\r\n" - "t=0 0\r\n" - "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d %s\r\n" - "%s%s", - endp->ci, endp->ci, addr, addr, - endp->net_end.local_port, endp->bts_end.payload_type, - endp->bts_end.payload_type, endp->tcfg->audio_name, - fmtp_extra ? fmtp_extra : "", fmtp_extra ? "\r\n" : ""); - return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record); -} - -/* - * handle incoming messages: - * - this can be a command (four letters, space, transaction id) - * - or a response (three numbers, space, transaction id) - */ -struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) -{ - struct mgcp_parse_data pdata; - int i, code, handled = 0; - struct msgb *resp = NULL; - char *data; - - if (msgb_l2len(msg) < 4) { - LOGP(DMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len); - return NULL; - } - - /* attempt to treat it as a response */ - if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) { - LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code); - return NULL; - } - - msg->l3h = &msg->l2h[4]; - - - /* - * Check for a duplicate message and respond. - * FIXME: Verify that the msg->l3h is NULL terminated. - */ - memset(&pdata, 0, sizeof(pdata)); - pdata.cfg = cfg; - data = strtok_r((char *) msg->l3h, "\r\n", &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); - } else { - gw = strtoul(mgcp, &endptr, 16); - if (gw > 0 && gw < cfg->trunk.number_endpoints && strcmp(endptr, "@mgw") == 0) - return &cfg->trunk.endpoints[gw]; - } - - LOGP(DMGCP, LOGL_ERROR, "Not able to find endpoint: '%s'\n", mgcp); - return NULL; -} - -/** - * @returns 0 when the status line was complete and transaction_id and - * endp out parameters are set. - */ -static int mgcp_analyze_header(struct mgcp_parse_data *pdata, char *data) -{ - int i = 0; - char *elem, *save = NULL; - - pdata->trans = "000000"; - - for (elem = strtok_r(data, " ", &save); elem; - elem = strtok_r(NULL, " ", &save)) { - switch (i) { - case 0: - pdata->trans = elem; - break; - case 1: - pdata->endp = find_endpoint(pdata->cfg, elem); - if (!pdata->endp) { - LOGP(DMGCP, LOGL_ERROR, - "Unable to find Endpoint `%s'\n", elem); - return -1; - } - break; - case 2: - if (strcmp("MGCP", elem)) { - LOGP(DMGCP, LOGL_ERROR, - "MGCP header parsing error\n"); - return -1; - } - break; - case 3: - if (strcmp("1.0", elem)) { - LOGP(DMGCP, LOGL_ERROR, "MGCP version `%s' " - "not supported\n", elem); - return -1; - } - break; - } - i++; - } - - if (i != 4) { - LOGP(DMGCP, LOGL_ERROR, "MGCP status line too short.\n"); - pdata->trans = "000000"; - pdata->endp = NULL; - return -1; - } - - return 0; -} - -static int verify_call_id(const struct mgcp_endpoint *endp, - const char *callid) -{ - if (strcmp(endp->callid, callid) != 0) { - LOGP(DMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n", - ENDPOINT_NUMBER(endp), endp->callid, callid); - return -1; - } - - return 0; -} - -static int verify_ci(const struct mgcp_endpoint *endp, - const char *_ci) -{ - uint32_t ci = strtoul(_ci, NULL, 10); - - if (ci != endp->ci) { - LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %u != %s\n", - ENDPOINT_NUMBER(endp), endp->ci, _ci); - return -1; - } - - return 0; -} - -static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p) -{ - if (p->found != 0) - return create_err_response(NULL, 500, "AUEP", p->trans); - else - return create_ok_response(p->endp, 200, "AUEP", p->trans); -} - -static int parse_conn_mode(const char *msg, int *conn_mode) -{ - int ret = 0; - if (strcmp(msg, "recvonly") == 0) - *conn_mode = MGCP_CONN_RECV_ONLY; - else if (strcmp(msg, "sendrecv") == 0) - *conn_mode = MGCP_CONN_RECV_SEND; - else if (strcmp(msg, "sendonly") == 0) - *conn_mode = MGCP_CONN_SEND_ONLY; - else if (strcmp(msg, "loopback") == 0) - *conn_mode = MGCP_CONN_LOOPBACK; - else { - LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg); - ret = -1; - } - - return ret; -} - -static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end, - struct mgcp_port_range *range, - int (*alloc)(struct mgcp_endpoint *endp, int port)) -{ - int i; - - if (range->mode == PORT_ALLOC_STATIC) { - end->local_alloc = PORT_ALLOC_STATIC; - return 0; - } - - /* attempt to find a port */ - for (i = 0; i < 200; ++i) { - int rc; - - if (range->last_port >= range->range_end) - range->last_port = range->range_start; - - rc = alloc(endp, range->last_port); - - range->last_port += 2; - if (rc == 0) { - end->local_alloc = PORT_ALLOC_DYNAMIC; - return 0; - } - - } - - LOGP(DMGCP, LOGL_ERROR, "Allocating a RTP/RTCP port failed 200 times 0x%x.\n", - ENDPOINT_NUMBER(endp)); - return -1; -} - -static int allocate_ports(struct mgcp_endpoint *endp) -{ - if (allocate_port(endp, &endp->net_end, &endp->cfg->net_ports, - mgcp_bind_net_rtp_port) != 0) - return -1; - - if (allocate_port(endp, &endp->bts_end, &endp->cfg->bts_ports, - mgcp_bind_bts_rtp_port) != 0) { - mgcp_rtp_end_reset(&endp->net_end); - return -1; - } - - if (endp->cfg->transcoder_ip && endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) { - if (allocate_port(endp, &endp->trans_net, - &endp->cfg->transcoder_ports, - mgcp_bind_trans_net_rtp_port) != 0) { - mgcp_rtp_end_reset(&endp->net_end); - mgcp_rtp_end_reset(&endp->bts_end); - return -1; - } - - if (allocate_port(endp, &endp->trans_bts, - &endp->cfg->transcoder_ports, - mgcp_bind_trans_bts_rtp_port) != 0) { - mgcp_rtp_end_reset(&endp->net_end); - mgcp_rtp_end_reset(&endp->bts_end); - mgcp_rtp_end_reset(&endp->trans_net); - return -1; - } - - /* remember that we have set up transcoding */ - endp->type = MGCP_RTP_TRANSCODED; - } - - return 0; -} - -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; - - 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) { - 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; - default: - LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", - *line, *line, ENDPOINT_NUMBER(endp)); - break; - } - } - - 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_free_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); - - if (local_options) - endp->local_options = talloc_strdup(tcfg->endpoints, local_options); - - if (parse_conn_mode(mode, &endp->conn_mode) != 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; - - /* 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; - - endp->allocated = 1; - endp->bts_end.payload_type = tcfg->audio_payload; - endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints, - tcfg->audio_fmtp_extra); - - /* 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_free_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); - - create_transcoder(endp); - return create_response_with_sdp(endp, "CRCX", p->trans); -error2: - mgcp_free_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; - char *line; - - 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) { - 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': - /* skip */ - break; - case 'M': - if (parse_conn_mode(line + 3, &endp->conn_mode) != 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 */ - break; - case 'a': - case 'o': - case 's': - case 't': - case 'v': - /* skip these SDP attributes */ - break; - case 'm': { - int port; - int payload; - - if (sscanf(line, "m=audio %d RTP/AVP %d", &port, &payload) == 2) { - endp->net_end.rtp_port = htons(port); - endp->net_end.rtcp_port = htons(port + 1); - endp->net_end.payload_type = payload; - } - break; - } - case 'c': { - char ipv4[16]; - - if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) { - inet_aton(ipv4, &endp->net_end.addr); - } - break; - } - default: - LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", - 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_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; - } - } - - /* 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 (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) { - 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_free_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); -} - -struct mgcp_config *mgcp_config_alloc(void) -{ - struct mgcp_config *cfg; - - cfg = talloc_zero(NULL, struct mgcp_config); - if (!cfg) { - LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n"); - return NULL; - } - - cfg->source_port = 2427; - cfg->source_addr = talloc_strdup(cfg, "0.0.0.0"); - - cfg->transcoder_remote_base = 4000; - - cfg->bts_ports.base_port = RTP_PORT_DEFAULT; - cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT; - - /* default trunk handling */ - cfg->trunk.cfg = cfg; - cfg->trunk.trunk_nr = 0; - cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL; - cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000"); - cfg->trunk.audio_payload = 126; - cfg->trunk.omit_rtcp = 0; - - INIT_LLIST_HEAD(&cfg->trunks); - - return cfg; -} - -struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr) -{ - struct mgcp_trunk_config *trunk; - - trunk = talloc_zero(cfg, struct mgcp_trunk_config); - if (!trunk) { - LOGP(DMGCP, LOGL_ERROR, "Failed to allocate.\n"); - return NULL; - } - - trunk->cfg = cfg; - trunk->trunk_type = MGCP_TRUNK_E1; - trunk->trunk_nr = nr; - trunk->audio_name = talloc_strdup(cfg, "AMR/8000"); - trunk->audio_payload = 126; - trunk->number_endpoints = 33; - trunk->omit_rtcp = 0; - llist_add_tail(&trunk->entry, &cfg->trunks); - return trunk; -} - -struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index) -{ - struct mgcp_trunk_config *trunk; - - llist_for_each_entry(trunk, &cfg->trunks, entry) - if (trunk->trunk_nr == index) - return trunk; - - return NULL; -} - -static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end) -{ - if (end->local_alloc == PORT_ALLOC_DYNAMIC) { - mgcp_free_rtp_port(end); - end->local_port = 0; - } - - end->packets = 0; - end->octets = 0; - memset(&end->addr, 0, sizeof(end->addr)); - end->rtp_port = end->rtcp_port = 0; - end->payload_type = -1; - end->local_alloc = -1; - talloc_free(end->fmtp_extra); - end->fmtp_extra = NULL; -} - -static void mgcp_rtp_end_init(struct mgcp_rtp_end *end) -{ - mgcp_rtp_end_reset(end); - end->rtp.fd = -1; - end->rtcp.fd = -1; -} - -int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg) -{ - int i; - - /* Initialize all endpoints */ - tcfg->endpoints = _talloc_zero_array(tcfg->cfg, - sizeof(struct mgcp_endpoint), - tcfg->number_endpoints, "endpoints"); - if (!tcfg->endpoints) - return -1; - - for (i = 0; i < tcfg->number_endpoints; ++i) { - tcfg->endpoints[i].ci = CI_UNUSED; - tcfg->endpoints[i].cfg = tcfg->cfg; - tcfg->endpoints[i].tcfg = tcfg; - mgcp_rtp_end_init(&tcfg->endpoints[i].net_end); - mgcp_rtp_end_init(&tcfg->endpoints[i].bts_end); - mgcp_rtp_end_init(&tcfg->endpoints[i].trans_net); - mgcp_rtp_end_init(&tcfg->endpoints[i].trans_bts); - } - - return 0; -} - -void mgcp_free_endp(struct mgcp_endpoint *endp) -{ - LOGP(DMGCP, LOGL_DEBUG, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp)); - endp->ci = CI_UNUSED; - endp->allocated = 0; - - talloc_free(endp->callid); - endp->callid = NULL; - - talloc_free(endp->local_options); - endp->local_options = NULL; - - mgcp_rtp_end_reset(&endp->bts_end); - mgcp_rtp_end_reset(&endp->net_end); - mgcp_rtp_end_reset(&endp->trans_net); - mgcp_rtp_end_reset(&endp->trans_bts); - endp->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; - endp->allow_patch = 0; - - memset(&endp->taps, 0, sizeof(endp->taps)); -} - -static int send_trans(struct mgcp_config *cfg, const char *buf, int len) -{ - struct sockaddr_in addr; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr = cfg->transcoder_in; - addr.sin_port = htons(2427); - return sendto(cfg->gw_fd.bfd.fd, buf, len, 0, - (struct sockaddr *) &addr, sizeof(addr)); -} - -static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, - const char *msg, const char *mode) -{ - char buf[2096]; - int len; - - /* hardcoded to AMR right now, we do not know the real type at this point */ - len = snprintf(buf, sizeof(buf), - "%s 42 %x@mgw MGCP 1.0\r\n" - "C: 4256\r\n" - "M: %s\r\n" - "\r\n" - "c=IN IP4 %s\r\n" - "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d %s\r\n", - msg, endpoint, mode, endp->cfg->source_addr, - port, endp->tcfg->audio_payload, - endp->tcfg->audio_payload, endp->tcfg->audio_name); - - if (len < 0) - return; - - buf[sizeof(buf) - 1] = '\0'; - - send_trans(endp->cfg, buf, len); -} - -static void send_dlcx(struct mgcp_endpoint *endp, int endpoint) -{ - char buf[2096]; - int len; - - len = snprintf(buf, sizeof(buf), - "DLCX 43 %x@mgw MGCP 1.0\r\n" - "C: 4256\r\n" - , endpoint); - - if (len < 0) - return; - - buf[sizeof(buf) - 1] = '\0'; - - send_trans(endp->cfg, buf, len); -} - -static 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 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; - mgcp_state_calc_loss(&endp->net_state, &endp->net_end, - &expected, &ploss); - jitter = mgcp_state_calc_jitter(&endp->net_state); - - 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); - 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_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_vty.c b/openbsc/src/libmgcp/mgcp_vty.c deleted file mode 100644 index 69984ee92..000000000 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ /dev/null @@ -1,941 +0,0 @@ -/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ -/* The protocol implementation */ - -/* - * (C) 2009-2011 by Holger Hans Peter Freyther - * (C) 2009-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include - -#include -#include -#include - -#include - -#define RTCP_OMIT_STR "Drop RTCP packets in both directions\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->net_ports.mode == PORT_ALLOC_STATIC) - vty_out(vty, " rtp net-base %u%s", g_cfg->net_ports.base_port, VTY_NEWLINE); - else - vty_out(vty, " rtp net-range %u %u%s", - g_cfg->net_ports.range_start, g_cfg->net_ports.range_end, VTY_NEWLINE); - - vty_out(vty, " rtp ip-dscp %d%s", g_cfg->endp_dscp, VTY_NEWLINE); - if (g_cfg->trunk.omit_rtcp) - vty_out(vty, " rtcp-omit%s", VTY_NEWLINE); - else - vty_out(vty, " no rtcp-omit%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, " loop %u%s", !!g_cfg->trunk.audio_loop, VTY_NEWLINE); - vty_out(vty, " number endpoints %u%s", g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE); - if (g_cfg->call_agent_addr) - vty_out(vty, " call-agent ip %s%s", g_cfg->call_agent_addr, VTY_NEWLINE); - if (g_cfg->transcoder_ip) - vty_out(vty, " transcoder-mgw %s%s", g_cfg->transcoder_ip, VTY_NEWLINE); - - if (g_cfg->transcoder_ports.mode == PORT_ALLOC_STATIC) - vty_out(vty, " rtp transcoder-base %u%s", g_cfg->transcoder_ports.base_port, VTY_NEWLINE); - else - vty_out(vty, " rtp transcoder-range %u %u%s", - g_cfg->transcoder_ports.range_start, g_cfg->transcoder_ports.range_end, VTY_NEWLINE); - vty_out(vty, " transcoder-remote-base %u%s", g_cfg->transcoder_remote_base, VTY_NEWLINE); - - return CMD_SUCCESS; -} - -static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg) -{ - int i; - - vty_out(vty, "%s trunk nr %d with %d endpoints:%s", - cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", - cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE); - - if (!cfg->endpoints) { - vty_out(vty, "No endpoints allocated yet.%s", VTY_NEWLINE); - return; - } - - for (i = 1; i < cfg->number_endpoints; ++i) { - struct mgcp_endpoint *endp = &cfg->endpoints[i]; - vty_out(vty, - " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u on %s " - "traffic received bts: %u 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); - } -} - -DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp", - SHOW_STR "Display information about the MGCP Media Gateway") -{ - struct mgcp_trunk_config *trunk; - - dump_trunk(vty, &g_cfg->trunk); - - llist_for_each_entry(trunk, &g_cfg->trunks, entry) - dump_trunk(vty, trunk); - - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp, - cfg_mgcp_cmd, - "mgcp", - "Configure the MGCP") -{ - vty->node = MGCP_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_local_ip, - cfg_mgcp_local_ip_cmd, - "local ip A.B.C.D", - "Local options for the SDP record\n" - IP_STR - "IPv4 Address to use in SDP record\n") -{ - bsc_replace_string(g_cfg, &g_cfg->local_ip, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bts_ip, - cfg_mgcp_bts_ip_cmd, - "bts ip A.B.C.D", - "BTS Audio source/destination options\n" - IP_STR - "IPv4 Address of the BTS\n") -{ - bsc_replace_string(g_cfg, &g_cfg->bts_ip, argv[0]); - inet_aton(g_cfg->bts_ip, &g_cfg->bts_in); - return CMD_SUCCESS; -} - -#define BIND_STR "Listen/Bind related socket option\n" -DEFUN(cfg_mgcp_bind_ip, - cfg_mgcp_bind_ip_cmd, - "bind ip A.B.C.D", - BIND_STR - IP_STR - "IPv4 Address to bind to\n") -{ - bsc_replace_string(g_cfg, &g_cfg->source_addr, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bind_port, - cfg_mgcp_bind_port_cmd, - "bind port <0-65534>", - BIND_STR - "Port information\n" - "UDP port to listen for MGCP messages\n") -{ - unsigned int port = atoi(argv[0]); - g_cfg->source_port = port; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bind_early, - cfg_mgcp_bind_early_cmd, - "bind early (0|1)", - BIND_STR - "Bind local ports on start up\n" - "Bind on demand\n" "Bind on startup\n") -{ - vty_out(vty, "bind early is deprecated, remove it from the config.\n"); - return CMD_WARNING; -} - -static void parse_base(struct mgcp_port_range *range, const char **argv) -{ - unsigned int port = atoi(argv[0]); - range->mode = PORT_ALLOC_STATIC; - range->base_port = port; -} - -static void parse_range(struct mgcp_port_range *range, const char **argv) -{ - range->mode = PORT_ALLOC_DYNAMIC; - range->range_start = atoi(argv[0]); - range->range_end = atoi(argv[1]); - range->last_port = g_cfg->bts_ports.range_start; -} - - -#define RTP_STR "RTP configuration\n" -#define BTS_START_STR "First UDP port allocated for the BTS side\n" -#define NET_START_STR "First UDP port allocated for the NET side\n" -#define UDP_PORT_STR "UDP Port number\n" -DEFUN(cfg_mgcp_rtp_bts_base_port, - cfg_mgcp_rtp_bts_base_port_cmd, - "rtp bts-base <0-65534>", - RTP_STR - BTS_START_STR - UDP_PORT_STR) -{ - parse_base(&g_cfg->bts_ports, argv); - return CMD_SUCCESS; -} - -#define RANGE_START_STR "Start of the range of ports\n" -#define RANGE_END_STR "End of the range of ports\n" -DEFUN(cfg_mgcp_rtp_bts_range, - cfg_mgcp_rtp_bts_range_cmd, - "rtp bts-range <0-65534> <0-65534>", - RTP_STR "Range of ports to use for the BTS side\n" - RANGE_START_STR RANGE_END_STR) -{ - parse_range(&g_cfg->bts_ports, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_net_range, - cfg_mgcp_rtp_net_range_cmd, - "rtp net-range <0-65534> <0-65534>", - RTP_STR "Range of ports to use for the NET side\n" - RANGE_START_STR RANGE_END_STR) -{ - parse_range(&g_cfg->net_ports, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_net_base_port, - cfg_mgcp_rtp_net_base_port_cmd, - "rtp net-base <0-65534>", - RTP_STR NET_START_STR UDP_PORT_STR) -{ - parse_base(&g_cfg->net_ports, argv); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_mgcp_rtp_bts_base_port, cfg_mgcp_rtp_base_port_cmd, - "rtp base <0-65534>", - RTP_STR BTS_START_STR UDP_PORT_STR) - -DEFUN(cfg_mgcp_rtp_transcoder_range, - cfg_mgcp_rtp_transcoder_range_cmd, - "rtp transcoder-range <0-65534> <0-65534>", - RTP_STR "Range of ports to use for the Transcoder\n" - RANGE_START_STR RANGE_END_STR) -{ - parse_range(&g_cfg->transcoder_ports, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_transcoder_base, - cfg_mgcp_rtp_transcoder_base_cmd, - "rtp transcoder-base <0-65534>", - RTP_STR "First UDP port allocated for the Transcoder side\n" - UDP_PORT_STR) -{ - parse_base(&g_cfg->transcoder_ports, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_ip_dscp, - cfg_mgcp_rtp_ip_dscp_cmd, - "rtp ip-dscp <0-255>", - RTP_STR - "Apply IP_TOS to the audio stream\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") - -DEFUN(cfg_mgcp_sdp_fmtp_extra, - cfg_mgcp_sdp_fmtp_extra_cmd, - "sdp audio fmtp-extra .NAME", - "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n" - "Extra Information\n") -{ - char *txt = argv_concat(argv, argc, 0); - if (!txt) - return CMD_WARNING; - - bsc_replace_string(g_cfg, &g_cfg->trunk.audio_fmtp_extra, txt); - talloc_free(txt); - return CMD_SUCCESS; -} - -#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 <1-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 <1-255>", - SDP_STR AUDIO_STR AUDIO_STR "Number\n" "Payload number\n") - - -DEFUN(cfg_mgcp_sdp_payload_name, - cfg_mgcp_sdp_payload_name_cmd, - "sdp audio-payload name NAME", - SDP_STR AUDIO_STR "Name\n" "Payload name\n") -{ - bsc_replace_string(g_cfg, &g_cfg->trunk.audio_name, argv[0]); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_mgcp_sdp_payload_name, cfg_mgcp_sdp_payload_name_cmd_old, - "sdp audio payload name NAME", - SDP_STR AUDIO_STR AUDIO_STR "Name\n" "Payload name\n") - -DEFUN(cfg_mgcp_loop, - cfg_mgcp_loop_cmd, - "loop (0|1)", - "Loop audio for all endpoints on main trunk\n" - "Don't Loop\n" "Loop\n") -{ - 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; -} - -#define CALL_AGENT_STR "Callagent information\n" -DEFUN(cfg_mgcp_agent_addr, - cfg_mgcp_agent_addr_cmd, - "call-agent ip A.B.C.D", - CALL_AGENT_STR IP_STR - "IPv4 Address of the callagent\n") -{ - bsc_replace_string(g_cfg, &g_cfg->call_agent_addr, argv[0]); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_mgcp_agent_addr, cfg_mgcp_agent_addr_cmd_old, - "call agent ip A.B.C.D", - CALL_AGENT_STR CALL_AGENT_STR IP_STR - "IPv4 Address of the callagent\n") - - -DEFUN(cfg_mgcp_transcoder, - cfg_mgcp_transcoder_cmd, - "transcoder-mgw A.B.C.D", - "Use a MGW to detranscoder RTP\n" - "The IP address of the MGW") -{ - bsc_replace_string(g_cfg, &g_cfg->transcoder_ip, argv[0]); - inet_aton(g_cfg->transcoder_ip, &g_cfg->transcoder_in); - - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_no_transcoder, - cfg_mgcp_no_transcoder_cmd, - "no transcoder-mgw", - NO_STR "Disable the transcoding\n") -{ - if (g_cfg->transcoder_ip) { - LOGP(DMGCP, LOGL_NOTICE, "Disabling transcoding on future calls.\n"); - talloc_free(g_cfg->transcoder_ip); - g_cfg->transcoder_ip = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_transcoder_remote_base, - cfg_mgcp_transcoder_remote_base_cmd, - "transcoder-remote-base <0-65534>", - "Set the base port for the transcoder\n" "The RTP base port on the transcoder") -{ - g_cfg->transcoder_remote_base = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_trunk, cfg_mgcp_trunk_cmd, - "trunk <1-64>", - "Configure a SS7 trunk\n" "Trunk Nr\n") -{ - struct mgcp_trunk_config *trunk; - int index = atoi(argv[0]); - - trunk = mgcp_trunk_num(g_cfg, index); - if (!trunk) - trunk = mgcp_trunk_alloc(g_cfg, index); - - if (!trunk) { - vty_out(vty, "%%Unable to allocate trunk %u.%s", - index, VTY_NEWLINE); - return CMD_WARNING; - } - - vty->node = TRUNK_NODE; - vty->index = trunk; - return CMD_SUCCESS; -} - -static int config_write_trunk(struct vty *vty) -{ - struct mgcp_trunk_config *trunk; - - llist_for_each_entry(trunk, &g_cfg->trunks, entry) { - vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE); - vty_out(vty, " sdp audio-payload number %d%s", - trunk->audio_payload, VTY_NEWLINE); - vty_out(vty, " sdp audio-payload name %s%s", - trunk->audio_name, VTY_NEWLINE); - vty_out(vty, " 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->audio_fmtp_extra) - vty_out(vty, " sdp audio fmtp-extra %s%s", - trunk->audio_fmtp_extra, VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_sdp_fmtp_extra, - cfg_trunk_sdp_fmtp_extra_cmd, - "sdp audio fmtp-extra .NAME", - "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n" - "Extra Information\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - char *txt = argv_concat(argv, argc, 0); - if (!txt) - return CMD_WARNING; - - bsc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt); - talloc_free(txt); - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_payload_number, - cfg_trunk_payload_number_cmd, - "sdp audio-payload number <1-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 <1-255>", - SDP_STR AUDIO_STR AUDIO_STR "Number\n" "Payload Number\n") - -DEFUN(cfg_trunk_payload_name, - cfg_trunk_payload_name_cmd, - "sdp audio-payload name NAME", - SDP_STR AUDIO_STR "Payload\n" "Payload Name\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - - bsc_replace_string(g_cfg, &trunk->audio_name, argv[0]); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_trunk_payload_name, cfg_trunk_payload_name_cmd_old, - "sdp audio payload name NAME", - SDP_STR AUDIO_STR AUDIO_STR "Payload\n" "Payload Name\n") - - -DEFUN(cfg_trunk_loop, - cfg_trunk_loop_cmd, - "loop (0|1)", - "Loop audio for all endpoints on this trunk\n" - "Don't Loop\n" "Loop\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - - trunk->audio_loop = atoi(argv[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(loop_endp, - loop_endp_cmd, - "loop-endpoint <0-64> NAME (0|1)", - "Loop a given endpoint\n" "Trunk number\n" - "The name in hex of the endpoint\n" "Disable the loop\n" "Enable the loop\n") -{ - struct mgcp_trunk_config *trunk; - struct mgcp_endpoint *endp; - - trunk = find_trunk(g_cfg, atoi(argv[0])); - if (!trunk) { - vty_out(vty, "%%Trunk %d not found in the config.%s", - atoi(argv[0]), VTY_NEWLINE); - return CMD_WARNING; - } - - if (!trunk->endpoints) { - vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", - trunk->trunk_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - int endp_no = strtoul(argv[1], NULL, 16); - if (endp_no < 1 || endp_no >= trunk->number_endpoints) { - vty_out(vty, "Loopback number %s/%d is invalid.%s", - argv[1], endp_no, VTY_NEWLINE); - return CMD_WARNING; - } - - - endp = &trunk->endpoints[endp_no]; - int loop = atoi(argv[2]); - - if (loop) - endp->conn_mode = MGCP_CONN_LOOPBACK; - else - endp->conn_mode = endp->orig_mode; - endp->allow_patch = 1; - - return CMD_SUCCESS; -} - -DEFUN(tap_call, - tap_call_cmd, - "tap-call <0-64> ENDPOINT (bts-in|bts-out|net-in|net-out) A.B.C.D <0-65534>", - "Forward data on endpoint to a different system\n" "Trunk number\n" - "The endpoint in hex\n" - "Forward the data coming from the bts\n" - "Forward the data coming from the bts leaving to the network\n" - "Forward the data coming from the net\n" - "Forward the data coming from the net leaving to the bts\n" - "destination IP of the data\n" "destination port\n") -{ - struct mgcp_rtp_tap *tap; - struct mgcp_trunk_config *trunk; - struct mgcp_endpoint *endp; - int port = 0; - - trunk = find_trunk(g_cfg, atoi(argv[0])); - if (!trunk) { - vty_out(vty, "%%Trunk %d not found in the config.%s", - atoi(argv[0]), VTY_NEWLINE); - return CMD_WARNING; - } - - if (!trunk->endpoints) { - vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", - trunk->trunk_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - int endp_no = strtoul(argv[1], NULL, 16); - if (endp_no < 1 || endp_no >= trunk->number_endpoints) { - vty_out(vty, "Endpoint number %s/%d is invalid.%s", - argv[1], endp_no, VTY_NEWLINE); - return CMD_WARNING; - } - - endp = &trunk->endpoints[endp_no]; - - if (strcmp(argv[2], "bts-in") == 0) { - port = MGCP_TAP_BTS_IN; - } else if (strcmp(argv[2], "bts-out") == 0) { - port = MGCP_TAP_BTS_OUT; - } else if (strcmp(argv[2], "net-in") == 0) { - port = MGCP_TAP_NET_IN; - } else if (strcmp(argv[2], "net-out") == 0) { - port = MGCP_TAP_NET_OUT; - } else { - vty_out(vty, "Unknown mode... tricked vty?%s", VTY_NEWLINE); - return CMD_WARNING; - } - - tap = &endp->taps[port]; - memset(&tap->forward, 0, sizeof(tap->forward)); - inet_aton(argv[3], &tap->forward.sin_addr); - tap->forward.sin_port = htons(atoi(argv[4])); - tap->enabled = 1; - return CMD_SUCCESS; -} - -DEFUN(free_endp, free_endp_cmd, - "free-endpoint <0-64> NUMBER", - "Free the given endpoint\n" "Trunk number\n" - "Endpoint number in hex.\n") -{ - struct mgcp_trunk_config *trunk; - struct mgcp_endpoint *endp; - - trunk = find_trunk(g_cfg, atoi(argv[0])); - if (!trunk) { - vty_out(vty, "%%Trunk %d not found in the config.%s", - atoi(argv[0]), VTY_NEWLINE); - return CMD_WARNING; - } - - if (!trunk->endpoints) { - vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", - trunk->trunk_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - int endp_no = strtoul(argv[1], NULL, 16); - if (endp_no < 1 || endp_no >= trunk->number_endpoints) { - vty_out(vty, "Endpoint number %s/%d is invalid.%s", - argv[1], endp_no, VTY_NEWLINE); - return CMD_WARNING; - } - - endp = &trunk->endpoints[endp_no]; - mgcp_free_endp(endp); - return CMD_SUCCESS; -} - -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; -} - - -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); - - install_default(MGCP_NODE); - install_element(MGCP_NODE, &ournode_exit_cmd); - install_element(MGCP_NODE, &ournode_end_cmd); - install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd); - install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_base_port_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_net_base_port_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_range_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_net_range_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_range_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_base_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd); - install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd); - install_element(MGCP_NODE, &cfg_mgcp_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_sdp_fmtp_extra_cmd); - - install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd); - install_node(&trunk_node, config_write_trunk); - install_default(TRUNK_NODE); - install_element(TRUNK_NODE, &ournode_exit_cmd); - install_element(TRUNK_NODE, &ournode_end_cmd); - install_element(TRUNK_NODE, &cfg_trunk_payload_number_cmd); - install_element(TRUNK_NODE, &cfg_trunk_payload_name_cmd); - install_element(TRUNK_NODE, &cfg_trunk_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_sdp_fmtp_extra_cmd); - - return 0; -} - -static int allocate_trunk(struct mgcp_trunk_config *trunk) -{ - int i; - struct mgcp_config *cfg = trunk->cfg; - - if (mgcp_endpoints_allocate(trunk) != 0) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to allocate %d endpoints on trunk %d.\n", - trunk->number_endpoints, trunk->trunk_nr); - return -1; - } - - /* early bind */ - for (i = 1; i < trunk->number_endpoints; ++i) { - struct mgcp_endpoint *endp = &trunk->endpoints[i]; - - if (cfg->bts_ports.mode == PORT_ALLOC_STATIC) { - cfg->last_bts_port += 2; - if (mgcp_bind_bts_rtp_port(endp, cfg->last_bts_port) != 0) { - LOGP(DMGCP, LOGL_FATAL, - "Failed to bind: %d\n", cfg->last_bts_port); - return -1; - } - endp->bts_end.local_alloc = PORT_ALLOC_STATIC; - } - - if (cfg->net_ports.mode == PORT_ALLOC_STATIC) { - cfg->last_net_port += 2; - if (mgcp_bind_net_rtp_port(endp, cfg->last_net_port) != 0) { - LOGP(DMGCP, LOGL_FATAL, - "Failed to bind: %d\n", cfg->last_net_port); - return -1; - } - endp->net_end.local_alloc = PORT_ALLOC_STATIC; - } - - if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL && - cfg->transcoder_ip && cfg->transcoder_ports.mode == PORT_ALLOC_STATIC) { - int rtp_port; - - /* network side */ - rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), - cfg->transcoder_ports.base_port); - if (mgcp_bind_trans_net_rtp_port(endp, rtp_port) != 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port); - return -1; - } - endp->trans_net.local_alloc = PORT_ALLOC_STATIC; - - /* bts side */ - rtp_port = rtp_calculate_port(endp_back_channel(ENDPOINT_NUMBER(endp)), - cfg->transcoder_ports.base_port); - if (mgcp_bind_trans_bts_rtp_port(endp, rtp_port) != 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port); - return -1; - } - endp->trans_bts.local_alloc = PORT_ALLOC_STATIC; - } - } - - return 0; -} - -int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg) -{ - int rc; - struct mgcp_trunk_config *trunk; - - g_cfg = cfg; - rc = vty_read_config_file(config_file, NULL); - if (rc < 0) { - fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); - return rc; - } - - - if (!g_cfg->bts_ip) - fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n"); - - if (!g_cfg->source_addr) { - fprintf(stderr, "You need to specify a bind address.\n"); - return -1; - } - - /* initialize the last ports */ - g_cfg->last_bts_port = rtp_calculate_port(0, g_cfg->bts_ports.base_port); - g_cfg->last_net_port = rtp_calculate_port(0, g_cfg->net_ports.base_port); - - if (allocate_trunk(&g_cfg->trunk) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to initialize the virtual trunk.\n"); - return -1; - } - - llist_for_each_entry(trunk, &g_cfg->trunks, entry) { - if (allocate_trunk(trunk) != 0) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to initialize E1 trunk %d.\n", trunk->trunk_nr); - return -1; - } - } - - return 0; -} - diff --git a/openbsc/src/libmsc/Makefile.am b/openbsc/src/libmsc/Makefile.am deleted file mode 100644 index c36ba926b..000000000 --- a/openbsc/src/libmsc/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) - -noinst_LIBRARIES = libmsc.a - -libmsc_a_SOURCES = auth.c \ - db.c \ - gsm_04_08.c gsm_04_11.c gsm_04_80.c \ - gsm_subscriber.c \ - mncc.c mncc_builtin.c mncc_sock.c \ - rrlp.c \ - silent_call.c \ - sms_queue.c \ - token_auth.c \ - ussd.c \ - vty_interface_layer3.c \ - transaction.c \ - osmo_msc.c - -if BUILD_SMPP -libmsc_a_SOURCES += smpp_smsc.c smpp_openbsc.c smpp_vty.c smpp_utils.c -endif diff --git a/openbsc/src/libmsc/auth.c b/openbsc/src/libmsc/auth.c deleted file mode 100644 index 10d8edf67..000000000 --- a/openbsc/src/libmsc/auth.c +++ /dev/null @@ -1,132 +0,0 @@ -/* Authentication related functions */ - -/* - * (C) 2010 by Sylvain Munaut - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include - -#include - - -static int -_use_xor(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) -{ - int i, l = ainfo->a3a8_ki_len; - - if ((l > A38_XOR_MAX_KEY_LEN) || (l < A38_XOR_MIN_KEY_LEN)) { - LOGP(DMM, LOGL_ERROR, "Invalid XOR key (len=%d) %s\n", - ainfo->a3a8_ki_len, - osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); - return -1; - } - - for (i=0; i<4; i++) - atuple->sres[i] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; - for (i=4; i<12; i++) - atuple->kc[i-4] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; - - return 0; -} - -static int -_use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) -{ - if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) { - LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n", - ainfo->a3a8_ki_len, - osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); - return -1; - } - - comp128(ainfo->a3a8_ki, atuple->rand, atuple->sres, atuple->kc); - - return 0; -} - -/* Return values - * -1 -> Internal error - * 0 -> Not available - * 1 -> Tuple returned, need to do auth, then enable cipher - * 2 -> Tuple returned, need to enable cipher - */ -int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr, int key_seq) -{ - struct gsm_auth_info ainfo; - int i, rc; - - /* Get subscriber info (if any) */ - rc = db_get_authinfo_for_subscr(&ainfo, subscr); - if (rc < 0) { - LOGP(DMM, LOGL_NOTICE, - "No retrievable Ki for subscriber, skipping auth\n"); - return rc == -ENOENT ? AUTH_NOT_AVAIL : -1; - } - - /* If possible, re-use the last tuple and skip auth */ - rc = db_get_lastauthtuple_for_subscr(atuple, subscr); - if ((rc == 0) && - (key_seq != GSM_KEY_SEQ_INVAL) && - (atuple->use_count < 3)) - { - atuple->use_count++; - db_sync_lastauthtuple_for_subscr(atuple, subscr); - DEBUGP(DMM, "Auth tuple use < 3, just doing ciphering\n"); - return AUTH_DO_CIPH; - } - - /* Generate a new one */ - atuple->use_count = 1; - atuple->key_seq = (atuple->key_seq + 1) % 7; - for (i=0; irand); i++) - atuple->rand[i] = random() & 0xff; - - switch (ainfo.auth_algo) { - case AUTH_ALGO_NONE: - DEBUGP(DMM, "No authentication for subscriber\n"); - return 0; - - case AUTH_ALGO_XOR: - if (_use_xor(&ainfo, atuple)) - return 0; - break; - - case AUTH_ALGO_COMP128v1: - if (_use_comp128_v1(&ainfo, atuple)) - return 0; - break; - - default: - DEBUGP(DMM, "Unsupported auth type algo_id=%d\n", - ainfo.auth_algo); - return 0; - } - - db_sync_lastauthtuple_for_subscr(atuple, subscr); - - DEBUGP(DMM, "Need to do authentication and ciphering\n"); - return AUTH_DO_AUTH_THAN_CIPH; -} - diff --git a/openbsc/src/libmsc/db.c b/openbsc/src/libmsc/db.c deleted file mode 100644 index 21abce9de..000000000 --- a/openbsc/src/libmsc/db.c +++ /dev/null @@ -1,1412 +0,0 @@ -/* Simple HLR/VLR database backend using dbi */ -/* (C) 2008 by Jan Luebbe - * (C) 2009 by Holger Hans Peter Freyther - * (C) 2009 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -static char *db_basename = NULL; -static char *db_dirname = NULL; -static dbi_conn conn; - -#define SCHEMA_REVISION "3" - -static char *create_stmts[] = { - "CREATE TABLE IF NOT EXISTS Meta (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "key TEXT UNIQUE NOT NULL, " - "value TEXT NOT NULL" - ")", - "INSERT OR IGNORE INTO Meta " - "(key, value) " - "VALUES " - "('revision', " SCHEMA_REVISION ")", - "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" - ")", - "CREATE TABLE IF NOT EXISTS AuthToken (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "subscriber_id INTEGER UNIQUE NOT NULL, " - "created TIMESTAMP NOT NULL, " - "token TEXT UNIQUE NOT NULL" - ")", - "CREATE TABLE IF NOT EXISTS Equipment (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "name TEXT, " - "classmark1 NUMERIC, " - "classmark2 BLOB, " - "classmark3 BLOB, " - "imei NUMERIC UNIQUE NOT NULL" - ")", - "CREATE TABLE IF NOT EXISTS EquipmentWatch (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "subscriber_id NUMERIC NOT NULL, " - "equipment_id NUMERIC NOT NULL, " - "UNIQUE (subscriber_id, equipment_id) " - ")", - "CREATE TABLE IF NOT EXISTS SMS (" - /* metadata, not part of sms */ - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "sent TIMESTAMP, " - "sender_id INTEGER NOT NULL, " - "receiver_id INTEGER NOT NULL, " - "deliver_attempts INTEGER NOT NULL DEFAULT 0, " - /* data directly copied/derived from SMS */ - "valid_until TIMESTAMP, " - "reply_path_req INTEGER NOT NULL, " - "status_rep_req INTEGER NOT NULL, " - "protocol_id INTEGER NOT NULL, " - "data_coding_scheme INTEGER NOT NULL, " - "ud_hdr_ind INTEGER NOT NULL, " - "dest_addr TEXT, " - "user_data BLOB, " /* TP-UD */ - /* additional data, interpreted from SMS */ - "header BLOB, " /* UD Header */ - "text TEXT " /* decoded UD after UDH */ - ")", - "CREATE TABLE IF NOT EXISTS VLR (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "subscriber_id NUMERIC UNIQUE NOT NULL, " - "last_bts NUMERIC NOT NULL " - ")", - "CREATE TABLE IF NOT EXISTS ApduBlobs (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "apdu_id_flags INTEGER NOT NULL, " - "subscriber_id INTEGER NOT NULL, " - "apdu BLOB " - ")", - "CREATE TABLE IF NOT EXISTS Counters (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "timestamp TIMESTAMP NOT NULL, " - "value INTEGER NOT NULL, " - "name TEXT NOT NULL " - ")", - "CREATE TABLE IF NOT EXISTS RateCounters (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "timestamp TIMESTAMP NOT NULL, " - "value INTEGER NOT NULL, " - "name TEXT NOT NULL, " - "idx INTEGER NOT NULL " - ")", - "CREATE TABLE IF NOT EXISTS AuthKeys (" - "subscriber_id INTEGER PRIMARY KEY, " - "algorithm_id INTEGER NOT NULL, " - "a3a8_ki BLOB " - ")", - "CREATE TABLE IF NOT EXISTS AuthLastTuples (" - "subscriber_id INTEGER PRIMARY KEY, " - "issued TIMESTAMP NOT NULL, " - "use_count INTEGER NOT NULL DEFAULT 0, " - "key_seq INTEGER NOT NULL, " - "rand BLOB NOT NULL, " - "sres BLOB NOT NULL, " - "kc BLOB NOT NULL " - ")", -}; - -void db_error_func(dbi_conn conn, void *data) -{ - const char *msg; - dbi_conn_error(conn, &msg); - LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg); -} - -static int 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 vom 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 set new revision (upgrade vom rev 2).\n"); - return -EINVAL; - } - dbi_result_free(result); - - return 0; -} - -static int check_db_revision(void) -{ - dbi_result result; - const char *rev_s; - - result = dbi_conn_query(conn, - "SELECT value FROM Meta WHERE key='revision'"); - if (!result) - return -EINVAL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -EINVAL; - } - rev_s = dbi_result_get_string(result, "value"); - if (!rev_s) { - dbi_result_free(result); - return -EINVAL; - } - if (!strcmp(rev_s, "2")) { - if (update_db_revision_2()) { - LOGP(DDB, LOGL_FATAL, "Failed to update database from schema revision '%s'.\n", rev_s); - dbi_result_free(result); - return -EINVAL; - } - } else if (!strcmp(rev_s, SCHEMA_REVISION)) { - /* everything is fine */ - } else { - LOGP(DDB, LOGL_FATAL, "Invalid database schema revision '%s'.\n", rev_s); - dbi_result_free(result); - return -EINVAL; - } - - dbi_result_free(result); - return 0; -} - -static int db_configure(void) -{ - dbi_result result; - - result = dbi_conn_query(conn, - "PRAGMA synchronous = FULL"); - if (!result) - return -EINVAL; - - dbi_result_free(result); - return 0; -} - -int db_init(const char *name) -{ - dbi_initialize(NULL); - - conn = dbi_conn_new("sqlite3"); - if (conn == NULL) { - LOGP(DDB, LOGL_FATAL, "Failed to create connection.\n"); - return 1; - } - - dbi_conn_error_handler( conn, db_error_func, NULL ); - - /* MySQL - dbi_conn_set_option(conn, "host", "localhost"); - dbi_conn_set_option(conn, "username", "your_name"); - dbi_conn_set_option(conn, "password", "your_password"); - dbi_conn_set_option(conn, "dbname", "your_dbname"); - dbi_conn_set_option(conn, "encoding", "UTF-8"); - */ - - /* SqLite 3 */ - db_basename = strdup(name); - db_dirname = strdup(name); - dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname)); - dbi_conn_set_option(conn, "dbname", basename(db_basename)); - - if (dbi_conn_connect(conn) < 0) - goto out_err; - - return 0; - -out_err: - free(db_dirname); - free(db_basename); - db_dirname = db_basename = NULL; - return -1; -} - - -int db_prepare(void) -{ - dbi_result result; - int i; - - for (i = 0; i < ARRAY_SIZE(create_stmts); i++) { - result = dbi_conn_query(conn, create_stmts[i]); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to create some table.\n"); - return 1; - } - dbi_result_free(result); - } - - if (check_db_revision() < 0) { - LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, " - "please update your database schema\n"); - return -1; - } - - db_configure(); - - return 0; -} - -int db_fini(void) -{ - dbi_conn_close(conn); - dbi_shutdown(); - - free(db_dirname); - free(db_basename); - return 0; -} - -struct gsm_subscriber *db_create_subscriber(struct gsm_network *net, char *imsi) -{ - dbi_result result; - struct gsm_subscriber *subscr; - - /* Is this subscriber known in the db? */ - subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); - if (subscr) { - result = dbi_conn_queryf(conn, - "UPDATE Subscriber set updated = datetime('now') " - "WHERE imsi = %s " , imsi); - if (!result) - LOGP(DDB, LOGL_ERROR, "failed to update timestamp\n"); - else - dbi_result_free(result); - return subscr; - } - - subscr = subscr_alloc(); - 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->net = net; - subscr->id = dbi_conn_sequence_last(conn, NULL); - strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1); - dbi_result_free(result); - LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi); - db_subscriber_alloc_exten(subscr); - return subscr; -} - -osmo_static_assert(sizeof(unsigned char) == sizeof(struct gsm48_classmark1), classmark1_size); - -static int get_equipment_by_subscr(struct gsm_subscriber *subscr) -{ - dbi_result result; - const char *string; - unsigned char cm1; - const unsigned char *cm2, *cm3; - struct gsm_equipment *equip = &subscr->equipment; - - result = dbi_conn_queryf(conn, - "SELECT Equipment.* " - "FROM Equipment JOIN EquipmentWatch ON " - "EquipmentWatch.equipment_id=Equipment.id " - "WHERE EquipmentWatch.subscriber_id = %llu " - "ORDER BY EquipmentWatch.updated DESC", subscr->id); - if (!result) - return -EIO; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -ENOENT; - } - - equip->id = dbi_result_get_ulonglong(result, "id"); - - string = dbi_result_get_string(result, "imei"); - if (string) - strncpy(equip->imei, string, sizeof(equip->imei)); - - string = dbi_result_get_string(result, "classmark1"); - if (string) { - cm1 = atoi(string) & 0xff; - memcpy(&equip->classmark1, &cm1, sizeof(equip->classmark1)); - } - - equip->classmark2_len = dbi_result_get_field_length(result, "classmark2"); - cm2 = dbi_result_get_binary(result, "classmark2"); - if (equip->classmark2_len > sizeof(equip->classmark2)) - equip->classmark2_len = sizeof(equip->classmark2); - memcpy(equip->classmark2, cm2, equip->classmark2_len); - - equip->classmark3_len = dbi_result_get_field_length(result, "classmark3"); - cm3 = dbi_result_get_binary(result, "classmark3"); - if (equip->classmark3_len > sizeof(equip->classmark3)) - equip->classmark3_len = sizeof(equip->classmark3); - memcpy(equip->classmark3, cm3, equip->classmark3_len); - - dbi_result_free(result); - - return 0; -} - -int db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo, - struct gsm_subscriber *subscr) -{ - dbi_result result; - const unsigned char *a3a8_ki; - - result = dbi_conn_queryf(conn, - "SELECT * FROM AuthKeys WHERE subscriber_id=%llu", - subscr->id); - if (!result) - return -EIO; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -ENOENT; - } - - ainfo->auth_algo = dbi_result_get_ulonglong(result, "algorithm_id"); - ainfo->a3a8_ki_len = dbi_result_get_field_length(result, "a3a8_ki"); - a3a8_ki = dbi_result_get_binary(result, "a3a8_ki"); - if (ainfo->a3a8_ki_len > sizeof(ainfo->a3a8_ki)) - ainfo->a3a8_ki_len = sizeof(ainfo->a3a8_ki); - memcpy(ainfo->a3a8_ki, a3a8_ki, ainfo->a3a8_ki_len); - - dbi_result_free(result); - - return 0; -} - -int db_sync_authinfo_for_subscr(struct gsm_auth_info *ainfo, - struct gsm_subscriber *subscr) -{ - dbi_result result; - struct gsm_auth_info ainfo_old; - int rc, upd; - unsigned char *ki_str; - - /* Deletion ? */ - if (ainfo == NULL) { - result = dbi_conn_queryf(conn, - "DELETE FROM AuthKeys WHERE subscriber_id=%llu", - subscr->id); - - if (!result) - return -EIO; - - dbi_result_free(result); - - return 0; - } - - /* Check if already existing */ - rc = db_get_authinfo_for_subscr(&ainfo_old, subscr); - if (rc && rc != -ENOENT) - return rc; - upd = rc ? 0 : 1; - - /* Update / Insert */ - dbi_conn_quote_binary_copy(conn, - ainfo->a3a8_ki, ainfo->a3a8_ki_len, &ki_str); - - if (!upd) { - result = dbi_conn_queryf(conn, - "INSERT INTO AuthKeys " - "(subscriber_id, algorithm_id, a3a8_ki) " - "VALUES (%llu, %u, %s)", - subscr->id, ainfo->auth_algo, ki_str); - } else { - result = dbi_conn_queryf(conn, - "UPDATE AuthKeys " - "SET algorithm_id=%u, a3a8_ki=%s " - "WHERE subscriber_id=%llu", - ainfo->auth_algo, ki_str, subscr->id); - } - - free(ki_str); - - if (!result) - return -EIO; - - dbi_result_free(result); - - return 0; -} - -int db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr) -{ - dbi_result result; - int len; - const unsigned char *blob; - - result = dbi_conn_queryf(conn, - "SELECT * FROM AuthLastTuples WHERE subscriber_id=%llu", - subscr->id); - if (!result) - return -EIO; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -ENOENT; - } - - memset(atuple, 0, sizeof(*atuple)); - - atuple->use_count = dbi_result_get_ulonglong(result, "use_count"); - atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq"); - - len = dbi_result_get_field_length(result, "rand"); - if (len != sizeof(atuple->rand)) - goto err_size; - - blob = dbi_result_get_binary(result, "rand"); - memcpy(atuple->rand, blob, len); - - len = dbi_result_get_field_length(result, "sres"); - if (len != sizeof(atuple->sres)) - goto err_size; - - blob = dbi_result_get_binary(result, "sres"); - memcpy(atuple->sres, blob, len); - - len = dbi_result_get_field_length(result, "kc"); - if (len != sizeof(atuple->kc)) - goto err_size; - - blob = dbi_result_get_binary(result, "kc"); - memcpy(atuple->kc, blob, len); - - dbi_result_free(result); - - return 0; - -err_size: - dbi_result_free(result); - return -EIO; -} - -int db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr) -{ - dbi_result result; - int rc, upd; - struct gsm_auth_tuple atuple_old; - unsigned char *rand_str, *sres_str, *kc_str; - - /* Deletion ? */ - if (atuple == NULL) { - result = dbi_conn_queryf(conn, - "DELETE FROM AuthLastTuples WHERE subscriber_id=%llu", - subscr->id); - - if (!result) - return -EIO; - - dbi_result_free(result); - - return 0; - } - - /* Check if already existing */ - rc = db_get_lastauthtuple_for_subscr(&atuple_old, subscr); - if (rc && rc != -ENOENT) - return rc; - upd = rc ? 0 : 1; - - /* Update / Insert */ - dbi_conn_quote_binary_copy(conn, - atuple->rand, sizeof(atuple->rand), &rand_str); - dbi_conn_quote_binary_copy(conn, - atuple->sres, sizeof(atuple->sres), &sres_str); - dbi_conn_quote_binary_copy(conn, - atuple->kc, sizeof(atuple->kc), &kc_str); - - if (!upd) { - result = dbi_conn_queryf(conn, - "INSERT INTO AuthLastTuples " - "(subscriber_id, issued, use_count, " - "key_seq, rand, sres, kc) " - "VALUES (%llu, datetime('now'), %u, " - "%u, %s, %s, %s ) ", - subscr->id, atuple->use_count, atuple->key_seq, - rand_str, sres_str, kc_str); - } else { - char *issued = atuple->key_seq == atuple_old.key_seq ? - "issued" : "datetime('now')"; - result = dbi_conn_queryf(conn, - "UPDATE AuthLastTuples " - "SET issued=%s, use_count=%u, " - "key_seq=%u, rand=%s, sres=%s, kc=%s " - "WHERE subscriber_id = %llu", - issued, atuple->use_count, atuple->key_seq, - rand_str, sres_str, kc_str, subscr->id); - } - - free(rand_str); - free(sres_str); - free(kc_str); - - if (!result) - return -EIO; - - dbi_result_free(result); - - return 0; -} - -static void db_set_from_query(struct gsm_subscriber *subscr, dbi_conn result) -{ - const char *string; - string = dbi_result_get_string(result, "imsi"); - if (string) - strncpy(subscr->imsi, string, GSM_IMSI_LENGTH); - - string = dbi_result_get_string(result, "tmsi"); - if (string) - subscr->tmsi = tmsi_from_string(string); - - string = dbi_result_get_string(result, "name"); - if (string) - strncpy(subscr->name, string, GSM_NAME_LENGTH); - - string = dbi_result_get_string(result, "extension"); - if (string) - strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH); - - subscr->lac = dbi_result_get_uint(result, "lac"); - - 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_uint(result, "authorized"); -} - -#define BASE_QUERY "SELECT * FROM Subscriber " -struct gsm_subscriber *db_get_subscriber(struct gsm_network *net, - enum gsm_subscriber_field field, - const char *id) -{ - dbi_result result; - char *quoted; - struct gsm_subscriber *subscr; - - switch (field) { - case GSM_SUBSCRIBER_IMSI: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE imsi = %s ", - quoted - ); - free(quoted); - break; - case GSM_SUBSCRIBER_TMSI: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE tmsi = %s ", - quoted - ); - free(quoted); - break; - case GSM_SUBSCRIBER_EXTENSION: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE extension = %s ", - quoted - ); - free(quoted); - break; - case GSM_SUBSCRIBER_ID: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE id = %s ", quoted); - free(quoted); - break; - default: - LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n"); - return NULL; - } - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n"); - return NULL; - } - if (!dbi_result_next_row(result)) { - DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n", - field, id); - dbi_result_free(result); - return NULL; - } - - subscr = subscr_alloc(); - subscr->net = net; - subscr->id = dbi_result_get_ulonglong(result, "id"); - - db_set_from_query(subscr, result); - DEBUGP(DDB, "Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n", - subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension, - subscr->lac, subscr->authorized); - dbi_result_free(result); - - get_equipment_by_subscr(subscr); - - return subscr; -} - -int db_subscriber_update(struct gsm_subscriber *subscr) -{ - char buf[32]; - dbi_result result; - - /* Copy the id to a string as queryf with %llu is failing */ - sprintf(buf, "%llu", subscr->id); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE id = %s", buf); - - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber: %llu\n", subscr->id); - return -EIO; - } - if (!dbi_result_next_row(result)) { - DEBUGP(DDB, "Failed to find the Subscriber. %llu\n", - subscr->id); - dbi_result_free(result); - return -EIO; - } - - db_set_from_query(subscr, result); - dbi_result_free(result); - get_equipment_by_subscr(subscr); - - return 0; -} - -int db_sync_subscriber(struct gsm_subscriber *subscriber) -{ - dbi_result result; - char tmsi[14]; - char *q_tmsi, *q_name, *q_extension; - - dbi_conn_quote_string_copy(conn, - subscriber->name, &q_name); - dbi_conn_quote_string_copy(conn, - subscriber->extension, &q_extension); - - if (subscriber->tmsi != GSM_RESERVED_TMSI) { - sprintf(tmsi, "%u", subscriber->tmsi); - dbi_conn_quote_string_copy(conn, - tmsi, - &q_tmsi); - } else - q_tmsi = strdup("NULL"); - - 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_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 (;;) { - subscriber->tmsi = rand(); - if (subscriber->tmsi == GSM_RESERVED_TMSI) - continue; - - sprintf(tmsi, "%u", subscriber->tmsi); - dbi_conn_quote_string_copy(conn, tmsi, &tmsi_quoted); - result = dbi_conn_queryf(conn, - "SELECT * FROM Subscriber " - "WHERE tmsi = %s ", - tmsi_quoted); - - free(tmsi_quoted); - - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " - "while allocating new TMSI.\n"); - return 1; - } - if (dbi_result_get_numrows(result)) { - dbi_result_free(result); - continue; - } - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n", - subscriber->tmsi, subscriber->imsi); - return db_sync_subscriber(subscriber); - } - dbi_result_free(result); - } - return 0; -} - -int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber) -{ - dbi_result result = NULL; - uint32_t try; - - for (;;) { - try = (rand()%(GSM_MAX_EXTEN-GSM_MIN_EXTEN+1)+GSM_MIN_EXTEN); - result = dbi_conn_queryf(conn, - "SELECT * FROM Subscriber " - "WHERE extension = %i", - try - ); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " - "while allocating new extension.\n"); - return 1; - } - if (dbi_result_get_numrows(result)){ - dbi_result_free(result); - continue; - } - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - break; - } - dbi_result_free(result); - } - sprintf(subscriber->extension, "%i", try); - DEBUGP(DDB, "Allocated extension %i for IMSI %s.\n", try, subscriber->imsi); - return db_sync_subscriber(subscriber); -} -/* - * try to allocate a new unique token for this subscriber and return it - * via a parameter. if the subscriber already has a token, return - * an error. - */ - -int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t *token) -{ - dbi_result result; - uint32_t try; - - for (;;) { - try = rand(); - if (!try) /* 0 is an invalid token */ - continue; - result = dbi_conn_queryf(conn, - "SELECT * FROM AuthToken " - "WHERE subscriber_id = %llu OR token = \"%08X\" ", - subscriber->id, try); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken " - "while allocating new token.\n"); - return 1; - } - if (dbi_result_get_numrows(result)) { - dbi_result_free(result); - continue; - } - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - break; - } - dbi_result_free(result); - } - result = dbi_conn_queryf(conn, - "INSERT INTO AuthToken " - "(subscriber_id, created, token) " - "VALUES " - "(%llu, datetime('now'), \"%08X\") ", - subscriber->id, try); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for " - "IMSI %s.\n", try, subscriber->imsi); - return 1; - } - dbi_result_free(result); - *token = try; - DEBUGP(DDB, "Allocated token %08X for IMSI %s.\n", try, subscriber->imsi); - - return 0; -} - -int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM_IMEI_LENGTH]) -{ - unsigned long long equipment_id, watch_id; - dbi_result result; - - strncpy(subscriber->equipment.imei, imei, - sizeof(subscriber->equipment.imei)-1), - - result = dbi_conn_queryf(conn, - "INSERT OR IGNORE INTO Equipment " - "(imei, created, updated) " - "VALUES " - "(%s, datetime('now'), datetime('now')) ", - imei); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to create Equipment by IMEI.\n"); - return 1; - } - - equipment_id = 0; - if (dbi_result_get_numrows_affected(result)) { - equipment_id = dbi_conn_sequence_last(conn, NULL); - } - dbi_result_free(result); - - if (equipment_id) - DEBUGP(DDB, "New Equipment: ID %llu, IMEI %s\n", equipment_id, imei); - else { - result = dbi_conn_queryf(conn, - "SELECT id FROM Equipment " - "WHERE imei = %s ", - imei - ); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n"); - return 1; - } - if (!dbi_result_next_row(result)) { - LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n"); - dbi_result_free(result); - return 1; - } - equipment_id = dbi_result_get_ulonglong(result, "id"); - dbi_result_free(result); - } - - result = dbi_conn_queryf(conn, - "INSERT OR IGNORE INTO EquipmentWatch " - "(subscriber_id, equipment_id, created, updated) " - "VALUES " - "(%llu, %llu, datetime('now'), datetime('now')) ", - subscriber->id, equipment_id); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to create EquipmentWatch.\n"); - return 1; - } - - watch_id = 0; - if (dbi_result_get_numrows_affected(result)) - watch_id = dbi_conn_sequence_last(conn, NULL); - - dbi_result_free(result); - if (watch_id) - DEBUGP(DDB, "New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", - equipment_id, subscriber->imsi, imei); - else { - result = dbi_conn_queryf(conn, - "UPDATE EquipmentWatch " - "SET updated = datetime('now') " - "WHERE subscriber_id = %llu AND equipment_id = %llu ", - subscriber->id, equipment_id); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n"); - return 1; - } - dbi_result_free(result); - DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", - equipment_id, subscriber->imsi, imei); - } - - return 0; -} - -/* store an [unsent] SMS to the database */ -int db_sms_store(struct gsm_sms *sms) -{ - dbi_result result; - char *q_text, *q_daddr; - unsigned char *q_udata; - char *validity_timestamp = "2222-2-2"; - - /* FIXME: generate validity timestamp based on validity_minutes */ - - dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text); - dbi_conn_quote_string_copy(conn, (char *)sms->dst.addr, &q_daddr); - dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len, - &q_udata); - /* FIXME: correct validity period */ - result = dbi_conn_queryf(conn, - "INSERT INTO SMS " - "(created, sender_id, receiver_id, valid_until, " - "reply_path_req, status_rep_req, protocol_id, " - "data_coding_scheme, ud_hdr_ind, dest_addr, " - "user_data, text) VALUES " - "(datetime('now'), %llu, %llu, %u, " - "%u, %u, %u, %u, %u, %s, %s, %s)", - sms->sender->id, - sms->receiver ? sms->receiver->id : 0, validity_timestamp, - sms->reply_path_req, sms->status_rep_req, sms->protocol_id, - sms->data_coding_scheme, sms->ud_hdr_ind, - q_daddr, q_udata, q_text); - free(q_text); - free(q_daddr); - free(q_udata); - - if (!result) - return -EIO; - - dbi_result_free(result); - return 0; -} - -static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result) -{ - struct gsm_sms *sms = sms_alloc(); - long long unsigned int sender_id, receiver_id; - const char *text, *daddr; - const unsigned char *user_data; - - if (!sms) - return NULL; - - sms->id = dbi_result_get_ulonglong(result, "id"); - - sender_id = dbi_result_get_ulonglong(result, "sender_id"); - sms->sender = subscr_get_by_id(net, sender_id); - strncpy(sms->src.addr, sms->sender->extension, sizeof(sms->src.addr)-1); - - receiver_id = dbi_result_get_ulonglong(result, "receiver_id"); - sms->receiver = subscr_get_by_id(net, receiver_id); - - /* FIXME: validity */ - /* FIXME: those should all be get_uchar, but sqlite3 is braindead */ - sms->reply_path_req = dbi_result_get_uint(result, "reply_path_req"); - sms->status_rep_req = dbi_result_get_uint(result, "status_rep_req"); - sms->ud_hdr_ind = dbi_result_get_uint(result, "ud_hdr_ind"); - sms->protocol_id = dbi_result_get_uint(result, "protocol_id"); - sms->data_coding_scheme = dbi_result_get_uint(result, - "data_coding_scheme"); - /* sms->msg_ref is temporary and not stored in DB */ - - daddr = dbi_result_get_string(result, "dest_addr"); - if (daddr) { - strncpy(sms->dst.addr, daddr, sizeof(sms->dst.addr)); - sms->dst.addr[sizeof(sms->dst.addr)-1] = '\0'; - } - - sms->user_data_len = dbi_result_get_field_length(result, "user_data"); - user_data = dbi_result_get_binary(result, "user_data"); - if (sms->user_data_len > sizeof(sms->user_data)) - sms->user_data_len = (uint8_t) sizeof(sms->user_data); - memcpy(sms->user_data, user_data, sms->user_data_len); - - text = dbi_result_get_string(result, "text"); - if (text) { - strncpy(sms->text, text, sizeof(sms->text)); - sms->text[sizeof(sms->text)-1] = '\0'; - } - return sms; -} - -struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT * FROM SMS WHERE SMS.id = %llu", id); - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(net, result); - - dbi_result_free(result); - - return sms; -} - -/* retrieve the next unsent SMS with ID >= min_id */ -struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT SMS.* " - "FROM SMS JOIN Subscriber ON " - "SMS.receiver_id = Subscriber.id " - "WHERE SMS.id >= %llu AND SMS.sent IS NULL " - "AND Subscriber.lac > 0 " - "ORDER BY SMS.id LIMIT 1", - min_id); - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(net, result); - - dbi_result_free(result); - - return sms; -} - -struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, - unsigned long long min_subscr_id, - unsigned int failed) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT SMS.* " - "FROM SMS JOIN Subscriber ON " - "SMS.receiver_id = Subscriber.id " - "WHERE SMS.receiver_id >= %llu AND SMS.sent IS NULL " - "AND Subscriber.lac > 0 AND SMS.deliver_attempts < %u " - "ORDER BY SMS.receiver_id, SMS.id LIMIT 1", - min_subscr_id, failed); - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(net, result); - - dbi_result_free(result); - - return sms; -} - -/* retrieve the next unsent SMS for a given subscriber */ -struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT SMS.* " - "FROM SMS JOIN Subscriber ON " - "SMS.receiver_id = Subscriber.id " - "WHERE SMS.receiver_id = %llu AND SMS.sent IS NULL " - "AND Subscriber.lac > 0 " - "ORDER BY SMS.id LIMIT 1", - subscr->id); - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(subscr->net, result); - - dbi_result_free(result); - - return sms; -} - -/* mark a given SMS as read */ -int db_sms_mark_sent(struct gsm_sms *sms) -{ - dbi_result result; - - result = dbi_conn_queryf(conn, - "UPDATE SMS " - "SET sent = datetime('now') " - "WHERE id = %llu", sms->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id); - return 1; - } - - dbi_result_free(result); - return 0; -} - -/* increase the number of attempted deliveries */ -int db_sms_inc_deliver_attempts(struct gsm_sms *sms) -{ - dbi_result result; - - result = dbi_conn_queryf(conn, - "UPDATE SMS " - "SET deliver_attempts = deliver_attempts + 1 " - "WHERE id = %llu", sms->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for " - "SMS %llu.\n", sms->id); - return 1; - } - - dbi_result_free(result); - return 0; -} - -int db_apdu_blob_store(struct gsm_subscriber *subscr, - 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 eea073614..000000000 --- a/openbsc/src/libmsc/gsm_04_08.c +++ /dev/null @@ -1,3283 +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 "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 - -void *tall_locop_ctx; -void *tall_authciphop_ctx; - -int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, uint32_t tmsi); -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 uint32_t new_callref = 0x80000001; - -void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg) -{ - net->mncc_recv(net, msg); -} - -static int gsm48_conn_sendmsg(struct msgb *msg, struct gsm_subscriber_connection *conn, - struct gsm_trans *trans) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data; - - /* if we get passed a transaction reference, do some common - * work that the caller no longer has to do */ - if (trans) { - gh->proto_discr = trans->protocol | (trans->transaction_id << 4); - msg->lchan = trans->conn->lchan; - } - - - if (msg->lchan) { - struct e1inp_sign_link *sign_link = - msg->lchan->ts->trx->rsl_link; - - msg->dst = sign_link; - if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC) - DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) " - "Sending '%s' to MS.\n", - sign_link->trx->bts->nr, - sign_link->trx->nr, msg->lchan->ts->nr, - gh->proto_discr & 0xf0, - gsm48_cc_msg_name(gh->msg_type)); - else - DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) " - "Sending 0x%02x to MS.\n", - sign_link->trx->bts->nr, - sign_link->trx->nr, msg->lchan->ts->nr, - gh->proto_discr, gh->msg_type); - } - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message) -{ - struct gsm48_hdr *gh; - struct msgb *ss_notify; - - ss_notify = gsm0480_create_notifySS(message); - if (!ss_notify) - return -1; - - gsm0480_wrap_invoke(ss_notify, GSM0480_OP_CODE_NOTIFY_SS, 0); - uint8_t *data = msgb_push(ss_notify, 1); - data[0] = ss_notify->len - 1; - gh = (struct gsm48_hdr *) msgb_push(ss_notify, sizeof(*gh)); - gh->msg_type = GSM48_MT_CC_FACILITY; - return gsm48_conn_sendmsg(ss_notify, trans->conn, trans); -} - -void release_security_operation(struct gsm_subscriber_connection *conn) -{ - if (!conn->sec_operation) - return; - - talloc_free(conn->sec_operation); - conn->sec_operation = NULL; - msc_release_connection(conn); -} - -void allocate_security_operation(struct gsm_subscriber_connection *conn) -{ - conn->sec_operation = talloc_zero(tall_authciphop_ctx, - struct gsm_security_operation); -} - -int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, - gsm_cbfn *cb, void *cb_data) -{ - struct gsm_network *net = conn->bts->network; - struct gsm_subscriber *subscr = conn->subscr; - struct gsm_security_operation *op; - struct gsm_auth_tuple atuple; - int status = -1, rc; - - /* Check if we _can_ enable encryption. Cases where we can't: - * - Encryption disabled in config - * - Channel already secured (nothing to do) - * - Subscriber equipment doesn't support configured encryption - */ - if (!net->a5_encryption) { - status = GSM_SECURITY_NOAVAIL; - } else if (conn->lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { - DEBUGP(DMM, "Requesting to secure an already secure channel"); - status = GSM_SECURITY_ALREADY; - } else if (!ms_cm2_a5n_support(subscr->equipment.classmark2, - net->a5_encryption)) { - DEBUGP(DMM, "Subscriber equipment doesn't support requested encryption"); - status = GSM_SECURITY_NOAVAIL; - } - - /* If not done yet, try to get info for this user */ - if (status < 0) { - rc = auth_get_tuple_for_subscr(&atuple, subscr, key_seq); - if (rc <= 0) - status = GSM_SECURITY_NOAVAIL; - } - - /* Are we done yet ? */ - if (status >= 0) - return cb ? - cb(GSM_HOOK_RR_SECURITY, status, NULL, conn, cb_data) : - 0; - - /* Start an operation (can't have more than one pending !!!) */ - if (conn->sec_operation) - return -EBUSY; - - allocate_security_operation(conn); - op = conn->sec_operation; - op->cb = cb; - op->cb_data = cb_data; - memcpy(&op->atuple, &atuple, sizeof(struct gsm_auth_tuple)); - - /* FIXME: Should start a timer for completion ... */ - - /* Then do whatever is needed ... */ - if (rc == AUTH_DO_AUTH_THAN_CIPH) { - /* Start authentication */ - return gsm48_tx_mm_auth_req(conn, op->atuple.rand, op->atuple.key_seq); - } else if (rc == AUTH_DO_CIPH) { - /* Start ciphering directly */ - return gsm0808_cipher_mode(conn, net->a5_encryption, - op->atuple.kc, 8, 0); - } - - return -EINVAL; /* not reached */ -} - -static int authorize_subscriber(struct gsm_loc_updating_operation *loc, - struct gsm_subscriber *subscriber) -{ - if (!subscriber) - return 0; - - /* - * Do not send accept yet as more information should arrive. Some - * phones will not send us the information and we will have to check - * what we want to do with that. - */ - if (loc && (loc->waiting_for_imsi || loc->waiting_for_imei)) - return 0; - - switch (subscriber->net->auth_policy) { - case GSM_AUTH_POLICY_CLOSED: - return subscriber->authorized; - case GSM_AUTH_POLICY_TOKEN: - if (subscriber->authorized) - return subscriber->authorized; - return (subscriber->flags & GSM_SUBSCRIBER_FIRST_CONTACT); - case GSM_AUTH_POLICY_ACCEPT_ALL: - return 1; - default: - return 0; - } -} - -static void release_loc_updating_req(struct gsm_subscriber_connection *conn) -{ - if (!conn->loc_operation) - return; - - /* No need to keep the connection up */ - release_anchor(conn); - - osmo_timer_del(&conn->loc_operation->updating_timer); - talloc_free(conn->loc_operation); - conn->loc_operation = NULL; - msc_release_connection(conn); -} - -static void allocate_loc_updating_req(struct gsm_subscriber_connection *conn) -{ - if (conn->loc_operation) - LOGP(DMM, LOGL_ERROR, "Connection already had operation.\n"); - release_loc_updating_req(conn); - - conn->loc_operation = talloc_zero(tall_locop_ctx, - struct gsm_loc_updating_operation); -} - -static int _gsm0408_authorize_sec_cb(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct gsm_subscriber_connection *conn = data; - int rc = 0; - - switch (event) { - case GSM_SECURITY_AUTH_FAILED: - release_loc_updating_req(conn); - break; - - case GSM_SECURITY_NOAVAIL: - case GSM_SECURITY_SUCCEEDED: - /* We're all good */ - db_subscriber_alloc_tmsi(conn->subscr); - rc = gsm0408_loc_upd_acc(conn, conn->subscr->tmsi); - if (conn->bts->network->send_mm_info) { - /* send MM INFO with network name */ - rc = gsm48_tx_mm_info(conn); - } - - /* call subscr_update after putting the loc_upd_acc - * in the transmit queue, since S_SUBSCR_ATTACHED might - * trigger further action like SMS delivery */ - subscr_update(conn->subscr, conn->bts, - GSM_SUBSCRIBER_UPDATE_ATTACHED); - - /* - * The gsm0408_loc_upd_acc sends a MI with the TMSI. The - * MS needs to respond with a TMSI REALLOCATION COMPLETE - * (even if the TMSI is the same). - */ - break; - - default: - rc = -EINVAL; - }; - - return rc; -} - -static int gsm0408_authorize(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - if (authorize_subscriber(conn->loc_operation, conn->subscr)) - return gsm48_secure_channel(conn, - conn->loc_operation->key_seq, - _gsm0408_authorize_sec_cb, NULL); - return 0; -} - -void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) -{ - struct gsm_trans *trans, *temp; - - /* avoid someone issuing a clear */ - conn->in_release = 1; - - /* - * Cancel any outstanding location updating request - * operation taking place on the subscriber connection. - */ - release_loc_updating_req(conn); - - /* We might need to cancel the paging response or such. */ - if (conn->sec_operation && conn->sec_operation->cb) { - conn->sec_operation->cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, - NULL, conn, conn->sec_operation->cb_data); - } - - release_security_operation(conn); - release_anchor(conn); - - /* - * Free all transactions that are associated with the released - * connection. The transaction code will inform the CC or SMS - * facilities that will send the release indications. As part of - * the CC REL_IND the remote leg might be released and this will - * trigger the call to trans_free. This is something the llist - * macro can not handle and we will need to re-iterate the list. - * - * TODO: Move the trans_list into the subscriber connection and - * create a pending list for MT transactions. These exist before - * we have a subscriber connection. - */ -restart: - llist_for_each_entry_safe(trans, temp, &conn->bts->network->trans_list, entry) { - if (trans->conn == conn) { - trans_free(trans); - goto restart; - } - } -} - -void gsm0408_clear_all_trans(struct gsm_network *net, int protocol) -{ - struct gsm_trans *trans, *temp; - - LOGP(DCC, LOGL_NOTICE, "Clearing all currently active transactions!!!\n"); - - llist_for_each_entry_safe(trans, temp, &net->trans_list, entry) { - if (trans->protocol == protocol) { - trans->callref = 0; - trans_free(trans); - } - } -} - -/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */ -int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause) -{ - struct gsm_bts *bts = conn->bts; - struct msgb *msg; - - osmo_counter_inc(bts->network->stats.loc_upd_resp.reject); - - msg = gsm48_create_loc_upd_rej(cause); - if (!msg) { - LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n"); - return -1; - } - - msg->lchan = conn->lchan; - - LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT " - "LAC=%u BTS=%u\n", conn->subscr ? - subscr_name(conn->subscr) : "unknown", - bts->location_area_code, bts->nr); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */ -int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, uint32_t tmsi) -{ - struct gsm_bts *bts = conn->bts; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm48_loc_area_id *lai; - uint8_t *mid; - - msg->lchan = conn->lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT; - - lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); - gsm48_generate_lai(lai, bts->network->country_code, - bts->network->network_code, bts->location_area_code); - - mid = msgb_put(msg, GSM48_MID_TMSI_LEN); - gsm48_generate_mid_from_tmsi(mid, tmsi); - - DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n"); - - osmo_counter_inc(bts->network->stats.loc_upd_resp.accept); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Transmit Chapter 9.2.10 Identity Request */ -static int mm_tx_identity_req(struct gsm_subscriber_connection *conn, uint8_t id_type) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - msg->lchan = conn->lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_ID_REQ; - gh->data[0] = id_type; - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - - -/* Parse Chapter 9.2.11 Identity Response */ -static int mm_rx_id_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm_lchan *lchan = msg->lchan; - struct gsm_bts *bts = lchan->ts->trx->bts; - struct gsm_network *net = bts->network; - uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; - char mi_string[GSM48_MI_SIZE]; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); - DEBUGP(DMM, "IDENTITY RESPONSE: mi_type=0x%02x MI(%s)\n", - 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, mi_string); - if (!conn->subscr) - conn->subscr = db_create_subscriber(net, mi_string); - } - if (conn->loc_operation) - conn->loc_operation->waiting_for_imsi = 0; - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* update subscribe <-> IMEI mapping */ - if (conn->subscr) { - db_subscriber_assoc_imei(conn->subscr, mi_string); - db_sync_equipment(&conn->subscr->equipment); - } - if (conn->loc_operation) - conn->loc_operation->waiting_for_imei = 0; - break; - } - - /* Check if we can let the mobile station enter */ - return gsm0408_authorize(conn, msg); -} - - -static void loc_upd_rej_cb(void *data) -{ - struct gsm_subscriber_connection *conn = data; - struct gsm_lchan *lchan = conn->lchan; - struct gsm_bts *bts = lchan->ts->trx->bts; - - LOGP(DMM, LOGL_DEBUG, "Location Updating Request procedure timedout.\n"); - gsm0408_loc_upd_rej(conn, bts->network->reject_cause); - release_loc_updating_req(conn); -} - -static void schedule_reject(struct gsm_subscriber_connection *conn) -{ - conn->loc_operation->updating_timer.cb = loc_upd_rej_cb; - conn->loc_operation->updating_timer.data = conn; - osmo_timer_schedule(&conn->loc_operation->updating_timer, 5, 0); -} - -static const char *lupd_name(uint8_t type) -{ - switch (type) { - case GSM48_LUPD_NORMAL: - return "NORMAL"; - case GSM48_LUPD_PERIODIC: - return "PEROIDOC"; - case GSM48_LUPD_IMSI_ATT: - return "IMSI ATTACH"; - default: - return "UNKNOWN"; - } -} - -/* Chapter 9.2.15: Receive Location Updating Request */ -static int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_loc_upd_req *lu; - struct gsm_subscriber *subscr = NULL; - struct gsm_bts *bts = conn->bts; - 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_type=0x%02x MI(%s) type=%s ", mi_type, mi_string, - lupd_name(lu->type)); - - osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len); - - switch (lu->type) { - case GSM48_LUPD_NORMAL: - osmo_counter_inc(bts->network->stats.loc_upd_type.normal); - break; - case GSM48_LUPD_IMSI_ATT: - osmo_counter_inc(bts->network->stats.loc_upd_type.attach); - break; - case GSM48_LUPD_PERIODIC: - osmo_counter_inc(bts->network->stats.loc_upd_type.periodic); - break; - } - - /* - * Pseudo Spoof detection: Just drop a second/concurrent - * location updating request. - */ - if (conn->loc_operation) { - DEBUGPC(DMM, "ignoring request due an existing one: %p.\n", - conn->loc_operation); - gsm0408_loc_upd_rej(conn, GSM48_REJECT_PROTOCOL_ERROR); - return 0; - } - - allocate_loc_updating_req(conn); - - conn->loc_operation->key_seq = lu->key_seq; - - switch (mi_type) { - case GSM_MI_TYPE_IMSI: - DEBUGPC(DMM, "\n"); - /* we always want the IMEI, too */ - mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI); - conn->loc_operation->waiting_for_imei = 1; - - /* look up subscriber based on IMSI, create if not found */ - subscr = subscr_get_by_imsi(bts->network, mi_string); - if (!subscr) { - subscr = db_create_subscriber(bts->network, mi_string); - } - break; - case GSM_MI_TYPE_TMSI: - DEBUGPC(DMM, "\n"); - /* look up the subscriber based on TMSI, request IMSI if it fails */ - subscr = subscr_get_by_tmsi(bts->network, - tmsi_from_string(mi_string)); - if (!subscr) { - /* send IDENTITY REQUEST message to get IMSI */ - mm_tx_identity_req(conn, GSM_MI_TYPE_IMSI); - conn->loc_operation->waiting_for_imsi = 1; - } - /* we always want the IMEI, too */ - mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI); - conn->loc_operation->waiting_for_imei = 1; - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* no sim card... FIXME: what to do ? */ - DEBUGPC(DMM, "unimplemented mobile identity type\n"); - break; - default: - DEBUGPC(DMM, "unknown mobile identity type\n"); - break; - } - - /* schedule the reject timer */ - schedule_reject(conn); - - if (!subscr) { - DEBUGPC(DRR, "<- Can't find any subscriber for this ID\n"); - /* FIXME: request id? close channel? */ - return -EINVAL; - } - - conn->subscr = subscr; - conn->subscr->equipment.classmark1 = lu->classmark1; - - /* check if we can let the subscriber into our network immediately - * or if we need to wait for identity responses. */ - return gsm0408_authorize(conn, msg); -} - -/* Turn int into semi-octet representation: 98 => 0x89 */ -static uint8_t bcdify(uint8_t value) -{ - uint8_t ret; - - ret = value / 10; - ret |= (value % 10) << 4; - - return ret; -} - - -/* Section 9.2.15a */ -int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm_network *net = conn->bts->network; - struct gsm_bts *bts = conn->bts; - uint8_t *ptr8; - int name_len, name_pad; - - time_t cur_t; - struct tm* gmt_time; - struct tm* local_time; - int tzunits; - - 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(ptr8, net->name_long); - - } - - 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(ptr8, net->name_short); - - } - - /* Section 10.5.3.9 */ - cur_t = time(NULL); - gmt_time = gmtime(&cur_t); - - ptr8 = msgb_put(msg, 8); - ptr8[0] = GSM48_IE_NET_TIME_TZ; - ptr8[1] = bcdify(gmt_time->tm_year % 100); - ptr8[2] = bcdify(gmt_time->tm_mon + 1); - ptr8[3] = bcdify(gmt_time->tm_mday); - ptr8[4] = bcdify(gmt_time->tm_hour); - ptr8[5] = bcdify(gmt_time->tm_min); - ptr8[6] = bcdify(gmt_time->tm_sec); - - if (bts->tz.override) { - /* Convert tz.hr and tz.mn to units */ - if (bts->tz.hr < 0) { - tzunits = ((bts->tz.hr/-1)*4); - tzunits = tzunits + (bts->tz.mn/15); - ptr8[7] = bcdify(tzunits); - /* Set negative time */ - ptr8[7] |= 0x08; - } - else { - tzunits = bts->tz.hr*4; - tzunits = tzunits + (bts->tz.mn/15); - ptr8[7] = bcdify(tzunits); - } - } - 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); - } - - DEBUGP(DMM, "-> MM INFO\n"); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Section 9.2.2 */ -int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, uint8_t *rand, int key_seq) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_auth_req *ar = (struct gsm48_auth_req *) msgb_put(msg, sizeof(*ar)); - - DEBUGP(DMM, "-> AUTH REQ (rand = %s)\n", osmo_hexdump(rand, 16)); - - msg->lchan = conn->lchan; - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_AUTH_REQ; - - ar->key_seq = key_seq; - - /* 16 bytes RAND parameters */ - if (rand) - memcpy(ar->rand, rand, 16); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Section 9.2.1 */ -int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn) -{ - DEBUGP(DMM, "-> AUTH REJECT\n"); - return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_AUTH_REJ); -} - -static int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn) -{ - DEBUGP(DMM, "-> CM SERVICE ACK\n"); - return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_ACC); -} - -/* 9.2.6 CM service reject */ -static int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, - enum gsm48_reject_value value) -{ - struct msgb *msg; - - msg = gsm48_create_mm_serv_rej(value); - if (!msg) { - LOGP(DMM, LOGL_ERROR, "Failed to allocate CM Service Reject.\n"); - return -1; - } - - DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value); - msg->lchan = conn->lchan; - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -static int _gsm48_rx_mm_serv_req_sec_cb( - unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct gsm_subscriber_connection *conn = data; - int rc = 0; - - /* 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); - break; - - case GSM_SECURITY_SUCCEEDED: - /* nothing to do. CIPHER MODE COMMAND is - * implicit CM SERV ACK */ - break; - - default: - rc = -EINVAL; - }; - - return rc; -} - -/* - * Handle CM Service Requests - * a) Verify that the packet is long enough to contain the information - * we require otherwsie reject with INCORRECT_MESSAGE - * b) Try to parse the TMSI. If we do not have one reject - * c) Check that we know the subscriber with the TMSI otherwise reject - * with a HLR cause - * d) Set the subscriber on the gsm_lchan and accept - */ -static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - uint8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - - struct gsm_bts *bts = conn->bts; - struct gsm_subscriber *subscr; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_service_request *req = - (struct gsm48_service_request *)gh->data; - /* unfortunately in Phase1 the classmark2 length is variable */ - uint8_t classmark2_len = gh->data[1]; - uint8_t *classmark2 = gh->data+2; - uint8_t mi_len = *(classmark2 + classmark2_len); - uint8_t *mi = (classmark2 + classmark2_len + 1); - - DEBUGP(DMM, "<- CM SERVICE REQUEST "); - if (msg->data_len < sizeof(struct gsm48_service_request*)) { - DEBUGPC(DMM, "wrong sized message\n"); - return gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - if (msg->data_len < req->mi_len + 6) { - DEBUGPC(DMM, "does not fit in packet\n"); - return gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - mi_type = mi[0] & GSM_MI_TYPE_MASK; - if (mi_type != GSM_MI_TYPE_TMSI) { - DEBUGPC(DMM, "mi_type is not TMSI: %d\n", mi_type); - return gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); - DEBUGPC(DMM, "serv_type=0x%02x mi_type=0x%02x M(%s)\n", - req->cm_service_type, mi_type, mi_string); - - osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len)); - - if (is_siemens_bts(bts)) - send_siemens_mrpci(msg->lchan, classmark2-1); - - subscr = subscr_get_by_tmsi(bts->network, - tmsi_from_string(mi_string)); - - /* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */ - if (!subscr) - return gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_IMSI_UNKNOWN_IN_HLR); - if (subscr->lac == GSM_LAC_RESERVED_DETACHED) - /* If the subscriber is not attached, reject service */ - return gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_IMSI_UNKNOWN_IN_VLR); - - if (!conn->subscr) - conn->subscr = subscr; - else if (conn->subscr == subscr) - subscr_put(subscr); /* lchan already has a ref, don't need another one */ - else { - DEBUGP(DMM, "<- CM Channel already owned by someone else?\n"); - subscr_put(subscr); - } - - subscr->equipment.classmark2_len = classmark2_len; - memcpy(subscr->equipment.classmark2, classmark2, classmark2_len); - db_sync_equipment(&subscr->equipment); - - /* we will send a MM message soon */ - conn->expire_timer_stopped = 1; - - return gsm48_secure_channel(conn, req->cipher_key_seq, - _gsm48_rx_mm_serv_req_sec_cb, NULL); -} - -static int gsm48_rx_mm_imsi_detach_ind(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm_bts *bts = conn->bts; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_imsi_detach_ind *idi = - (struct gsm48_imsi_detach_ind *) gh->data; - uint8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK; - char mi_string[GSM48_MI_SIZE]; - struct gsm_subscriber *subscr = NULL; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len); - DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s)", - mi_type, mi_string); - - osmo_counter_inc(bts->network->stats.loc_upd_type.detach); - - switch (mi_type) { - case GSM_MI_TYPE_TMSI: - DEBUGPC(DMM, "\n"); - subscr = subscr_get_by_tmsi(bts->network, - tmsi_from_string(mi_string)); - break; - case GSM_MI_TYPE_IMSI: - DEBUGPC(DMM, "\n"); - subscr = subscr_get_by_imsi(bts->network, mi_string); - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* no sim card... FIXME: what to do ? */ - DEBUGPC(DMM, ": unimplemented mobile identity type\n"); - break; - default: - DEBUGPC(DMM, ": unknown mobile identity type\n"); - break; - } - - if (subscr) { - subscr_update(subscr, bts, - GSM_SUBSCRIBER_UPDATE_DETACHED); - DEBUGP(DMM, "Subscriber: %s\n", subscr_name(subscr)); - - subscr->equipment.classmark1 = idi->classmark1; - db_sync_equipment(&subscr->equipment); - - subscr_put(subscr); - } else - DEBUGP(DMM, "Unknown Subscriber ?!?\n"); - - /* FIXME: iterate over all transactions and release them, - * imagine an IMSI DETACH happening during an active call! */ - - release_anchor(conn); - return 0; -} - -static int gsm48_rx_mm_status(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DMM, "MM STATUS (reject cause 0x%02x)\n", gh->data[0]); - - return 0; -} - -/* Chapter 9.2.3: Authentication Response */ -static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_auth_resp *ar = (struct gsm48_auth_resp*) gh->data; - struct gsm_network *net = conn->bts->network; - - DEBUGP(DMM, "MM AUTHENTICATION RESPONSE (sres = %s): ", - osmo_hexdump(ar->sres, 4)); - - /* Safety check */ - if (!conn->sec_operation) { - DEBUGP(DMM, "No authentication/cipher operation in progress !!!\n"); - return -EIO; - } - - /* Validate SRES */ - if (memcmp(conn->sec_operation->atuple.sres, ar->sres,4)) { - int rc; - gsm_cbfn *cb = conn->sec_operation->cb; - - DEBUGPC(DMM, "Invalid (expected %s)\n", - osmo_hexdump(conn->sec_operation->atuple.sres, 4)); - - if (cb) - cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, - NULL, conn, conn->sec_operation->cb_data); - - rc = gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return rc; - } - - DEBUGPC(DMM, "OK\n"); - - /* Start ciphering */ - return gsm0808_cipher_mode(conn, net->a5_encryption, - conn->sec_operation->atuple.kc, 8, 0); -} - -/* Receive a GSM 04.08 Mobility Management (MM) message */ -static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (gh->msg_type & 0xbf) { - case GSM48_MT_MM_LOC_UPD_REQUEST: - DEBUGP(DMM, "LOCATION UPDATING REQUEST: "); - rc = mm_rx_loc_upd_req(conn, msg); - break; - case GSM48_MT_MM_ID_RESP: - rc = mm_rx_id_resp(conn, msg); - break; - case GSM48_MT_MM_CM_SERV_REQ: - rc = gsm48_rx_mm_serv_req(conn, msg); - break; - case GSM48_MT_MM_STATUS: - rc = gsm48_rx_mm_status(msg); - break; - case GSM48_MT_MM_TMSI_REALL_COMPL: - DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n", - conn->subscr ? - subscr_name(conn->subscr) : - "unknown subscriber"); - release_loc_updating_req(conn); - break; - case GSM48_MT_MM_IMSI_DETACH_IND: - rc = gsm48_rx_mm_imsi_detach_ind(conn, msg); - break; - case GSM48_MT_MM_CM_REEST_REQ: - DEBUGP(DMM, "CM REESTABLISH REQUEST: Not implemented\n"); - break; - case GSM48_MT_MM_AUTH_RESP: - rc = gsm48_rx_mm_auth_resp(conn, msg); - break; - default: - LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n", - gh->msg_type); - break; - } - - return rc; -} - -/* Receive a PAGING RESPONSE message from the MS */ -static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm_bts *bts = conn->bts; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_pag_resp *resp; - uint8_t *classmark2_lv = gh->data + 1; - uint8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - struct gsm_subscriber *subscr = NULL; - int rc = 0; - - resp = (struct gsm48_pag_resp *) &gh->data[0]; - gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), - mi_string, &mi_type); - DEBUGP(DRR, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n", - mi_type, mi_string); - - switch (mi_type) { - case GSM_MI_TYPE_TMSI: - subscr = subscr_get_by_tmsi(bts->network, - tmsi_from_string(mi_string)); - break; - case GSM_MI_TYPE_IMSI: - subscr = subscr_get_by_imsi(bts->network, mi_string); - break; - } - - if (!subscr) { - DEBUGP(DRR, "<- Can't find any subscriber for this ID\n"); - /* FIXME: request id? close channel? */ - return -EINVAL; - } - DEBUGP(DRR, "<- Channel was requested by %s\n", - subscr->name && strlen(subscr->name) ? subscr->name : subscr->imsi); - - subscr->equipment.classmark2_len = *classmark2_lv; - memcpy(subscr->equipment.classmark2, classmark2_lv+1, *classmark2_lv); - db_sync_equipment(&subscr->equipment); - - /* We received a paging */ - conn->expire_timer_stopped = 1; - - rc = gsm48_handle_paging_resp(conn, msg, subscr); - return rc; -} - -static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t apdu_id_flags; - uint8_t apdu_len; - uint8_t *apdu_data; - - apdu_id_flags = gh->data[0]; - apdu_len = gh->data[1]; - apdu_data = gh->data+2; - - DEBUGP(DRR, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s", - apdu_id_flags, apdu_len, osmo_hexdump(apdu_data, apdu_len)); - - return db_apdu_blob_store(conn->subscr, apdu_id_flags, apdu_len, apdu_data); -} - -/* Receive a GSM 04.08 Radio Resource (RR) message */ -static int gsm0408_rcv_rr(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (gh->msg_type) { - case GSM48_MT_RR_PAG_RESP: - rc = gsm48_rx_rr_pag_resp(conn, msg); - break; - case GSM48_MT_RR_APP_INFO: - rc = gsm48_rx_rr_app_info(conn, msg); - break; - default: - LOGP(DRR, LOGL_NOTICE, "MSC: Unimplemented " - "GSM 04.08 RR msg type 0x%02x\n", gh->msg_type); - break; - } - - return rc; -} - -int gsm48_send_rr_app_info(struct gsm_subscriber_connection *conn, uint8_t apdu_id, - uint8_t apdu_len, const uint8_t *apdu) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - msg->lchan = conn->lchan; - - DEBUGP(DRR, "TX APPLICATION INFO id=0x%02x, len=%u\n", - apdu_id, apdu_len); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2 + apdu_len); - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_APP_INFO; - gh->data[0] = apdu_id; - gh->data[1] = apdu_len; - memcpy(gh->data+2, apdu, apdu_len); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Call Control */ - -/* The entire call control code is written in accordance with Figure 7.10c - * for 'very early assignment', i.e. we allocate a TCH/F during IMMEDIATE - * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY - * it for voice */ - -static void new_cc_state(struct gsm_trans *trans, int state) -{ - if (state > 31 || state < 0) - return; - - DEBUGP(DCC, "new state %s -> %s\n", - gsm48_cc_state_name(trans->cc.state), - gsm48_cc_state_name(state)); - - trans->cc.state = state; -} - -static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - uint8_t *cause, *call_state; - - gh->msg_type = GSM48_MT_CC_STATUS; - - cause = msgb_put(msg, 3); - cause[0] = 2; - cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER; - cause[2] = 0x80 | 30; /* response to status inquiry */ - - call_state = msgb_put(msg, 1); - call_state[0] = 0xc0 | 0x00; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_tx_simple(struct gsm_subscriber_connection *conn, - uint8_t pdisc, uint8_t msg_type) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - msg->lchan = conn->lchan; - - gh->proto_discr = pdisc; - gh->msg_type = msg_type; - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -static void gsm48_stop_cc_timer(struct gsm_trans *trans) -{ - if (osmo_timer_pending(&trans->cc.timer)) { - DEBUGP(DCC, "stopping pending timer T%x\n", trans->cc.Tcurrent); - osmo_timer_del(&trans->cc.timer); - trans->cc.Tcurrent = 0; - } -} - -static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans, - int msg_type, struct gsm_mncc *mncc) -{ - struct msgb *msg; - unsigned char *data; - - if (trans) - if (trans->conn && trans->conn->lchan) - DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " - "Sending '%s' to MNCC.\n", - trans->conn->lchan->ts->trx->bts->nr, - trans->conn->lchan->ts->trx->nr, - trans->conn->lchan->ts->nr, trans->transaction_id, - (trans->subscr)?(trans->subscr->extension):"-", - get_mncc_name(msg_type)); - else - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Sending '%s' to MNCC.\n", - (trans->subscr)?(trans->subscr->extension):"-", - get_mncc_name(msg_type)); - else - DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) " - "Sending '%s' to MNCC.\n", get_mncc_name(msg_type)); - - mncc->msg_type = msg_type; - - msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC"); - if (!msg) - return -ENOMEM; - - data = msgb_put(msg, sizeof(struct gsm_mncc)); - memcpy(data, mncc, sizeof(struct gsm_mncc)); - - cc_tx_to_mncc(net, msg); - - return 0; -} - -int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans, - uint32_t callref, int location, int value) -{ - struct gsm_mncc rel; - - memset(&rel, 0, sizeof(rel)); - rel.callref = callref; - mncc_set_cause(&rel, location, value); - if (trans && trans->cc.state == GSM_CSTATE_RELEASE_REQ) - return mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); - return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); -} - -/* Call Control Specific transaction release. - * gets called by trans_free, DO NOT CALL YOURSELF! */ -void _gsm48_cc_trans_free(struct gsm_trans *trans) -{ - gsm48_stop_cc_timer(trans); - - /* send release to L4, if callref still exists */ - if (trans->callref) { - /* Ressource unavailable */ - mncc_release_ind(trans->subscr->net, trans, trans->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - } - if (trans->cc.state != GSM_CSTATE_NULL) - new_cc_state(trans, GSM_CSTATE_NULL); - if (trans->conn) - trau_mux_unmap(&trans->conn->lchan->ts->e1_link, trans->callref); -} - -static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg); - -/* call-back from paging the B-end of the connection */ -static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *_conn, void *param) -{ - int found = 0; - struct gsm_subscriber_connection *conn = _conn; - struct gsm_network **paging_request = param, *net; - struct gsm_trans *transt, *tmp; - - if (hooknum != GSM_HOOK_RR_PAGING) - return -EINVAL; - - net = *paging_request; - if (!net) { - DEBUGP(DCC, "Error Network not set!\n"); - return -EINVAL; - } - - /* check all tranactions (without lchan) for subscriber */ - llist_for_each_entry_safe(transt, tmp, &net->trans_list, entry) { - if (transt->paging_request != paging_request || transt->conn) - continue; - switch (event) { - case GSM_PAGING_SUCCEEDED: - if (!conn) // paranoid - break; - DEBUGP(DCC, "Paging subscr %s succeeded!\n", - transt->subscr->extension); - found = 1; - /* Assign lchan */ - if (!transt->conn) { - transt->paging_request = NULL; - transt->conn = conn; - conn->put_channel = 1; - } - /* send SETUP request to called party */ - gsm48_cc_tx_setup(transt, &transt->cc.msg); - break; - case GSM_PAGING_EXPIRED: - case GSM_PAGING_BUSY: - DEBUGP(DCC, "Paging subscr %s expired!\n", - transt->subscr->extension); - /* Temporarily out of order */ - found = 1; - mncc_release_ind(transt->subscr->net, transt, - transt->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_DEST_OOO); - transt->callref = 0; - transt->paging_request = NULL; - trans_free(transt); - break; - } - } - - talloc_free(paging_request); - - /* - * FIXME: The queue needs to be kicked. This is likely to go through a RF - * failure and then the subscr will be poke again. This needs a lot of fixing - * in the subscriber queue code. - */ - if (!found && conn) - conn->put_channel = 1; - return 0; -} - -static int tch_recv_mncc(struct gsm_network *net, 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; - - 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; -} - -/* 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, *old_lchan; - int rc; - struct gsm_network *net; - struct gsm_trans *trans; - - if (subsys != SS_ABISIP) - return 0; - - /* in case we use direct BTS-to-BTS RTP */ - if (ipacc_rtp_direct) - return 0; - - switch (signal) { - case S_ABISIP_CRCX_ACK: - /* in case we don't use direct BTS-to-BTS RTP */ - /* the BTS has successfully bound a TCH to a local ip/port, - * which means we can connect our UDP socket to it */ - if (lchan->abis_ip.rtp_socket) { - rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - } - - lchan->abis_ip.rtp_socket = rtp_socket_create(); - if (!lchan->abis_ip.rtp_socket) - return -EIO; - - rc = rtp_socket_connect(lchan->abis_ip.rtp_socket, - lchan->abis_ip.bound_ip, - lchan->abis_ip.bound_port); - if (rc < 0) - return -EIO; - - /* check if any transactions on this lchan still have - * a tch_recv_mncc request pending */ - net = lchan->ts->trx->bts->network; - llist_for_each_entry(trans, &net->trans_list, entry) { - if (trans->conn && trans->conn->lchan == lchan && trans->tch_recv) { - DEBUGP(DCC, "pending tch_recv_mncc request\n"); - tch_recv_mncc(net, trans->callref, 1); - } - } - - /* - * 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. - */ - old_lchan = bsc_handover_pending(lchan); - if (old_lchan) - switch_for_handover(old_lchan, lchan); - break; - case S_ABISIP_DLCX_IND: - /* the BTS tells us a RTP stream has been disconnected */ - if (lchan->abis_ip.rtp_socket) { - rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - } - - break; - } - - return 0; -} - -/* map two ipaccess RTP streams onto each other */ -static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan) -{ - struct gsm_bts *bts = lchan->ts->trx->bts; - struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts; - int rc; - - DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n", - bts->nr, lchan->ts->trx->nr, lchan->ts->nr, - remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr); - - if (bts->type != remote_bts->type) { - LOGP(DCC, LOGL_ERROR, "Cannot switch calls between different BTS types yet\n"); - return -EINVAL; - } - - // todo: map between different bts types - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMO_SYSMO: - if (!ipacc_rtp_direct) { - /* connect the TCH's to our RTP proxy */ - rc = rsl_ipacc_mdcx_to_rtpsock(lchan); - if (rc < 0) - return rc; - rc = rsl_ipacc_mdcx_to_rtpsock(remote_lchan); - if (rc < 0) - return rc; - /* connect them with each other */ - rtp_socket_proxy(lchan->abis_ip.rtp_socket, - remote_lchan->abis_ip.rtp_socket); - } else { - /* directly connect TCH RTP streams to each other */ - rc = rsl_ipacc_mdcx(lchan, remote_lchan->abis_ip.bound_ip, - remote_lchan->abis_ip.bound_port, - remote_lchan->abis_ip.rtp_payload2); - if (rc < 0) - return rc; - rc = rsl_ipacc_mdcx(remote_lchan, lchan->abis_ip.bound_ip, - lchan->abis_ip.bound_port, - lchan->abis_ip.rtp_payload2); - } - break; - case GSM_BTS_TYPE_BS11: - case GSM_BTS_TYPE_RBS2000: - case GSM_BTS_TYPE_NOKIA_SITE: - trau_mux_map_lchan(lchan, remote_lchan); - break; - default: - LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type); - return -EINVAL; - } - - return 0; -} - -/* bridge channels of two transactions */ -static int tch_bridge(struct gsm_network *net, uint32_t *refs) -{ - struct gsm_trans *trans1 = trans_find_by_callref(net, refs[0]); - struct gsm_trans *trans2 = trans_find_by_callref(net, refs[1]); - - if (!trans1 || !trans2) - return -EIO; - - if (!trans1->conn || !trans2->conn) - return -EIO; - - /* 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; - lchan = trans->conn->lchan; - bts = lchan->ts->trx->bts; - - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMO_SYSMO: - if (ipacc_rtp_direct) { - LOGP(DCC, LOGL_ERROR, "Error: RTP proxy is disabled\n"); - return -EINVAL; - } - /* in case, we don't have a RTP socket yet, we note this - * in the transaction and try later */ - if (!lchan->abis_ip.rtp_socket) { - trans->tch_recv = enable; - DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); - return 0; - } - if (enable) { - /* connect the TCH's to our RTP proxy */ - rc = rsl_ipacc_mdcx_to_rtpsock(lchan); - if (rc < 0) - return rc; - /* assign socket to application interface */ - rtp_socket_upstream(lchan->abis_ip.rtp_socket, - net, callref); - } else - rtp_socket_upstream(lchan->abis_ip.rtp_socket, - net, 0); - break; - case GSM_BTS_TYPE_BS11: - case GSM_BTS_TYPE_RBS2000: - case GSM_BTS_TYPE_NOKIA_SITE: - 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->subscr->net, trans, trans->callref, - l4_location, l4_cause); - trans->callref = 0; - } - - if (disconnect && trans->callref) { - /* process disconnect towards layer 4 */ - mncc_set_cause(&l4_rel, l4_location, l4_cause); - mncc_recvmsg(trans->subscr->net, trans, MNCC_DISC_IND, &l4_rel); - } - - /* process disconnect towards mobile station */ - if (disconnect || release) { - mncc_set_cause(&mo_rel, mo_location, mo_cause); - mo_rel.cause.diag[0] = ((trans->cc.Tcurrent & 0xf00) >> 8) + '0'; - mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0'; - mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0'; - mo_rel.cause.diag_len = 3; - - if (disconnect) - gsm48_cc_tx_disconnect(trans, &mo_rel); - if (release) - gsm48_cc_tx_release(trans, &mo_rel); - } - -} - -static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, - int sec, int micro) -{ - DEBUGP(DCC, "starting timer T%x with %d seconds\n", current, sec); - trans->cc.timer.cb = gsm48_cc_timeout; - trans->cc.timer.data = trans; - osmo_timer_schedule(&trans->cc.timer, sec, micro); - trans->cc.Tcurrent = current; -} - -static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t msg_type = gh->msg_type & 0xbf; - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc setup; - - memset(&setup, 0, sizeof(struct gsm_mncc)); - setup.callref = trans->callref; - if (trans->conn && trans->conn->lchan) - setup.lchan_type = trans->conn->lchan->type; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* emergency setup is identified by msg_type */ - if (msg_type == GSM48_MT_CC_EMERG_SETUP) - setup.emergency = 1; - - /* use subscriber as calling party number */ - setup.fields |= MNCC_F_CALLING; - strncpy(setup.calling.number, trans->subscr->extension, - sizeof(setup.calling.number)-1); - strncpy(setup.imsi, trans->subscr->imsi, - sizeof(setup.imsi)-1); - - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - setup.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&setup.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - setup.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&setup.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* called party bcd number */ - if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { - setup.fields |= MNCC_F_CALLED; - gsm48_decode_called(&setup.called, - TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - setup.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&setup.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - setup.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&setup.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - /* CLIR suppression */ - if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_SUPP)) - setup.clir.sup = 1; - /* CLIR invocation */ - if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_INVOC)) - setup.clir.inv = 1; - /* cc cap */ - if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { - setup.fields |= MNCC_F_CCCAP; - gsm48_decode_cccap(&setup.cccap, - TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); - } - - new_cc_state(trans, GSM_CSTATE_INITIATED); - - LOGP(DCC, LOGL_INFO, "Subscriber %s (%s) sends SETUP to %s\n", - subscr_name(trans->subscr), trans->subscr->extension, - setup.called.number); - - osmo_counter_inc(trans->subscr->net->stats.call.mo_setup); - - /* indicate setup to MNCC */ - mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_IND, &setup); - - /* MNCC code will modify the channel asynchronously, we should - * ipaccess-bind only after the modification has been made to the - * lchan->tch_mode */ - return 0; -} - -static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm_mncc *setup = arg; - int rc, trans_id; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - /* transaction id must not be assigned */ - if (trans->transaction_id != 0xff) { /* unasssigned */ - DEBUGP(DCC, "TX Setup with assigned transaction. " - "This is not allowed!\n"); - /* Temporarily out of order */ - rc = mncc_release_ind(trans->subscr->net, trans, trans->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - trans->callref = 0; - trans_free(trans); - return rc; - } - - /* Get free transaction_id */ - trans_id = trans_assign_trans_id(trans->subscr, GSM48_PDISC_CC, 0); - if (trans_id < 0) { - /* no free transaction ID */ - rc = mncc_release_ind(trans->subscr->net, trans, trans->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - trans->callref = 0; - trans_free(trans); - return rc; - } - trans->transaction_id = trans_id; - - gh->msg_type = GSM48_MT_CC_SETUP; - - gsm48_start_cc_timer(trans, 0x303, GSM48_T303); - - /* bearer capability */ - if (setup->fields & MNCC_F_BEARER_CAP) - gsm48_encode_bearer_cap(msg, 0, &setup->bearer_cap); - /* facility */ - if (setup->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &setup->facility); - /* progress */ - if (setup->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &setup->progress); - /* calling party BCD number */ - if (setup->fields & MNCC_F_CALLING) - gsm48_encode_calling(msg, &setup->calling); - /* called party BCD number */ - if (setup->fields & MNCC_F_CALLED) - gsm48_encode_called(msg, &setup->called); - /* user-user */ - if (setup->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &setup->useruser); - /* redirecting party BCD number */ - if (setup->fields & MNCC_F_REDIRECTING) - gsm48_encode_redirecting(msg, &setup->redirecting); - /* signal */ - if (setup->fields & MNCC_F_SIGNAL) - gsm48_encode_signal(msg, setup->signal); - - new_cc_state(trans, GSM_CSTATE_CALL_PRESENT); - - osmo_counter_inc(trans->subscr->net->stats.call.mt_setup); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc call_conf; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x310, GSM48_T310); - - memset(&call_conf, 0, sizeof(struct gsm_mncc)); - call_conf.callref = trans->callref; - if (trans->conn && trans->conn->lchan) - 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); - } - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - call_conf.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&call_conf.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* cc cap */ - if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { - call_conf.fields |= MNCC_F_CCCAP; - gsm48_decode_cccap(&call_conf.cccap, - TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); - } - - new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_CALL_CONF_IND, - &call_conf); -} - -static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *proceeding = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_CALL_PROC; - - new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC); - - /* bearer capability */ - if (proceeding->fields & MNCC_F_BEARER_CAP) - gsm48_encode_bearer_cap(msg, 0, &proceeding->bearer_cap); - /* facility */ - if (proceeding->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &proceeding->facility); - /* progress */ - if (proceeding->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &proceeding->progress); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc alerting; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x301, GSM48_T301); - - memset(&alerting, 0, sizeof(struct gsm_mncc)); - alerting.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - alerting.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&alerting.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - - /* progress */ - if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { - alerting.fields |= MNCC_F_PROGRESS; - gsm48_decode_progress(&alerting.progress, - TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - alerting.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&alerting.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED); - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_ALERT_IND, - &alerting); -} - -static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *alerting = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_ALERTING; - - /* facility */ - if (alerting->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &alerting->facility); - /* progress */ - if (alerting->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &alerting->progress); - /* user-user */ - if (alerting->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &alerting->useruser); - - new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *progress = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_PROGRESS; - - /* progress */ - gsm48_encode_progress(msg, 1, &progress->progress); - /* user-user */ - if (progress->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &progress->useruser); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *connect = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_CONNECT; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x313, GSM48_T313); - - /* facility */ - if (connect->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &connect->facility); - /* progress */ - if (connect->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &connect->progress); - /* connected number */ - if (connect->fields & MNCC_F_CONNECTED) - gsm48_encode_connected(msg, &connect->connected); - /* user-user */ - if (connect->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &connect->useruser); - - new_cc_state(trans, GSM_CSTATE_CONNECT_IND); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc connect; - - gsm48_stop_cc_timer(trans); - - memset(&connect, 0, sizeof(struct gsm_mncc)); - connect.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* use subscriber as connected party number */ - connect.fields |= MNCC_F_CONNECTED; - strncpy(connect.connected.number, trans->subscr->extension, - sizeof(connect.connected.number)-1); - strncpy(connect.imsi, trans->subscr->imsi, - sizeof(connect.imsi)-1); - - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - connect.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&connect.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - connect.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&connect.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - connect.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&connect.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST); - osmo_counter_inc(trans->subscr->net->stats.call.mt_connect); - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_CNF, &connect); -} - - -static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc connect_ack; - - gsm48_stop_cc_timer(trans); - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - osmo_counter_inc(trans->subscr->net->stats.call.mo_connect_ack); - - memset(&connect_ack, 0, sizeof(struct gsm_mncc)); - connect_ack.callref = trans->callref; - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_COMPL_IND, - &connect_ack); -} - -static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_CONNECT_ACK; - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc disc; - - gsm48_stop_cc_timer(trans); - - new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ); - - memset(&disc, 0, sizeof(struct gsm_mncc)); - disc.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0); - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - disc.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&disc.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - disc.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&disc.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - disc.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&disc.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - disc.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&disc.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_DISC_IND, &disc); - -} - -static struct gsm_mncc_cause default_cause = { - .location = GSM48_CAUSE_LOC_PRN_S_LU, - .coding = 0, - .rec = 0, - .rec_val = 0, - .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, - .diag_len = 0, - .diag = { 0 }, -}; - -static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *disc = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_DISCONNECT; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x306, GSM48_T306); - - /* cause */ - if (disc->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &disc->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - /* facility */ - if (disc->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &disc->facility); - /* progress */ - if (disc->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &disc->progress); - /* user-user */ - if (disc->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &disc->useruser); - - /* store disconnect cause for T306 expiry */ - memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc)); - - new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc rel; - int rc; - - gsm48_stop_cc_timer(trans); - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - rel.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&rel.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - rel.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&rel.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - rel.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&rel.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - rel.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&rel.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) { - /* release collision 5.4.5 */ - rc = mncc_recvmsg(trans->subscr->net, trans, MNCC_REL_CNF, &rel); - } else { - rc = gsm48_tx_simple(trans->conn, - GSM48_PDISC_CC | (trans->transaction_id << 4), - GSM48_MT_CC_RELEASE_COMPL); - rc = mncc_recvmsg(trans->subscr->net, trans, MNCC_REL_IND, &rel); - } - - new_cc_state(trans, GSM_CSTATE_NULL); - - trans->callref = 0; - trans_free(trans); - - return rc; -} - -static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *rel = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_RELEASE; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x308, GSM48_T308); - - /* cause */ - if (rel->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 0, &rel->cause); - /* facility */ - if (rel->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &rel->facility); - /* user-user */ - if (rel->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &rel->useruser); - - trans->cc.T308_second = 0; - memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc)); - - if (trans->cc.state != GSM_CSTATE_RELEASE_REQ) - new_cc_state(trans, GSM_CSTATE_RELEASE_REQ); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc rel; - int rc = 0; - - gsm48_stop_cc_timer(trans); - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - rel.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&rel.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - rel.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&rel.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - rel.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&rel.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - rel.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&rel.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - if (trans->callref) { - switch (trans->cc.state) { - case GSM_CSTATE_CALL_PRESENT: - rc = mncc_recvmsg(trans->subscr->net, trans, - MNCC_REJ_IND, &rel); - break; - case GSM_CSTATE_RELEASE_REQ: - rc = mncc_recvmsg(trans->subscr->net, trans, - MNCC_REL_CNF, &rel); - break; - default: - rc = mncc_recvmsg(trans->subscr->net, trans, - MNCC_REL_IND, &rel); - } - } - - trans->callref = 0; - trans_free(trans); - - return rc; -} - -static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *rel = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - int ret; - - gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; - - trans->callref = 0; - - gsm48_stop_cc_timer(trans); - - /* cause */ - if (rel->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 0, &rel->cause); - /* facility */ - if (rel->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &rel->facility); - /* user-user */ - if (rel->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &rel->useruser); - - ret = gsm48_conn_sendmsg(msg, trans->conn, trans); - - trans_free(trans); - - return ret; -} - -static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc fac; - - memset(&fac, 0, sizeof(struct gsm_mncc)); - fac.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0); - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - fac.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&fac.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - fac.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&fac.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_FACILITY_IND, &fac); -} - -static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *fac = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_FACILITY; - - /* facility */ - gsm48_encode_facility(msg, 1, &fac->facility); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc hold; - - memset(&hold, 0, sizeof(struct gsm_mncc)); - hold.callref = trans->callref; - return mncc_recvmsg(trans->subscr->net, trans, MNCC_HOLD_IND, &hold); -} - -static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_HOLD_ACK; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *hold_rej = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_HOLD_REJ; - - /* cause */ - if (hold_rej->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &hold_rej->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc retrieve; - - memset(&retrieve, 0, sizeof(struct gsm_mncc)); - retrieve.callref = trans->callref; - return mncc_recvmsg(trans->subscr->net, trans, MNCC_RETRIEVE_IND, - &retrieve); -} - -static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_RETR_ACK; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *retrieve_rej = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_RETR_REJ; - - /* cause */ - if (retrieve_rej->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &retrieve_rej->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc dtmf; - - memset(&dtmf, 0, sizeof(struct gsm_mncc)); - dtmf.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* keypad facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { - dtmf.fields |= MNCC_F_KEYPAD; - gsm48_decode_keypad(&dtmf.keypad, - TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); - } - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_START_DTMF_IND, &dtmf); -} - -static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *dtmf = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_START_DTMF_ACK; - - /* keypad */ - if (dtmf->fields & MNCC_F_KEYPAD) - gsm48_encode_keypad(msg, dtmf->keypad); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *dtmf = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_START_DTMF_REJ; - - /* cause */ - if (dtmf->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &dtmf->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc dtmf; - - memset(&dtmf, 0, sizeof(struct gsm_mncc)); - dtmf.callref = trans->callref; - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_STOP_DTMF_IND, &dtmf); -} - -static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc modify; - - memset(&modify, 0, sizeof(struct gsm_mncc)); - modify.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - modify.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&modify.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - } - - new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_IND, &modify); -} - -static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *modify = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_MODIFY; - - gsm48_start_cc_timer(trans, 0x323, GSM48_T323); - - /* bearer capability */ - gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); - - new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc modify; - - gsm48_stop_cc_timer(trans); - - memset(&modify, 0, sizeof(struct gsm_mncc)); - modify.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - modify.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&modify.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - } - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_CNF, &modify); -} - -static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *modify = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; - - /* bearer capability */ - gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc modify; - - gsm48_stop_cc_timer(trans); - - memset(&modify, 0, sizeof(struct gsm_mncc)); - modify.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - modify.fields |= GSM48_IE_BEARER_CAP; - gsm48_decode_bearer_cap(&modify.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - } - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - modify.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&modify.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_REJ, &modify); -} - -static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *modify = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; - - /* bearer capability */ - gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); - /* cause */ - gsm48_encode_cause(msg, 1, &modify->cause); - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *notify = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_NOTIFY; - - /* notify */ - gsm48_encode_notify(msg, notify->notify); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); -// struct tlv_parsed tp; - struct gsm_mncc notify; - - memset(¬ify, 0, sizeof(struct gsm_mncc)); - notify.callref = trans->callref; -// tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len); - if (payload_len >= 1) - gsm48_decode_notify(¬ify.notify, gh->data); - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_NOTIFY_IND, ¬ify); -} - -static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *user = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_USER_INFO; - - /* user-user */ - if (user->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 1, &user->useruser); - /* more data */ - if (user->more) - gsm48_encode_more(msg); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc user; - - memset(&user, 0, sizeof(struct gsm_mncc)); - user.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0); - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - user.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&user.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* more data */ - if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA)) - user.more = 1; - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_USERINFO_IND, &user); -} - -static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *mode = arg; - - return gsm0808_assign_req(trans->conn, mode->lchan_mode, 1); -} - -static struct downstate { - uint32_t states; - int type; - int (*rout) (struct gsm_trans *trans, void *arg); -} downstatelist[] = { - /* mobile originating call establishment */ - {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */ - MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc}, - {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */ - MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, - {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */ - MNCC_SETUP_RSP, gsm48_cc_tx_connect}, - {SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */ - MNCC_PROGRESS_REQ, gsm48_cc_tx_progress}, - /* mobile terminating call establishment */ - {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */ - MNCC_SETUP_REQ, gsm48_cc_tx_setup}, - {SBIT(GSM_CSTATE_CONNECT_REQUEST), - MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack}, - /* signalling during call */ - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_NOTIFY_REQ, gsm48_cc_tx_notify}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), - MNCC_FACILITY_REQ, gsm48_cc_tx_facility}, - {ALL_STATES, - MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack}, - {ALL_STATES, - MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej}, - {ALL_STATES, - MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_MODIFY_REQ, gsm48_cc_tx_modify}, - {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), - MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete}, - {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), - MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo}, - /* clearing */ - {SBIT(GSM_CSTATE_INITIATED), - MNCC_REJ_REQ, gsm48_cc_tx_release_compl}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */ - MNCC_DISC_REQ, gsm48_cc_tx_disconnect}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ - MNCC_REL_REQ, gsm48_cc_tx_release}, - /* special */ - {ALL_STATES, - MNCC_LCHAN_MODIFY, _gsm48_lchan_modify}, -}; - -#define DOWNSLLEN \ - (sizeof(downstatelist) / sizeof(struct downstate)) - - -int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) -{ - int i, rc = 0; - struct gsm_trans *trans = NULL, *transt; - struct gsm_subscriber_connection *conn = NULL; - struct gsm_bts *bts = NULL; - struct gsm_mncc *data = arg, rel; - - DEBUGP(DMNCC, "receive message %s\n", get_mncc_name(msg_type)); - - /* handle special messages */ - switch(msg_type) { - case MNCC_BRIDGE: - return tch_bridge(net, arg); - case MNCC_FRAME_DROP: - return tch_recv_mncc(net, data->callref, 0); - case MNCC_FRAME_RECV: - return tch_recv_mncc(net, data->callref, 1); - case GSM_TCHF_FRAME: - /* Find callref */ - trans = trans_find_by_callref(net, data->callref); - if (!trans) { - LOGP(DMNCC, LOGL_ERROR, "TCH frame for non-existing trans\n"); - return -EIO; - } - if (!trans->conn) { - LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without conn\n"); - return 0; - } - if (trans->conn->lchan->type != GSM_LCHAN_TCH_F) { - /* This should be LOGL_ERROR or NOTICE, but - * unfortuantely it happens for a couple of frames at - * the beginning of every RTP connection */ - LOGP(DMNCC, LOGL_DEBUG, "TCH frame for lchan != TCH_F\n"); - return 0; - } - bts = trans->conn->lchan->ts->trx->bts; - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMO_SYSMO: - if (!trans->conn->lchan->abis_ip.rtp_socket) { - DEBUGP(DMNCC, "TCH frame to lchan without RTP connection\n"); - return 0; - } - return rtp_send_frame(trans->conn->lchan->abis_ip.rtp_socket, arg); - case GSM_BTS_TYPE_BS11: - case GSM_BTS_TYPE_RBS2000: - case GSM_BTS_TYPE_NOKIA_SITE: - return trau_send_frame(trans->conn->lchan, arg); - default: - LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type); - } - return -EINVAL; - } - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = data->callref; - - /* Find callref */ - trans = trans_find_by_callref(net, data->callref); - - /* Callref unknown */ - if (!trans) { - struct gsm_subscriber *subscr; - - if (msg_type != MNCC_SETUP_REQ) { - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC with " - "unknown callref %d\n", data->called.number, - get_mncc_name(msg_type), data->callref); - /* Invalid call reference */ - return mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_INVAL_TRANS_ID); - } - if (!data->called.number[0] && !data->imsi[0]) { - DEBUGP(DCC, "(bts - trx - ts - ti) " - "Received '%s' from MNCC with " - "no number or IMSI\n", get_mncc_name(msg_type)); - /* Invalid number */ - return mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_INV_NR_FORMAT); - } - /* New transaction due to setup, find subscriber */ - if (data->called.number[0]) - subscr = subscr_get_by_extension(net, - data->called.number); - else - subscr = subscr_get_by_imsi(net, data->imsi); - /* If subscriber is not found */ - if (!subscr) { - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC with " - "unknown subscriber %s\n", data->called.number, - get_mncc_name(msg_type), data->called.number); - /* Unknown subscriber */ - return mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_UNASSIGNED_NR); - } - /* If subscriber is not "attached" */ - if (!subscr->lac) { - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC with " - "detached subscriber %s\n", data->called.number, - get_mncc_name(msg_type), data->called.number); - subscr_put(subscr); - /* Temporarily out of order */ - return mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_DEST_OOO); - } - /* Create transaction */ - trans = trans_alloc(subscr, GSM48_PDISC_CC, 0xff, data->callref); - if (!trans) { - DEBUGP(DCC, "No memory for trans.\n"); - subscr_put(subscr); - /* Ressource unavailable */ - mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - return -ENOMEM; - } - /* Find lchan */ - conn = connection_for_subscr(subscr); - - /* If subscriber has no lchan */ - if (!conn) { - /* find transaction with this subscriber already paging */ - llist_for_each_entry(transt, &net->trans_list, entry) { - /* Transaction of our lchan? */ - if (transt == trans || - transt->subscr != subscr) - continue; - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC with " - "unallocated channel, paging already " - "started for lac %d.\n", - data->called.number, - get_mncc_name(msg_type), subscr->lac); - subscr_put(subscr); - trans_free(trans); - return 0; - } - /* store setup informations until paging was successfull */ - memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc)); - - /* Get a channel */ - trans->paging_request = talloc_zero(subscr->net, struct gsm_network*); - if (!trans->paging_request) { - LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n"); - subscr_put(subscr); - trans_free(trans); - return 0; - } - - *trans->paging_request = subscr->net; - subscr_get_channel(subscr, RSL_CHANNEED_TCH_F, setup_trig_pag_evt, trans->paging_request); - - subscr_put(subscr); - return 0; - } - /* Assign lchan */ - trans->conn = conn; - subscr_put(subscr); - } - - if (trans->conn) - conn = trans->conn; - - /* if paging did not respond yet */ - if (!conn) { - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC in paging state\n", - (trans->subscr)?(trans->subscr->extension):"-", - get_mncc_name(msg_type)); - mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_NORM_CALL_CLEAR); - if (msg_type == MNCC_REL_REQ) - rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); - else - rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); - trans->callref = 0; - trans_free(trans); - return rc; - } - - DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) " - "Received '%s' from MNCC in state %d (%s)\n", - conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr, - trans->transaction_id, - (trans->conn->subscr)?(trans->conn->subscr->extension):"-", - get_mncc_name(msg_type), trans->cc.state, - gsm48_cc_state_name(trans->cc.state)); - - /* Find function for current state and message */ - for (i = 0; i < DOWNSLLEN; i++) - if ((msg_type == downstatelist[i].type) - && ((1 << trans->cc.state) & downstatelist[i].states)) - break; - if (i == DOWNSLLEN) { - DEBUGP(DCC, "Message unhandled at this state.\n"); - return 0; - } - - rc = downstatelist[i].rout(trans, arg); - - return rc; -} - - -static struct datastate { - uint32_t states; - int type; - int (*rout) (struct gsm_trans *trans, struct msgb *msg); -} datastatelist[] = { - /* mobile originating call establishment */ - {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ - GSM48_MT_CC_SETUP, gsm48_cc_rx_setup}, - {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ - GSM48_MT_CC_EMERG_SETUP, gsm48_cc_rx_setup}, - {SBIT(GSM_CSTATE_CONNECT_IND), /* 5.2.1.2 */ - GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack}, - /* mobile terminating call establishment */ - {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.2 */ - GSM48_MT_CC_CALL_CONF, gsm48_cc_rx_call_conf}, - {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* ???? | 5.2.2.3.2 */ - GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting}, - {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */ - GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect}, - /* signalling during call */ - {ALL_STATES - SBIT(GSM_CSTATE_NULL), - GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify}, - {ALL_STATES, - GSM48_MT_CC_START_DTMF, gsm48_cc_rx_start_dtmf}, - {ALL_STATES, - GSM48_MT_CC_STOP_DTMF, gsm48_cc_rx_stop_dtmf}, - {ALL_STATES, - GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_HOLD, gsm48_cc_rx_hold}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_RETR, gsm48_cc_rx_retrieve}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify}, - {SBIT(GSM_CSTATE_MO_TERM_MODIFY), - GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete}, - {SBIT(GSM_CSTATE_MO_TERM_MODIFY), - GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo}, - /* clearing */ - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ - GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.4.1.2.2 */ - GSM48_MT_CC_RELEASE, gsm48_cc_rx_release}, - {ALL_STATES, /* 5.4.3.4 */ - GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl}, -}; - -#define DATASLLEN \ - (sizeof(datastatelist) / sizeof(struct datastate)) - -static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t msg_type = gh->msg_type & 0xbf; - uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */ - struct gsm_trans *trans = NULL; - int i, rc = 0; - - if (msg_type & 0x80) { - DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type); - return -EINVAL; - } - - /* Find transaction */ - trans = trans_find_by_id(conn->subscr, GSM48_PDISC_CC, transaction_id); - - DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " - "Received '%s' from MS in state %d (%s)\n", - conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr, - transaction_id, (conn->subscr)?(conn->subscr->extension):"-", - gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0, - gsm48_cc_state_name(trans?(trans->cc.state):0)); - - /* Create transaction */ - if (!trans) { - DEBUGP(DCC, "Unknown transaction ID %x, " - "creating new trans.\n", transaction_id); - /* Create transaction */ - trans = trans_alloc(conn->subscr, GSM48_PDISC_CC, - transaction_id, new_callref++); - if (!trans) { - DEBUGP(DCC, "No memory for trans.\n"); - rc = gsm48_tx_simple(conn, - GSM48_PDISC_CC | (transaction_id << 4), - GSM48_MT_CC_RELEASE_COMPL); - return -ENOMEM; - } - /* Assign transaction */ - trans->conn = conn; - } - - /* find function for current state and message */ - for (i = 0; i < DATASLLEN; i++) - if ((msg_type == datastatelist[i].type) - && ((1 << trans->cc.state) & datastatelist[i].states)) - break; - if (i == DATASLLEN) { - DEBUGP(DCC, "Message unhandled at this state.\n"); - return 0; - } - - assert(trans->subscr); - - rc = datastatelist[i].rout(trans, msg); - - return rc; -} - -/* Create a dummy to wait five seconds */ -static void release_anchor(struct gsm_subscriber_connection *conn) -{ - if (!conn->anch_operation) - return; - - osmo_timer_del(&conn->anch_operation->timeout); - talloc_free(conn->anch_operation); - conn->anch_operation = NULL; -} - -static void anchor_timeout(void *_data) -{ - struct gsm_subscriber_connection *con = _data; - - release_anchor(con); - msc_release_connection(con); -} - -int gsm0408_new_conn(struct gsm_subscriber_connection *conn) -{ - conn->anch_operation = talloc_zero(conn, struct gsm_anchor_operation); - if (!conn->anch_operation) - return -1; - - conn->anch_operation->timeout.data = conn; - conn->anch_operation->timeout.cb = anchor_timeout; - osmo_timer_schedule(&conn->anch_operation->timeout, 5, 0); - return 0; -} - -/* here we get data from the BSC level... */ -int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gh->proto_discr & 0x0f; - int rc = 0; - - if (silent_call_reroute(conn, msg)) - return silent_call_rx(conn, msg); - - switch (pdisc) { - case GSM48_PDISC_CC: - release_anchor(conn); - rc = gsm0408_rcv_cc(conn, msg); - break; - case GSM48_PDISC_MM: - rc = gsm0408_rcv_mm(conn, msg); - break; - case GSM48_PDISC_RR: - rc = gsm0408_rcv_rr(conn, msg); - break; - case GSM48_PDISC_SMS: - release_anchor(conn); - rc = gsm0411_rcv_sms(conn, msg); - break; - case GSM48_PDISC_MM_GPRS: - case GSM48_PDISC_SM_GPRS: - LOGP(DRLL, LOGL_NOTICE, "Unimplemented " - "GSM 04.08 discriminator 0x%02x\n", pdisc); - break; - case GSM48_PDISC_NC_SS: - release_anchor(conn); - rc = handle_rcv_ussd(conn, msg); - break; - default: - LOGP(DRLL, LOGL_NOTICE, "Unknown " - "GSM 04.08 discriminator 0x%02x\n", pdisc); - break; - } - - return rc; -} - -/* - * This will be ran by the linker when loading the DSO. We use it to - * do system initialization, e.g. registration of signal handlers. - */ -static __attribute__((constructor)) void on_dso_load_0408(void) -{ - osmo_signal_register_handler(SS_ABISIP, handle_abisip_signal, NULL); -} diff --git a/openbsc/src/libmsc/gsm_04_11.c b/openbsc/src/libmsc/gsm_04_11.c deleted file mode 100644 index 2fc250bc8..000000000 --- a/openbsc/src/libmsc/gsm_04_11.c +++ /dev/null @@ -1,1004 +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 - -#ifdef BUILD_SMPP -#include "smpp_smsc.h" -extern int smpp_try_deliver(struct gsm_sms *sms, - struct gsm_subscriber_connection *conn); -#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->sender) - subscr_put(sms->sender); - if (sms->receiver) - subscr_put(sms->receiver); -#ifdef BUILD_SMPP - if (sms->smpp.esme) - smpp_esme_put(sms->smpp.esme); -#endif - - talloc_free(sms); -} - -struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, - struct gsm_subscriber *sender, - int dcs, const char *text) -{ - struct gsm_sms *sms = sms_alloc(); - - if (!sms) - return NULL; - - sms->receiver = subscr_get(receiver); - strncpy(sms->text, text, sizeof(sms->text)-1); - - sms->sender = subscr_get(sender); - strncpy(sms->src.addr, sms->sender->extension, sizeof(sms->src.addr)-1); - sms->reply_path_req = 0; - sms->status_rep_req = 0; - sms->ud_hdr_ind = 0; - sms->protocol_id = 0; /* implicit */ - sms->data_coding_scheme = dcs; - strncpy(sms->dst.addr, receiver->extension, sizeof(sms->dst.addr)-1); - /* Generate user_data */ - sms->user_data_len = gsm_7bit_encode(sms->user_data, sms->text); - - return sms; -} - - -static void send_signal(int sig_no, - struct gsm_trans *trans, - struct gsm_sms *sms, - int paging_result) -{ - struct sms_signal_data sig; - sig.trans = trans; - sig.sms = sms; - sig.paging_result = paging_result; - 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 a TPDU derived from struct gsm_sms, - * returns total size of TPDU */ -static int gsm340_gen_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; -} - -/* process an incoming TPDU (called from RP-DATA) - * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */ -static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - uint8_t *smsp = msgb_sms(msg); - struct gsm_sms *gsms; - unsigned int sms_alphabet; - uint8_t sms_mti, sms_mms, sms_vpf, sms_rp; - uint8_t *sms_vp; - uint8_t da_len_bytes; - uint8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */ - int rc = 0; - - osmo_counter_inc(conn->bts->network->stats.sms.submitted); - - gsms = sms_alloc(); - if (!gsms) - return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - - /* invert those fields where 0 means active/present */ - sms_mti = *smsp & 0x03; - sms_mms = !!(*smsp & 0x04); - sms_vpf = (*smsp & 0x18) >> 3; - gsms->status_rep_req = (*smsp & 0x20); - gsms->ud_hdr_ind = (*smsp & 0x40); - sms_rp = (*smsp & 0x80); - - smsp++; - gsms->msg_ref = *smsp++; - - /* length in bytes of the destination address */ - da_len_bytes = 2 + *smsp/2 + *smsp%2; - if (da_len_bytes > 12) { - LOGP(DLSMS, LOGL_ERROR, "Destination Address > 12 bytes ?!?\n"); - rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; - goto out; - } else if (da_len_bytes < 4) { - LOGP(DLSMS, LOGL_ERROR, "Destination Address < 4 bytes ?!?\n"); - rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; - goto out; - } - memset(address_lv, 0, sizeof(address_lv)); - memcpy(address_lv, smsp, da_len_bytes); - /* mangle first byte to reflect length in bytes, not digits */ - address_lv[0] = da_len_bytes - 1; - - gsms->dst.ton = (address_lv[1] >> 4) & 7; - gsms->dst.npi = address_lv[1] & 0xF; - /* convert to real number */ - gsm48_decode_bcd_number(gsms->dst.addr, - sizeof(gsms->dst.addr), address_lv, 1); - smsp += da_len_bytes; - - gsms->protocol_id = *smsp++; - gsms->data_coding_scheme = *smsp++; - - sms_alphabet = gsm338_get_sms_alphabet(gsms->data_coding_scheme); - if (sms_alphabet == 0xffffffff) { - sms_free(gsms); - return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - } - - switch (sms_vpf) { - case GSM340_TP_VPF_RELATIVE: - sms_vp = smsp++; - break; - case GSM340_TP_VPF_ABSOLUTE: - case GSM340_TP_VPF_ENHANCED: - sms_vp = smsp; - /* the additional functionality indicator... */ - if (sms_vpf == GSM340_TP_VPF_ENHANCED && *smsp & (1<<7)) - smsp++; - smsp += 7; - break; - case GSM340_TP_VPF_NONE: - sms_vp = 0; - break; - default: - LOGP(DLSMS, LOGL_NOTICE, - "SMS Validity period not implemented: 0x%02x\n", sms_vpf); - return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - } - gsms->user_data_len = *smsp++; - if (gsms->user_data_len) { - memcpy(gsms->user_data, smsp, gsms->user_data_len); - - switch (sms_alphabet) { - case DCS_7BIT_DEFAULT: - gsm_7bit_decode(gsms->text, smsp, gsms->user_data_len); - break; - case DCS_8BIT_DATA: - case DCS_UCS2: - case DCS_NONE: - break; - } - } - - gsms->sender = subscr_get(conn->subscr); - - LOGP(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(gsms->sender), 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); - - /* determine gsms->receiver based on dialled number */ - gsms->receiver = subscr_get_by_extension(conn->bts->network, gsms->dst.addr); - if (!gsms->receiver) { -#ifdef BUILD_SMPP - rc = smpp_try_deliver(gsms, conn); - if (rc == 1) { - rc = 1; /* cause 1: unknown subscriber */ - osmo_counter_inc(conn->bts->network->stats.sms.no_receiver); - } else if (rc < 0) { - rc = 21; /* cause 21: short message transfer rejected */ - /* FIXME: handle the error somehow? */ - } -#else - rc = 1; /* cause 1: unknown subscriber */ - osmo_counter_inc(conn->bts->network->stats.sms.no_receiver); -#endif - goto out; - } - - switch (sms_mti) { - case GSM340_SMS_SUBMIT_MS2SC: - /* MS is submitting a SMS */ - rc = gsm340_rx_sms_submit(msg, gsms); - break; - case GSM340_SMS_COMMAND_MS2SC: - case GSM340_SMS_DELIVER_REP_MS2SC: - LOGP(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; - -out: - sms_free(gsms); - - return rc; -} - -/* Prefix msg with a RP-DATA header and send as SMR DATA */ -static int gsm411_rp_sendmsg(struct gsm411_smr_inst *inst, struct msgb *msg, - uint8_t rp_msg_type, uint8_t rp_msg_ref, - int rl_msg_type) -{ - struct gsm411_rp_hdr *rp; - uint8_t len = msg->len; - - /* GSM 04.11 RP-DATA header */ - rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp)); - rp->len = len + 2; - rp->msg_type = rp_msg_type; - rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */ - - return gsm411_smr_send(inst, rl_msg_type, msg); -} - -static int gsm411_send_rp_ack(struct gsm_trans *trans, uint8_t msg_ref) -{ - struct msgb *msg = gsm411_msgb_alloc(); - - DEBUGP(DLSMS, "TX: SMS RP ACK\n"); - - return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, GSM411_MT_RP_ACK_MT, - msg_ref, GSM411_SM_RL_REPORT_REQ); -} - -static int gsm411_send_rp_error(struct gsm_trans *trans, - uint8_t msg_ref, uint8_t cause) -{ - struct msgb *msg = gsm411_msgb_alloc(); - - msgb_tv_put(msg, 1, cause); - - LOGP(DLSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause, - get_value_string(gsm411_rp_cause_strs, cause)); - - return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, - GSM411_MT_RP_ERROR_MT, msg_ref, GSM411_SM_RL_REPORT_REQ); -} - -/* Receive a 04.11 TPDU inside RP-DATA / user data */ -static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph, - uint8_t src_len, uint8_t *src, - uint8_t dst_len, uint8_t *dst, - uint8_t tpdu_len, uint8_t *tpdu) -{ - int rc = 0; - - if (src_len && src) - LOGP(DLSMS, LOGL_ERROR, "RP-DATA (MO) with SRC ?!?\n"); - - if (!dst_len || !dst || !tpdu_len || !tpdu) { - LOGP(DLSMS, LOGL_ERROR, - "RP-DATA (MO) without DST or TPDU ?!?\n"); - gsm411_send_rp_error(trans, rph->msg_ref, - GSM411_RP_CAUSE_INV_MAND_INF); - return -EIO; - } - msg->l4h = tpdu; - - DEBUGP(DLSMS, "DST(%u,%s)\n", dst_len, osmo_hexdump(dst, dst_len)); - - rc = gsm340_rx_tpdu(trans->conn, msg); - if (rc == 0) - return gsm411_send_rp_ack(trans, rph->msg_ref); - else if (rc > 0) - return gsm411_send_rp_error(trans, rph->msg_ref, rc); - else - return rc; -} - -/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */ -static int gsm411_rx_rp_data(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - uint8_t src_len, dst_len, rpud_len; - uint8_t *src = NULL, *dst = NULL , *rp_ud = NULL; - - /* in the MO case, this should always be zero length */ - src_len = rph->data[0]; - if (src_len) - src = &rph->data[1]; - - dst_len = rph->data[1+src_len]; - if (dst_len) - dst = &rph->data[1+src_len+1]; - - rpud_len = rph->data[1+src_len+1+dst_len]; - if (rpud_len) - rp_ud = &rph->data[1+src_len+1+dst_len+1]; - - DEBUGP(DLSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n", - src_len, dst_len, rpud_len); - return gsm411_rx_rp_ud(msg, trans, rph, src_len, src, dst_len, dst, - rpud_len, rp_ud); -} - -/* Receive a 04.11 RP-ACK message (response to RP-DATA from us) */ -static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - struct gsm_sms *sms = trans->sms.sms; - - /* Acnkowledgement to MT RP_DATA, i.e. the MS confirms it - * successfully received a SMS. We can now safely mark it as - * transmitted */ - - if (!sms) { - LOGP(DLSMS, LOGL_ERROR, "RX RP-ACK but no sms in transaction?!?\n"); - return gsm411_send_rp_error(trans, rph->msg_ref, - GSM411_RP_CAUSE_PROTOCOL_ERR); - } - - /* mark this SMS as sent in database */ - db_sms_mark_sent(sms); - - send_signal(S_SMS_DELIVERED, trans, sms, 0); - - sms_free(sms); - trans->sms.sms = NULL; - - /* check for more messages for this subscriber */ - sms = db_sms_get_unsent_for_subscr(trans->subscr); - if (sms) - gsm411_send_sms(trans->conn, sms); - - return 0; -} - -static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - struct gsm_network *net = trans->conn->bts->network; - struct gsm_sms *sms = trans->sms.sms; - uint8_t cause_len = rph->data[0]; - uint8_t cause = rph->data[1]; - - /* Error in response to MT RP_DATA, i.e. the MS did not - * successfully receive the SMS. We need to investigate - * the cause and take action depending on it */ - - LOGP(DLSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n", - subscr_name(trans->conn->subscr), cause_len, cause, - get_value_string(gsm411_rp_cause_strs, cause)); - - if (!sms) { - LOGP(DLSMS, LOGL_ERROR, - "RX RP-ERR, but no sms in transaction?!?\n"); - return -EINVAL; -#if 0 - return gsm411_send_rp_error(trans, rph->msg_ref, - GSM411_RP_CAUSE_PROTOCOL_ERR); -#endif - } - - if (cause == GSM411_RP_CAUSE_MT_MEM_EXCEEDED) { - /* MS has not enough memory to store the message. We need - * to store this in our database and wait for a SMMA message */ - /* FIXME */ - send_signal(S_SMS_MEM_EXCEEDED, trans, sms, 0); - osmo_counter_inc(net->stats.sms.rp_err_mem); - } else { - send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); - osmo_counter_inc(net->stats.sms.rp_err_other); - } - - sms_free(sms); - trans->sms.sms = NULL; - - return 0; -} - -static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - struct gsm_sms *sms; - 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); - - /* check for more messages for this subscriber */ - sms = db_sms_get_unsent_for_subscr(trans->subscr); - if (sms) - gsm411_send_sms(trans->conn, sms); - - return rc; -} - -/* receive RL DATA */ -static int gsm411_rx_rl_data(struct msgb *msg, struct gsm48_hdr *gh, - struct gsm_trans *trans) -{ - struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; - uint8_t msg_type = rp_data->msg_type & 0x07; - int rc = 0; - - switch (msg_type) { - case GSM411_MT_RP_DATA_MO: - DEBUGP(DLSMS, "RX SMS RP-DATA (MO)\n"); - rc = gsm411_rx_rp_data(msg, trans, rp_data); - break; - case GSM411_MT_RP_SMMA_MO: - DEBUGP(DLSMS, "RX SMS RP-SMMA\n"); - rc = gsm411_rx_rp_smma(msg, trans, rp_data); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); - rc = -EINVAL; - break; - } - - return rc; -} - -/* receive RL REPORT */ -static int gsm411_rx_rl_report(struct msgb *msg, struct gsm48_hdr *gh, - struct gsm_trans *trans) -{ - struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; - uint8_t msg_type = rp_data->msg_type & 0x07; - int rc = 0; - - switch (msg_type) { - case GSM411_MT_RP_ACK_MO: - DEBUGP(DLSMS, "RX SMS RP-ACK (MO)\n"); - rc = gsm411_rx_rp_ack(msg, trans, rp_data); - break; - case GSM411_MT_RP_ERROR_MO: - DEBUGP(DLSMS, "RX SMS RP-ERROR (MO)\n"); - rc = gsm411_rx_rp_error(msg, trans, rp_data); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); - rc = -EINVAL; - break; - } - - return rc; -} - -/* receive SM-RL sap message from SMR - * NOTE: Message is freed by sender - */ -int gsm411_rl_recv(struct gsm411_smr_inst *inst, int msg_type, - struct msgb *msg) -{ - struct gsm_trans *trans = - container_of(inst, struct gsm_trans, sms.smr_inst); - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (msg_type) { - case GSM411_SM_RL_DATA_IND: - rc = gsm411_rx_rl_data(msg, gh, trans); - break; - case GSM411_SM_RL_REPORT_IND: - if (gh) - rc = gsm411_rx_rl_report(msg, gh, trans); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Unhandled SM-RL message 0x%x\n", msg_type); - rc = -EINVAL; - } - - return rc; -} - -/* receive MNCCSMS sap message from SMC - * NOTE: Message is freed by sender - */ -static int gsm411_mn_recv(struct gsm411_smc_inst *inst, int msg_type, - struct msgb *msg) -{ - struct gsm_trans *trans = - container_of(inst, struct gsm_trans, sms.smc_inst); - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (msg_type) { - case GSM411_MNSMS_EST_IND: - case GSM411_MNSMS_DATA_IND: - DEBUGP(DLSMS, "MNSMS-DATA/EST-IND\n"); - rc = gsm411_smr_recv(&trans->sms.smr_inst, msg_type, msg); - break; - case GSM411_MNSMS_ERROR_IND: - if (gh) - DEBUGP(DLSMS, "MNSMS-ERROR-IND, cause %d (%s)\n", - gh->data[0], - get_value_string(gsm411_cp_cause_strs, - gh->data[0])); - else - DEBUGP(DLSMS, "MNSMS-ERROR-IND, no cause\n"); - rc = gsm411_smr_recv(&trans->sms.smr_inst, msg_type, msg); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Unhandled MNCCSMS msg 0x%x\n", msg_type); - rc = -EINVAL; - } - - return rc; -} - -/* Entry point for incoming GSM48_PDISC_SMS from abis_rsl.c */ -int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t msg_type = gh->msg_type; - uint8_t transaction_id = ((gh->proto_discr >> 4) ^ 0x8); /* flip */ - struct gsm_trans *trans; - int new_trans = 0; - int rc = 0; - - if (!conn->subscr) - return -EIO; - /* FIXME: send some error message */ - - DEBUGP(DLSMS, "receiving data (trans_id=%x)\n", transaction_id); - trans = trans_find_by_id(conn->subscr, 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->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->subscr, - 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 = 42; - int transaction_id; - int rc; - - transaction_id = - trans_assign_trans_id(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); - return -EBUSY; - } - - DEBUGP(DLSMS, "send_sms_lchan()\n"); - - /* FIXME: allocate transaction with message reference */ - trans = trans_alloc(conn->subscr, GSM48_PDISC_SMS, - transaction_id, new_callref++); - if (!trans) { - LOGP(DLSMS, LOGL_ERROR, "No memory for trans\n"); - send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0); - sms_free(sms); - /* FIXME: send some error message */ - return -ENOMEM; - } - 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 TPDU */ - rc = gsm340_gen_tpdu(msg, sms); - if (rc < 0) { - send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); - sms_free(sms); - trans->sms.sms = NULL; - trans_free(trans); - msgb_free(msg); - return rc; - } - - *rp_ud_len = rc; - - DEBUGP(DLSMS, "TX: SMS DELIVER\n"); - - osmo_counter_inc(conn->bts->network->stats.sms.delivered); - db_sms_inc_deliver_attempts(trans->sms.sms); - - return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, - GSM411_MT_RP_DATA_MT, msg_ref, GSM411_SM_RL_DATA_REQ); -} - -/* paging callback. Here we get called if paging a subscriber has - * succeeded or failed. */ -static int paging_cb_send_sms(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *_conn, void *_sms) -{ - struct gsm_subscriber_connection *conn = _conn; - struct gsm_sms *sms = _sms; - int rc = 0; - - DEBUGP(DLSMS, "paging_cb_send_sms(hooknum=%u, event=%u, msg=%p," - "conn=%p, sms=%p/id: %llu)\n", hooknum, event, msg, conn, sms, sms->id); - - if (hooknum != GSM_HOOK_RR_PAGING) - return -EINVAL; - - switch (event) { - case GSM_PAGING_SUCCEEDED: - gsm411_send_sms(conn, sms); - break; - case GSM_PAGING_EXPIRED: - case GSM_PAGING_OOM: - case GSM_PAGING_BUSY: - send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, event); - sms_free(sms); - rc = -ETIMEDOUT; - break; - default: - LOGP(DLSMS, LOGL_ERROR, "Unhandled paging event: %d\n", event); - } - - return rc; -} - -/* high-level function to send a SMS to a given subscriber. The function - * will take care of paging the subscriber, establishing the RLL SAPI3 - * connection, etc. */ -int gsm411_send_sms_subscr(struct gsm_subscriber *subscr, - struct gsm_sms *sms) -{ - struct gsm_subscriber_connection *conn; - - /* check if we already have an open lchan to the subscriber. - * if yes, send the SMS this way */ - conn = connection_for_subscr(subscr); - if (conn) { - return gsm411_send_sms(conn, sms); - } - - /* if not, we have to start paging */ - subscr_get_channel(subscr, RSL_CHANNEED_SDCCH, paging_cb_send_sms, sms); - return 0; -} - -void _gsm411_sms_trans_free(struct gsm_trans *trans) -{ - /* cleanup SMS instance */ - gsm411_smr_clear(&trans->sms.smr_inst); - trans->sms.smr_inst.rl_recv = NULL; - trans->sms.smr_inst.mn_send = NULL; - - gsm411_smc_clear(&trans->sms.smc_inst); - trans->sms.smc_inst.mn_recv = NULL; - trans->sms.smc_inst.mm_send = NULL; - - if (trans->sms.sms) { - LOGP(DLSMS, LOGL_ERROR, "Transaction contains SMS.\n"); - send_signal(S_SMS_UNKNOWN_ERROR, trans, trans->sms.sms, 0); - sms_free(trans->sms.sms); - trans->sms.sms = NULL; - } -} - -void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn) -{ - struct gsm_network *net; - struct gsm_trans *trans, *tmp; - - net = conn->bts->network; - - llist_for_each_entry_safe(trans, tmp, &net->trans_list, entry) - if (trans->conn == conn) { - struct gsm_sms *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 39738a5ee..000000000 --- a/openbsc/src/libmsc/gsm_04_80.c +++ /dev/null @@ -1,175 +0,0 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ - -/* (C) 2008-2009 by Harald Welte - * (C) 2008, 2009, 2010 by Holger Hans Peter Freyther - * (C) 2009 by Mike Haben - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, uint8_t tag) -{ - uint8_t *data = msgb_push(msgb, 2); - - data[0] = tag; - data[1] = msgb->len - 2; - return data; -} - -static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag, - uint8_t value) -{ - uint8_t *data = msgb_push(msgb, 3); - - data[0] = tag; - data[1] = 1; - data[2] = value; - return data; -} - - -/* Send response to a mobile-originated ProcessUnstructuredSS-Request */ -int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, - const struct msgb *in_msg, const char *response_text, - const struct ussd_request *req) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - uint8_t *ptr8; - int response_len; - - /* First put the payload text into the message */ - ptr8 = msgb_put(msg, 0); - response_len = gsm_7bit_encode(ptr8, response_text); - msgb_put(msg, response_len); - - /* Then wrap it as an Octet String */ - msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG); - - /* Pre-pend the DCS octet string */ - msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F); - - /* Then wrap these as a Sequence */ - msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); - - /* Pre-pend the operation code */ - msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, - GSM0480_OP_CODE_PROCESS_USS_REQ); - - /* Wrap the operation code and IA5 string as a sequence */ - msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); - - /* Pre-pend the invoke ID */ - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); - - /* Wrap this up as a Return Result component */ - msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); - - /* Wrap the component in a Facility message */ - msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); - - /* And finally pre-pend the L3 header */ - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id - | (1<<7); /* TI direction = 1 */ - gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, - const struct msgb *in_msg, - const struct ussd_request *req) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - /* First insert the problem code */ - msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL, - GSM_0480_GEN_PROB_CODE_UNRECOGNISED); - - /* Before it insert the invoke ID */ - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); - - /* Wrap this up as a Reject component */ - msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT); - - /* Wrap the component in a Facility message */ - msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); - - /* And finally pre-pend the L3 header */ - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_NC_SS; - gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */ - gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn, int level, const char *text) -{ - struct gsm48_hdr *gh; - struct msgb *msg; - - msg = gsm0480_create_unstructuredSS_Notify(level, text); - if (!msg) - return -1; - - gsm0480_wrap_invoke(msg, GSM0480_OP_CODE_USS_NOTIFY, 0); - gsm0480_wrap_facility(msg); - - /* And finally pre-pend the L3 header */ - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_NC_SS; - gh->msg_type = GSM0480_MTYPE_REGISTER; - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn) -{ - struct gsm48_hdr *gh; - struct msgb *msg; - - msg = gsm48_msgb_alloc(); - if (!msg) - return -1; - - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_NC_SS; - gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} diff --git a/openbsc/src/libmsc/gsm_subscriber.c b/openbsc/src/libmsc/gsm_subscriber.c deleted file mode 100644 index 5ace8f666..000000000 --- a/openbsc/src/libmsc/gsm_subscriber.c +++ /dev/null @@ -1,472 +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 - -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; - - /* back reference */ - struct gsm_subscriber *subscr; - - /* the requested channel type */ - int channel_type; - - /* what did we do */ - int state; - - /* the callback data */ - gsm_cbfn *cbfn; - void *param; -}; - -enum { - REQ_STATE_INITIAL, - REQ_STATE_QUEUED, - REQ_STATE_PAGED, - REQ_STATE_FAILED_START, - REQ_STATE_DISPATCHED, -}; - -/* - * We got the channel assigned and can now hand this channel - * over to one of our callbacks. - */ -static int subscr_paging_dispatch(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct subscr_request *request; - struct gsm_subscriber_connection *conn = data; - struct gsm_subscriber *subscr = param; - struct paging_signal_data sig_data; - - /* There is no request anymore... */ - if (llist_empty(&subscr->requests)) - return -1; - - /* Dispatch signal */ - sig_data.subscr = subscr; - sig_data.bts = conn ? conn->bts : NULL; - sig_data.conn = conn; - sig_data.paging_result = event; - osmo_signal_dispatch( - SS_PAGING, - event == GSM_PAGING_SUCCEEDED ? - S_PAGING_SUCCEEDED : S_PAGING_EXPIRED, - &sig_data - ); - - /* - * FIXME: What to do with paging requests coming during - * this callback? We must be sure to not start paging when - * we have an active connection to a subscriber and to make - * the subscr_put_channel work as required... - */ - request = (struct subscr_request *)subscr->requests.next; - request->state = REQ_STATE_DISPATCHED; - llist_del(&request->entry); - subscr->in_callback = 1; - request->cbfn(hooknum, event, msg, data, request->param); - subscr->in_callback = 0; - - if (event != GSM_PAGING_SUCCEEDED) { - /* - * This is a workaround for a bigger issue. We have - * issued paging that might involve multiple BTSes - * and one of them have failed now. We will stop the - * other paging requests as well as the next timeout - * would work on the next paging request and the queue - * will do bad things. This should be fixed by counting - * the outstanding results. - */ - paging_request_stop(NULL, subscr, NULL, NULL); - subscr_put_channel(subscr); - } - - subscr_put(subscr); - talloc_free(request); - return 0; -} - -static int subscr_paging_sec_cb(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - int rc; - - switch (event) { - case GSM_SECURITY_AUTH_FAILED: - /* Dispatch as paging failure */ - rc = subscr_paging_dispatch( - GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, - msg, data, param); - break; - - case GSM_SECURITY_NOAVAIL: - case GSM_SECURITY_SUCCEEDED: - /* Dispatch as paging failure */ - rc = subscr_paging_dispatch( - GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, - msg, data, param); - break; - - default: - rc = -EINVAL; - } - - return rc; -} - -static int subscr_paging_cb(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct gsm_subscriber_connection *conn = data; - struct gsm48_hdr *gh; - struct gsm48_pag_resp *pr; - - /* Other cases mean problem, dispatch direclty */ - if (event != GSM_PAGING_SUCCEEDED) - return subscr_paging_dispatch(hooknum, event, msg, data, param); - - /* Get paging response */ - gh = msgb_l3(msg); - pr = (struct gsm48_pag_resp *)gh->data; - - /* We _really_ have a channel, secure it now ! */ - return gsm48_secure_channel(conn, pr->key_seq, subscr_paging_sec_cb, param); -} - - -static void subscr_send_paging_request(struct gsm_subscriber *subscr) -{ - struct subscr_request *request; - int rc; - - assert(!llist_empty(&subscr->requests)); - - request = (struct subscr_request *)subscr->requests.next; - request->state = REQ_STATE_PAGED; - rc = paging_request(subscr->net, subscr, request->channel_type, - subscr_paging_cb, subscr); - - /* paging failed, quit now */ - if (rc <= 0) { - request->state = REQ_STATE_FAILED_START; - subscr_paging_cb(GSM_HOOK_RR_PAGING, GSM_PAGING_BUSY, - NULL, NULL, subscr); - } -} - -void subscr_get_channel(struct gsm_subscriber *subscr, - int type, gsm_cbfn *cbfn, void *param) -{ - struct subscr_request *request; - - request = talloc(tall_sub_req_ctx, struct subscr_request); - if (!request) { - if (cbfn) - cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_OOM, - NULL, NULL, param); - return; - } - - memset(request, 0, sizeof(*request)); - request->subscr = subscr_get(subscr); - request->channel_type = type; - request->cbfn = cbfn; - request->param = param; - request->state = REQ_STATE_INITIAL; - - /* - * FIXME: We might be able to assign more than one - * channel, e.g. voice and SMS submit at the same - * time. - */ - if (!subscr->in_callback && llist_empty(&subscr->requests)) { - /* add to the list, send a request */ - llist_add_tail(&request->entry, &subscr->requests); - subscr_send_paging_request(subscr); - } else { - /* this will be picked up later, from subscr_put_channel */ - llist_add_tail(&request->entry, &subscr->requests); - request->state = REQ_STATE_QUEUED; - } -} - -void subscr_put_channel(struct gsm_subscriber *subscr) -{ - /* - * FIXME: Continue with other requests now... by checking - * the gsm_subscriber inside the gsm_lchan. Drop the ref count - * of the lchan after having asked the next requestee to handle - * the channel. - */ - /* - * FIXME: is the lchan is of a different type we could still - * issue an immediate assignment for another channel and then - * close this one. - */ - /* - * Currently we will drop the last ref of the lchan which - * will result in a channel release on RSL and we will start - * the paging. This should work most of the time as the MS - * will listen to the paging requests before we timeout - */ - - if (subscr && !llist_empty(&subscr->requests)) - subscr_send_paging_request(subscr); -} - - -struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net, - 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 db_get_subscriber(net, GSM_SUBSCRIBER_TMSI, tmsi_string); -} - -struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net, - const char *imsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (strcmp(subscr->imsi, imsi) == 0) - return subscr_get(subscr); - } - - return db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); -} - -struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net, - const char *ext) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (strcmp(subscr->extension, ext) == 0) - return subscr_get(subscr); - } - - return db_get_subscriber(net, GSM_SUBSCRIBER_EXTENSION, ext); -} - -struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net, - unsigned long long id) -{ - struct gsm_subscriber *subscr; - char buf[32]; - sprintf(buf, "%llu", id); - - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (subscr->id == id) - return subscr_get(subscr); - } - - return db_get_subscriber(net, GSM_SUBSCRIBER_ID, buf); -} - -int subscr_update_expire_lu(struct gsm_subscriber *s, struct gsm_bts *bts) -{ - int rc; - - /* Table 10.5.33: The T3212 timeout value field is coded as the - * binary representation of the timeout value for - * periodic updating in decihours. Mark the subscriber as - * inactive if it missed two consecutive location updates. - * Timeout is twice the t3212 value plus one minute */ - - /* Is expiration handling enabled? */ - if (bts->si_common.chan_desc.t3212 == 0) - s->expire_lu = GSM_SUBSCRIBER_NO_EXPIRATION; - else - s->expire_lu = time(NULL) + - (bts->si_common.chan_desc.t3212 * 60 * 6 * 2) + 60; - - rc = db_sync_subscriber(s); - db_subscriber_update(s); - return rc; -} - -int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason) -{ - int rc; - - /* FIXME: Migrate pending requests from one BSC to another */ - switch (reason) { - case GSM_SUBSCRIBER_UPDATE_ATTACHED: - s->net = bts->network; - /* Indicate "attached to LAC" */ - s->lac = bts->location_area_code; - - LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n", - subscr_name(s), s->lac); - - /* - * 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, 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); - 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_network *net) -{ - db_subscriber_expire(net, subscr_expire_callback); -} - -int subscr_pending_requests(struct gsm_subscriber *sub) -{ - struct subscr_request *req; - int pending = 0; - - llist_for_each_entry(req, &sub->requests, entry) - pending += 1; - - return pending; -} - -int subscr_pending_clear(struct gsm_subscriber *sub) -{ - int deleted = 0; - struct subscr_request *req, *tmp; - - llist_for_each_entry_safe(req, tmp, &sub->requests, entry) { - subscr_put(req->subscr); - llist_del(&req->entry); - talloc_free(req); - deleted += 1; - } - - return deleted; -} - -int subscr_pending_dump(struct gsm_subscriber *sub, struct vty *vty) -{ - struct subscr_request *req; - - vty_out(vty, "Pending Requests for Subscriber %llu.%s", sub->id, VTY_NEWLINE); - llist_for_each_entry(req, &sub->requests, entry) { - vty_out(vty, "Channel type: %d State: %d Sub: %llu.%s", - req->channel_type, req->state, req->subscr->id, VTY_NEWLINE); - } - - return 0; -} - -int subscr_pending_kick(struct gsm_subscriber *sub) -{ - subscr_put_channel(sub); - return 0; -} diff --git a/openbsc/src/libmsc/mncc.c b/openbsc/src/libmsc/mncc.c deleted file mode 100644 index b48477225..000000000 --- a/openbsc/src/libmsc/mncc.c +++ /dev/null @@ -1,109 +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 - -static struct mncc_names { - char *name; - int value; -} mncc_names[] = { - {"MNCC_SETUP_REQ", 0x0101}, - {"MNCC_SETUP_IND", 0x0102}, - {"MNCC_SETUP_RSP", 0x0103}, - {"MNCC_SETUP_CNF", 0x0104}, - {"MNCC_SETUP_COMPL_REQ",0x0105}, - {"MNCC_SETUP_COMPL_IND",0x0106}, - {"MNCC_CALL_CONF_IND", 0x0107}, - {"MNCC_CALL_PROC_REQ", 0x0108}, - {"MNCC_PROGRESS_REQ", 0x0109}, - {"MNCC_ALERT_REQ", 0x010a}, - {"MNCC_ALERT_IND", 0x010b}, - {"MNCC_NOTIFY_REQ", 0x010c}, - {"MNCC_NOTIFY_IND", 0x010d}, - {"MNCC_DISC_REQ", 0x010e}, - {"MNCC_DISC_IND", 0x010f}, - {"MNCC_REL_REQ", 0x0110}, - {"MNCC_REL_IND", 0x0111}, - {"MNCC_REL_CNF", 0x0112}, - {"MNCC_FACILITY_REQ", 0x0113}, - {"MNCC_FACILITY_IND", 0x0114}, - {"MNCC_START_DTMF_IND", 0x0115}, - {"MNCC_START_DTMF_RSP", 0x0116}, - {"MNCC_START_DTMF_REJ", 0x0117}, - {"MNCC_STOP_DTMF_IND", 0x0118}, - {"MNCC_STOP_DTMF_RSP", 0x0119}, - {"MNCC_MODIFY_REQ", 0x011a}, - {"MNCC_MODIFY_IND", 0x011b}, - {"MNCC_MODIFY_RSP", 0x011c}, - {"MNCC_MODIFY_CNF", 0x011d}, - {"MNCC_MODIFY_REJ", 0x011e}, - {"MNCC_HOLD_IND", 0x011f}, - {"MNCC_HOLD_CNF", 0x0120}, - {"MNCC_HOLD_REJ", 0x0121}, - {"MNCC_RETRIEVE_IND", 0x0122}, - {"MNCC_RETRIEVE_CNF", 0x0123}, - {"MNCC_RETRIEVE_REJ", 0x0124}, - {"MNCC_USERINFO_REQ", 0x0125}, - {"MNCC_USERINFO_IND", 0x0126}, - {"MNCC_REJ_REQ", 0x0127}, - {"MNCC_REJ_IND", 0x0128}, - - {"MNCC_BRIDGE", 0x0200}, - {"MNCC_FRAME_RECV", 0x0201}, - {"MNCC_FRAME_DROP", 0x0202}, - {"MNCC_LCHAN_MODIFY", 0x0203}, - - {"GSM_TCH_FRAME", 0x0300}, - - {NULL, 0} }; - -char *get_mncc_name(int value) -{ - int i; - - for (i = 0; mncc_names[i].name; i++) { - if (mncc_names[i].value == value) - return mncc_names[i].name; - } - - return "MNCC_Unknown"; -} - -void mncc_set_cause(struct gsm_mncc *data, int loc, int val) -{ - data->fields |= MNCC_F_CAUSE; - data->cause.location = loc; - data->cause.value = val; -} - diff --git a/openbsc/src/libmsc/mncc_builtin.c b/openbsc/src/libmsc/mncc_builtin.c deleted file mode 100644 index 617cbf25f..000000000 --- a/openbsc/src/libmsc/mncc_builtin.c +++ /dev/null @@ -1,424 +0,0 @@ -/* mncc_builtin.c - default, minimal built-in MNCC Application for - * standalone bsc_hack (netowrk-in-the-box mode) */ - -/* (C) 2008-2010 by Harald Welte - * (C) 2009 by Andreas Eversberg - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -void *tall_call_ctx; - -static LLIST_HEAD(call_list); - -static uint32_t new_callref = 0x00000001; - -struct mncc_int mncc_int = { - .def_codec = { GSM48_CMODE_SPEECH_EFR, 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; -} - -static uint8_t determine_lchan_mode(struct gsm_mncc *setup) -{ - /* FIXME: check codec capabilities of the phone */ - - if (setup->lchan_type == GSM_LCHAN_TCH_F) - return mncc_int.def_codec[0]; - else - return mncc_int.def_codec[1]; -} - -/* on incoming call, look up database and send setup to remote subscr. */ -static int mncc_setup_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *setup) -{ - struct gsm_mncc mncc; - struct gsm_call *remote; - - memset(&mncc, 0, sizeof(struct gsm_mncc)); - mncc.callref = call->callref; - - /* already have remote call */ - if (call->remote_ref) - return 0; - - /* transfer mode 1 would be packet mode, which was never specified */ - if (setup->bearer_cap.mode != 0) { - LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support " - "packet mode\n", call->callref); - mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); - goto out_reject; - } - - /* we currently only do speech */ - if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) { - LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support " - "voice calls\n", call->callref); - mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); - goto out_reject; - } - - /* create remote call */ - if (!(remote = talloc_zero(tall_call_ctx, struct gsm_call))) { - mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - goto out_reject; - } - llist_add_tail(&remote->entry, &call_list); - remote->net = call->net; - remote->callref = new_callref++; - DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n", - call->callref, remote->callref); - - /* link remote call */ - call->remote_ref = remote->callref; - remote->remote_ref = call->callref; - - /* send call proceeding */ - memset(&mncc, 0, sizeof(struct gsm_mncc)); - mncc.callref = call->callref; - DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref); - mncc_tx_to_cc(call->net, MNCC_CALL_PROC_REQ, &mncc); - - /* modify mode */ - memset(&mncc, 0, sizeof(struct gsm_mncc)); - mncc.callref = call->callref; - mncc.lchan_mode = determine_lchan_mode(setup); - DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref); - mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, &mncc); - - /* send setup to remote */ -// setup->fields |= MNCC_F_SIGNAL; -// setup->signal = GSM48_SIGNAL_DIALTONE; - setup->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref); - return mncc_tx_to_cc(remote->net, MNCC_SETUP_REQ, setup); - -out_reject: - mncc_tx_to_cc(call->net, MNCC_REJ_REQ, &mncc); - free_call(call); - return 0; -} - -static int mncc_alert_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *alert) -{ - struct gsm_call *remote; - - /* send alerting to remote */ - if (!(remote = get_call_ref(call->remote_ref))) - return 0; - alert->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref); - return mncc_tx_to_cc(remote->net, MNCC_ALERT_REQ, alert); -} - -static int mncc_notify_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *notify) -{ - struct gsm_call *remote; - - /* send notify to remote */ - if (!(remote = get_call_ref(call->remote_ref))) - return 0; - notify->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref); - return mncc_tx_to_cc(remote->net, MNCC_NOTIFY_REQ, notify); -} - -static int mncc_setup_cnf(struct gsm_call *call, int msg_type, - struct gsm_mncc *connect) -{ - struct gsm_mncc connect_ack, frame_recv; - struct gsm_network *net = call->net; - struct gsm_call *remote; - uint32_t refs[2]; - - /* acknowledge connect */ - memset(&connect_ack, 0, sizeof(struct gsm_mncc)); - connect_ack.callref = call->callref; - DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref); - mncc_tx_to_cc(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack); - - /* send connect message to remote */ - if (!(remote = get_call_ref(call->remote_ref))) - return 0; - connect->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref); - mncc_tx_to_cc(remote->net, MNCC_SETUP_RSP, connect); - - /* bridge tch */ - refs[0] = call->callref; - refs[1] = call->remote_ref; - DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref); - - /* in direct mode, we always have to bridge the channels */ - if (ipacc_rtp_direct) - return mncc_tx_to_cc(call->net, MNCC_BRIDGE, refs); - - /* proxy mode */ - if (!net->handover.active) { - /* in the no-handover case, we can bridge, i.e. use - * the old RTP proxy code */ - return mncc_tx_to_cc(call->net, MNCC_BRIDGE, refs); - } else { - /* in case of handover, we need to re-write the RTP - * SSRC, sequence and timestamp values and thus - * need to enable RTP receive for both directions */ - memset(&frame_recv, 0, sizeof(struct gsm_mncc)); - frame_recv.callref = call->callref; - mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); - frame_recv.callref = call->remote_ref; - return mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); - } -} - -static int mncc_disc_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *disc) -{ - struct gsm_call *remote; - - /* send release */ - DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n", - call->callref, disc->cause.value); - mncc_tx_to_cc(call->net, MNCC_REL_REQ, disc); - - /* send disc to remote */ - if (!(remote = get_call_ref(call->remote_ref))) { - return 0; - } - disc->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n", - remote->callref, disc->cause.value); - return mncc_tx_to_cc(remote->net, MNCC_DISC_REQ, disc); -} - -static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) -{ - struct gsm_call *remote; - - /* send release to remote */ - if (!(remote = get_call_ref(call->remote_ref))) { - free_call(call); - return 0; - } - - rel->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n", - call->callref, rel->cause.value); - - /* - * Release this side of the call right now. Otherwise we end up - * in this method for the other call and will also try to release - * it and then we will end up with a double free and a crash - */ - free_call(call); - mncc_tx_to_cc(remote->net, MNCC_REL_REQ, rel); - - return 0; -} - -static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) -{ - free_call(call); - return 0; -} - -/* receiving a TCH/F frame from the BSC code */ -static int mncc_rcv_tchf(struct gsm_call *call, int msg_type, - struct gsm_data_frame *dfr) -{ - struct gsm_trans *remote_trans; - - remote_trans = trans_find_by_callref(call->net, call->remote_ref); - - /* this shouldn't really happen */ - if (!remote_trans || !remote_trans->conn) { - LOGP(DMNCC, LOGL_ERROR, "No transaction or transaction without lchan?!?\n"); - return -EIO; - } - - /* RTP socket of remote end has meanwhile died */ - if (!remote_trans->conn->lchan->abis_ip.rtp_socket) - return -EIO; - - return rtp_send_frame(remote_trans->conn->lchan->abis_ip.rtp_socket, dfr); -} - - -/* Internal MNCC handler input function (from CC -> MNCC -> here) */ -int int_mncc_recv(struct gsm_network *net, struct msgb *msg) -{ - void *arg = msgb_data(msg); - struct gsm_mncc *data = arg; - int msg_type = data->msg_type; - int callref; - struct gsm_call *call = NULL, *callt; - int rc = 0; - - /* Special messages */ - switch(msg_type) { - } - - /* find callref */ - callref = data->callref; - llist_for_each_entry(callt, &call_list, entry) { - if (callt->callref == callref) { - call = callt; - break; - } - } - - /* create callref, if setup is received */ - if (!call) { - if (msg_type != MNCC_SETUP_IND) - goto out_free; /* drop */ - /* create call */ - if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) { - struct gsm_mncc rel; - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = callref; - mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - mncc_tx_to_cc(net, MNCC_REL_REQ, &rel); - goto out_free; - } - llist_add_tail(&call->entry, &call_list); - call->net = net; - call->callref = callref; - DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref); - } - - switch (msg_type) { - case GSM_TCHF_FRAME: - case GSM_TCHF_FRAME_EFR: - break; - default: - DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref, - get_mncc_name(msg_type)); - break; - } - - switch(msg_type) { - case MNCC_SETUP_IND: - rc = mncc_setup_ind(call, msg_type, arg); - break; - case MNCC_SETUP_CNF: - rc = mncc_setup_cnf(call, msg_type, arg); - break; - case MNCC_SETUP_COMPL_IND: - break; - case MNCC_CALL_CONF_IND: - /* we now need to MODIFY the channel */ - data->lchan_mode = determine_lchan_mode(data); - mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, data); - break; - case MNCC_ALERT_IND: - rc = mncc_alert_ind(call, msg_type, arg); - break; - case MNCC_NOTIFY_IND: - rc = mncc_notify_ind(call, msg_type, arg); - break; - case MNCC_DISC_IND: - rc = mncc_disc_ind(call, msg_type, arg); - break; - case MNCC_REL_IND: - case MNCC_REJ_IND: - rc = mncc_rel_ind(call, msg_type, arg); - break; - case MNCC_REL_CNF: - rc = mncc_rel_cnf(call, msg_type, arg); - break; - case MNCC_FACILITY_IND: - break; - case MNCC_START_DTMF_IND: - break; - case MNCC_STOP_DTMF_IND: - break; - case MNCC_MODIFY_IND: - mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_SERV_OPT_UNIMPL); - DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n", - call->callref, data->cause.value); - rc = mncc_tx_to_cc(net, MNCC_MODIFY_REJ, data); - break; - case MNCC_MODIFY_CNF: - break; - case MNCC_HOLD_IND: - mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_SERV_OPT_UNIMPL); - DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n", - call->callref, data->cause.value); - rc = mncc_tx_to_cc(net, MNCC_HOLD_REJ, data); - break; - case MNCC_RETRIEVE_IND: - mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_SERV_OPT_UNIMPL); - DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n", - call->callref, data->cause.value); - rc = mncc_tx_to_cc(net, MNCC_RETRIEVE_REJ, data); - break; - case GSM_TCHF_FRAME: - case GSM_TCHF_FRAME_EFR: - rc = mncc_rcv_tchf(call, msg_type, arg); - break; - default: - LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref); - break; - } - -out_free: - 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 cf4bca87a..000000000 --- a/openbsc/src/libmsc/mncc_sock.c +++ /dev/null @@ -1,365 +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 - -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 (msg_type != GSM_TCHF_FRAME && - msg_type != GSM_TCHF_FRAME_EFR) { - /* release the request */ - struct gsm_mncc mncc_out; - memset(&mncc_out, 0, sizeof(mncc_out)); - mncc_out.callref = mncc_in->callref; - mncc_set_cause(&mncc_out, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_TEMP_FAILURE); - mncc_tx_to_cc(net, MNCC_REL_REQ, &mncc_out); - } - /* free the original message */ - msgb_free(msg); - return -1; - } - - /* FIXME: check for some maximum queue depth? */ - - /* Actually enqueue the message and mark socket write need */ - msgb_enqueue(&net->upqueue, msg); - net->mncc_state->conn_bfd.when |= BSC_FD_WRITE; - return 0; -} - -/* FIXME: move this to libosmocore */ -int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path); - -static void mncc_sock_close(struct mncc_sock_state *state) -{ - struct osmo_fd *bfd = &state->conn_bfd; - - LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has LOST connection\n"); - - close(bfd->fd); - bfd->fd = -1; - osmo_fd_unregister(bfd); - - /* re-enable the generation of ACCEPT for new connections */ - state->listen_bfd.when |= BSC_FD_READ; - - /* release all exisitng calls */ - gsm0408_clear_all_trans(state->net, GSM48_PDISC_CC); - - /* flush the queue */ - while (!llist_empty(&state->net->upqueue)) { - struct msgb *msg = msgb_dequeue(&state->net->upqueue); - msgb_free(msg); - } -} - -static int mncc_sock_read(struct osmo_fd *bfd) -{ - struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data; - struct gsm_mncc *mncc_prim; - struct msgb *msg; - int rc; - - msg = msgb_alloc(sizeof(*mncc_prim)+256, "mncc_sock_rx"); - if (!msg) - return -ENOMEM; - - mncc_prim = (struct gsm_mncc *) msg->tail; - - rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0); - if (rc == 0) - goto close; - - if (rc < 0) { - if (errno == EAGAIN) - return 0; - goto close; - } - - rc = mncc_tx_to_cc(state->net, mncc_prim->msg_type, mncc_prim); - - /* as we always synchronously process the message in mncc_send() and - * its callbacks, we can free the message here. */ - msgb_free(msg); - - return rc; - -close: - msgb_free(msg); - mncc_sock_close(state); - return -1; -} - -static int mncc_sock_write(struct osmo_fd *bfd) -{ - struct mncc_sock_state *state = bfd->data; - struct gsm_network *net = state->net; - int rc; - - while (!llist_empty(&net->upqueue)) { - struct msgb *msg, *msg2; - struct gsm_mncc *mncc_prim; - - /* peek at the beginning of the queue */ - msg = llist_entry(net->upqueue.next, struct msgb, list); - mncc_prim = (struct gsm_mncc *)msg->data; - - bfd->when &= ~BSC_FD_WRITE; - - /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */ - if (!msgb_length(msg)) { - LOGP(DMNCC, LOGL_ERROR, "message type (%d) with ZERO " - "bytes!\n", mncc_prim->msg_type); - goto dontsend; - } - - /* try to send it over the socket */ - rc = write(bfd->fd, msgb_data(msg), msgb_length(msg)); - if (rc == 0) - goto close; - if (rc < 0) { - if (errno == EAGAIN) { - bfd->when |= BSC_FD_WRITE; - break; - } - goto close; - } - -dontsend: - /* _after_ we send it, we can deueue */ - msg2 = msgb_dequeue(&net->upqueue); - assert(msg == msg2); - msgb_free(msg); - } - return 0; - -close: - mncc_sock_close(state); - - return -1; -} - -static int mncc_sock_cb(struct osmo_fd *bfd, unsigned int flags) -{ - int rc = 0; - - if (flags & BSC_FD_READ) - rc = mncc_sock_read(bfd); - if (rc < 0) - return rc; - - if (flags & BSC_FD_WRITE) - rc = mncc_sock_write(bfd); - - return rc; -} - -/** - * Send a version indication to the remote. - */ -static void queue_hello(struct mncc_sock_state *mncc) -{ - struct gsm_mncc_hello *hello; - struct msgb *msg; - - msg = msgb_alloc(512, "mncc hello"); - if (!msg) { - LOGP(DMNCC, LOGL_ERROR, "Failed to allocate hello.\n"); - mncc_sock_close(mncc); - return; - } - - hello = (struct gsm_mncc_hello *) msgb_put(msg, sizeof(*hello)); - hello->msg_type = MNCC_SOCKET_HELLO; - hello->version = MNCC_SOCK_VERSION; - hello->mncc_size = sizeof(struct gsm_mncc); - hello->data_frame_size = sizeof(struct gsm_data_frame); - hello->called_offset = offsetof(struct gsm_mncc, called); - hello->signal_offset = offsetof(struct gsm_mncc, signal); - hello->emergency_offset = offsetof(struct gsm_mncc, emergency); - hello->lchan_type_offset = offsetof(struct gsm_mncc, lchan_type); - - msgb_enqueue(&mncc->net->upqueue, msg); - mncc->conn_bfd.when |= BSC_FD_WRITE; -} - -/* accept a new connection */ -static int mncc_sock_accept(struct osmo_fd *bfd, unsigned int flags) -{ - struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data; - struct osmo_fd *conn_bfd = &state->conn_bfd; - struct sockaddr_un un_addr; - socklen_t len; - int rc; - - len = sizeof(un_addr); - rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len); - if (rc < 0) { - LOGP(DMNCC, LOGL_ERROR, "Failed to accept a new connection\n"); - return -1; - } - - if (conn_bfd->fd >= 0) { - LOGP(DMNCC, LOGL_NOTICE, "MNCC app connects but we already have " - "another active connection ?!?\n"); - /* We already have one MNCC app connected, this is all we support */ - state->listen_bfd.when &= ~BSC_FD_READ; - close(rc); - return 0; - } - - conn_bfd->fd = rc; - conn_bfd->when = BSC_FD_READ; - conn_bfd->cb = mncc_sock_cb; - conn_bfd->data = state; - - if (osmo_fd_register(conn_bfd) != 0) { - LOGP(DMNCC, LOGL_ERROR, "Failed to register new connection fd\n"); - close(conn_bfd->fd); - conn_bfd->fd = -1; - return -1; - } - - LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has connection with external " - "call control application\n"); - - queue_hello(state); - return 0; -} - - -int mncc_sock_init(struct gsm_network *net) -{ - struct mncc_sock_state *state; - struct osmo_fd *bfd; - int rc; - - state = talloc_zero(tall_bsc_ctx, struct mncc_sock_state); - if (!state) - return -ENOMEM; - - state->net = net; - state->conn_bfd.fd = -1; - - bfd = &state->listen_bfd; - - rc = osmo_unixsock_listen(bfd, SOCK_SEQPACKET, "/tmp/bsc_mncc"); - if (rc < 0) { - LOGP(DMNCC, LOGL_ERROR, "Could not create unix socket: %s\n", - strerror(errno)); - talloc_free(state); - return rc; - } - - bfd->when = BSC_FD_READ; - bfd->cb = mncc_sock_accept; - bfd->data = state; - - rc = osmo_fd_register(bfd); - if (rc < 0) { - LOGP(DMNCC, LOGL_ERROR, "Could not register listen fd: %d\n", rc); - close(bfd->fd); - talloc_free(state); - return rc; - } - - net->mncc_state = state; - - return 0; -} - -/* FIXME: move this to libosmocore */ -int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path) -{ - struct sockaddr_un local; - unsigned int namelen; - int rc; - - bfd->fd = socket(AF_UNIX, type, 0); - - if (bfd->fd < 0) { - fprintf(stderr, "Failed to create Unix Domain Socket.\n"); - return -1; - } - - local.sun_family = AF_UNIX; - strncpy(local.sun_path, path, sizeof(local.sun_path)); - local.sun_path[sizeof(local.sun_path) - 1] = '\0'; - unlink(local.sun_path); - - /* we use the same magic that X11 uses in Xtranssock.c for - * calculating the proper length of the sockaddr */ -#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) - local.sun_len = strlen(local.sun_path); -#endif -#if defined(BSD44SOCKETS) || defined(SUN_LEN) - namelen = SUN_LEN(&local); -#else - namelen = strlen(local.sun_path) + - offsetof(struct sockaddr_un, sun_path); -#endif - - rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); - if (rc != 0) { - fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n", - local.sun_path); - return -1; - } - - if (listen(bfd->fd, 0) != 0) { - fprintf(stderr, "Failed to listen.\n"); - return -1; - } - - return 0; -} diff --git a/openbsc/src/libmsc/osmo_msc.c b/openbsc/src/libmsc/osmo_msc.c deleted file mode 100644 index 31b72b925..000000000 --- a/openbsc/src/libmsc/osmo_msc.c +++ /dev/null @@ -1,177 +0,0 @@ -/* main MSC management code... */ - -/* - * (C) 2010,2013 by Holger Hans Peter Freyther - * (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); - if (conn->put_channel) { - conn->put_channel = 0; - subscr_put_channel(conn->subscr); - } - return 1; -} - -static int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, - uint16_t chosen_channel) -{ - gsm0408_new_conn(conn); - gsm0408_dispatch(conn, msg); - - /* TODO: do better */ - 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; - 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) -{ -} - -static void msc_assign_fail(struct gsm_subscriber_connection *conn, - uint8_t cause, uint8_t *rr_cause) -{ -} - -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); - if (conn->put_channel) { - conn->put_channel = 0; - subscr_put_channel(conn->subscr); - } - subscr_con_free(conn); -} diff --git a/openbsc/src/libmsc/rrlp.c b/openbsc/src/libmsc/rrlp.c deleted file mode 100644 index 161456a06..000000000 --- a/openbsc/src/libmsc/rrlp.c +++ /dev/null @@ -1,104 +0,0 @@ -/* Radio Resource LCS (Location) Protocol, GMS TS 04.31 */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - - -#include -#include -#include -#include - -/* RRLP msPositionReq, nsBased, - * Accuracy=60, Method=gps, ResponseTime=2, oneSet */ -static const uint8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 }; - -/* RRLP msPositionReq, msBasedPref, - Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ -static const uint8_t ms_pref_pos_req[] = { 0x40, 0x02, 0x79, 0x50 }; - -/* RRLP msPositionReq, msAssistedPref, - Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ -static const uint8_t ass_pref_pos_req[] = { 0x40, 0x03, 0x79, 0x50 }; - -static int send_rrlp_req(struct gsm_subscriber_connection *conn) -{ - struct gsm_network *net = conn->bts->network; - const uint8_t *req; - - switch (net->rrlp.mode) { - case RRLP_MODE_MS_BASED: - req = ms_based_pos_req; - break; - case RRLP_MODE_MS_PREF: - req = ms_pref_pos_req; - break; - case RRLP_MODE_ASS_PREF: - req = ass_pref_pos_req; - break; - case RRLP_MODE_NONE: - default: - return 0; - } - - return gsm48_send_rr_app_info(conn, 0x00, - sizeof(ms_based_pos_req), req); -} - -static int subscr_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_subscriber *subscr; - struct gsm_subscriber_connection *conn; - - switch (signal) { - case S_SUBSCR_ATTACHED: - /* A subscriber has attached. */ - subscr = signal_data; - conn = connection_for_subscr(subscr); - if (!conn) - break; - send_rrlp_req(conn); - break; - } - return 0; -} - -static int paging_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct paging_signal_data *psig_data = signal_data; - - switch (signal) { - case S_PAGING_SUCCEEDED: - /* A subscriber has attached. */ - send_rrlp_req(psig_data->conn); - break; - case S_PAGING_EXPIRED: - break; - } - return 0; -} - -void on_dso_load_rrlp(void) -{ - osmo_signal_register_handler(SS_SUBSCR, subscr_sig_cb, NULL); - osmo_signal_register_handler(SS_PAGING, paging_sig_cb, NULL); -} diff --git a/openbsc/src/libmsc/silent_call.c b/openbsc/src/libmsc/silent_call.c deleted file mode 100644 index cdc82b534..000000000 --- a/openbsc/src/libmsc/silent_call.c +++ /dev/null @@ -1,143 +0,0 @@ -/* GSM silent call feature */ - -/* - * (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* paging of the requested subscriber has completed */ -static int paging_cb_silent(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *_conn, void *_data) -{ - struct gsm_subscriber_connection *conn = _conn; - struct scall_signal_data sigdata; - int rc = 0; - - if (hooknum != GSM_HOOK_RR_PAGING) - return -EINVAL; - - DEBUGP(DLSMS, "paging_cb_silent: "); - - sigdata.conn = conn; - sigdata.data = _data; - - switch (event) { - case GSM_PAGING_SUCCEEDED: - DEBUGPC(DLSMS, "success, using Timeslot %u on ARFCN %u\n", - conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn); - conn->silent_call = 1; - /* increment lchan reference count */ - osmo_signal_dispatch(SS_SCALL, S_SCALL_SUCCESS, &sigdata); - break; - case GSM_PAGING_EXPIRED: - case GSM_PAGING_BUSY: - case GSM_PAGING_OOM: - DEBUGP(DLSMS, "expired\n"); - osmo_signal_dispatch(SS_SCALL, S_SCALL_EXPIRED, &sigdata); - break; - default: - rc = -EINVAL; - break; - } - - return rc; -} - -/* receive a layer 3 message from a silent call */ -int silent_call_rx(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - /* FIXME: do something like sending it through a UDP port */ - return 0; -} - -struct msg_match { - uint8_t pdisc; - uint8_t msg_type; -}; - -/* list of messages that are handled inside OpenBSC, even in a silent call */ -static const struct msg_match silent_call_accept[] = { - { GSM48_PDISC_MM, GSM48_MT_MM_LOC_UPD_REQUEST }, - { GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_REQ }, -}; - -/* decide if we need to reroute a message as part of a silent call */ -int silent_call_reroute(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gh->proto_discr & 0x0f; - int i; - - /* if we're not part of a silent call, never reroute */ - if (!conn->silent_call) - return 0; - - /* check if we are a special message that is handled in openbsc */ - for (i = 0; i < ARRAY_SIZE(silent_call_accept); i++) { - if (silent_call_accept[i].pdisc == pdisc && - silent_call_accept[i].msg_type == gh->msg_type) - return 0; - } - - /* otherwise, reroute */ - return 1; -} - - -/* initiate a silent call with a given subscriber */ -int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type) -{ - int rc; - - rc = paging_request(subscr->net, subscr, type, - paging_cb_silent, data); - return rc; -} - -/* end a silent call with a given subscriber */ -int gsm_silent_call_stop(struct gsm_subscriber *subscr) -{ - struct gsm_subscriber_connection *conn; - - conn = connection_for_subscr(subscr); - if (!conn) - return -EINVAL; - - /* did we actually establish a silent call for this guy? */ - if (!conn->silent_call) - return -EINVAL; - - conn->silent_call = 0; - msc_release_connection(conn); - - return 0; -} diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c deleted file mode 100644 index 8e0085d0c..000000000 --- a/openbsc/src/libmsc/smpp_openbsc.c +++ /dev/null @@ -1,553 +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 "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, addr); - break; - case NPI_ISDN_E163_E164: - case NPI_Private: - subscr = subscr_get_by_extension(net, addr); - break; - default: - LOGP(DSMPP, LOGL_NOTICE, "Unsupported NPI: %u\n", npi); - break; - } - - 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"); - return ESME_ROPTPARNOTALLWD; - } - sms_msg = t->value.octet; - sms_msg_len = t->length; - } else if (submit->sm_length) { - sms_msg = submit->short_message; - sms_msg_len = submit->sm_length; - } else { - sms_msg = NULL; - sms_msg_len = 0; - } - - sms = sms_alloc(); - sms->source = SMS_SOURCE_SMPP; - sms->smpp.sequence_nr = submit->sequence_number; - - /* fill in the destination address */ - sms->receiver = dest; - sms->dst.ton = submit->dest_addr_ton; - sms->dst.npi = submit->dest_addr_npi; - strncpy(sms->dst.addr, dest->extension, sizeof(sms->dst.addr)-1); - - /* fill in the source address */ - sms->sender = subscr_get_by_id(net, 1); - sms->src.ton = submit->source_addr_ton; - sms->src.npi = submit->source_addr_npi; - strncpy(sms->src.addr, (char *)submit->source_addr, sizeof(sms->src.addr)-1); - - if (submit->esm_class & 0x40) - sms->ud_hdr_ind = 1; - - if (submit->esm_class & 0x80) { - sms->reply_path_req = 1; -#warning Implement reply path - } - - if (submit->data_coding == 0x00 || /* SMSC default */ - submit->data_coding == 0x01) { /* GSM default alphabet */ - sms->data_coding_scheme = GSM338_DCS_1111_7BIT; - mode = MODE_7BIT; - } else if ((submit->data_coding & 0xFC) == 0xF0) { /* 03.38 DCS default */ - /* pass DCS 1:1 through from SMPP to GSM */ - sms->data_coding_scheme = submit->data_coding; - mode = MODE_7BIT; - } else if (submit->data_coding == 0x02 || - submit->data_coding == 0x04) { - /* 8-bit binary */ - sms->data_coding_scheme = GSM338_DCS_1111_8BIT_DATA; - mode = MODE_8BIT; - } else if ((submit->data_coding & 0xFC) == 0xF4) { /* 03.38 DCS 8bit */ - /* pass DCS 1:1 through from SMPP to GSM */ - sms->data_coding_scheme = submit->data_coding; - mode = MODE_8BIT; - } else if (submit->data_coding == 0x80) { - /* 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; -} - -/*! \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; - int rc = 0; - - if (!sms) - return 0; - - if (sms->source != SMS_SOURCE_SMPP) - return 0; - - switch (signal) { - 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; - } - - 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; - struct osmo_esme *esme; - 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; - } - - 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 */ - smpp_tx_alert(esme, TON_Subscriber_Number, - NPI_Land_Mobile_E212, subscr->imsi, - smpp_avail_status); - } - - return 0; -} - -/* GSM 03.38 6.2.1 Character expanding (no decode!) */ -static int gsm_7bit_expand(char *text, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind) -{ - int i = 0; - int shift = 0; - uint8_t c; - - /* skip the user data header */ - if (ud_hdr_ind) { - /* get user data header length + 1 (for the 'user data header length'-field) */ - shift = ((user_data[0] + 1) * 8) / 7; - if ((((user_data[0] + 1) * 8) % 7) != 0) - shift++; - septet_l = septet_l - shift; - } - - for (i = 0; i < septet_l; i++) { - c = - ((user_data[((i + shift) * 7 + 7) >> 3] << - (7 - (((i + shift) * 7 + 7) & 7))) | - (user_data[((i + shift) * 7) >> 3] >> - (((i + shift) * 7) & 7))) & 0x7f; - - *(text++) = c; - } - - *text = '\0'; - - return i; -} - - -/* FIXME: libsmpp34 helpers, they should be part of libsmpp34! */ -void append_tlv(tlv_t **req_tlv, uint16_t tag, - const uint8_t *data, uint16_t len) -{ - tlv_t tlv; - - memset(&tlv, 0, sizeof(tlv)); - tlv.tag = tag; - tlv.length = len; - memcpy(tlv.value.octet, data, tlv.length); - build_tlv(req_tlv, &tlv); -} -void append_tlv_u8(tlv_t **req_tlv, uint16_t tag, uint8_t val) -{ - tlv_t tlv; - - memset(&tlv, 0, sizeof(tlv)); - tlv.tag = tag; - tlv.length = 1; - tlv.value.val08 = val; - build_tlv(req_tlv, &tlv); -} -void append_tlv_u16(tlv_t **req_tlv, uint16_t tag, uint16_t val) -{ - tlv_t tlv; - - memset(&tlv, 0, sizeof(tlv)); - tlv.tag = tag; - tlv.length = 2; - tlv.value.val16 = htons(val); - build_tlv(req_tlv, &tlv); -} - -/* Append the Osmocom vendor-specific additional TLVs to a SMPP msg */ -static void append_osmo_tlvs(tlv_t **req_tlv, const struct gsm_lchan *lchan) -{ - int idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, 1); - const struct gsm_meas_rep *mr = &lchan->meas_rep[idx]; - const struct gsm_meas_rep_unidir *ul_meas = &mr->ul; - const struct gsm_meas_rep_unidir *dl_meas = &mr->dl; - - /* Osmocom vendor-specific SMPP34 extensions */ - append_tlv_u16(req_tlv, TLVID_osmo_arfcn, lchan->ts->trx->arfcn); - if (mr->flags & MEAS_REP_F_MS_L1) { - uint8_t ms_dbm; - append_tlv_u8(req_tlv, TLVID_osmo_ta, mr->ms_l1.ta); - ms_dbm = ms_pwr_dbm(lchan->ts->trx->bts->band, mr->ms_l1.pwr); - append_tlv_u8(req_tlv, TLVID_osmo_ms_l1_txpwr, ms_dbm); - } else if (mr->flags & MEAS_REP_F_MS_TO) - append_tlv_u8(req_tlv, TLVID_osmo_ta, mr->ms_timing_offset); - - append_tlv_u16(req_tlv, TLVID_osmo_rxlev_ul, - rxlev2dbm(ul_meas->full.rx_lev)); - append_tlv_u8(req_tlv, TLVID_osmo_rxqual_ul, ul_meas->full.rx_qual); - - if (mr->flags & MEAS_REP_F_DL_VALID) { - append_tlv_u16(req_tlv, TLVID_osmo_rxlev_dl, - rxlev2dbm(dl_meas->full.rx_lev)); - append_tlv_u8(req_tlv, TLVID_osmo_rxqual_dl, - dl_meas->full.rx_qual); - } - - if (lchan->conn && lchan->conn->subscr) { - struct gsm_subscriber *subscr = lchan->conn->subscr; - size_t imei_len = strlen(subscr->equipment.imei); - if (imei_len) - append_tlv(req_tlv, TLVID_osmo_imei, - (uint8_t *)subscr->equipment.imei, imei_len+1); - } -} - -static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms, - struct gsm_subscriber_connection *conn) -{ - struct deliver_sm_t deliver; - uint8_t dcs; - int mode; - - memset(&deliver, 0, sizeof(deliver)); - deliver.command_length = 0; - deliver.command_id = DELIVER_SM; - deliver.command_status = ESME_ROK; - - strcpy((char *)deliver.service_type, "CMT"); - if (esme->acl && esme->acl->deliver_src_imsi) { - deliver.source_addr_ton = TON_Subscriber_Number; - deliver.source_addr_npi = NPI_Land_Mobile_E212; - snprintf((char *)deliver.source_addr, - sizeof(deliver.source_addr), "%s", - sms->sender->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", - sms->sender->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 && conn->lchan) - append_osmo_tlvs(&deliver.tlv, conn->lchan); - - return smpp_tx_deliver(esme, &deliver); -} - -static struct smsc *g_smsc; - -int smpp_try_deliver(struct gsm_sms *sms, struct gsm_subscriber_connection *conn) -{ - struct osmo_esme *esme; - struct osmo_smpp_addr dst; - - memset(&dst, 0, sizeof(dst)); - dst.ton = sms->dst.ton; - dst.npi = sms->dst.npi; - memcpy(dst.addr, sms->dst.addr, sizeof(dst.addr)); - - esme = smpp_route(g_smsc, &dst); - if (!esme) - return 1; /* unknown subscriber */ - - return deliver_to_esme(esme, sms, conn); -} - -struct smsc *smsc_from_vty(struct vty *v) -{ - /* FIXME: this is ugly */ - return g_smsc; -} - -/*! \brief Initialize the OpenBSC SMPP interface */ -int smpp_openbsc_init(void *ctx, uint16_t port) -{ - struct smsc *smsc = talloc_zero(ctx, struct smsc); - int rc; - - rc = smpp_smsc_init(smsc, port); - if (rc < 0) - talloc_free(smsc); - - osmo_signal_register_handler(SS_SMS, smpp_sms_cb, smsc); - osmo_signal_register_handler(SS_SUBSCR, smpp_subscr_cb, smsc); - - g_smsc = smsc; - - smpp_vty_init(); - - return rc; -} - -void smpp_openbsc_set_net(struct gsm_network *net) -{ - g_smsc->priv = net; -} diff --git a/openbsc/src/libmsc/smpp_smsc.c b/openbsc/src/libmsc/smpp_smsc.c deleted file mode 100644 index 64ed2005f..000000000 --- a/openbsc/src/libmsc/smpp_smsc.c +++ /dev/null @@ -1,942 +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 "smpp_smsc.h" - -#include -#include - -/*! \brief Ugly wrapper. libsmpp34 should do this itself! */ -#define SMPP34_UNPACK(rc, type, str, data, len) \ - memset(str, 0, sizeof(*str)); \ - rc = smpp34_unpack(type, str, data, len) - -enum emse_bind { - ESME_BIND_RX = 0x01, - ESME_BIND_TX = 0x02, -}; - -const struct value_string smpp_status_strs[] = { - { ESME_ROK, "No Error" }, - { ESME_RINVMSGLEN, "Message Length is invalid" }, - { ESME_RINVCMDLEN, "Command Length is invalid" }, - { ESME_RINVCMDID, "Invalid Command ID" }, - { ESME_RINVBNDSTS, "Incorrect BIND Status for given command" }, - { ESME_RALYBND, "ESME Already in Bound State" }, - { ESME_RINVPRTFLG, "Invalid Priority Flag" }, - { ESME_RINVREGDLVFLG, "Invalid Registered Delivery Flag" }, - { ESME_RSYSERR, "System Error" }, - { ESME_RINVSRCADR, "Invalid Source Address" }, - { ESME_RINVDSTADR, "Invalid Destination Address" }, - { ESME_RINVMSGID, "Message ID is invalid" }, - { ESME_RBINDFAIL, "Bind failed" }, - { ESME_RINVPASWD, "Invalid Password" }, - { ESME_RINVSYSID, "Invalid System ID" }, - { ESME_RCANCELFAIL, "Cancel SM Failed" }, - { ESME_RREPLACEFAIL, "Replace SM Failed" }, - { ESME_RMSGQFUL, "Message Queue Full" }, - { ESME_RINVSERTYP, "Invalid Service Type" }, - { ESME_RINVNUMDESTS, "Invalid number of destinations" }, - { ESME_RINVDLNAME, "Invalid Distribution List name" }, - { ESME_RINVDESTFLAG, "Destination flag is invalid" }, - { ESME_RINVSUBREP, "Invalid submit with replace request" }, - { ESME_RINVESMCLASS, "Invalid esm_class field data" }, - { ESME_RCNTSUBDL, "Cannot Submit to Distribution List" }, - { ESME_RSUBMITFAIL, "submit_sm or submit_multi failed" }, - { ESME_RINVSRCTON, "Invalid Source address TON" }, - { ESME_RINVSRCNPI, "Invalid Sourec address NPI" }, - { ESME_RINVDSTTON, "Invalid Destination address TON" }, - { ESME_RINVDSTNPI, "Invalid Desetination address NPI" }, - { ESME_RINVSYSTYP, "Invalid system_type field" }, - { ESME_RINVREPFLAG, "Invalid replace_if_present field" }, - { ESME_RINVNUMMSGS, "Invalid number of messages" }, - { ESME_RTHROTTLED, "Throttling error (ESME has exceeded message limits)" }, - { ESME_RINVSCHED, "Invalid Scheduled Delivery Time" }, - { ESME_RINVEXPIRY, "Invalid message validity period (Expiry time)" }, - { ESME_RINVDFTMSGID, "Predefined Message Invalid or Not Found" }, - { ESME_RX_T_APPN, "ESME Receiver Temporary App Error Code" }, - { ESME_RX_P_APPN, "ESME Receiver Permanent App Error Code" }, - { ESME_RX_R_APPN, "ESME Receiver Reject Message Error Code" }, - { ESME_RQUERYFAIL, "query_sm request failed" }, - { ESME_RINVOPTPARSTREAM,"Error in the optional part of the PDU Body" }, - { ESME_ROPTPARNOTALLWD, "Optional Parameter not allowed" }, - { ESME_RINVPARLEN, "Invalid Parameter Length" }, - { ESME_RMISSINGOPTPARAM,"Expected Optional Parameter missing" }, - { ESME_RINVOPTPARAMVAL, "Invalid Optional Parameter Value" }, - { ESME_RDELIVERYFAILURE,"Delivery Failure (used for data_sm_resp)" }, - { ESME_RUNKNOWNERR, "Unknown Error" }, - { 0, NULL } -}; - -/*! \brief compare if two SMPP addresses are equal */ -int smpp_addr_eq(const struct osmo_smpp_addr *a, - const struct osmo_smpp_addr *b) -{ - if (a->ton == b->ton && - a->npi == b->npi && - !strcmp(a->addr, b->addr)) - return 1; - - return 0; -} - - -struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, - const char *sys_id) -{ - struct osmo_smpp_acl *acl; - - llist_for_each_entry(acl, &smsc->acl_list, list) { - if (!strcmp(acl->system_id, sys_id)) - return acl; - } - - return NULL; -} - -struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id) -{ - struct osmo_smpp_acl *acl; - - if (strlen(sys_id) > SMPP_SYS_ID_LEN) - return NULL; - - if (smpp_acl_by_system_id(smsc, sys_id)) - return NULL; - - acl = talloc_zero(smsc, struct osmo_smpp_acl); - if (!acl) - return NULL; - - acl->smsc = smsc; - strcpy(acl->system_id, sys_id); - INIT_LLIST_HEAD(&acl->route_list); - - llist_add_tail(&acl->list, &smsc->acl_list); - - return acl; -} - -void smpp_acl_delete(struct osmo_smpp_acl *acl) -{ - struct osmo_smpp_route *r, *r2; - - llist_del(&acl->list); - - /* kill any active ESMEs */ - if (acl->esme) { - struct osmo_esme *esme = acl->esme; - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - esme->wqueue.bfd.fd = -1; - esme->acl = NULL; - smpp_esme_put(esme); - } - - /* delete all routes for this ACL */ - llist_for_each_entry_safe(r, r2, &acl->route_list, list) { - llist_del(&r->list); - llist_del(&r->global_list); - talloc_free(r); - } - - talloc_free(acl); -} - -static struct osmo_smpp_route *route_alloc(struct osmo_smpp_acl *acl) -{ - struct osmo_smpp_route *r; - - r = talloc_zero(acl, struct osmo_smpp_route); - if (!r) - return NULL; - - llist_add_tail(&r->list, &acl->route_list); - llist_add_tail(&r->global_list, &acl->smsc->route_list); - - return r; -} - -int smpp_route_pfx_add(struct osmo_smpp_acl *acl, - const struct osmo_smpp_addr *pfx) -{ - struct osmo_smpp_route *r; - - llist_for_each_entry(r, &acl->route_list, list) { - if (r->type == SMPP_ROUTE_PREFIX && - smpp_addr_eq(&r->u.prefix, pfx)) - return -EEXIST; - } - - r = route_alloc(acl); - if (!r) - return -ENOMEM; - r->type = SMPP_ROUTE_PREFIX; - r->acl = acl; - memcpy(&r->u.prefix, pfx, sizeof(r->u.prefix)); - - return 0; -} - -int smpp_route_pfx_del(struct osmo_smpp_acl *acl, - const struct osmo_smpp_addr *pfx) -{ - struct osmo_smpp_route *r, *r2; - - llist_for_each_entry_safe(r, r2, &acl->route_list, list) { - if (r->type == SMPP_ROUTE_PREFIX && - smpp_addr_eq(&r->u.prefix, pfx)) { - llist_del(&r->list); - talloc_free(r); - return 0; - } - } - - return -ENODEV; -} - - -/*! \brief increaes the use/reference count */ -void smpp_esme_get(struct osmo_esme *esme) -{ - esme->use++; -} - -static void esme_destroy(struct osmo_esme *esme) -{ - osmo_wqueue_clear(&esme->wqueue); - if (esme->wqueue.bfd.fd >= 0) { - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - } - llist_del(&esme->list); - talloc_free(esme); -} - -static uint32_t esme_inc_seq_nr(struct osmo_esme *esme) -{ - esme->own_seq_nr++; - if (esme->own_seq_nr > 0x7fffffff) - esme->own_seq_nr = 1; - - return esme->own_seq_nr; -} - -/*! \brief decrease the use/reference count, free if it is 0 */ -void smpp_esme_put(struct osmo_esme *esme) -{ - esme->use--; - if (esme->use <= 0) - esme_destroy(esme); -} - -/*! \brief try to find a SMPP route (ESME) for given destination */ -struct osmo_esme * -smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest) -{ - struct osmo_smpp_route *r; - struct osmo_smpp_acl *acl = NULL; - - DEBUGP(DSMPP, "Looking up route for (%u/%u/%s)\n", - dest->ton, dest->npi, dest->addr); - - /* search for a specific route */ - llist_for_each_entry(r, &smsc->route_list, global_list) { - switch (r->type) { - case SMPP_ROUTE_PREFIX: - DEBUGP(DSMPP, "Checking prefix route (%u/%u/%s)->%s\n", - r->u.prefix.ton, r->u.prefix.npi, r->u.prefix.addr, - r->acl->system_id); - if (r->u.prefix.ton == dest->ton && - r->u.prefix.npi == dest->npi && - !strncmp(r->u.prefix.addr, dest->addr, - strlen(r->u.prefix.addr))) { - DEBUGP(DSMPP, "Found prefix route ACL\n"); - acl = r->acl; - } - break; - default: - break; - } - - if (acl) - break; - } - - if (!acl) { - /* check for default route */ - if (smsc->def_route) { - DEBUGP(DSMPP, "Using existing default route\n"); - acl = smsc->def_route; - } - } - - if (acl && acl->esme) { - struct osmo_esme *esme; - DEBUGP(DSMPP, "ACL even has ESME, we can route to it!\n"); - esme = acl->esme; - if (esme->bind_flags & ESME_BIND_RX) - return esme; - else - LOGP(DSMPP, LOGL_NOTICE, "[%s] is matching route, " - "but not bound for Rx, discarding MO SMS\n", - esme->system_id); - } - - return NULL; -} - - -/*! \brief initialize the libsmpp34 data structure for a response */ -#define INIT_RESP(type, resp, req) { \ - memset((resp), 0, sizeof(*(resp))); \ - (resp)->command_length = 0; \ - (resp)->command_id = type; \ - (resp)->command_status = ESME_ROK; \ - (resp)->sequence_number = (req)->sequence_number; \ -} - -/*! \brief pack a libsmpp34 data strcutrure and send it to the ESME */ -#define PACK_AND_SEND(esme, ptr) pack_and_send(esme, (ptr)->command_id, ptr) -static int pack_and_send(struct osmo_esme *esme, uint32_t type, void *ptr) -{ - struct msgb *msg = msgb_alloc(4096, "SMPP_Tx"); - int rc, rlen; - if (!msg) - return -ENOMEM; - - rc = smpp34_pack(type, msg->tail, msgb_tailroom(msg), &rlen, ptr); - if (rc != 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error during smpp34_pack(): %s\n", - esme->system_id, smpp34_strerror); - msgb_free(msg); - return -EINVAL; - } - msgb_put(msg, rlen); - - return osmo_wqueue_enqueue(&esme->wqueue, msg); -} - -/*! \brief transmit a generic NACK to a remote ESME */ -static int smpp_tx_gen_nack(struct osmo_esme *esme, uint32_t seq, uint32_t status) -{ - struct generic_nack_t nack; - char buf[SMALL_BUFF]; - - nack.command_length = 0; - nack.command_id = GENERIC_NACK; - nack.sequence_number = seq; - nack.command_status = status; - - LOGP(DSMPP, LOGL_ERROR, "[%s] Tx GENERIC NACK: %s\n", - esme->system_id, str_command_status(status, buf)); - - return PACK_AND_SEND(esme, &nack); -} - -/*! \brief retrieve SMPP command ID from a msgb */ -static inline uint32_t smpp_msgb_cmdid(struct msgb *msg) -{ - uint8_t *tmp = msgb_data(msg) + 4; - return ntohl(*(uint32_t *)tmp); -} - -/*! \brief retrieve SMPP sequence number from a msgb */ -static inline uint32_t smpp_msgb_seq(struct msgb *msg) -{ - uint8_t *tmp = msgb_data(msg); - return ntohl(*(uint32_t *)tmp); -} - -/*! \brief handle an incoming SMPP generic NACK */ -static int smpp_handle_gen_nack(struct osmo_esme *esme, struct msgb *msg) -{ - struct generic_nack_t nack; - char buf[SMALL_BUFF]; - int rc; - - SMPP34_UNPACK(rc, GENERIC_NACK, &nack, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - LOGP(DSMPP, LOGL_ERROR, "[%s] Rx GENERIC NACK: %s\n", - esme->system_id, str_command_status(nack.command_status, buf)); - - return 0; -} - -static int _process_bind(struct osmo_esme *esme, uint8_t if_version, - uint32_t bind_flags, const char *sys_id, - const char *passwd) -{ - struct osmo_smpp_acl *acl; - - if (if_version != SMPP_VERSION) - return ESME_RSYSERR; - - if (esme->bind_flags) - return ESME_RALYBND; - - esme->smpp_version = if_version; - snprintf(esme->system_id, sizeof(esme->system_id), "%s", sys_id); - - acl = smpp_acl_by_system_id(esme->smsc, esme->system_id); - if (!esme->smsc->accept_all) { - if (!acl) { - /* This system is unknown */ - return ESME_RINVSYSID; - } else { - if (strlen(acl->passwd) && - strcmp(acl->passwd, passwd)) { - return ESME_RINVPASWD; - } - } - } - if (acl) { - esme->acl = acl; - acl->esme = esme; - } - - esme->bind_flags = bind_flags; - - return ESME_ROK; -} - - -/*! \brief handle an incoming SMPP BIND RECEIVER */ -static int smpp_handle_bind_rx(struct osmo_esme *esme, struct msgb *msg) -{ - struct bind_receiver_t bind; - struct bind_receiver_resp_t bind_r; - int rc; - - SMPP34_UNPACK(rc, BIND_RECEIVER, &bind, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(BIND_TRANSMITTER_RESP, &bind_r, &bind); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Rx from (Version %02x)\n", - bind.system_id, bind.interface_version); - - rc = _process_bind(esme, bind.interface_version, ESME_BIND_RX, - (const char *)bind.system_id, (const char *)bind.password); - bind_r.command_status = rc; - - return PACK_AND_SEND(esme, &bind_r); -} - -/*! \brief handle an incoming SMPP BIND TRANSMITTER */ -static int smpp_handle_bind_tx(struct osmo_esme *esme, struct msgb *msg) -{ - struct bind_transmitter_t bind; - struct bind_transmitter_resp_t bind_r; - struct tlv_t tlv; - int rc; - - SMPP34_UNPACK(rc, BIND_TRANSMITTER, &bind, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(BIND_TRANSMITTER_RESP, &bind_r, &bind); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Tx (Version %02x)\n", - bind.system_id, bind.interface_version); - - rc = _process_bind(esme, bind.interface_version, ESME_BIND_TX, - (const char *)bind.system_id, (const char *)bind.password); - bind_r.command_status = rc; - - /* build response */ - snprintf((char *)bind_r.system_id, sizeof(bind_r.system_id), "%s", - esme->smsc->system_id); - - /* add interface version TLV */ - tlv.tag = TLVID_sc_interface_version; - tlv.length = sizeof(uint8_t); - tlv.value.val16 = esme->smpp_version; - build_tlv(&bind_r.tlv, &tlv); - - return PACK_AND_SEND(esme, &bind_r); -} - -/*! \brief handle an incoming SMPP BIND TRANSCEIVER */ -static int smpp_handle_bind_trx(struct osmo_esme *esme, struct msgb *msg) -{ - struct bind_transceiver_t bind; - struct bind_transceiver_resp_t bind_r; - int rc; - - SMPP34_UNPACK(rc, BIND_TRANSCEIVER, &bind, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(BIND_TRANSCEIVER_RESP, &bind_r, &bind); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Trx (Version %02x)\n", - bind.system_id, bind.interface_version); - - rc = _process_bind(esme, bind.interface_version, ESME_BIND_RX|ESME_BIND_TX, - (const char *)bind.system_id, (const char *)bind.password); - bind_r.command_status = rc; - - return PACK_AND_SEND(esme, &bind_r); -} - -/*! \brief handle an incoming SMPP UNBIND */ -static int smpp_handle_unbind(struct osmo_esme *esme, struct msgb *msg) -{ - struct unbind_t unbind; - struct unbind_resp_t unbind_r; - int rc; - - SMPP34_UNPACK(rc, UNBIND, &unbind, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(UNBIND_RESP, &unbind_r, &unbind); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx UNBIND\n", esme->system_id); - - if (esme->bind_flags == 0) { - unbind_r.command_status = ESME_RINVBNDSTS; - goto err; - } - - esme->bind_flags = 0; -err: - return PACK_AND_SEND(esme, &unbind_r); -} - -/*! \brief handle an incoming SMPP ENQUIRE LINK */ -static int smpp_handle_enq_link(struct osmo_esme *esme, struct msgb *msg) -{ - struct enquire_link_t enq; - struct enquire_link_resp_t enq_r; - int rc; - - SMPP34_UNPACK(rc, ENQUIRE_LINK, &enq, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - LOGP(DSMPP, LOGL_DEBUG, "[%s] Rx Enquire Link\n", esme->system_id); - - INIT_RESP(ENQUIRE_LINK_RESP, &enq_r, &enq); - - LOGP(DSMPP, LOGL_DEBUG, "[%s] Tx Enquire Link Response\n", esme->system_id); - - return PACK_AND_SEND(esme, &enq_r); -} - -/*! \brief send a SUBMIT-SM RESPONSE to a remote ESME */ -int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr, - uint32_t command_status, char *msg_id) -{ - struct submit_sm_resp_t submit_r; - - memset(&submit_r, 0, sizeof(submit_r)); - submit_r.command_length = 0; - submit_r.command_id = SUBMIT_SM_RESP; - submit_r.command_status = command_status; - submit_r.sequence_number= sequence_nr; - snprintf((char *) submit_r.message_id, sizeof(submit_r.message_id), "%s", msg_id); - - return PACK_AND_SEND(esme, &submit_r); -} - -static const struct value_string smpp_avail_strs[] = { - { 0, "Available" }, - { 1, "Denied" }, - { 2, "Unavailable" }, - { 0, NULL } -}; - -/*! \brief send an ALERT_NOTIFICATION to a remote ESME */ -int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, - const char *addr, uint8_t avail_status) -{ - struct alert_notification_t alert; - struct tlv_t tlv; - - memset(&alert, 0, sizeof(alert)); - alert.command_length = 0; - alert.command_id = ALERT_NOTIFICATION; - alert.command_status = ESME_ROK; - alert.sequence_number = esme_inc_seq_nr(esme); - alert.source_addr_ton = ton; - alert.source_addr_npi = npi; - snprintf((char *)alert.source_addr, sizeof(alert.source_addr), "%s", addr); - - tlv.tag = TLVID_ms_availability_status; - tlv.length = sizeof(uint8_t); - tlv.value.val08 = avail_status; - build_tlv(&alert.tlv, &tlv); - - LOGP(DSMPP, LOGL_DEBUG, "[%s] Tx ALERT_NOTIFICATION (%s/%u/%u): %s\n", - esme->system_id, alert.source_addr, alert.source_addr_ton, - alert.source_addr_npi, - get_value_string(smpp_avail_strs, avail_status)); - - return PACK_AND_SEND(esme, &alert); -} - -/* \brief send a DELIVER-SM message to given ESME */ -int smpp_tx_deliver(struct osmo_esme *esme, struct deliver_sm_t *deliver) -{ - deliver->sequence_number = esme_inc_seq_nr(esme); - - return PACK_AND_SEND(esme, deliver); -} - -/*! \brief handle an incoming SMPP DELIVER-SM RESPONSE */ -static int smpp_handle_deliver_resp(struct osmo_esme *esme, struct msgb *msg) -{ - struct deliver_sm_resp_t deliver_r; - int rc; - - memset(&deliver_r, 0, sizeof(deliver_r)); - SMPP34_UNPACK(rc, DELIVER_SM_RESP, &deliver_r, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx DELIVER-SM RESP (%s)\n", - esme->system_id, get_value_string(smpp_status_strs, - deliver_r.command_status)); - - return 0; -} - -/*! \brief handle an incoming SMPP SUBMIT-SM */ -static int smpp_handle_submit(struct osmo_esme *esme, struct msgb *msg) -{ - struct submit_sm_t submit; - struct submit_sm_resp_t submit_r; - int rc; - - memset(&submit, 0, sizeof(submit)); - SMPP34_UNPACK(rc, SUBMIT_SM, &submit, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(SUBMIT_SM_RESP, &submit_r, &submit); - - if (!(esme->bind_flags & ESME_BIND_TX)) { - submit_r.command_status = ESME_RINVBNDSTS; - return PACK_AND_SEND(esme, &submit_r); - } - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx SUBMIT-SM (%s/%u/%u)\n", - esme->system_id, submit.destination_addr, - submit.dest_addr_ton, submit.dest_addr_npi); - - INIT_RESP(SUBMIT_SM_RESP, &submit_r, &submit); - - rc = handle_smpp_submit(esme, &submit, &submit_r); - if (rc == 0) - return PACK_AND_SEND(esme, &submit_r); - - return rc; -} - -/*! \brief one complete SMPP PDU from the ESME has been received */ -static int smpp_pdu_rx(struct osmo_esme *esme, struct msgb *msg __uses) -{ - uint32_t cmd_id = smpp_msgb_cmdid(msg); - int rc = 0; - - LOGP(DSMPP, LOGL_DEBUG, "[%s] smpp_pdu_rx(%s)\n", esme->system_id, - osmo_hexdump(msgb_data(msg), msgb_length(msg))); - - switch (cmd_id) { - case GENERIC_NACK: - rc = smpp_handle_gen_nack(esme, msg); - break; - case BIND_RECEIVER: - rc = smpp_handle_bind_rx(esme, msg); - break; - case BIND_TRANSMITTER: - rc = smpp_handle_bind_tx(esme, msg); - break; - case BIND_TRANSCEIVER: - rc = smpp_handle_bind_trx(esme, msg); - break; - case UNBIND: - rc = smpp_handle_unbind(esme, msg); - break; - case ENQUIRE_LINK: - rc = smpp_handle_enq_link(esme, msg); - break; - case SUBMIT_SM: - rc = smpp_handle_submit(esme, msg); - break; - case DELIVER_SM_RESP: - rc = smpp_handle_deliver_resp(esme, msg); - break; - case DELIVER_SM: - break; - case DATA_SM: - break; - case CANCEL_SM: - case QUERY_SM: - case REPLACE_SM: - case SUBMIT_MULTI: - LOGP(DSMPP, LOGL_NOTICE, "[%s] Unimplemented PDU Commmand " - "0x%08x\n", esme->system_id, cmd_id); - break; - default: - LOGP(DSMPP, LOGL_ERROR, "[%s] Unknown PDU Command 0x%08x\n", - esme->system_id, cmd_id); - rc = smpp_tx_gen_nack(esme, smpp_msgb_seq(msg), ESME_RINVCMDID); - break; - } - - return rc; -} - -/* !\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; - 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); - msgb_free(esme->read_msg); - esme->read_msg = NULL; - esme->read_idx = 0; - esme->read_len = 0; - esme->read_state = READ_ST_IN_LEN; - } - break; - } - - return 0; -dead_socket: - msgb_free(esme->read_msg); - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - esme->wqueue.bfd.fd = -1; - smpp_esme_put(esme); - - return 0; -} - -/* call-back of write queue once it wishes to write a message to the socket */ -static int esme_link_write_cb(struct osmo_fd *ofd, struct msgb *msg) -{ - struct osmo_esme *esme = ofd->data; - int rc; - - rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); - if (rc == 0) { - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - esme->wqueue.bfd.fd = -1; - smpp_esme_put(esme); - } else if (rc < msgb_length(msg)) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Short write\n", esme->system_id); - return -1; - } - - return 0; -} - -/* callback for already-accepted new TCP socket */ -static int link_accept_cb(struct smsc *smsc, int fd, - struct sockaddr_storage *s, socklen_t s_len) -{ - struct osmo_esme *esme = talloc_zero(smsc, struct osmo_esme); - if (!esme) { - close(fd); - return -ENOMEM; - } - - smpp_esme_get(esme); - esme->own_seq_nr = rand(); - esme_inc_seq_nr(esme); - esme->smsc = smsc; - osmo_wqueue_init(&esme->wqueue, 10); - esme->wqueue.bfd.fd = fd; - esme->wqueue.bfd.data = esme; - esme->wqueue.bfd.when = BSC_FD_READ; - - if (osmo_fd_register(&esme->wqueue.bfd) != 0) { - close(fd); - talloc_free(esme); - return -EIO; - } - - esme->wqueue.read_cb = esme_link_read_cb; - esme->wqueue.write_cb = esme_link_write_cb; - - esme->sa_len = OSMO_MIN(sizeof(esme->sa), s_len); - memcpy(&esme->sa, s, esme->sa_len); - - llist_add_tail(&esme->list, &smsc->esme_list); - - return 0; -} - -/* callback of listening TCP socket */ -static int smsc_fd_cb(struct osmo_fd *ofd, unsigned int what) -{ - int rc; - struct sockaddr_storage sa; - socklen_t sa_len = sizeof(sa); - - rc = accept(ofd->fd, (struct sockaddr *)&sa, &sa_len); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "Accept returns %d (%s)\n", - rc, strerror(errno)); - return rc; - } - return link_accept_cb(ofd->data, rc, &sa, sa_len); -} - -/*! \brief Initialize the SMSC-side SMPP implementation */ -int smpp_smsc_init(struct smsc *smsc, uint16_t port) -{ - int rc; - - /* default port for SMPP */ - if (port == 0) - port = 2775; - - /* This will not work if we were to actually ever use FD 0 - * (stdin) for this ... */ - if (smsc->listen_ofd.fd <= 0) { - INIT_LLIST_HEAD(&smsc->esme_list); - INIT_LLIST_HEAD(&smsc->acl_list); - INIT_LLIST_HEAD(&smsc->route_list); - smsc->listen_ofd.data = smsc; - smsc->listen_ofd.cb = smsc_fd_cb; - } else { - close(smsc->listen_ofd.fd); - osmo_fd_unregister(&smsc->listen_ofd); - } - - rc = osmo_sock_init_ofd(&smsc->listen_ofd, AF_UNSPEC, SOCK_STREAM, - IPPROTO_TCP, NULL, port, - OSMO_SOCK_F_BIND); - - /* if there is an error, try to re-bind to the old port */ - if (rc < 0) { - rc = osmo_sock_init_ofd(&smsc->listen_ofd, AF_UNSPEC, - SOCK_STREAM, IPPROTO_TCP, NULL, - smsc->listen_port, OSMO_SOCK_F_BIND); - } else - smsc->listen_port = port; - - return rc; -} diff --git a/openbsc/src/libmsc/smpp_smsc.h b/openbsc/src/libmsc/smpp_smsc.h deleted file mode 100644 index 21d28dda6..000000000 --- a/openbsc/src/libmsc/smpp_smsc.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef _SMPP_SMSC_H -#define _SMPP_SMSC_H - -#include -#include - -#include -#include -#include - -#include -#include -#include - -#define SMPP_SYS_ID_LEN 16 -#define SMPP_PASSWD_LEN 16 - -#define MODE_7BIT 7 -#define MODE_8BIT 8 - -enum esme_read_state { - READ_ST_IN_LEN = 0, - READ_ST_IN_MSG = 1, -}; - -struct osmo_smpp_acl; - -struct osmo_smpp_addr { - uint8_t ton; - uint8_t npi; - char addr[21+1]; -}; - -struct osmo_esme { - struct llist_head list; - struct smsc *smsc; - struct osmo_smpp_acl *acl; - int use; - - uint32_t own_seq_nr; - - struct osmo_wqueue wqueue; - struct sockaddr_storage sa; - socklen_t sa_len; - - enum esme_read_state read_state; - uint32_t read_len; - uint32_t read_idx; - struct msgb *read_msg; - - uint8_t smpp_version; - char system_id[SMPP_SYS_ID_LEN+1]; - - uint8_t bind_flags; -}; - -struct osmo_smpp_acl { - struct llist_head list; - struct smsc *smsc; - struct osmo_esme *esme; - char *description; - char system_id[SMPP_SYS_ID_LEN+1]; - char passwd[SMPP_PASSWD_LEN+1]; - int default_route; - int deliver_src_imsi; - int osmocom_ext; - int dcs_transparent; - struct llist_head route_list; -}; - -enum osmo_smpp_rtype { - SMPP_ROUTE_NONE, - SMPP_ROUTE_PREFIX, -}; - -struct osmo_smpp_route { - struct llist_head list; /*!< in acl.route_list */ - struct llist_head global_list; /*!< in smsc->route_list */ - struct osmo_smpp_acl *acl; - enum osmo_smpp_rtype type; - union { - struct osmo_smpp_addr prefix; - } u; -}; - - -struct smsc { - struct osmo_fd listen_ofd; - struct llist_head esme_list; - struct llist_head acl_list; - struct llist_head route_list; - uint16_t listen_port; - char system_id[SMPP_SYS_ID_LEN+1]; - int accept_all; - struct osmo_smpp_acl *def_route; - void *priv; -}; - -int smpp_addr_eq(const struct osmo_smpp_addr *a, - const struct osmo_smpp_addr *b); - -int smpp_smsc_init(struct smsc *smsc, uint16_t port); - -void smpp_esme_get(struct osmo_esme *esme); -void smpp_esme_put(struct osmo_esme *esme); - -struct osmo_esme * -smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest); - -struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id); -struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, - const char *sys_id); -void smpp_acl_delete(struct osmo_smpp_acl *acl); - -int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr, - uint32_t command_status, char *msg_id); - -int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, - const char *addr, uint8_t avail_status); - -int smpp_tx_deliver(struct osmo_esme *esme, struct deliver_sm_t *deliver); - -int handle_smpp_submit(struct osmo_esme *esme, struct submit_sm_t *submit, - struct submit_sm_resp_t *submit_r); - -int smpp_route_pfx_add(struct osmo_smpp_acl *acl, - const struct osmo_smpp_addr *pfx); -int smpp_route_pfx_del(struct osmo_smpp_acl *acl, - const struct osmo_smpp_addr *pfx); - -int smpp_vty_init(void); - -int smpp_determine_scheme(uint8_t dcs, uint8_t *data_coding, int *mode); -#endif diff --git a/openbsc/src/libmsc/smpp_utils.c b/openbsc/src/libmsc/smpp_utils.c deleted file mode 100644 index d0850d8c1..000000000 --- a/openbsc/src/libmsc/smpp_utils.c +++ /dev/null @@ -1,62 +0,0 @@ - -/* (C) 2012-2013 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License 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 4829eb900..000000000 --- a/openbsc/src/libmsc/smpp_vty.c +++ /dev/null @@ -1,516 +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_port, cfg_smpp_port_cmd, - "local-tcp-port <1-65535>", - "Set the local TCP port on which we listen for SMPP\n" - "TCP port number") -{ - struct smsc *smsc = smsc_from_vty(vty); - uint16_t port = atoi(argv[0]); - int rc; - - rc = smpp_smsc_init(smsc, port); - if (rc < 0) { - vty_out(vty, "%% Cannot bind to new port %u nor to " - "old port %u%s", port, smsc->listen_port, VTY_NEWLINE); - return CMD_WARNING; - } - - if (port != smsc->listen_port) { - vty_out(vty, "%% Cannot bind to new port %u, staying on old" - "port %u%s", port, smsc->listen_port, VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_smpp_sys_id, cfg_smpp_sys_id_cmd, - "system-id ID", - "Set the System ID of this SMSC\n" - "Alphanumeric SMSC System ID\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - - if (strlen(argv[0])+1 > sizeof(smsc->system_id)) - return CMD_WARNING; - - strcpy(smsc->system_id, argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_smpp_policy, cfg_smpp_policy_cmd, - "policy (accept-all|closed)", - "Set the authentication policy of this SMSC\n" - "Accept all SMPP connections independeint of system ID / passwd\n" - "Accept only SMPP connections from ESMEs explicitly configured") -{ - struct smsc *smsc = smsc_from_vty(vty); - - if (!strcmp(argv[0], "accept-all")) - smsc->accept_all = 1; - else - smsc->accept_all = 0; - - return CMD_SUCCESS; -} - - -static int config_write_smpp(struct vty *vty) -{ - struct smsc *smsc = smsc_from_vty(vty); - - vty_out(vty, "smpp%s", VTY_NEWLINE); - vty_out(vty, " local-tcp-port %u%s", smsc->listen_port, VTY_NEWLINE); - if (strlen(smsc->system_id) > 0) - vty_out(vty, " system-id %s%s", smsc->system_id, VTY_NEWLINE); - vty_out(vty, " policy %s%s", - smsc->accept_all ? "accept-all" : "closed", VTY_NEWLINE); - - 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_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); - install_default(SMPP_NODE); - install_element(CONFIG_NODE, &cfg_smpp_cmd); - - install_element(SMPP_NODE, &cfg_smpp_port_cmd); - install_element(SMPP_NODE, &cfg_smpp_sys_id_cmd); - install_element(SMPP_NODE, &cfg_smpp_policy_cmd); - install_element(SMPP_NODE, &cfg_esme_cmd); - install_element(SMPP_NODE, &cfg_no_esme_cmd); - - install_node(&esme_node, config_write_esme); - 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_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(SMPP_ESME_NODE, &ournode_exit_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 c8745c3da..000000000 --- a/openbsc/src/libmsc/sms_queue.c +++ /dev/null @@ -1,500 +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->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->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_NOTICE, "Attempting to send %d SMS\n", attempts); - - do { - struct gsm_sms_pending *pending; - struct gsm_sms *sms; - - - sms = take_next_sms(smsq); - if (!sms) - break; - - rounds += 1; - - /* - * This code needs to detect a loop. It assumes that no SMS - * will vanish during the time this is executed. We will remember - * the id of the first GSM subscriber we see and then will - * compare this. The Database code should make sure that we will - * see all other subscribers first before seeing this one again. - * - * It is always scary to have an infinite loop like this. - */ - if (!initialized) { - first_sub = sms->receiver->id; - initialized = 1; - } else if (first_sub == sms->receiver->id) { - sms_free(sms); - break; - } - - /* no need to send a pending sms */ - if (sms_is_in_pending(smsq, sms)) { - LOGP(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); -} - -/* - * Kick off the queue again. - */ -int sms_queue_trigger(struct gsm_sms_queue *smsq) -{ - if (osmo_timer_pending(&smsq->push_queue)) - return 0; - - osmo_timer_schedule(&smsq->push_queue, 1, 0); - return 0; -} - -int sms_queue_start(struct gsm_network *network, int max_pending) -{ - struct gsm_sms_queue *sms = talloc_zero(network, struct gsm_sms_queue); - if (!sms) { - LOGP(DMSC, LOGL_ERROR, "Failed to create the SMS queue.\n"); - return -1; - } - - osmo_signal_register_handler(SS_SUBSCR, sms_subscr_cb, network); - osmo_signal_register_handler(SS_SMS, sms_sms_cb, network); - - network->sms_queue = sms; - INIT_LLIST_HEAD(&sms->pending_sms); - sms->max_fail = 1; - sms->network = network; - sms->max_pending = max_pending; - sms->push_queue.data = sms; - sms->push_queue.cb = sms_submit_pending; - sms->resend_pending.data = sms; - sms->resend_pending.cb = sms_resend_pending; - - sms_submit_pending(sms); - - return 0; -} - -static int sub_ready_for_sm(struct gsm_network *net, struct gsm_subscriber *subscr) -{ - struct gsm_sms *sms; - struct gsm_sms_pending *pending; - struct gsm_subscriber_connection *conn; - - /* - * The code used to be very clever and tried to submit - * a SMS during the Location Updating Request. This has - * two issues: - * 1.) The Phone might not be ready yet, e.g. the C155 - * will not respond to the Submit when it is booting. - * 2.) The queue is already trying to submit SMS to the - * user and by not responding to the paging request - * we will set the LAC back to 0. We would have to - * stop the paging and move things over. - * - * We need to be careful in what we try here. - */ - - /* check if we have pending requests */ - pending = sms_subscriber_find_pending(net->sms_queue, subscr); - if (pending) { - LOGP(DMSC, LOGL_NOTICE, - "Pending paging while subscriber %llu attached.\n", - subscr->id); - return 0; - } - - conn = connection_for_subscr(subscr); - if (!conn) - return -1; - - /* Now try to deliver any pending SMS to this sub */ - sms = db_sms_get_unsent_for_subscr(subscr); - if (!sms) - return -1; - gsm411_send_sms(conn, sms); - return 0; -} - -static int sms_subscr_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_subscriber *subscr = signal_data; - - if (signal != S_SUBSCR_ATTACHED) - return 0; - - /* this is readyForSM */ - return sub_ready_for_sm(handler_data, subscr); -} - -static int sms_sms_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_network *network = handler_data; - struct sms_signal_data *sig_sms = signal_data; - struct gsm_sms_pending *pending; - - /* We got a new SMS and maybe should launch the queue again. */ - if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) { - sms_queue_trigger(network->sms_queue); - return 0; - } - - if (!sig_sms->sms) - return -1; - - - /* - * Find the entry of our queue. The SMS subsystem will submit - * sms that are not in our control as we just have a channel - * open anyway. - */ - pending = sms_find_pending(network->sms_queue, sig_sms->sms); - if (!pending) - return 0; - - switch (signal) { - case S_SMS_DELIVERED: - /* - * Create place for a new SMS but keep the pending data - * so we will not attempt to send the SMS for this subscriber - * as we still have an open channel and will attempt to submit - * SMS to it anyway. - */ - network->sms_queue->pending -= 1; - sms_submit_pending(network->sms_queue); - sms_pending_free(pending); - break; - case S_SMS_MEM_EXCEEDED: - network->sms_queue->pending -= 1; - sms_pending_free(pending); - sms_queue_trigger(network->sms_queue); - break; - case S_SMS_UNKNOWN_ERROR: - /* - * There can be many reasons for this failure. E.g. the paging - * timed out, the subscriber was not paged at all, or there was - * a protocol error. The current strategy is to try sending the - * next SMS for busy/oom and to retransmit when we have paged. - * - * When the paging expires three times we will disable the - * subscriber. If we have some kind of other transmit error we - * should flag the SMS as bad. - */ - switch (sig_sms->paging_result) { - case 0: - /* BAD SMS? */ - db_sms_inc_deliver_attempts(sig_sms->sms); - sms_pending_failed(pending, 0); - break; - case GSM_PAGING_EXPIRED: - sms_pending_failed(pending, 1); - break; - - case GSM_PAGING_OOM: - case GSM_PAGING_BUSY: - network->sms_queue->pending -= 1; - sms_pending_free(pending); - sms_queue_trigger(network->sms_queue); - break; - default: - LOGP(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 45b5a8eab..000000000 --- a/openbsc/src/libmsc/token_auth.c +++ /dev/null @@ -1,160 +0,0 @@ -/* SMS based token authentication for ad-hoc GSM networks */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a 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->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->net, 1); - - sms = sms_from_text(subscr, sender, 0, sms_str); - - subscr_put(sender); - talloc_free(sms_str); - if (!sms) { - rc = -ENOMEM; - goto unauth; - } - - rc = gsm411_send_sms_subscr(subscr, sms); - - /* FIXME: else, delete the subscirber from database */ -unauth: - - /* make sure we don't allow him in again unless he clicks the web UI */ - subscr->authorized = 0; - db_sync_subscriber(subscr); - if (rc) { - struct gsm_subscriber_connection *conn = connection_for_subscr(subscr); - if (conn) { - uint8_t auth_rand[16]; - /* kick the subscriber off the network */ - gsm48_tx_mm_auth_req(conn, auth_rand, 0); - gsm48_tx_mm_auth_rej(conn); - /* FIXME: close the channel early ?*/ - //gsm48_send_rr_Release(lchan); - } - } - } - - return rc; -} - -static int token_sms_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct sms_signal_data *sig = signal_data; - struct gsm_sms *sms = sig->sms;; - struct gsm_subscriber_connection *conn; - uint8_t auth_rand[16]; - - - if (signal != S_SMS_DELIVERED) - return 0; - - - /* these are not the droids we've been looking for */ - if (!sms->receiver || - !(sms->receiver->flags & GSM_SUBSCRIBER_FIRST_CONTACT)) - return 0; - - - if (sms->receiver->net->auth_policy != GSM_AUTH_POLICY_TOKEN) - return 0; - - - conn = connection_for_subscr(sms->receiver); - if (conn) { - /* kick the subscriber off the network */ - gsm48_tx_mm_auth_req(conn, auth_rand, 0); - gsm48_tx_mm_auth_rej(conn); - /* FIXME: close the channel early ?*/ - //gsm48_send_rr_Release(lchan); - } - - return 0; -} - -//static __attribute__((constructor)) void on_dso_load_token(void) -void on_dso_load_token(void) -{ - osmo_signal_register_handler(SS_SUBSCR, token_subscr_cb, NULL); - osmo_signal_register_handler(SS_SMS, token_sms_cb, NULL); -} diff --git a/openbsc/src/libmsc/transaction.c b/openbsc/src/libmsc/transaction.c deleted file mode 100644 index c1441969d..000000000 --- a/openbsc/src/libmsc/transaction.c +++ /dev/null @@ -1,164 +0,0 @@ -/* GSM 04.07 Transaction handling */ - -/* (C) 2009 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void *tall_trans_ctx; - -void _gsm48_cc_trans_free(struct gsm_trans *trans); - -struct gsm_trans *trans_find_by_id(struct gsm_subscriber *subscr, - uint8_t proto, uint8_t trans_id) -{ - struct gsm_trans *trans; - struct gsm_network *net = subscr->net; - - llist_for_each_entry(trans, &net->trans_list, entry) { - if (trans->subscr == subscr && - trans->protocol == proto && - trans->transaction_id == trans_id) - return trans; - } - return NULL; -} - -struct gsm_trans *trans_find_by_callref(struct gsm_network *net, - 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_subscriber *subscr, - uint8_t protocol, uint8_t trans_id, - uint32_t callref) -{ - struct gsm_trans *trans; - - DEBUGP(DCC, "subscr=%p, subscr->net=%p\n", subscr, subscr->net); - - trans = talloc_zero(tall_trans_ctx, struct gsm_trans); - if (!trans) - return NULL; - - trans->subscr = subscr; - subscr_get(trans->subscr); - - trans->protocol = protocol; - trans->transaction_id = trans_id; - trans->callref = callref; - - llist_add_tail(&trans->entry, &subscr->net->trans_list); - - return trans; -} - -void trans_free(struct gsm_trans *trans) -{ - switch (trans->protocol) { - case GSM48_PDISC_CC: - _gsm48_cc_trans_free(trans); - break; - case GSM48_PDISC_SMS: - _gsm411_sms_trans_free(trans); - break; - } - - /* FIXME: implement a sane way to stop this. */ - if (!trans->conn && trans->paging_request) { - LOGP(DNM, LOGL_ERROR, - "Transaction freed while paging for sub: %llu\n", - trans->subscr->id); - trans->paging_request = NULL; - } - - if (trans->subscr) { - subscr_put(trans->subscr); - trans->subscr = NULL; - } - - llist_del(&trans->entry); - - if (trans->conn) - msc_release_connection(trans->conn); - - - talloc_free(trans); -} - -/* allocate an unused transaction ID for the given subscriber - * in the given protocol using the ti_flag specified */ -int trans_assign_trans_id(struct gsm_subscriber *subscr, - uint8_t protocol, uint8_t ti_flag) -{ - struct gsm_network *net = subscr->net; - struct gsm_trans *trans; - unsigned int used_tid_bitmask = 0; - int i, j, h; - - if (ti_flag) - ti_flag = 0x8; - - /* generate bitmask of already-used TIDs for this (subscr,proto) */ - llist_for_each_entry(trans, &net->trans_list, entry) { - if (trans->subscr != subscr || - trans->protocol != protocol || - trans->transaction_id == 0xff) - continue; - used_tid_bitmask |= (1 << trans->transaction_id); - } - - /* find a new one, trying to go in a 'circular' pattern */ - for (h = 6; h > 0; h--) - if (used_tid_bitmask & (1 << (h | ti_flag))) - break; - for (i = 0; i < 7; i++) { - j = ((h + i) % 7) | ti_flag; - if ((used_tid_bitmask & (1 << j)) == 0) - return j; - } - - return -1; -} - -int trans_has_conn(const struct gsm_subscriber_connection *conn) -{ - struct gsm_trans *trans; - - llist_for_each_entry(trans, &conn->bts->network->trans_list, entry) - if (trans->conn == conn) - return 1; - - return 0; -} diff --git a/openbsc/src/libmsc/ussd.c b/openbsc/src/libmsc/ussd.c deleted file mode 100644 index 76ee101a1..000000000 --- a/openbsc/src/libmsc/ussd.c +++ /dev/null @@ -1,79 +0,0 @@ -/* Network-specific handling of mobile-originated USSDs. */ - -/* (C) 2008-2009 by Harald Welte - * (C) 2008, 2009 by Holger Hans Peter Freyther - * (C) 2009 by Mike Haben - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -/* This module defines the network-specific handling of mobile-originated - USSD messages. */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -/* Declarations of USSD strings to be recognised */ -const char USSD_TEXT_OWN_NUMBER[] = "*#100#"; - -/* Forward declarations of network-specific handler functions */ -static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req); - - -/* Entrypoint - handler function common to all mobile-originated USSDs */ -int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - int rc; - struct ussd_request req; - struct gsm48_hdr *gh; - - memset(&req, 0, sizeof(req)); - gh = msgb_l3(msg); - rc = gsm0480_decode_ussd_request(gh, msgb_l3len(msg), &req); - if (req.text[0] == 0xFF) /* Release-Complete */ - return 0; - - if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.text)) { - DEBUGP(DMM, "USSD: Own number requested\n"); - rc = send_own_number(conn, msg, &req); - } else { - DEBUGP(DMM, "Unhandled USSD %s\n", req.text); - rc = gsm0480_send_ussd_reject(conn, msg, &req); - } - - /* check if we can release it */ - msc_release_connection(conn); - return rc; -} - -/* A network-specific handler function */ -static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req) -{ - char *own_number = conn->subscr->extension; - char response_string[GSM_EXTENSION_LENGTH + 20]; - - /* Need trailing CR as EOT character */ - snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); - return gsm0480_send_ussd_response(conn, msg, response_string, req); -} diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c deleted file mode 100644 index 79c345771..000000000 --- a/openbsc/src/libmsc/vty_interface_layer3.c +++ /dev/null @@ -1,987 +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 - -extern struct gsm_network *gsmnet_from_vty(struct vty *v); - -static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr, int pending) -{ - int rc; - struct gsm_auth_info ainfo; - struct gsm_auth_tuple atuple; - char expire_time[200]; - - vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, - subscr->authorized, VTY_NEWLINE); - if (subscr->name) - vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); - if (subscr->extension) - vty_out(vty, " Extension: %s%s", subscr->extension, - VTY_NEWLINE); - vty_out(vty, " LAC: %d/0x%x%s", - subscr->lac, subscr->lac, VTY_NEWLINE); - vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); - if (subscr->tmsi != GSM_RESERVED_TMSI) - vty_out(vty, " TMSI: %08X%s", subscr->tmsi, - VTY_NEWLINE); - - rc = db_get_authinfo_for_subscr(&ainfo, subscr); - if (!rc) { - vty_out(vty, " A3A8 algorithm id: %d%s", - ainfo.auth_algo, VTY_NEWLINE); - vty_out(vty, " A3A8 Ki: %s%s", - osmo_hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len), - VTY_NEWLINE); - } - - rc = db_get_lastauthtuple_for_subscr(&atuple, subscr); - if (!rc) { - vty_out(vty, " A3A8 last tuple (used %d times):%s", - atuple.use_count, VTY_NEWLINE); - vty_out(vty, " seq # : %d%s", - atuple.key_seq, VTY_NEWLINE); - vty_out(vty, " RAND : %s%s", - osmo_hexdump(atuple.rand, sizeof(atuple.rand)), - VTY_NEWLINE); - vty_out(vty, " SRES : %s%s", - osmo_hexdump(atuple.sres, sizeof(atuple.sres)), - VTY_NEWLINE); - vty_out(vty, " Kc : %s%s", - osmo_hexdump(atuple.kc, sizeof(atuple.kc)), - VTY_NEWLINE); - } - - /* print the expiration time of a subscriber */ - strftime(expire_time, sizeof(expire_time), - "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu)); - expire_time[sizeof(expire_time) - 1] = '\0'; - vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE); - - if (pending) - vty_out(vty, " Pending: %d%s", - subscr_pending_requests(subscr), VTY_NEWLINE); - - vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); -} - - -/* Subscriber */ -DEFUN(show_subscr_cache, - show_subscr_cache_cmd, - "show subscriber cache", - SHOW_STR "Show information about subscribers\n" - "Display contents of subscriber cache\n") -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, &active_subscribers, entry) { - vty_out(vty, " Subscriber:%s", VTY_NEWLINE); - subscr_dump_full_vty(vty, subscr, 0); - } - - return CMD_SUCCESS; -} - -DEFUN(sms_send_pend, - sms_send_pend_cmd, - "sms send pending", - "SMS related comamnds\n" "SMS Sending related commands\n" - "Send all pending SMS") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_sms *sms; - int id = 0; - - while (1) { - sms = db_sms_get_unsent_by_subscr(gsmnet, id, UINT_MAX); - if (!sms) - break; - - gsm411_send_sms_subscr(sms->receiver, sms); - - id = sms->receiver->id + 1; - } - - return CMD_SUCCESS; -} - -static int _send_sms_str(struct gsm_subscriber *receiver, - struct gsm_subscriber *sender, - char *str, uint8_t tp_pid) -{ - struct gsm_sms *sms; - - sms = sms_from_text(receiver, sender, 0, str); - sms->protocol_id = tp_pid; - - /* store in database for the queue */ - if (db_sms_store(sms) != 0) { - LOGP(DLSMS, LOGL_ERROR, "Failed to store SMS in Database\n"); - sms_free(sms); - return CMD_WARNING; - } - - sms_free(sms); - sms_queue_trigger(receiver->net->sms_queue); - return CMD_SUCCESS; -} - -static struct gsm_subscriber *get_subscr_by_argv(struct gsm_network *gsmnet, - const char *type, - const char *id) -{ - if (!strcmp(type, "extension")) - return subscr_get_by_extension(gsmnet, id); - else if (!strcmp(type, "imsi")) - return subscr_get_by_imsi(gsmnet, id); - else if (!strcmp(type, "tmsi")) - return subscr_get_by_tmsi(gsmnet, atoi(id)); - else if (!strcmp(type, "id")) - return subscr_get_by_id(gsmnet, atoi(id)); - - return NULL; -} -#define SUBSCR_TYPES "(extension|imsi|tmsi|id)" -#define SUBSCR_HELP "Operations on a Subscriber\n" \ - "Identify subscriber by his extension (phone number)\n" \ - "Identify subscriber by his IMSI\n" \ - "Identify subscriber by his TMSI\n" \ - "Identify subscriber by his database ID\n" \ - "Identifier for the subscriber\n" - -DEFUN(show_subscr, - show_subscr_cmd, - "show subscriber " SUBSCR_TYPES " ID", - SHOW_STR SUBSCR_HELP) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - subscr_dump_full_vty(vty, subscr, 1); - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_send_pending_sms, - subscriber_send_pending_sms_cmd, - "subscriber " SUBSCR_TYPES " ID sms pending-send", - SUBSCR_HELP "SMS Operations\n" "Send pending SMS\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr; - 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 send .LINE", - SUBSCR_HELP - "Silent SMS Operation\n" "Send Silent SMS\n" "Actual SMS text\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - struct gsm_subscriber *sender = get_subscr_by_argv(gsmnet, argv[2], argv[3]); - char *str; - int rc; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - rc = CMD_WARNING; - goto err; - } - - if (!sender) { - vty_out(vty, "%% No sender found for %s %s%s", - argv[2], argv[3], VTY_NEWLINE); - rc = CMD_WARNING; - goto err; - } - - str = argv_concat(argv, argc, 4); - rc = _send_sms_str(subscr, sender, str, 64); - talloc_free(str); - -err: - if (sender) - subscr_put(sender); - - if (subscr) - subscr_put(subscr); - - return rc; -} - -#define CHAN_TYPES "(any|tch/f|tch/any|sdcch)" -#define CHAN_TYPE_HELP \ - "Any channel\n" \ - "TCH/F channel\n" \ - "Any TCH channel\n" \ - "SDCCH channel\n" - -DEFUN(subscriber_silent_call_start, - subscriber_silent_call_start_cmd, - "subscriber " SUBSCR_TYPES " ID silent-call start (any|tch/f|tch/any|sdcch)", - SUBSCR_HELP "Silent call operation\n" "Start silent call\n" - CHAN_TYPE_HELP) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - int rc, type; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[2], "tch/f")) - type = RSL_CHANNEED_TCH_F; - else if (!strcmp(argv[2], "tch/any")) - type = RSL_CHANNEED_TCH_ForH; - else if (!strcmp(argv[2], "sdcch")) - type = RSL_CHANNEED_SDCCH; - else - type = RSL_CHANNEED_ANY; /* Defaults to ANY */ - - rc = gsm_silent_call_start(subscr, vty, type); - if (rc <= 0) { - vty_out(vty, "%% Subscriber not attached%s", - VTY_NEWLINE); - subscr_put(subscr); - return CMD_WARNING; - } - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_silent_call_stop, - subscriber_silent_call_stop_cmd, - "subscriber " SUBSCR_TYPES " ID silent-call stop", - SUBSCR_HELP "Silent call operation\n" "Stop silent call\n" - CHAN_TYPE_HELP) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - int rc; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - rc = gsm_silent_call_stop(subscr); - if (rc < 0) { - subscr_put(subscr); - return CMD_WARNING; - } - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_ussd_notify, - subscriber_ussd_notify_cmd, - "subscriber " SUBSCR_TYPES " ID ussd-notify (0|1|2) .TEXT", - SUBSCR_HELP "Send a USSD notify to the subscriber\n" - "Alerting Level 0\n" - "Alerting Level 1\n" - "Alerting Level 2\n" - "Text of USSD message to send\n") -{ - char *text; - struct gsm_subscriber_connection *conn; - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - int level; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - level = atoi(argv[2]); - text = argv_concat(argv, argc, 3); - if (!text) { - subscr_put(subscr); - return CMD_WARNING; - } - - conn = connection_for_subscr(subscr); - if (!conn) { - vty_out(vty, "%% An active connection is required for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - subscr_put(subscr); - talloc_free(text); - return CMD_WARNING; - } - - gsm0480_send_ussdNotify(conn, level, text); - gsm0480_send_releaseComplete(conn); - - subscr_put(subscr); - talloc_free(text); - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_authorizde, - ena_subscr_authorized_cmd, - "subscriber " SUBSCR_TYPES " ID authorized (0|1)", - SUBSCR_HELP "(De-)Authorize subscriber in HLR\n" - "Subscriber should NOT be authorized\n" - "Subscriber should be authorized\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - subscr->authorized = atoi(argv[2]); - db_sync_subscriber(subscr); - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_name, - ena_subscr_name_cmd, - "subscriber " SUBSCR_TYPES " ID name .NAME", - SUBSCR_HELP "Set the name of the subscriber\n" - "Name of the Subscriber\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - char *name; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - name = argv_concat(argv, argc, 2); - if (!name) { - subscr_put(subscr); - return CMD_WARNING; - } - - strncpy(subscr->name, name, sizeof(subscr->name)); - talloc_free(name); - db_sync_subscriber(subscr); - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_extension, - ena_subscr_extension_cmd, - "subscriber " SUBSCR_TYPES " ID extension EXTENSION", - SUBSCR_HELP "Set the extension (phone number) of the subscriber\n" - "Extension (phone number)\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - const char *ext = argv[2]; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - strncpy(subscr->extension, ext, sizeof(subscr->extension)); - db_sync_subscriber(subscr); - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_clear, - ena_subscr_clear_cmd, - "subscriber " SUBSCR_TYPES " ID clear-requests", - SUBSCR_HELP "Clear the paging requests for this subscriber\n") -{ - int del; - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - del = subscr_pending_clear(subscr); - vty_out(vty, "Cleared %d pending requests.%s", del, VTY_NEWLINE); - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_pend, - ena_subscr_pend_cmd, - "subscriber " SUBSCR_TYPES " ID show-pending", - SUBSCR_HELP "Clear the paging requests for this subscriber\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - subscr_pending_dump(subscr, vty); - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_kick, - ena_subscr_kick_cmd, - "subscriber " SUBSCR_TYPES " ID kick-pending", - SUBSCR_HELP "Clear the paging requests for this subscriber\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - subscr_pending_kick(subscr); - subscr_put(subscr); - - return CMD_SUCCESS; -} - -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); - vty_out(vty, "%d subscriber(s) were purged.%s", purged, VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(subscriber_update, - subscriber_update_cmd, - "subscriber " SUBSCR_TYPES " ID update", - SUBSCR_HELP "Update the subscriber data from the dabase.\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - subscr_update_from_db(subscr); - subscr_put(subscr); - return CMD_SUCCESS; -} - -static int scall_cbfn(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct scall_signal_data *sigdata = signal_data; - struct vty *vty = sigdata->data; - - switch (signal) { - case S_SCALL_SUCCESS: - vty_out(vty, "%% silent call on ARFCN %u timeslot %u%s", - sigdata->conn->lchan->ts->trx->arfcn, sigdata->conn->lchan->ts->nr, - VTY_NEWLINE); - break; - case S_SCALL_EXPIRED: - vty_out(vty, "%% silent call expired paging%s", VTY_NEWLINE); - break; - } - return 0; -} - -DEFUN(show_stats, - show_stats_cmd, - "show statistics", - SHOW_STR "Display network statistics\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - openbsc_vty_print_statistics(vty, net); - vty_out(vty, "Channel Requests : %lu total, %lu no channel%s", - osmo_counter_get(net->stats.chreq.total), - osmo_counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); - vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s", - osmo_counter_get(net->stats.loc_upd_type.attach), - osmo_counter_get(net->stats.loc_upd_type.normal), - osmo_counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE); - vty_out(vty, "IMSI Detach Indications : %lu%s", - osmo_counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE); - vty_out(vty, "Location Update Response: %lu accept, %lu reject%s", - osmo_counter_get(net->stats.loc_upd_resp.accept), - osmo_counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE); - vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, " - "%lu completed, %lu failed%s", - osmo_counter_get(net->stats.handover.attempted), - osmo_counter_get(net->stats.handover.no_channel), - osmo_counter_get(net->stats.handover.timeout), - osmo_counter_get(net->stats.handover.completed), - osmo_counter_get(net->stats.handover.failed), VTY_NEWLINE); - vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s", - osmo_counter_get(net->stats.sms.submitted), - osmo_counter_get(net->stats.sms.no_receiver), VTY_NEWLINE); - vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s", - osmo_counter_get(net->stats.sms.delivered), - osmo_counter_get(net->stats.sms.rp_err_mem), - osmo_counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE); - vty_out(vty, "MO Calls : %lu setup, %lu connect ack%s", - osmo_counter_get(net->stats.call.mo_setup), - osmo_counter_get(net->stats.call.mo_connect_ack), VTY_NEWLINE); - vty_out(vty, "MT Calls : %lu setup, %lu connect%s", - osmo_counter_get(net->stats.call.mt_setup), - osmo_counter_get(net->stats.call.mt_connect), VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(show_smsqueue, - show_smsqueue_cmd, - "show sms-queue", - SHOW_STR "Display SMSqueue statistics\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_stats(net->sms_queue, vty); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_trigger, - smsqueue_trigger_cmd, - "sms-queue trigger", - "SMS Queue\n" "Trigger sending messages\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_trigger(net->sms_queue); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_max, - smsqueue_max_cmd, - "sms-queue max-pending <1-500>", - "SMS Queue\n" "SMS to deliver in parallel\n" "Amount\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_set_max_pending(net->sms_queue, atoi(argv[0])); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_clear, - smsqueue_clear_cmd, - "sms-queue clear", - "SMS Queue\n" "Clear the queue of pending SMS\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_clear(net->sms_queue); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_fail, - smsqueue_fail_cmd, - "sms-queue max-failure <1-500>", - "SMS Queue\n" "Maximum amount of delivery failures\n" "Amount\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_set_max_failure(net->sms_queue, atoi(argv[0])); - return CMD_SUCCESS; -} - - -DEFUN(cfg_mncc_int, cfg_mncc_int_cmd, - "mncc-int", "Configure internal MNCC handler") -{ - vty->node = MNCC_INT_NODE; - - return CMD_SUCCESS; -} - -static struct cmd_node mncc_int_node = { - MNCC_INT_NODE, - "%s(config-mncc-int)# ", - 1, -}; - -static const struct value_string tchf_codec_names[] = { - { GSM48_CMODE_SPEECH_V1, "fr" }, - { GSM48_CMODE_SPEECH_EFR, "efr" }, - { GSM48_CMODE_SPEECH_AMR, "amr" }, - { 0, NULL } -}; - -static const struct value_string tchh_codec_names[] = { - { GSM48_CMODE_SPEECH_V1, "hr" }, - { GSM48_CMODE_SPEECH_AMR, "amr" }, - { 0, NULL } -}; - -static int config_write_mncc_int(struct vty *vty) -{ - 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); - - 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; -} - -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_send_sms_cmd); - install_element_ve(&subscriber_silent_sms_cmd); - install_element_ve(&subscriber_silent_call_start_cmd); - install_element_ve(&subscriber_silent_call_stop_cmd); - install_element_ve(&subscriber_ussd_notify_cmd); - install_element_ve(&subscriber_update_cmd); - install_element_ve(&show_stats_cmd); - install_element_ve(&show_smsqueue_cmd); - - install_element(ENABLE_NODE, &ena_subscr_name_cmd); - install_element(ENABLE_NODE, &ena_subscr_extension_cmd); - install_element(ENABLE_NODE, &ena_subscr_authorized_cmd); - install_element(ENABLE_NODE, &ena_subscr_a3a8_cmd); - install_element(ENABLE_NODE, &ena_subscr_clear_cmd); - install_element(ENABLE_NODE, &ena_subscr_pend_cmd); - install_element(ENABLE_NODE, &ena_subscr_kick_cmd); - install_element(ENABLE_NODE, &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(CONFIG_NODE, &cfg_mncc_int_cmd); - install_node(&mncc_int_node, config_write_mncc_int); - install_default(MNCC_INT_NODE); - install_element(MNCC_INT_NODE, &ournode_exit_cmd); - install_element(MNCC_INT_NODE, &ournode_end_cmd); - install_element(MNCC_INT_NODE, &mnccint_def_codec_f_cmd); - install_element(MNCC_INT_NODE, &mnccint_def_codec_h_cmd); - - install_element(CFG_LOG_NODE, &log_level_sms_cmd); - - return 0; -} diff --git a/openbsc/src/libtrau/Makefile.am b/openbsc/src/libtrau/Makefile.am deleted file mode 100644 index 0c8cf17b5..000000000 --- a/openbsc/src/libtrau/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOABIS_LIBS) $(COVERAGE_LDFLAGS) - -noinst_LIBRARIES = libtrau.a - -libtrau_a_SOURCES = rtp_proxy.c trau_mux.c trau_upqueue.c diff --git a/openbsc/src/libtrau/rtp_proxy.c b/openbsc/src/libtrau/rtp_proxy.c deleted file mode 100644 index 0074b4a04..000000000 --- a/openbsc/src/libtrau/rtp_proxy.c +++ /dev/null @@ -1,757 +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 - -/* attempt to determine byte order */ -#include -#include - -#ifndef __BYTE_ORDER -# ifdef __APPLE__ -# define __BYTE_ORDER __DARWIN_BYTE_ORDER -# define __LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN -# define __BIG_ENDIAN __DARWIN_BIG_ENDIAN -# else -# error "__BYTE_ORDER should be defined by someone" -# endif -#endif - -static LLIST_HEAD(rtp_sockets); - -/* should we mangle the CNAME inside SDES of RTCP packets? We disable - * this by default, as it seems to be not needed */ -static int mangle_rtcp_cname = 0; - -enum rtp_bfd_priv { - RTP_PRIV_NONE, - RTP_PRIV_RTP, - RTP_PRIV_RTCP -}; - -#define RTP_ALLOC_SIZE 1500 - -/* according to RFC 1889 */ -struct rtcp_hdr { - uint8_t byte0; - uint8_t type; - uint16_t length; -} __attribute__((packed)); - -#define RTCP_TYPE_SDES 202 - -#define RTCP_IE_CNAME 1 - -/* according to RFC 3550 */ -struct rtp_hdr { -#if __BYTE_ORDER == __LITTLE_ENDIAN - uint8_t csrc_count:4, - extension:1, - padding:1, - version:2; - uint8_t payload_type:7, - marker:1; -#elif __BYTE_ORDER == __BIG_ENDIAN - uint8_t version:2, - padding:1, - extension:1, - csrc_count:4; - uint8_t marker:1, - payload_type:7; -#endif - uint16_t sequence; - uint32_t timestamp; - uint32_t ssrc; -} __attribute__((packed)); - -struct rtp_x_hdr { - uint16_t by_profile; - uint16_t length; -} __attribute__((packed)); - -#define RTP_VERSION 2 - -/* decode an rtp frame and create a new buffer with payload */ -static int rtp_decode(struct msgb *msg, 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; - 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 < 0) { - 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 != 33) { - DEBUGPC(DLMUX, "received RTP full rate frame with " - "payload length != 32 (len = %d)\n", - payload_len); - return -EINVAL; - } - break; - case RTP_PT_GSM_EFR: - msg_type = GSM_TCHF_FRAME_EFR; - break; - default: - DEBUGPC(DLMUX, "received RTP frame with unknown payload " - "type %d\n", rtph->payload_type); - return -EINVAL; - } - - new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len, - "GSM-DATA"); - if (!new_msg) - return -ENOMEM; - frame = (struct gsm_data_frame *)(new_msg->data); - frame->msg_type = msg_type; - frame->callref = callref; - memcpy(frame->data, payload, payload_len); - msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len); - - *data = new_msg; - return 0; -} - -/* "to - from" */ -static void tv_difference(struct timeval *diff, const struct timeval *from, - const struct timeval *__to) -{ - struct timeval _to = *__to, *to = &_to; - - if (to->tv_usec < from->tv_usec) { - to->tv_sec -= 1; - to->tv_usec += 1000000; - } - - diff->tv_usec = to->tv_usec - from->tv_usec; - diff->tv_sec = to->tv_sec - from->tv_sec; -} - -/*! \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; - int payload_type; - int payload_len; - int duration; /* in samples */ - - if (rs->tx_action != RTP_SEND_DOWNSTREAM) { - /* initialize sequences */ - rs->tx_action = RTP_SEND_DOWNSTREAM; - rs->transmit.ssrc = rand(); - rs->transmit.sequence = random(); - rs->transmit.timestamp = random(); - } - - switch (frame->msg_type) { - case GSM_TCHF_FRAME: - payload_type = RTP_PT_GSM_FULL; - payload_len = 33; - duration = 160; - break; - case GSM_TCHF_FRAME_EFR: - payload_type = RTP_PT_GSM_EFR; - payload_len = 31; - duration = 160; - break; - default: - DEBUGPC(DLMUX, "unsupported message type %d\n", - frame->msg_type); - return -EINVAL; - } - - { - struct timeval tv, tv_diff; - long int usec_diff, frame_diff; - - gettimeofday(&tv, NULL); - tv_difference(&tv_diff, &rs->transmit.last_tv, &tv); - rs->transmit.last_tv = tv; - - usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec; - frame_diff = (usec_diff / 20000); - - if (abs(frame_diff) > 1) { - long int frame_diff_excess = frame_diff - 1; - - LOGP(DLMUX, LOGL_NOTICE, - "Correcting frame difference of %ld frames\n", frame_diff_excess); - rs->transmit.sequence += frame_diff_excess; - rs->transmit.timestamp += frame_diff_excess * duration; - } - } - - msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL"); - if (!msg) - return -ENOMEM; - rtph = (struct rtp_hdr *)msg->data; - rtph->version = RTP_VERSION; - rtph->padding = 0; - rtph->extension = 0; - rtph->csrc_count = 0; - rtph->marker = 0; - rtph->payload_type = payload_type; - rtph->sequence = htons(rs->transmit.sequence++); - rtph->timestamp = htonl(rs->transmit.timestamp); - rs->transmit.timestamp += duration; - rtph->ssrc = htonl(rs->transmit.ssrc); - memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len); - msgb_put(msg, sizeof(struct rtp_hdr) + payload_len); - msgb_enqueue(&rss->tx_queue, msg); - rss->bfd.when |= BSC_FD_WRITE; - - return 0; -} - -/* iterate over all chunks in one RTCP message, look for CNAME IEs and - * replace all of those with 'new_cname' */ -static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh, - 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) { } - break; - } - len = *cur++; - - if (tag == RTCP_IE_CNAME) { - /* we've found the CNAME, lets mangle it */ - if (len < strlen(new_cname)) { - /* we need to make more space */ - int increase = strlen(new_cname) - len; - - msgb_push(msg, increase); - memmove(cur+len+increase, cur+len, - rtcp_end - (cur+len)); - /* FIXME: we have to respect RTCP - * padding/alignment rules! */ - len += increase; - *(cur-1) += increase; - rtcp_end += increase; - *rtcp_len += increase; - } - /* copy new CNAME into message */ - memcpy(cur, new_cname, strlen(new_cname)); - /* FIXME: zero the padding in case new CNAME - * is smaller than old one !!! */ - } - cur += len; - } - } - - return 0; -} - -static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs) -{ - struct rtp_sub_socket *rss = &rs->rtcp; - struct rtcp_hdr *rtph; - uint16_t old_len; - int rc; - - if (!mangle_rtcp_cname) - return 0; - - printf("RTCP\n"); - /* iterate over list of RTCP messages */ - rtph = (struct rtcp_hdr *)msg->data; - while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) { - old_len = (ntohs(rtph->length) + 1) * 4; - if ((void *)rtph + old_len > (void *)msg->data + msg->len) { - DEBUGPC(DLMUX, "received RTCP packet too short for " - "length element\n"); - return -EINVAL; - } - if (rtph->type == RTCP_TYPE_SDES) { - char new_cname[255]; - strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr), - sizeof(new_cname)); - new_cname[sizeof(new_cname)-1] = '\0'; - rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len, - new_cname); - if (rc < 0) - return rc; - } - rtph = (void *)rtph + old_len; - } - - return 0; -} - -/* read from incoming RTP/RTCP socket */ -static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss) -{ - int rc; - struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP"); - struct msgb *new_msg; - struct rtp_sub_socket *other_rss; - - if (!msg) - return -ENOMEM; - - rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE); - if (rc <= 0) { - rss->bfd.when &= ~BSC_FD_READ; - return rc; - } - - msgb_put(msg, rc); - - switch (rs->rx_action) { - case RTP_PROXY: - if (!rs->proxy.other_sock) { - rc = -EIO; - goto out_free; - } - if (rss->bfd.priv_nr == RTP_PRIV_RTP) - other_rss = &rs->proxy.other_sock->rtp; - else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) { - other_rss = &rs->proxy.other_sock->rtcp; - /* modify RTCP SDES CNAME */ - rc = rtcp_mangle(msg, rs); - if (rc < 0) - goto out_free; - } else { - rc = -EINVAL; - goto out_free; - } - msgb_enqueue(&other_rss->tx_queue, msg); - other_rss->bfd.when |= BSC_FD_WRITE; - break; - - case RTP_RECV_UPSTREAM: - if (!rs->receive.callref || !rs->receive.net) { - rc = -EIO; - goto out_free; - } - if (rss->bfd.priv_nr == RTP_PRIV_RTCP) { - if (!mangle_rtcp_cname) { - msgb_free(msg); - break; - } - /* modify RTCP SDES CNAME */ - rc = rtcp_mangle(msg, rs); - if (rc < 0) - goto out_free; - msgb_enqueue(&rss->tx_queue, msg); - rss->bfd.when |= BSC_FD_WRITE; - break; - } - if (rss->bfd.priv_nr != RTP_PRIV_RTP) { - rc = -EINVAL; - goto out_free; - } - rc = rtp_decode(msg, rs->receive.callref, &new_msg); - if (rc < 0) - goto out_free; - msgb_free(msg); - trau_tx_to_mncc(rs->receive.net, new_msg); - break; - - case RTP_NONE: /* if socket exists, but disabled by app */ - msgb_free(msg); - break; - } - - return 0; - -out_free: - msgb_free(msg); - return rc; -} - -/* \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 9272ac04f..000000000 --- a/openbsc/src/libtrau/trau_mux.c +++ /dev/null @@ -1,329 +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 - -/* 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 -}; - -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[] = { 0, 0, 0, 1, 0 }; - -/* 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; - struct gsm_data_frame *frame; - unsigned char *data; - int i, j, k, l, o; - /* frame shall be sent to upqueue */ - if (!(ue = lookup_trau_upqueue(src_e1_ss))) - return -EINVAL; - if (!ue->callref) - return -EINVAL; - if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check))) - DEBUGPC(DLMUX, "illegal trau (C1-C5) %s\n", - osmo_hexdump(tf.c_bits, sizeof(c_bits_check))); - msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, - "GSM-DATA"); - if (!msg) - return -ENOMEM; - - frame = (struct gsm_data_frame *)msg->data; - memset(frame, 0, sizeof(struct gsm_data_frame)); - data = frame->data; - data[0] = 0xd << 4; - /* reassemble d-bits */ - i = 0; /* counts bits */ - j = 4; /* counts output bits */ - k = gsm_fr_map[0]-1; /* current number bit in element */ - l = 0; /* counts element bits */ - o = 0; /* offset input bits */ - while (i < 260) { - data[j/8] |= (tf.d_bits[k+o] << (7-(j%8))); - if (--k < 0) { - o += gsm_fr_map[l]; - k = gsm_fr_map[++l]-1; - } - i++; - j++; - } - frame->msg_type = GSM_TCHF_FRAME; - frame->callref = ue->callref; - msgb_put(msg, sizeof(struct gsm_data_frame) + 33); - 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; -} - -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; - int i, j, k, l, o; - unsigned char *data = frame->data; - struct decoded_trau_frame tf; - - mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts); - if (!mx) - return -EINVAL; - - switch (frame->msg_type) { - case GSM_TCHF_FRAME: - /* set c-bits and t-bits */ - tf.c_bits[0] = 1; - tf.c_bits[1] = 1; - tf.c_bits[2] = 1; - tf.c_bits[3] = 0; - tf.c_bits[4] = 0; - memset(&tf.c_bits[5], 0, 6); - memset(&tf.c_bits[11], 1, 10); - memset(&tf.t_bits[0], 1, 4); - /* reassemble d-bits */ - i = 0; /* counts bits */ - j = 4; /* counts input bits */ - k = gsm_fr_map[0]-1; /* current number bit in element */ - l = 0; /* counts element bits */ - o = 0; /* offset output bits */ - while (i < 260) { - tf.d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1; - if (--k < 0) { - o += gsm_fr_map[l]; - k = gsm_fr_map[++l]-1; - } - i++; - j++; - } - break; - default: - DEBUGPC(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); -} diff --git a/openbsc/src/libtrau/trau_upqueue.c b/openbsc/src/libtrau/trau_upqueue.c deleted file mode 100644 index f8edaf0ff..000000000 --- a/openbsc/src/libtrau/trau_upqueue.c +++ /dev/null @@ -1,27 +0,0 @@ -/* trau_upqueue.c - Pass msgb's up the chain */ - -/* (C) 2010 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for 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 63972c838..000000000 --- a/openbsc/src/osmo-bsc/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) $(LIBOSMOABIS_CFLAGS) -AM_LDFLAGS = $(COVERAGE_LDFLAGS) - -bin_PROGRAMS = osmo-bsc - - -osmo_bsc_SOURCES = osmo_bsc_main.c osmo_bsc_vty.c osmo_bsc_api.c \ - osmo_bsc_grace.c osmo_bsc_msc.c osmo_bsc_sccp.c \ - osmo_bsc_filter.c osmo_bsc_bssap.c osmo_bsc_audio.c osmo_bsc_ctrl.c -# once again since TRAU uses CC symbol :( -osmo_bsc_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libmsc/libmsc.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(top_builddir)/src/libctrl/libctrl.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOSCCP_LIBS) $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS) $(LIBOSMOABIS_LIBS) 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 df8c0444f..000000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_api.c +++ /dev/null @@ -1,362 +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 - -#define return_when_not_connected(conn) \ - if (!conn->sccp_con) {\ - LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \ - return; \ - } - -#define return_when_not_connected_val(conn, ret) \ - if (!conn->sccp_con) {\ - LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \ - return ret; \ - } - -#define queue_msg_or_return(resp) \ - if (!resp) { \ - LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); \ - return; \ - } \ - bsc_queue_for_msc(conn->sccp_con, resp); - -static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause); -static int complete_layer3(struct gsm_subscriber_connection *conn, - struct msgb *msg, struct osmo_msc_data *msc); - -static uint16_t get_network_code_for_msc(struct osmo_msc_data *msc) -{ - if (msc->core_ncc != -1) - return msc->core_ncc; - return msc->network->network_code; -} - -static uint16_t get_country_code_for_msc(struct osmo_msc_data *msc) -{ - if (msc->core_mcc != -1) - return msc->core_mcc; - return msc->network->country_code; -} - -static 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); -} - -/* - * Instruct to reserve data for a new connectiom, create the complete - * layer three message, send it to open the connection. - */ -static int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, - uint16_t chosen_channel) -{ - struct osmo_msc_data *msc; - - LOGP(DMSC, LOGL_INFO, "Tx MSC COMPL L3\n"); - - /* find the MSC link we want to use */ - msc = bsc_find_msc(conn, msg); - if (!msc) { - LOGP(DMSC, LOGL_ERROR, "Failed to find a MSC for a connection.\n"); - return -1; - } - - return complete_layer3(conn, msg, msc); -} - -static int complete_layer3(struct gsm_subscriber_connection *conn, - struct msgb *msg, struct osmo_msc_data *msc) -{ - struct msgb *resp; - uint16_t network_code; - uint16_t country_code; - - /* allocate resource for a new connection */ - if (bsc_create_new_connection(conn, msc) != 0) - return BSC_API_CONN_POL_REJECT; - - network_code = get_network_code_for_msc(conn->sccp_con->msc); - country_code = get_country_code_for_msc(conn->sccp_con->msc); - - bsc_scan_bts_msg(conn, msg); - resp = gsm0808_create_layer3(msg, network_code, country_code, - conn->bts->location_area_code, - conn->bts->cell_identity); - if (!resp) { - LOGP(DMSC, LOGL_DEBUG, "Failed to create layer3 message.\n"); - sccp_connection_free(conn->sccp_con->sccp); - bsc_delete_connection(conn->sccp_con); - return BSC_API_CONN_POL_REJECT; - } - - if (bsc_open_connection(conn->sccp_con, resp) != 0) { - sccp_connection_free(conn->sccp_con->sccp); - bsc_delete_connection(conn->sccp_con); - msgb_free(resp); - return BSC_API_CONN_POL_REJECT; - } - - return BSC_API_CONN_POL_ACCEPT; -} - -/* - * Plastic surgery... we want to give up the current connection - */ -static int move_to_msc(struct gsm_subscriber_connection *_conn, - struct msgb *msg, struct osmo_msc_data *msc) -{ - struct osmo_bsc_sccp_con *old_con = _conn->sccp_con; - - /* - * 1. Give up the old connection. - * This happens by sending a clear request to the MSC, - * it should end with the MSC releasing the connection. - */ - old_con->conn = NULL; - bsc_clear_request(_conn, 0); - - /* - * 2. Attempt to create a new connection to the local - * MSC. If it fails the caller will need to handle this - * properly. - */ - _conn->sccp_con = NULL; - if (complete_layer3(_conn, msg, msc) != BSC_API_CONN_POL_ACCEPT) { - gsm0808_clear(_conn); - subscr_con_free(_conn); - return 1; - } - - return 2; -} - -static int handle_cc_setup(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gh->proto_discr & 0x0f; - uint8_t mtype = gh->msg_type & 0xbf; - - struct osmo_msc_data *msc; - struct gsm_mncc_number called; - struct tlv_parsed tp; - unsigned payload_len; - - char _dest_nr[35]; - - /* - * Do we have a setup message here? if not return fast. - */ - if (pdisc != GSM48_PDISC_CC || mtype != GSM48_MT_CC_SETUP) - return 0; - - payload_len = msgb_l3len(msg) - sizeof(*gh); - - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { - LOGP(DMSC, LOGL_ERROR, "Called BCD not present in setup.\n"); - return -1; - } - - memset(&called, 0, sizeof(called)); - gsm48_decode_called(&called, - TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1); - - if (called.plan != 1 && called.plan != 0) - return 0; - - if (called.plan == 1 && called.type == 1) { - _dest_nr[0] = _dest_nr[1] = '0'; - memcpy(_dest_nr + 2, called.number, sizeof(called.number)); - } else - memcpy(_dest_nr, called.number, sizeof(called.number)); - - /* - * Check if the connection should be moved... - */ - llist_for_each_entry(msc, &conn->bts->network->bsc_data->mscs, entry) { - if (msc->type != MSC_CON_TYPE_LOCAL) - continue; - if (!msc->local_pref) - continue; - if (regexec(&msc->local_pref_reg, _dest_nr, 0, NULL, 0) != 0) - continue; - - return move_to_msc(conn, msg, msc); - } - - return 0; -} - - -static void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) -{ - 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; - - 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 gsm48_multi_rate_conf *conf) -{ - struct osmo_msc_data *msc; - - 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; - - conf->ver = 1; - conf->icmi = 1; - - /* maybe gcc see's it is copy of _one_ byte */ - conf->m4_75 = msc->amr_conf.m4_75; - conf->m5_15 = msc->amr_conf.m5_15; - conf->m5_90 = msc->amr_conf.m5_90; - conf->m6_70 = msc->amr_conf.m6_70; - conf->m7_40 = msc->amr_conf.m7_40; - conf->m7_95 = msc->amr_conf.m7_95; - conf->m10_2 = msc->amr_conf.m10_2; - conf->m12_2 = msc->amr_conf.m12_2; -} - -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 660d88497..000000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_audio.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * ipaccess audio handling - * - * (C) 2009-2010 by Holger Hans Peter Freyther - * (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 c2c241732..000000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_bssap.c +++ /dev/null @@ -1,556 +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 - -static uint16_t read_data16(const uint8_t *data) -{ - uint16_t res; - - memcpy(&res, data, sizeof(res)); - return res; -} - -/* - * helpers for the assignment command - */ -enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio) -{ - if (audio->hr) { - switch (audio->ver) { - case 1: - return GSM0808_PERM_HR1; - break; - case 2: - return GSM0808_PERM_HR2; - break; - case 3: - return GSM0808_PERM_HR3; - break; - default: - LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); - return GSM0808_PERM_FR1; - } - } else { - switch (audio->ver) { - case 1: - return GSM0808_PERM_FR1; - break; - case 2: - return GSM0808_PERM_FR2; - break; - case 3: - return GSM0808_PERM_FR3; - break; - default: - LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); - return GSM0808_PERM_HR1; - } - } -} - -enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech) -{ - switch (speech) { - case GSM0808_PERM_HR1: - case GSM0808_PERM_FR1: - return GSM48_CMODE_SPEECH_V1; - break; - case GSM0808_PERM_HR2: - case GSM0808_PERM_FR2: - return GSM48_CMODE_SPEECH_EFR; - break; - case GSM0808_PERM_HR3: - case GSM0808_PERM_FR3: - return GSM48_CMODE_SPEECH_AMR; - break; - } - - LOGP(DMSC, LOGL_FATAL, "Should not be reached.\n"); - return GSM48_CMODE_SPEECH_AMR; -} - -static int bssmap_handle_reset_ack(struct osmo_msc_data *msc, - struct msgb *msg, unsigned int length) -{ - LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n"); - return 0; -} - -/* GSM 08.08 § 3.2.1.19 */ -static int bssmap_handle_paging(struct osmo_msc_data *msc, - struct msgb *msg, unsigned int payload_length) -{ - struct gsm_subscriber *subscr; - struct tlv_parsed tp; - char mi_string[GSM48_MI_SIZE]; - uint32_t tmsi = GSM_RESERVED_TMSI; - unsigned int lac = GSM_LAC_RESERVED_ALL_BTS; - uint8_t data_length; - const uint8_t *data; - uint8_t chan_needed = RSL_CHANNEED_ANY; - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0); - - if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) { - LOGP(DMSC, LOGL_ERROR, "Mandatory IMSI not present.\n"); - return -1; - } else if ((TLVP_VAL(&tp, GSM0808_IE_IMSI)[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) { - LOGP(DMSC, LOGL_ERROR, "Wrong content in the IMSI\n"); - return -1; - } - - if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) { - LOGP(DMSC, LOGL_ERROR, "Mandatory CELL IDENTIFIER LIST not present.\n"); - return -1; - } - - if (TLVP_PRESENT(&tp, GSM0808_IE_TMSI) && - TLVP_LEN(&tp, GSM0808_IE_TMSI) == 4) { - tmsi = ntohl(*(uint32_t *) TLVP_VAL(&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 = ntohs(read_data16(&data[1])); - } else if (data_length > 1 || (data[0] & 0x0f) != CELL_IDENT_BSS) { - LOGP(DMSC, LOGL_ERROR, "Unsupported Cell Identifier List: %s\n", osmo_hexdump(data, data_length)); - return -1; - } - - if (TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_NEEDED) && TLVP_LEN(&tp, GSM0808_IE_CHANNEL_NEEDED) == 1) - chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03; - - if (TLVP_PRESENT(&tp, GSM0808_IE_EMLPP_PRIORITY)) { - LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n"); - } - - subscr = subscr_get_or_create(msc->network, mi_string); - if (!subscr) { - LOGP(DMSC, LOGL_ERROR, "Failed to allocate a subscriber for %s\n", mi_string); - return -1; - } - - subscr->lac = lac; - subscr->tmsi = tmsi; - - LOGP(DMSC, LOGL_INFO, "Paging request from MSC IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", mi_string, tmsi, tmsi, lac); - bsc_grace_paging_request(subscr, chan_needed, msc); - return 0; -} - -/* - * GSM 08.08 § 3.1.9.1 and 3.2.1.21... - * release our gsm_subscriber_connection and send message - */ -static int bssmap_handle_clear_command(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int payload_length) -{ - struct msgb *resp; - - /* TODO: handle the cause of this package */ - - if (conn->conn) { - LOGP(DMSC, LOGL_INFO, "Releasing all transactions on %p\n", conn); - gsm0808_clear(conn->conn); - subscr_con_free(conn->conn); - conn->conn = NULL; - } - - /* send the clear complete message */ - resp = gsm0808_create_clear_complete(); - if (!resp) { - LOGP(DMSC, LOGL_ERROR, "Sending clear complete failed.\n"); - return -1; - } - - bsc_queue_for_msc(conn, resp); - return 0; -} - -/* - * GSM 08.08 § 3.4.7 cipher mode handling. We will have to pick - * the cipher to be used for this. In case we are already using - * a cipher we will have to send cipher mode reject to the MSC, - * otherwise we will have to pick something that we and the MS - * is supporting. Currently we are doing it in a rather static - * way by picking one ecnryption or no encrytpion. - */ -static int bssmap_handle_cipher_mode(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int payload_length) -{ - uint16_t len; - struct gsm_network *network = NULL; - const uint8_t *data; - struct tlv_parsed tp; - struct msgb *resp; - int reject_cause = -1; - int include_imeisv = 1; - - if (!conn->conn) { - LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n"); - goto reject; - } - - if (conn->ciphering_handled) { - LOGP(DMSC, LOGL_ERROR, "Already seen ciphering command. Protocol Error.\n"); - goto reject; - } - - conn->ciphering_handled = 1; - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_ENCRYPTION_INFORMATION)) { - LOGP(DMSC, LOGL_ERROR, "IE Encryption Information missing.\n"); - goto reject; - } - - /* - * check if our global setting is allowed - * - Currently we check for A5/0 and A5/1 - * - Copy the key if that is necessary - * - Otherwise reject - */ - len = TLVP_LEN(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); - if (len < 1) { - LOGP(DMSC, LOGL_ERROR, "IE Encryption Information is too short.\n"); - goto reject; - } - - network = conn->conn->bts->network; - data = TLVP_VAL(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); - - if (TLVP_PRESENT(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)) - include_imeisv = TLVP_VAL(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)[0] & 0x1; - - if (network->a5_encryption == 0 && (data[0] & 0x1) == 0x1) { - gsm0808_cipher_mode(conn->conn, 0, NULL, 0, include_imeisv); - } else if (network->a5_encryption != 0 && (data[0] & 0x2) == 0x2) { - gsm0808_cipher_mode(conn->conn, 1, &data[1], len - 1, include_imeisv); - } else { - LOGP(DMSC, LOGL_ERROR, "Can not select encryption...\n"); - goto reject; - } - - return 0; - -reject: - resp = gsm0808_create_cipher_reject(reject_cause); - if (!resp) { - LOGP(DMSC, LOGL_ERROR, "Sending the cipher reject failed.\n"); - return -1; - } - - bsc_queue_for_msc(conn, resp); - return -1; -} - -/* - * Handle the assignment request message. - * - * See §3.2.1.1 for the message type - */ -static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int length) -{ - struct msgb *resp; - struct osmo_msc_data *msc; - struct tlv_parsed tp; - uint8_t *data; - uint16_t cic; - 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; - } - - cic = ntohs(read_data16(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE))); - timeslot = cic & 0x1f; - multiplex = (cic & ~0x1f) >> 5; - - /* - * Currently we only support a limited subset of all - * possible channel types. The limitation ends by not using - * multi-slot, limiting the channel coding, speech... - */ - if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) { - LOGP(DMSC, LOGL_ERROR, "ChannelType len !=3 not supported: %d\n", - TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE)); - goto reject; - } - - /* - * Try to figure out if we support the proposed speech codecs. For - * now we will always pick the full rate codecs. - */ - - data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE); - if ((data[0] & 0xf) != 0x1) { - LOGP(DMSC, LOGL_ERROR, "ChannelType != speech: %d\n", data[0]); - goto reject; - } - - /* - * go through the list of preferred codecs of our gsm network - * and try to find it among the permitted codecs. If we found - * it we will send chan_mode to the right mode and break the - * inner loop. The outer loop will exit due chan_mode having - * the correct value. - */ - full_rate = 0; - msc = conn->msc; - for (supported = 0; - chan_mode == GSM48_CMODE_SIGN && supported < msc->audio_length; - ++supported) { - - int perm_val = audio_support_to_gsm88(msc->audio_support[supported]); - for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) { - if ((data[i] & 0x7f) == perm_val) { - chan_mode = gsm88_to_chan_mode(perm_val); - full_rate = (data[i] & 0x4) == 0; - break; - } else if ((data[i] & 0x80) == 0x00) { - break; - } - } - } - - if (chan_mode == GSM48_CMODE_SIGN) { - LOGP(DMSC, LOGL_ERROR, "No supported audio type found.\n"); - goto reject; - } - - /* map it to a MGCP Endpoint and a RTP port */ - port = mgcp_timeslot_to_endpoint(multiplex, timeslot); - conn->rtp_port = rtp_calculate_port(port, msc->rtp_base); - - return gsm0808_assign_req(conn->conn, chan_mode, full_rate); - -reject: - resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); - if (!resp) { - LOGP(DMSC, LOGL_ERROR, "Channel allocation failure.\n"); - return -1; - } - - bsc_queue_for_msc(conn, resp); - return -1; -} - -static int bssmap_rcvmsg_udt(struct osmo_msc_data *msc, - struct msgb *msg, unsigned int length) -{ - int ret = 0; - - if (length < 1) { - LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); - return -1; - } - - LOGP(DMSC, LOGL_INFO, "Rx MSC UDT BSSMAP %s\n", - gsm0808_bssmap_name(msg->l4h[0])); - - switch (msg->l4h[0]) { - case BSS_MAP_MSG_RESET_ACKNOWLEDGE: - ret = bssmap_handle_reset_ack(msc, msg, length); - break; - case BSS_MAP_MSG_PAGING: - ret = bssmap_handle_paging(msc, msg, length); - break; - } - - return ret; -} - -static int bssmap_rcvmsg_dt1(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int length) -{ - int ret = 0; - - if (length < 1) { - LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); - return -1; - } - - LOGP(DMSC, LOGL_INFO, "Rx MSC DT1 BSSMAP %s\n", - gsm0808_bssmap_name(msg->l4h[0])); - - switch (msg->l4h[0]) { - case BSS_MAP_MSG_CLEAR_CMD: - ret = bssmap_handle_clear_command(conn, msg, length); - break; - case BSS_MAP_MSG_CIPHER_MODE_CMD: - ret = bssmap_handle_cipher_mode(conn, msg, length); - break; - case BSS_MAP_MSG_ASSIGMENT_RQST: - ret = bssmap_handle_assignm_req(conn, msg, length); - break; - default: - LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", - gsm0808_bssmap_name(msg->l4h[0])); - break; - } - - return ret; -} - -static int dtap_rcvmsg(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int length) -{ - struct dtap_header *header; - struct msgb *gsm48; - uint8_t *data; - int rc, dtap_rc; - - LOGP(DMSC, LOGL_DEBUG, "Rx MSC DTAP: %s\n", - osmo_hexdump(msg->l3h, length)); - - if (!conn->conn) { - LOGP(DMSC, LOGL_ERROR, "No subscriber connection available\n"); - return -1; - } - - header = (struct dtap_header *) msg->l3h; - if (sizeof(*header) >= length) { - LOGP(DMSC, LOGL_ERROR, "The DTAP header does not fit. Wanted: %zu got: %u\n", sizeof(*header), length); - LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length)); - return -1; - } - - if (header->length > length - sizeof(*header)) { - LOGP(DMSC, LOGL_ERROR, "The DTAP l4 information does not fit: header: %u length: %u\n", header->length, length); - LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length)); - return -1; - } - - LOGP(DMSC, LOGL_INFO, "Rx MSC DTAP, SAPI: %u CHAN: %u\n", header->link_id & 0x07, header->link_id & 0xC0); - - /* forward the data */ - gsm48 = gsm48_msgb_alloc(); - if (!gsm48) { - LOGP(DMSC, LOGL_ERROR, "Allocation of the message failed.\n"); - return -1; - } - - gsm48->l3h = gsm48->data; - data = msgb_put(gsm48, length - sizeof(*header)); - memcpy(data, msg->l3h + sizeof(*header), length - sizeof(*header)); - - /* pass it to the filter for extra actions */ - rc = bsc_scan_msc_msg(conn->conn, gsm48); - dtap_rc = gsm0808_submit_dtap(conn->conn, gsm48, header->link_id, 1); - if (rc == BSS_SEND_USSD) - bsc_send_welcome_ussd(conn->conn); - return dtap_rc; -} - -int bsc_handle_udt(struct osmo_msc_data *msc, - struct msgb *msgb, unsigned int length) -{ - struct bssmap_header *bs; - - LOGP(DMSC, LOGL_DEBUG, "Rx MSC UDT: %s\n", - osmo_hexdump(msgb->l3h, length)); - - if (length < sizeof(*bs)) { - LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); - return -1; - } - - bs = (struct bssmap_header *) msgb->l3h; - if (bs->length < length - sizeof(*bs)) - return -1; - - switch (bs->type) { - case BSSAP_MSG_BSS_MANAGEMENT: - msgb->l4h = &msgb->l3h[sizeof(*bs)]; - bssmap_rcvmsg_udt(msc, msgb, length - sizeof(*bs)); - break; - default: - LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", - gsm0808_bssmap_name(bs->type)); - } - - return 0; -} - -int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int len) -{ - if (len < sizeof(struct bssmap_header)) { - LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); - } - - switch (msg->l3h[0]) { - case BSSAP_MSG_BSS_MANAGEMENT: - msg->l4h = &msg->l3h[sizeof(struct bssmap_header)]; - bssmap_rcvmsg_dt1(conn, msg, len - sizeof(struct bssmap_header)); - break; - case BSSAP_MSG_DTAP: - dtap_rcvmsg(conn, msg, len); - break; - default: - LOGP(DMSC, LOGL_NOTICE, "Unimplemented BSSAP msg type: %s\n", - gsm0808_bssap_name(msg->l3h[0])); - } - - return -1; -} 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 72abcf427..000000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_ctrl.c +++ /dev/null @@ -1,505 +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 - -void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_connection *msc_con) -{ - struct ctrl_cmd *trap; - struct ctrl_handle *ctrl; - struct osmo_msc_data *msc_data; - - msc_data = (struct osmo_msc_data *) msc_con->write_queue.bfd.data; - ctrl = msc_data->network->ctrl; - - trap = ctrl_cmd_trap(cmd); - if (!trap) { - LOGP(DCTRL, LOGL_ERROR, "Failed to create trap.\n"); - return; - } - - ctrl_cmd_send_to_all(ctrl, trap); - ctrl_cmd_send(&msc_con->write_queue, trap); - - talloc_free(trap); -} - -CTRL_CMD_DEFINE(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 set_msc_connection_status(struct ctrl_cmd *cmd, void *data) -{ - return CTRL_CMD_ERROR; -} - -static int verify_msc_connection_status(struct ctrl_cmd *cmd, const char *value, void *data) -{ - cmd->reply = "Read-only property"; - return 1; -} - -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(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 set_bts_connection_status(struct ctrl_cmd *cmd, void *data) -{ - return CTRL_CMD_ERROR; -} - -static int verify_bts_connection_status(struct ctrl_cmd *cmd, const char *value, void *data) -{ - cmd->reply = "Read-only property"; - return 1; -} - -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", oper, admin, policy); - - osmo_bsc_send_trap(cmd, msc_con); - talloc_free(cmd); -} - -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 osmo_msc_data *msc; - struct bts_location *curloc, *lastloc; - int ret; - struct gsm_network *gsmnet = (struct gsm_network *)data; - 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)) - llist_for_each_entry(msc, &gsmnet->bsc_data->mscs, entry) - generate_location_state_trap(bts, msc->msc_con); - - cleanup_locations(&bts->loc_list); - - return ret; - -oom: - cmd->reply = "OOM"; - return CTRL_CMD_ERROR; -} - -static int verify_bts_loc(struct ctrl_cmd *cmd, const char *value, void *data) -{ - char *saveptr, *latstr, *lonstr, *heightstr, *tstampstr, *validstr, *tmp; - time_t tstamp; - int valid; - double lat, lon, height __attribute__((unused)); - - tmp = talloc_strdup(cmd, value); - if (!tmp) - return 1; - - tstampstr = strtok_r(tmp, ",", &saveptr); - validstr = strtok_r(NULL, ",", &saveptr); - latstr = strtok_r(NULL, ",", &saveptr); - lonstr = strtok_r(NULL, ",", &saveptr); - heightstr = strtok_r(NULL, "\0", &saveptr); - - if ((tstampstr == NULL) || (validstr == NULL) || (latstr == NULL) || - (lonstr == NULL) || (heightstr == NULL)) - goto err; - - tstamp = atol(tstampstr); - valid = get_string_value(bts_loc_fix_names, validstr); - lat = atof(latstr); - lon = atof(lonstr); - height = atof(heightstr); - talloc_free(tmp); - tmp = NULL; - - if (((tstamp == 0) && (valid != BTS_LOC_FIX_INVALID)) || (lat < -90) || (lat > 90) || - (lon < -180) || (lon > 180) || (valid < 0)) { - goto err; - } - - return 0; - -err: - talloc_free(tmp); - cmd->reply = talloc_strdup(cmd, "The format is ,(invalid|fix2d|fix3d),,,"); - return 1; -} - -CTRL_CMD_DEFINE(bts_rf_state, "rf_state"); -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; -} - -static int set_bts_rf_state(struct ctrl_cmd *cmd, void *data) -{ - cmd->reply = "set is unimplemented"; - return CTRL_CMD_ERROR; -} - -static int verify_bts_rf_state(struct ctrl_cmd *cmd, const char *value, void *data) -{ - return 0; -} - -CTRL_CMD_DEFINE(net_rf_lock, "rf_locked"); -static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data) -{ - cmd->reply = "get only works for the individual trx properties."; - return CTRL_CMD_ERROR; -} - -static int set_net_rf_lock(struct ctrl_cmd *cmd, void *data) -{ - int locked = atoi(cmd->value); - struct gsm_network *net = cmd->node; - if (!net) { - cmd->reply = "net not found."; - return CTRL_CMD_ERROR; - } - - if (!net->bsc_data->rf_ctrl) { - cmd->reply = "RF Ctrl is not enabled in the BSC Configuration"; - return CTRL_CMD_ERROR; - } - - osmo_bsc_rf_schedule_lock(net->bsc_data->rf_ctrl, - 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; -} - -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 = ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_state); - 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_rf_lock); - 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 = 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 957ceaf95..000000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_filter.c +++ /dev/null @@ -1,291 +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 - -static void handle_lu_request(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh; - struct gsm48_loc_upd_req *lu; - struct gsm48_loc_area_id lai; - struct gsm_network *net; - - if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu)) { - LOGP(DMSC, LOGL_ERROR, "LU too small to look at: %u\n", msgb_l3len(msg)); - return; - } - - net = conn->bts->network; - - gh = msgb_l3(msg); - lu = (struct gsm48_loc_upd_req *) gh->data; - - gsm48_generate_lai(&lai, net->country_code, net->network_code, - conn->bts->location_area_code); - - if (memcmp(&lai, &lu->lai, sizeof(lai)) != 0) { - LOGP(DMSC, LOGL_DEBUG, "Marking con for welcome USSD.\n"); - conn->sccp_con->new_subscriber = 1; - } -} - -/* extract a subscriber from the paging response */ -static struct gsm_subscriber *extract_sub(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - uint8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - struct gsm48_hdr *gh; - struct gsm48_pag_resp *resp; - struct gsm_subscriber *subscr; - - if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*resp)) { - LOGP(DMSC, LOGL_ERROR, "PagingResponse too small: %u\n", msgb_l3len(msg)); - return NULL; - } - - gh = msgb_l3(msg); - resp = (struct gsm48_pag_resp *) &gh->data[0]; - - gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), - mi_string, &mi_type); - DEBUGP(DRR, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n", - mi_type, mi_string); - - switch (mi_type) { - case GSM_MI_TYPE_TMSI: - subscr = subscr_active_by_tmsi(conn->bts->network, - tmsi_from_string(mi_string)); - break; - case GSM_MI_TYPE_IMSI: - subscr = subscr_active_by_imsi(conn->bts->network, mi_string); - break; - default: - subscr = NULL; - break; - } - - return subscr; -} - -/* we will need to stop the paging request */ -static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm_subscriber *subscr = extract_sub(conn, msg); - - if (!subscr) { - LOGP(DMSC, LOGL_ERROR, "Non active subscriber got paged.\n"); - return -1; - } - - paging_request_stop(conn->bts, subscr, conn, msg); - subscr_put(subscr); - return 0; -} - -static int is_cm_service_for_emerg(struct msgb *msg) -{ - struct gsm48_service_request *cm; - struct gsm48_hdr *gh = msgb_l3(msg); - - if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*cm)) { - LOGP(DMSC, LOGL_ERROR, "CM ServiceRequest does not fit.\n"); - return 0; - } - - cm = (struct gsm48_service_request *) &gh->data[0]; - return cm->cm_service_type == GSM48_CMSERV_EMERGENCY; -} - -struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh; - int8_t pdisc; - uint8_t mtype; - struct osmo_bsc_data *bsc; - struct osmo_msc_data *msc, *pag_msc; - struct gsm_subscriber *subscr; - int is_emerg = 0; - - bsc = conn->bts->network->bsc_data; - - if (msgb_l3len(msg) < sizeof(*gh)) { - LOGP(DMSC, LOGL_ERROR, "There is no GSM48 header here.\n"); - return NULL; - } - - gh = msgb_l3(msg); - pdisc = gh->proto_discr & 0x0f; - mtype = gh->msg_type & 0xbf; - - /* - * We are asked to select a MSC here but they are not equal. We - * want to respond to a paging request on the MSC where we got the - * request from. This is where we need to decide where this connection - * will go. - */ - if (pdisc == GSM48_PDISC_RR && mtype == GSM48_MT_RR_PAG_RESP) - goto paging; - else if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) { - is_emerg = is_cm_service_for_emerg(msg); - goto round_robin; - } else - goto round_robin; - -round_robin: - llist_for_each_entry(msc, &bsc->mscs, entry) { - if (!msc->msc_con->is_authenticated) - continue; - if (!is_emerg && msc->type != MSC_CON_TYPE_NORMAL) - continue; - if (is_emerg && !msc->allow_emerg) - continue; - - /* force round robin by moving it to the end */ - llist_move_tail(&msc->entry, &bsc->mscs); - return msc; - } - - return NULL; - -paging: - subscr = extract_sub(conn, msg); - - if (!subscr) { - LOGP(DMSC, LOGL_ERROR, "Got paged but no subscriber found.\n"); - return NULL; - } - - pag_msc = paging_get_data(conn->bts, subscr); - subscr_put(subscr); - - llist_for_each_entry(msc, &bsc->mscs, entry) { - if (msc != pag_msc) - continue; - - /* - * We don't check if the MSC is connected. In case it - * is not the connection will be dropped. - */ - - /* force round robin by moving it to the end */ - llist_move_tail(&msc->entry, &bsc->mscs); - return msc; - } - - LOGP(DMSC, LOGL_ERROR, "Got paged but no request found.\n"); - return NULL; -} - - -/** - * This is used to scan a message for extra functionality of the BSC. This - * includes scanning for location updating requests/acceptd and then send - * a welcome USSD message to the subscriber. - */ -int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gh->proto_discr & 0x0f; - uint8_t mtype = gh->msg_type & 0xbf; - - if (pdisc == GSM48_PDISC_MM) { - if (mtype == GSM48_MT_MM_LOC_UPD_REQUEST) - handle_lu_request(conn, msg); - } else if (pdisc == GSM48_PDISC_RR) { - if (mtype == GSM48_MT_RR_PAG_RESP) - handle_page_resp(conn, msg); - } - - return 0; -} - -static int send_welcome_ussd(struct gsm_subscriber_connection *conn) -{ - struct osmo_bsc_sccp_con *bsc_con; - - bsc_con = conn->sccp_con; - if (!bsc_con) { - LOGP(DMSC, LOGL_DEBUG, "No SCCP connection associated.\n"); - return 0; - } - - if (!bsc_con->msc->ussd_welcome_txt) { - LOGP(DMSC, LOGL_DEBUG, "No USSD Welcome text defined.\n"); - return 0; - } - - return BSS_SEND_USSD; -} - -int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn) -{ - gsm0480_send_ussdNotify(conn, 1, conn->sccp_con->msc->ussd_welcome_txt); - gsm0480_send_releaseComplete(conn); - - return 0; -} - -/** - * Messages coming back from the MSC. - */ -int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct osmo_msc_data *msc; - struct gsm_network *net; - struct gsm48_loc_area_id *lai; - struct gsm48_hdr *gh; - uint8_t mtype; - - if (msgb_l3len(msg) < sizeof(*gh)) { - LOGP(DMSC, LOGL_ERROR, "GSM48 header does not fit.\n"); - return -1; - } - - gh = (struct gsm48_hdr *) msgb_l3(msg); - mtype = gh->msg_type & 0xbf; - net = conn->bts->network; - msc = conn->sccp_con->msc; - - if (mtype == GSM48_MT_MM_LOC_UPD_ACCEPT) { - if (msc->core_ncc != -1 || msc->core_mcc != -1) { - if (msgb_l3len(msg) >= sizeof(*gh) + sizeof(*lai)) { - lai = (struct gsm48_loc_area_id *) &gh->data[0]; - gsm48_generate_lai(lai, net->country_code, - net->network_code, - conn->bts->location_area_code); - } - } - - if (conn->sccp_con->new_subscriber) - return send_welcome_ussd(conn); - return 0; - } - - 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 341971ef2..000000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_grace.c +++ /dev/null @@ -1,151 +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 (!network->bsc_data->rf_ctrl) - return 1; - if (bts->excl_from_rf_lock) - return 1; - return network->bsc_data->rf_ctrl->policy == S_RF_ON; -} - -/** - * Try to not page if everything the cell is not on. - */ -int bsc_grace_paging_request(struct gsm_subscriber *subscr, int chan_needed, - struct osmo_msc_data *msc) -{ - struct gsm_bts *bts = NULL; - - if (!subscr->net->bsc_data->rf_ctrl) - goto page; - if (subscr->net->bsc_data->rf_ctrl->policy == S_RF_ON) - goto page; - - /* - * Check if there is any BTS that is on for the given lac. Start - * with NULL and iterate through all bts. - */ - do { - bts = gsm_bts_by_lac(subscr->net, subscr->lac, bts); - if (!bts) - break; - - /* - * continue if the BTS is not excluded from the lock - */ - if (!bts->excl_from_rf_lock) - continue; - - /* - * now page on this bts - */ - paging_request_bts(bts, subscr, chan_needed, NULL, msc); - } while (1); - - /* All bts are either off or in the grace period */ - return 0; -page: - return paging_request(subscr->net, subscr, chan_needed, NULL, msc); -} - -static int handle_sub(struct gsm_lchan *lchan, const char *text) -{ - struct gsm_subscriber_connection *conn; - - /* only send it to TCH */ - if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F) - return -1; - - /* only send on the primary channel */ - conn = lchan->conn; - if (!conn) - return -1; - - if (conn->lchan != lchan) - return -1; - - /* only when active */ - if (lchan->state != LCHAN_S_ACTIVE) - return -1; - - gsm0480_send_ussdNotify(conn, 0, text); - gsm0480_send_releaseComplete(conn); - - return 0; -} - -/* - * The place to handle the grace mode. Right now we will send - * USSD messages to the subscriber, in the future we might start - * a timer to have different modes for the grace period. - */ -static int handle_grace(struct gsm_network *network) -{ - int ts_nr, lchan_nr; - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - - if (!network->bsc_data->mid_call_txt) - return 0; - - llist_for_each_entry(bts, &network->bts_list, list) { - llist_for_each_entry(trx, &bts->trx_list, list) { - for (ts_nr = 0; ts_nr < TRX_NR_TS; ++ts_nr) { - struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; - for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; ++lchan_nr) { - handle_sub(&ts->lchan[lchan_nr], - network->bsc_data->mid_call_txt); - } - } - } - } - return 0; -} - -static int handle_rf_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct rf_signal_data *sig; - - if (subsys != SS_RF) - return -1; - - sig = signal_data; - - if (signal == S_RF_GRACE) - handle_grace(sig->net); - - return 0; -} - -static __attribute__((constructor)) void on_dso_load_grace(void) -{ - osmo_signal_register_handler(SS_RF, handle_rf_signal, NULL); -} 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 864d62970..000000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_main.c +++ /dev/null @@ -1,277 +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 - -#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 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 = strdup(optarg); - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - case 'r': - rf_ctrl = optarg; - break; - default: - /* ignore */ - break; - } - } -} - -extern enum node_type bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OsmoBSC", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - -extern int bsc_shutdown_net(struct gsm_network *net); -static void signal_handler(int signal) -{ - struct osmo_msc_data *msc; - - fprintf(stdout, "signal %u received\n", signal); - - switch (signal) { - case SIGINT: - bsc_shutdown_net(bsc_gsmnet); - osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); - sleep(3); - exit(0); - break; - case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report(tall_vty_ctx, stderr); - talloc_report_full(tall_bsc_ctx, stderr); - break; - case SIGUSR2: - if (!bsc_gsmnet->bsc_data) - return; - llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) - bsc_msc_lost(msc->msc_con); - break; - default: - break; - } -} - -int main(int argc, char **argv) -{ - struct osmo_msc_data *msc; - struct osmo_bsc_data *data; - int rc; - - tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); - - osmo_init_logging(&log_info); - - bts_init(); - libosmo_abis_init(tall_bsc_ctx); - - /* enable filters */ - - /* This needs to precede handle_options() */ - vty_info.copyright = openbsc_copyright; - vty_init(&vty_info); - bsc_vty_init(&log_info); - - /* parse options */ - handle_options(argc, argv); - - /* seed the PRNG */ - srand(time(NULL)); - - /* initialize SCCP */ - sccp_set_log_area(DSCCP); - - - rc = bsc_bootstrap_network(NULL, config_file); - if (rc < 0) { - fprintf(stderr, "Bootstrapping the network failed. exiting.\n"); - exit(1); - } - bsc_api_init(bsc_gsmnet, osmo_bsc_api()); - - bsc_gsmnet->ctrl = controlif_setup(bsc_gsmnet, 4249); - if (!bsc_gsmnet) { - fprintf(stderr, "Failed to init the control interface. Exiting.\n"); - exit(1); - } - - rc = bsc_ctrl_cmds_install(bsc_gsmnet); - if (rc < 0) { - fprintf(stderr, "Failed to install control commands. Exiting.\n"); - exit(1); - } - - data = bsc_gsmnet->bsc_data; - if (rf_ctrl) - bsc_replace_string(data, &data->rf_ctrl_name, rf_ctrl); - - if (data->rf_ctrl_name) { - 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 5517d3080..000000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_msc.c +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Handle the connection to the MSC. This include ping/timeout/reconnect - * (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 - - -static void initialize_if_needed(struct bsc_msc_connection *conn); -static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn); -static void send_id_get_response(struct osmo_msc_data *data, int fd); -static void send_ping(struct osmo_msc_data *data); - -/* - * MGCP forwarding code - */ -static int mgcp_do_read(struct osmo_fd *fd) -{ - struct osmo_msc_data *data = (struct osmo_msc_data *) fd->data; - struct msgb *mgcp; - int ret; - - mgcp = msgb_alloc_headroom(4096, 128, "mgcp_from_gw"); - if (!mgcp) { - LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n"); - return -1; - } - - ret = read(fd->fd, mgcp->data, 4096 - 128); - if (ret <= 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno)); - msgb_free(mgcp); - return -1; - } else if (ret > 4096 - 128) { - LOGP(DMGCP, LOGL_ERROR, "Too much data: %d\n", ret); - msgb_free(mgcp); - return -1; - } - - mgcp->l2h = msgb_put(mgcp, ret); - msc_queue_write(data->msc_con, mgcp, IPAC_PROTO_MGCP_OLD); - return 0; -} - -static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg) -{ - int ret; - - LOGP(DMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len); - - ret = write(fd->fd, msg->data, msg->len); - if (ret != msg->len) - LOGP(DMGCP, LOGL_ERROR, "Failed to forward message to MGCP GW (%s).\n", strerror(errno)); - - return ret; -} - -static void mgcp_forward(struct osmo_msc_data *data, struct msgb *msg) -{ - struct msgb *mgcp; - - if (msgb_l2len(msg) > 4096) { - LOGP(DMGCP, LOGL_ERROR, "Can not forward too big message.\n"); - return; - } - - mgcp = msgb_alloc(4096, "mgcp_to_gw"); - if (!mgcp) { - LOGP(DMGCP, LOGL_ERROR, "Failed to send message.\n"); - return; - } - - msgb_put(mgcp, msgb_l2len(msg)); - memcpy(mgcp->data, msg->l2h, mgcp->len); - if (osmo_wqueue_enqueue(&data->mgcp_agent, mgcp) != 0) { - LOGP(DMGCP, LOGL_FATAL, "Could not queue message to MGCP GW.\n"); - msgb_free(mgcp); - } -} - -static int mgcp_create_port(struct osmo_msc_data *data) -{ - int on; - struct sockaddr_in addr; - - data->mgcp_agent.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); - if (data->mgcp_agent.bfd.fd < 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno); - return -1; - } - - on = 1; - setsockopt(data->mgcp_agent.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - /* try to bind the socket */ - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr.sin_port = 0; - - if (bind(data->mgcp_agent.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to bind to any port.\n"); - close(data->mgcp_agent.bfd.fd); - data->mgcp_agent.bfd.fd = -1; - return -1; - } - - /* connect to the remote */ - addr.sin_port = htons(2427); - if (connect(data->mgcp_agent.bfd.fd, (struct sockaddr *) & addr, sizeof(addr)) < 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to connect to local MGCP GW. %s\n", strerror(errno)); - close(data->mgcp_agent.bfd.fd); - data->mgcp_agent.bfd.fd = -1; - return -1; - } - - osmo_wqueue_init(&data->mgcp_agent, 10); - data->mgcp_agent.bfd.when = BSC_FD_READ; - data->mgcp_agent.bfd.data = data; - data->mgcp_agent.read_cb = mgcp_do_read; - data->mgcp_agent.write_cb = mgcp_do_write; - - if (osmo_fd_register(&data->mgcp_agent.bfd) != 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to register BFD\n"); - close(data->mgcp_agent.bfd.fd); - data->mgcp_agent.bfd.fd = -1; - return -1; - } - - return 0; -} - -/* - * Send data to the network - */ -int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto) -{ - ipaccess_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; -} - -static int msc_alink_do_write(struct osmo_fd *fd, struct msgb *msg) -{ - int ret; - - LOGP(DMSC, LOGL_DEBUG, "Sending SCCP to MSC: %u\n", msgb_l2len(msg)); - LOGP(DLMI, LOGL_DEBUG, "MSC TX %s\n", osmo_hexdump(msg->data, msg->len)); - - ret = write(fd->fd, msg->data, msg->len); - if (ret < msg->len) - perror("MSC: Failed to send SCCP"); - - return ret; -} - -static void handle_ctrl(struct osmo_msc_data *msc, struct msgb *msg) -{ - int ret; - struct ctrl_cmd *cmd; - - cmd = ctrl_cmd_parse(msc->msc_con, msg); - if (!cmd) { - LOGP(DMSC, LOGL_ERROR, "Failed to parse control message.\n"); - cmd = talloc_zero(msc->msc_con, struct ctrl_cmd); - if (!cmd) { - LOGP(DMSC, LOGL_ERROR, "OOM!\n"); - return; - } - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "Failed to parse control message."; - - ctrl_cmd_send(&msc->msc_con->write_queue, cmd); - talloc_free(cmd); - - return; - } - - ret = ctrl_cmd_handle(cmd, msc->network); - if (ret != CTRL_CMD_HANDLED) - ctrl_cmd_send(&msc->msc_con->write_queue, cmd); - talloc_free(cmd); -} - -static void osmo_ext_handle(struct osmo_msc_data *msc, struct msgb *msg) -{ - struct ipaccess_head *hh; - struct ipaccess_head_ext *hh_ext; - - hh = (struct ipaccess_head *) msg->data; - hh_ext = (struct ipaccess_head_ext *) hh->data; - if (msg->len < sizeof(*hh) + sizeof(*hh_ext)) { - LOGP(DMSC, LOGL_ERROR, "Packet too short for extended header.\n"); - return; - } - - msg->l2h = hh_ext->data; - if (hh_ext->proto == IPAC_PROTO_EXT_MGCP) - mgcp_forward(msc, msg); - else if (hh_ext->proto == IPAC_PROTO_EXT_LAC) - send_lacs(msc->network, msc->msc_con); - else if (hh_ext->proto == IPAC_PROTO_EXT_CTRL) - handle_ctrl(msc, msg); -} - -static int ipaccess_a_fd_cb(struct osmo_fd *bfd) -{ - struct msgb *msg; - struct ipaccess_head *hh; - struct osmo_msc_data *data = (struct osmo_msc_data *) bfd->data; - int ret; - - ret = ipa_msg_recv(bfd->fd, &msg); - if (ret <= 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; - ipaccess_rcvmsg_base(msg, bfd); - - /* initialize the networking. This includes sending a GSM08.08 message */ - msg->cb[0] = (unsigned long) data; - if (hh->proto == IPAC_PROTO_IPACCESS) { - 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); - } else if (msg->l2h[0] == IPAC_MSGT_PONG) { - osmo_timer_del(&data->pong_timer); - } - } else if (hh->proto == IPAC_PROTO_SCCP) { - sccp_system_incoming(msg); - } else if (hh->proto == IPAC_PROTO_MGCP_OLD) { - mgcp_forward(data, msg); - } else if (hh->proto == IPAC_PROTO_OSMO) { - osmo_ext_handle(data, msg); - } - - msgb_free(msg); - return 0; -} - -static void send_ping(struct osmo_msc_data *data) -{ - struct msgb *msg; - - msg = msgb_alloc_headroom(4096, 128, "ping"); - if (!msg) { - LOGP(DMSC, LOGL_ERROR, "Failed to create PING.\n"); - return; - } - - msg->l2h = msgb_put(msg, 1); - msg->l2h[0] = IPAC_MSGT_PING; - - msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS); -} - -static void msc_ping_timeout_cb(void *_data) -{ - struct osmo_msc_data *data = (struct osmo_msc_data *) _data; - if (data->ping_timeout < 0) - return; - - send_ping(data); - - /* 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_pong_timeout_cb(void *_data) -{ - struct osmo_msc_data *data = (struct osmo_msc_data *) _data; - - LOGP(DMSC, LOGL_ERROR, "MSC didn't answer PING. Closing connection.\n"); - bsc_msc_lost(data->msc_con); -} - -static void msc_connection_connected(struct bsc_msc_connection *con) -{ - struct msc_signal_data sig; - struct osmo_msc_data *data; - int ret, on; - on = 1; - ret = setsockopt(con->write_queue.bfd.fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); - if (ret != 0) - LOGP(DMSC, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); - - data = (struct osmo_msc_data *) con->write_queue.bfd.data; - msc_ping_timeout_cb(data); - - sig.data = data; - osmo_signal_dispatch(SS_MSC, S_MSC_CONNECTED, &sig); -} - -/* - * The connection to the MSC was lost and we will need to free all - * resources and then attempt to reconnect. - */ -static void msc_connection_was_lost(struct bsc_msc_connection *msc) -{ - struct msc_signal_data sig; - struct osmo_msc_data *data; - - LOGP(DMSC, LOGL_ERROR, "Lost MSC connection. Freing stuff.\n"); - - data = (struct osmo_msc_data *) msc->write_queue.bfd.data; - osmo_timer_del(&data->ping_timer); - osmo_timer_del(&data->pong_timer); - - sig.data = data; - osmo_signal_dispatch(SS_MSC, S_MSC_LOST, &sig); - - msc->is_authenticated = 0; - bsc_msc_schedule_connect(msc); -} - -static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn) -{ - struct ipac_ext_lac_cmd *lac; - struct gsm_bts *bts; - struct msgb *msg; - int lacs = 0; - - if (llist_empty(&net->bts_list)) { - LOGP(DMSC, LOGL_ERROR, "No BTSs configured. Not sending LACs.\n"); - return; - } - - msg = msgb_alloc_headroom(4096, 128, "LAC Command"); - if (!msg) { - LOGP(DMSC, LOGL_ERROR, "Failed to create the LAC command.\n"); - return; - } - - lac = (struct ipac_ext_lac_cmd *) msgb_put(msg, sizeof(*lac)); - lac->add_remove = 1; - - llist_for_each_entry(bts, &net->bts_list, list) { - if (lacs++ == 0) - lac->lac = htons(bts->location_area_code); - else - msgb_put_u16(msg, htons(bts->location_area_code)); - } - - lac->nr_extra_lacs = lacs - 1; - ipaccess_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 void send_id_get_response(struct osmo_msc_data *data, int fd) -{ - struct msc_signal_data sig; - struct msgb *msg; - - msg = bsc_msc_id_get_resp(data->bsc_token); - if (!msg) - return; - msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS); - - sig.data = data; - osmo_signal_dispatch(SS_MSC, S_MSC_AUTHENTICATED, &sig); -} - -int osmo_bsc_msc_init(struct osmo_msc_data *data) -{ - if (mgcp_create_port(data) != 0) - return -1; - - data->msc_con = bsc_msc_create(data, &data->dests); - if (!data->msc_con) { - LOGP(DMSC, LOGL_ERROR, "Creating the MSC network connection failed.\n"); - return -1; - } - - data->ping_timer.cb = msc_ping_timeout_cb; - data->ping_timer.data = data; - data->pong_timer.cb = msc_pong_timeout_cb; - data->pong_timer.data = data; - - data->msc_con->write_queue.bfd.data = data; - data->msc_con->connection_loss = msc_connection_was_lost; - data->msc_con->connected = msc_connection_connected; - data->msc_con->write_queue.read_cb = ipaccess_a_fd_cb; - data->msc_con->write_queue.write_cb = msc_alink_do_write; - bsc_msc_connect(data->msc_con); - - return 0; -} - -struct osmo_msc_data *osmo_msc_data_find(struct gsm_network *net, int nr) -{ - struct osmo_msc_data *msc_data; - - llist_for_each_entry(msc_data, &net->bsc_data->mscs, entry) - if (msc_data->nr == nr) - return msc_data; - return NULL; -} - -struct osmo_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr) -{ - struct osmo_msc_data *msc_data; - - /* check if there is already one */ - msc_data = osmo_msc_data_find(net, nr); - if (msc_data) - return msc_data; - - msc_data = talloc_zero(net, struct osmo_msc_data); - if (!msc_data) - return NULL; - - llist_add_tail(&msc_data->entry, &net->bsc_data->mscs); - - /* Init back pointer */ - msc_data->network = net; - - INIT_LLIST_HEAD(&msc_data->dests); - msc_data->ping_timeout = 20; - msc_data->pong_timeout = 5; - msc_data->core_ncc = -1; - msc_data->core_mcc = -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 3533d6df0..000000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_sccp.c +++ /dev/null @@ -1,300 +0,0 @@ -/* Interaction with the SCCP subsystem */ -/* - * (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 - -/* SCCP helper */ -#define SCCP_IT_TIMER 60 - -static LLIST_HEAD(active_connections); - -static void free_queued(struct osmo_bsc_sccp_con *conn) -{ - struct msgb *msg; - - while (!llist_empty(&conn->sccp_queue)) { - /* this is not allowed to fail */ - msg = msgb_dequeue(&conn->sccp_queue); - msgb_free(msg); - } - - conn->sccp_queue_size = 0; -} - -static void send_queued(struct osmo_bsc_sccp_con *conn) -{ - struct msgb *msg; - - while (!llist_empty(&conn->sccp_queue)) { - /* this is not allowed to fail */ - msg = msgb_dequeue(&conn->sccp_queue); - sccp_connection_write(conn->sccp, msg); - msgb_free(msg); - conn->sccp_queue_size -= 1; - } -} - -static void msc_outgoing_sccp_data(struct sccp_connection *conn, - struct msgb *msg, unsigned int len) -{ - struct osmo_bsc_sccp_con *bsc_con = - (struct osmo_bsc_sccp_con *) conn->data_ctx; - - bsc_handle_dt1(bsc_con, msg, len); -} - -static void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state) -{ - struct osmo_bsc_sccp_con *con_data; - - if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) { - con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx; - if(con_data->conn) { - LOGP(DMSC, LOGL_ERROR, - "ERROR: The lchan is still associated.\n"); - gsm0808_clear(con_data->conn); - subscr_con_free(con_data->conn); - con_data->conn = NULL; - } - - con_data->sccp = NULL; - free_queued(con_data); - sccp_connection_free(conn); - bsc_delete_connection(con_data); - } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) { - LOGP(DMSC, LOGL_DEBUG, "Connection established: %p\n", conn); - con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx; - - osmo_timer_del(&con_data->sccp_cc_timeout); - osmo_timer_schedule(&con_data->sccp_it_timeout, SCCP_IT_TIMER, 0); - - send_queued(con_data); - } -} - -static void bsc_sccp_force_free(struct osmo_bsc_sccp_con *data) -{ - if (data->conn) { - gsm0808_clear(data->conn); - subscr_con_free(data->conn); - data->conn = NULL; - } - - free_queued(data); - sccp_connection_force_free(data->sccp); - data->sccp = NULL; - bsc_delete_connection(data); -} - -static void sccp_it_timeout(void *_data) -{ - struct osmo_bsc_sccp_con *data = - (struct osmo_bsc_sccp_con *) _data; - - sccp_connection_send_it(data->sccp); - osmo_timer_schedule(&data->sccp_it_timeout, SCCP_IT_TIMER, 0); -} - -static void sccp_cc_timeout(void *_data) -{ - struct osmo_bsc_sccp_con *data = - (struct osmo_bsc_sccp_con *) _data; - - if (data->sccp->connection_state >= SCCP_CONNECTION_STATE_ESTABLISHED) - return; - - LOGP(DMSC, LOGL_ERROR, "The connection was never established.\n"); - bsc_sccp_force_free(data); -} - -static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg, - void *global_ctx, void *ctx) -{ - struct bsc_msc_connection *msc_con; - - if (conn) { - struct osmo_bsc_sccp_con *bsc_con = conn->data_ctx; - msc_con = bsc_con->msc->msc_con; - } else { - msc_con = ctx; - } - - msc_queue_write(msc_con, msg, IPAC_PROTO_SCCP); -} - -static int msc_sccp_accept(struct sccp_connection *connection, void *data) -{ - LOGP(DMSC, LOGL_DEBUG, "Rejecting incoming SCCP connection.\n"); - return -1; -} - -static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data) -{ - struct osmo_msc_data *msc = (struct osmo_msc_data *) msgb->cb[0]; - return bsc_handle_udt(msc, msgb, length); -} - -int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg) -{ - struct sccp_connection *sccp = conn->sccp; - - if (sccp->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { - LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp); - msgb_free(msg); - } else if (sccp->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED - && conn->sccp_queue_size == 0) { - sccp_connection_write(sccp, msg); - msgb_free(msg); - } else if (conn->sccp_queue_size > 10) { - LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp); - msgb_free(msg); - } else { - LOGP(DMSC, LOGL_DEBUG, "Queueing packet on %p. Queue size: %d\n", sccp, conn->sccp_queue_size); - conn->sccp_queue_size += 1; - msgb_enqueue(&conn->sccp_queue, msg); - } - - return 0; -} - -int bsc_create_new_connection(struct gsm_subscriber_connection *conn, - struct osmo_msc_data *msc) -{ - struct osmo_bsc_sccp_con *bsc_con; - struct sccp_connection *sccp; - - /* This should not trigger */ - if (!msc->msc_con->is_authenticated) { - LOGP(DMSC, LOGL_ERROR, - "How did this happen? MSC is not connected. Dropping.\n"); - return -1; - } - - if (!bsc_grace_allow_new_connection(conn->bts->network, conn->bts)) { - LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n"); - return -1; - } - - sccp = sccp_connection_socket(); - if (!sccp) { - LOGP(DMSC, LOGL_ERROR, "Failed to allocate memory.\n"); - return -ENOMEM; - } - - 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 -1; - } - - /* callbacks */ - sccp->state_cb = msc_outgoing_sccp_state; - sccp->data_cb = msc_outgoing_sccp_data; - sccp->data_ctx = bsc_con; - - /* prepare the timers */ - bsc_con->sccp_it_timeout.cb = sccp_it_timeout; - bsc_con->sccp_it_timeout.data = bsc_con; - bsc_con->sccp_cc_timeout.cb = sccp_cc_timeout; - bsc_con->sccp_cc_timeout.data = bsc_con; - - INIT_LLIST_HEAD(&bsc_con->sccp_queue); - - bsc_con->sccp = sccp; - bsc_con->msc = msc; - bsc_con->conn = conn; - llist_add_tail(&bsc_con->entry, &active_connections); - conn->sccp_con = bsc_con; - return 0; -} - -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_close_connections(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) - 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_close_connections(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 49ae665a6..000000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_vty.c +++ /dev/null @@ -1,599 +0,0 @@ -/* Osmo BSC VTY Configuration */ -/* (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 - - -#define IPA_STR "IP.ACCESS specific\n" - -extern struct gsm_network *bsc_gsmnet; - -static struct osmo_bsc_data *osmo_bsc_data(struct vty *vty) -{ - return bsc_gsmnet->bsc_data; -} - -static struct osmo_msc_data *osmo_msc_data(struct vty *vty) -{ - return osmo_msc_data_find(bsc_gsmnet, (int) vty->index); -} - -static struct cmd_node bsc_node = { - BSC_NODE, - "%s(bsc)#", - 1, -}; - -static struct cmd_node msc_node = { - MSC_NODE, - "%s(config-msc)# ", - 1, -}; - -DEFUN(cfg_net_msc, cfg_net_msc_cmd, - "msc [<0-1000>]", "Configure MSC details\n" "MSC connection to configure\n") -{ - int index = argc == 1 ? atoi(argv[0]) : 0; - struct osmo_msc_data *msc; - - msc = osmo_msc_data_alloc(bsc_gsmnet, index); - if (!msc) { - vty_out(vty, "%%Failed to allocate MSC data.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - vty->index = (void *) index; - vty->node = MSC_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc, cfg_net_bsc_cmd, - "bsc", "Configure BSC\n") -{ - vty->node = BSC_NODE; - return CMD_SUCCESS; -} - -static void write_msc_amr_options(struct vty *vty, struct osmo_msc_data *msc) -{ -#define WRITE_AMR(vty, msc, name, var) \ - vty_out(vty, " amr-config %s %s%s", \ - name, msc->amr_conf.var ? "allowed" : "forbidden", \ - VTY_NEWLINE); - - WRITE_AMR(vty, msc, "12_2k", m12_2); - WRITE_AMR(vty, msc, "10_2k", m10_2); - WRITE_AMR(vty, msc, "7_95k", m7_95); - WRITE_AMR(vty, msc, "7_40k", m7_40); - WRITE_AMR(vty, msc, "6_70k", m6_70); - WRITE_AMR(vty, msc, "5_90k", m5_90); - WRITE_AMR(vty, msc, "5_15k", m5_15); - WRITE_AMR(vty, msc, "4_75k", m4_75); -#undef WRITE_AMR -} - -static void write_msc(struct vty *vty, struct osmo_msc_data *msc) -{ - struct bsc_msc_dest *dest; - - vty_out(vty, "msc %d%s", msc->nr, VTY_NEWLINE); - if (msc->bsc_token) - vty_out(vty, " token %s%s", msc->bsc_token, VTY_NEWLINE); - if (msc->core_ncc != -1) - vty_out(vty, " core-mobile-network-code %d%s", - msc->core_ncc, VTY_NEWLINE); - if (msc->core_mcc != -1) - vty_out(vty, " core-mobile-country-code %d%s", - msc->core_mcc, VTY_NEWLINE); - vty_out(vty, " ip.access rtp-base %d%s", msc->rtp_base, VTY_NEWLINE); - 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->ussd_welcome_txt) - vty_out(vty, " bsc-welcome-text %s%s", msc->ussd_welcome_txt, 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); - - /* write amr options */ - write_msc_amr_options(vty, msc); -} - -static int config_write_msc(struct vty *vty) -{ - struct osmo_msc_data *msc; - struct osmo_bsc_data *bsc = osmo_bsc_data(vty); - - llist_for_each_entry(msc, &bsc->mscs, entry) - write_msc(vty, msc); - - return CMD_SUCCESS; -} - -static int config_write_bsc(struct vty *vty) -{ - struct osmo_bsc_data *bsc = osmo_bsc_data(vty); - - vty_out(vty, "bsc%s", VTY_NEWLINE); - if (bsc->mid_call_txt) - vty_out(vty, " mid-call-text %s%s", bsc->mid_call_txt, VTY_NEWLINE); - vty_out(vty, " mid-call-timeout %d%s", bsc->mid_call_timeout, VTY_NEWLINE); - if (bsc->rf_ctrl_name) - vty_out(vty, " bsc-rf-socket %s%s", - bsc->rf_ctrl_name, VTY_NEWLINE); - - if (bsc->auto_off_timeout != -1) - vty_out(vty, " bsc-auto-rf-off %d%s", - bsc->auto_off_timeout, VTY_NEWLINE); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_token, - cfg_net_bsc_token_cmd, - "token TOKEN", - "A token for the BSC to be sent to the MSC\n" "A token\n") -{ - struct osmo_msc_data *data = osmo_msc_data(vty); - - bsc_replace_string(osmo_bsc_data(vty), &data->bsc_token, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_ncc, - cfg_net_bsc_ncc_cmd, - "core-mobile-network-code <1-999>", - "Use this network code for the backbone\n" "NCC value\n") -{ - struct osmo_msc_data *data = osmo_msc_data(vty); - data->core_ncc = 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 backbone\n" "MCC value\n") -{ - struct osmo_msc_data *data = osmo_msc_data(vty); - data->core_mcc = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_rtp_base, - cfg_net_bsc_rtp_base_cmd, - "ip.access rtp-base <1-65000>", - IPA_STR - "Set the rtp-base port for the RTP stream\n" - "Port number\n") -{ - struct osmo_msc_data *data = osmo_msc_data(vty); - data->rtp_base = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_codec_list, - cfg_net_bsc_codec_list_cmd, - "codec-list .LIST", - "Set the allowed audio codecs\n" - "List of audio codecs\n") -{ - struct osmo_msc_data *data = osmo_msc_data(vty); - int saw_fr, saw_hr; - int i; - - saw_fr = saw_hr = 0; - - /* free the old list... if it exists */ - if (data->audio_support) { - talloc_free(data->audio_support); - data->audio_support = NULL; - data->audio_length = 0; - } - - /* create a new array */ - data->audio_support = - talloc_zero_array(osmo_bsc_data(vty), struct gsm_audio_support *, argc); - data->audio_length = argc; - - for (i = 0; i < argc; ++i) { - /* check for hrX or frX */ - if (strlen(argv[i]) != 3 - || argv[i][1] != 'r' - || (argv[i][0] != 'h' && argv[i][0] != 'f') - || argv[i][2] < 0x30 - || argv[i][2] > 0x39) - goto error; - - data->audio_support[i] = talloc_zero(data->audio_support, - struct gsm_audio_support); - data->audio_support[i]->ver = atoi(argv[i] + 2); - - if (strncmp("hr", argv[i], 2) == 0) { - data->audio_support[i]->hr = 1; - saw_hr = 1; - } else if (strncmp("fr", argv[i], 2) == 0) { - data->audio_support[i]->hr = 0; - saw_fr = 1; - } - - if (saw_hr && saw_fr) { - vty_out(vty, "Can not have full-rate and half-rate codec.%s", - VTY_NEWLINE); - return CMD_ERR_INCOMPLETE; - } - } - - return CMD_SUCCESS; - -error: - vty_out(vty, "Codec name must be hrX or frX. Was '%s'%s", - argv[i], VTY_NEWLINE); - return CMD_ERR_INCOMPLETE; -} - -DEFUN(cfg_net_msc_dest, - cfg_net_msc_dest_cmd, - "dest A.B.C.D <1-65000> <0-255>", - "Add a destination to a MUX/MSC\n" - "IP Address\n" "Port\n" "DSCP\n") -{ - struct bsc_msc_dest *dest; - struct osmo_msc_data *data = osmo_msc_data(vty); - - dest = talloc_zero(osmo_bsc_data(vty), struct bsc_msc_dest); - if (!dest) { - vty_out(vty, "%%Failed to create structure.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - dest->ip = talloc_strdup(dest, argv[0]); - if (!dest->ip) { - vty_out(vty, "%%Failed to copy dest ip.%s", VTY_NEWLINE); - talloc_free(dest); - return CMD_WARNING; - } - - dest->port = atoi(argv[1]); - dest->dscp = atoi(argv[2]); - llist_add_tail(&dest->list, &data->dests); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_no_dest, - cfg_net_msc_no_dest_cmd, - "no dest A.B.C.D <1-65000> <0-255>", - NO_STR "Remove a destination to a MUX/MSC\n" - "IP Address\n" "Port\n" "DSCP\n") -{ - struct bsc_msc_dest *dest, *tmp; - struct osmo_msc_data *data = osmo_msc_data(vty); - - int port = atoi(argv[1]); - int dscp = atoi(argv[2]); - - llist_for_each_entry_safe(dest, tmp, &data->dests, list) { - if (port != dest->port || dscp != dest->dscp - || strcmp(dest->ip, argv[0]) != 0) - continue; - - llist_del(&dest->list); - talloc_free(dest); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_ping_time, - cfg_net_msc_ping_time_cmd, - "timeout-ping NR", - "Set the PING interval, negative for not sending PING\n" - "Timeout in seconds\n") -{ - struct osmo_msc_data *data = osmo_msc_data(vty); - data->ping_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_pong_time, - cfg_net_msc_pong_time_cmd, - "timeout-pong NR", - "Set the time to wait for a PONG\n" "Timeout in seconds\n") -{ - struct osmo_msc_data *data = osmo_msc_data(vty); - data->pong_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_welcome_ussd, - cfg_net_msc_welcome_ussd_cmd, - "bsc-welcome-text .TEXT", - "Set the USSD notification to be sent.\n" "Text to be sent\n") -{ - struct osmo_msc_data *data = osmo_msc_data(vty); - char *str = argv_concat(argv, argc, 0); - if (!str) - return CMD_WARNING; - - bsc_replace_string(osmo_bsc_data(vty), &data->ussd_welcome_txt, str); - talloc_free(str); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_type, - cfg_net_msc_type_cmd, - "type (normal|local)", - "Select the MSC type\n" - "Plain GSM MSC\n" "Special MSC for local call routing\n") -{ - struct osmo_msc_data *data = osmo_msc_data(vty); - - if (strcmp(argv[0], "normal") == 0) - data->type = MSC_CON_TYPE_NORMAL; - else if (strcmp(argv[0], "local") == 0) - data->type = MSC_CON_TYPE_LOCAL; - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_emerg, - cfg_net_msc_emerg_cmd, - "allow-emergency (allow|deny)", - "Allow CM ServiceRequests with type emergency\n" - "Allow\n" "Deny\n") -{ - struct osmo_msc_data *data = osmo_msc_data(vty); - data->allow_emerg = strcmp("allow", argv[0]) == 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_local_prefix, - cfg_net_msc_local_prefix_cmd, - "local-prefix REGEXP", - "Prefix for local numbers\n" "REGEXP used\n") -{ - struct osmo_msc_data *msc = osmo_msc_data(vty); - - if (gsm_parse_reg(msc, &msc->local_pref_reg, &msc->local_pref, argc, argv) != 0) { - vty_out(vty, "%%Failed to parse the regexp: '%s'%s", - argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -#define AMR_CONF_STR "AMR Multirate Configuration\n" -#define AMR_COMMAND(name) \ - DEFUN(cfg_net_msc_amr_##name, \ - cfg_net_msc_amr_##name##_cmd, \ - "amr-config " #name "k (allowed|forbidden)", \ - AMR_CONF_STR "Bitrate\n" "Allowed\n" "Forbidden\n") \ -{ \ - struct osmo_msc_data *msc = osmo_msc_data(vty); \ - \ - msc->amr_conf.m##name = strcmp(argv[0], "allowed") == 0; \ - return CMD_SUCCESS; \ -} - -AMR_COMMAND(12_2) -AMR_COMMAND(10_2) -AMR_COMMAND(7_95) -AMR_COMMAND(7_40) -AMR_COMMAND(6_70) -AMR_COMMAND(5_90) -AMR_COMMAND(5_15) -AMR_COMMAND(4_75) - -DEFUN(cfg_net_bsc_mid_call_text, - cfg_net_bsc_mid_call_text_cmd, - "mid-call-text .TEXT", - "Set the USSD notifcation to be send.\n" "Text to be sent\n") -{ - struct osmo_bsc_data *data = osmo_bsc_data(vty); - char *txt = argv_concat(argv, argc, 0); - if (!txt) - return CMD_WARNING; - - bsc_replace_string(data, &data->mid_call_txt, txt); - talloc_free(txt); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_mid_call_timeout, - cfg_net_bsc_mid_call_timeout_cmd, - "mid-call-timeout NR", - "Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n") -{ - struct osmo_bsc_data *data = osmo_bsc_data(vty); - data->mid_call_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_rf_socket, - cfg_net_rf_socket_cmd, - "bsc-rf-socket PATH", - "Set the filename for the RF control interface.\n" "RF Control path\n") -{ - struct osmo_bsc_data *data = osmo_bsc_data(vty); - - bsc_replace_string(data, &data->rf_ctrl_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_rf_off_time, - cfg_net_rf_off_time_cmd, - "bsc-auto-rf-off <1-65000>", - "Disable RF on MSC Connection\n" "Timeout\n") -{ - struct osmo_bsc_data *data = osmo_bsc_data(vty); - data->auto_off_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_no_rf_off_time, - cfg_net_no_rf_off_time_cmd, - "no bsc-auto-rf-off", - NO_STR "Disable RF on MSC Connection\n") -{ - struct osmo_bsc_data *data = osmo_bsc_data(vty); - data->auto_off_timeout = -1; - return CMD_SUCCESS; -} - -DEFUN(show_statistics, - show_statistics_cmd, - "show statistics", - SHOW_STR "Statistics about the BSC\n") -{ - openbsc_vty_print_statistics(vty, bsc_gsmnet); - return CMD_SUCCESS; -} - -DEFUN(show_mscs, - show_mscs_cmd, - "show mscs", - SHOW_STR "MSC Connections and State\n") -{ - struct osmo_msc_data *msc; - llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) { - vty_out(vty, "MSC Nr: %d is connected: %d auth: %d.%s", - msc->nr, - msc->msc_con ? msc->msc_con->is_connected : -1, - msc->msc_con ? msc->msc_con->is_authenticated : -1, - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(show_pos, - show_pos_cmd, - "show position", - SHOW_STR "Position information of the BTS\n") -{ - struct gsm_bts *bts; - struct bts_location *curloc; - struct tm time; - char timestr[50]; - - llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { - if (llist_empty(&bts->loc_list)) { - vty_out(vty, "BTS Nr: %d position invalid%s", bts->nr, - VTY_NEWLINE); - continue; - } - curloc = llist_entry(bts->loc_list.next, struct bts_location, list); - if (gmtime_r(&curloc->tstamp, &time) == NULL) { - vty_out(vty, "Time conversion failed for BTS %d%s", bts->nr, - VTY_NEWLINE); - continue; - } - if (asctime_r(&time, timestr) == NULL) { - vty_out(vty, "Time conversion failed for BTS %d%s", bts->nr, - VTY_NEWLINE); - continue; - } - /* Last character in asctime is \n */ - timestr[strlen(timestr)-1] = 0; - - vty_out(vty, "BTS Nr: %d position: %s time: %s%s", bts->nr, - get_value_string(bts_loc_fix_names, curloc->valid), timestr, - VTY_NEWLINE); - vty_out(vty, " lat: %f lon: %f height: %f%s", curloc->lat, curloc->lon, - curloc->height, VTY_NEWLINE); - } - return CMD_SUCCESS; -} - -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); - 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_node(&msc_node, config_write_msc); - install_default(MSC_NODE); - install_element(MSC_NODE, &cfg_net_bsc_token_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_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_ping_time_cmd); - install_element(MSC_NODE, &cfg_net_msc_pong_time_cmd); - install_element(MSC_NODE, &cfg_net_msc_welcome_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_ve(&show_statistics_cmd); - install_element_ve(&show_mscs_cmd); - install_element_ve(&show_pos_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 0456cf108..000000000 --- a/openbsc/src/osmo-bsc_mgcp/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) - -bin_PROGRAMS = osmo-bsc_mgcp - -osmo_bsc_mgcp_SOURCES = mgcp_main.c -osmo_bsc_mgcp_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ - $(top_builddir)/src/libmgcp/libmgcp.a -lrt \ - $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) 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 540f7602b..000000000 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c +++ /dev/null @@ -1,304 +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 "../../bscconfig.h" - -/* this is here for the vty... it will never be called */ -void subscr_put() { abort(); } - -#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 mgcp_change_cb(struct mgcp_trunk_config *cfg, int endpoint, int state) -{ - if (state != MGCP_ENDP_MDCX) - return 0; - - mgcp_send_dummy(&cfg->endpoints[endpoint]); - 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", - 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_free_endp(&reset_trunk->endpoints[i]); - } - - return 0; -} - -extern enum node_type bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OpenBSC MGCP", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - -int main(int argc, char **argv) -{ - struct gsm_network dummy_network; - struct sockaddr_in addr; - int on = 1, rc; - - tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent"); - - osmo_init_ignore_signals(); - osmo_init_logging(&log_info); - - cfg = mgcp_config_alloc(); - if (!cfg) - return -1; - - vty_info.copyright = openbsc_copyright; - vty_init(&vty_info); - logging_vty_add_cmds(&log_info); - mgcp_vty_init(); - - handle_options(argc, argv); - - rc = mgcp_parse_config(config_file, cfg); - if (rc < 0) - return rc; - - rc = telnet_init(tall_bsc_ctx, &dummy_network, 4243); - if (rc < 0) - return rc; - - /* set some callbacks */ - cfg->reset_cb = mgcp_rsip_cb; - cfg->change_cb = mgcp_change_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 e2ba551a0..000000000 --- a/openbsc/src/osmo-bsc_nat/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(COVERAGE_LDFLAGS) - -bin_PROGRAMS = osmo-bsc_nat - - -osmo_bsc_nat_SOURCES = bsc_filter.c bsc_mgcp_utils.c bsc_nat.c bsc_nat_utils.c \ - bsc_nat_vty.c bsc_sccp.c bsc_ussd.c bsc_nat_ctrl.c \ - bsc_nat_rewrite.c bsc_nat_filter.c -osmo_bsc_nat_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ - $(top_builddir)/src/libmgcp/libmgcp.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libctrl/libctrl.a \ - -lrt $(LIBOSMOSCCP_LIBS) $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOABIS_LIBS) 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 bbbba6994..000000000 --- a/openbsc/src/osmo-bsc_nat/bsc_filter.c +++ /dev/null @@ -1,216 +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; - 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 8bb6075d1..000000000 --- a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c +++ /dev/null @@ -1,1049 +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 - -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(*(uint16_t *)TLVP_VAL(&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_free_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, - bsc->nat->mgcp_cfg->source_addr, - 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 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; - } - } - - /* we need to generate a new and patched message */ - bsc_msg = bsc_mgcp_rewrite((char *) nat->mgcp_msg, nat->mgcp_length, sccp->bsc_endp, - nat->mgcp_cfg->source_addr, mgcp_endp->bts_end.local_port); - 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; - 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); - 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_free_endp(endp); -} - -/* - * 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; - } - - /* 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, - bsc->nat->mgcp_cfg->source_addr, endp->net_end.local_port); - - 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 slen; - int ret; - char buf[40]; - - 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; - } - - slen = sprintf((char *) output->l3h, "%s %s %x@mgw MGCP 1.0%s", - op, buf, endp, 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) -{ - 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); - } else if (strncmp(dlcx_str, token, (sizeof dlcx_str) - 1) == 0) { - patch_mgcp(output, "DLCX", token, endpoint, len, cr); - } else if (strncmp(mdcx_str, token, (sizeof mdcx_str) - 1) == 0) { - patch_mgcp(output, "MDCX", token, endpoint, len, cr); - } 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) { - if (sscanf(token, "m=audio %*d RTP/AVP %d", &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 %d%s", - port, payload, cr ? "\r\n" : "\n"); - 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 (!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)); - } - - 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_free_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 12373397d..000000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ /dev/null @@ -1,1609 +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 - -#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 "../../bscconfig.h" - -#define SCCP_CLOSE_TIME 20 -#define SCCP_CLOSE_TIME_TIMEOUT 19 - -static const char *config_file = "bsc-nat.cfg"; -static struct in_addr local_addr; -static struct osmo_fd bsc_listen; -static const char *msc_ip = NULL; -static struct osmo_timer_list sccp_close; -static int daemonize = 0; - -const char *openbsc_copyright = - "Copyright (C) 2010 Holger Hans Peter Freyther and On-Waves\r\n" - "License AGPLv3+: GNU AGPL version 3 or later \r\n" - "This is free software: you are free to change and redistribute it.\r\n" - "There is NO WARRANTY, to the extent permitted by law.\r\n"; - -static struct bsc_nat *nat; -static void bsc_send_data(struct bsc_connection *bsc, const uint8_t *data, unsigned int length, int); -static void msc_send_reset(struct bsc_msc_connection *con); -static void bsc_stat_reject(int filter, struct bsc_connection *bsc, int normal); - -struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num) -{ - struct bsc_config *conf; - - llist_for_each_entry(conf, &nat->bsc_configs, entry) - if (conf->nr == num) - return conf; - - return NULL; -} - -static void queue_for_msc(struct bsc_msc_connection *con, struct msgb *msg) -{ - if (!con) { - LOGP(DLINP, LOGL_ERROR, "No MSC Connection assigned. Check your code.\n"); - msgb_free(msg); - return; - } - - - if (osmo_wqueue_enqueue(&con->write_queue, msg) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the write.\n"); - msgb_free(msg); - } -} - -static void send_reset_ack(struct bsc_connection *bsc) -{ - static const uint8_t gsm_reset_ack[] = { - 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, - 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03, - 0x00, 0x01, 0x31, - }; - - bsc_send_data(bsc, gsm_reset_ack, sizeof(gsm_reset_ack), IPAC_PROTO_SCCP); -} - -static void send_ping(struct bsc_connection *bsc) -{ - static const uint8_t id_ping[] = { - IPAC_MSGT_PING, - }; - - bsc_send_data(bsc, id_ping, sizeof(id_ping), IPAC_PROTO_IPACCESS); -} - -static void send_pong(struct bsc_connection *bsc) -{ - static const uint8_t id_pong[] = { - IPAC_MSGT_PONG, - }; - - bsc_send_data(bsc, id_pong, sizeof(id_pong), IPAC_PROTO_IPACCESS); -} - -static void bsc_pong_timeout(void *_bsc) -{ - struct bsc_connection *bsc = _bsc; - - LOGP(DNAT, LOGL_ERROR, "BSC Nr: %d PONG timeout.\n", bsc->cfg->nr); - bsc_close_connection(bsc); -} - -static void bsc_ping_timeout(void *_bsc) -{ - struct bsc_connection *bsc = _bsc; - - if (bsc->nat->ping_timeout < 0) - return; - - send_ping(bsc); - - /* send another ping in 20 seconds */ - osmo_timer_schedule(&bsc->ping_timeout, bsc->nat->ping_timeout, 0); - - /* also start a pong timer */ - osmo_timer_schedule(&bsc->pong_timeout, bsc->nat->pong_timeout, 0); -} - -static void start_ping_pong(struct bsc_connection *bsc) -{ - bsc->pong_timeout.data = bsc; - bsc->pong_timeout.cb = bsc_pong_timeout; - bsc->ping_timeout.data = bsc; - bsc->ping_timeout.cb = bsc_ping_timeout; - - bsc_ping_timeout(bsc); -} - -static void send_id_ack(struct bsc_connection *bsc) -{ - static const uint8_t id_ack[] = { - IPAC_MSGT_ID_ACK - }; - - bsc_send_data(bsc, id_ack, sizeof(id_ack), IPAC_PROTO_IPACCESS); -} - -static void send_id_req(struct bsc_connection *bsc) -{ - static const uint8_t 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, - }; - - bsc_send_data(bsc, id_req, sizeof(id_req), IPAC_PROTO_IPACCESS); -} - -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; - - ipaccess_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 allocate clear command.\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; - - ipaccess_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) -{ - ipaccess_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(nat->token); - if (!msg) - return; - - ipaccess_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_nat_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 { - ipaccess_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->con_type == NAT_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->con_type = NAT_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_nat_reject_cause *cause) -{ - struct msgb *payload; - struct msgb *refuse; - - if (con_type == NAT_CON_TYPE_LU) - payload = gsm48_create_loc_upd_rej(cause->lu_reject_cause); - else if (con_type == NAT_CON_TYPE_CM_SERV_REQ || con_type == NAT_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->con_type = NAT_CON_TYPE_LOCAL_REJECT; - con->con_local = NAT_CON_END_LOCAL; - con->has_remote_ref = 1; - con->remote_ref = con->patched_ref; - - /* 1. create a confirmation */ - cc = sccp_create_cc(&con->remote_ref, &con->real_ref); - if (!cc) - goto send_refuse; - - /* 2. create the DT1 */ - gsm0808_prepend_dtap_header(payload, 0); - udt = sccp_create_dt1(&con->real_ref, payload->data, payload->len); - if (!udt) { - msgb_free(cc); - goto send_refuse; - } - - /* 3. send a Clear Command */ - clear = nat_creat_clrc(con, 0x20); - if (!clear) { - msgb_free(cc); - msgb_free(udt); - goto send_refuse; - } - - /* 4. send a RLSD */ - rlsd = sccp_create_rlsd(&con->remote_ref, &con->real_ref, - SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); - if (!rlsd) { - msgb_free(cc); - msgb_free(udt); - msgb_free(clear); - goto send_refuse; - } - - bsc_write(bsc, cc, IPAC_PROTO_SCCP); - bsc_write(bsc, udt, IPAC_PROTO_SCCP); - bsc_write(bsc, clear, IPAC_PROTO_SCCP); - bsc_write(bsc, rlsd, IPAC_PROTO_SCCP); - msgb_free(payload); - return; - } - - -send_refuse: - if (payload) - msgb_free(payload); - - refuse = sccp_create_refuse(parsed->src_local_ref, - SCCP_REFUSAL_SCCP_FAILURE, NULL, 0); - if (!refuse) { - LOGP(DNAT, LOGL_ERROR, - "Creating refuse msg failed for SCCP 0x%x on BSC Nr: %d.\n", - sccp_src_ref_to_int(parsed->src_local_ref), bsc->cfg->nr); - return; - } - - bsc_write(bsc, refuse, IPAC_PROTO_SCCP); -} - -static void bsc_nat_send_paging(struct bsc_connection *bsc, struct msgb *msg) -{ - if (bsc->cfg->forbid_paging) { - LOGP(DNAT, LOGL_DEBUG, "Paging forbidden for BTS: %d\n", bsc->cfg->nr); - return; - } - - bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), IPAC_PROTO_SCCP); -} - -static void bsc_nat_handle_paging(struct bsc_nat *nat, struct msgb *msg) -{ - struct bsc_connection *bsc; - const uint8_t *paging_start; - int paging_length, i, ret; - - ret = bsc_nat_find_paging(msg, &paging_start, &paging_length); - if (ret != 0) { - LOGP(DNAT, LOGL_ERROR, "Could not parse paging message: %d\n", ret); - return; - } - - /* This is quite expensive now */ - for (i = 0; i < paging_length; i += 2) { - unsigned int _lac = ntohs(*(unsigned int *) &paging_start[i]); - unsigned int paged = 0; - llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) { - if (!bsc->cfg) - continue; - if (!bsc->authenticated) - continue; - if (!bsc_config_handles_lac(bsc->cfg, _lac)) - continue; - bsc_nat_send_paging(bsc, msg); - paged += 1; - } - - /* highlight a possible config issue */ - if (paged == 0) - LOGP(DNAT, LOGL_ERROR, "No BSC for LAC %d/0x%d\n", _lac, _lac); - - } -} - - -/* - * Update the auth status. This can be either a CIPHER MODE COMAMND or - * a CM Serivce Accept. Maybe also LU Accept or such in the future. - */ -static void update_con_authorize(struct nat_sccp_connection *con, - struct bsc_nat_parsed *parsed, - struct msgb *msg) -{ - if (!con) - return; - if (con->authorized) - return; - - if (parsed->bssap == BSSAP_MSG_BSS_MANAGEMENT && - parsed->gsm_type == BSS_MAP_MSG_CIPHER_MODE_CMD) { - con->authorized = 1; - } else if (parsed->bssap == BSSAP_MSG_DTAP) { - uint8_t msg_type, proto; - uint32_t len; - struct gsm48_hdr *hdr48; - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return; - - proto = hdr48->proto_discr & 0x0f; - msg_type = hdr48->msg_type & 0xbf; - if (proto == GSM48_PDISC_MM && - msg_type == GSM48_MT_MM_CM_SERV_ACC) - con->authorized = 1; - } -} - -static int forward_sccp_to_bts(struct bsc_msc_connection *msc_con, struct msgb *msg) -{ - struct nat_sccp_connection *con = NULL; - struct bsc_connection *bsc; - struct bsc_nat_parsed *parsed; - int proto; - - /* filter, drop, patch the message? */ - parsed = bsc_nat_parse(msg); - if (!parsed) { - LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n"); - return -1; - } - - if (bsc_nat_filter_ipa(DIR_BSC, msg, parsed)) - goto exit; - - proto = parsed->ipa_proto; - - /* Route and modify the SCCP packet */ - if (proto == IPAC_PROTO_SCCP) { - switch (parsed->sccp_type) { - case SCCP_MSG_TYPE_UDT: - /* forward UDT messages to every BSC */ - goto send_to_all; - break; - case SCCP_MSG_TYPE_RLSD: - case SCCP_MSG_TYPE_CREF: - case SCCP_MSG_TYPE_DT1: - case SCCP_MSG_TYPE_IT: - con = patch_sccp_src_ref_to_bsc(msg, parsed, nat); - if (parsed->gsm_type == BSS_MAP_MSG_ASSIGMENT_RQST) { - osmo_counter_inc(nat->stats.sccp.calls); - - if (con) { - struct rate_ctr_group *ctrg; - ctrg = con->bsc->cfg->stats.ctrg; - rate_ctr_inc(&ctrg->ctr[BCFG_CTR_SCCP_CALLS]); - if (bsc_mgcp_assign_patch(con, msg) != 0) - LOGP(DNAT, LOGL_ERROR, "Failed to assign...\n"); - } else - LOGP(DNAT, LOGL_ERROR, "Assignment command but no BSC.\n"); - } else if (con && con->con_local == NAT_CON_END_USSD && - parsed->gsm_type == BSS_MAP_MSG_CLEAR_CMD) { - LOGP(DNAT, LOGL_NOTICE, "Clear Command for USSD Connection. Ignoring.\n"); - con = NULL; - } - break; - case SCCP_MSG_TYPE_CC: - con = patch_sccp_src_ref_to_bsc(msg, parsed, nat); - if (!con || update_sccp_src_ref(con, parsed) != 0) - goto exit; - break; - case SCCP_MSG_TYPE_RLC: - LOGP(DNAT, LOGL_ERROR, "Unexpected release complete from MSC.\n"); - goto exit; - break; - case SCCP_MSG_TYPE_CR: - /* MSC never opens a SCCP connection, fall through */ - default: - goto exit; - } - - if (!con && parsed->sccp_type == SCCP_MSG_TYPE_RLSD) { - LOGP(DNAT, LOGL_NOTICE, "Sending fake RLC on RLSD message to network.\n"); - /* Exchange src/dest for the reply */ - nat_send_rlc(msc_con, parsed->dest_local_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; - struct ipaccess_head *hh; - int ret; - - msc_con = (struct bsc_msc_connection *) bfd->data; - - ret = ipa_msg_recv(bfd->fd, &msg); - if (ret <= 0) { - if (ret == 0) - LOGP(DNAT, LOGL_FATAL, "The connection the MSC was lost, exiting\n"); - else - LOGP(DNAT, LOGL_ERROR, "Failed to parse ip access message: %d\n", ret); - - bsc_msc_lost(msc_con); - return -1; - } - - LOGP(DNAT, LOGL_DEBUG, "MSG 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; - ipaccess_rcvmsg_base(msg, bfd); - - /* initialize the networking. This includes sending a GSM08.08 message */ - if (hh->proto == IPAC_PROTO_IPACCESS) { - 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); - - 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 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); - const int len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME); - - 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; - } - - llist_for_each_entry(conf, &bsc->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) { - 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); - return; - } - } - - LOGP(DNAT, LOGL_ERROR, "No bsc found for token '%s' on fd: %d.\n", token, - bsc->write_queue.bfd.fd); -} - -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_nat_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) { - bsc_stat_reject(filter, bsc, 0); - goto exit3; - } - - if (!create_sccp_src_ref(bsc, parsed)) - goto exit2; - con = patch_sccp_src_ref_to_msc(msg, parsed, bsc); - con->msc_con = bsc->nat->msc_con; - con_msc = con->msc_con; - con->con_type = con_type; - con->imsi_checked = filter; - bsc_nat_extract_lac(bsc, con, parsed, msg); - if (imsi) - con->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) { - 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_check_ussd(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->imsi); - talloc_free(parsed); - parsed = NULL; - } else if (con->con_local == NAT_CON_END_USSD) { - bsc_check_ussd(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) { - struct tlv_parsed tvp; - int ret; - ret = ipaccess_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"); - 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; - struct ipaccess_head *hh; - struct ipaccess_head_ext *hh_ext; - int ret; - - ret = ipa_msg_recv(bfd->fd, &msg); - if (ret <= 0) { - if (ret == 0) - LOGP(DNAT, LOGL_ERROR, - "The connection to the BSC Nr: %d was lost. Cleaning it\n", - bsc->cfg ? bsc->cfg->nr : -1); - else - LOGP(DNAT, LOGL_ERROR, - "Stream error on BSC Nr: %d. Failed to parse ip access message: %d\n", - bsc->cfg ? bsc->cfg->nr : -1, ret); - - bsc_close_connection(bsc); - return -1; - } - - - LOGP(DNAT, LOGL_DEBUG, "MSG from BSC: %s proto: %d\n", osmo_hexdump(msg->data, msg->len), msg->l2h[0]); - - /* Handle messages from the BSC */ - hh = (struct ipaccess_head *) msg->data; - - /* stop the pong timeout */ - if (hh->proto == IPAC_PROTO_IPACCESS) { - if (msg->l2h[0] == IPAC_MSGT_PONG) { - osmo_timer_del(&bsc->pong_timeout); - msgb_free(msg); - return 0; - } else if (msg->l2h[0] == IPAC_MSGT_PING) { - send_pong(bsc); - msgb_free(msg); - return 0; - } - /* Message contains the ipaccess_head_ext header, investigate further */ - } else if (hh->proto == IPAC_PROTO_OSMO && - msg->len > sizeof(*hh) + sizeof(*hh_ext)) { - - hh_ext = (struct ipaccess_head_ext *) hh->data; - /* l2h is where the actual command data is expected */ - msg->l2h = hh_ext->data; - - if (hh_ext->proto == IPAC_PROTO_EXT_CTRL) - return bsc_nat_handle_ctrlif_msg(bsc, msg); - } - - /* FIXME: Currently no PONG is sent to the BSC */ - /* FIXME: Currently no ID ACK is sent to the BSC */ - forward_sccp_to_msc(bsc, msg); - - return 0; -} - -static int ipaccess_listen_bsc_cb(struct osmo_fd *bfd, unsigned int what) -{ - struct bsc_connection *bsc; - int fd, rc, on; - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - - if (!(what & BSC_FD_READ)) - return 0; - - fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len); - if (fd < 0) { - perror("accept"); - return fd; - } - - /* count the reconnect */ - osmo_counter_inc(nat->stats.bsc.reconn); - - /* - * if we are not connected to a msc... just close the socket - */ - if (!bsc_nat_msc_is_connected(nat)) { - LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due lack of MSC connection.\n"); - close(fd); - return 0; - } - - if (nat->blocked) { - LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due NAT being blocked.\n"); - close(fd); - return 0; - } - - on = 1; - rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); - if (rc != 0) - LOGP(DNAT, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); - - rc = setsockopt(fd, IPPROTO_IP, IP_TOS, - &nat->bsc_ip_dscp, sizeof(nat->bsc_ip_dscp)); - if (rc != 0) - LOGP(DNAT, LOGL_ERROR, "Failed to set IP_TOS: %s\n", strerror(errno)); - - /* todo... do something with the connection */ - /* todo... use GNUtls to see if we want to trust this as a BTS */ - - /* - * - */ - bsc = bsc_connection_alloc(nat); - if (!bsc) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate BSC struct.\n"); - close(fd); - return -1; - } - - bsc->write_queue.bfd.data = bsc; - bsc->write_queue.bfd.fd = fd; - bsc->write_queue.read_cb = ipaccess_bsc_read_cb; - bsc->write_queue.write_cb = bsc_write_cb; - bsc->write_queue.bfd.when = BSC_FD_READ; - if (osmo_fd_register(&bsc->write_queue.bfd) < 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to register BSC fd.\n"); - close(fd); - talloc_free(bsc); - return -2; - } - - LOGP(DNAT, LOGL_NOTICE, "BSC connection on %d with IP: %s\n", - fd, inet_ntoa(sa.sin_addr)); - - llist_add(&bsc->list_entry, &nat->bsc_connections); - bsc->last_id = 0; - - send_id_ack(bsc); - send_id_req(bsc); - send_mgcp_reset(bsc); - - /* - * start the hangup timer - */ - bsc->id_timeout.data = bsc; - bsc->id_timeout.cb = ipaccess_close_bsc; - osmo_timer_schedule(&bsc->id_timeout, nat->auth_timeout, 0); - return 0; -} - -static void print_usage() -{ - printf("Usage: bsc_nat\n"); -} - -static void print_help() -{ - printf(" Some useful help...\n"); - printf(" -h --help this text\n"); - printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n"); - printf(" -D --daemonize Fork the process into a background daemon\n"); - printf(" -s --disable-color\n"); - printf(" -c --config-file filename The config file to use.\n"); - printf(" -m --msc=IP. The address of the MSC.\n"); - printf(" -l --local=IP. The local address of this BSC.\n"); -} - -static void handle_options(int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"debug", 1, 0, 'd'}, - {"daemonize", 0, 0, 'D'}, - {"config-file", 1, 0, 'c'}, - {"disable-color", 0, 0, 's'}, - {"timestamp", 0, 0, 'T'}, - {"msc", 1, 0, 'm'}, - {"local", 1, 0, 'l'}, - {0, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hd:sTPc:m:l:D", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(); - print_help(); - exit(0); - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - daemonize = 1; - break; - case 'c': - config_file = strdup(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.\n", - sccp_src_ref_to_int(&conn->real_ref), - sccp_src_ref_to_int(&conn->patched_ref)); - sccp_connection_destroy(conn); - destroyed = 1; - } - - if (!destroyed) - goto out; - - /* now close out any BSC */ - llist_for_each_entry_safe(bsc, bsc_tmp, &nat->bsc_connections, list_entry) - bsc_maybe_close(bsc); - -out: - osmo_timer_schedule(&sccp_close, SCCP_CLOSE_TIME, 0); -} - -extern void *tall_msgb_ctx; -extern void *tall_ctr_ctx; -static void talloc_init_ctx() -{ - tall_bsc_ctx = talloc_named_const(NULL, 0, "nat"); - tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); - tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); -} - -extern enum node_type 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; - } - - vty_info.copyright = openbsc_copyright; - vty_init(&vty_info); - logging_vty_add_cmds(&log_info); - bsc_nat_vty_init(nat); - - - /* parse options */ - local_addr.s_addr = INADDR_ANY; - handle_options(argc, argv); - - rate_ctr_init(tall_bsc_ctx); - - /* init vty and parse */ - telnet_init(tall_bsc_ctx, NULL, 4244); - if (mgcp_parse_config(config_file, nat->mgcp_cfg) < 0) { - fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); - return -3; - } - - /* over rule the VTY config */ - if (msc_ip) - bsc_nat_set_msc_ip(nat, msc_ip); - - /* seed the PRNG */ - srand(time(NULL)); - - /* - * Setup the MGCP code.. - */ - if (bsc_mgcp_nat_init(nat) != 0) - return -4; - - /* connect to the MSC */ - nat->msc_con = bsc_msc_create(nat, &nat->dests); - if (!nat->msc_con) { - fprintf(stderr, "Creating a bsc_msc_connection failed.\n"); - exit(1); - } - - nat->ctrl = bsc_nat_controlif_setup(nat, 4250); - if (!nat->ctrl) { - fprintf(stderr, "Creating the control interface failed.\n"); - exit(1); - } - - nat->msc_con->connection_loss = msc_connection_was_lost; - nat->msc_con->connected = msc_connection_connected; - nat->msc_con->write_queue.read_cb = ipaccess_msc_read_cb; - nat->msc_con->write_queue.write_cb = ipaccess_msc_write_cb;; - nat->msc_con->write_queue.bfd.data = nat->msc_con; - bsc_msc_connect(nat->msc_con); - - /* wait for the BSC */ - rc = make_sock(&bsc_listen, IPPROTO_TCP, ntohl(local_addr.s_addr), - 5000, 0, ipaccess_listen_bsc_cb, nat); - if (rc != 0) { - fprintf(stderr, "Failed to listen for BSC.\n"); - exit(1); - } - - rc = bsc_ussd_init(nat); - if (rc != 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to bind the USSD socket.\n"); - exit(1); - } - - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - osmo_init_ignore_signals(); - - if (daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - perror("Error during daemonize"); - exit(1); - } - } - - /* recycle timer */ - sccp_set_log_area(DSCCP); - sccp_close.cb = sccp_close_unconfirmed; - sccp_close.data = NULL; - osmo_timer_schedule(&sccp_close, SCCP_CLOSE_TIME, 0); - - while (1) { - osmo_select_main(0); - } - - return 0; -} - -/* Close all connections handed out to the USSD module */ -int bsc_close_ussd_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 6ff4541a0..000000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_ctrl.c +++ /dev/null @@ -1,324 +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 - - -#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 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 *nr_str, *tmp, *saveptr = NULL; - - /* Skip over the beginning (bsc.) */ - tmp = strtok_r(cmd->variable, ".", &saveptr); - tmp = strtok_r(NULL, ".", &saveptr); - tmp = strtok_r(NULL, ".", &saveptr); - nr_str = strtok_r(NULL, ".", &saveptr); - if (!nr_str) { - cmd->reply = "command incomplete"; - goto err; - } - nr = atoi(nr_str); - - tmp = strtok_r(NULL, "\0", &saveptr); - if (!tmp) { - 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, tmp); - if (!bsc_cmd->variable) { - cmd->reply = "OOM"; - goto err; - } - - if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) { - cmd->reply = "Sending failed"; - goto err; - } - pending->ccon = cmd->ccon; - pending->ccon->closed_cb = ctrl_conn_closed_cb; - pending->cmd = cmd; - - /* Setup the timeout */ - pending->timeout.data = pending; - pending->timeout.cb = pending_timeout_cb; - /* TODO: Make timeout configurable */ - osmo_timer_schedule(&pending->timeout, 10, 0); - llist_add_tail(&pending->list_entry, &bsc->cmd_pending); - - goto done; - } - } - /* We end up here if there's no bsc to handle our LAC */ - cmd->reply = "no BSC with this nr"; -err: - ret = CTRL_CMD_ERROR; -done: - if (bsc_cmd) - 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; -} - -struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, int port) -{ - struct ctrl_handle *ctrl; - int rc; - - - ctrl = controlif_setup(NULL, 4250); - 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"); - osmo_fd_unregister(&ctrl->listen_fd); - close(ctrl->listen_fd.fd); - talloc_free(ctrl); - return NULL; - } - - g_nat = nat; - return ctrl; -} - 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 8ccc26259..000000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * Access filtering - */ -/* - * (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 - -int bsc_nat_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu) -{ - struct bsc_nat_barr_entry *n; - n = rb_entry(root->rb_node, struct bsc_nat_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_nat_barr_entry, node); - }; - - return 0; -} - -static int insert_barr_node(struct bsc_nat_barr_entry *entry, struct rb_root *root) -{ - struct rb_node **new = &root->rb_node, *parent = NULL; - - while (*new) { - int rc; - struct bsc_nat_barr_entry *this; - this = rb_entry(*new, struct bsc_nat_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(DNAT, 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_nat_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_nat_barr_entry *entry; - entry = talloc_zero(ctx, struct bsc_nat_barr_entry); - if (!entry) { - LOGP(DNAT, 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_nat_acc_lst *lst, const char *mi_string) -{ - struct bsc_nat_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) - return 0; - } - - return 1; -} - -/* apply white/black list */ -static int auth_imsi(struct bsc_connection *bsc, const char *imsi, - struct bsc_nat_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_nat_acc_lst *nat_lst = NULL; - struct bsc_nat_acc_lst *bsc_lst = NULL; - - /* 1. global check for barred imsis */ - if (bsc_nat_barr_find(&bsc->nat->imsi_black_list, imsi, &cm, &lu)) { - cause->cm_reject_cause = cm; - cause->lu_reject_cause = lu; - LOGP(DNAT, LOGL_DEBUG, - "Blocking subscriber IMSI %s with CM: %d LU: %d\n", - imsi, cm, lu); - return -1; - } - - - bsc_lst = bsc_nat_acc_lst_find(bsc->nat, bsc->cfg->acc_lst_name); - nat_lst = bsc_nat_acc_lst_find(bsc->nat, bsc->nat->acc_lst_name); - - - if (bsc_lst) { - /* 2. BSC allow */ - if (bsc_nat_lst_check_allow(bsc_lst, imsi) == 0) - return 1; - - /* 3. BSC deny */ - if (lst_check_deny(bsc_lst, imsi) == 0) { - LOGP(DNAT, LOGL_ERROR, - "Filtering %s by imsi_deny on bsc nr: %d.\n", imsi, bsc->cfg->nr); - rate_ctr_inc(&bsc_lst->stats->ctr[ACC_LIST_BSC_FILTER]); - return -2; - } - - } - - /* 4. NAT deny */ - if (nat_lst) { - if (lst_check_deny(nat_lst, imsi) == 0) { - LOGP(DNAT, LOGL_ERROR, - "Filtering %s by nat imsi_deny on bsc nr: %d.\n", imsi, bsc->cfg->nr); - rate_ctr_inc(&nat_lst->stats->ctr[ACC_LIST_NAT_FILTER]); - return -3; - } - } - - return 1; -} - -static int _cr_check_loc_upd(struct bsc_connection *bsc, - 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(DNAT, 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(bsc, mi_string); - return 1; -} - -static int _cr_check_cm_serv_req(struct bsc_connection *bsc, - 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(DNAT, 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 = NAT_CON_TYPE_SSA; - rc = gsm48_extract_mi((uint8_t *) &req->classmark, - length - classmark_offset, mi_string, &mi_type); - if (rc < 0) { - LOGP(DNAT, 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(bsc, mi_string); - return 1; -} - -static int _cr_check_pag_resp(struct bsc_connection *bsc, - 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(DNAT, 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(DNAT, 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(bsc, mi_string); - return 1; -} - -static int _dt_check_id_resp(struct bsc_connection *bsc, - uint8_t *data, unsigned int length, - struct nat_sccp_connection *con, - struct bsc_nat_reject_cause *cause) -{ - char mi_string[GSM48_MI_SIZE]; - uint8_t mi_type; - - if (length < 2) { - LOGP(DNAT, LOGL_ERROR, "mi does not fit.\n"); - return -1; - } - - if (data[0] < length - 1) { - LOGP(DNAT, 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; - - con->imsi_checked = 1; - con->imsi = talloc_strdup(con, mi_string); - return auth_imsi(bsc, mi_string, cause); -} - - -/* 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_nat_reject_cause *cause) -{ - struct tlv_parsed tp; - struct gsm48_hdr *hdr48; - int hdr48_len; - int len, ret = 0; - uint8_t msg_type, proto; - - *con_type = NAT_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); - - proto = hdr48->proto_discr & 0x0f; - msg_type = hdr48->msg_type & 0xbf; - if (proto == GSM48_PDISC_MM && - msg_type == GSM48_MT_MM_LOC_UPD_REQUEST) { - *con_type = NAT_CON_TYPE_LU; - ret = _cr_check_loc_upd(bsc, &hdr48->data[0], - hdr48_len - sizeof(*hdr48), imsi); - } else if (proto == GSM48_PDISC_MM && - msg_type == GSM48_MT_MM_CM_SERV_REQ) { - *con_type = NAT_CON_TYPE_CM_SERV_REQ; - ret = _cr_check_cm_serv_req(bsc, &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 = NAT_CON_TYPE_PAG_RESP; - ret = _cr_check_pag_resp(bsc, &hdr48->data[0], - hdr48_len - sizeof(*hdr48), imsi); - } else { - /* We only want to filter the above, let other things pass */ - *con_type = NAT_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(bsc, *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_nat_reject_cause *cause) -{ - uint32_t len; - uint8_t msg_type, proto; - struct gsm48_hdr *hdr48; - - cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - - if (con->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; - - proto = hdr48->proto_discr & 0x0f; - msg_type = hdr48->msg_type & 0xbf; - if (proto != GSM48_PDISC_MM || msg_type != GSM48_MT_MM_ID_RESP) - return 0; - - return _dt_check_id_resp(bsc, &hdr48->data[0], - len - sizeof(*hdr48), con, 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 06071c475..000000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c +++ /dev/null @@ -1,640 +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 - -static char *match_and_rewrite_number(void *ctx, const char *number, - const char *imsi, - struct llist_head *list) -{ - 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) - 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, 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)) - return NULL; - - /* only ISDN plan */ - if (called->plan != 1) - 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, &nat->num_rewr); -} - - -/** - * 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 = NULL; - 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 */ - new_number = rewrite_isdn_number(nat, msg, imsi, &called); - - if (!new_number) { - LOGP(DNAT, LOGL_DEBUG, "No IMSI match found, returning message.\n"); - return NULL; - } - - if (strlen(new_number) > sizeof(called.number)) { - LOGP(DNAT, LOGL_ERROR, "Number is too long for structure.\n"); - talloc_free(new_number); - 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(DNAT, LOGL_ERROR, "Failed to allocate.\n"); - talloc_free(new_number); - 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 */ - if (strncmp(new_number, "00", 2) == 0) { - called.type = 1; - strncpy(called.number, new_number + 2, sizeof(called.number)); - } else { - /* rewrite international to unknown */ - if (called.type == 1) - called.type = 0; - strncpy(called.number, new_number, sizeof(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); - 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); -} - -/** - * This is a helper for GSM 04.11 8.2.5.2 Destination address element - */ -void sms_encode_addr_element(struct msgb *out, const char *new_number, - int format, int tp_data) -{ - uint8_t new_addr_len; - uint8_t new_addr[26]; - - /* - * Copy the new number. We let libosmocore encode it, then set - * the extension followed after the length. Depending on if - * we want to write RP we will let the TLV code add the - * length for us or we need to use strlen... This is not very clear - * as of 03.40 and 04.11. - */ - new_addr_len = gsm48_encode_bcd_number(new_addr, ARRAY_SIZE(new_addr), - 1, new_number); - new_addr[1] = format; - if (tp_data) { - uint8_t *data = msgb_put(out, new_addr_len); - memcpy(data, new_addr, new_addr_len); - data[0] = strlen(new_number); - } else { - msgb_lv_put(out, new_addr_len - 1, new_addr + 1); - } -} - -static struct msgb *sms_create_new(uint8_t type, uint8_t ref, - struct gsm48_hdr *old_hdr48, - const uint8_t *orig_addr_ptr, - int orig_addr_len, const char *new_number, - const uint8_t *data_ptr, int data_len, - uint8_t tpdu_first_byte, - const int old_dest_len, const char *new_dest_nr) -{ - struct gsm48_hdr *new_hdr48; - struct msgb *out; - - /* - * We need to re-create the patched structure. This is why we have - * saved the above pointers. - */ - out = msgb_alloc_headroom(4096, 128, "changed-smsc"); - if (!out) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); - return NULL; - } - - out->l2h = out->data; - msgb_v_put(out, GSM411_MT_RP_DATA_MO); - msgb_v_put(out, ref); - msgb_lv_put(out, orig_addr_len, orig_addr_ptr); - - sms_encode_addr_element(out, new_number, 0x91, 0); - - - /* Patch the TPDU from here on */ - - /** - * Do we need to put a new TP-Destination-Address (TP-DA) here or - * can we copy the old thing? For the TP-DA we need to find out the - * new size. - */ - if (new_dest_nr) { - uint8_t *data, *new_size; - - /* reserve the size and write the header */ - new_size = msgb_put(out, 1); - out->l3h = new_size + 1; - msgb_v_put(out, tpdu_first_byte); - msgb_v_put(out, data_ptr[1]); - - /* encode the new number and put it */ - if (strncmp(new_dest_nr, "00", 2) == 0) - sms_encode_addr_element(out, new_dest_nr + 2, 0x91, 1); - else - sms_encode_addr_element(out, new_dest_nr, 0x81, 1); - - /* Copy the rest after the TP-DS */ - data = msgb_put(out, data_len - 2 - 1 - old_dest_len); - memcpy(data, &data_ptr[2 + 1 + old_dest_len], data_len - 2 - 1 - old_dest_len); - - /* fill in the new size */ - new_size[0] = msgb_l3len(out); - } else { - msgb_v_put(out, data_len); - msgb_tv_fixed_put(out, tpdu_first_byte, data_len - 1, &data_ptr[1]); - } - - /* prepend GSM 04.08 header */ - new_hdr48 = (struct gsm48_hdr *) msgb_push(out, sizeof(*new_hdr48) + 1); - memcpy(new_hdr48, old_hdr48, sizeof(*old_hdr48)); - new_hdr48->data[0] = msgb_l2len(out); - - return out; -} - -/** - * Parse the SMS and check if it needs to be rewritten - */ -static struct msgb *rewrite_sms(struct bsc_nat *nat, struct msgb *msg, - struct bsc_nat_parsed *parsed, const char *imsi, - struct gsm48_hdr *hdr48, const uint32_t len) -{ - unsigned int payload_len; - unsigned int cp_len; - - uint8_t ref; - uint8_t orig_addr_len, *orig_addr_ptr; - uint8_t dest_addr_len, *dest_addr_ptr; - uint8_t data_len, *data_ptr; - char smsc_addr[30]; - - - uint8_t dest_len, orig_dest_len; - char _dest_nr[30]; - char *dest_nr; - char *new_dest_nr; - - char *new_number = NULL; - uint8_t tpdu_hdr; - struct msgb *out; - - payload_len = len - sizeof(*hdr48); - if (payload_len < 1) { - LOGP(DNAT, LOGL_ERROR, "SMS too short for things. %d\n", payload_len); - return NULL; - } - - cp_len = hdr48->data[0]; - if (payload_len + 1 < cp_len) { - LOGP(DNAT, LOGL_ERROR, "SMS RPDU can not fit in: %d %d\n", cp_len, payload_len); - return NULL; - } - - if (hdr48->data[1] != GSM411_MT_RP_DATA_MO) - return NULL; - - if (cp_len < 5) { - LOGP(DNAT, LOGL_ERROR, "RD-DATA can not fit in the CP len: %d\n", cp_len); - return NULL; - } - - /* RP */ - ref = hdr48->data[2]; - orig_addr_len = hdr48->data[3]; - orig_addr_ptr = &hdr48->data[4]; - - /* the +1 is for checking if the following element has some space */ - if (cp_len < 3 + orig_addr_len + 1) { - LOGP(DNAT, LOGL_ERROR, "RP-Originator addr does not fit: %d\n", orig_addr_len); - return NULL; - } - - dest_addr_len = hdr48->data[3 + orig_addr_len + 1]; - dest_addr_ptr = &hdr48->data[3 + orig_addr_len + 2]; - - if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1) { - LOGP(DNAT, LOGL_ERROR, "RP-Destination addr does not fit: %d\n", dest_addr_len); - return NULL; - } - gsm48_decode_bcd_number(smsc_addr, ARRAY_SIZE(smsc_addr), dest_addr_ptr - 1, 1); - - data_len = hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 1]; - data_ptr = &hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 2]; - - if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1 + data_len) { - LOGP(DNAT, LOGL_ERROR, "RP-Data does not fit: %d\n", data_len); - return NULL; - } - - if (data_len < 3) { - LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT is too short.\n"); - return NULL; - } - - /* TP-PDU starts here */ - if ((data_ptr[0] & 0x03) != GSM340_SMS_SUBMIT_MS2SC) - return NULL; - - /* - * look into the phone number. The length is in semi-octets, we will - * need to add the byte for the number type as well. - */ - orig_dest_len = data_ptr[2]; - dest_len = ((orig_dest_len + 1) / 2) + 1; - if (data_len < dest_len + 3 || dest_len < 2) { - LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT can not have TP-DestAddr.\n"); - return NULL; - } - - if ((data_ptr[3] & 0x80) == 0) { - LOGP(DNAT, LOGL_ERROR, "TP-DestAddr has extension. Not handled.\n"); - return NULL; - } - - if ((data_ptr[3] & 0x0F) == 0) { - LOGP(DNAT, LOGL_ERROR, "TP-DestAddr is of unknown type.\n"); - return NULL; - } - - /** - * Besides of what I think I read in GSM 03.40 and 04.11 the TP-DA - * contains the semi-octets as length (strlen), change it to the - * the number of bytes, but then change it back. - */ - data_ptr[2] = dest_len; - gsm48_decode_bcd_number(_dest_nr + 2, ARRAY_SIZE(_dest_nr) - 2, - &data_ptr[2], 1); - data_ptr[2] = orig_dest_len; - if ((data_ptr[3] & 0x70) == 0x10) { - _dest_nr[0] = _dest_nr[1] = '0'; - dest_nr = &_dest_nr[0]; - } else { - dest_nr = &_dest_nr[2]; - } - - /** - * Call functions to rewrite the data - */ - tpdu_hdr = sms_new_tpdu_hdr(nat, imsi, dest_nr, data_ptr[0]); - new_number = find_new_smsc(nat, msg, imsi, smsc_addr, dest_nr); - new_dest_nr = sms_new_dest_nr(nat, msg, imsi, dest_nr); - - if (tpdu_hdr == data_ptr[0] && !new_number && !new_dest_nr) - return NULL; - - out = sms_create_new(GSM411_MT_RP_DATA_MO, ref, hdr48, - orig_addr_ptr, orig_addr_len, - new_number ? new_number : smsc_addr, - data_ptr, data_len, tpdu_hdr, - dest_len, new_dest_nr); - talloc_free(new_number); - talloc_free(new_dest_nr); - return out; -} - -struct msgb *bsc_nat_rewrite_msg(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *parsed, const char *imsi) -{ - struct gsm48_hdr *hdr48; - uint32_t len; - uint8_t msg_type, proto; - struct msgb *new_msg = NULL, *sccp; - uint8_t link_id; - - if (!imsi || strlen(imsi) < 5) - return msg; - - /* only care about DTAP messages */ - if (parsed->bssap != BSSAP_MSG_DTAP) - return msg; - if (!parsed->dest_local_ref) - return msg; - - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return msg; - - link_id = msg->l3h[1]; - proto = hdr48->proto_discr & 0x0f; - msg_type = hdr48->msg_type & 0xbf; - - if (proto == GSM48_PDISC_CC && msg_type == GSM48_MT_CC_SETUP) - new_msg = rewrite_setup(nat, msg, parsed, imsi, hdr48, len); - else if (proto == GSM48_PDISC_SMS && msg_type == GSM411_MT_CP_DATA) - new_msg = rewrite_sms(nat, msg, parsed, imsi, hdr48, len); - - if (!new_msg) - return msg; - - /* wrap with DTAP, SCCP, then IPA. TODO: Stop copying */ - gsm0808_prepend_dtap_header(new_msg, link_id); - sccp = sccp_create_dt1(parsed->dest_local_ref, new_msg->data, new_msg->len); - talloc_free(new_msg); - - if (!sccp) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); - return msg; - } - - ipaccess_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; - } - - /* 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_utils.c b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c deleted file mode 100644 index 45c224ce8..000000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c +++ /dev/null @@ -1,566 +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 - -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, -}; - -static const struct rate_ctr_desc acc_list_ctr_description[] = { - [ACC_LIST_BSC_FILTER] = { "access-list.bsc-filter", "Rejected by rule for BSC"}, - [ACC_LIST_NAT_FILTER] = { "access-list.nat-filter", "Rejected by rule for NAT"}, -}; - -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, -}; - -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->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_set_msc_ip(struct bsc_nat *nat, const char *ip) -{ - bsc_replace_string(nat, &nat->main_dest->ip, ip); -} - -struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat) -{ - struct bsc_connection *con = talloc_zero(nat, struct bsc_connection); - if (!con) - return NULL; - - con->nat = nat; - osmo_wqueue_init(&con->write_queue, 100); - INIT_LLIST_HEAD(&con->cmd_pending); - INIT_LLIST_HEAD(&con->pending_dlcx); - return con; -} - -struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token) -{ - struct bsc_config *conf = talloc_zero(nat, struct bsc_config); - if (!conf) - return NULL; - - conf->token = talloc_strdup(conf, token); - conf->nr = nat->num_bsc; - conf->nat = nat; - conf->max_endpoints = 32; - conf->paging_group = PAGIN_GROUP_UNASSIGNED; - - INIT_LLIST_HEAD(&conf->lac_list); - - llist_add_tail(&conf->entry, &nat->bsc_configs); - ++nat->num_bsc; - - conf->stats.ctrg = rate_ctr_group_alloc(conf, &bsc_cfg_ctrg_desc, conf->nr); - if (!conf->stats.ctrg) { - talloc_free(conf); - return NULL; - } - - return conf; -} - -void bsc_config_free(struct bsc_config *cfg) -{ - rate_ctr_group_free(cfg->stats.ctrg); -} - -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 */ - ipaccess_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; -} - -int bsc_nat_lst_check_allow(struct bsc_nat_acc_lst *lst, const char *mi_string) -{ - struct bsc_nat_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 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 [] = { - [NAT_CON_TYPE_NONE] = "n/a", - [NAT_CON_TYPE_LU] = "Location Update", - [NAT_CON_TYPE_CM_SERV_REQ] = "CM Serv Req", - [NAT_CON_TYPE_PAG_RESP] = "Paging Response", - [NAT_CON_TYPE_SSA] = "Supplementar Service Activation", - [NAT_CON_TYPE_LOCAL_REJECT] = "Local Reject", - [NAT_CON_TYPE_OTHER] = "Other", -}; - -const char *bsc_con_type_to_string(int type) -{ - return con_types[type]; -} - -struct bsc_nat_acc_lst *bsc_nat_acc_lst_find(struct bsc_nat *nat, const char *name) -{ - struct bsc_nat_acc_lst *lst; - - if (!name) - return NULL; - - llist_for_each_entry(lst, &nat->access_lists, list) - if (strcmp(lst->name, name) == 0) - return lst; - - return NULL; -} - -struct bsc_nat_acc_lst *bsc_nat_acc_lst_get(struct bsc_nat *nat, const char *name) -{ - struct bsc_nat_acc_lst *lst; - - lst = bsc_nat_acc_lst_find(nat, name); - if (lst) - return lst; - - lst = talloc_zero(nat, struct bsc_nat_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, &nat->access_lists); - return lst; -} - -void bsc_nat_acc_lst_delete(struct bsc_nat_acc_lst *lst) -{ - llist_del(&lst->list); - rate_ctr_group_free(lst->stats); - talloc_free(lst); -} - -struct bsc_nat_acc_lst_entry *bsc_nat_acc_lst_entry_create(struct bsc_nat_acc_lst *lst) -{ - struct bsc_nat_acc_lst_entry *entry; - - entry = talloc_zero(lst, struct bsc_nat_acc_lst_entry); - if (!entry) - return NULL; - - llist_add_tail(&entry->list, &lst->fltr_list); - return entry; -} - -int bsc_nat_msc_is_connected(struct bsc_nat *nat) -{ - return nat->msc_con->is_connected; -} - -static const int con_to_ctr[] = { - [NAT_CON_TYPE_NONE] = -1, - [NAT_CON_TYPE_LU] = BCFG_CTR_CON_TYPE_LU, - [NAT_CON_TYPE_CM_SERV_REQ] = BCFG_CTR_CON_CMSERV_RQ, - [NAT_CON_TYPE_PAG_RESP] = BCFG_CTR_CON_PAG_RESP, - [NAT_CON_TYPE_SSA] = BCFG_CTR_CON_SSA, - [NAT_CON_TYPE_LOCAL_REJECT] = -1, - [NAT_CON_TYPE_OTHER] = BCFG_CTR_CON_OTHER, -}; - -int bsc_conn_type_to_ctr(struct nat_sccp_connection *conn) -{ - return con_to_ctr[conn->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 46dc38e14..000000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c +++ /dev/null @@ -1,1160 +0,0 @@ -/* OpenBSC NAT interface to quagga VTY */ -/* (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 - -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 write_acc_lst(struct vty *vty, struct bsc_nat_acc_lst *lst) -{ - struct bsc_nat_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%s", - lst->name, entry->imsi_deny, VTY_NEWLINE); - } -} - -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_nat_acc_lst *lst; - struct bsc_nat_paging_group *pgroup; - - vty_out(vty, "nat%s", VTY_NEWLINE); - vty_out(vty, " msc ip %s%s", _nat->main_dest->ip, VTY_NEWLINE); - vty_out(vty, " msc port %d%s", _nat->main_dest->port, VTY_NEWLINE); - vty_out(vty, " timeout auth %d%s", _nat->auth_timeout, VTY_NEWLINE); - vty_out(vty, " timeout ping %d%s", _nat->ping_timeout, VTY_NEWLINE); - vty_out(vty, " timeout pong %d%s", _nat->pong_timeout, VTY_NEWLINE); - if (_nat->token) - vty_out(vty, " token %s%s", _nat->token, VTY_NEWLINE); - vty_out(vty, " ip-dscp %d%s", _nat->bsc_ip_dscp, VTY_NEWLINE); - if (_nat->acc_lst_name) - vty_out(vty, " access-list-name %s%s", _nat->acc_lst_name, VTY_NEWLINE); - if (_nat->imsi_black_list_fn) - vty_out(vty, " imsi-black-list-file-name %s%s", - _nat->imsi_black_list_fn, VTY_NEWLINE); - if (_nat->ussd_lst_name) - vty_out(vty, " ussd-list-name %s%s", _nat->ussd_lst_name, VTY_NEWLINE); - if (_nat->ussd_query) - vty_out(vty, " ussd-query %s%s", _nat->ussd_query, VTY_NEWLINE); - if (_nat->ussd_token) - vty_out(vty, " ussd-token %s%s", _nat->ussd_token, VTY_NEWLINE); - if (_nat->ussd_local) - vty_out(vty, " ussd-local-ip %s%s", _nat->ussd_local, VTY_NEWLINE); - - if (_nat->num_rewr_name) - vty_out(vty, " number-rewrite %s%s", _nat->num_rewr_name, VTY_NEWLINE); - if (_nat->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); - - llist_for_each_entry(lst, &_nat->access_lists, list) - write_acc_lst(vty, lst); - llist_for_each_entry(pgroup, &_nat->paging_groups, entry) - write_pgroup_lst(vty, pgroup); - if (_nat->mgcp_ipa) - vty_out(vty, " mgcp-through-msc-ipa%s", 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); - 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); -} - -static int config_write_bsc(struct vty *vty) -{ - struct bsc_config *bsc; - - llist_for_each_entry(bsc, &_nat->bsc_configs, entry) - config_write_bsc_single(vty, bsc); - return CMD_SUCCESS; -} - - -DEFUN(show_sccp, show_sccp_cmd, "show sccp connections", - SHOW_STR "Display information about SCCP\n" - "All active connections\n") -{ - struct nat_sccp_connection *con; - vty_out(vty, "Listing all open SCCP connections%s", VTY_NEWLINE); - - llist_for_each_entry(con, &_nat->sccp_connections, list_entry) { - vty_out(vty, "For BSC Nr: %d BSC ref: 0x%x; MUX ref: 0x%x; Network has ref: %d ref: 0x%x MSC/BSC mux: 0x%x/0x%x type: %s%s", - con->bsc->cfg ? con->bsc->cfg->nr : -1, - sccp_src_ref_to_int(&con->real_ref), - sccp_src_ref_to_int(&con->patched_ref), - con->has_remote_ref, - sccp_src_ref_to_int(&con->remote_ref), - con->msc_endp, con->bsc_endp, - bsc_con_type_to_string(con->con_type), - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(show_bsc, show_bsc_cmd, "show bsc connections", - SHOW_STR BSC_STR - "All active connections\n") -{ - struct bsc_connection *con; - struct sockaddr_in sock; - socklen_t len = sizeof(sock); - - llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { - getpeername(con->write_queue.bfd.fd, (struct sockaddr *) &sock, &len); - vty_out(vty, "BSC nr: %d auth: %d fd: %d peername: %s pending-stats: %u%s", - con->cfg ? con->cfg->nr : -1, - con->authenticated, con->write_queue.bfd.fd, - inet_ntoa(sock.sin_addr), con->pending_dlcx_count, - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(show_bsc_mgcp, show_bsc_mgcp_cmd, "show bsc mgcp NR", - SHOW_STR BSC_STR MGCP_STR "Identifier of the BSC\n") -{ - struct bsc_connection *con; - int nr = atoi(argv[0]); - int i, j, endp; - - llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { - int max; - if (!con->cfg) - continue; - if (con->cfg->nr != nr) - continue; - - /* this bsc has no audio endpoints yet */ - if (!con->_endpoint_status) - continue; - - vty_out(vty, "MGCP Status for %d%s", con->cfg->nr, VTY_NEWLINE); - max = bsc_mgcp_nr_multiplexes(con->max_endpoints); - for (i = 0; i < max; ++i) { - for (j = 0; 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", - nat->msc_con->is_connected, 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.\n"); - return CMD_WARNING; - } - - vty_out(vty, "MSC is connected: %d%s\n", - _nat->msc_con->is_connected, 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", "Configute the NAT") -{ - vty->index = _nat; - vty->node = NAT_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_msc_ip, - cfg_nat_msc_ip_cmd, - "msc ip A.B.C.D", - "MSC related configuration\n" - "Configure the IP address\n" IP_STR) -{ - bsc_nat_set_msc_ip(_nat, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_msc_port, - cfg_nat_msc_port_cmd, - "msc port <1-65500>", - "MSC related configuration\n" - "Configure the port\n" - "Port number\n") -{ - _nat->main_dest->port = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_auth_time, - cfg_nat_auth_time_cmd, - "timeout auth <1-256>", - "Timeout configuration\n" - "Authentication timeout\n" - "Timeout in seconds\n") -{ - _nat->auth_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ping_time, - cfg_nat_ping_time_cmd, - "timeout ping NR", - "Timeout configuration\n" - "Time between two pings\n" - "Timeout in seconds\n") -{ - _nat->ping_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_pong_time, - cfg_nat_pong_time_cmd, - "timeout pong NR", - "Timeout configuration\n" - "Waiting for pong timeout\n" - "Timeout in seconds\n") -{ - _nat->pong_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_token, cfg_nat_token_cmd, - "token TOKEN", - "Authentication token configuration\n" - "Token of the BSC, currently transferred in cleartext\n") -{ - bsc_replace_string(_nat, &_nat->token, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_bsc_ip_dscp, cfg_nat_bsc_ip_dscp_cmd, - "ip-dscp <0-255>", - "Set the IP DSCP for the BSCs to use\n" "Set the IP_TOS attribute") -{ - _nat->bsc_ip_dscp = atoi(argv[0]); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_nat_bsc_ip_dscp, cfg_nat_bsc_ip_tos_cmd, - "ip-tos <0-255>", - "Use ip-dscp in the future.\n" "Set the DSCP\n") - - -DEFUN(cfg_nat_acc_lst_name, - cfg_nat_acc_lst_name_cmd, - "access-list-name NAME", - "Set the name of the access list to use.\n" - "The name of the to be used access list.") -{ - bsc_replace_string(_nat, &_nat->acc_lst_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_no_acc_lst_name, - cfg_nat_no_acc_lst_name_cmd, - "no access-list-name", - NO_STR "Remove the access list from the NAT.\n") -{ - if (_nat->acc_lst_name) { - talloc_free(_nat->acc_lst_name); - _nat->acc_lst_name = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_imsi_black_list_fn, - cfg_nat_imsi_black_list_fn_cmd, - "imsi-black-list-file-name NAME", - "IMSI black listing\n" "Filename IMSI and reject-cause\n") -{ - - bsc_replace_string(_nat, &_nat->imsi_black_list_fn, argv[0]); - if (_nat->imsi_black_list_fn) { - int rc; - struct osmo_config_list *rewr = NULL; - rewr = osmo_config_list_parse(_nat, _nat->imsi_black_list_fn); - rc = bsc_nat_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_nat_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_nat_barr_adapt(_nat, &_nat->imsi_black_list, NULL); - return CMD_SUCCESS; -} - -static int replace_rules(struct bsc_nat *nat, char **name, - struct llist_head *head, const char *file) -{ - struct osmo_config_list *rewr = NULL; - - bsc_replace_string(nat, name, file); - if (*name) { - rewr = osmo_config_list_parse(nat, *name); - bsc_nat_num_rewr_entry_adapt(nat, head, rewr); - talloc_free(rewr); - return CMD_SUCCESS; - } else { - bsc_nat_num_rewr_entry_adapt(nat, head, NULL); - return CMD_SUCCESS; - } -} - -DEFUN(cfg_nat_number_rewrite, - cfg_nat_number_rewrite_cmd, - "number-rewrite FILENAME", - "Set the file with rewriting rules.\n" "Filename") -{ - return replace_rules(_nat, &_nat->num_rewr_name, - &_nat->num_rewr, argv[0]); -} - -DEFUN(cfg_nat_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_ussd_lst_name, - cfg_nat_ussd_lst_name_cmd, - "ussd-list-name NAME", - "Set the name of the access list to check for IMSIs for USSD message\n" - "The name of the access list for HLR USSD handling") -{ - bsc_replace_string(_nat, &_nat->ussd_lst_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ussd_query, - cfg_nat_ussd_query_cmd, - "ussd-query REGEXP", - "Set the USSD query to match with the ussd-list-name\n" - "The query to match") -{ - if (gsm_parse_reg(_nat, &_nat->ussd_query_re, &_nat->ussd_query, argc, argv) != 0) - return CMD_WARNING; - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ussd_token, - cfg_nat_ussd_token_cmd, - "ussd-token TOKEN", - "Set the token used to identify the USSD module\n" "Secret key\n") -{ - bsc_replace_string(_nat, &_nat->ussd_token, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ussd_local, - cfg_nat_ussd_local_cmd, - "ussd-local-ip A.B.C.D", - "Set the IP to listen for the USSD Provider\n" "IP Address\n") -{ - bsc_replace_string(_nat, &_nat->ussd_local, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_mgcp_ipa, - cfg_nat_mgcp_ipa_cmd, - "mgcp-through-msc-ipa", - "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; -} - -/* per BSC configuration */ -DEFUN(cfg_bsc, cfg_bsc_cmd, "bsc BSC_NR", - "BSC configuration\n" "Identifier of the BSC\n") -{ - int bsc_nr = atoi(argv[0]); - struct bsc_config *bsc; - - if (bsc_nr > _nat->num_bsc) { - vty_out(vty, "%% The next unused BSC number is %u%s", - _nat->num_bsc, VTY_NEWLINE); - return CMD_WARNING; - } else if (bsc_nr == _nat->num_bsc) { - /* allocate a new one */ - bsc = bsc_config_alloc(_nat, "unknown"); - } else - bsc = bsc_config_num(_nat, bsc_nr); - - if (!bsc) - return CMD_WARNING; - - vty->index = bsc; - vty->node = NAT_BSC_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_token, cfg_bsc_token_cmd, "token TOKEN", - "Authentication token configuration\n" - "Token of the BSC, currently transferred in cleartext\n") -{ - struct bsc_config *conf = vty->index; - - bsc_replace_string(conf, &conf->token, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_lac, cfg_bsc_lac_cmd, "location_area_code <0-65535>", - "Add the Location Area Code (LAC) of this BSC\n" "LAC\n") -{ - struct bsc_config *tmp; - struct bsc_config *conf = vty->index; - - int lac = atoi(argv[0]); - - if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { - vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - /* verify that the LACs are unique */ - llist_for_each_entry(tmp, &_nat->bsc_configs, entry) { - if (bsc_config_handles_lac(tmp, lac)) { - vty_out(vty, "%% LAC %d is already used.%s", lac, VTY_NEWLINE); - return CMD_ERR_INCOMPLETE; - } - } - - bsc_config_add_lac(conf, lac); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_no_lac, cfg_bsc_no_lac_cmd, - "no location_area_code <0-65535>", - NO_STR "Remove the Location Area Code (LAC) of this BSC\n" "LAC\n") -{ - int lac = atoi(argv[0]); - struct bsc_config *conf = vty->index; - - bsc_config_del_lac(conf, lac); - return CMD_SUCCESS; -} - - - -DEFUN(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_nat_acc_lst *acc; - struct bsc_nat_acc_lst_entry *entry; - - acc = bsc_nat_acc_lst_get(_nat, argv[0]); - if (!acc) - return CMD_WARNING; - - entry = bsc_nat_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]", - "Access list commands\n" - "Name of the access list\n" - "Add denied IMSI to the list\n" - "Regexp for IMSIs\n") -{ - struct bsc_nat_acc_lst *acc; - struct bsc_nat_acc_lst_entry *entry; - - acc = bsc_nat_acc_lst_get(_nat, argv[0]); - if (!acc) - return CMD_WARNING; - - entry = bsc_nat_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; - return CMD_SUCCESS; -} - -/* naming to follow Zebra... */ -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_nat_acc_lst *acc; - acc = bsc_nat_acc_lst_find(_nat, argv[0]); - if (!acc) - return CMD_WARNING; - - bsc_nat_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_nat_acc_lst *acc; - acc = bsc_nat_acc_lst_find(_nat, 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(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_nat_barr_entry *entry; - entry = rb_entry(node, struct bsc_nat_barr_entry, node); - - vty_out(vty, " IMSI(%s) CM-Reject-Cause(%d) LU-Reject-Cause(%d)%s", - entry->imsi, entry->cm_reject_cause, entry->lu_reject_cause, - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - - -DEFUN(cfg_bsc_acc_lst_name, - cfg_bsc_acc_lst_name_cmd, - "access-list-name NAME", - "Set the name of the access list to use.\n" - "The name of the to be used access list.") -{ - struct bsc_config *conf = vty->index; - - bsc_replace_string(conf, &conf->acc_lst_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_no_acc_lst_name, - cfg_bsc_no_acc_lst_name_cmd, - "no access-list-name", - NO_STR "Do not use an access-list for the BSC.\n") -{ - struct bsc_config *conf = vty->index; - - if (conf->acc_lst_name) { - talloc_free(conf->acc_lst_name); - conf->acc_lst_name = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_max_endps, cfg_bsc_max_endps_cmd, - "max-endpoints <1-1024>", - "Highest endpoint to use (exclusively)\n" "Number of ports\n") -{ - struct bsc_config *conf = vty->index; - - conf->max_endpoints = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_paging, - cfg_bsc_paging_cmd, - "paging forbidden (0|1)", - PAGING_STR "Forbid sending PAGING REQUESTS to the BSC.\n" - "Do not forbid\n" "Forbid\n") -{ - struct bsc_config *conf = vty->index; - - if (strcmp("1", argv[0]) == 0) - conf->forbid_paging = 1; - else - conf->forbid_paging = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_desc, - cfg_bsc_desc_cmd, - "description DESC", - "Provide a description for the given BSC.\n" "Description\n") -{ - struct bsc_config *conf = vty->index; - - bsc_replace_string(conf, &conf->description, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_paging_grp, - cfg_bsc_paging_grp_cmd, - "paging group <0-1000>", - PAGING_STR "Use a paging group\n" "Paging Group to use\n") -{ - struct bsc_config *conf = vty->index; - conf->paging_group = atoi(argv[0]); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_bsc_paging_grp, cfg_bsc_old_grp_cmd, - "paging-group <0-1000>", - "Use a paging group\n" "Paging Group to use\n") - -DEFUN(cfg_bsc_no_paging_grp, - cfg_bsc_no_paging_grp_cmd, - "no paging group", - NO_STR PAGING_STR "Disable the usage of a paging group.\n") -{ - struct bsc_config *conf = vty->index; - conf->paging_group = PAGIN_GROUP_UNASSIGNED; - return CMD_SUCCESS; -} - -DEFUN(test_regex, test_regex_cmd, - "test regex PATTERN STRING", - "Test utilities\n" - "Regexp testing\n" "The regexp pattern\n" - "The string to match\n") -{ - regex_t reg; - char *str = NULL; - - memset(®, 0, sizeof(reg)); - if (gsm_parse_reg(_nat, ®, &str, 1, argv) != 0) - return CMD_WARNING; - - vty_out(vty, "String matches allow pattern: %d%s", - regexec(®, argv[1], 0, NULL, 0) == 0, VTY_NEWLINE); - - talloc_free(str); - regfree(®); - return CMD_SUCCESS; -} - -DEFUN(set_last_endp, set_last_endp_cmd, - "set bsc last-used-endpoint <0-9999999999> <0-1024>", - "Set a value\n" "Operate on a BSC\n" - "Last used endpoint for an assignment\n" "BSC configuration number\n" - "Endpoint number used\n") -{ - struct bsc_connection *con; - int nr = atoi(argv[0]); - int endp = atoi(argv[1]); - - - llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { - if (!con->cfg) - continue; - if (con->cfg->nr != nr) - continue; - - con->last_endpoint = endp; - vty_out(vty, "Updated the last endpoint for %d to %d.%s", - con->cfg->nr, con->last_endpoint, VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(block_new_conn, block_new_conn_cmd, - "nat-block (block|unblock)", - "Block the NAT for new connections\n" - "Block\n" "Unblock\n") -{ - _nat->blocked = argv[0][0] == 'b'; - vty_out(vty, "%%Going to %s the NAT.%s", - _nat->blocked ? "block" : "unblock", VTY_NEWLINE); - return CMD_SUCCESS; -} - -/* paging group */ -DEFUN(cfg_nat_pgroup, cfg_nat_pgroup_cmd, - "paging-group <0-1000>", - "Create a Paging Group\n" "Number of the Group\n") -{ - int group = atoi(argv[0]); - struct bsc_nat_paging_group *pgroup; - pgroup = bsc_nat_paging_group_num(_nat, group); - if (!pgroup) - pgroup = bsc_nat_paging_group_create(_nat, group); - if (!pgroup) { - vty_out(vty, "Failed to create the group.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - vty->index = pgroup; - vty->node = PGROUP_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_no_pgroup, cfg_nat_no_pgroup_cmd, - "no paging-group <0-1000>", - NO_STR "Delete paging-group\n" "Paging-group number\n") -{ - int group = atoi(argv[0]); - struct bsc_nat_paging_group *pgroup; - pgroup = bsc_nat_paging_group_num(_nat, group); - if (!pgroup) { - vty_out(vty, "No such paging group %d.%s", group, VTY_NEWLINE); - return CMD_WARNING; - } - - bsc_nat_paging_group_delete(pgroup); - return CMD_SUCCESS; -} - -DEFUN(cfg_pgroup_lac, cfg_pgroup_lac_cmd, - "location_area_code <0-65535>", - "Add the Location Area Code (LAC)\n" "LAC\n") -{ - struct bsc_nat_paging_group *pgroup = vty->index; - - int lac = atoi(argv[0]); - if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { - vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - bsc_nat_paging_group_add_lac(pgroup, lac); - return CMD_SUCCESS; -} - -DEFUN(cfg_pgroup_no_lac, cfg_pgroup_no_lac_cmd, - "no location_area_code <0-65535>", - NO_STR "Remove the Location Area Code (LAC)\n" "LAC\n") -{ - int lac = atoi(argv[0]); - struct bsc_nat_paging_group *pgroup = vty->index; - - bsc_nat_paging_group_del_lac(pgroup, lac); - return CMD_SUCCESS; -} - -int bsc_nat_vty_init(struct bsc_nat *nat) -{ - _nat = nat; - - /* show commands */ - install_element_ve(&show_sccp_cmd); - install_element_ve(&show_bsc_cmd); - install_element_ve(&show_bsc_cfg_cmd); - install_element_ve(&show_stats_cmd); - install_element_ve(&show_stats_lac_cmd); - install_element_ve(&close_bsc_cmd); - install_element_ve(&show_msc_cmd); - install_element_ve(&test_regex_cmd); - install_element_ve(&show_bsc_mgcp_cmd); - install_element_ve(&show_acc_lst_cmd); - install_element_ve(&show_bar_lst_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); - install_default(NAT_NODE); - install_element(NAT_NODE, &ournode_exit_cmd); - install_element(NAT_NODE, &ournode_end_cmd); - install_element(NAT_NODE, &cfg_nat_msc_ip_cmd); - install_element(NAT_NODE, &cfg_nat_msc_port_cmd); - install_element(NAT_NODE, &cfg_nat_auth_time_cmd); - install_element(NAT_NODE, &cfg_nat_ping_time_cmd); - install_element(NAT_NODE, &cfg_nat_pong_time_cmd); - install_element(NAT_NODE, &cfg_nat_token_cmd); - install_element(NAT_NODE, &cfg_nat_bsc_ip_dscp_cmd); - install_element(NAT_NODE, &cfg_nat_bsc_ip_tos_cmd); - install_element(NAT_NODE, &cfg_nat_acc_lst_name_cmd); - install_element(NAT_NODE, &cfg_nat_no_acc_lst_name_cmd); - install_element(NAT_NODE, &cfg_nat_imsi_black_list_fn_cmd); - install_element(NAT_NODE, &cfg_nat_no_imsi_black_list_fn_cmd); - install_element(NAT_NODE, &cfg_nat_ussd_lst_name_cmd); - install_element(NAT_NODE, &cfg_nat_ussd_query_cmd); - install_element(NAT_NODE, &cfg_nat_ussd_token_cmd); - install_element(NAT_NODE, &cfg_nat_ussd_local_cmd); - install_element(NAT_NODE, &cfg_nat_mgcp_ipa_cmd); - - /* access-list */ - install_element(NAT_NODE, &cfg_lst_imsi_allow_cmd); - install_element(NAT_NODE, &cfg_lst_imsi_deny_cmd); - install_element(NAT_NODE, &cfg_lst_no_cmd); - - /* number rewriting */ - install_element(NAT_NODE, &cfg_nat_number_rewrite_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_pgroup_cmd); - install_element(NAT_NODE, &cfg_nat_no_pgroup_cmd); - install_node(&pgroup_node, config_write_pgroup); - install_default(PGROUP_NODE); - install_element(PGROUP_NODE, &ournode_exit_cmd); - install_element(PGROUP_NODE, &ournode_end_cmd); - install_element(PGROUP_NODE, &cfg_pgroup_lac_cmd); - install_element(PGROUP_NODE, &cfg_pgroup_no_lac_cmd); - - /* BSC subgroups */ - install_element(NAT_NODE, &cfg_bsc_cmd); - install_node(&bsc_node, config_write_bsc); - install_default(NAT_BSC_NODE); - install_element(NAT_BSC_NODE, &ournode_exit_cmd); - install_element(NAT_BSC_NODE, &ournode_end_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_token_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); - - mgcp_vty_init(); - - return 0; -} - - -/* called by the telnet interface... we have our own init above */ -int bsc_vty_init(const struct log_info *cat) -{ - logging_vty_add_cmds(cat); - return 0; -} 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 25c13d4f4..000000000 --- a/openbsc/src/osmo-bsc_nat/bsc_sccp.c +++ /dev/null @@ -1,249 +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 (memcmp(ref, &conn->patched_ref, sizeof(*ref)) == 0) - return -1; - } - - return 0; -} - -/* copied from sccp.c */ -static int assign_src_local_reference(struct sccp_source_reference *ref, struct bsc_nat *nat) -{ - static uint32_t last_ref = 0x50000; - int wrapped = 0; - - do { - struct sccp_source_reference reference; - reference.octet1 = (last_ref >> 0) & 0xff; - reference.octet2 = (last_ref >> 8) & 0xff; - reference.octet3 = (last_ref >> 16) & 0xff; - - ++last_ref; - /* do not use the reversed word and wrap around */ - if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) { - LOGP(DNAT, LOGL_NOTICE, "Wrapped searching for a free code\n"); - last_ref = 0; - ++wrapped; - } - - if (sccp_ref_is_free(&reference, nat) == 0) { - *ref = reference; - return 0; - } - } while (wrapped != 2); - - LOGP(DNAT, LOGL_ERROR, "Finding a free reference failed\n"); - return -1; -} - -struct nat_sccp_connection *create_sccp_src_ref(struct bsc_connection *bsc, - struct bsc_nat_parsed *parsed) -{ - struct nat_sccp_connection *conn; - - /* Some commercial BSCs like to reassign there SRC ref */ - llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { - if (conn->bsc != bsc) - continue; - if (memcmp(&conn->real_ref, parsed->src_local_ref, sizeof(conn->real_ref)) != 0) - continue; - - /* the BSC has reassigned the SRC ref and we failed to keep track */ - memset(&conn->remote_ref, 0, sizeof(conn->remote_ref)); - if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) { - LOGP(DNAT, LOGL_ERROR, "BSC %d reused src ref: %d and we failed to generate a new id.\n", - bsc->cfg->nr, sccp_src_ref_to_int(parsed->src_local_ref)); - bsc_mgcp_dlcx(conn); - llist_del(&conn->list_entry); - talloc_free(conn); - return NULL; - } else { - clock_gettime(CLOCK_MONOTONIC, &conn->creation_time); - bsc_mgcp_dlcx(conn); - return conn; - } - } - - - conn = talloc_zero(bsc->nat, struct nat_sccp_connection); - if (!conn) { - LOGP(DNAT, LOGL_ERROR, "Memory allocation failure.\n"); - return NULL; - } - - conn->bsc = bsc; - clock_gettime(CLOCK_MONOTONIC, &conn->creation_time); - conn->real_ref = *parsed->src_local_ref; - if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to assign a ref.\n"); - talloc_free(conn); - return NULL; - } - - bsc_mgcp_init(conn); - llist_add_tail(&conn->list_entry, &bsc->nat->sccp_connections); - rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_SCCP_CONN]); - osmo_counter_inc(bsc->cfg->nat->stats.sccp.conn); - - LOGP(DNAT, LOGL_DEBUG, "Created 0x%x <-> 0x%x mapping for con %p\n", - sccp_src_ref_to_int(&conn->real_ref), - sccp_src_ref_to_int(&conn->patched_ref), bsc); - - return conn; -} - -int update_sccp_src_ref(struct nat_sccp_connection *sccp, struct bsc_nat_parsed *parsed) -{ - if (!parsed->dest_local_ref || !parsed->src_local_ref) { - LOGP(DNAT, LOGL_ERROR, "CC MSG should contain both local and dest address.\n"); - return -1; - } - - sccp->remote_ref = *parsed->src_local_ref; - sccp->has_remote_ref = 1; - LOGP(DNAT, LOGL_DEBUG, "Updating 0x%x to remote 0x%x on %p\n", - sccp_src_ref_to_int(&sccp->patched_ref), - sccp_src_ref_to_int(&sccp->remote_ref), sccp->bsc); - - return 0; -} - -void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed) -{ - struct nat_sccp_connection *conn; - - llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { - if (memcmp(parsed->src_local_ref, - &conn->patched_ref, sizeof(conn->patched_ref)) == 0) { - - sccp_connection_destroy(conn); - return; - } - } - - LOGP(DNAT, LOGL_ERROR, "Can not remove connection: 0x%x\n", - sccp_src_ref_to_int(parsed->src_local_ref)); -} - -/* - * We have a message from the MSC to the BSC. The MSC is using - * an address that was assigned by the MUX, we need to update the - * dest reference to the real network. - */ -struct nat_sccp_connection *patch_sccp_src_ref_to_bsc(struct msgb *msg, - struct bsc_nat_parsed *parsed, - struct bsc_nat *nat) -{ - struct nat_sccp_connection *conn; - - if (!parsed->dest_local_ref) { - LOGP(DNAT, LOGL_ERROR, "MSG should contain dest_local_ref.\n"); - return NULL; - } - - - llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { - if (!equal(parsed->dest_local_ref, &conn->patched_ref)) - continue; - - /* Change the dest address to the real one */ - *parsed->dest_local_ref = conn->real_ref; - return conn; - } - - return NULL; -} - -/* - * These are message to the MSC. We will need to find the BSC - * Connection by either the SRC or the DST local reference. - * - * In case of a CR we need to work by the SRC local reference - * in all other cases we need to work by the destination local - * reference.. - */ -struct nat_sccp_connection *patch_sccp_src_ref_to_msc(struct msgb *msg, - struct bsc_nat_parsed *parsed, - struct bsc_connection *bsc) -{ - struct nat_sccp_connection *conn; - - llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { - if (conn->bsc != bsc) - continue; - - if (parsed->src_local_ref) { - if (equal(parsed->src_local_ref, &conn->real_ref)) { - *parsed->src_local_ref = conn->patched_ref; - return conn; - } - } else if (parsed->dest_local_ref) { - if (equal(parsed->dest_local_ref, &conn->remote_ref)) - return conn; - } else { - LOGP(DNAT, LOGL_ERROR, "Header has neither loc/dst ref.\n"); - return NULL; - } - } - - return NULL; -} - -struct nat_sccp_connection *bsc_nat_find_con_by_bsc(struct bsc_nat *nat, - struct sccp_source_reference *ref) -{ - struct nat_sccp_connection *conn; - - llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { - if (memcmp(ref, &conn->real_ref, sizeof(*ref)) == 0) - return conn; - } - - return NULL; -} 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 e0c5c6dda..000000000 --- a/openbsc/src/osmo-bsc_nat/bsc_ussd.c +++ /dev/null @@ -1,427 +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 - -#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_close_ussd_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); - talloc_free(con); -} - -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; - struct ipaccess_head *hh; - int ret; - - ret = ipa_msg_recv(bfd->fd, &msg); - if (ret <= 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 = ipaccess_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"); - return ret; - } - if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME)) - ussd_auth_con(&tvp, conn); - } - - msgb_free(msg); - } else if (hh->proto == IPAC_PROTO_SCCP) { - forward_sccp(conn->nat, msg); - } else { - msgb_free(msg); - } - - return 0; -} - -static void ussd_auth_cb(void *_data) -{ - LOGP(DNAT, LOGL_ERROR, "USSD module didn't authenticate\n"); - bsc_nat_ussd_destroy((struct bsc_nat_ussd_con *) _data); -} - -static void ussd_auth_con(struct tlv_parsed *tvp, struct bsc_nat_ussd_con *conn) -{ - const char *token; - int len; - if (!conn->nat->ussd_token) { - LOGP(DNAT, LOGL_ERROR, "No USSD token set. Closing\n"); - bsc_nat_ussd_destroy(conn); - return; - } - - token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME); - len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME); - - /* last byte should be a NULL */ - if (strlen(conn->nat->ussd_token) != len - 1) - goto disconnect; - /* compare everything including the null byte */ - if (memcmp(conn->nat->ussd_token, token, len) != 0) - goto disconnect; - - /* it is authenticated now */ - if (conn->nat->ussd_con && conn->nat->ussd_con != conn) - bsc_nat_ussd_destroy(conn->nat->ussd_con); - - LOGP(DNAT, LOGL_ERROR, "USSD token specified. USSD provider is connected.\n"); - osmo_timer_del(&conn->auth_timeout); - conn->authorized = 1; - conn->nat->ussd_con = conn; - return; - -disconnect: - LOGP(DNAT, LOGL_ERROR, "Wrong USSD token by client: %d\n", - conn->queue.bfd.fd); - bsc_nat_ussd_destroy(conn); -} - -static void ussd_start_auth(struct bsc_nat_ussd_con *conn) -{ - struct msgb *msg; - - conn->auth_timeout.data = conn; - conn->auth_timeout.cb = ussd_auth_cb; - osmo_timer_schedule(&conn->auth_timeout, conn->nat->auth_timeout, 0); - - msg = msgb_alloc_headroom(4096, 128, "auth message"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate auth msg\n"); - return; - } - - msgb_v_put(msg, IPAC_MSGT_ID_GET); - bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS); -} - -static int ussd_listen_cb(struct osmo_fd *bfd, unsigned int what) -{ - struct bsc_nat_ussd_con *conn; - struct bsc_nat *nat; - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - int fd; - - if (!(what & BSC_FD_READ)) - return 0; - - fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len); - if (fd < 0) { - perror("accept"); - return fd; - } - - nat = (struct bsc_nat *) bfd->data; - osmo_counter_inc(nat->stats.ussd.reconn); - - conn = bsc_nat_ussd_alloc(nat); - if (!conn) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate USSD con struct.\n"); - close(fd); - return -1; - } - - osmo_wqueue_init(&conn->queue, 10); - conn->queue.bfd.data = conn; - conn->queue.bfd.fd = fd; - conn->queue.bfd.when = BSC_FD_READ; - conn->queue.read_cb = ussd_read_cb; - conn->queue.write_cb = bsc_write_cb; - - if (osmo_fd_register(&conn->queue.bfd) < 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to register USSD fd.\n"); - bsc_nat_ussd_destroy(conn); - return -1; - } - - LOGP(DNAT, LOGL_NOTICE, "USSD Connection on %d with IP: %s\n", - fd, inet_ntoa(sa.sin_addr)); - - /* do authentication */ - ussd_start_auth(conn); - return 0; -} - -int bsc_ussd_init(struct bsc_nat *nat) -{ - struct in_addr addr; - - addr.s_addr = INADDR_ANY; - if (nat->ussd_local) - inet_aton(nat->ussd_local, &addr); - - nat->ussd_listen.data = nat; - return make_sock(&nat->ussd_listen, IPPROTO_TCP, - ntohl(addr.s_addr), 5001, 0, ussd_listen_cb, nat); -} - -static int forward_ussd_simple(struct nat_sccp_connection *con, struct msgb *input) -{ - struct msgb *copy; - struct bsc_nat_ussd_con *ussd; - - if (!con->bsc->nat->ussd_con) - return -1; - - copy = msgb_alloc_headroom(4096, 128, "forward bts"); - if (!copy) { - LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); - return -1; - } - - /* copy the data into the copy */ - copy->l2h = msgb_put(copy, msgb_l2len(input)); - memcpy(copy->l2h, input->l2h, msgb_l2len(input)); - - /* send it out */ - ussd = con->bsc->nat->ussd_con; - bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP); - return 0; -} - -static int forward_ussd(struct nat_sccp_connection *con, const struct ussd_request *req, - struct msgb *input) -{ - struct msgb *msg, *copy; - struct ipac_msgt_sccp_state *state; - struct bsc_nat_ussd_con *ussd; - uint16_t lac, ci; - - if (!con->bsc->nat->ussd_con) - return -1; - - msg = msgb_alloc_headroom(4096, 128, "forward ussd"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); - return -1; - } - - copy = msgb_alloc_headroom(4096, 128, "forward bts"); - if (!copy) { - LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); - msgb_free(msg); - return -1; - } - - copy->l2h = msgb_put(copy, msgb_l2len(input)); - memcpy(copy->l2h, input->l2h, msgb_l2len(input)); - - msg->l2h = msgb_put(msg, 1); - msg->l2h[0] = IPAC_MSGT_SCCP_OLD; - - /* fill out the data */ - state = (struct ipac_msgt_sccp_state *) msgb_put(msg, sizeof(*state)); - state->trans_id = req->transaction_id; - state->invoke_id = req->invoke_id; - memcpy(&state->src_ref, &con->remote_ref, sizeof(con->remote_ref)); - memcpy(&state->dst_ref, &con->real_ref, sizeof(con->real_ref)); - memcpy(state->imsi, con->imsi, strlen(con->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_check_ussd(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_nat_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->con_type != NAT_CON_TYPE_SSA) - return 0; - - if (!con->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->imsi) > GSM_IMSI_LENGTH) - return 0; - - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return 0; - - proto = hdr48->proto_discr & 0x0f; - msg_type = hdr48->msg_type & 0xbf; - ti = (hdr48->proto_discr & 0x70) >> 4; - if (proto != GSM48_PDISC_NC_SS) - return 0; - - if (msg_type == GSM0480_MTYPE_REGISTER) { - - /* now check if it is a IMSI we care about */ - lst = bsc_nat_acc_lst_find(con->bsc->nat, - con->bsc->nat->ussd_lst_name); - if (!lst) - return 0; - - if (bsc_nat_lst_check_allow(lst, con->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->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->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 ce2f25d27..000000000 --- a/openbsc/src/osmo-nitb/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(COVERAGE_CFLAGS) \ - $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) $(LIBSMPP34_CFLAGS) - -AM_LDFLAGS = $(COVERAGE_LDFLAGS) - -bin_PROGRAMS = osmo-nitb - -osmo_nitb_SOURCES = bsc_hack.c -osmo_nitb_LDADD = \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libmsc/libmsc.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libctrl/libctrl.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - -ldbi -ldl $(LIBCRYPT) \ - $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOABIS_LIBS) $(LIBSMPP34_LIBS) diff --git a/openbsc/src/osmo-nitb/bsc_hack.c b/openbsc/src/osmo-nitb/bsc_hack.c deleted file mode 100644 index 172fb8269..000000000 --- a/openbsc/src/osmo-nitb/bsc_hack.c +++ /dev/null @@ -1,348 +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 "../../bscconfig.h" - -/* MCC and MNC for the Location Area Identifier */ -struct gsm_network *bsc_gsmnet = 0; -static struct osmo_bsc_rf *rf_ctrl; -static const char *database_name = "hlr.sqlite3"; -static const char *config_file = "openbsc.cfg"; -static const char *rf_ctrl_name = NULL; -extern const char *openbsc_copyright; -static int daemonize = 0; -static int use_mncc_sock = 0; -static int use_db_counter = 1; - -/* timer to store statistics */ -#define DB_SYNC_INTERVAL 60, 0 -#define EXPIRE_INTERVAL 10, 0 - -static struct osmo_timer_list db_sync_timer; - -static void create_pcap_file(char *file) -{ - mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - int fd = open(file, O_WRONLY|O_TRUNC|O_CREAT, mode); - - if (fd < 0) { - perror("Failed to open file for pcap"); - return; - } - - e1_set_pcap_fd(fd); -} - -static void print_usage() -{ - printf("Usage: bsc_hack\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(" -p --pcap file The filename of the pcap file\n"); - printf(" -T --timestamp Prefix every log line with a timestamp\n"); - printf(" -V --version. Print the version of OpenBSC.\n"); - printf(" -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC\n"); - printf(" -e --log-level number. Set a global loglevel.\n"); - printf(" -m --mncc-sock Disable built-in MNCC handler and offer socket\n"); - printf(" -C --no-dbcounter Disable regular syncing of counters to database\n"); - printf(" -r --rf-ctl NAME. A unix domain socket to listen for cmds.\n"); -} - -static void handle_options(int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"debug", 1, 0, 'd'}, - {"daemonize", 0, 0, 'D'}, - {"config-file", 1, 0, 'c'}, - {"disable-color", 0, 0, 's'}, - {"database", 1, 0, 'l'}, - {"authorize-everyone", 0, 0, 'a'}, - {"pcap", 1, 0, 'p'}, - {"timestamp", 0, 0, 'T'}, - {"version", 0, 0, 'V' }, - {"rtp-proxy", 0, 0, 'P'}, - {"log-level", 1, 0, 'e'}, - {"mncc-sock", 0, 0, 'm'}, - {"no-dbcounter", 0, 0, 'C'}, - {"rf-ctl", 1, 0, 'r'}, - {0, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hd:Dsl:ar:p:TPVc:e:mCr:", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(); - print_help(); - exit(0); - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - daemonize = 1; - break; - case 'l': - database_name = strdup(optarg); - break; - case 'c': - config_file = strdup(optarg); - break; - case 'p': - create_pcap_file(optarg); - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'P': - ipacc_rtp_direct = 0; - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - case 'm': - use_mncc_sock = 1; - break; - case 'C': - use_db_counter = 0; - break; - case 'V': - print_version(1); - exit(0); - break; - case 'r': - rf_ctrl_name = optarg; - break; - default: - /* ignore */ - break; - } - } -} - -extern void *tall_vty_ctx; -static void signal_handler(int signal) -{ - fprintf(stdout, "signal %u received\n", signal); - - switch (signal) { - case SIGINT: - bsc_shutdown_net(bsc_gsmnet); - osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); - sleep(3); - exit(0); - break; - case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report(tall_vty_ctx, stderr); - talloc_report_full(tall_bsc_ctx, stderr); - break; - case SIGUSR2: - talloc_report_full(tall_vty_ctx, stderr); - break; - default: - break; - } -} - -/* timer handling */ -static int _db_store_counter(struct osmo_counter *counter, void *data) -{ - return db_store_counter(counter); -} - -static void db_sync_timer_cb(void *data) -{ - /* store counters to database and re-schedule */ - osmo_counters_for_each(_db_store_counter, NULL); - osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); -} - -static void subscr_expire_cb(void *data) -{ - subscr_expire(bsc_gsmnet); - osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL); -} - -void talloc_ctx_init(void); - -extern enum node_type bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OpenBSC", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - -int main(int argc, char **argv) -{ - int rc; - - vty_info.copyright = openbsc_copyright; - - tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); - talloc_ctx_init(); - on_dso_load_token(); - on_dso_load_rrlp(); - on_dso_load_ho_dec(); - - libosmo_abis_init(tall_bsc_ctx); - osmo_init_logging(&log_info); - bts_init(); - - /* This needs to precede handle_options() */ - vty_init(&vty_info); - bsc_vty_init(&log_info); - -#ifdef BUILD_SMPP - if (smpp_openbsc_init(tall_bsc_ctx, 0) < 0) - return -1; -#endif - - /* parse options */ - handle_options(argc, argv); - - /* internal MNCC handler or MNCC socket? */ - if (use_mncc_sock) { - rc = bsc_bootstrap_network(mncc_sock_from_cc, config_file); - if (rc >= 0) - mncc_sock_init(bsc_gsmnet); - } else - rc = bsc_bootstrap_network(int_mncc_recv, config_file); - if (rc < 0) - exit(1); -#ifdef BUILD_SMPP - smpp_openbsc_set_net(bsc_gsmnet); -#endif - bsc_api_init(bsc_gsmnet, msc_bsc_api()); - - bsc_gsmnet->ctrl = controlif_setup(bsc_gsmnet, 4249); - if (!bsc_gsmnet->ctrl) { - printf("Failed to initialize control interface. Exiting.\n"); - return -1; - } - - /* seed the PRNG */ - srand(time(NULL)); - - if (rf_ctrl_name) { - rf_ctrl = osmo_bsc_rf_create(rf_ctrl_name, bsc_gsmnet); - if (!rf_ctrl) { - fprintf(stderr, "Failed to create the RF service.\n"); - exit(1); - } - } - - if (db_init(database_name)) { - printf("DB: Failed to init database. Please check the option settings.\n"); - return -1; - } - printf("DB: Database initialized.\n"); - - if (db_prepare()) { - printf("DB: Failed to prepare database.\n"); - return -1; - } - printf("DB: Database prepared.\n"); - - /* setup the timer */ - db_sync_timer.cb = db_sync_timer_cb; - db_sync_timer.data = NULL; - if (use_db_counter) - osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); - - bsc_gsmnet->subscr_expire_timer.cb = subscr_expire_cb; - bsc_gsmnet->subscr_expire_timer.data = NULL; - osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL); - - signal(SIGINT, &signal_handler); - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - signal(SIGUSR2, &signal_handler); - osmo_init_ignore_signals(); - - /* start the SMS queue */ - if (sms_queue_start(bsc_gsmnet, 20) != 0) - return -1; - - if (daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - perror("Error during daemonize"); - exit(1); - } - } - - while (1) { - log_reset_context(); - osmo_select_main(0); - } -} diff --git a/openbsc/src/utils/Makefile.am b/openbsc/src/utils/Makefile.am deleted file mode 100644 index fd1f7aebf..000000000 --- a/openbsc/src/utils/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(COVERAGE_LDFLAGS) - -bin_PROGRAMS = bs11_config isdnsync - -if BUILD_SMPP -noinst_PROGRAMS = smpp_mirror -endif - -bs11_config_SOURCES = bs11_config.c -bs11_config_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOABIS_LIBS) - -isdnsync_SOURCES = isdnsync.c - -smpp_mirror_SOURCES = smpp_mirror.c -smpp_mirror_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) $(LIBSMPP34_LIBS) diff --git a/openbsc/src/utils/bs11_config.c b/openbsc/src/utils/bs11_config.c deleted file mode 100644 index e8acb461a..000000000 --- a/openbsc/src/utils/bs11_config.c +++ /dev/null @@ -1,949 +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 - -static const uint8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 }; - -/* dummy function to keep gsm_data.c happy */ -struct osmo_counter *osmo_counter_alloc(const char *name) -{ - return NULL; -} - -int handle_serial_msg(struct msgb *rx_msg); - -/* create all objects for an initial configuration */ -static int create_objects(struct gsm_bts *bts) -{ - fprintf(stdout, "Crating Objects for minimal config\n"); - abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr), - obj_li_attr); - abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL); - abis_nm_bs11_create_object(bts, BS11_OBJ_ALCO, 0, 0, NULL); - abis_nm_bs11_create_object(bts, BS11_OBJ_CCLK, 0, 0, NULL); - abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 0, - sizeof(obj_bbsig0_attr), obj_bbsig0_attr); - abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 0, - sizeof(obj_pa0_attr), obj_pa0_attr); - abis_nm_bs11_create_envaBTSE(bts, 0); - abis_nm_bs11_create_envaBTSE(bts, 1); - abis_nm_bs11_create_envaBTSE(bts, 2); - abis_nm_bs11_create_envaBTSE(bts, 3); - - abis_nm_bs11_conn_oml_tei(bts, 0, 1, 0xff, TEI_OML); - - abis_nm_bs11_set_trx_power(bts->c0, BS11_TRX_POWER_GSM_30mW); - - sleep(1); - - abis_nm_bs11_set_trx1_pw(bts, trx1_password); - - sleep(1); - - return 0; -} - -static int create_trx1(struct gsm_bts *bts) -{ - uint8_t bbsig1_attr[sizeof(obj_bbsig0_attr)+12]; - uint8_t *cur = bbsig1_attr; - struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, 1); - - if (!trx) - trx = gsm_bts_trx_alloc(bts); - - fprintf(stdout, "Crating Objects for TRX1\n"); - - abis_nm_bs11_set_trx1_pw(bts, trx1_password); - - sleep(1); - - cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10, - (uint8_t *)trx1_password); - memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr)); - abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 1, - sizeof(bbsig1_attr), bbsig1_attr); - abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 1, - sizeof(obj_pa0_attr), obj_pa0_attr); - abis_nm_bs11_set_trx_power(trx, BS11_TRX_POWER_GSM_30mW); - - return 0; -} - -static char *serial_port = "/dev/ttyUSB0"; -static char *fname_safety = "BTSBMC76.SWI"; -static char *fname_software = "HS011106.SWL"; -static int delay_ms = 0; -static int win_size = 8; -static int param_disconnect = 0; -static int param_restart = 0; -static int param_forced = 0; -static struct gsm_bts *g_bts; - -static int file_is_readable(const char *fname) -{ - int rc; - struct stat st; - - rc = stat(fname, &st); - if (rc < 0) - return 0; - - if (S_ISREG(st.st_mode) && (st.st_mode & S_IRUSR)) - return 1; - - return 0; -} - -static int percent; -static int percent_old; - -/* callback function passed to the ABIS OML code */ -static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg, - void *data, void *param) -{ - if (hook != GSM_HOOK_NM_SWLOAD) - return 0; - - switch (event) { - case NM_MT_LOAD_INIT_ACK: - fprintf(stdout, "Software Load Initiate ACK\n"); - break; - case NM_MT_LOAD_INIT_NACK: - fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); - exit(5); - break; - case NM_MT_LOAD_END_ACK: - if (data) { - /* we did a safety load and must activate it */ - abis_nm_software_activate(g_bts, fname_safety, - swload_cbfn, g_bts); - sleep(5); - } - break; - case NM_MT_LOAD_END_NACK: - fprintf(stderr, "ERROR: Software Load End NACK\n"); - exit(3); - break; - case NM_MT_ACTIVATE_SW_NACK: - fprintf(stderr, "ERROR: Activate Software NACK\n"); - exit(4); - break; - case NM_MT_ACTIVATE_SW_ACK: - bs11cfg_state = STATE_NONE; - - break; - case NM_MT_LOAD_SEG_ACK: - percent = abis_nm_software_load_status(g_bts); - if (percent > percent_old) - printf("Software Download Progress: %d%%\n", percent); - percent_old = percent; - break; - } - return 0; -} - -static const struct value_string bs11_linkst_names[] = { - { 0, "Down" }, - { 1, "Up" }, - { 2, "Restoring" }, - { 0, NULL } -}; - -static const char *linkstate_name(uint8_t linkstate) -{ - return get_value_string(bs11_linkst_names, linkstate); -} - -static const struct value_string mbccu_load_names[] = { - { 0, "No Load" }, - { 1, "Load BTSCAC" }, - { 2, "Load BTSDRX" }, - { 3, "Load BTSBBX" }, - { 4, "Load BTSARC" }, - { 5, "Load" }, - { 0, NULL } -}; - -static const char *mbccu_load_name(uint8_t linkstate) -{ - return get_value_string(mbccu_load_names, linkstate); -} - -static const char *bts_phase_name(uint8_t phase) -{ - switch (phase) { - case BS11_STATE_WARM_UP: - case BS11_STATE_WARM_UP_2: - return "Warm Up"; - break; - case BS11_STATE_LOAD_SMU_SAFETY: - return "Load SMU Safety"; - break; - case BS11_STATE_LOAD_SMU_INTENDED: - return "Load SMU Intended"; - break; - case BS11_STATE_LOAD_MBCCU: - return "Load MBCCU"; - break; - case BS11_STATE_SOFTWARE_RQD: - return "Software required"; - break; - case BS11_STATE_WAIT_MIN_CFG: - case BS11_STATE_WAIT_MIN_CFG_2: - return "Wait minimal config"; - break; - case BS11_STATE_MAINTENANCE: - return "Maintenance"; - break; - case BS11_STATE_NORMAL: - return "Normal"; - break; - case BS11_STATE_ABIS_LOAD: - return "Abis load"; - break; - default: - return "Unknown"; - break; - } -} - -static const char *trx_power_name(uint8_t pwr) -{ - switch (pwr) { - case BS11_TRX_POWER_GSM_2W: - return "2W (GSM)"; - case BS11_TRX_POWER_GSM_250mW: - return "250mW (GSM)"; - case BS11_TRX_POWER_GSM_80mW: - return "80mW (GSM)"; - case BS11_TRX_POWER_GSM_30mW: - return "30mW (GSM)"; - case BS11_TRX_POWER_DCS_3W: - return "3W (DCS)"; - case BS11_TRX_POWER_DCS_1W6: - return "1.6W (DCS)"; - case BS11_TRX_POWER_DCS_500mW: - return "500mW (DCS)"; - case BS11_TRX_POWER_DCS_160mW: - return "160mW (DCS)"; - default: - return "unknown value"; - } -} - -static const char *pll_mode_name(uint8_t mode) -{ - switch (mode) { - case BS11_LI_PLL_LOCKED: - return "E1 Locked"; - case BS11_LI_PLL_STANDALONE: - return "Standalone"; - default: - return "unknown"; - } -} - -static const char *cclk_acc_name(uint8_t acc) -{ - switch (acc) { - case 0: - /* Out of the demanded +/- 0.05ppm */ - return "Medium"; - case 1: - /* Synchronized with Abis, within demanded tolerance +/- 0.05ppm */ - return "High"; - default: - return "unknown"; - } -} - -static const char *bport_lcfg_name(uint8_t lcfg) -{ - switch (lcfg) { - case BS11_LINE_CFG_STAR: - return "Star"; - case BS11_LINE_CFG_MULTIDROP: - return "Multi-Drop"; - default: - return "unknown"; - } -} - -static const char *obj_name(struct abis_om_fom_hdr *foh) -{ - static char retbuf[256]; - - retbuf[0] = 0; - - switch (foh->obj_class) { - case NM_OC_BS11: - strcat(retbuf, "BS11 "); - switch (foh->obj_inst.bts_nr) { - case BS11_OBJ_PA: - sprintf(retbuf+strlen(retbuf), "Power Amplifier %d ", - foh->obj_inst.ts_nr); - break; - case BS11_OBJ_LI: - sprintf(retbuf+strlen(retbuf), "Line Interface "); - break; - case BS11_OBJ_CCLK: - sprintf(retbuf+strlen(retbuf), "CCLK "); - break; - } - break; - case NM_OC_SITE_MANAGER: - strcat(retbuf, "SITE MANAGER "); - break; - case NM_OC_BS11_BPORT: - sprintf(retbuf+strlen(retbuf), "BPORT%u ", - foh->obj_inst.bts_nr); - break; - } - return retbuf; -} - -static void print_state(struct tlv_parsed *tp) -{ - if (TLVP_PRESENT(tp, NM_ATT_BS11_BTS_STATE)) { - uint8_t phase, mbccu; - if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 1) { - phase = *TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE); - printf("PHASE: %u %-20s ", phase & 0xf, - bts_phase_name(phase)); - } - if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 2) { - mbccu = *(TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE)+1); - printf("MBCCU0: %-11s MBCCU1: %-11s ", - mbccu_load_name(mbccu & 0xf), mbccu_load_name(mbccu >> 4)); - } - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_E1_STATE) && - TLVP_LEN(tp, NM_ATT_BS11_E1_STATE) >= 1) { - uint8_t e1_state = *TLVP_VAL(tp, NM_ATT_BS11_E1_STATE); - printf("Abis-link: %-9s ", linkstate_name(e1_state & 0xf)); - } - printf("\n"); -} - -static int print_attr(struct tlv_parsed *tp) -{ - if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_PCB_SERIAL)) { - printf("\tBS-11 ESN PCB Serial Number: %s\n", - TLVP_VAL(tp, NM_ATT_BS11_ESN_PCB_SERIAL)); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_HW_CODE_NO)) { - printf("\tBS-11 ESN Hardware Code Number: %s\n", - TLVP_VAL(tp, NM_ATT_BS11_ESN_HW_CODE_NO)+6); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_FW_CODE_NO)) { - printf("\tBS-11 ESN Firmware Code Number: %s\n", - TLVP_VAL(tp, NM_ATT_BS11_ESN_FW_CODE_NO)+6); - } -#if 0 - if (TLVP_PRESENT(tp, NM_ATT_BS11_BOOT_SW_VERS)) { - printf("BS-11 Boot Software Version: %s\n", - TLVP_VAL(tp, NM_ATT_BS11_BOOT_SW_VERS)+6); - } -#endif - if (TLVP_PRESENT(tp, NM_ATT_ABIS_CHANNEL) && - TLVP_LEN(tp, NM_ATT_ABIS_CHANNEL) >= 3) { - const uint8_t *chan = TLVP_VAL(tp, NM_ATT_ABIS_CHANNEL); - printf("\tE1 Channel: Port=%u Timeslot=%u ", - chan[0], chan[1]); - if (chan[2] == 0xff) - printf("(Full Slot)\n"); - else - printf("Subslot=%u\n", chan[2]); - } - if (TLVP_PRESENT(tp, NM_ATT_TEI)) - printf("\tTEI: %d\n", *TLVP_VAL(tp, NM_ATT_TEI)); - if (TLVP_PRESENT(tp, NM_ATT_BS11_TXPWR) && - TLVP_LEN(tp, NM_ATT_BS11_TXPWR) >= 1) { - printf("\tTRX Power: %s\n", - trx_power_name(*TLVP_VAL(tp, NM_ATT_BS11_TXPWR))); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL_MODE) && - TLVP_LEN(tp, NM_ATT_BS11_PLL_MODE) >= 1) { - printf("\tPLL Mode: %s\n", - pll_mode_name(*TLVP_VAL(tp, NM_ATT_BS11_PLL_MODE))); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL) && - TLVP_LEN(tp, NM_ATT_BS11_PLL) >= 4) { - const uint8_t *vp = TLVP_VAL(tp, NM_ATT_BS11_PLL); - printf("\tPLL Set Value=%d, Work Value=%d\n", - vp[0] << 8 | vp[1], vp[2] << 8 | vp[3]); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_ACCURACY) && - TLVP_LEN(tp, NM_ATT_BS11_CCLK_ACCURACY) >= 1) { - const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_ACCURACY); - printf("\tCCLK Accuracy: %s (%d)\n", cclk_acc_name(*acc), *acc); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_TYPE) && - TLVP_LEN(tp, NM_ATT_BS11_CCLK_TYPE) >= 1) { - const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_TYPE); - printf("\tCCLK Type=%d\n", *acc); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_LINE_CFG) && - TLVP_LEN(tp, NM_ATT_BS11_LINE_CFG) >= 1) { - const uint8_t *lcfg = TLVP_VAL(tp, NM_ATT_BS11_LINE_CFG); - printf("\tLine Configuration: %s (%d)\n", - bport_lcfg_name(*lcfg), *lcfg); - } - - - - return 0; -} - -static void cmd_query(void) -{ - struct gsm_bts_trx *trx = g_bts->c0; - - bs11cfg_state = STATE_QUERY; - abis_nm_bs11_get_serno(g_bts); - abis_nm_bs11_get_oml_tei_ts(g_bts); - abis_nm_bs11_get_pll_mode(g_bts); - abis_nm_bs11_get_cclk(g_bts); - abis_nm_bs11_get_trx_power(trx); - trx = gsm_bts_trx_num(g_bts, 1); - if (trx) - abis_nm_bs11_get_trx_power(trx); - abis_nm_bs11_get_bport_line_cfg(g_bts, 0); - abis_nm_bs11_get_bport_line_cfg(g_bts, 1); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; -} - -/* handle a response from the BTS to a GET STATE command */ -static int handle_state_resp(enum abis_bs11_phase state) -{ - int rc = 0; - - switch (state) { - case BS11_STATE_WARM_UP: - case BS11_STATE_LOAD_SMU_SAFETY: - case BS11_STATE_LOAD_SMU_INTENDED: - case BS11_STATE_LOAD_MBCCU: - break; - case BS11_STATE_SOFTWARE_RQD: - bs11cfg_state = STATE_SWLOAD; - /* send safety load. Use g_bts as private 'param' - * argument, so our swload_cbfn can distinguish - * a safety load from a regular software */ - if (file_is_readable(fname_safety)) - rc = abis_nm_software_load(g_bts, 0xff, fname_safety, - win_size, param_forced, - swload_cbfn, g_bts); - else - fprintf(stderr, "No valid Safety Load file \"%s\"\n", - fname_safety); - break; - case BS11_STATE_WAIT_MIN_CFG: - case BS11_STATE_WAIT_MIN_CFG_2: - bs11cfg_state = STATE_SWLOAD; - rc = create_objects(g_bts); - break; - case BS11_STATE_MAINTENANCE: - if (command) { - if (!strcmp(command, "disconnect")) - abis_nm_bs11_factory_logon(g_bts, 0); - else if (!strcmp(command, "reconnect")) - rc = abis_nm_bs11_bsc_disconnect(g_bts, 1); - else if (!strcmp(command, "software") - && bs11cfg_state != STATE_SWLOAD) { - bs11cfg_state = STATE_SWLOAD; - /* send software (FIXME: over A-bis?) */ - if (file_is_readable(fname_software)) - rc = abis_nm_bs11_load_swl(g_bts, fname_software, - win_size, param_forced, - swload_cbfn); - else - fprintf(stderr, "No valid Software file \"%s\"\n", - fname_software); - } else if (!strcmp(command, "delete-trx1")) { - printf("Locing BBSIG and PA objects of TRX1\n"); - abis_nm_chg_adm_state(g_bts, NM_OC_BS11, - BS11_OBJ_BBSIG, 0, 1, - NM_STATE_LOCKED); - abis_nm_chg_adm_state(g_bts, NM_OC_BS11, - BS11_OBJ_PA, 0, 1, - NM_STATE_LOCKED); - sleep(1); - printf("Deleting BBSIG and PA objects of TRX1\n"); - abis_nm_bs11_delete_object(g_bts, BS11_OBJ_BBSIG, 1); - abis_nm_bs11_delete_object(g_bts, BS11_OBJ_PA, 1); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "create-trx1")) { - create_trx1(g_bts); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "pll-e1-locked")) { - abis_nm_bs11_set_pll_locked(g_bts, 1); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "pll-standalone")) { - abis_nm_bs11_set_pll_locked(g_bts, 0); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "pll-setvalue")) { - abis_nm_bs11_set_pll(g_bts, atoi(value)); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "pll-workvalue")) { - /* To set the work value we need to login as FIELD */ - abis_nm_bs11_factory_logon(g_bts, 0); - sleep(1); - abis_nm_bs11_infield_logon(g_bts, 1); - sleep(1); - abis_nm_bs11_set_pll(g_bts, atoi(value)); - sleep(1); - abis_nm_bs11_infield_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "oml-tei")) { - abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML); - command = NULL; - } else if (!strcmp(command, "restart")) { - abis_nm_bs11_restart(g_bts); - command = NULL; - } else if (!strcmp(command, "query")) { - cmd_query(); - } else if (!strcmp(command, "create-bport1")) { - abis_nm_bs11_create_bport(g_bts, 1); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "delete-bport1")) { - abis_nm_chg_adm_state(g_bts, NM_OC_BS11_BPORT, 1, 0xff, 0xff, NM_STATE_LOCKED); - sleep(1); - abis_nm_bs11_delete_bport(g_bts, 1); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "bport0-star")) { - abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_STAR); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "bport0-multidrop")) { - abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_MULTIDROP); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "bport1-multidrop")) { - abis_nm_bs11_set_bport_line_cfg(g_bts, 1, BS11_LINE_CFG_MULTIDROP); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } - - } - break; - case BS11_STATE_NORMAL: - if (command) { - if (!strcmp(command, "reconnect")) - abis_nm_bs11_factory_logon(g_bts, 0); - else if (!strcmp(command, "disconnect")) - abis_nm_bs11_bsc_disconnect(g_bts, 0); - else if (!strcmp(command, "query")) { - cmd_query(); - } - } else if (param_disconnect) { - param_disconnect = 0; - abis_nm_bs11_bsc_disconnect(g_bts, 0); - if (param_restart) { - param_restart = 0; - abis_nm_bs11_restart(g_bts); - } - } - break; - default: - break; - } - return rc; -} - -/* handle a fully-received message/packet from the RS232 port */ -static int abis_nm_bs11cfg_rcvmsg(struct msgb *rx_msg) -{ - struct e1inp_sign_link *link = rx_msg->dst; - struct abis_om_hdr *oh; - struct abis_om_fom_hdr *foh; - struct tlv_parsed tp; - int rc = -1; - -#if 0 - if (rx_msg->len < LAPD_HDR_LEN - + sizeof(struct abis_om_fom_hdr) - + sizeof(struct abis_om_hdr)) { - if (!memcmp(rx_msg->data + 2, too_fast, - sizeof(too_fast))) { - fprintf(stderr, "BS11 tells us we're too " - "fast, try --delay bigger than %u\n", - delay_ms); - return -E2BIG; - } else - fprintf(stderr, "unknown BS11 message\n"); - } -#endif - - oh = (struct abis_om_hdr *) msgb_l2(rx_msg); - foh = (struct abis_om_fom_hdr *) oh->data; - switch (foh->msg_type) { - case NM_MT_BS11_LMT_LOGON_ACK: - printf("LMT LOGON: ACK\n\n"); - if (bs11cfg_state == STATE_NONE) - bs11cfg_state = STATE_LOGON_ACK; - rc = abis_nm_bs11_get_state(g_bts); - break; - case NM_MT_BS11_LMT_LOGOFF_ACK: - printf("LMT LOGOFF: ACK\n"); - exit(0); - break; - case NM_MT_BS11_GET_STATE_ACK: - rc = abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); - print_state(&tp); - if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) && - TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1) - rc = handle_state_resp(*TLVP_VAL(&tp, NM_ATT_BS11_BTS_STATE)); - break; - case NM_MT_GET_ATTR_RESP: - printf("\n%sATTRIBUTES:\n", obj_name(foh)); - abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); - rc = print_attr(&tp); - //osmo_hexdump(foh->data, oh->length-sizeof(*foh)); - break; - case NM_MT_BS11_SET_ATTR_ACK: - printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) ACK\n", - foh->obj_class, foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); - rc = 0; - break; - case NM_MT_BS11_SET_ATTR_NACK: - printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) NACK\n", - foh->obj_class, foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); - break; - case NM_MT_GET_ATTR_NACK: - printf("\n%sGET ATTR NACK\n", obj_name(foh)); - break; - case NM_MT_BS11_CREATE_OBJ_ACK: - printf("\n%sCREATE OBJECT ACK\n", obj_name(foh)); - break; - case NM_MT_BS11_CREATE_OBJ_NACK: - printf("\n%sCREATE OBJECT NACK\n", obj_name(foh)); - break; - case NM_MT_BS11_DELETE_OBJ_ACK: - printf("\n%sDELETE OBJECT ACK\n", obj_name(foh)); - break; - case NM_MT_BS11_DELETE_OBJ_NACK: - printf("\n%sDELETE OBJECT NACK\n", obj_name(foh)); - break; - default: - rc = abis_nm_rcvmsg(rx_msg); - } - if (rc < 0) { - perror("ERROR in main loop"); - //break; - } - /* flush the queue of pending messages to be sent. */ - abis_nm_queue_send_next(link->trx->bts); - if (rc == 1) - return rc; - - switch (bs11cfg_state) { - case STATE_NONE: - abis_nm_bs11_factory_logon(g_bts, 1); - break; - case STATE_LOGON_ACK: - osmo_timer_schedule(&status_timer, 5, 0); - break; - default: - break; - } - - return rc; -} - -void status_timer_cb(void *data) -{ - abis_nm_bs11_get_state(g_bts); -} - -static void print_banner(void) -{ - printf("bs11_config (C) 2009-2010 by Harald Welte and Dieter Spaar\n"); - printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); -} - -static void print_help(void) -{ - printf("bs11_config [options] [command]\n"); - printf("\nSupported options:\n"); - printf("\t-h --help\t\t\tPrint this help text\n"); - printf("\t-p --port \t\tSpecify serial port\n"); - printf("\t-s --software \t\tSpecify Software file\n"); - printf("\t-S --safety \t\tSpecify Safety Load file\n"); - printf("\t-d --delay \t\t\tSpecify delay in milliseconds\n"); - printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n"); - printf("\t-w --win-size \t\tSpecify Window Size\n"); - printf("\t-f --forced\t\t\tForce Software Load\n"); - printf("\nSupported commands:\n"); - printf("\tquery\t\t\tQuery the BS-11 about serial number and configuration\n"); - printf("\tdisconnect\t\tDisconnect A-bis link (go into administrative state)\n"); - printf("\tresconnect\t\tReconnect A-bis link (go into normal state)\n"); - printf("\trestart\t\t\tRestart the BTS\n"); - printf("\tsoftware\t\tDownload Software (only in administrative state)\n"); - printf("\tcreate-trx1\t\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n"); - printf("\tdelete-trx1\t\tDelete objects for TRX1\n"); - printf("\tpll-e1-locked\t\tSet the PLL to be locked to E1 clock\n"); - printf("\tpll-standalone\t\tSet the PLL to be in standalone mode\n"); - printf("\tpll-setvalue \tSet the PLL set value\n"); - printf("\tpll-workvalue \tSet the PLL work value\n"); - printf("\toml-tei\t\t\tSet OML E1 TS and TEI\n"); - printf("\tbport0-star\t\tSet BPORT0 line config to star\n"); - printf("\tbport0-multidrop\tSet BPORT0 line config to multidrop\n"); - printf("\tbport1-multidrop\tSet BPORT1 line config to multidrop\n"); - printf("\tcreate-bport1\t\tCreate BPORT1 object\n"); - printf("\tdelete-bport1\t\tDelete BPORT1 object\n"); -} - -static void handle_options(int argc, char **argv) -{ - int option_index = 0; - print_banner(); - - while (1) { - int c; - static struct option long_options[] = { - { "help", 0, 0, 'h' }, - { "port", 1, 0, 'p' }, - { "software", 1, 0, 's' }, - { "safety", 1, 0, 'S' }, - { "delay", 1, 0, 'd' }, - { "disconnect", 0, 0, 'D' }, - { "win-size", 1, 0, 'w' }, - { "forced", 0, 0, 'f' }, - { "restart", 0, 0, 'r' }, - { "debug", 1, 0, 'b'}, - }; - - c = getopt_long(argc, argv, "hp:s:S:td:Dw:fra:", - long_options, &option_index); - - if (c == -1) - break; - - switch (c) { - case 'h': - print_help(); - exit(0); - case 'p': - serial_port = optarg; - break; - case 'b': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 's': - fname_software = optarg; - break; - case 'S': - fname_safety = optarg; - break; - case 'd': - delay_ms = atoi(optarg); - break; - case 'w': - win_size = atoi(optarg); - break; - case 'D': - param_disconnect = 1; - break; - case 'f': - param_forced = 1; - break; - case 'r': - param_disconnect = 1; - param_restart = 1; - break; - default: - break; - } - } - if (optind < argc) - command = argv[optind]; - if (optind+1 < argc) - value = argv[optind+1]; - -} - -static int num_sigint; - -static void signal_handler(int signal) -{ - fprintf(stdout, "\nsignal %u received\n", signal); - - switch (signal) { - case SIGINT: - num_sigint++; - abis_nm_bs11_factory_logon(g_bts, 0); - if (num_sigint >= 3) - exit(0); - break; - } -} - -static int bs11cfg_sign_link(struct msgb *msg) -{ - msg->dst = oml_link; - return abis_nm_bs11cfg_rcvmsg(msg); -} - -struct e1inp_line_ops bs11cfg_e1inp_line_ops = { - .sign_link = bs11cfg_sign_link, -}; - -extern int bts_model_bs11_init(void); -int main(int argc, char **argv) -{ - struct gsm_network *gsmnet; - struct e1inp_line *line; - - osmo_init_logging(&log_info); - handle_options(argc, argv); - bts_model_bs11_init(); - - gsmnet = gsm_network_init(1, 1, NULL); - if (!gsmnet) { - fprintf(stderr, "Unable to allocate gsm network\n"); - exit(1); - } - g_bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_BS11, HARDCODED_TSC, - HARDCODED_BSIC); - - /* Override existing OML callback handler to set our own. */ - g_bts->model->oml_rcvmsg = abis_nm_bs11cfg_rcvmsg; - - libosmo_abis_init(tall_bs11cfg_ctx); - - /* Initialize virtual E1 line over rs232. */ - line = talloc_zero(tall_bs11cfg_ctx, struct e1inp_line); - if (!line) { - fprintf(stderr, "Unable to allocate memory for virtual E1 line\n"); - exit(1); - } - /* set the serial port. */ - bs11cfg_e1inp_line_ops.cfg.rs232.port = serial_port; - bs11cfg_e1inp_line_ops.cfg.rs232.delay = delay_ms; - - line->driver = e1inp_driver_find("rs232"); - if (!line->driver) { - fprintf(stderr, "cannot find `rs232' driver, giving up.\n"); - exit(1); - } - e1inp_line_bind_ops(line, &bs11cfg_e1inp_line_ops); - - /* configure and create signalling link for OML. */ - e1inp_ts_config_sign(&line->ts[0], line); - g_bts->oml_link = oml_link = - e1inp_sign_link_create(&line->ts[0], E1INP_SIGN_OML, - g_bts->c0, TEI_OML, 0); - - e1inp_line_update(line); - - signal(SIGINT, &signal_handler); - - abis_nm_bs11_factory_logon(g_bts, 1); - //abis_nm_bs11_get_serno(g_bts); - - status_timer.cb = status_timer_cb; - - while (1) { - if (osmo_select_main(0) < 0) - break; - } - - abis_nm_bs11_factory_logon(g_bts, 0); - - exit(0); -} diff --git a/openbsc/src/utils/isdnsync.c b/openbsc/src/utils/isdnsync.c deleted file mode 100644 index cc8ff6723..000000000 --- a/openbsc/src/utils/isdnsync.c +++ /dev/null @@ -1,189 +0,0 @@ -/* isdnsync.c - * - * Author Andreas Eversberg - * - * All rights reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a 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/smpp_mirror.c b/openbsc/src/utils/smpp_mirror.c deleted file mode 100644 index 59e341d38..000000000 --- a/openbsc/src/utils/smpp_mirror.c +++ /dev/null @@ -1,317 +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); - - return osmo_wqueue_enqueue(&esme->wqueue, msg); -} -/* 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: - 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 void 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; - } -} - -static int smpp_esme_init(struct esme *esme, const char *host, uint16_t port) -{ - int rc; - - if (port == 0) - port = 2775; - - esme->own_seq_nr = rand(); - esme_inc_seq_nr(esme); - osmo_wqueue_init(&esme->wqueue, 10); - esme->wqueue.bfd.data = esme; - esme->wqueue.read_cb = esme_read_cb; - esme->wqueue.write_cb = esme_write_cb; - - rc = osmo_sock_init_ofd(&esme->wqueue.bfd, AF_UNSPEC, SOCK_STREAM, - IPPROTO_TCP, host, port, OSMO_SOCK_F_CONNECT); - if (rc < 0) - return rc; - - return bind_transceiver(esme); -} - - -int main(int argc, char **argv) -{ - struct esme esme; - char *host = "localhost"; - int port = 0; - int rc; - - memset(&esme, 0, sizeof(esme)); - - osmo_init_logging(&log_info); - - snprintf((char *) esme.system_id, sizeof(esme.system_id), "mirror"); - snprintf((char *) esme.password, sizeof(esme.password), "mirror"); - esme.smpp_version = 0x34; - - if (argc >= 2) - host = argv[1]; - if (argc >= 3) - port = atoi(argv[2]); - - rc = smpp_esme_init(&esme, host, port); - if (rc < 0) - exit(1); - - while (1) { - osmo_select_main(0); - } - - exit(0); -} diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am deleted file mode 100644 index c9afeedd4..000000000 --- a/openbsc/tests/Makefile.am +++ /dev/null @@ -1,61 +0,0 @@ -SUBDIRS = gsm0408 db channel mgcp gprs si abis - -if BUILD_NAT -SUBDIRS += bsc-nat -endif - -if BUILD_SMPP -SUBDIRS += smpp -endif - - -# The `:;' works around a Bash 3.2 bug when the output is not writeable. -$(srcdir)/package.m4: $(top_srcdir)/configure.ac - :;{ \ - echo '# Signature of the current package.' && \ - echo 'm4_define([AT_PACKAGE_NAME],' && \ - echo ' [$(PACKAGE_NAME)])' && \ - echo 'm4_define([AT_PACKAGE_TARNAME],' && \ - echo ' [$(PACKAGE_TARNAME)])' && \ - echo 'm4_define([AT_PACKAGE_VERSION],' && \ - echo ' [$(PACKAGE_VERSION)])' && \ - echo 'm4_define([AT_PACKAGE_STRING],' && \ - echo ' [$(PACKAGE_STRING)])' && \ - echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \ - echo ' [$(PACKAGE_BUGREPORT)])'; \ - echo 'm4_define([AT_PACKAGE_URL],' && \ - echo ' [$(PACKAGE_URL)])'; \ - } >'$(srcdir)/package.m4' - -EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) vty_test_runner.py -TESTSUITE = $(srcdir)/testsuite -DISTCLEANFILES = atconfig - -if ENABLE_VTY_TESTS -python-tests: $(BUILT_SOURCES) - osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v - osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v - $(PYTHON) $(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v - rm -f $(top_builddir)/hlr.sqlite3 -else -python-tests: $(BUILT_SOURCES) - echo "Not running python-based tests (determined at configure-time)" -endif - -check-local: atconfig $(TESTSUITE) - $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) - $(MAKE) $(AM_MAKEFLAGS) python-tests - -installcheck-local: atconfig $(TESTSUITE) - $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \ - $(TESTSUITEFLAGS) - -clean-local: - test ! -f '$(TESTSUITE)' || \ - $(SHELL) '$(TESTSUITE)' --clean - -AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te -AUTOTEST = $(AUTOM4TE) --language=autotest -$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4 - $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at - mv $@.tmp $@ diff --git a/openbsc/tests/abis/Makefile.am b/openbsc/tests/abis/Makefile.am deleted file mode 100644 index c2e38deb8..000000000 --- a/openbsc/tests/abis/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOABIS_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) $(COVERAGE_CFLAGS) - -EXTRA_DIST = abis_test.ok - -noinst_PROGRAMS = abis_test - -abis_test_SOURCES = abis_test.c - -abis_test_LDADD = \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) \ - $(LIBOSMOGSM_LIBS) diff --git a/openbsc/tests/abis/abis_test.c b/openbsc/tests/abis/abis_test.c deleted file mode 100644 index e7e78d202..000000000 --- a/openbsc/tests/abis/abis_test.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * (C) 2012 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include -#include - -#include -#include -#include - -static const uint8_t simple_config[] = { - /*0, 13, */ - 66, 18, 0, 3, 1, 2, 3, 19, 0, 3, 3, 4, 5, -}; - -static const uint8_t dual_config[] = { - /*0, 26, */ - 66, 18, 0, 3, 1, 2, 3, 19, 0, 3, 3, 4, 5, - 66, 18, 0, 3, 9, 7, 5, 19, 0, 3, 6, 7, 8, -}; - -static const uint8_t load_config[] = { - 0x42, 0x12, 0x00, 0x08, 0x31, 0x36, 0x38, 0x64, - 0x34, 0x37, 0x32, 0x00, 0x13, 0x00, 0x0b, 0x76, - 0x32, 0x30, 0x30, 0x62, 0x31, 0x34, 0x33, 0x64, - 0x30, 0x00, 0x42, 0x12, 0x00, 0x08, 0x31, 0x36, - 0x38, 0x64, 0x34, 0x37, 0x32, 0x00, 0x13, 0x00, - 0x0b, 0x76, 0x32, 0x30, 0x30, 0x62, 0x31, 0x34, - 0x33, 0x64, 0x31, 0x00 -}; - -static void test_simple_sw_config(void) -{ - struct abis_nm_sw_descr descr[1]; - int rc; - - rc = abis_nm_parse_sw_config(simple_config, ARRAY_SIZE(simple_config), - &descr[0], ARRAY_SIZE(descr)); - if (rc != 1) { - printf("FAILED to parse the File Id/File version\n"); - abort(); - } - - if (descr[0].len != 13) { - printf("WRONG SIZE: %d\n", descr[0].len); - abort(); - } - - printf("Start: %u len: %zu\n", descr[0].start - simple_config, descr[0].len); - printf("file_id: %s\n", osmo_hexdump(descr[0].file_id, descr[0].file_id_len)); - printf("file_ver: %s\n", osmo_hexdump(descr[0].file_ver, descr[0].file_ver_len)); -} - -static void test_simple_sw_short(void) -{ - struct abis_nm_sw_descr descr[1]; - int i; - - for (i = 1; i < ARRAY_SIZE(simple_config); ++i) { - int rc = abis_nm_parse_sw_config(simple_config, - ARRAY_SIZE(simple_config) - i, &descr[0], - ARRAY_SIZE(descr)); - if (rc >= 1) { - printf("SHOULD not have parsed: %d\n", rc); - abort(); - } - } -} - -static void test_dual_sw_config(void) -{ - struct abis_nm_sw_descr descr[2]; - int rc; - - rc = abis_nm_parse_sw_config(dual_config, ARRAY_SIZE(dual_config), - &descr[0], ARRAY_SIZE(descr)); - if (rc != 2) { - printf("FAILED to parse the File Id/File version\n"); - abort(); - } - - if (descr[0].len != 13) { - printf("WRONG SIZE0: %d\n", descr[0].len); - abort(); - } - - if (descr[1].len != 13) { - printf("WRONG SIZE1: %d\n", descr[1].len); - abort(); - } - - printf("Start: %u len: %zu\n", descr[0].start - dual_config, descr[0].len); - printf("file_id: %s\n", osmo_hexdump(descr[0].file_id, descr[0].file_id_len)); - printf("file_ver: %s\n", osmo_hexdump(descr[0].file_ver, descr[0].file_ver_len)); - - printf("Start: %u len: %zu\n", descr[1].start - dual_config, descr[1].len); - printf("file_id: %s\n", osmo_hexdump(descr[1].file_id, descr[1].file_id_len)); - printf("file_ver: %s\n", osmo_hexdump(descr[1].file_ver, descr[1].file_ver_len)); -} - -static void test_sw_selection(void) -{ - struct abis_nm_sw_descr descr[8], tmp; - int rc, pos; - - rc = abis_nm_parse_sw_config(load_config, ARRAY_SIZE(load_config), - &descr[0], ARRAY_SIZE(descr)); - if (rc != 2) { - printf("FAILED to parse the File Id/File version\n"); - abort(); - } - - printf("Start: %u len: %zu\n", descr[0].start - load_config, descr[0].len); - printf("file_id: %s\n", osmo_hexdump(descr[0].file_id, descr[0].file_id_len)); - printf("file_ver: %s\n", osmo_hexdump(descr[0].file_ver, descr[0].file_ver_len)); - - printf("Start: %u len: %zu\n", descr[1].start - load_config, descr[1].len); - printf("file_id: %s\n", osmo_hexdump(descr[1].file_id, descr[1].file_id_len)); - printf("file_ver: %s\n", osmo_hexdump(descr[1].file_ver, descr[1].file_ver_len)); - - /* start */ - pos = abis_nm_select_newest_sw(descr, rc); - if (pos != 1) { - printf("Selected the wrong version: %d\n", pos); - abort(); - } - printf("SELECTED: %d\n", pos); - - /* shuffle */ - tmp = descr[0]; - descr[0] = descr[1]; - descr[1] = tmp; - pos = abis_nm_select_newest_sw(descr, rc); - if (pos != 0) { - printf("Selected the wrong version: %d\n", pos); - abort(); - } - printf("SELECTED: %d\n", pos); -} - -int main(int argc, char **argv) -{ - osmo_init_logging(&log_info); - test_simple_sw_config(); - test_simple_sw_short(); - test_dual_sw_config(); - test_sw_selection(); - - return EXIT_SUCCESS; -} diff --git a/openbsc/tests/abis/abis_test.ok b/openbsc/tests/abis/abis_test.ok deleted file mode 100644 index 2f99f9d49..000000000 --- a/openbsc/tests/abis/abis_test.ok +++ /dev/null @@ -1,17 +0,0 @@ -Start: 0 len: 13 -file_id: 01 02 03 -file_ver: 03 04 05 -Start: 0 len: 13 -file_id: 01 02 03 -file_ver: 03 04 05 -Start: 13 len: 13 -file_id: 09 07 05 -file_ver: 06 07 08 -Start: 0 len: 26 -file_id: 31 36 38 64 34 37 32 00 -file_ver: 76 32 30 30 62 31 34 33 64 30 00 -Start: 26 len: 26 -file_id: 31 36 38 64 34 37 32 00 -file_ver: 76 32 30 30 62 31 34 33 64 31 00 -SELECTED: 1 -SELECTED: 0 diff --git a/openbsc/tests/atlocal.in b/openbsc/tests/atlocal.in deleted file mode 100644 index bfbecd48f..000000000 --- a/openbsc/tests/atlocal.in +++ /dev/null @@ -1,2 +0,0 @@ -enable_nat_test='@osmo_ac_build_nat@' -enable_smpp_test='@osmo_ac_build_smpp@' diff --git a/openbsc/tests/bsc-nat/Makefile.am b/openbsc/tests/bsc-nat/Makefile.am deleted file mode 100644 index 084767829..000000000 --- a/openbsc/tests/bsc-nat/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(COVERAGE_LDFLAGS) - -EXTRA_DIST = bsc_nat_test.ok bsc_data.c barr.cfg barr_dup.cfg - -noinst_PROGRAMS = bsc_nat_test - -bsc_nat_test_SOURCES = bsc_nat_test.c \ - $(top_srcdir)/src/osmo-bsc_nat/bsc_filter.c \ - $(top_srcdir)/src/osmo-bsc_nat/bsc_sccp.c \ - $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_utils.c \ - $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_filter.c \ - $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite.c \ - $(top_srcdir)/src/osmo-bsc_nat/bsc_mgcp_utils.c -bsc_nat_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ - $(top_srcdir)/src/libctrl/libctrl.a \ - $(top_srcdir)/src/libmgcp/libmgcp.a \ - $(top_srcdir)/src/libtrau/libtrau.a \ - $(top_srcdir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lrt \ - $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOABIS_LIBS) diff --git a/openbsc/tests/bsc-nat/barr.cfg b/openbsc/tests/bsc-nat/barr.cfg deleted file mode 100644 index a9a4a2b31..000000000 --- a/openbsc/tests/bsc-nat/barr.cfg +++ /dev/null @@ -1,12 +0,0 @@ -12123124:3:2: -12123123:3:1: -12123128:3:6: -12123125:3:3: -12123127:3:5: -12123126:3:4: -12123120:3:4: -12123119:3:4: -12123118:3:4: -12123117:3:4: -12123116:3:4: -12123115:3:4: diff --git a/openbsc/tests/bsc-nat/barr_dup.cfg b/openbsc/tests/bsc-nat/barr_dup.cfg deleted file mode 100644 index ea94631ce..000000000 --- a/openbsc/tests/bsc-nat/barr_dup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -12123124:3:2: -12123124:3:2: diff --git a/openbsc/tests/bsc-nat/bsc_data.c b/openbsc/tests/bsc-nat/bsc_data.c deleted file mode 100644 index 5a76689e9..000000000 --- a/openbsc/tests/bsc-nat/bsc_data.c +++ /dev/null @@ -1,255 +0,0 @@ -/* test data */ - -/* BSC -> MSC, CR */ -static const uint8_t bsc_cr[] = { -0x00, 0x2e, 0xfd, -0x01, 0x00, 0x00, 0x15, 0x02, 0x02, 0x04, 0x02, -0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05, -0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x1c, 0xc3, -0x51, 0x17, 0x12, 0x05, 0x08, 0x20, 0x72, 0xf4, -0x90, 0x20, 0x1d, 0x50, 0x08, 0x29, 0x47, 0x80, -0x00, 0x00, 0x00, 0x00, 0x80, 0x00 }; - -static const uint8_t bsc_cr_patched[] = { -0x00, 0x2e, 0xfd, -0x01, 0x00, 0x00, 0x05, 0x02, 0x02, 0x04, 0x02, -0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05, -0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x1c, 0xc3, -0x51, 0x17, 0x12, 0x05, 0x08, 0x20, 0x72, 0xf4, -0x90, 0x20, 0x1d, 0x50, 0x08, 0x29, 0x47, 0x80, -0x00, 0x00, 0x00, 0x00, 0x80, 0x00 }; - -/* CC, MSC -> BSC */ -static const uint8_t msc_cc[] = { -0x00, 0x0a, 0xfd, -0x02, 0x00, 0x00, 0x05, 0x01, 0x1f, 0xe4, 0x02, -0x01, 0x00 }; -static const uint8_t msc_cc_patched[] = { -0x00, 0x0a, 0xfd, -0x02, 0x00, 0x00, 0x15, 0x01, 0x1f, 0xe4, 0x02, -0x01, 0x00 }; - -/* Classmark, BSC -> MSC */ -static const uint8_t bsc_dtap[] = { -0x00, 0x17, 0xfd, -0x06, 0x01, 0x1f, 0xe4, 0x00, 0x01, 0x10, 0x00, -0x0e, 0x54, 0x12, 0x03, 0x50, 0x18, 0x93, 0x13, -0x06, 0x60, 0x14, 0x45, 0x00, 0x81, 0x00 }; - -static const uint8_t bsc_dtap_patched[] = { -0x00, 0x17, 0xfd, -0x06, 0x01, 0x1f, 0xe4, 0x00, 0x01, 0x10, 0x00, -0x0e, 0x54, 0x12, 0x03, 0x50, 0x18, 0x93, 0x13, -0x06, 0x60, 0x14, 0x45, 0x00, 0x81, 0x00 }; - -/* Clear command, MSC -> BSC */ -static const uint8_t msc_dtap[] = { -0x00, 0x0d, 0xfd, -0x06, 0x00, 0x00, 0x05, 0x00, 0x01, 0x06, 0x00, -0x04, 0x20, 0x04, 0x01, 0x09 }; -static const uint8_t msc_dtap_patched[] = { -0x00, 0x0d, 0xfd, -0x06, 0x00, 0x00, 0x15, 0x00, 0x01, 0x06, 0x00, -0x04, 0x20, 0x04, 0x01, 0x09 }; - -/*RLSD, MSC -> BSC */ -static const uint8_t msc_rlsd[] = { -0x00, 0x0a, 0xfd, -0x04, 0x00, 0x00, 0x05, 0x01, 0x1f, 0xe4, 0x00, -0x01, 0x00 }; -static const uint8_t msc_rlsd_patched[] = { -0x00, 0x0a, 0xfd, -0x04, 0x00, 0x00, 0x15, 0x01, 0x1f, 0xe4, 0x00, -0x01, 0x00 }; - -/* RLC, BSC -> MSC */ -static const uint8_t bsc_rlc[] = { -0x00, 0x07, 0xfd, -0x05, 0x01, 0x1f, 0xe4, 0x00, 0x00, 0x15 }; - -static const uint8_t bsc_rlc_patched[] = { -0x00, 0x07, 0xfd, -0x05, 0x01, 0x1f, 0xe4, 0x00, 0x00, 0x05 }; - - -/* a paging command */ -static const uint8_t paging_by_lac_cmd[] = { -0x00, 0x22, 0xfd, 0x09, -0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x02, 0x00, -0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x12, 0x00, -0x10, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10, 0x02, -0x01, 0x50, 0x02, 0x30, 0x1a, 0x03, 0x05, 0x20, -0x15 }; - -/* an assignment command */ -static const uint8_t ass_cmd[] = { -0x00, 0x12, 0xfd, 0x06, -0x00, 0x00, 0x49, 0x00, 0x01, 0x0b, 0x00, 0x09, -0x01, 0x0b, 0x03, 0x01, 0x0a, 0x11, 0x01, 0x00, -0x01 }; - -/* identity response */ -static const uint8_t id_resp[] = { -0x00, 0x15, 0xfd, 0x06, 0x01, 0x1c, 0xdc, -0x00, 0x01, 0x0e, 0x01, 0x00, 0x0b, 0x05, 0x59, -0x08, 0x29, 0x40, 0x21, 0x03, 0x07, 0x48, 0x66, -0x31 -}; - -/* sms code msg */ -static const uint8_t smsc_rewrite[] = { -0x00, 0x30, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, -0x01, 0x29, 0x01, 0x03, 0x26, 0x09, 0x01, 0x23, -0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08, -0x00, 0x10, 0x50, 0x17, 0x21, 0x0c, 0x0f, 0x81, -0x00, 0x94, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, -0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, -0xbf, 0xeb, 0x20 -}; - -static const uint8_t smsc_rewrite_patched[] = { -0x00, 0x31, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, -0x01, 0x2a, 0x01, 0x03, 0x27, 0x09, 0x01, 0x24, -0x00, 0x0c, 0x00, 0x08, 0x91, 0x66, 0x66, 0x66, -0x66, 0x66, 0x66, 0xf7, 0x17, 0x01, 0x0c, 0x0f, -0x81, 0x00, 0x94, 0x51, 0x87, 0x86, 0x78, 0x46, -0xf5, 0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, -0xca, 0xbf, 0xeb, 0x20 -}; - -static const uint8_t smsc_rewrite_patched_hdr[] = { -0x00, 0x30, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, -0x01, 0x29, 0x01, 0x03, 0x26, 0x09, 0x01, 0x23, -0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08, -0x00, 0x10, 0x50, 0x17, 0x01, 0x0c, 0x0f, 0x81, -0x00, 0x94, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, -0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, -0xbf, 0xeb, 0x20 -}; - -static const uint8_t smsc_rewrite_num_patched[] = { -0x00, 0x2f, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, -0x01, 0x28, 0x01, 0x03, 0x25, 0x09, 0x01, 0x22, -0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08, -0x00, 0x10, 0x50, 0x16, 0x21, 0x0c, 0x0d, 0x91, - 0x23, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, -0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, -0xbf, 0xeb, 0x20 -}; - -static const uint8_t smsc_rewrite_num_patched_tp_srr[] = { -0x00, 0x2f, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, -0x01, 0x28, 0x01, 0x03, 0x25, 0x09, 0x01, 0x22, -0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08, -0x00, 0x10, 0x50, 0x16, 0x01, 0x0c, 0x0d, 0x91, - 0x23, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, -0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, -0xbf, 0xeb, 0x20 -}; - -/* - * MGCP messages - */ - -/* nothing to patch */ -static const char crcx[] = "CRCX 23265295 8@mgw MGCP 1.0\r\nC: 394b0439fb\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n"; -static const char crcx_patched[] = "CRCX 23265295 1e@mgw MGCP 1.0\r\nC: 394b0439fb\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n"; - - -/* patch the ip and port */ -static const char crcx_resp[] = "200 23265295\r\nI: 1\r\n\r\nv=0\r\nc=IN IP4 172.16.18.2\r\nm=audio 4002 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\n"; -static const char crcx_resp_patched[] = "200 23265295\r\nI: 1\r\n\r\nv=0\r\nc=IN IP4 10.0.0.1\r\nm=audio 999 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\na=fmtp:98 mode-set=2\r\n"; - -/* patch the ip and port */ -static const char mdcx[] = "MDCX 23330829 8@mgw MGCP 1.0\r\nC: 394b0439fb\r\nI: 1\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 1049380491 0 IN IP4 172.16.18.2\r\ns=-\r\nc=IN IP4 172.16.18.2\r\nt=0 0\r\nm=audio 4410 RTP/AVP 126\r\na=rtpmap:126 AMR/8000/1\r\na=fmtp:126 mode-set=2;start-mode=0\r\na=ptime:20\r\na=recvonly\r\nm=image 4412 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"; -static const char mdcx_patched[] = "MDCX 23330829 1e@mgw MGCP 1.0\r\nC: 394b0439fb\r\nI: 1\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 1049380491 0 IN IP4 172.16.18.2\r\ns=-\r\nc=IN IP4 10.0.0.23\r\nt=0 0\r\nm=audio 6666 RTP/AVP 126\r\na=rtpmap:126 AMR/8000/1\r\na=fmtp:126 mode-set=2;start-mode=0\r\na=ptime:20\r\na=recvonly\r\nm=image 4412 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"; - - -static const char mdcx_resp[] = "200 23330829\r\n\r\nv=0\r\nc=IN IP4 172.16.18.2\r\nm=audio 4002 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\n"; -static const char mdcx_resp_patched[] = "200 23330829\r\n\r\nv=0\r\nc=IN IP4 10.0.0.23\r\nm=audio 5555 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\na=fmtp:98 mode-set=2\r\n"; - -/* different line ending */ -static const char mdcx_resp2[] = "200 33330829\n\nv=0\nc=IN IP4 172.16.18.2\nm=audio 4002 RTP/AVP 98\na=rtpmap:98 AMR/8000\n"; -static const char mdcx_resp_patched2[] = "200 33330829\n\nv=0\nc=IN IP4 10.0.0.23\nm=audio 5555 RTP/AVP 98\na=rtpmap:98 AMR/8000\na=fmtp:98 mode-set=2\n"; - -struct mgcp_patch_test { - const char *orig; - const char *patch; - const char *ip; - const int port; -}; - -static const struct mgcp_patch_test mgcp_messages[] = { - { - .orig = crcx, - .patch = crcx_patched, - .ip = "0.0.0.0", - .port = 2323, - }, - { - .orig = crcx_resp, - .patch = crcx_resp_patched, - .ip = "10.0.0.1", - .port = 999, - }, - { - .orig = mdcx, - .patch = mdcx_patched, - .ip = "10.0.0.23", - .port = 6666, - }, - { - .orig = mdcx_resp, - .patch = mdcx_resp_patched, - .ip = "10.0.0.23", - .port = 5555, - }, - { - .orig = mdcx_resp2, - .patch = mdcx_resp_patched2, - .ip = "10.0.0.23", - .port = 5555, - }, -}; - -/* CC Setup messages */ -static const uint8_t cc_setup_national[] = { - 0x00, 0x20, 0xfd, 0x06, 0x01, 0x12, - 0x6d, 0x00, 0x01, 0x19, 0x01, 0x00, 0x16, 0x03, - 0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, - 0x81, 0x5e, 0x06, 0x81, 0x10, 0x27, 0x33, 0x63, - 0x66, 0x15, 0x02, 0x11, 0x01 -}; - -static const uint8_t cc_setup_national_patched[] = { - 0x00, 0x21, 0xfd, 0x06, 0x01, 0x12, - 0x6d, 0x00, 0x01, 0x1a, 0x01, 0x00, 0x17, 0x03, - 0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, - 0x81, 0x5e, 0x07, 0x91, 0x94, 0x71, 0x32, 0x33, - 0x66, 0xf6, 0x15, 0x02, 0x11, 0x01 -}; - -/* patch the phone number of cc_setup_national_patched */ -static const uint8_t cc_setup_national_patched_patched[] = { - 0x00, 0x21, 0xfd, 0x06, 0x01, 0x12, - 0x6d, 0x00, 0x01, 0x1a, 0x01, 0x00, 0x17, 0x03, - 0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, - 0x81, 0x5e, 0x07, 0x91, 0x63, 0x71, 0x32, 0x33, - 0x66, 0xf6, 0x15, 0x02, 0x11, 0x01 -}; - -static const uint8_t cc_setup_international[] = { - 0x00, 0x22, 0xfd, 0x06, 0x01, 0x13, - 0xe7, 0x00, 0x01, 0x1b, 0x01, 0x00, 0x18, 0x03, - 0x45, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, - 0x81, 0x5e, 0x08, 0x81, 0x00, 0x94, 0x71, 0x33, - 0x63, 0x66, 0x03, 0x15, 0x02, 0x11, 0x01 -}; - -static const uint8_t cc_setup_national_again[] = { - 0x00, 0x22, 0xfd, 0x06, 0x01, 0x12, 0x6d, 0x00, - 0x01, 0x1b, 0x01, 0x00, 0x18, 0x03, 0x05, 0x04, - 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81, 0x5e, - 0x08, 0x81, 0x63, 0x94, 0x71, 0x32, 0x33, 0x66, - 0xf6, 0x15, 0x02, 0x11, 0x01 -}; diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.c b/openbsc/tests/bsc-nat/bsc_nat_test.c deleted file mode 100644 index 4a244dba3..000000000 --- a/openbsc/tests/bsc-nat/bsc_nat_test.c +++ /dev/null @@ -1,1339 +0,0 @@ -/* - * BSC NAT Message filtering - * - * (C) 2010-2013 by Holger Hans Peter Freyther - * (C) 2010-2013 by On-Waves - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include - -/* test messages for ipa */ -static uint8_t ipa_id[] = { - 0x00, 0x01, 0xfe, 0x06, -}; - -/* SCCP messages are below */ -static uint8_t gsm_reset[] = { - 0x00, 0x12, 0xfd, - 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe, - 0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04, - 0x01, 0x20, -}; - -static const uint8_t gsm_reset_ack[] = { - 0x00, 0x13, 0xfd, - 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, - 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03, - 0x00, 0x01, 0x31, -}; - -static const uint8_t gsm_paging[] = { - 0x00, 0x20, 0xfd, - 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, - 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x10, - 0x00, 0x0e, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10, - 0x02, 0x01, 0x31, 0x97, 0x61, 0x1a, 0x01, 0x06, -}; - -/* BSC -> MSC connection open */ -static const uint8_t bssmap_cr[] = { - 0x00, 0x2c, 0xfd, - 0x01, 0x01, 0x02, 0x03, 0x02, 0x02, 0x04, 0x02, - 0x42, 0xfe, 0x0f, 0x1f, 0x00, 0x1d, 0x57, 0x05, - 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x12, 0xc3, - 0x50, 0x17, 0x10, 0x05, 0x24, 0x11, 0x03, 0x33, - 0x19, 0xa2, 0x08, 0x29, 0x47, 0x10, 0x02, 0x01, - 0x31, 0x97, 0x61, 0x00 -}; - -/* MSC -> BSC connection confirm */ -static const uint8_t bssmap_cc[] = { - 0x00, 0x0a, 0xfd, - 0x02, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, -}; - -/* MSC -> BSC released */ -static const uint8_t bssmap_released[] = { - 0x00, 0x0e, 0xfd, - 0x04, 0x00, 0x00, 0x03, 0x01, 0x02, 0x03, 0x00, 0x01, 0x0f, - 0x02, 0x23, 0x42, 0x00, -}; - -/* BSC -> MSC released */ -static const uint8_t bssmap_release_complete[] = { - 0x00, 0x07, 0xfd, - 0x05, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03 -}; - -/* both directions IT timer */ -static const uint8_t connnection_it[] = { - 0x00, 0x0b, 0xfd, - 0x10, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, - 0x00, 0x00, 0x00, 0x00, -}; - -/* error in both directions */ -static const uint8_t proto_error[] = { - 0x00, 0x05, 0xfd, - 0x0f, 0x22, 0x33, 0x44, 0x00, -}; - -/* MGCP wrap... */ -static const uint8_t mgcp_msg[] = { - 0x00, 0x03, 0xfc, - 0x20, 0x20, 0x20, -}; - -/* location updating request */ -static const uint8_t bss_lu[] = { - 0x00, 0x2e, 0xfd, - 0x01, 0x91, 0x45, 0x14, 0x02, 0x02, 0x04, 0x02, - 0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05, - 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x14, 0xc3, - 0x50, 0x17, 0x12, 0x05, 0x08, 0x70, 0x72, 0xf4, - 0x80, 0xff, 0xfe, 0x30, 0x08, 0x29, 0x44, 0x50, - 0x12, 0x03, 0x24, 0x01, 0x95, 0x00 -}; - -/* paging response */ -static const uint8_t pag_resp[] = { - 0x00, 0x2c, 0xfd, 0x01, 0xe5, 0x68, - 0x14, 0x02, 0x02, 0x04, 0x02, 0x42, 0xfe, 0x0f, - 0x1f, 0x00, 0x1d, 0x57, 0x05, 0x08, 0x00, 0x72, - 0xf4, 0x80, 0x20, 0x16, 0xc3, 0x50, 0x17, 0x10, - 0x06, 0x27, 0x01, 0x03, 0x30, 0x18, 0x96, 0x08, - 0x29, 0x26, 0x30, 0x32, 0x11, 0x42, 0x01, 0x19, - 0x00 -}; - -struct filter_result { - const uint8_t *data; - const uint16_t length; - const int dir; - const int result; -}; - -static const struct filter_result results[] = { - { - .data = ipa_id, - .length = ARRAY_SIZE(ipa_id), - .dir = DIR_MSC, - .result = 1, - }, - { - .data = gsm_reset, - .length = ARRAY_SIZE(gsm_reset), - .dir = DIR_MSC, - .result = 1, - }, - { - .data = gsm_reset_ack, - .length = ARRAY_SIZE(gsm_reset_ack), - .dir = DIR_BSC, - .result = 1, - }, - { - .data = gsm_paging, - .length = ARRAY_SIZE(gsm_paging), - .dir = DIR_BSC, - .result = 0, - }, - { - .data = bssmap_cr, - .length = ARRAY_SIZE(bssmap_cr), - .dir = DIR_MSC, - .result = 0, - }, - { - .data = bssmap_cc, - .length = ARRAY_SIZE(bssmap_cc), - .dir = DIR_BSC, - .result = 0, - }, - { - .data = bssmap_released, - .length = ARRAY_SIZE(bssmap_released), - .dir = DIR_MSC, - .result = 0, - }, - { - .data = bssmap_release_complete, - .length = ARRAY_SIZE(bssmap_release_complete), - .dir = DIR_BSC, - .result = 0, - }, - { - .data = mgcp_msg, - .length = ARRAY_SIZE(mgcp_msg), - .dir = DIR_MSC, - .result = 0, - }, - { - .data = connnection_it, - .length = ARRAY_SIZE(connnection_it), - .dir = DIR_BSC, - .result = 0, - }, - { - .data = connnection_it, - .length = ARRAY_SIZE(connnection_it), - .dir = DIR_MSC, - .result = 0, - }, - { - .data = proto_error, - .length = ARRAY_SIZE(proto_error), - .dir = DIR_BSC, - .result = 0, - }, - { - .data = proto_error, - .length = ARRAY_SIZE(proto_error), - .dir = DIR_MSC, - .result = 0, - }, - -}; - -static void test_filter(void) -{ - int i; - - - /* start testinh with proper messages */ - printf("Testing BSS Filtering.\n"); - for (i = 0; i < ARRAY_SIZE(results); ++i) { - int result; - struct bsc_nat_parsed *parsed; - struct msgb *msg = msgb_alloc(4096, "test-message"); - - printf("Going to test item: %d\n", i); - memcpy(msg->data, results[i].data, results[i].length); - msg->l2h = msgb_put(msg, results[i].length); - - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Failed to parse the message\n"); - continue; - } - - result = bsc_nat_filter_ipa(results[i].dir, msg, parsed); - if (result != results[i].result) { - printf("FAIL: Not the expected result got: %d wanted: %d\n", - result, results[i].result); - } - - msgb_free(msg); - } -} - -#include "bsc_data.c" - -static void copy_to_msg(struct msgb *msg, const uint8_t *data, unsigned int length) -{ - msgb_reset(msg); - msg->l2h = msgb_put(msg, length); - memcpy(msg->l2h, data, msgb_l2len(msg)); -} - -static void verify_msg(struct msgb *out, const uint8_t *ref, int ref_len) -{ - if (out->len != ref_len) { - printf("FAIL: The size should match: %d vs. %d\n", - out->len, ref_len); - printf("%s\n", osmo_hexdump(out->data, out->len)); - printf("Wanted\n"); - printf("%s\n", osmo_hexdump(ref, ref_len)); - abort(); - } - - if (memcmp(out->data, ref, out->len) != 0) { - printf("FAIL: the data should be changed.\n"); - printf("%s\n", osmo_hexdump(out->data, out->len)); - printf("Wanted\n"); - printf("%s\n", osmo_hexdump(ref, ref_len)); - abort(); - } -} - - -#define VERIFY(con_found, con, msg, ver, str) \ - if (!con_found) { \ - printf("Failed to find connection.\n"); \ - abort(); \ - } \ - if (con_found->bsc != con) { \ - printf("Got connection of the wrong BSC: %d\n", \ - con_found->bsc->cfg->nr); \ - abort(); \ - } \ - if (memcmp(msg->data, ver, sizeof(ver)) != 0) { \ - printf("Failed to patch the %s msg.\n", str); \ - abort(); \ - } - -/* test conn tracking once */ -static void test_contrack() -{ - struct bsc_nat *nat; - struct bsc_connection *con; - struct nat_sccp_connection *con_found; - struct nat_sccp_connection *rc_con; - struct bsc_nat_parsed *parsed; - struct msgb *msg; - - printf("Testing connection tracking.\n"); - nat = bsc_nat_alloc(); - con = bsc_connection_alloc(nat); - con->cfg = bsc_config_alloc(nat, "foo"); - bsc_config_add_lac(con->cfg, 23); - bsc_config_add_lac(con->cfg, 49); - bsc_config_add_lac(con->cfg, 42); - bsc_config_del_lac(con->cfg, 49); - bsc_config_add_lac(con->cfg, 1111); - msg = msgb_alloc(4096, "test"); - - /* 1.) create a connection */ - copy_to_msg(msg, bsc_cr, sizeof(bsc_cr)); - parsed = bsc_nat_parse(msg); - con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); - if (con_found != NULL) { - printf("Con should not exist realref(%u)\n", - sccp_src_ref_to_int(&con_found->real_ref)); - abort(); - } - rc_con = create_sccp_src_ref(con, parsed); - if (!rc_con) { - printf("Failed to create a ref\n"); - abort(); - } - con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); - if (!con_found) { - printf("Failed to find connection.\n"); - abort(); - } - if (con_found->bsc != con) { - printf("Got connection of the wrong BSC: %d\n", - con_found->bsc->cfg->nr); - abort(); - } - if (con_found != rc_con) { - printf("Failed to find the right connection.\n"); - abort(); - } - if (memcmp(msg->data, bsc_cr_patched, sizeof(bsc_cr_patched)) != 0) { - printf("Failed to patch the BSC CR msg.\n"); - abort(); - } - talloc_free(parsed); - - /* 2.) get the cc */ - copy_to_msg(msg, msc_cc, sizeof(msc_cc)); - parsed = bsc_nat_parse(msg); - con_found = patch_sccp_src_ref_to_bsc(msg, parsed, nat); - VERIFY(con_found, con, msg, msc_cc_patched, "MSC CC"); - if (update_sccp_src_ref(con_found, parsed) != 0) { - printf("Failed to update the SCCP con.\n"); - abort(); - } - - /* 3.) send some data */ - copy_to_msg(msg, bsc_dtap, sizeof(bsc_dtap)); - parsed = bsc_nat_parse(msg); - con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); - VERIFY(con_found, con, msg, bsc_dtap_patched, "BSC DTAP"); - - /* 4.) receive some data */ - copy_to_msg(msg, msc_dtap, sizeof(msc_dtap)); - parsed = bsc_nat_parse(msg); - con_found = patch_sccp_src_ref_to_bsc(msg, parsed, nat); - VERIFY(con_found, con, msg, msc_dtap_patched, "MSC DTAP"); - - /* 5.) close the connection */ - copy_to_msg(msg, msc_rlsd, sizeof(msc_rlsd)); - parsed = bsc_nat_parse(msg); - con_found = patch_sccp_src_ref_to_bsc(msg, parsed, nat); - VERIFY(con_found, con, msg, msc_rlsd_patched, "MSC RLSD"); - - /* 6.) confirm the connection close */ - copy_to_msg(msg, bsc_rlc, sizeof(bsc_rlc)); - parsed = bsc_nat_parse(msg); - con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); - if (!con_found) { - printf("Failed to find connection.\n"); - abort(); - } - if (con_found->bsc != con) { - printf("Got connection of the wrong BSC: %d\n", - con_found->bsc->cfg->nr); - abort(); - } - if (memcmp(msg->data, bsc_rlc_patched, sizeof(bsc_rlc_patched)) != 0) { - printf("Failed to patch the BSC CR msg.\n"); - abort(); - } - remove_sccp_src_ref(con, msg, parsed); - talloc_free(parsed); - - copy_to_msg(msg, bsc_rlc, sizeof(bsc_rlc)); - parsed = bsc_nat_parse(msg); - con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); - - /* verify that it is gone */ - if (con_found != NULL) { - printf("Con should not exist real_ref(%u)\n", - sccp_src_ref_to_int(&con_found->real_ref)); - abort(); - } - talloc_free(parsed); - - - bsc_config_free(con->cfg); - talloc_free(nat); - msgb_free(msg); -} - -static void test_paging(void) -{ - struct bsc_nat *nat; - struct bsc_connection *con; - struct bsc_config *cfg; - - printf("Testing paging by lac.\n"); - - nat = bsc_nat_alloc(); - con = bsc_connection_alloc(nat); - cfg = bsc_config_alloc(nat, "unknown"); - con->cfg = cfg; - bsc_config_add_lac(cfg, 23); - con->authenticated = 1; - llist_add(&con->list_entry, &nat->bsc_connections); - - /* Test it by not finding it */ - if (bsc_config_handles_lac(cfg, 8213) != 0) { - printf("Should not be handled.\n"); - abort(); - } - - /* Test by finding it */ - bsc_config_del_lac(cfg, 23); - bsc_config_add_lac(cfg, 8213); - if (bsc_config_handles_lac(cfg, 8213) == 0) { - printf("Should have found it.\n"); - abort(); - } -} - -static void test_mgcp_allocations(void) -{ -#if 0 - struct bsc_connection *bsc; - struct bsc_nat *nat; - struct nat_sccp_connection con; - int i, j, multiplex; - - printf("Testing MGCP.\n"); - memset(&con, 0, sizeof(con)); - - nat = bsc_nat_alloc(); - nat->bsc_endpoints = talloc_zero_array(nat, - struct bsc_endpoint, - 65); - nat->mgcp_cfg = mgcp_config_alloc(); - nat->mgcp_cfg->trunk.number_endpoints = 64; - - bsc = bsc_connection_alloc(nat); - bsc->cfg = bsc_config_alloc(nat, "foo"); - bsc->cfg->max_endpoints = 60; - bsc_config_add_lac(bsc->cfg, 2323); - bsc->last_endpoint = 0x22; - con.bsc = bsc; - - bsc_init_endps_if_needed(bsc); - - i = 1; - do { - if (bsc_assign_endpoint(bsc, &con) != 0) { - printf("failed to allocate... on iteration %d\n", i); - break; - } - ++i; - } while(1); - - multiplex = bsc_mgcp_nr_multiplexes(bsc->cfg->max_endpoints); - for (i = 0; i < multiplex; ++i) { - for (j = 0; j < 32; ++j) - printf("%d", bsc->_endpoint_status[i*32 + j]); - printf(": %d of %d\n", i*32 + 32, 32 * 8); - } -#endif -} - -static void test_mgcp_ass_tracking(void) -{ - struct bsc_connection *bsc; - struct bsc_nat *nat; - struct nat_sccp_connection con; - struct bsc_nat_parsed *parsed; - struct msgb *msg; - - printf("Testing MGCP.\n"); - memset(&con, 0, sizeof(con)); - - nat = bsc_nat_alloc(); - nat->bsc_endpoints = talloc_zero_array(nat, - struct bsc_endpoint, - 33); - nat->mgcp_cfg = mgcp_config_alloc(); - nat->mgcp_cfg->trunk.number_endpoints = 64; - mgcp_endpoints_allocate(&nat->mgcp_cfg->trunk); - - bsc = bsc_connection_alloc(nat); - bsc->cfg = bsc_config_alloc(nat, "foo"); - bsc_config_add_lac(bsc->cfg, 2323); - bsc->last_endpoint = 0x1e; - con.bsc = bsc; - - msg = msgb_alloc(4096, "foo"); - copy_to_msg(msg, ass_cmd, sizeof(ass_cmd)); - parsed = bsc_nat_parse(msg); - - if (msg->l2h[16] != 0 || - msg->l2h[17] != 0x1) { - printf("Input is not as expected.. %s 0x%x\n", - osmo_hexdump(msg->l2h, msgb_l2len(msg)), - msg->l2h[17]); - abort(); - } - - if (bsc_mgcp_assign_patch(&con, msg) != 0) { - printf("Failed to handle assignment.\n"); - abort(); - } - - if (con.msc_endp != 1) { - printf("Timeslot should be 1.\n"); - abort(); - } - - if (con.bsc_endp != 0x1) { - printf("Assigned timeslot should have been 1.\n"); - abort(); - } - if (con.bsc->_endpoint_status[0x1] != 1) { - printf("The status on the BSC is wrong.\n"); - abort(); - } - - int multiplex, timeslot; - mgcp_endpoint_to_timeslot(0x1, &multiplex, ×lot); - - uint16_t cic = htons(timeslot & 0x1f); - if (memcmp(&cic, &msg->l2h[16], sizeof(cic)) != 0) { - printf("Message was not patched properly\n"); - printf("data cic: 0x%x %s\n", cic, osmo_hexdump(msg->l2h, msgb_l2len(msg))); - abort(); - } - - talloc_free(parsed); - - bsc_mgcp_dlcx(&con); - if (con.bsc_endp != -1 || con.msc_endp != -1 || - con.bsc->_endpoint_status[1] != 0 || con.bsc->last_endpoint != 0x1) { - printf("Clearing should remove the mapping.\n"); - abort(); - } - - bsc_config_free(bsc->cfg); - talloc_free(nat); -} - -/* test the code to find a given connection */ -static void test_mgcp_find(void) -{ - struct bsc_nat *nat; - struct bsc_connection *con; - struct nat_sccp_connection *sccp_con; - - printf("Testing finding of a BSC Connection\n"); - - nat = bsc_nat_alloc(); - con = bsc_connection_alloc(nat); - llist_add(&con->list_entry, &nat->bsc_connections); - - sccp_con = talloc_zero(con, struct nat_sccp_connection); - sccp_con->msc_endp = 12; - sccp_con->bsc_endp = 12; - sccp_con->bsc = con; - llist_add(&sccp_con->list_entry, &nat->sccp_connections); - - if (bsc_mgcp_find_con(nat, 11) != NULL) { - printf("Found the wrong connection.\n"); - abort(); - } - - if (bsc_mgcp_find_con(nat, 12) != sccp_con) { - printf("Didn't find the connection\n"); - abort(); - } - - /* free everything */ - talloc_free(nat); -} - -static void test_mgcp_rewrite(void) -{ - int i; - struct msgb *output; - printf("Testing rewriting MGCP messages.\n"); - - for (i = 0; i < ARRAY_SIZE(mgcp_messages); ++i) { - const char *orig = mgcp_messages[i].orig; - const char *patc = mgcp_messages[i].patch; - const char *ip = mgcp_messages[i].ip; - const int port = mgcp_messages[i].port; - - char *input = strdup(orig); - - output = bsc_mgcp_rewrite(input, strlen(input), 0x1e, ip, port); - if (msgb_l2len(output) != strlen(patc)) { - printf("Wrong sizes for test: %d %d != %d != %d\n", i, msgb_l2len(output), strlen(patc), strlen(orig)); - printf("String '%s' vs '%s'\n", (const char *) output->l2h, patc); - abort(); - } - - if (memcmp(output->l2h, patc, msgb_l2len(output)) != 0) { - printf("Broken on %d msg: '%s'\n", i, (const char *) output->l2h); - abort(); - } - - msgb_free(output); - free(input); - } -} - -static void test_mgcp_parse(void) -{ - int code, ci; - char transaction[60]; - - printf("Testing MGCP response parsing.\n"); - - if (bsc_mgcp_parse_response(crcx_resp, &code, transaction) != 0) { - printf("Failed to parse CRCX resp.\n"); - abort(); - } - - if (code != 200) { - printf("Failed to parse the CODE properly. Got: %d\n", code); - abort(); - } - - if (strcmp(transaction, "23265295") != 0) { - printf("Failed to parse transaction id: '%s'\n", transaction); - abort(); - } - - ci = bsc_mgcp_extract_ci(crcx_resp); - if (ci != 1) { - printf("Failed to parse the CI. Got: %d\n", ci); - abort(); - } -} - -struct cr_filter { - const uint8_t *data; - int length; - int result; - int contype; - - const char *bsc_imsi_allow; - const char *bsc_imsi_deny; - const char *nat_imsi_deny; -}; - -static struct cr_filter cr_filter[] = { - { - .data = bssmap_cr, - .length = sizeof(bssmap_cr), - .result = 1, - .contype = NAT_CON_TYPE_CM_SERV_REQ, - }, - { - .data = bss_lu, - .length = sizeof(bss_lu), - .result = 1, - .contype = NAT_CON_TYPE_LU, - }, - { - .data = pag_resp, - .length = sizeof(pag_resp), - .result = 1, - .contype = NAT_CON_TYPE_PAG_RESP, - }, - { - /* nat deny is before blank/null BSC */ - .data = bss_lu, - .length = sizeof(bss_lu), - .result = -3, - .nat_imsi_deny = "[0-9]*", - .contype = NAT_CON_TYPE_LU, - }, - { - /* BSC allow is before NAT deny */ - .data = bss_lu, - .length = sizeof(bss_lu), - .result = 1, - .nat_imsi_deny = "[0-9]*", - .bsc_imsi_allow = "2440[0-9]*", - .contype = NAT_CON_TYPE_LU, - }, - { - /* BSC allow is before NAT deny */ - .data = bss_lu, - .length = sizeof(bss_lu), - .result = 1, - .bsc_imsi_allow = "[0-9]*", - .nat_imsi_deny = "[0-9]*", - .contype = NAT_CON_TYPE_LU, - }, - { - /* filter as deny is first */ - .data = bss_lu, - .length = sizeof(bss_lu), - .result = 1, - .bsc_imsi_deny = "[0-9]*", - .bsc_imsi_allow = "[0-9]*", - .nat_imsi_deny = "[0-9]*", - .contype = NAT_CON_TYPE_LU, - }, - { - /* deny by nat rule */ - .data = bss_lu, - .length = sizeof(bss_lu), - .result = -3, - .bsc_imsi_deny = "000[0-9]*", - .nat_imsi_deny = "[0-9]*", - .contype = NAT_CON_TYPE_LU, - }, - { - /* deny by bsc rule */ - .data = bss_lu, - .length = sizeof(bss_lu), - .result = -2, - .bsc_imsi_deny = "[0-9]*", - .contype = NAT_CON_TYPE_LU, - }, - -}; - -static void test_cr_filter() -{ - int i, res, contype; - struct msgb *msg = msgb_alloc(4096, "test_cr_filter"); - struct bsc_nat_parsed *parsed; - struct bsc_nat_acc_lst *nat_lst, *bsc_lst; - struct bsc_nat_acc_lst_entry *nat_entry, *bsc_entry; - struct bsc_nat_reject_cause cause; - - struct bsc_nat *nat = bsc_nat_alloc(); - struct bsc_connection *bsc = bsc_connection_alloc(nat); - bsc->cfg = bsc_config_alloc(nat, "foo"); - bsc_config_add_lac(bsc->cfg, 1234); - bsc->cfg->acc_lst_name = "bsc"; - nat->acc_lst_name = "nat"; - - nat_lst = bsc_nat_acc_lst_get(nat, "nat"); - bsc_lst = bsc_nat_acc_lst_get(nat, "bsc"); - - bsc_entry = bsc_nat_acc_lst_entry_create(bsc_lst); - nat_entry = bsc_nat_acc_lst_entry_create(nat_lst); - - for (i = 0; i < ARRAY_SIZE(cr_filter); ++i) { - char *imsi; - msgb_reset(msg); - copy_to_msg(msg, cr_filter[i].data, cr_filter[i].length); - - if (gsm_parse_reg(nat_entry, &nat_entry->imsi_deny_re, &nat_entry->imsi_deny, - cr_filter[i].nat_imsi_deny ? 1 : 0, - &cr_filter[i].nat_imsi_deny) != 0) - abort(); - if (gsm_parse_reg(bsc_entry, &bsc_entry->imsi_allow_re, &bsc_entry->imsi_allow, - cr_filter[i].bsc_imsi_allow ? 1 : 0, - &cr_filter[i].bsc_imsi_allow) != 0) - abort(); - if (gsm_parse_reg(bsc_entry, &bsc_entry->imsi_deny_re, &bsc_entry->imsi_deny, - cr_filter[i].bsc_imsi_deny ? 1 : 0, - &cr_filter[i].bsc_imsi_deny) != 0) - abort(); - - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Failed to parse the message\n"); - abort(); - } - - memset(&cause, 0, sizeof(cause)); - res = bsc_nat_filter_sccp_cr(bsc, msg, parsed, &contype, &imsi, &cause); - if (res != cr_filter[i].result) { - printf("FAIL: Wrong result %d for test %d.\n", res, i); - abort(); - } - - if (contype != cr_filter[i].contype) { - printf("FAIL: Wrong contype %d for test %d.\n", res, contype); - abort(); - } - - talloc_steal(parsed, imsi); - talloc_free(parsed); - } - - msgb_free(msg); -} - -static void test_dt_filter() -{ - int i; - struct msgb *msg = msgb_alloc(4096, "test_dt_filter"); - struct bsc_nat_parsed *parsed; - struct bsc_nat_reject_cause cause; - - struct bsc_nat *nat = bsc_nat_alloc(); - struct bsc_connection *bsc = bsc_connection_alloc(nat); - struct nat_sccp_connection *con = talloc_zero(0, struct nat_sccp_connection); - - bsc->cfg = bsc_config_alloc(nat, "foo"); - bsc_config_add_lac(bsc->cfg, 23); - con->bsc = bsc; - - msgb_reset(msg); - copy_to_msg(msg, id_resp, ARRAY_SIZE(id_resp)); - - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Could not parse ID resp\n"); - abort(); - } - - if (parsed->bssap != BSSAP_MSG_DTAP) { - printf("FAIL: It should be dtap\n"); - abort(); - } - - /* gsm_type is actually the size of the dtap */ - if (parsed->gsm_type < msgb_l3len(msg) - 3) { - printf("FAIL: Not enough space for the content\n"); - abort(); - } - - memset(&cause, 0, sizeof(cause)); - if (bsc_nat_filter_dt(bsc, msg, con, parsed, &cause) != 1) { - printf("FAIL: Should have passed..\n"); - abort(); - } - - /* just some basic length checking... */ - for (i = ARRAY_SIZE(id_resp); i >= 0; --i) { - msgb_reset(msg); - copy_to_msg(msg, id_resp, ARRAY_SIZE(id_resp)); - - parsed = bsc_nat_parse(msg); - if (!parsed) - continue; - - con->imsi_checked = 0; - memset(&cause, 0, sizeof(cause)); - bsc_nat_filter_dt(bsc, msg, con, parsed, &cause); - } -} - -static void test_setup_rewrite() -{ - struct msgb *msg = msgb_alloc(4096, "test_dt_filter"); - struct msgb *out; - struct bsc_nat_parsed *parsed; - const char *imsi = "27408000001234"; - - struct bsc_nat *nat = bsc_nat_alloc(); - - /* a fake list */ - struct osmo_config_list entries; - struct osmo_config_entry entry; - - INIT_LLIST_HEAD(&entries.entry); - entry.mcc = "274"; - entry.mnc = "08"; - entry.option = "^0([1-9])"; - entry.text = "0049"; - llist_add_tail(&entry.list, &entries.entry); - bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); - - /* verify that nothing changed */ - msgb_reset(msg); - copy_to_msg(msg, cc_setup_international, ARRAY_SIZE(cc_setup_international)); - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Could not parse ID resp\n"); - abort(); - } - - out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); - if (msg != out) { - printf("FAIL: The message should not have been changed\n"); - abort(); - } - - verify_msg(out, cc_setup_international, ARRAY_SIZE(cc_setup_international)); - talloc_free(parsed); - - /* verify that something in the message changes */ - msgb_reset(msg); - copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national)); - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Could not parse ID resp\n"); - abort(); - } - - out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); - if (!out) { - printf("FAIL: A new message should be created.\n"); - abort(); - } - - if (msg == out) { - printf("FAIL: The message should have changed\n"); - abort(); - } - - verify_msg(out, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); - msgb_free(out); - - /* Make sure that a wildcard is matching */ - entry.mnc = "*"; - bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); - msg = msgb_alloc(4096, "test_dt_filter"); - copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national)); - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Could not parse ID resp\n"); - abort(); - } - - out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); - if (!out) { - printf("FAIL: A new message should be created.\n"); - abort(); - } - - if (msg == out) { - printf("FAIL: The message should have changed\n"); - abort(); - } - - verify_msg(out, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); - msgb_free(out); - - /* Make sure that a wildcard is matching */ - entry.mnc = "09"; - bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); - msg = msgb_alloc(4096, "test_dt_filter"); - copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national)); - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Could not parse ID resp\n"); - abort(); - } - - out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); - if (out != msg) { - printf("FAIL: The message should be unchanged.\n"); - abort(); - } - - verify_msg(out, cc_setup_national, ARRAY_SIZE(cc_setup_national)); - msgb_free(out); - - /* Now see what happens to an international number */ - entry.mnc = "*"; - entry.option = "^\\+[0-9][0-9]([1-9])"; - entry.text = "0036"; - bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); - msg = msgb_alloc(4096, "test_dt_filter"); - copy_to_msg(msg, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Could not parse ID resp %d\n", __LINE__); - abort(); - } - - out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); - if (!out) { - printf("FAIL: A new message should be created %d.\n", __LINE__); - abort(); - } - - if (msg == out) { - printf("FAIL: The message should have changed %d\n", __LINE__); - abort(); - } - - verify_msg(out, cc_setup_national_patched_patched, - ARRAY_SIZE(cc_setup_national_patched_patched)); - msgb_free(out); - - /* go from international back to national */ - entry.mnc = "*"; - entry.option = "^\\+([0-9])"; - entry.text = "36"; - bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); - msg = msgb_alloc(4096, "test_dt_filter"); - copy_to_msg(msg, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Could not parse ID resp %d\n", __LINE__); - abort(); - } - - out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); - if (!out) { - printf("FAIL: A new message should be created %d.\n", __LINE__); - abort(); - } - - if (msg == out) { - printf("FAIL: The message should have changed %d\n", __LINE__); - abort(); - } - - verify_msg(out, cc_setup_national_again, - ARRAY_SIZE(cc_setup_national_again)); - msgb_free(out); -} - -static void test_sms_smsc_rewrite() -{ - struct msgb *msg = msgb_alloc(4096, "SMSC rewrite"), *out; - struct bsc_nat_parsed *parsed; - const char *imsi = "515039900406700"; - - struct bsc_nat *nat = bsc_nat_alloc(); - - /* a fake list */ - struct osmo_config_list smsc_entries, dest_entries, clear_entries; - struct osmo_config_entry smsc_entry, dest_entry, clear_entry; - - INIT_LLIST_HEAD(&smsc_entries.entry); - INIT_LLIST_HEAD(&dest_entries.entry); - INIT_LLIST_HEAD(&clear_entries.entry); - smsc_entry.mcc = "^515039"; - smsc_entry.option = "639180000105()"; - smsc_entry.text = "6666666666667"; - llist_add_tail(&smsc_entry.list, &smsc_entries.entry); - dest_entry.mcc = "515"; - dest_entry.mnc = "03"; - dest_entry.option = "^0049"; - dest_entry.text = ""; - llist_add_tail(&dest_entry.list, &dest_entries.entry); - clear_entry.mcc = "^515039"; - clear_entry.option = "^0049"; - clear_entry.text = ""; - llist_add_tail(&clear_entry.list, &clear_entries.entry); - - bsc_nat_num_rewr_entry_adapt(nat, &nat->smsc_rewr, &smsc_entries); - bsc_nat_num_rewr_entry_adapt(nat, &nat->tpdest_match, &dest_entries); - bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, &clear_entries); - - printf("Testing SMSC rewriting.\n"); - - /* - * Check if the SMSC address is changed - */ - copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Could not parse SMS\n"); - abort(); - } - - out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); - if (out == msg) { - printf("FAIL: This should have changed.\n"); - abort(); - } - - verify_msg(out, smsc_rewrite_patched, ARRAY_SIZE(smsc_rewrite_patched)); - msgb_free(out); - - /* clear out the filter for SMSC */ - printf("Attempting to only rewrite the HDR\n"); - bsc_nat_num_rewr_entry_adapt(nat, &nat->smsc_rewr, NULL); - msg = msgb_alloc(4096, "SMSC rewrite"); - copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Could not parse SMS\n"); - abort(); - } - - out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); - if (out == msg) { - printf("FAIL: This should have changed.\n"); - abort(); - } - - verify_msg(out, smsc_rewrite_patched_hdr, ARRAY_SIZE(smsc_rewrite_patched_hdr)); - msgb_free(out); - - /* clear out the next filter */ - printf("Attempting to change nothing.\n"); - bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, NULL); - msg = msgb_alloc(4096, "SMSC rewrite"); - copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Could not parse SMS\n"); - abort(); - } - - out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); - if (out != msg) { - printf("FAIL: This should not have changed.\n"); - abort(); - } - - verify_msg(out, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); - msgb_free(out); -} - -static void test_sms_number_rewrite(void) -{ - struct msgb *msg, *out; - struct bsc_nat_parsed *parsed; - const char *imsi = "515039900406700"; - - struct bsc_nat *nat = bsc_nat_alloc(); - - /* a fake list */ - struct osmo_config_list num_entries, clear_entries; - struct osmo_config_entry num_entry, clear_entry; - - INIT_LLIST_HEAD(&num_entries.entry); - num_entry.mcc = "^515039"; - num_entry.option = "^0049()"; - num_entry.text = "0032"; - llist_add_tail(&num_entry.list, &num_entries.entry); - - bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_num_rewr, &num_entries); - - printf("Testing SMS TP-DA rewriting.\n"); - - /* - * Check if the SMSC address is changed - */ - msg = msgb_alloc(4096, "SMSC rewrite"); - copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Could not parse SMS\n"); - abort(); - } - - out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); - if (out == msg) { - printf("FAIL: This should have changed.\n"); - abort(); - } - - verify_msg(out, smsc_rewrite_num_patched, - ARRAY_SIZE(smsc_rewrite_num_patched)); - msgb_free(out); - - /* - * Now with TP-SRR rewriting enabled - */ - INIT_LLIST_HEAD(&clear_entries.entry); - clear_entry.mcc = "^515039"; - clear_entry.option = ""; - clear_entry.text = ""; - llist_add_tail(&clear_entry.list, &clear_entries.entry); - bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, &clear_entries); - - msg = msgb_alloc(4096, "SMSC rewrite"); - copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); - parsed = bsc_nat_parse(msg); - if (!parsed) { - printf("FAIL: Could not parse SMS\n"); - abort(); - } - - out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); - if (out == msg) { - printf("FAIL: This should have changed.\n"); - abort(); - } - - verify_msg(out, smsc_rewrite_num_patched_tp_srr, - ARRAY_SIZE(smsc_rewrite_num_patched_tp_srr)); - msgb_free(out); -} - -static void test_barr_list_parsing(void) -{ - int rc; - int cm, lu; - struct rb_node *node; - struct rb_root root = RB_ROOT; - struct osmo_config_list *lst = osmo_config_list_parse(NULL, "barr.cfg"); - if (lst == NULL) - abort(); - - rc = bsc_nat_barr_adapt(NULL, &root, lst); - if (rc != 0) - abort(); - talloc_free(lst); - - - for (node = rb_first(&root); node; node = rb_next(node)) { - struct bsc_nat_barr_entry *entry; - entry = rb_entry(node, struct bsc_nat_barr_entry, node); - printf("IMSI: %s CM: %d LU: %d\n", entry->imsi, - entry->cm_reject_cause, entry->lu_reject_cause); - } - - /* do the look up now.. */ - rc = bsc_nat_barr_find(&root, "12123119", &cm, &lu); - if (!rc) { - printf("Failed to find the IMSI.\n"); - abort(); - } - - if (cm != 3 || lu != 4) { - printf("Found CM(%d) and LU(%d)\n", cm, lu); - abort(); - } - - /* empty and check that it is empty */ - bsc_nat_barr_adapt(NULL, &root, NULL); - if (!RB_EMPTY_ROOT(&root)) { - printf("Failed to empty the list.\n"); - abort(); - } - - /* check that dup results in an error */ - lst = osmo_config_list_parse(NULL, "barr_dup.cfg"); - if (lst == NULL) { - printf("Failed to parse list with dups\n"); - abort(); - } - - rc = bsc_nat_barr_adapt(NULL, &root, lst); - if (rc != -1) { - printf("It should have failed due dup\n"); - abort(); - } - talloc_free(lst); - - /* dump for reference */ - for (node = rb_first(&root); node; node = rb_next(node)) { - struct bsc_nat_barr_entry *entry; - entry = rb_entry(node, struct bsc_nat_barr_entry, node); - printf("IMSI: %s CM: %d LU: %d\n", entry->imsi, - entry->cm_reject_cause, entry->lu_reject_cause); - - } -} - -static void test_nat_extract_lac() -{ - int res; - struct bsc_connection *bsc; - struct bsc_nat *nat; - struct nat_sccp_connection con; - struct bsc_nat_parsed *parsed; - struct msgb *msg = msgb_alloc(4096, "test-message"); - - printf("Testing LAC extraction from SCCP CR\n"); - - /* initialize the testcase */ - nat = bsc_nat_alloc(); - bsc = bsc_connection_alloc(nat); - bsc->cfg = bsc_config_alloc(nat, "foo"); - - memset(&con, 0, sizeof(con)); - con.bsc = bsc; - - /* create the SCCP CR */ - msg->l2h = msgb_put(msg, ARRAY_SIZE(bssmap_cr)); - memcpy(msg->l2h, bssmap_cr, ARRAY_SIZE(bssmap_cr)); - - /* parse it and pass it on */ - parsed = bsc_nat_parse(msg); - res = bsc_nat_extract_lac(bsc, &con, parsed, msg); - OSMO_ASSERT(res == 0); - - /* verify the LAC */ - OSMO_ASSERT(con.lac == 8210); - OSMO_ASSERT(con.ci == 50000); -} - -int main(int argc, char **argv) -{ - sccp_set_log_area(DSCCP); - osmo_init_logging(&log_info); - - test_filter(); - test_contrack(); - test_paging(); - test_mgcp_ass_tracking(); - test_mgcp_find(); - test_mgcp_rewrite(); - test_mgcp_parse(); - test_cr_filter(); - test_dt_filter(); - test_setup_rewrite(); - test_sms_smsc_rewrite(); - test_sms_number_rewrite(); - test_mgcp_allocations(); - test_barr_list_parsing(); - test_nat_extract_lac(); - - printf("Testing execution completed.\n"); - return 0; -} - -/* stub */ -void bsc_nat_send_mgcp_to_msc(struct bsc_nat *nat, struct msgb *msg) -{ - abort(); -} diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.ok b/openbsc/tests/bsc-nat/bsc_nat_test.ok deleted file mode 100644 index ab04f4273..000000000 --- a/openbsc/tests/bsc-nat/bsc_nat_test.ok +++ /dev/null @@ -1,39 +0,0 @@ -Testing BSS Filtering. -Going to test item: 0 -Going to test item: 1 -Going to test item: 2 -Going to test item: 3 -Going to test item: 4 -Going to test item: 5 -Going to test item: 6 -Going to test item: 7 -Going to test item: 8 -Going to test item: 9 -Going to test item: 10 -Going to test item: 11 -Going to test item: 12 -Testing connection tracking. -Testing paging by lac. -Testing MGCP. -Testing finding of a BSC Connection -Testing rewriting MGCP messages. -Testing MGCP response parsing. -Testing SMSC rewriting. -Attempting to only rewrite the HDR -Attempting to change nothing. -Testing SMS TP-DA rewriting. -IMSI: 12123115 CM: 3 LU: 4 -IMSI: 12123116 CM: 3 LU: 4 -IMSI: 12123117 CM: 3 LU: 4 -IMSI: 12123118 CM: 3 LU: 4 -IMSI: 12123119 CM: 3 LU: 4 -IMSI: 12123120 CM: 3 LU: 4 -IMSI: 12123123 CM: 3 LU: 1 -IMSI: 12123124 CM: 3 LU: 2 -IMSI: 12123125 CM: 3 LU: 3 -IMSI: 12123126 CM: 3 LU: 4 -IMSI: 12123127 CM: 3 LU: 5 -IMSI: 12123128 CM: 3 LU: 6 -IMSI: 12123124 CM: 3 LU: 2 -Testing LAC extraction from SCCP CR -Testing execution completed. diff --git a/openbsc/tests/channel/Makefile.am b/openbsc/tests/channel/Makefile.am deleted file mode 100644 index 89f015a8a..000000000 --- a/openbsc/tests/channel/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) - -EXTRA_DIST = channel_test.ok - -noinst_PROGRAMS = channel_test - -channel_test_SOURCES = channel_test.c -channel_test_LDADD = -ldl $(LIBOSMOCORE_LIBS) \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libmsc/libmsc.a -ldbi $(LIBOSMOGSM_LIBS) diff --git a/openbsc/tests/channel/channel_test.c b/openbsc/tests/channel/channel_test.c deleted file mode 100644 index ab0d9eb0b..000000000 --- a/openbsc/tests/channel/channel_test.c +++ /dev/null @@ -1,96 +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 General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include - -#include -#include - -#include -#include -#include - -static int s_end = 0; -static struct gsm_subscriber_connection s_conn; - -/* our handler */ -static int subscr_cb(unsigned int hook, unsigned int event, struct msgb *msg, void *data, void *param) -{ - assert(hook == 101); - assert(event == 200); - assert(msg == (void*)0x1323L); - assert(data == &s_conn); - assert(param == (void*)0x2342L); - printf("Reached, didn't crash, test passed\n"); - s_end = true; - return 0; -} - -/* mock object for testing, directly invoke the cb... maybe later through the timer */ -void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscriber, int type, gsm_cbfn *cbfn, void *data) -{ - cbfn(101, 200, (void*)0x1323L, &s_conn, data); -} - - -int main(int argc, char **argv) -{ - struct gsm_network *network; - struct gsm_bts *bts; - - osmo_init_logging(&log_info); - - printf("Testing the gsm_subscriber chan logic\n"); - - /* Create a dummy network */ - network = gsm_network_init(1, 1, NULL); - if (!network) - exit(1); - bts = gsm_bts_alloc(network); - bts->location_area_code = 23; - - /* Create a dummy subscriber */ - struct gsm_subscriber *subscr = subscr_alloc(); - subscr->lac = 23; - subscr->net = network; - - /* Ask for a channel... */ - subscr_get_channel(subscr, RSL_CHANNEED_TCH_F, subscr_cb, (void*)0x2342L); - - while (!s_end) { - osmo_select_main(0); - } - - return EXIT_SUCCESS; -} - -void _abis_nm_sendmsg() {} -void sms_alloc() {} -void gsm_net_update_ctype(struct gsm_network *network) {} -void gsm48_secure_channel() {} -void paging_request_stop() {} -void vty_out() {} -void* connection_for_subscr(void) { abort(); return NULL; } - - -struct tlv_definition nm_att_tlvdef; - diff --git a/openbsc/tests/channel/channel_test.ok b/openbsc/tests/channel/channel_test.ok deleted file mode 100644 index 7976aee0f..000000000 --- a/openbsc/tests/channel/channel_test.ok +++ /dev/null @@ -1,2 +0,0 @@ -Testing the gsm_subscriber chan logic -Reached, didn't crash, test passed diff --git a/openbsc/tests/db/Makefile.am b/openbsc/tests/db/Makefile.am deleted file mode 100644 index 6f735d04b..000000000 --- a/openbsc/tests/db/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBSMPP34_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(COVERAGE_LDFLAGS) - -EXTRA_DIST = db_test.ok - -noinst_PROGRAMS = db_test - -db_test_SOURCES = db_test.c -db_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libmsc/libmsc.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) \ - $(LIBOSMOGSM_LIBS) $(LIBSMPP34_LIBS) $(LIBOSMOVTY_LIBS) -ldl -ldbi - diff --git a/openbsc/tests/db/db_test.c b/openbsc/tests/db/db_test.c deleted file mode 100644 index c0ee618e7..000000000 --- a/openbsc/tests/db/db_test.c +++ /dev/null @@ -1,118 +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 General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include - -#include -#include -#include - -static struct gsm_network dummy_net; - -#define SUBSCR_PUT(sub) \ - sub->net = &dummy_net; \ - subscr_put(sub); - -#define COMPARE(original, copy) \ - if (original->id != copy->id) \ - printf("Ids do not match in %s:%d %llu %llu\n", \ - __FUNCTION__, __LINE__, original->id, copy->id); \ - if (original->lac != copy->lac) \ - printf("LAC do not match in %s:%d %d %d\n", \ - __FUNCTION__, __LINE__, original->lac, copy->lac); \ - if (original->authorized != copy->authorized) \ - printf("Authorize do not match in %s:%d %d %d\n", \ - __FUNCTION__, __LINE__, original->authorized, \ - copy->authorized); \ - if (strcmp(original->imsi, copy->imsi) != 0) \ - printf("IMSIs do not match in %s:%d '%s' '%s'\n", \ - __FUNCTION__, __LINE__, original->imsi, copy->imsi); \ - if (original->tmsi != copy->tmsi) \ - printf("TMSIs do not match in %s:%d '%u' '%u'\n", \ - __FUNCTION__, __LINE__, original->tmsi, copy->tmsi); \ - if (strcmp(original->name, copy->name) != 0) \ - printf("names do not match in %s:%d '%s' '%s'\n", \ - __FUNCTION__, __LINE__, original->name, copy->name); \ - if (strcmp(original->extension, copy->extension) != 0) \ - printf("names do not match in %s:%d '%s' '%s'\n", \ - __FUNCTION__, __LINE__, original->extension, copy->extension); \ - -int main() -{ - printf("Testing subscriber database code.\n"); - osmo_init_logging(&log_info); - - if (db_init("hlr.sqlite3")) { - printf("DB: Failed to init database. Please check the option settings.\n"); - return 1; - } - printf("DB: Database initialized.\n"); - - if (db_prepare()) { - printf("DB: Failed to prepare database.\n"); - return 1; - } - printf("DB: Database prepared.\n"); - - struct gsm_subscriber *alice = NULL; - struct gsm_subscriber *alice_db; - - char *alice_imsi = "3243245432345"; - alice = db_create_subscriber(NULL, alice_imsi); - db_sync_subscriber(alice); - alice_db = db_get_subscriber(NULL, GSM_SUBSCRIBER_IMSI, alice->imsi); - COMPARE(alice, alice_db); - SUBSCR_PUT(alice_db); - SUBSCR_PUT(alice); - - alice_imsi = "3693245423445"; - alice = db_create_subscriber(NULL, alice_imsi); - db_subscriber_assoc_imei(alice, "1234567890"); - db_subscriber_alloc_tmsi(alice); - alice->lac=42; - db_sync_subscriber(alice); - alice_db = db_get_subscriber(NULL, GSM_SUBSCRIBER_IMSI, alice_imsi); - COMPARE(alice, alice_db); - SUBSCR_PUT(alice); - SUBSCR_PUT(alice_db); - - alice_imsi = "9993245423445"; - alice = db_create_subscriber(NULL, alice_imsi); - db_subscriber_alloc_tmsi(alice); - alice->lac=42; - db_sync_subscriber(alice); - db_subscriber_assoc_imei(alice, "1234567890"); - db_subscriber_assoc_imei(alice, "6543560920"); - alice_db = db_get_subscriber(NULL, GSM_SUBSCRIBER_IMSI, alice_imsi); - COMPARE(alice, alice_db); - SUBSCR_PUT(alice); - SUBSCR_PUT(alice_db); - - db_fini(); - - printf("Done\n"); - return 0; -} - -/* stubs */ -void vty_out() {} diff --git a/openbsc/tests/db/db_test.ok b/openbsc/tests/db/db_test.ok deleted file mode 100644 index 2632a8c8a..000000000 --- a/openbsc/tests/db/db_test.ok +++ /dev/null @@ -1,4 +0,0 @@ -Testing subscriber database code. -DB: Database initialized. -DB: Database prepared. -Done diff --git a/openbsc/tests/gprs/Makefile.am b/openbsc/tests/gprs/Makefile.am deleted file mode 100644 index 904d567c1..000000000 --- a/openbsc/tests/gprs/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) - -EXTRA_DIST = gprs_test.ok - -noinst_PROGRAMS = gprs_test - -gprs_test_SOURCES = gprs_test.c diff --git a/openbsc/tests/gprs/gprs_test.c b/openbsc/tests/gprs/gprs_test.c deleted file mode 100644 index 7b0e64113..000000000 --- a/openbsc/tests/gprs/gprs_test.c +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include - -#include - -#define ASSERT_FALSE(x) if (x) { printf("Should have returned false.\n"); abort(); } -#define ASSERT_TRUE(x) if (!x) { printf("Should have returned true.\n"); abort(); } - -/** - * GSM 04.64 8.4.2 Receipt of unacknowledged information - */ -static int nu_is_retransmission(uint16_t nu, uint16_t vur) -{ - int ret = gprs_llc_is_retransmit(nu, vur); - printf("N(U) = %d, V(UR) = %d => %s\n", nu, vur, - ret == 1 ? "retransmit" : "new"); - return ret; -} - -static void test_8_4_2() -{ - printf("Testing gprs_llc_is_retransmit.\n"); - - ASSERT_FALSE(nu_is_retransmission(0, 0)); - ASSERT_TRUE (nu_is_retransmission(0, 1)); - - /* expect 1... check for retransmissions */ - ASSERT_TRUE (nu_is_retransmission(0, 1)); - ASSERT_TRUE (nu_is_retransmission(511, 1)); - ASSERT_TRUE (nu_is_retransmission(483, 1)); - ASSERT_TRUE (nu_is_retransmission(482, 1)); - ASSERT_FALSE(nu_is_retransmission(481, 1)); - - /* expect 511... check for retransmissions */ - ASSERT_FALSE(nu_is_retransmission(0, 240)); // ahead - ASSERT_FALSE(nu_is_retransmission(0, 511)); // ahead - ASSERT_FALSE(nu_is_retransmission(1, 511)); // ahead - ASSERT_FALSE(nu_is_retransmission(511, 511)); // same - ASSERT_TRUE (nu_is_retransmission(510, 511)); // behind - ASSERT_TRUE (nu_is_retransmission(481, 511)); // behind - ASSERT_FALSE(nu_is_retransmission(479, 511)); // wrapped -} - -int main(int argc, char **argv) -{ - test_8_4_2(); - - printf("Done.\n"); - return EXIT_SUCCESS; -} diff --git a/openbsc/tests/gprs/gprs_test.ok b/openbsc/tests/gprs/gprs_test.ok deleted file mode 100644 index 39d37c120..000000000 --- a/openbsc/tests/gprs/gprs_test.ok +++ /dev/null @@ -1,16 +0,0 @@ -Testing gprs_llc_is_retransmit. -N(U) = 0, V(UR) = 0 => new -N(U) = 0, V(UR) = 1 => retransmit -N(U) = 0, V(UR) = 1 => retransmit -N(U) = 511, V(UR) = 1 => retransmit -N(U) = 483, V(UR) = 1 => retransmit -N(U) = 482, V(UR) = 1 => retransmit -N(U) = 481, V(UR) = 1 => new -N(U) = 0, V(UR) = 240 => new -N(U) = 0, V(UR) = 511 => new -N(U) = 1, V(UR) = 511 => new -N(U) = 511, V(UR) = 511 => new -N(U) = 510, V(UR) = 511 => retransmit -N(U) = 481, V(UR) = 511 => retransmit -N(U) = 479, V(UR) = 511 => new -Done. diff --git a/openbsc/tests/gsm0408/Makefile.am b/openbsc/tests/gsm0408/Makefile.am deleted file mode 100644 index b000c0802..000000000 --- a/openbsc/tests/gsm0408/Makefile.am +++ /dev/null @@ -1,11 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) -noinst_PROGRAMS = gsm0408_test - -EXTRA_DIST = gsm0408_test.ok - -gsm0408_test_SOURCES = gsm0408_test.c -gsm0408_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libmsc/libmsc.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -ldbi diff --git a/openbsc/tests/gsm0408/gsm0408_test.c b/openbsc/tests/gsm0408/gsm0408_test.c deleted file mode 100644 index 60a151faf..000000000 --- a/openbsc/tests/gsm0408/gsm0408_test.c +++ /dev/null @@ -1,104 +0,0 @@ -/* simple test for the gsm0408 formatting functions */ -/* - * (C) 2008 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include - -#include -#include -#include - -#define COMPARE(result, op, value) \ - if (!((result) op (value))) {\ - fprintf(stderr, "Compare failed. Was %x should be %x in %s:%d\n",result, value, __FILE__, __LINE__); \ - exit(-1); \ - } - -#define COMPARE_STR(result, value) \ - if (strcmp(result, value) != 0) { \ - fprintf(stderr, "Compare failed. Was %s should be %s in %s:%d\n",result, value, __FILE__, __LINE__); \ - exit(-1); \ - } - -/* - * Test Location Area Identifier formatting. Table 10.5.3 of 04.08 - */ -static void test_location_area_identifier(void) -{ - struct gsm48_loc_area_id lai48; - - printf("Testing test location area identifier\n"); - - /* - * Test the default/test setup. Coming from - * bsc_hack.c dumps - */ - gsm48_generate_lai(&lai48, 1, 1, 1); - COMPARE(lai48.digits[0], ==, 0x00); - COMPARE(lai48.digits[1], ==, 0xF1); - COMPARE(lai48.digits[2], ==, 0x10); - COMPARE(lai48.lac, ==, htons(0x0001)); - - gsm48_generate_lai(&lai48, 602, 1, 15); - COMPARE(lai48.digits[0], ==, 0x06); - COMPARE(lai48.digits[1], ==, 0xF2); - COMPARE(lai48.digits[2], ==, 0x10); - COMPARE(lai48.lac, ==, htons(0x000f)); -} - -static void test_mi_functionality(void) -{ - const char *imsi_odd = "987654321098763"; - const char *imsi_even = "9876543210987654"; - const uint32_t tmsi = 0xfabeacd0; - uint8_t mi[128]; - unsigned int mi_len; - char mi_parsed[GSM48_MI_SIZE]; - - printf("Testing parsing and generating TMSI/IMSI\n"); - - /* tmsi code */ - mi_len = gsm48_generate_mid_from_tmsi(mi, tmsi); - gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len - 2); - COMPARE((uint32_t)strtoul(mi_parsed, NULL, 10), ==, tmsi); - - /* imsi code */ - mi_len = gsm48_generate_mid_from_imsi(mi, imsi_odd); - gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len -2); - printf("hex: %s\n", osmo_hexdump(mi, mi_len)); - COMPARE_STR(mi_parsed, imsi_odd); - - mi_len = gsm48_generate_mid_from_imsi(mi, imsi_even); - gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len -2); - printf("hex: %s\n", osmo_hexdump(mi, mi_len)); - COMPARE_STR(mi_parsed, imsi_even); -} - -int main(int argc, char **argv) -{ - test_location_area_identifier(); - test_mi_functionality(); - - printf("Done.\n"); - return EXIT_SUCCESS; -} diff --git a/openbsc/tests/gsm0408/gsm0408_test.ok b/openbsc/tests/gsm0408/gsm0408_test.ok deleted file mode 100644 index 52c601e2d..000000000 --- a/openbsc/tests/gsm0408/gsm0408_test.ok +++ /dev/null @@ -1,5 +0,0 @@ -Testing test location area identifier -Testing parsing and generating TMSI/IMSI -hex: 17 08 99 78 56 34 12 90 78 36 -hex: 17 09 91 78 56 34 12 90 78 56 f4 -Done. diff --git a/openbsc/tests/mgcp/Makefile.am b/openbsc/tests/mgcp/Makefile.am deleted file mode 100644 index 8c365b416..000000000 --- a/openbsc/tests/mgcp/Makefile.am +++ /dev/null @@ -1,14 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(COVERAGE_LDFLAGS) - -EXTRA_DIST = mgcp_test.ok - -noinst_PROGRAMS = mgcp_test - -mgcp_test_SOURCES = mgcp_test.c - -mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libmgcp/libmgcp.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) -lrt $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c deleted file mode 100644 index 6307300f1..000000000 --- a/openbsc/tests/mgcp/mgcp_test.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * (C) 2011-2012 by Holger Hans Peter Freyther - * (C) 2011-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 - -#define AUEP1 "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n" -#define AUEP1_RET "200 158663169 OK\r\n" -#define AUEP2 "AUEP 18983213 ds/e1-2/1@172.16.6.66 MGCP 1.0\r\n" -#define AUEP2_RET "500 18983213 FAIL\r\n" -#define EMPTY "\r\n" -#define EMPTY_RET NULL -#define SHORT "CRCX \r\n" -#define SHORT_RET "510 000000 FAIL\r\n" - -#define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@172.16.6.66 MGCP 1.0\r\n" -#define MDCX_ERR_RET "510 18983213 FAIL\r\n" -#define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n" -#define MDCX_RET "400 18983214 FAIL\r\n" -#define MDCX3 "MDCX 18983215 1@mgw MGCP 1.0\r\n" -#define MDCX3_RET "200 18983215 OK\r\n" \ - "I: 1\n" \ - "\n" \ - "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ - "c=IN IP4 0.0.0.0\r\n" \ - "t=0 0\r\n" \ - "m=audio 0 RTP/AVP 126\r\n" \ - "a=rtpmap:126 AMR/8000\r\n" - -#define SHORT2 "CRCX 1" -#define SHORT2_RET "510 000000 FAIL\r\n" -#define SHORT3 "CRCX 1 1@mgw" -#define SHORT4 "CRCX 1 1@mgw MGCP" -#define SHORT5 "CRCX 1 1@mgw MGCP 1.0" - -#define CRCX "CRCX 2 1@mgw MGCP 1.0\r\n" \ - "M: sendrecv\r\n" \ - "C: 2\r\n" \ - "\r\n" \ - "v=0\r\n" \ - "c=IN IP4 123.12.12.123\r\n" \ - "m=audio 5904 RTP/AVP 97\r\n" \ - "a=rtpmap:97 GSM-EFR/8000\r\n" - -#define CRCX_RET "200 2 OK\r\n" \ - "I: 1\n" \ - "\n" \ - "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ - "c=IN IP4 0.0.0.0\r\n" \ - "t=0 0\r\n" \ - "m=audio 0 RTP/AVP 126\r\n" \ - "a=rtpmap:126 AMR/8000\r\n" - - -#define CRCX_ZYN "CRCX 2 1@mgw MGCP 1.0\r" \ - "M: sendrecv\r" \ - "C: 2\r\r" \ - "v=0\r" \ - "c=IN IP4 123.12.12.123\r" \ - "m=audio 5904 RTP/AVP 97\r" \ - "a=rtpmap:97 GSM-EFR/8000\r" - -#define CRCX_ZYN_RET "200 2 OK\r\n" \ - "I: 2\n" \ - "\n" \ - "v=0\r\n" \ - "o=- 2 23 IN IP4 0.0.0.0\r\n" \ - "c=IN IP4 0.0.0.0\r\n" \ - "t=0 0\r\n" \ - "m=audio 0 RTP/AVP 126\r\n" \ - "a=rtpmap:126 AMR/8000\r\n" - -#define DLCX "DLCX 7 1@mgw MGCP 1.0\r\n" \ - "C: 2\r\n" - -#define DLCX_RET "250 7 OK\r\n" \ - "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" - -#define RQNT "RQNT 186908780 1@mgw MGCP 1.0\r\n" \ - "X: B244F267488\r\n" \ - "S: D/9\r\n" - -#define RQNT2 "RQNT 186908781 1@mgw MGCP 1.0\r\n" \ - "X: ADD4F26746F\r\n" \ - "R: D/[0-9#*](N), G/ft, fxr/t38\r\n" - -#define RQNT1_RET "200 186908780 OK\r\n" -#define RQNT2_RET "200 186908781 OK\r\n" - -struct mgcp_test { - const char *name; - const char *req; - const char *exp_resp; -}; - -static const struct mgcp_test tests[] = { - { "AUEP1", AUEP1, AUEP1_RET }, - { "AUEP2", AUEP2, AUEP2_RET }, - { "MDCX1", MDCX_WRONG_EP, MDCX_ERR_RET }, - { "MDCX2", MDCX_UNALLOCATED, MDCX_RET }, - { "CRCX", CRCX, CRCX_RET }, - { "MDCX3", MDCX3, MDCX3_RET }, - { "DLCX", DLCX, DLCX_RET }, - { "CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET }, - { "EMPTY", EMPTY, EMPTY_RET }, - { "SHORT1", SHORT, SHORT_RET }, - { "SHORT2", SHORT2, SHORT2_RET }, - { "SHORT3", SHORT3, SHORT2_RET }, - { "SHORT4", SHORT4, SHORT2_RET }, - { "RQNT1", RQNT, RQNT1_RET }, - { "RQNT2", RQNT2, RQNT2_RET }, - { "DLCX", DLCX, DLCX_RET }, -}; - -static const struct mgcp_test retransmit[] = { - { "CRCX", CRCX, CRCX_RET }, - { "RQNT1", RQNT, RQNT1_RET }, - { "RQNT2", RQNT2, RQNT2_RET }, - { "MDCX3", MDCX3, MDCX3_RET }, - { "DLCX", DLCX, DLCX_RET }, -}; - -static struct msgb *create_msg(const char *str) -{ - struct msgb *msg; - - msg = msgb_alloc_headroom(4096, 128, "MGCP msg"); - int len = sprintf((char *)msg->data, "%s", str); - msg->l2h = msgb_put(msg, len); - return msg; -} - -static void test_messages(void) -{ - struct mgcp_config *cfg; - int i; - - cfg = mgcp_config_alloc(); - - cfg->trunk.number_endpoints = 64; - mgcp_endpoints_allocate(&cfg->trunk); - - mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); - - for (i = 0; i < ARRAY_SIZE(tests); i++) { - const struct mgcp_test *t = &tests[i]; - struct msgb *inp; - struct msgb *msg; - - printf("Testing %s\n", t->name); - - inp = create_msg(t->req); - msg = mgcp_handle_message(cfg, inp); - msgb_free(inp); - if (!t->exp_resp) { - if (msg) - printf("%s failed '%s'\n", t->name, (char *) msg->data); - } else if (strcmp((char *) msg->data, t->exp_resp) != 0) - printf("%s failed '%s'\n", t->name, (char *) msg->data); - msgb_free(msg); - } - - talloc_free(cfg); -} - -static void test_retransmission(void) -{ - struct mgcp_config *cfg; - int i; - - cfg = mgcp_config_alloc(); - - cfg->trunk.number_endpoints = 64; - mgcp_endpoints_allocate(&cfg->trunk); - - mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); - - for (i = 0; i < ARRAY_SIZE(retransmit); i++) { - const struct mgcp_test *t = &retransmit[i]; - struct msgb *inp; - struct msgb *msg; - - printf("Testing %s\n", t->name); - - inp = create_msg(t->req); - msg = mgcp_handle_message(cfg, inp); - msgb_free(inp); - if (strcmp((char *) msg->data, t->exp_resp) != 0) - printf("%s failed '%s'\n", t->name, (char *) msg->data); - msgb_free(msg); - - /* Retransmit... */ - printf("Re-transmitting %s\n", t->name); - inp = create_msg(t->req); - msg = mgcp_handle_message(cfg, inp); - msgb_free(inp); - if (strcmp((char *) msg->data, t->exp_resp) != 0) - printf("%s failed '%s'\n", t->name, (char *) msg->data); - msgb_free(msg); - } - - talloc_free(cfg); -} - -static int rqnt_cb(struct mgcp_endpoint *endp, char _tone) -{ - ptrdiff_t tone = _tone; - endp->cfg->data = (void *) tone; - return 0; -} - -static void test_rqnt_cb(void) -{ - struct mgcp_config *cfg; - struct msgb *inp, *msg; - - cfg = mgcp_config_alloc(); - cfg->rqnt_cb = rqnt_cb; - - cfg->trunk.number_endpoints = 64; - mgcp_endpoints_allocate(&cfg->trunk); - - mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); - - inp = create_msg(CRCX); - msgb_free(mgcp_handle_message(cfg, inp)); - msgb_free(inp); - - /* send the RQNT and check for the CB */ - inp = create_msg(RQNT); - msg = mgcp_handle_message(cfg, inp); - if (strncmp((const char *) msg->l2h, "200", 3) != 0) { - printf("FAILED: message is not 200. '%s'\n", msg->l2h); - abort(); - } - - if (cfg->data != (void *) '9') { - printf("FAILED: callback not called: %p\n", cfg->data); - abort(); - } - - msgb_free(msg); - msgb_free(inp); - - inp = create_msg(DLCX); - msgb_free(mgcp_handle_message(cfg, inp)); - msgb_free(inp); - talloc_free(cfg); -} - -struct pl_test { - int cycles; - uint16_t base_seq; - uint16_t max_seq; - uint32_t packets; - - uint32_t expected; - int loss; -}; - -static const struct pl_test pl_test_dat[] = { - /* basic.. just one package */ - { .cycles = 0, .base_seq = 0, .max_seq = 0, .packets = 1, .expected = 1, .loss = 0}, - /* some packages and a bit of loss */ - { .cycles = 0, .base_seq = 0, .max_seq = 100, .packets = 100, .expected = 101, .loss = 1}, - /* wrap around */ - { .cycles = 1<<16, .base_seq = 0xffff, .max_seq = 2, .packets = 4, .expected = 4, .loss = 0}, - /* min loss */ - { .cycles = 0, .base_seq = 0, .max_seq = 0, .packets = UINT_MAX, .expected = 1, .loss = INT_MIN }, - /* max loss, with wrap around on expected max */ - { .cycles = INT_MAX, .base_seq = 0, .max_seq = UINT16_MAX, .packets = 0, .expected = ((uint32_t)(INT_MAX) + UINT16_MAX + 1), .loss = INT_MAX }, -}; - -static void test_packet_loss_calc(void) -{ - int i; - printf("Testing packet loss calculation.\n"); - - for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) { - uint32_t expected; - int loss; - struct mgcp_rtp_state state; - struct mgcp_rtp_end rtp; - memset(&state, 0, sizeof(state)); - memset(&rtp, 0, sizeof(rtp)); - - state.initialized = 1; - state.base_seq = pl_test_dat[i].base_seq; - state.max_seq = pl_test_dat[i].max_seq; - state.cycles = pl_test_dat[i].cycles; - - rtp.packets = pl_test_dat[i].packets; - mgcp_state_calc_loss(&state, &rtp, &expected, &loss); - - if (loss != pl_test_dat[i].loss || expected != pl_test_dat[i].expected) { - printf("FAIL: Wrong exp/loss at idx(%d) Loss(%d vs. %d) Exp(%u vs. %u)\n", - i, loss, pl_test_dat[i].loss, - expected, pl_test_dat[i].expected); - } - } -} - -static void test_mgcp_stats(void) -{ - printf("Testing stat parsing\n"); - - uint32_t bps, bos, pr, _or, jitter; - struct msgb *msg; - int loss; - int rc; - - msg = create_msg(DLCX_RET); - rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter); - printf("Parsing result: %d\n", rc); - if (bps != 0 || bos != 0 || pr != 0 || _or != 0 || loss != 0 || jitter != 0) - printf("FAIL: Parsing failed1.\n"); - msgb_free(msg); - - msg = create_msg("250 7 OK\r\nP: PS=10, OS=20, PR=30, OR=40, PL=-3, JI=40\r\n"); - rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter); - printf("Parsing result: %d\n", rc); - if (bps != 10 || bos != 20 || pr != 30 || _or != 40 || loss != -3 || jitter != 40) - printf("FAIL: Parsing failed2.\n"); - msgb_free(msg); -} - -int main(int argc, char **argv) -{ - osmo_init_logging(&log_info); - - test_messages(); - test_retransmission(); - test_packet_loss_calc(); - test_rqnt_cb(); - test_mgcp_stats(); - - printf("Done\n"); - return EXIT_SUCCESS; -} diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok deleted file mode 100644 index 8711e38f2..000000000 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ /dev/null @@ -1,31 +0,0 @@ -Testing AUEP1 -Testing AUEP2 -Testing MDCX1 -Testing MDCX2 -Testing CRCX -Testing MDCX3 -Testing DLCX -Testing CRCX_ZYN -Testing EMPTY -Testing SHORT1 -Testing SHORT2 -Testing SHORT3 -Testing SHORT4 -Testing RQNT1 -Testing RQNT2 -Testing DLCX -Testing CRCX -Re-transmitting CRCX -Testing RQNT1 -Re-transmitting RQNT1 -Testing RQNT2 -Re-transmitting RQNT2 -Testing MDCX3 -Re-transmitting MDCX3 -Testing DLCX -Re-transmitting DLCX -Testing packet loss calculation. -Testing stat parsing -Parsing result: 0 -Parsing result: 0 -Done diff --git a/openbsc/tests/si/Makefile.am b/openbsc/tests/si/Makefile.am deleted file mode 100644 index 795bd3081..000000000 --- a/openbsc/tests/si/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) - -EXTRA_DIST = si_test.ok - -noinst_PROGRAMS = si_test - -si_test_SOURCES = si_test.c - -si_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) -lrt $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) diff --git a/openbsc/tests/si/si_test.c b/openbsc/tests/si/si_test.c deleted file mode 100644 index 16f6ea39d..000000000 --- a/openbsc/tests/si/si_test.c +++ /dev/null @@ -1,156 +0,0 @@ - - -#include -#include -#include - -#include - -#include - -#define DBG(...) - -#define VERIFY(res, cmp, wanted) \ - if (!(res cmp wanted)) { \ - printf("ASSERT failed: %s:%d Wanted: %d %s %d\n", \ - __FILE__, __LINE__, res, # cmp, wanted); \ - } - - -static int freqs1[] = { - 12, 70, 121, 190, 250, 320, 401, 475, 520, 574, 634, 700, 764, 830, 905, 980 -}; - -static int freqs2[] = { - 402, 460, 1, 67, 131, 197, 272, 347, -}; - -static int freqs3[] = { - 68, 128, 198, 279, 353, 398, 452, - -}; - -static int w_out[] = { - 122, 2, 69, 204, 75, 66, 60, 70, 83, 3, 24, 67, 54, 64, 70, 9, -}; - -static int range128[] = { - 1, 1 + 127, -}; - -static int range256[] = { - 1, 1 + 128, -}; - -static int range512[] = { - 1, 1+ 511, -}; - - -static void test_arfcn_filter() -{ - int arfcns[50], i, res, f0_included; - for (i = 0; i < ARRAY_SIZE(arfcns); ++i) - arfcns[i] = (i + 1) * 2; - - /* check that the arfcn is taken out. f0_included is only set for Range1024 */ - f0_included = 24; - res = range_enc_filter_arfcns(ARFCN_RANGE_512, arfcns, ARRAY_SIZE(arfcns), - arfcns[0], &f0_included); - VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1); - VERIFY(f0_included, ==, 0); - for (i = 0; i < res; ++i) - VERIFY(arfcns[i], ==, ((i+2) * 2) - (2+1)); - - /* check with range1024 */ - for (i = 0; i < ARRAY_SIZE(arfcns); ++i) - arfcns[i] = (i + 1) * 2; - res = range_enc_filter_arfcns(ARFCN_RANGE_1024, arfcns, ARRAY_SIZE(arfcns), - arfcns[0], &f0_included); - VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1); - VERIFY(f0_included, ==, 1); - for (i = 0; i < res; ++i) - VERIFY(arfcns[i], ==, ((i + 2) * 2) - 1); - - /* check with range1024, not included */ - for (i = 0; i < ARRAY_SIZE(arfcns); ++i) - arfcns[i] = (i + 1) * 2; - res = range_enc_filter_arfcns(ARFCN_RANGE_1024, arfcns, ARRAY_SIZE(arfcns), - 11, &f0_included); - VERIFY(res, ==, ARRAY_SIZE(arfcns)); - VERIFY(f0_included, ==, 0); - for (i = 0; i < res; ++i) - VERIFY(arfcns[i], ==, ((i + 1) * 2) - 1); -} - -static void test_print_encoding() -{ - int rc; - int w[17]; - uint8_t chan_list[16]; - memset(chan_list, 0x23, sizeof(chan_list)); - - for (rc = 0; rc < ARRAY_SIZE(w); ++rc) - switch (rc % 3) { - case 0: - w[rc] = 0xAAAA; - break; - case 1: - w[rc] = 0x5555; - break; - case 2: - w[rc] = 0x9696; - break; - } - - rc = range_enc_range512(chan_list, 0x96, w); - VERIFY(rc, ==, 0); - - printf("Range512: %s\n", osmo_hexdump(chan_list, ARRAY_SIZE(chan_list))); -} - -int main(int argc, char **argv) -{ - int ws[(sizeof(freqs1)/sizeof(freqs1[0]))]; - int i, f0 = 0xFFFFFF; - - memset(&ws[0], 0x23, sizeof(ws)); - - i = range_enc_find_index(1023, freqs1, ARRAY_SIZE(freqs1)); - printf("Element is: %d => freqs[i] = %d\n", i, freqs1[i]); - VERIFY(i, ==, 2); - - i = range_enc_find_index(511, freqs2, ARRAY_SIZE(freqs2)); - printf("Element is: %d => freqs[i] = %d\n", i, freqs2[i]); - VERIFY(i, ==, 2); - - i = range_enc_find_index(511, freqs3, ARRAY_SIZE(freqs3)); - printf("Element is: %d => freqs[i] = %d\n", i, freqs3[i]); - VERIFY(i, ==, 0); - - i = range_enc_arfcns(1023, freqs1, ARRAY_SIZE(freqs1), ws, 0); - VERIFY(i, ==, 0); - - for (i = 0; i < sizeof(freqs1)/sizeof(freqs1[0]); ++i) { - printf("w[%d]=%d\n", i, ws[i]); - VERIFY(ws[i], ==, w_out[i]); - } - - i = range_enc_determine_range(range128, ARRAY_SIZE(range128), &f0); - VERIFY(i, ==, ARFCN_RANGE_128); - VERIFY(f0, ==, 1); - - i = range_enc_determine_range(range256, ARRAY_SIZE(range256), &f0); - VERIFY(i, ==, ARFCN_RANGE_256); - VERIFY(f0, ==, 1); - - i = range_enc_determine_range(range512, ARRAY_SIZE(range512), &f0); - VERIFY(i, ==, ARFCN_RANGE_512); - VERIFY(f0, ==, 1); - - - test_arfcn_filter(); - test_print_encoding(); - - return 0; -} diff --git a/openbsc/tests/si/si_test.ok b/openbsc/tests/si/si_test.ok deleted file mode 100644 index 6c4b028b0..000000000 --- a/openbsc/tests/si/si_test.ok +++ /dev/null @@ -1,20 +0,0 @@ -Element is: 2 => freqs[i] = 121 -Element is: 2 => freqs[i] = 1 -Element is: 0 => freqs[i] = 68 -w[0]=122 -w[1]=2 -w[2]=69 -w[3]=204 -w[4]=75 -w[5]=66 -w[6]=60 -w[7]=70 -w[8]=83 -w[9]=3 -w[10]=24 -w[11]=67 -w[12]=54 -w[13]=64 -w[14]=70 -w[15]=9 -Range512: 88 4b 2a 95 65 95 55 2c a9 55 aa 55 6a 95 59 55 diff --git a/openbsc/tests/smpp/Makefile.am b/openbsc/tests/smpp/Makefile.am deleted file mode 100644 index eb259656b..000000000 --- a/openbsc/tests/smpp/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_srcdir)/src/libmsc -AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(COVERAGE_LDFLAGS) - -EXTRA_DIST = smpp_test.ok smpp_test.err - -noinst_PROGRAMS = smpp_test - -smpp_test_SOURCES = smpp_test.c \ - $(top_srcdir)/src/libmsc/smpp_utils.c -smpp_test_LDADD = $(LIBOSMOCORE_LIBS) \ - $(top_srcdir)/src/libcommon/libcommon.a diff --git a/openbsc/tests/smpp/smpp_test.c b/openbsc/tests/smpp/smpp_test.c deleted file mode 100644 index 62fa9d2e9..000000000 --- a/openbsc/tests/smpp/smpp_test.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * (C) 2013 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include - -#include -#include - -#include "smpp_smsc.h" - -struct coding_test { - uint8_t dcs; - uint8_t coding; - int mode; - int res; -}; - -static struct coding_test codecs[] = { - { .dcs = 0xf6 , .coding = 0x02, .mode = MODE_8BIT, .res = 0 }, - { .dcs = 0xf2 , .coding = 0x01, .mode = MODE_7BIT, .res = 0 }, - { .dcs = 0x02 , .coding = 0x01, .mode = MODE_7BIT, .res = 0 }, - { .dcs = 0x06 , .coding = 0x02, .mode = MODE_8BIT, .res = 0 }, - { .dcs = 0x0A , .coding = 0x08, .mode = MODE_8BIT, .res = 0 }, - { .dcs = 0x0E , .coding = 0xFF, .mode = 0xFF, .res = -1 }, - { .dcs = 0xE0 , .coding = 0xFF, .mode = 0xFF, .res = -1 }, -}; - -static void test_coding_scheme(void) -{ - int i; - printf("Testing coding scheme support\n"); - - for (i = 0; i < ARRAY_SIZE(codecs); ++i) { - uint8_t coding; - int mode, res; - - res = smpp_determine_scheme(codecs[i].dcs, &coding, &mode); - OSMO_ASSERT(res == codecs[i].res); - if (res != -1) { - OSMO_ASSERT(mode == codecs[i].mode); - OSMO_ASSERT(coding == codecs[i].coding); - } - } -} - -int main(int argc, char **argv) -{ - osmo_init_logging(&log_info); - log_set_use_color(osmo_stderr_target, 0); - log_set_print_filename(osmo_stderr_target, 0); - - test_coding_scheme(); - return EXIT_SUCCESS; -} diff --git a/openbsc/tests/smpp/smpp_test.err b/openbsc/tests/smpp/smpp_test.err deleted file mode 100644 index ec966ba47..000000000 --- a/openbsc/tests/smpp/smpp_test.err +++ /dev/null @@ -1,2 +0,0 @@ -SMPP MO Unknown Data Coding 0x0e -SMPP MO Unknown Data Coding 0xe0 diff --git a/openbsc/tests/smpp/smpp_test.ok b/openbsc/tests/smpp/smpp_test.ok deleted file mode 100644 index fd44804d1..000000000 --- a/openbsc/tests/smpp/smpp_test.ok +++ /dev/null @@ -1 +0,0 @@ -Testing coding scheme support diff --git a/openbsc/tests/sms.txt b/openbsc/tests/sms.txt deleted file mode 100644 index 06c885b8b..000000000 --- a/openbsc/tests/sms.txt +++ /dev/null @@ -1,50 +0,0 @@ -03 02 01 0a 02 43 0b 00 1d 39 01 1a 00 01 00 07 91 55 11 18 31 28 00 0e 31 20 04 81 21 43 00 00 ff 04 d4 f2 9c 0e - -03 02 01 0a 02 43 0b 00 1d - -39 - TransactionID 3, SMS messages :: gh->proto_discr -01 - CP-DATA :: gh->msg_type -1a - Length: 26 :: gh->data[0] -00 - MTI 0 RP-DATA (ms->n) -01 - MR 1 -00 - RP-OA -07 - RP-DA (SMSC Length) -91 - International No, Numbering Plan -55 11 18 31 28 00 - 551181138200 -0e - RP-UD len (14) - -TPDU (14 byte): -31 - MTI(01), VPF(2:relative), MMS(0), SRI(1), UDHI(0), RP(0) -20 - Message Reference -04 - DA length -81 - Numbering Plan, National number -21 43 - DA 1234 -00 - PID -00 - DCS -ff - Validity period -04 - User Data length (04) -d4 f2 9c 0e - gsm_default 7bit encoded "Test" (4 byte) - -03 02 01 0a 02 43 0b 00 9f 09 01 9c 00 da 00 07 91 88 96 13 00 00 99 90 11 7b 04 81 22 22 00 08 ff 86 6c 38 8c 50 92 80 88 4c 00 4d 00 4d 00 41 6a 19 67 03 74 06 8c a1 7d b2 00 20 00 20 51 68 74 03 99 96 52 75 7d b2 8d ef 6a 19 67 03 ff 0c 6a 19 67 03 96 f6 98 a8 96 aa ff 01 8b 93 60 a8 80 70 66 0e 51 32 84 c4 ff 0c 97 48 6d 3b 62 95 8c c7 ff 01 73 fe 57 28 52 a0 51 65 90 01 96 50 91 cf 59 27 80 6f 76 df 6d 0b 57 fa 96 8a 91 77 5e 63 53 61 ff 0c 8a cb 4e 0a 7d b2 64 1c 5c 0b 30 0c 6a 19 67 03 30 0d - -03 02 01 0a 02 43 0b 00 9f - lower levels -09 - TransactionID 0, SMS messages -01 - CP-DATA -9c - Length: (156) -00 - MTI 0 RP-DATA (ms->n) -da - MR (?) -00 - RP-OA -07 - RP-DA (SMSC Length) -91 - International No. -88 96 13 00 00 99 -90 - RP-UD len (144) -11 - -7b - Message Reference -04 - DA length -81 - Numbering Plan -22 22 - Address 2222 -00 - PID -08 - DCS (UCS2 charset) -ff - Validity period -86 - User Data length (134) -6c 38 8c 50 92 80 88 4c 00 4d 00 4d 00 41 6a 19 67 03 74 06 8c a1 7d b2 00 20 00 20 51 68 74 03 99 96 52 75 7d b2 8d ef 6a 19 67 03 ff 0c 6a 19 67 03 96 f6 98 a8 96 aa ff 01 8b 93 60 a8 80 70 66 0e 51 32 84 c4 ff 0c 97 48 6d 3b 62 95 8c c7 ff 01 73 fe 57 28 52 a0 51 65 90 01 96 50 91 cf 59 27 80 6f 76 df 6d 0b 57 fa 96 8a 91 77 5e 63 53 61 ff 0c 8a cb 4e 0a 7d b2 64 1c 5c 0b 30 0c 6a 19 67 03 30 0d - diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at deleted file mode 100644 index ea3fa1ce5..000000000 --- a/openbsc/tests/testsuite.at +++ /dev/null @@ -1,61 +0,0 @@ -AT_INIT -AT_BANNER([Regression tests.]) - -AT_SETUP([gsm0408]) -AT_KEYWORDS([gsm0408]) -cat $abs_srcdir/gsm0408/gsm0408_test.ok > expout -AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [], [expout], [ignore]) -AT_CLEANUP - -AT_SETUP([db]) -AT_KEYWORDS([db]) -cat $abs_srcdir/db/db_test.ok > expout -AT_CHECK([$abs_top_builddir/tests/db/db_test], [], [expout], [ignore]) -AT_CLEANUP - -AT_SETUP([channel]) -AT_KEYWORDS([channel]) -cat $abs_srcdir/channel/channel_test.ok > expout -AT_CHECK([$abs_top_builddir/tests/channel/channel_test], [], [expout], [ignore]) -AT_CLEANUP - -AT_SETUP([mgcp]) -AT_KEYWORDS([mgcp]) -cat $abs_srcdir/mgcp/mgcp_test.ok > expout -AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore]) -AT_CLEANUP - -AT_SETUP([gprs]) -AT_KEYWORDS([gprs]) -cat $abs_srcdir/gprs/gprs_test.ok > expout -AT_CHECK([$abs_top_builddir/tests/gprs/gprs_test], [], [expout], [ignore]) -AT_CLEANUP - -AT_SETUP([bsc-nat]) -AT_KEYWORDS([bsc-nat]) -AT_CHECK([test "$enable_nat_test" != no || exit 77]) -cp $abs_srcdir/bsc-nat/barr.cfg . -cp $abs_srcdir/bsc-nat/barr_dup.cfg . -cat $abs_srcdir/bsc-nat/bsc_nat_test.ok > expout -AT_CHECK([$abs_top_builddir/tests/bsc-nat/bsc_nat_test], [], [expout], [ignore]) -AT_CLEANUP - -AT_SETUP([smpp]) -AT_KEYWORDS([smpp]) -AT_CHECK([test "$enable_smpp_test" != no || exit 77]) -cat $abs_srcdir/smpp/smpp_test.ok > expout -cat $abs_srcdir/smpp/smpp_test.err > experr -AT_CHECK([$abs_top_builddir/tests/smpp/smpp_test], [], [expout], [experr]) -AT_CLEANUP - -AT_SETUP([si]) -AT_KEYWORDS([si]) -cat $abs_srcdir/si/si_test.ok > expout -AT_CHECK([$abs_top_builddir/tests/si/si_test], [], [expout], [ignore]) -AT_CLEANUP - -AT_SETUP([abis]) -AT_KEYWORDS([abis]) -cat $abs_srcdir/abis/abis_test.ok > expout -AT_CHECK([$abs_top_builddir/tests/abis/abis_test], [], [expout], [ignore]) -AT_CLEANUP diff --git a/openbsc/tests/vty_test_runner.py b/openbsc/tests/vty_test_runner.py deleted file mode 100644 index 778cde04c..000000000 --- a/openbsc/tests/vty_test_runner.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env python - -# (C) 2013 by Katerina Barone-Adesi -# (C) 2013 by Holger Hans Peter Freyther -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import os -import time -import unittest - -import osmopy.obscvty as obscvty -import osmopy.osmoutil as osmoutil - -confpath = '.' - -class TestVTYBase(unittest.TestCase): - - def vty_command(self): - raise Exception("Needs to be implemented by a subclass") - - def vty_app(self): - raise Exception("Needs to be implemented by a subclass") - - def setUp(self): - osmo_vty_cmd = self.vty_command()[:] - config_index = osmo_vty_cmd.index('-c') - if config_index: - cfi = config_index + 1 - osmo_vty_cmd[cfi] = os.path.join(confpath, osmo_vty_cmd[cfi]) - - try: - print "Launch: %s from %s" % (' '.join(osmo_vty_cmd), os.getcwd()) - self.proc = osmoutil.popen_devnull(osmo_vty_cmd) - except OSError: - print >> sys.stderr, "Current directory: %s" % os.getcwd() - print >> sys.stderr, "Consider setting -b" - time.sleep(1) - - appstring = self.vty_app()[2] - appport = self.vty_app()[0] - self.vty = obscvty.VTYInteract(appstring, "127.0.0.1", appport) - - def tearDown(self): - self.vty = None - osmoutil.end_proc(self.proc) - -class TestVTYNITB(TestVTYBase): - - def vty_command(self): - return ["./src/osmo-nitb/osmo-nitb", "-c", - "doc/examples/osmo-nitb/nanobts/openbsc.cfg"] - - def vty_app(self): - return (4242, "./src/osmo-nitb/osmo-nitb", "OpenBSC", "nitb") - - def testEnableDisablePeriodicLU(self): - self.vty.enable() - self.vty.command("configure terminal") - self.vty.command("network") - self.vty.command("bts 0") - - # Test invalid input - self.vty.verify("periodic location update 0", ['% Unknown command.']) - self.vty.verify("periodic location update 5", ['% Unknown command.']) - self.vty.verify("periodic location update 1531", ['% Unknown command.']) - - # Enable periodic lu.. - self.vty.verify("periodic location update 60", ['']) - res = self.vty.command("write terminal") - self.assert_(res.find('periodic location update 60') > 0) - self.assertEquals(res.find('no periodic location update'), -1) - - # Now disable it.. - self.vty.verify("no periodic location update", ['']) - res = self.vty.command("write terminal") - self.assertEquals(res.find('periodic location update 60'), -1) - self.assert_(res.find('no periodic location update') > 0) - -class TestVTYNAT(TestVTYBase): - - def vty_command(self): - return ["./src/osmo-bsc_nat/osmo-bsc_nat", "-c", - "doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"] - - def vty_app(self): - return (4244, "src/osmo-bsc_nat/osmo-bsc_nat", "OsmoBSCNAT", "nat") - - def testMoo(self): - pass - - -def add_nat_test(suite, workdir): - if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc_nat/osmo-bsc_nat")): - print("Skipping the NAT test") - return - test = unittest.TestLoader().loadTestsFromTestCase(TestVTYNAT) - suite.addTest(test) - -if __name__ == '__main__': - import argparse - import sys - - workdir = '.' - - parser = argparse.ArgumentParser() - parser.add_argument("-v", "--verbose", dest="verbose", - action="store_true", help="verbose mode") - parser.add_argument("-p", "--pythonconfpath", dest="p", - help="searchpath for config") - parser.add_argument("-w", "--workdir", dest="w", - help="Working directory") - args = parser.parse_args() - - verbose_level = 1 - if args.verbose: - verbose_level = 2 - - if args.w: - workdir = args.w - - if args.p: - confpath = args.p - - print "confpath %s, workdir %s" % (confpath, workdir) - os.chdir(workdir) - print "Running tests for specific VTY commands" - suite = unittest.TestSuite() - suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestVTYNITB)) - add_nat_test(suite, workdir) - res = unittest.TextTestRunner(verbosity=verbose_level).run(suite) - sys.exit(len(res.errors) + len(res.failures)) diff --git a/openbsc/tools/hlrstat.pl b/openbsc/tools/hlrstat.pl deleted file mode 100755 index 668fc9a4a..000000000 --- a/openbsc/tools/hlrstat.pl +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/perl - -use strict; -use DBI; -my $dbh = DBI->connect("dbi:SQLite:dbname=hlr.sqlite3","",""); - - -my %mcc_names; -my %mcc_mnc_names; - -sub get_mcc_mnc_name($) -{ - my $mcc_mnc = shift; - my $ret = $mcc_mnc; - - if ($mcc_mnc_names{$mcc_mnc} ne '') { - $ret = $mcc_mnc_names{$mcc_mnc}; - } - - return $ret; -} - -sub read_networks($) -{ - my $filename = shift; - my $cur_name; - - open(INFILE, $filename); - while (my $l = ) { - chomp($l); - if ($l =~ /^#/) { - next; - } - if ($l =~ /^\t/) { - my ($mcc, $mnc, $brand, $r) = split(' ', $l, 4); - #printf("%s|%s|%s\n", $mcc, $mnc, $brand); - $mcc_mnc_names{"$mcc-$mnc"} = $brand; - $mcc_names{$mcc} = $cur_name; - } elsif ($l =~ /^(\w\w)\t(.*)/) { - #printf("%s|%s\n", $1, $2); - $cur_name = $2; - } - } - close(INFILE); -} - -read_networks("networks.tab"); - -my %oper_count; -my %country_count; - -#my $sth = $dbh->prepare("SELECT imsi FROM subscriber where authorized=1"); -my $sth = $dbh->prepare("SELECT imsi FROM subscriber"); - -$sth->execute(); - -while (my $href = $sth->fetchrow_hashref) { - my ($mcc, $mnc) = $$href{imsi} =~ /(\d{3})(\d{2}).*/; - #printf("%s %s-%s \n", $$href{imsi}, $mcc, $mnc); - $oper_count{"$mcc-$mnc"}++; - $country_count{$mcc}++; -} - - -foreach my $c (sort{$country_count{$b} <=> $country_count{$a}} keys %country_count) { - printf("%s: %d\n", $mcc_names{$c}, $country_count{$c}); - - foreach my $k (sort{$oper_count{$b} <=> $oper_count{$a}} keys %oper_count) { - if ($k =~ /^$c-/) { - printf("\t%s: %d\n", get_mcc_mnc_name($k), $oper_count{$k}); - } - } -} diff --git a/osmoappdesc.py b/osmoappdesc.py new file mode 100644 index 000000000..9d6fbe67e --- /dev/null +++ b/osmoappdesc.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +# (C) 2013 by Katerina Barone-Adesi +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see + + +# Most systems won't be able to use these, so they're separated out +nitb_e1_configs = [ + "doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg", + "doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg", + "doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg", + "doc/examples/osmo-nitb/bs11/openbsc.cfg", + "doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg", + "doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg", + "doc/examples/osmo-nitb/rbs2308/openbsc.cfg" +] + + +app_configs = { + "osmo-bsc": ["doc/examples/osmo-bsc/osmo-bsc.cfg"], + "nat": ["doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"], + "mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"], + "gbproxy": ["doc/examples/osmo-gbproxy/osmo-gbproxy.cfg"], + "sgsn": ["doc/examples/osmo-sgsn/osmo-sgsn.cfg"], + "nitb": ["doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg", + "doc/examples/osmo-nitb/nanobts/openbsc.cfg"] +} + + +apps = [(4242, "src/osmo-bsc/osmo-bsc", "OsmoBSC", "osmo-bsc"), + (4244, "src/osmo-bsc_nat/osmo-bsc_nat", "OsmoBSCNAT", "nat"), + (4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp"), + (4246, "src/gprs/osmo-gbproxy", "OsmoGbProxy", "gbproxy"), + (4245, "src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn"), + (4242, "src/osmo-nitb/osmo-nitb", "OpenBSC", "nitb") + ] + +vty_command = ["./src/osmo-nitb/osmo-nitb", "-c", + "doc/examples/osmo-nitb/nanobts/openbsc.cfg"] + +vty_app = apps[-1] diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 000000000..2cdbb9d1c --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) + +SUBDIRS = libcommon libmgcp libbsc libmsc libtrau libctrl osmo-nitb osmo-bsc_mgcp utils ipaccess gprs + +# Conditional modules +if BUILD_NAT +SUBDIRS += osmo-bsc_nat +endif +if BUILD_BSC +SUBDIRS += osmo-bsc +endif diff --git a/src/gprs/.gitignore b/src/gprs/.gitignore new file mode 100644 index 000000000..7cfefbac2 --- /dev/null +++ b/src/gprs/.gitignore @@ -0,0 +1,2 @@ +gsn_restart +osmo_*.cfg* diff --git a/src/gprs/Makefile.am b/src/gprs/Makefile.am new file mode 100644 index 000000000..f4b135467 --- /dev/null +++ b/src/gprs/Makefile.am @@ -0,0 +1,24 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) $(LIBOSMOGB_CFLAGS) $(COVERAGE_CFLAGS) +OSMO_LIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOGB_LIBS) + +noinst_HEADERS = gprs_sndcp.h + +if HAVE_LIBGTP +bin_PROGRAMS = osmo-gbproxy osmo-sgsn +else +bin_PROGRAMS = osmo-gbproxy +endif + +osmo_gbproxy_SOURCES = gb_proxy.c gb_proxy_main.c gb_proxy_vty.c +osmo_gbproxy_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ + $(OSMO_LIBS) + +osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c gprs_sndcp_vty.c \ + sgsn_main.c sgsn_vty.c sgsn_libgtp.c \ + gprs_llc.c gprs_llc_vty.c crc24.c +osmo_sgsn_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ + -lgtp $(OSMO_LIBS) diff --git a/src/gprs/crc24.c b/src/gprs/crc24.c new file mode 100644 index 000000000..1a420ed66 --- /dev/null +++ b/src/gprs/crc24.c @@ -0,0 +1,67 @@ +/* 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/src/gprs/gb_proxy.c b/src/gprs/gb_proxy.c new file mode 100644 index 000000000..d753abed1 --- /dev/null +++ b/src/gprs/gb_proxy.c @@ -0,0 +1,690 @@ +/* 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 + +struct gbprox_peer { + struct llist_head list; + + /* NS-VC over which we send/receive data to this BVC */ + struct gprs_nsvc *nsvc; + + /* 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]; +}; + +/* Linked list of all Gb peers (except SGSN) */ +static LLIST_HEAD(gbprox_bts_peers); + +/* Find the gbprox_peer by its BVCI */ +static struct gbprox_peer *peer_by_bvci(uint16_t bvci) +{ + struct gbprox_peer *peer; + llist_for_each_entry(peer, &gbprox_bts_peers, list) { + if (peer->bvci == bvci) + return peer; + } + return NULL; +} + +static struct gbprox_peer *peer_by_nsvc(struct gprs_nsvc *nsvc) +{ + struct gbprox_peer *peer; + llist_for_each_entry(peer, &gbprox_bts_peers, list) { + if (peer->nsvc == nsvc) + return peer; + } + return NULL; +} + +/* look-up a peer by its Routeing Area Code (RAC) */ +static struct gbprox_peer *peer_by_rac(const uint8_t *ra) +{ + struct gbprox_peer *peer; + llist_for_each_entry(peer, &gbprox_bts_peers, list) { + if (!memcmp(peer->ra, ra, 6)) + return peer; + } + return NULL; +} + +/* look-up a peer by its Location Area Code (LAC) */ +static struct gbprox_peer *peer_by_lac(const uint8_t *la) +{ + struct gbprox_peer *peer; + llist_for_each_entry(peer, &gbprox_bts_peers, list) { + if (!memcmp(peer->ra, la, 5)) + return peer; + } + return NULL; +} + +static struct gbprox_peer *peer_alloc(uint16_t bvci) +{ + struct gbprox_peer *peer; + + peer = talloc_zero(tall_bsc_ctx, struct gbprox_peer); + if (!peer) + return NULL; + + peer->bvci = bvci; + llist_add(&peer->list, &gbprox_bts_peers); + + return peer; +} + +#if 0 +static void peer_free(struct gbprox_peer *peer) +{ + llist_del(&peer->list); + talloc_free(peer); +} +#endif + +/* FIXME: this needs to go to libosmocore/msgb.c */ +static struct msgb *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; + + new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data); + new_msg->l2h = new_msg->_data + (msg->l2h - msg->_data); + new_msg->l3h = new_msg->_data + (msg->l3h - msg->_data); + 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); + + new_cb->bssgph = new_msg->_data + (old_cb->bssgph - msg->_data); + 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 */ + 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; +} + +/* 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); +} + +/* feed a message down the NS-VC associated with the specified peer */ +static int gbprox_relay2sgsn(struct msgb *old_msg, 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 = msgb_copy(old_msg, "msgb_relay2sgsn"); + + DEBUGP(DGPRS, "NSEI=%u proxying BTS->SGSN (NS_BVCI=%u, NSEI=%u)\n", + msgb_nsei(msg), ns_bvci, gbcfg.nsip_sgsn_nsei); + + msgb_bvci(msg) = ns_bvci; + msgb_nsei(msg) = gbcfg.nsip_sgsn_nsei; + + strip_ns_hdr(msg); + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} + +/* feed a message down the NS-VC associated with the specified peer */ +static int gbprox_relay2peer(struct msgb *old_msg, struct gbprox_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 = msgb_copy(old_msg, "msgb_relay2peer"); + + DEBUGP(DGPRS, "NSEI=%u proxying SGSN->BSS (NS_BVCI=%u, NSEI=%u)\n", + msgb_nsei(msg), ns_bvci, peer->nsvc->nsei); + + msgb_bvci(msg) = ns_bvci; + msgb_nsei(msg) = peer->nsvc->nsei; + + /* Strip the old NS header, it will be replaced with a new one */ + strip_ns_hdr(msg); + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} + +static int block_unblock_peer(uint16_t ptp_bvci, uint8_t pdu_type) +{ + struct gbprox_peer *peer; + + peer = peer_by_bvci(ptp_bvci); + if (!peer) { + LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", + ptp_bvci); + return -ENOENT; + } + + switch (pdu_type) { + case BSSGP_PDUT_BVC_BLOCK_ACK: + peer->blocked = 1; + break; + case BSSGP_PDUT_BVC_UNBLOCK_ACK: + peer->blocked = 0; + 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 msgb *msg, uint16_t ptp_bvci, + uint16_t ns_bvci) +{ + struct gbprox_peer *peer; + + peer = peer_by_bvci(ptp_bvci); + if (!peer) { + LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", + ptp_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 signalling message from a BSS-side NS-VC */ +static int gbprox_rx_sig_from_bss(struct msgb *msg, struct gprs_nsvc *nsvc, + 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 gbprox_peer *from_peer; + struct gprs_ra_id raid; + + if (ns_bvci != 0 && ns_bvci != 1) { + LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u BVCI=%u is not signalling\n", + nsvc->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", nsvc->nsei); + return -EINVAL; + } + + bssgp_tlv_parse(&tp, bgph->data, data_len); + + switch (pdu_type) { + case BSSGP_PDUT_SUSPEND: + case BSSGP_PDUT_RESUME: + /* We implement RAC snooping during SUSPEND/RESUME, since + * it establishes a relationsip between BVCI/peer and the + * routeing area code. 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 = peer_by_nsvc(nsvc); + 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 " + "RAC snooping: RAC %u-%u-%u-%u behind BVCI=%u, " + "NSVCI=%u\n",nsvc->nsei, raid.mcc, raid.mnc, raid.lac, + raid.rac , from_peer->bvci, nsvc->nsvci); + /* 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", + nsvc->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", nsvc->nsei); + return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, + nsvc->nsei, 0, ns_bvci); + } + from_peer = peer_by_bvci(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 NSVCI=%u/NSEI=%u\n", bvci, + nsvc->nsvci, nsvc->nsei); + from_peer = peer_alloc(bvci); + from_peer->nsvc = nsvc; + } + 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", nsvc->nsei, + bvci, raid.mcc, raid.mnc, raid.lac, + raid.rac); + } + } + break; + } + + /* Normally, we can simply pass on all signalling messages from BSS to + * SGSN */ + return gbprox_relay2sgsn(msg, ns_bvci); +err_no_peer: + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) cannot find peer based on RAC\n", + nsvc->nsei); + return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg); +err_mand_ie: + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) missing mandatory RA IE\n", + nsvc->nsei); + 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 msgb *msg, struct tlv_parsed *tp, + struct gprs_nsvc *nsvc, uint16_t ns_bvci) +{ + struct gbprox_peer *peer = NULL; + + LOGP(DGPRS, LOGL_INFO, "NSEI=%u(SGSN) BSSGP PAGING ", + nsvc->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); + } else if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { + peer = peer_by_rac(TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); + LOGPC(DGPRS, LOGL_INFO, "routing by RAC to peer BVCI=%u\n", + peer ? peer->bvci : -1); + } else if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { + peer = peer_by_lac(TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA)); + LOGPC(DGPRS, LOGL_INFO, "routing by LAC to peer BVCI=%u\n", + peer ? peer->bvci : -1); + } else + LOGPC(DGPRS, LOGL_INFO, "\n"); + + if (!peer) { + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) BSSGP PAGING: " + "unable to route, missing IE\n", nsvc->nsei); + 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 msgb *msg, struct tlv_parsed *tp, + struct gprs_nsvc *nsvc, uint16_t ns_bvci) +{ + struct gbprox_peer *peer; + uint16_t ptp_bvci; + + if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, + NULL, 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 = peer_by_bvci(ptp_bvci); + if (!peer) { + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u BVCI=%u: Cannot find BSS\n", + nsvc->nsei, ptp_bvci); + return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, + NULL, 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, &gbprox_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 msgb *msg, struct gprs_nsvc *nsvc, + 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 gbprox_peer *peer; + uint16_t bvci; + int rc = 0; + + if (ns_bvci != 0 && ns_bvci != 1) { + LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BVCI=%u is not " + "signalling\n", nsvc->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", nsvc->nsei); + return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); + } + + rc = bssgp_tlv_parse(&tp, bgph->data, data_len); + + switch (pdu_type) { + case BSSGP_PDUT_BVC_RESET: + rc = rx_reset_from_sgsn(msg, &tp, nsvc, ns_bvci); + break; + case BSSGP_PDUT_FLUSH_LL: + case BSSGP_PDUT_BVC_RESET_ACK: + /* 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(msg, bvci, ns_bvci); + break; + case BSSGP_PDUT_PAGING_PS: + case BSSGP_PDUT_PAGING_CS: + /* process the paging request (LAC/RAC lookup) */ + rc = gbprox_rx_paging(msg, &tp, nsvc, ns_bvci); + break; + case BSSGP_PDUT_STATUS: + /* Some exception has occurred */ + LOGP(DGPRS, LOGL_NOTICE, + "NSEI=%u(SGSN) BSSGP STATUS ", nsvc->nsei); + if (!TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) { + LOGPC(DGPRS, LOGL_NOTICE, "\n"); + goto err_mand_ie; + } + LOGPC(DGPRS, LOGL_NOTICE, + "cause=0x%02x(%s) ", *TLVP_VAL(&tp, BSSGP_IE_CAUSE), + bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE))); + if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { + uint16_t bvci = tlvp_val16_unal(&tp, BSSGP_IE_BVCI); + LOGPC(DGPRS, LOGL_NOTICE, + "BVCI=%u\n", ntohs(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: + /* RAC IE is mandatory */ + if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) + goto err_mand_ie; + peer = peer_by_rac(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", nsvc->nsei, + pdu_type == BSSGP_PDUT_BVC_UNBLOCK_ACK ? "UN":""); + /* should we send STATUS ? */ + } else { + /* Mark BVC as (un)blocked */ + block_unblock_peer(bvci, pdu_type); + } + rc = gbprox_relay2bvci(msg, bvci, ns_bvci); + break; + case BSSGP_PDUT_SGSN_INVOKE_TRACE: + LOGP(DGPRS, LOGL_ERROR, + "NSEI=%u(SGSN) BSSGP INVOKE TRACE not supported\n",nsvc->nsei); + rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg); + break; + default: + LOGP(DGPRS, LOGL_NOTICE, "BSSGP PDU type 0x%02x unknown\n", + pdu_type); + rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); + break; + } + + return rc; +err_mand_ie: + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) missing mandatory IE\n", + nsvc->nsei); + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); +err_no_peer: + LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) cannot find peer based on RAC\n", + nsvc->nsei); + return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg); +} + +/* Main input function for Gb proxy */ +int gbprox_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci) +{ + int rc; + struct gbprox_peer *peer; + + /* Only BVCI=0 messages need special treatment */ + if (ns_bvci == 0 || ns_bvci == 1) { + if (nsvc->remote_end_is_sgsn) + rc = gbprox_rx_sig_from_sgsn(msg, nsvc, ns_bvci); + else + rc = gbprox_rx_sig_from_bss(msg, nsvc, ns_bvci); + } else { + /* All other BVCI are PTP and thus can be simply forwarded */ + if (!nsvc->remote_end_is_sgsn) { + return gbprox_relay2sgsn(msg, ns_bvci); + } + /* else: SGSN -> BSS direction */ + peer = peer_by_bvci(ns_bvci); + if (!peer) { + LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for " + "BVCI=%u via NSVC=%u/NSEI=%u\n", ns_bvci, + nsvc->nsvci, nsvc->nsei); + peer = peer_alloc(ns_bvci); + peer->nsvc = nsvc; + } + if (peer->blocked) { + LOGP(DGPRS, LOGL_NOTICE, "Dropping PDU for " + "blocked BVCI=%u via NSVC=%u/NSEI=%u\n", + ns_bvci, nsvc->nsvci, nsvc->nsei); + return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, NULL, msg); + } + rc = gbprox_relay2peer(msg, peer, 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 ns_signal_data *nssd = signal_data; + struct gprs_nsvc *nsvc = nssd->nsvc; + struct gbprox_peer *peer; + + if (subsys != SS_L_NS) + return 0; + + if (signal == S_NS_RESET && nsvc->nsei == gbcfg.nsip_sgsn_nsei) { + /* 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"); + 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 = peer_by_nsvc(nsvc); + 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 { + /* iterate over all BTS peers and send the respective PDU */ + llist_for_each_entry(peer, &gbprox_bts_peers, list) { + switch (signal) { + case S_NS_RESET: + gprs_ns_tx_reset(peer->nsvc, nssd->cause); + break; + case S_NS_BLOCK: + gprs_ns_tx_block(peer->nsvc, nssd->cause); + break; + case S_NS_UNBLOCK: + gprs_ns_tx_unblock(peer->nsvc); + break; + } + } + } + return 0; +} + + +#include + +gDEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy", + SHOW_STR "Display information about the Gb proxy") +{ + struct gbprox_peer *peer; + + llist_for_each_entry(peer, &gbprox_bts_peers, list) { + struct gprs_nsvc *nsvc = peer->nsvc; + struct gprs_ra_id raid; + gsm48_parse_ra(&raid, peer->ra); + + vty_out(vty, "NSEI %5u, NS-VC %5u, PTP-BVCI %5u, " + "RAC %u-%u-%u-%u", + nsvc->nsei, nsvc->nsvci, peer->bvci, + raid.mcc, raid.mnc, raid.lac, raid.rac); + if (nsvc->ll == GPRS_NS_LL_UDP || nsvc->ll == GPRS_NS_LL_FR_GRE) + vty_out(vty, " %s:%u", + inet_ntoa(nsvc->ip.bts_addr.sin_addr), + ntohs(nsvc->ip.bts_addr.sin_port)); + if (peer->blocked) + vty_out(vty, " [BVC-BLOCKED]"); + + vty_out(vty, "%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} diff --git a/src/gprs/gb_proxy_main.c b/src/gprs/gb_proxy_main.c new file mode 100644 index 000000000..b5b11fdea --- /dev/null +++ b/src/gprs/gb_proxy_main.c @@ -0,0 +1,312 @@ +/* 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 "../../bscconfig.h" + +/* this is here for the vty... it will never be called */ +void subscr_put() { abort(); } + +#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; +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(msg, nsvc, bvci); + 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 = strdup(optarg); + break; + case 'T': + log_set_print_timestamp(osmo_stderr_target, 1); + break; + case 'e': + log_set_log_level(osmo_stderr_target, atoi(optarg)); + break; + case 'V': + print_version(1); + exit(0); + break; + default: + break; + } + } +} + +extern void *tall_msgb_ctx; + +extern enum node_type bsc_vty_go_parent(struct vty *vty); + +static struct vty_app_info vty_info = { + .name = "OsmoGbProxy", + .version = PACKAGE_VERSION, + .go_parent_cb = bsc_vty_go_parent, + .is_config_node = bsc_vty_is_config_node, +}; + +/* default categories */ +static struct log_info_cat gprs_categories[] = { + [DGPRS] = { + .name = "DGPRS", + .description = "GPRS Packet Service", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DNS] = { + .name = "DNS", + .description = "GPRS Network Service (NS)", + .enabled = 1, .loglevel = LOGL_INFO, + }, + [DBSSGP] = { + .name = "DBSSGP", + .description = "GPRS BSS Gateway Protocol (BSSGP)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, +}; + +static const struct log_info gprs_log_info = { + .filter_fn = gprs_log_filter_fn, + .cat = gprs_categories, + .num_cat = ARRAY_SIZE(gprs_categories), +}; + +int main(int argc, char **argv) +{ + struct gsm_network dummy_network; + int rc; + + tall_bsc_ctx = talloc_named_const(NULL, 0, "nsip_proxy"); + tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); + + signal(SIGINT, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); + osmo_init_ignore_signals(); + + osmo_init_logging(&gprs_log_info); + + vty_info.copyright = openbsc_copyright; + vty_init(&vty_info); + logging_vty_add_cmds(&gprs_log_info); + gbproxy_vty_init(); + + handle_options(argc, argv); + + rate_ctr_init(tall_bsc_ctx); + + rc = telnet_init(tall_bsc_ctx, &dummy_network, 4246); + if (rc < 0) + exit(1); + + bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_bsc_ctx); + if (!bssgp_nsi) { + LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); + exit(1); + } + 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, NULL); + + rc = gbproxy_parse_config(config_file, &gbcfg); + if (rc < 0) { + LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n"); + exit(2); + } + + if (!gprs_nsvc_by_nsei(gbcfg.nsi, gbcfg.nsip_sgsn_nsei)) { + LOGP(DGPRS, LOGL_FATAL, "You cannot proxy to NSEI %u " + "without creating that NSEI before\n", + gbcfg.nsip_sgsn_nsei); + exit(2); + } + + rc = gprs_ns_nsip_listen(bssgp_nsi); + if (rc < 0) { + LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n"); + exit(2); + } + + rc = gprs_ns_frgre_listen(bssgp_nsi); + if (rc < 0) { + LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE " + "socket. Do you have CAP_NET_RAW?\n"); + exit(2); + } + + if (daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + perror("Error during daemonize"); + exit(1); + } + } + + /* Reset all the persistent NS-VCs that we've read from the config */ + gbprox_reset_persistent_nsvcs(bssgp_nsi); + + while (1) { + rc = osmo_select_main(0); + if (rc < 0) + exit(3); + } + + exit(0); +} diff --git a/src/gprs/gb_proxy_vty.c b/src/gprs/gb_proxy_vty.c new file mode 100644 index 000000000..bfa1f3bd1 --- /dev/null +++ b/src/gprs/gb_proxy_vty.c @@ -0,0 +1,106 @@ +/* + * (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 + +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 int config_write_gbproxy(struct vty *vty) +{ + vty_out(vty, "gbproxy%s", VTY_NEWLINE); + + vty_out(vty, " sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei, + 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 port = atoi(argv[0]); + + g_cfg->nsip_sgsn_nsei = port; + return CMD_SUCCESS; +} + +int gbproxy_vty_init(void) +{ + install_element_ve(&show_gbproxy_cmd); + + install_element(CONFIG_NODE, &cfg_gbproxy_cmd); + install_node(&gbproxy_node, config_write_gbproxy); + install_default(GBPROXY_NODE); + install_element(GBPROXY_NODE, &ournode_exit_cmd); + install_element(GBPROXY_NODE, &ournode_end_cmd); + install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd); + + return 0; +} + +int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg) +{ + int rc; + + g_cfg = cfg; + rc = vty_read_config_file(config_file, NULL); + if (rc < 0) { + fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); + return rc; + } + + return 0; +} + diff --git a/src/gprs/gprs_gmm.c b/src/gprs/gprs_gmm.c new file mode 100644 index 000000000..1f4d22c0f --- /dev/null +++ b/src/gprs/gprs_gmm.c @@ -0,0 +1,1634 @@ +/* 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-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 + +#define PTMSI_ALLOC + +/* Section 11.2.2 / Table 11.4 MM timers netwokr side */ +#define GSM0408_T3322_SECS 6 /* DETACH_REQ -> DETACH_ACC */ +#define GSM0408_T3350_SECS 6 /* waiting for ATT/RAU/TMSI COMPL */ +#define GSM0408_T3360_SECS 6 /* waiting for AUTH/CIPH RESP */ +#define GSM0408_T3370_SECS 6 /* waiting for ID RESP */ + +/* Section 11.2.2 / Table 11.4a MM timers netwokr side */ +#define GSM0408_T3313_SECS 30 /* waiting for paging response */ +#define GSM0408_T3314_SECS 44 /* force to STBY on expiry */ +#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 */ + +extern struct sgsn_instance *sgsn; + +/* Protocol related stuff, should go into libosmocore */ + +/* 10.5.5.14 GPRS MM Cause / Table 10.5.147 */ +const struct value_string gmm_cause_names[] = { + { GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown in HLR" }, + { GMM_CAUSE_ILLEGAL_MS, "Illegal MS" }, + { GMM_CAUSE_ILLEGAL_ME, "Illegal ME" }, + { GMM_CAUSE_GPRS_NOTALLOWED, "GPRS services not allowed" }, + { GMM_CAUSE_GPRS_OTHER_NOTALLOWED, + "GPRS services and non-GPRS services not allowed" }, + { GMM_CAUSE_MS_ID_NOT_DERIVED, + "MS identity cannot be derived by the network" }, + { GMM_CAUSE_IMPL_DETACHED, "Implicitly detached" }, + { GMM_CAUSE_PLMN_NOTALLOWED, "PLMN not allowed" }, + { GMM_CAUSE_LA_NOTALLOWED, "Location Area not allowed" }, + { GMM_CAUSE_ROAMING_NOTALLOWED, + "Roaming not allowed in this location area" }, + { GMM_CAUSE_NO_GPRS_PLMN, + "GPRS services not allowed in this PLMN" }, + { GMM_CAUSE_MSC_TEMP_NOTREACH, "MSC temporarily not reachable" }, + { GMM_CAUSE_NET_FAIL, "Network failure" }, + { GMM_CAUSE_CONGESTION, "Congestion" }, + { GMM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" }, + { GMM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" }, + { GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL, + "Message type non-existant or not implemented" }, + { GMM_CAUSE_MSGT_INCOMP_P_STATE, + "Message type not compatible with protocol state" }, + { GMM_CAUSE_IE_NOTEXIST_NOTIMPL, + "Information element non-existent or not implemented" }, + { GMM_CAUSE_COND_IE_ERR, "Conditional IE error" }, + { GMM_CAUSE_MSG_INCOMP_P_STATE, + "Message not compatible with protocol state " }, + { GMM_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, + { 0, NULL } +}; + +/* 10.5.6.6 SM Cause / Table 10.5.157 */ +const struct value_string gsm_cause_names[] = { + { GSM_CAUSE_INSUFF_RSRC, "Insufficient resources" }, + { GSM_CAUSE_MISSING_APN, "Missing or unknown APN" }, + { GSM_CAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" }, + { GSM_CAUSE_AUTH_FAILED, "User Authentication failed" }, + { GSM_CAUSE_ACT_REJ_GGSN, "Activation rejected by GGSN" }, + { GSM_CAUSE_ACT_REJ_UNSPEC, "Activation rejected, unspecified" }, + { GSM_CAUSE_SERV_OPT_NOTSUPP, "Service option not supported" }, + { GSM_CAUSE_REQ_SERV_OPT_NOTSUB, + "Requested service option not subscribed" }, + { GSM_CAUSE_SERV_OPT_TEMP_OOO, + "Service option temporarily out of order" }, + { GSM_CAUSE_NSAPI_IN_USE, "NSAPI already used" }, + { GSM_CAUSE_DEACT_REGULAR, "Regular deactivation" }, + { GSM_CAUSE_QOS_NOT_ACCEPTED, "QoS not accepted" }, + { GSM_CAUSE_NET_FAIL, "Network Failure" }, + { GSM_CAUSE_REACT_RQD, "Reactivation required" }, + { GSM_CAUSE_FEATURE_NOTSUPP, "Feature not supported " }, + { GSM_CAUSE_INVALID_TRANS_ID, "Invalid transaction identifier" }, + { GSM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" }, + { GSM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" }, + { GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL, + "Message type non-existant or not implemented" }, + { GSM_CAUSE_MSGT_INCOMP_P_STATE, + "Message type not compatible with protocol state" }, + { GSM_CAUSE_IE_NOTEXIST_NOTIMPL, + "Information element non-existent or not implemented" }, + { GSM_CAUSE_COND_IE_ERR, "Conditional IE error" }, + { GSM_CAUSE_MSG_INCOMP_P_STATE, + "Message not compatible with protocol state " }, + { GSM_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, + { 0, NULL } +}; + +/* 10.5.5.2 */ +const struct value_string gprs_att_t_strs[] = { + { GPRS_ATT_T_ATTACH, "GPRS attach" }, + { GPRS_ATT_T_ATT_WHILE_IMSI, "GPRS attach while IMSI attached" }, + { GPRS_ATT_T_COMBINED, "Combined GPRS/IMSI attach" }, + { 0, NULL } +}; + +const struct value_string gprs_upd_t_strs[] = { + { GPRS_UPD_T_RA, "RA updating" }, + { GPRS_UPD_T_RA_LA, "combined RA/LA updating" }, + { GPRS_UPD_T_RA_LA_IMSI_ATT, "combined RA/LA updating + IMSI attach" }, + { GPRS_UPD_T_PERIODIC, "periodic updating" }, + { 0, NULL } +}; + +/* 10.5.5.5 */ +const struct value_string gprs_det_t_mo_strs[] = { + { GPRS_DET_T_MO_GPRS, "GPRS detach" }, + { GPRS_DET_T_MO_IMSI, "IMSI detach" }, + { GPRS_DET_T_MO_COMBINED, "Combined GPRS/IMSI detach" }, + { 0, NULL } +}; + +static const struct tlv_definition gsm48_gmm_att_tlvdef = { + .def = { + [GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_FIXED, 1 }, + [GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 }, + [GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 }, + [GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 }, + [GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 }, + [GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 }, + [GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 }, + }, +}; + +static const struct tlv_definition gsm48_sm_att_tlvdef = { + .def = { + [GSM48_IE_GSM_APN] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_PROTO_CONF_OPT] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_PDP_ADDR] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_AA_TMR] = { TLV_TYPE_TV, 1 }, + [GSM48_IE_GSM_NAME_FULL] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_NAME_SHORT] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GSM_TIMEZONE] = { TLV_TYPE_FIXED, 1 }, + [GSM48_IE_GSM_UTC_AND_TZ] = { TLV_TYPE_FIXED, 7 }, + [GSM48_IE_GSM_LSA_ID] = { TLV_TYPE_TLV, 0 }, + }, +}; + +/* 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)) + LOGP(DMM, LOGL_ERROR, "Starting MM timer %u while old " + "timer %u pending\n", T, mm->T); + mm->T = T; + mm->num_T_exp = 0; + + /* FIXME: we should do this only once ? */ + mm->timer.data = mm; + mm->timer.cb = &mmctx_timer_cb; + + osmo_timer_schedule(&mm->timer, seconds, 0); +} + +static void mmctx_timer_stop(struct sgsn_mm_ctx *mm, unsigned int T) +{ + if (mm->T != T) + LOGP(DMM, LOGL_ERROR, "Stopping MM timer %u but " + "%u is running\n", T, mm->T); + osmo_timer_del(&mm->timer); +} + +/* Send a message through the underlying layer */ +static int gsm48_gmm_sendmsg(struct msgb *msg, int command, + struct sgsn_mm_ctx *mm) +{ + if (mm) + rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_SIG_OUT]); + + /* caller needs to provide TLLI, BVCI and NSEI */ + return gprs_llc_tx_ui(msg, GPRS_SAPI_GMM, command, mm); +} + +/* copy identifiers from old message to new message, this + * is required so lower layers can route it correctly */ +static void gmm_copy_id(struct msgb *msg, const struct msgb *old) +{ + msgb_tlli(msg) = msgb_tlli(old); + msgb_bvci(msg) = msgb_bvci(old); + msgb_nsei(msg) = msgb_nsei(old); +} + +/* Store BVCI/NSEI in MM context */ +static void msgid2mmctx(struct sgsn_mm_ctx *mm, const struct msgb *msg) +{ + mm->bvci = msgb_bvci(msg); + mm->nsei = msgb_nsei(msg); +} + +/* Store BVCI/NSEI in MM context */ +static void mmctx2msgid(struct msgb *msg, const struct sgsn_mm_ctx *mm) +{ + msgb_tlli(msg) = mm->tlli; + msgb_bvci(msg) = mm->bvci; + msgb_nsei(msg) = mm->nsei; +} + +/* 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(gmm_cause_names, cause)); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + if (sm) { + gh->proto_discr = GSM48_PDISC_SM_GPRS; + gh->msg_type = GSM48_MT_GSM_STATUS; + } else { + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_STATUS; + } + gh->data[0] = cause; + + return gsm48_gmm_sendmsg(msg, 0, mmctx); +} +static int gsm48_tx_gmm_status(struct sgsn_mm_ctx *mmctx, uint8_t cause) +{ + struct msgb *msg = gsm48_msgb_alloc(); + + mmctx2msgid(msg, mmctx); + return _tx_status(msg, cause, mmctx, 0); +}; +static int gsm48_tx_gmm_status_oldmsg(struct msgb *oldmsg, uint8_t cause) +{ + struct msgb *msg = gsm48_msgb_alloc(); + + gmm_copy_id(msg, oldmsg); + return _tx_status(msg, cause, NULL, 0); +} +static int gsm48_tx_sm_status(struct sgsn_mm_ctx *mmctx, uint8_t cause) +{ + struct msgb *msg = gsm48_msgb_alloc(); + + mmctx2msgid(msg, mmctx); + return _tx_status(msg, cause, mmctx, 1); +}; +static int gsm48_tx_sm_status_oldmsg(struct msgb *oldmsg, uint8_t cause) +{ + struct msgb *msg = gsm48_msgb_alloc(); + + gmm_copy_id(msg, oldmsg); + return _tx_status(msg, cause, NULL, 1); +} + + +static struct gsm48_qos default_qos = { + .delay_class = 4, /* best effort */ + .reliab_class = GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT, + .peak_tput = GSM48_QOS_PEAK_TPUT_32000bps, + .preced_class = GSM48_QOS_PC_NORMAL, + .mean_tput = GSM48_QOS_MEAN_TPUT_BEST_EFFORT, + .traf_class = GSM48_QOS_TC_INTERACTIVE, + .deliv_order = GSM48_QOS_DO_UNORDERED, + .deliv_err_sdu = GSM48_QOS_ERRSDU_YES, + .max_sdu_size = GSM48_QOS_MAXSDU_1520, + .max_bitrate_up = GSM48_QOS_MBRATE_63k, + .max_bitrate_down = GSM48_QOS_MBRATE_63k, + .resid_ber = GSM48_QOS_RBER_5e_2, + .sdu_err_ratio = GSM48_QOS_SERR_1e_2, + .handling_prio = 3, + .xfer_delay = 0x10, /* 200ms */ + .guar_bitrate_up = GSM48_QOS_MBRATE_0k, + .guar_bitrate_down = GSM48_QOS_MBRATE_0k, + .sig_ind = 0, /* not optimised for signalling */ + .max_bitrate_down_ext = 0, /* use octet 9 */ + .guar_bitrate_down_ext = 0, /* use octet 13 */ +}; + +/* Chapter 9.4.2: Attach accept */ +static int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm48_attach_ack *aa; + uint8_t *mid; +#if 0 + uint8_t *ptsig; +#endif + + DEBUGP(DMM, "<- GPRS ATTACH ACCEPT (new P-TMSI=0x%08x)\n", mm->p_tmsi); + + mmctx2msgid(msg, mm); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_ATTACH_ACK; + + aa = (struct gsm48_attach_ack *) msgb_put(msg, sizeof(*aa)); + aa->force_stby = 0; /* not indicated */ + aa->att_result = 1; /* GPRS only */ + aa->ra_upd_timer = GPRS_TMR_MINUTE | 10; + 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; + + /* Optional: Negotiated Ready timer value */ +#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: MS-identity (combined attach) */ + /* Optional: GMM cause (partial attach result for combined attach) */ + + return gsm48_gmm_sendmsg(msg, 0, mm); +} + +/* Chapter 9.4.5: Attach reject */ +static int _tx_gmm_att_rej(struct msgb *msg, uint8_t gmm_cause) +{ + struct gsm48_hdr *gh; + + DEBUGP(DMM, "<- GPRS ATTACH REJECT\n"); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_ATTACH_REJ; + gh->data[0] = gmm_cause; + + return gsm48_gmm_sendmsg(msg, 0, NULL); +} +static int gsm48_tx_gmm_att_rej_oldmsg(const struct msgb *old_msg, + uint8_t gmm_cause) +{ + struct msgb *msg = gsm48_msgb_alloc(); + gmm_copy_id(msg, old_msg); + return _tx_gmm_att_rej(msg, gmm_cause); +} +static int gsm48_tx_gmm_att_rej(struct sgsn_mm_ctx *mm, + uint8_t gmm_cause) +{ + struct msgb *msg = gsm48_msgb_alloc(); + mmctx2msgid(msg, mm); + return _tx_gmm_att_rej(msg, gmm_cause); +} + +/* Chapter 9.4.6.2 Detach accept */ +static int gsm48_tx_gmm_det_ack(struct sgsn_mm_ctx *mm, uint8_t force_stby) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + DEBUGP(DMM, "<- GPRS DETACH ACCEPT\n"); + + 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_DETACH_ACK; + gh->data[0] = force_stby; + + return gsm48_gmm_sendmsg(msg, 0, mm); +} + +/* Transmit Chapter 9.4.12 Identity Request */ +static int gsm48_tx_gmm_id_req(struct sgsn_mm_ctx *mm, uint8_t id_type) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + DEBUGP(DMM, "<- GPRS IDENTITY REQUEST: mi_type=%02x\n", 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); +} + +#if 0 +/* Section 9.4.9: Authentication and Ciphering Request */ +static int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, uint8_t *rand, + uint8_t key_seq, uint8_t algo) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm48_auth_ciph_req *acreq; + uint8_t *m_rand, *m_cksn; + + DEBUGP(DMM, "<- GPRS AUTH AND CIPHERING REQ (rand = %s)\n", + osmo_hexdump(rand, 16)); + + mmctx2msgid(msg, mm); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_AUTH_CIPH_REQ; + + acreq = (struct gsm48_auth_ciph_req *) msgb_put(msg, sizeof(*acreq)); + acreq->ciph_alg = algo & 0xf; + acreq->imeisv_req = 0x1; + acreq->force_stby = 0x0; + acreq->ac_ref_nr = 0x0; /* FIXME: increment this? */ + + /* Only if authentication is requested we need to set RAND + CKSN */ + if (rand) { + m_rand = msgb_put(msg, 16+1); + m_rand[0] = GSM48_IE_GMM_AUTH_RAND; + memcpy(m_rand+1, rand, 16); + + m_cksn = msgb_put(msg, 1+1); + m_cksn[0] = GSM48_IE_GMM_CIPH_CKSN; + m_cksn[1] = key_seq; + } + + /* Start T3360 */ + mmctx_timer_start(mm, 3360, GSM0408_T3360_SECS); + + /* FIXME: make sure we don't send any other messages to the MS */ + + return gsm48_gmm_sendmsg(msg, 1, mm); +} + +/* Section 9.4.11: Authentication and Ciphering Reject */ +static int gsm48_tx_gmm_auth_ciph_rej(struct sgsn_mm_ctx *mm) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + DEBUGP(DMM, "<- 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); +} +#endif + +/* 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; + int rc; + + /* FIXME: Stop T3360 */ + + rc = tlv_parse(&tp, &gsm48_gmm_att_tlvdef, acr->data, + (msg->data + msg->len) - acr->data, 0, 0); + + /* FIXME: compare ac_ref? */ + + if (!TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_SRES) || + !TLVP_PRESENT(&tp, GSM48_IE_GMM_IMEISV)) { + /* FIXME: missing mandatory IE */ + } + + /* FIXME: compare SRES with what we expected */ + /* FIXME: enable LLC cipheirng */ + return 0; +} + +/* Check if we can already authorize a subscriber */ +static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx, + enum gprs_t3350_mode t3350_mode) +{ + if (strlen(ctx->imei) && strlen(ctx->imsi)) { +#ifdef PTMSI_ALLOC + /* Start T3350 and re-transmit up to 5 times until ATTACH COMPLETE */ + ctx->t3350_mode = t3350_mode; + mmctx_timer_start(ctx, 3350, GSM0408_T3350_SECS); +#endif + ctx->mm_state = GMM_REGISTERED_NORMAL; + return gsm48_tx_gmm_att_ack(ctx); + } + if (!strlen(ctx->imei)) { + ctx->mm_state = GMM_COMMON_PROC_INIT; + ctx->t3370_id_type = GSM_MI_TYPE_IMEI; + mmctx_timer_start(ctx, 3370, GSM0408_T3370_SECS); + return gsm48_tx_gmm_id_req(ctx, GSM_MI_TYPE_IMEI); + } + + if (!strlen(ctx->imsi)) { + ctx->mm_state = GMM_COMMON_PROC_INIT; + ctx->t3370_id_type = GSM_MI_TYPE_IMSI; + mmctx_timer_start(ctx, 3370, GSM0408_T3370_SECS); + return gsm48_tx_gmm_id_req(ctx, GSM_MI_TYPE_IMSI); + } + + return 0; +} + +/* 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]); + DEBUGP(DMM, "-> GMM IDENTITY RESPONSE: mi_type=0x%02x MI(%s) ", + mi_type, mi_string); + + if (!ctx) { + DEBUGP(DMM, "from unknown TLLI 0x%08x?!?\n", msgb_tlli(msg)); + 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) { + DEBUGP(DMM, "Deleting old MM Context for same IMSI " + "p_tmsi_old=0x%08x, p_tmsi_new=0x%08x\n", + ictx->p_tmsi, ctx->p_tmsi); + gprs_llgmm_assign(ictx->llme, ictx->tlli, + 0xffffffff, GPRS_ALGO_GEA0, NULL); + /* FIXME: this is a hard free, we don't + * clean-up any PDP contexts on the + * libgtp side */ + sgsn_mm_ctx_free(ictx); + } + } + strncpy(ctx->imsi, mi_string, sizeof(ctx->imei)); + break; + case GSM_MI_TYPE_IMEI: + strncpy(ctx->imei, mi_string, sizeof(ctx->imei)); + break; + case GSM_MI_TYPE_IMEISV: + break; + } + + DEBUGPC(DMM, "\n"); + /* Check if we can let the mobile station enter */ + return gsm48_gmm_authorize(ctx, ctx->t3350_mode); +} + +/* 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, *old_ra_info, *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; + + DEBUGP(DMM, "-> GMM ATTACH REQUEST "); + + /* As per TS 04.08 Chapter 4.7.1.4, the attach request arrives either + * with a foreign TLLI (P-TMSI that was allocated to the MS before), + * or with random TLLI. */ + + cid = bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); + + /* MS network capability 10.5.5.12 */ + msnc_len = *cur++; + msnc = cur; + if (msnc_len > sizeof(ctx->ms_network_capa.buf)) + goto err_inval; + cur += msnc_len; + + /* aTTACH Type 10.5.5.2 */ + att_type = *cur++ & 0x0f; + + /* DRX parameter 10.5.5.6 */ + drx_par = *cur++ << 8; + drx_par |= *cur++; + + /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ + mi_len = *cur++; + mi = cur; + if (mi_len > 8) + goto err_inval; + mi_type = *mi & GSM_MI_TYPE_MASK; + cur += mi_len; + + gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); + + DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, + get_value_string(gprs_att_t_strs, att_type)); + + /* Old routing area identification 10.5.5.15 */ + old_ra_info = cur; + 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; + + /* 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 + /* As a temorary hack, we simply assume that the IMSI exists, + * as long as it is part of 'our' network */ + char mccmnc[16]; + snprintf(mccmnc, sizeof(mccmnc), "%03d%02d", ra_id.mcc, ra_id.mnc); + if (strncmp(mccmnc, mi_string, 5) && + (sgsn->cfg.acl_enabled && + !sgsn_acl_lookup(mi_string))) { + LOGP(DMM, LOGL_INFO, "Rejecting ATTACH REQUESET IMSI=%s\n", + mi_string); + return gsm48_tx_gmm_att_rej_oldmsg(msg, + GMM_CAUSE_GPRS_NOTALLOWED); + } + ctx = sgsn_mm_ctx_alloc(0, &ra_id); + if (!ctx) + return gsm48_tx_gmm_att_rej_oldmsg(msg, GMM_CAUSE_NET_FAIL); + strncpy(ctx->imsi, mi_string, sizeof(ctx->imsi)); +#endif + } + ctx->tlli = msgb_tlli(msg); + ctx->llme = llme; + msgid2mmctx(ctx, msg); + break; + case GSM_MI_TYPE_TMSI: + memcpy(&tmsi, mi+1, 4); + tmsi = ntohl(tmsi); + /* Try to find MM context based on P-TMSI */ + if (!ctx) + ctx = sgsn_mm_ctx_by_ptmsi(tmsi); + if (!ctx) { + /* Allocate a context as most of our code expects one. + * Context will not have an IMSI ultil ID RESP is received */ + ctx = sgsn_mm_ctx_alloc(msgb_tlli(msg), &ra_id); + ctx->p_tmsi = tmsi; + } + ctx->tlli = msgb_tlli(msg); + ctx->llme = llme; + msgid2mmctx(ctx, msg); + break; + default: + LOGP(DMM, LOGL_NOTICE, "Rejecting ATTACH REQUEST with " + "MI type %u\n", mi_type); + return gsm48_tx_gmm_att_rej_oldmsg(msg, GMM_CAUSE_MS_ID_NOT_DERIVED); + } + /* Update MM Context with currient RA and Cell ID */ + ctx->ra = ra_id; + ctx->cell_id = cid; + /* Update MM Context with other data */ + ctx->drx_parms = drx_par; + ctx->ms_radio_access_capa.len = ms_ra_acc_cap_len; + memcpy(ctx->ms_radio_access_capa.buf, ms_ra_acc_cap, + ctx->ms_radio_access_capa.len); + ctx->ms_network_capa.len = msnc_len; + memcpy(ctx->ms_network_capa.buf, msnc, msnc_len); + +#ifdef PTMSI_ALLOC + /* Allocate a new P-TMSI (+ P-TMSI signature) and update TLLI */ + ctx->p_tmsi_old = ctx->p_tmsi; + ctx->p_tmsi = sgsn_alloc_ptmsi(); +#endif + /* Even if there is no P-TMSI allocated, the MS will switch from + * foreign TLLI to local TLLI */ + ctx->tlli_new = gprs_tmsi2tlli(ctx->p_tmsi, TLLI_LOCAL); + + /* Inform LLC layer about new TLLI but keep old active */ + gprs_llgmm_assign(ctx->llme, ctx->tlli, ctx->tlli_new, + GPRS_ALGO_GEA0, NULL); + + DEBUGPC(DMM, "\n"); + return gsm48_gmm_authorize(ctx, GMM_T3350_MODE_ATT); + +err_inval: + DEBUGPC(DMM, "\n"); + return gsm48_tx_gmm_att_rej_oldmsg(msg, GMM_CAUSE_SEM_INCORR_MSG); +} + +/* 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); + struct sgsn_pdp_ctx *pdp, *pdp2; + uint8_t detach_type, power_off; + int rc; + + 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 */ + + DEBUGP(DMM, "-> 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" : ""); + + /* Mark MM state as deregistered */ + ctx->mm_state = GMM_DEREGISTERED; + + /* delete all existing PDP contexts for this MS */ + llist_for_each_entry_safe(pdp, pdp2, &ctx->pdp_list, list) { + LOGP(DMM, LOGL_NOTICE, "Dropping PDP context for NSAPI=%u " + "due to GPRS DETACH REQUEST\n", pdp->nsapi); + sgsn_delete_pdp_ctx(pdp); + /* FIXME: the callback wants to transmit a DEACT PDP CTX ACK, + * which is quite stupid for a MS that has just detached.. */ + } + + /* force_stby = 0 */ + rc = gsm48_tx_gmm_det_ack(ctx, 0); + + /* TLLI unassignment */ + gprs_llgmm_assign(ctx->llme, ctx->tlli, 0xffffffff, + GPRS_ALGO_GEA0, NULL); + + return rc; +} + +/* Chapter 9.4.15: Routing area update accept */ +static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm48_ra_upd_ack *rua; + uint8_t *mid; + + DEBUGP(DMM, "<- 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_TMR_MINUTE | 10; + + 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 + + /* Option: MS ID, ... */ + return gsm48_gmm_sendmsg(msg, 0, mm); +} + +/* Chapter 9.4.17: Routing area update reject */ +static int gsm48_tx_gmm_ra_upd_rej(struct msgb *old_msg, uint8_t cause) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + DEBUGP(DMM, "<- ROUTING AREA UPDATE REJECT\n"); + + gmm_copy_id(msg, old_msg); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_RA_UPD_REJ; + gh->data[0] = cause; + gh->data[1] = 0; /* ? */ + + /* Option: P-TMSI signature, allocated P-TMSI, MS ID, ... */ + return gsm48_gmm_sendmsg(msg, 0, NULL); +} + +static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx, + const uint8_t *pdp_status) +{ + struct sgsn_pdp_ctx *pdp, *pdp2; + /* 24.008 4.7.5.1.3: If the PDP context status information element is + * included in ROUTING AREA UPDATE REQUEST message, then the network + * shall deactivate all those PDP contexts locally (without peer to + * peer signalling between the MS and the network), which are not in SM + * state PDP-INACTIVE on network side but are indicated by the MS as + * being in state PDP-INACTIVE. */ + + llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) { + if (pdp->nsapi < 8) { + if (!(pdp_status[0] & (1 << pdp->nsapi))) { + LOGP(DMM, LOGL_NOTICE, "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)))) { + LOGP(DMM, LOGL_NOTICE, "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) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); + uint8_t *cur = gh->data; + uint8_t *ms_ra_acc_cap; + uint8_t ms_ra_acc_cap_len; + struct gprs_ra_id old_ra_id; + struct tlv_parsed tp; + uint8_t upd_type; + int rc; + + /* Update Type 10.5.5.18 */ + upd_type = *cur++ & 0x0f; + + DEBUGP(DMM, "-> GMM RA UPDATE REQUEST type=\"%s\" ", + 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++; + ms_ra_acc_cap = cur; + if (ms_ra_acc_cap_len > 52) + return gsm48_tx_gmm_ra_upd_rej(msg, GMM_CAUSE_PROTO_ERR_UNSPEC); + cur += ms_ra_acc_cap_len; + + /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status, + * DRX parameter, MS network capability */ + rc = 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: + DEBUGPC(DMM, " unsupported in Mode III, is your SI13 corrupt?\n"); + return gsm48_tx_gmm_ra_upd_rej(msg, GMM_CAUSE_PROTO_ERR_UNSPEC); + break; + case GPRS_UPD_T_RA: + case GPRS_UPD_T_PERIODIC: + break; + } + + /* Look-up the MM context based on old RA-ID and TLLI */ + mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &old_ra_id); + if (!mmctx || mmctx->mm_state == GMM_DEREGISTERED) { + /* send a XID reset to re-set all LLC sequence numbers + * in the MS */ + DEBUGPC(DMM, " LLC XID RESET "); + gprs_llgmm_reset(llme); + /* The MS has to perform GPRS attach */ + DEBUGPC(DMM, " REJECT\n"); + /* Device is still IMSI atached for CS but initiate GPRS ATTACH */ + return gsm48_tx_gmm_ra_upd_rej(msg, GMM_CAUSE_MS_ID_NOT_DERIVED); + } + + /* Store new BVCI/NSEI in MM context (FIXME: delay until we ack?) */ + msgid2mmctx(mmctx, msg); + /* Bump the statistics of received signalling msgs for this MM context */ + rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); + + /* Update the MM context with the new RA-ID */ + bssgp_parse_cell_id(&mmctx->ra, msgb_bcid(msg)); + /* Update the MM context with the new (i.e. foreign) TLLI */ + mmctx->tlli = msgb_tlli(msg); + /* FIXME: Update the MM context with the MS radio acc capabilities */ + /* FIXME: Update the MM context with the MS network capabilities */ + + rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_RA_UPDATE]); + + DEBUGPC(DMM, " ACCEPT\n"); +#ifdef PTMSI_ALLOC + 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, GSM0408_T3350_SECS); +#endif + /* Even if there is no P-TMSI allocated, the MS will switch from + * foreign TLLI to local TLLI */ + mmctx->tlli_new = gprs_tmsi2tlli(mmctx->p_tmsi, TLLI_LOCAL); + + /* Inform LLC layer about new TLLI but keep old active */ + gprs_llgmm_assign(mmctx->llme, mmctx->tlli, mmctx->tlli_new, + GPRS_ALGO_GEA0, NULL); + + /* Look at PDP Context Status IE and see if MS's view of + * activated/deactivated NSAPIs agrees with our view */ + if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) { + const uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS); + process_ms_ctx_status(mmctx, pdp_status); + } + + /* Make sure we are NORMAL (i.e. not SUSPENDED anymore) */ + mmctx->mm_state = GMM_REGISTERED_NORMAL; + + /* Send RA UPDATE ACCEPT */ + return gsm48_tx_gmm_ra_upd_ack(mmctx); +} + +static int gsm48_rx_gmm_status(struct sgsn_mm_ctx *mmctx, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DMM, "-> GPRS MM STATUS (cause: %s)\n", + get_value_string(gmm_cause_names, gh->data[0])); + + return 0; +} + +/* GPRS Mobility Management */ +static int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg, + struct gprs_llc_llme *llme) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); + int rc; + + /* MMCTX can be NULL when called */ + + if (!mmctx && + gh->msg_type != GSM48_MT_GMM_ATTACH_REQ && + gh->msg_type != GSM48_MT_GMM_RA_UPD_REQ) { + LOGP(DMM, LOGL_NOTICE, "Cannot handle GMM for unknown MM CTX\n"); + gprs_llgmm_reset(llme); + return gsm48_tx_gmm_status_oldmsg(msg, GMM_CAUSE_MS_ID_NOT_DERIVED); + } + + 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_ID_RESP: + rc = gsm48_rx_gmm_id_resp(mmctx, msg); + break; + case GSM48_MT_GMM_STATUS: + rc = gsm48_rx_gmm_status(mmctx, msg); + break; + case GSM48_MT_GMM_DETACH_REQ: + rc = gsm48_rx_gmm_det_req(mmctx, msg); + break; + case GSM48_MT_GMM_ATTACH_COMPL: + /* only in case SGSN offered new P-TMSI */ + DEBUGP(DMM, "-> ATTACH COMPLETE\n"); + mmctx_timer_stop(mmctx, 3350); + mmctx->p_tmsi_old = 0; + /* Unassign the old TLLI */ + mmctx->tlli = mmctx->tlli_new; + gprs_llgmm_assign(mmctx->llme, 0xffffffff, mmctx->tlli_new, + GPRS_ALGO_GEA0, NULL); + rc = 0; + break; + case GSM48_MT_GMM_RA_UPD_COMPL: + /* only in case SGSN offered new P-TMSI */ + DEBUGP(DMM, "-> ROUTEING AREA UPDATE COMPLETE\n"); + mmctx_timer_stop(mmctx, 3350); + mmctx->p_tmsi_old = 0; + /* Unassign the old TLLI */ + mmctx->tlli = mmctx->tlli_new; + gprs_llgmm_assign(mmctx->llme, 0xffffffff, mmctx->tlli_new, + GPRS_ALGO_GEA0, NULL); + rc = 0; + break; + case GSM48_MT_GMM_PTMSI_REALL_COMPL: + DEBUGP(DMM, "-> PTMSI REALLLICATION COMPLETE\n"); + mmctx_timer_stop(mmctx, 3350); + mmctx->p_tmsi_old = 0; + /* Unassign the old TLLI */ + mmctx->tlli = mmctx->tlli_new; + //gprs_llgmm_assign(mmctx->llme, 0xffffffff, mmctx->tlli_new, GPRS_ALGO_GEA0, NULL); + rc = 0; + break; + case GSM48_MT_GMM_AUTH_CIPH_RESP: + rc = gsm48_rx_gmm_auth_ciph_resp(mmctx, msg); + break; + default: + DEBUGP(DMM, "Unknown GSM 04.08 GMM msg type 0x%02x\n", + gh->msg_type); + rc = gsm48_tx_gmm_status(mmctx, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL); + break; + } + + return rc; +} + +static void mmctx_timer_cb(void *_mm) +{ + struct sgsn_mm_ctx *mm = _mm; + + mm->num_T_exp++; + + switch (mm->T) { + case 3350: /* waiting for ATTACH COMPLETE */ + if (mm->num_T_exp >= 5) { + LOGP(DMM, LOGL_NOTICE, "T3350 expired >= 5 times\n"); + mm->mm_state = GMM_DEREGISTERED; + /* 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; + } + osmo_timer_schedule(&mm->timer, GSM0408_T3350_SECS, 0); + break; + case 3360: /* waiting for AUTH AND CIPH RESP */ + if (mm->num_T_exp >= 5) { + LOGP(DMM, LOGL_NOTICE, "T3360 expired >= 5 times\n"); + mm->mm_state = GMM_DEREGISTERED; + break; + } + /* FIXME: re-transmit the respective msg and re-start timer */ + osmo_timer_schedule(&mm->timer, GSM0408_T3360_SECS, 0); + break; + case 3370: /* waiting for IDENTITY RESPONSE */ + if (mm->num_T_exp >= 5) { + LOGP(DMM, LOGL_NOTICE, "T3370 expired >= 5 times\n"); + gsm48_tx_gmm_att_rej(mm, GMM_CAUSE_MS_ID_NOT_DERIVED); + mm->mm_state = GMM_DEREGISTERED; + break; + } + /* re-tranmit IDENTITY REQUEST and re-start timer */ + gsm48_tx_gmm_id_req(mm, mm->t3370_id_type); + osmo_timer_schedule(&mm->timer, GSM0408_T3370_SECS, 0); + break; + default: + LOGP(DMM, LOGL_ERROR, "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)) + LOGP(DMM, LOGL_ERROR, "Starting MM timer %u while old " + "timer %u pending\n", T, pdp->T); + pdp->T = T; + pdp->num_T_exp = 0; + + /* FIXME: we should do this only once ? */ + pdp->timer.data = pdp; + pdp->timer.cb = &pdpctx_timer_cb; + + osmo_timer_schedule(&pdp->timer, seconds, 0); +} + + +#if 0 +static void msgb_put_pdp_addr_ipv4(struct msgb *msg, uint32_t ipaddr) +{ + uint8_t v[6]; + + v[0] = PDP_TYPE_ORG_IETF; + v[1] = PDP_TYPE_N_IETF_IPv4; + *(uint32_t *)(v+2) = htonl(ipaddr); + + msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v); +} + +static void msgb_put_pdp_addr_ppp(struct msgb *msg) +{ + uint8_t v[2]; + + v[0] = PDP_TYPE_ORG_ETSI; + v[1] = PDP_TYPE_N_ETSI_PPP; + + msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v); +} +#endif + +/* Section 9.5.2: Ativate PDP Context Accept */ +int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + uint8_t transaction_id = pdp->ti ^ 0x8; /* flip */ + + DEBUGP(DMM, "<- ACTIVATE PDP CONTEXT ACK\n"); + + mmctx2msgid(msg, pdp->mm); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); + gh->msg_type = GSM48_MT_GSM_ACT_PDP_ACK; + + /* Negotiated LLC SAPI */ + msgb_v_put(msg, pdp->sapi); + + /* FIXME: copy QoS parameters from original request */ + //msgb_lv_put(msg, pdp->lib->qos_neg.l, pdp->lib->qos_neg.v); + msgb_lv_put(msg, sizeof(default_qos), (uint8_t *)&default_qos); + + /* Radio priority 10.5.7.2 */ + msgb_v_put(msg, pdp->lib->radio_pri); + + /* PDP address */ + /* Highest 4 bits of first byte need to be set to 1, otherwise + * the IE is identical with the 04.08 PDP Address IE */ + pdp->lib->eua.v[0] &= ~0xf0; + msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, + pdp->lib->eua.l, pdp->lib->eua.v); + pdp->lib->eua.v[0] |= 0xf0; + + /* Optional: Protocol configuration options (FIXME: why 'req') */ + if (pdp->lib->pco_req.l && pdp->lib->pco_req.v) + msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT, + pdp->lib->pco_req.l, pdp->lib->pco_req.v); + + /* Optional: Packet Flow Identifier */ + + return gsm48_gmm_sendmsg(msg, 0, pdp->mm); +} + +/* Section 9.5.3: Activate PDP Context reject */ +int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid, + uint8_t cause, uint8_t pco_len, uint8_t *pco_v) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + uint8_t transaction_id = tid ^ 0x8; /* flip */ + + DEBUGP(DMM, "<- ACTIVATE PDP CONTEXT REJ(cause=%u)\n", cause); + + mmctx2msgid(msg, mm); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); + gh->msg_type = GSM48_MT_GSM_ACT_PDP_REJ; + + msgb_v_put(msg, cause); + if (pco_len && pco_v) + msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT, pco_len, pco_v); + + return gsm48_gmm_sendmsg(msg, 0, mm); +} + +/* Section 9.5.8: Deactivate PDP Context Request */ +static int _gsm48_tx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, uint8_t tid, + uint8_t sm_cause) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + uint8_t transaction_id = tid ^ 0x8; /* flip */ + + DEBUGP(DMM, "<- DEACTIVATE PDP CONTEXT REQ\n"); + + mmctx2msgid(msg, mm); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); + gh->msg_type = GSM48_MT_GSM_DEACT_PDP_REQ; + + msgb_v_put(msg, sm_cause); + + return gsm48_gmm_sendmsg(msg, 0, mm); +} +int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause) +{ + pdpctx_timer_start(pdp, 3395, GSM0408_T3395_SECS); + + return _gsm48_tx_gsm_deact_pdp_req(pdp->mm, pdp->ti, sm_cause); +} + +/* Section 9.5.9: Deactivate PDP Context Accept */ +static int _gsm48_tx_gsm_deact_pdp_acc(struct sgsn_mm_ctx *mm, uint8_t tid) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + uint8_t transaction_id = tid ^ 0x8; /* flip */ + + DEBUGP(DMM, "<- DEACTIVATE PDP CONTEXT ACK\n"); + + mmctx2msgid(msg, mm); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); + gh->msg_type = GSM48_MT_GSM_DEACT_PDP_ACK; + + return gsm48_gmm_sendmsg(msg, 0, mm); +} +int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp) +{ + return _gsm48_tx_gsm_deact_pdp_acc(pdp->mm, pdp->ti); +} + +/* Section 9.5.1: Activate PDP Context Request */ +static int gsm48_rx_gsm_act_pdp_req(struct sgsn_mm_ctx *mmctx, + struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); + struct gsm48_act_pdp_ctx_req *act_req = (struct gsm48_act_pdp_ctx_req *) gh->data; + uint8_t req_qos_len, req_pdpa_len; + uint8_t *req_qos, *req_pdpa; + struct tlv_parsed tp; + uint8_t transaction_id = (gh->proto_discr >> 4); + struct sgsn_ggsn_ctx *ggsn; + struct sgsn_pdp_ctx *pdp; + + DEBUGP(DMM, "-> 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 */ + + /* 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)); + + 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; + } + + DEBUGPC(DMM, "\n"); + + /* 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; + + /* FIXME: determine GGSN based on APN and subscription options */ + if (TLVP_PRESENT(&tp, GSM48_IE_GSM_APN)) {} + + /* 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); + } + + /* Check if NSAPI is already in use */ + pdp = sgsn_pdp_ctx_by_nsapi(mmctx, act_req->req_nsapi); + if (pdp) { + /* We already have a PDP context for this TLLI + NSAPI tuple */ + if (pdp->sapi == act_req->req_llc_sapi && + pdp->ti == transaction_id) { + /* This apparently is a re-transmission of a PDP CTX + * ACT REQ (our ACT ACK must have got dropped) */ + return gsm48_tx_gsm_act_pdp_acc(pdp); + } + + /* Send reject with GSM_CAUSE_NSAPI_IN_USE */ + return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, + GSM_CAUSE_NSAPI_IN_USE, + 0, NULL); + } + + /* Only increment counter for a real activation, after we checked + * for re-transmissions */ + rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PDP_CTX_ACT]); + + ggsn = sgsn_ggsn_ctx_by_id(0); + if (!ggsn) { + LOGP(DGPRS, LOGL_ERROR, "No GGSN context 0 found!\n"); + return -EIO; + } + ggsn->gsn = sgsn->gsn; + pdp = sgsn_create_pdp_ctx(ggsn, mmctx, act_req->req_nsapi, &tp); + if (!pdp) + return -1; + + /* Store SAPI and Transaction Identifier */ + pdp->sapi = act_req->req_llc_sapi; + pdp->ti = transaction_id; + + return 0; +} + +/* Section 9.5.8: Deactivate PDP Context Request */ +static int gsm48_rx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); + uint8_t transaction_id = (gh->proto_discr >> 4); + struct sgsn_pdp_ctx *pdp; + + DEBUGP(DMM, "-> DEACTIVATE PDP CONTEXT REQ (cause: %s)\n", + get_value_string(gsm_cause_names, gh->data[0])); + + pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id); + if (!pdp) { + LOGP(DMM, LOGL_NOTICE, "Deactivate PDP Context Request for " + "non-existing PDP Context (IMSI=%s, TI=%u)\n", + mm->imsi, transaction_id); + return _gsm48_tx_gsm_deact_pdp_acc(mm, transaction_id); + } + + return sgsn_delete_pdp_ctx(pdp); +} + +/* Section 9.5.9: Deactivate PDP Context Accept */ +static int gsm48_rx_gsm_deact_pdp_ack(struct sgsn_mm_ctx *mm, struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); + uint8_t transaction_id = (gh->proto_discr >> 4); + struct sgsn_pdp_ctx *pdp; + + DEBUGP(DMM, "-> DEACTIVATE PDP CONTEXT ACK\n"); + + pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id); + if (!pdp) { + LOGP(DMM, LOGL_NOTICE, "Deactivate PDP Context Accept for " + "non-existing PDP Context (IMSI=%s, TI=%u)\n", + mm->imsi, transaction_id); + return 0; + } + + return sgsn_delete_pdp_ctx(pdp); +} + +static int gsm48_rx_gsm_status(struct sgsn_mm_ctx *ctx, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DMM, "-> GPRS SM STATUS (cause: %s)\n", + get_value_string(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) { + LOGP(DMM, LOGL_NOTICE, "T3395 expired >= 5 times\n"); + pdp->state = PDP_STATE_INACTIVE; + sgsn_delete_pdp_ctx(pdp); + break; + } + gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL); + osmo_timer_schedule(&pdp->timer, GSM0408_T3395_SECS, 0); + break; + default: + LOGP(DMM, LOGL_ERROR, "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"); + gsm48_tx_gmm_status_oldmsg(msg, GMM_CAUSE_IMPL_DETACHED); + return gsm48_tx_sm_status_oldmsg(msg, GSM_CAUSE_PROTO_ERR_UNSPEC); + } + + 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: + DEBUGP(DMM, "Unimplemented GSM 04.08 GSM msg type 0x%02x\n", + gh->msg_type); + rc = gsm48_tx_sm_status(mmctx, GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL); + break; + default: + DEBUGP(DMM, "Unknown GSM 04.08 GSM msg type 0x%02x\n", + gh->msg_type); + rc = gsm48_tx_sm_status(mmctx, GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL); + break; + + } + + return rc; +} + +/* Main entry point for incoming 04.08 GPRS messages */ +int gsm0408_gprs_rcvmsg(struct msgb *msg, struct gprs_llc_llme *llme) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); + uint8_t pdisc = gh->proto_discr & 0x0f; + struct sgsn_mm_ctx *mmctx; + struct gprs_ra_id ra_id; + int rc = -EINVAL; + + bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); + mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id); + if (mmctx) { + msgid2mmctx(mmctx, msg); + rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); + mmctx->llme = llme; + } + + /* MMCTX can be NULL */ + + switch (pdisc) { + case GSM48_PDISC_MM_GPRS: + rc = gsm0408_rcv_gmm(mmctx, msg, llme); + break; + case GSM48_PDISC_SM_GPRS: + rc = gsm0408_rcv_gsm(mmctx, msg, llme); + break; + default: + DEBUGP(DMM, "Unknown GSM 04.08 discriminator 0x%02x\n", + pdisc); + /* FIXME: return status message */ + break; + } + + return rc; +} + +int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli) +{ + struct sgsn_mm_ctx *mmctx; + + mmctx = sgsn_mm_ctx_by_tlli(tlli, raid); + if (!mmctx) { + LOGP(DMM, LOGL_NOTICE, "SUSPEND request for unknown " + "TLLI=%08x\n", tlli); + return -EINVAL; + } + + if (mmctx->mm_state != GMM_REGISTERED_NORMAL) { + LOGP(DMM, LOGL_NOTICE, "SUSPEND request while state " + "!= REGISTERED (TLLI=%08x)\n", tlli); + return -EINVAL; + } + + /* Transition from REGISTERED_NORMAL to REGISTERED_SUSPENDED */ + mmctx->mm_state = GMM_REGISTERED_SUSPENDED; + return 0; +} + +int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli, + uint8_t suspend_ref) +{ + struct sgsn_mm_ctx *mmctx; + + /* FIXME: make use of suspend reference? */ + + mmctx = sgsn_mm_ctx_by_tlli(tlli, raid); + if (!mmctx) { + LOGP(DMM, LOGL_NOTICE, "RESUME request for unknown " + "TLLI=%08x\n", tlli); + return -EINVAL; + } + + if (mmctx->mm_state != GMM_REGISTERED_SUSPENDED) { + LOGP(DMM, LOGL_NOTICE, "RESUME request while state " + "!= SUSPENDED (TLLI=%08x)\n", tlli); + /* FIXME: should we not simply ignore it? */ + return -EINVAL; + } + + /* Transition from SUSPENDED to NORMAL */ + mmctx->mm_state = GMM_REGISTERED_NORMAL; + return 0; +} diff --git a/src/gprs/gprs_llc.c b/src/gprs/gprs_llc.c new file mode 100644 index 000000000..3075573e9 --- /dev/null +++ b/src/gprs/gprs_llc.c @@ -0,0 +1,957 @@ +/* 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 + +/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU + * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */ +static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx) +{ + struct bssgp_dl_ud_par dup; + const uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x20 }; + + memset(&dup, 0, sizeof(dup)); + /* before we have received some identity from the MS, we might + * not yet have a MMC context (e.g. XID negotiation of primarly + * LLC connection fro GMM sapi). */ + if (mmctx) { + dup.imsi = mmctx->imsi; + dup.drx_parms = mmctx->drx_parms; + dup.ms_ra_cap.len = mmctx->ms_radio_access_capa.len; + dup.ms_ra_cap.v = mmctx->ms_radio_access_capa.buf; + } + 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[] = { + [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; + +/* If the TLLI is foreign, return its local version */ +static inline uint32_t tlli_foreign2local(uint32_t tlli) +{ + uint32_t new_tlli; + + if (gprs_tlli_type(tlli) == TLLI_FOREIGN) { + new_tlli = tlli | 0x40000000; + DEBUGP(DLLC, "TLLI 0x%08x is foreign, converting to " + "local TLLI 0x%08x\n", tlli, new_tlli); + } else + new_tlli = tlli; + + return new_tlli; +} + +/* lookup LLC Entity based on DLCI (TLLI+SAPI tuple) */ +static struct gprs_llc_lle *lle_by_tlli_sapi(uint32_t tlli, uint8_t sapi) +{ + struct gprs_llc_llme *llme; + + tlli = tlli_foreign2local(tlli); + + llist_for_each_entry(llme, &gprs_llc_llmes, list) { + if (llme->tlli == tlli || llme->old_tlli == tlli) + return &llme->lle[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; + + for (i = 0; i < ARRAY_SIZE(llme->lle); i++) + lle_init(llme, i); + + llist_add(&llme->list, &gprs_llc_llmes); + + return llme; +} + +static void llme_free(struct gprs_llc_llme *llme) +{ + llist_del(&llme->list); + talloc_free(llme); +} + +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, +}; + +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 } +}; + +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; +}; + +#define LLC_ALLOC_SIZE 16384 +#define UI_HDR_LEN 3 +#define N202 4 +#define CRC24_LENGTH 3 + +static 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; +} + +#if 0 +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: + ; + } + +} + +static void t201_expired(void *data) +{ + struct gprs_llc_lle *lle = data; + + if (lle->retrans_ctr < lle->params.n200) { + /* FIXME: transmit apropriate supervisory frame (8.6.4.1) */ + /* FIXME: set timer T201 */ + lle->retrans_ctr++; + } +} +#endif + +int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command, + enum gprs_llc_u_cmd u_cmd, int pf_bit) +{ + uint8_t *fcs, *llch; + uint8_t addr, ctrl; + uint32_t fcs_calc; + + /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ + + /* Address Field */ + addr = sapi & 0xf; + if (command) + addr |= 0x40; + + /* 6.3 Figure 8 */ + ctrl = 0xe0 | u_cmd; + if (pf_bit) + ctrl |= 0x10; + + /* prepend LLC UI header */ + llch = msgb_push(msg, 2); + llch[0] = addr; + llch[1] = ctrl; + + /* append FCS to end of frame */ + fcs = msgb_put(msg, 3); + fcs_calc = gprs_llc_fcs(llch, fcs - llch); + fcs[0] = fcs_calc & 0xff; + fcs[1] = (fcs_calc >> 8) & 0xff; + fcs[2] = (fcs_calc >> 16) & 0xff; + + /* Identifiers passed down: (BVCI, NSEI) */ + + /* Send BSSGP-DL-UNITDATA.req */ + return _bssgp_tx_dl_ud(msg, NULL); +} + +/* Send XID response to LLE */ +static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, + int command) +{ + /* copy identifiers from LLE to ensure lower layers can route */ + msgb_tlli(msg) = lle->llme->tlli; + msgb_bvci(msg) = lle->llme->bvci; + msgb_nsei(msg) = lle->llme->nsei; + + return gprs_llc_tx_u(msg, lle->sapi, command, GPRS_LLC_U_XID, 1); +} + +/* Transmit a UI frame over the given SAPI */ +int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command, + void *mmctx) +{ + struct gprs_llc_lle *lle; + uint8_t *fcs, *llch; + uint8_t addr, ctrl[2]; + uint32_t fcs_calc; + uint16_t nu = 0; + uint32_t oc; + + /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ + + /* look-up or create the LL Entity for this (TLLI, SAPI) tuple */ + lle = lle_by_tlli_sapi(msgb_tlli(msg), sapi); + if (!lle) { + struct gprs_llc_llme *llme; + LOGP(DLLC, LOGL_ERROR, "LLC TX: unknown TLLI 0x%08x, " + "creating LLME on the fly\n", msgb_tlli(msg)); + llme = llme_alloc(msgb_tlli(msg)); + lle = &llme->lle[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); + return -EFBIG; + } + + /* Update LLE's (BVCI, NSEI) tuple */ + lle->llme->bvci = msgb_bvci(msg); + lle->llme->nsei = msgb_nsei(msg); + + /* Obtain current values for N(u) and OC */ + nu = lle->vu_send; + oc = lle->oc_ui_send; + /* Increment V(U) */ + lle->vu_send = (lle->vu_send + 1) % 512; + /* Increment Overflow Counter, if needed */ + if ((lle->vu_send + 1) / 512) + lle->oc_ui_send += 512; + + /* Address Field */ + addr = sapi & 0xf; + if (command) + addr |= 0x40; + + /* Control Field */ + ctrl[0] = 0xc0; + ctrl[0] |= nu >> 6; + ctrl[1] = (nu << 2) & 0xfc; + ctrl[1] |= 0x01; /* Protected Mode */ + + /* prepend LLC UI header */ + llch = msgb_push(msg, 3); + llch[0] = addr; + llch[1] = ctrl[0]; + llch[2] = ctrl[1]; + + /* append FCS to end of frame */ + fcs = msgb_put(msg, 3); + fcs_calc = gprs_llc_fcs(llch, fcs - llch); + fcs[0] = fcs_calc & 0xff; + fcs[1] = (fcs_calc >> 8) & 0xff; + fcs[2] = (fcs_calc >> 16) & 0xff; + + /* encrypt information field + FCS, if needed! */ + if (lle->llme->algo != GPRS_ALGO_GEA0) { + uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */ + uint16_t crypt_len = (fcs + 3) - (llch + 3); + uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK]; + uint32_t iv; + int rc, i; + uint64_t kc = *(uint64_t *)&lle->llme->kc; + + /* Compute the 'Input' Paraemeter */ + iv = gprs_cipher_gen_input_ui(iov_ui, sapi, nu, oc); + + /* Compute the keystream that we need to XOR with the data */ + rc = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo, + kc, iv, GPRS_CIPH_SGSN2MS); + if (rc < 0) { + LOGP(DLLC, LOGL_ERROR, "Error crypting UI frame: %d\n", rc); + return rc; + } + + /* XOR the cipher output with the information field + FCS */ + for (i = 0; i < crypt_len; i++) + *(llch + 3 + i) ^= cipher_out[i]; + + /* Mark frame as encrypted */ + ctrl[1] |= 0x02; + } + + /* Identifiers passed down: (BVCI, NSEI) */ + + /* Send BSSGP-DL-UNITDATA.req */ + return _bssgp_tx_dl_ud(msg, mmctx); +} + +/* According to 6.4.1.6 / Figure 11 */ +static int msgb_put_xid_par(struct msgb *msg, uint8_t type, uint8_t length, uint8_t *data) +{ + uint8_t header_len = 1; + uint8_t *cur; + + /* type is a 5-bit field... */ + if (type > 0x1f) + return -EINVAL; + + if (length > 3) + header_len = 2; + + cur = msgb_put(msg, length + header_len); + + /* build the header without or with XL bit */ + if (length <= 3) { + *cur++ = (type << 2) | (length & 3); + } else { + *cur++ = 0x80 | (type << 2) | (length >> 6); + *cur++ = (length << 2); + } + + /* copy over the payload of the parameter*/ + memcpy(cur, data, length); + + return length + header_len; +} + +static void rx_llc_xid(struct gprs_llc_lle *lle, + struct gprs_llc_hdr_parsed *gph) +{ + /* FIXME: 8.5.3.3: check if XID is invalid */ + if (gph->is_cmd) { + /* FIXME: implement XID negotiation using SNDCP */ + struct msgb *resp; + uint8_t *xid; + resp = msgb_alloc_headroom(4096, 1024, "LLC_XID"); + xid = msgb_put(resp, gph->data_len); + memcpy(xid, gph->data, gph->data_len); + gprs_llc_tx_xid(lle, resp, 0); + } else { + /* FIXME: if we had sent a XID reset, send + * LLGMM-RESET.conf to GMM */ + /* FIXME: implement XID negotiation using SNDCP */ + } +} + +static void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph) +{ + DEBUGP(DLLC, "LLC SAPI=%u %c %c FCS=0x%06x", + gph->sapi, gph->is_cmd ? 'C' : 'R', gph->ack_req ? 'A' : ' ', + gph->fcs); + + if (gph->cmd) + DEBUGPC(DLLC, "CMD=%s ", get_value_string(llc_cmd_strs, gph->cmd)); + + if (gph->data) + DEBUGPC(DLLC, "DATA "); + + DEBUGPC(DLLC, "\n"); +} +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: + ; + } + + return 0; +} + +/* parse a GPRS LLC header, also check for invalid frames */ +static 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; +} + +/* receive an incoming LLC PDU (BSSGP-UL-UNITDATA-IND, 7.2.4.2) */ +int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) +{ +#if 0 + struct bssgp_ud_hdr *udh = (struct bssgp_ud_hdr *) msgb_bssgph(msg); +#endif + struct gprs_llc_hdr *lh = (struct gprs_llc_hdr *) msgb_llch(msg); + struct gprs_llc_hdr_parsed llhp; + struct gprs_llc_lle *lle; + int rc = 0; + + /* Identifiers from DOWN: NSEI, BVCI, TLLI */ + + memset(&llhp, 0, sizeof(llhp)); + rc = gprs_llc_hdr_parse(&llhp, (uint8_t *) lh, TLVP_LEN(tv, BSSGP_IE_LLC_PDU)); + gprs_llc_hdr_dump(&llhp); + if (rc < 0) { + LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n"); + return rc; + } + + switch (gprs_tlli_type(msgb_tlli(msg))) { + case TLLI_LOCAL: + case TLLI_FOREIGN: + case TLLI_RANDOM: + case TLLI_AUXILIARY: + break; + default: + LOGP(DLLC, LOGL_ERROR, + "Discarding frame with strange TLLI type\n"); + break; + } + + /* find the LLC Entity for this TLLI+SAPI tuple */ + lle = lle_by_tlli_sapi(msgb_tlli(msg), llhp.sapi); + + /* 7.2.1.1 LLC belonging to unassigned TLLI+SAPI shall be discarded, + * except UID and XID frames with SAPI=1 */ + if (!lle) { + if (llhp.sapi == GPRS_SAPI_GMM && + (llhp.cmd == GPRS_LLC_XID || llhp.cmd == GPRS_LLC_UI)) { + struct gprs_llc_llme *llme; + /* FIXME: don't use the TLLI but the 0xFFFF unassigned? */ + llme = llme_alloc(msgb_tlli(msg)); + LOGP(DLLC, LOGL_DEBUG, "LLC RX: unknown TLLI 0x%08x, " + "creating LLME on the fly\n", msgb_tlli(msg)); + lle = &llme->lle[llhp.sapi]; + } else { + LOGP(DLLC, LOGL_NOTICE, + "unknown TLLI/SAPI: Silently dropping\n"); + return 0; + } + } + + /* decrypt information field + FCS, if needed! */ + if (llhp.is_encrypted) { + uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */ + uint16_t crypt_len = llhp.data_len + 3; + uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK]; + uint32_t iv; + uint64_t kc = *(uint64_t *)&lle->llme->kc; + int rc, i; + + if (lle->llme->algo == GPRS_ALGO_GEA0) { + LOGP(DLLC, LOGL_NOTICE, "encrypted frame for LLC that " + "has no KC/Algo! Dropping.\n"); + return 0; + } + + iv = gprs_cipher_gen_input_ui(iov_ui, lle->sapi, llhp.seq_tx, + lle->oc_ui_recv); + rc = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo, + kc, iv, GPRS_CIPH_MS2SGSN); + if (rc < 0) { + LOGP(DLLC, LOGL_ERROR, "Error decrypting frame: %d\n", + rc); + return rc; + } + + /* XOR the cipher output with the information field + FCS */ + for (i = 0; i < crypt_len; i++) + *(llhp.data + i) ^= cipher_out[i]; + } else { + if (lle->llme->algo != GPRS_ALGO_GEA0) { + LOGP(DLLC, LOGL_NOTICE, "unencrypted frame for LLC " + "that is supposed to be encrypted. Dropping.\n"); + return 0; + } + } + + /* We have to do the FCS check _after_ decryption */ + llhp.fcs_calc = gprs_llc_fcs((uint8_t *)lh, llhp.crc_length); + if (llhp.fcs != llhp.fcs_calc) { + LOGP(DLLC, LOGL_INFO, "Dropping frame with invalid FCS\n"); + return -EIO; + } + + /* Update LLE's (BVCI, NSEI) tuple */ + lle->llme->bvci = msgb_bvci(msg); + lle->llme->nsei = msgb_nsei(msg); + + /* Receive and Process the actual LLC frame */ + rc = gprs_llc_hdr_rx(&llhp, lle); + if (rc < 0) + return rc; + + /* llhp.data is only set when we need to send LL_[UNIT]DATA_IND up */ + if (llhp.data) { + msgb_gmmh(msg) = llhp.data; + switch (llhp.sapi) { + case GPRS_SAPI_GMM: + /* send LL_UNITDATA_IND to GMM */ + rc = gsm0408_gprs_rcvmsg(msg, lle->llme); + break; + case GPRS_SAPI_SNDCP3: + case GPRS_SAPI_SNDCP5: + case GPRS_SAPI_SNDCP9: + case GPRS_SAPI_SNDCP11: + /* send LL_DATA_IND/LL_UNITDATA_IND to SNDCP */ + rc = sndcp_llunitdata_ind(msg, lle, llhp.data, llhp.data_len); + break; + case GPRS_SAPI_SMS: + /* FIXME */ + case GPRS_SAPI_TOM2: + case GPRS_SAPI_TOM8: + /* FIXME: send LL_DATA_IND/LL_UNITDATA_IND to TOM */ + default: + LOGP(DLLC, LOGL_NOTICE, "Unsupported SAPI %u\n", llhp.sapi); + rc = -EINVAL; + break; + } + } + + return rc; +} + +/* 04.64 Chapter 7.2.1.1 LLGMM-ASSIGN */ +int gprs_llgmm_assign(struct gprs_llc_llme *llme, + uint32_t old_tlli, uint32_t new_tlli, + enum gprs_ciph_algo alg, const uint8_t *kc) +{ + unsigned int i; + + /* Update the crypto parameters */ + llme->algo = alg; + if (alg != GPRS_ALGO_GEA0) + memcpy(llme->kc, kc, sizeof(llme->kc)); + + if (old_tlli == 0xffffffff && new_tlli != 0xffffffff) { + /* TLLI Assignment 8.3.1 */ + /* New TLLI shall be assigned and used when (re)transmitting LLC frames */ + /* If old TLLI != 0xffffffff was assigned to LLME, then TLLI + * old is unassigned. Only TLLI new shall be accepted when + * received from peer. */ + if (llme->old_tlli != 0xffffffff) { + llme->old_tlli = 0xffffffff; + llme->tlli = new_tlli; + } else { + /* If TLLI old == 0xffffffff was assigned to LLME, then this is + * TLLI assignmemt according to 8.3.1 */ + llme->old_tlli = 0xffffffff; + llme->tlli = new_tlli; + llme->state = GPRS_LLMS_ASSIGNED; + /* 8.5.3.1 For all LLE's */ + for (i = 0; i < ARRAY_SIZE(llme->lle); i++) { + struct gprs_llc_lle *l = &llme->lle[i]; + l->vu_send = l->vu_recv = 0; + l->retrans_ctr = 0; + l->state = GPRS_LLES_ASSIGNED_ADM; + /* FIXME Set parameters according to table 9 */ + } + } + } else if (old_tlli != 0xffffffff && new_tlli != 0xffffffff) { + /* TLLI Change 8.3.2 */ + /* Both TLLI Old and TLLI New are assigned; use New when + * (re)transmitting. Accept both Old and New on Rx */ + llme->old_tlli = llme->tlli; + llme->tlli = new_tlli; + llme->state = GPRS_LLMS_ASSIGNED; + } else if (old_tlli != 0xffffffff && new_tlli == 0xffffffff) { + /* TLLI Unassignment 8.3.3) */ + llme->tlli = llme->old_tlli = 0; + llme->state = GPRS_LLMS_UNASSIGNED; + for (i = 0; i < ARRAY_SIZE(llme->lle); i++) { + struct gprs_llc_lle *l = &llme->lle[i]; + l->state = GPRS_LLES_UNASSIGNED; + } + llme_free(llme); + } else + return -EINVAL; + + return 0; +} + +/* Chapter 7.2.1.2 LLGMM-RESET.req */ +int gprs_llgmm_reset(struct gprs_llc_llme *llme) +{ + struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID"); + int random = rand(); + + /* First XID component must be RESET */ + msgb_put_xid_par(msg, GPRS_LLC_XID_T_RESET, 0, NULL); + /* randomly select new IOV-UI */ + msgb_put_xid_par(msg, GPRS_LLC_XID_T_IOV_UI, 4, (uint8_t *) &random); + + /* FIXME: Start T200, wait for XID response */ + return gprs_llc_tx_xid(&llme->lle[1], msg, 1); +} + +int gprs_llc_init(const char *cipher_plugin_path) +{ + return gprs_cipher_load(cipher_plugin_path); +} diff --git a/src/gprs/gprs_llc_vty.c b/src/gprs/gprs_llc_vty.c new file mode 100644 index 000000000..ab5269922 --- /dev/null +++ b/src/gprs/gprs_llc_vty.c @@ -0,0 +1,108 @@ +/* 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 + +struct value_string gprs_llc_state_strs[] = { + { GPRS_LLES_UNASSIGNED, "TLLI Unassigned" }, + { GPRS_LLES_ASSIGNED_ADM, "TLLI Assigned" }, + { GPRS_LLES_LOCAL_EST, "Local Establishment" }, + { GPRS_LLES_REMOTE_EST, "Remote Establishment" }, + { GPRS_LLES_ABM, "Asynchronous Balanced Mode" }, + { GPRS_LLES_LOCAL_REL, "Local Release" }, + { GPRS_LLES_TIMER_REC, "Timer Recovery" }, +}; + +static void vty_dump_lle(struct vty *vty, struct gprs_llc_lle *lle) +{ + struct gprs_llc_params *par = &lle->params; + vty_out(vty, " SAPI %2u State %s VUsend=%u, VUrecv=%u", lle->sapi, + get_value_string(gprs_llc_state_strs, lle->state), + lle->vu_send, lle->vu_recv); + vty_out(vty, " Vsent=%u Vack=%u Vrecv=%u, RetransCtr=%u%s", + lle->v_sent, lle->v_ack, lle->v_recv, + lle->retrans_ctr, VTY_NEWLINE); + vty_out(vty, " T200=%u, N200=%u, N201-U=%u, N201-I=%u, mD=%u, " + "mU=%u, kD=%u, kU=%u%s", par->t200_201, par->n200, + par->n201_u, par->n201_i, par->mD, par->mU, par->kD, + par->kU, VTY_NEWLINE); +} + +static uint8_t valid_sapis[] = { 1, 2, 3, 5, 7, 8, 9, 11 }; + +static void vty_dump_llme(struct vty *vty, struct gprs_llc_llme *llme) +{ + unsigned int i; + + vty_out(vty, "TLLI %08x (Old TLLI %08x) BVCI=%u NSEI=%u: State %s%s", + llme->tlli, llme->old_tlli, llme->bvci, llme->nsei, + get_value_string(gprs_llc_state_strs, llme->state), VTY_NEWLINE); + + for (i = 0; i < ARRAY_SIZE(valid_sapis); i++) { + struct gprs_llc_lle *lle; + uint8_t sapi = valid_sapis[i]; + + if (sapi >= ARRAY_SIZE(llme->lle)) + continue; + + lle = &llme->lle[sapi]; + vty_dump_lle(vty, lle); + } +} + + +DEFUN(show_llc, show_llc_cmd, + "show llc", + SHOW_STR "Display information about the LLC protocol") +{ + struct gprs_llc_llme *llme; + + vty_out(vty, "State of LLC Entities%s", VTY_NEWLINE); + llist_for_each_entry(llme, &gprs_llc_llmes, list) { + vty_dump_llme(vty, llme); + } + return CMD_SUCCESS; +} + +int gprs_llc_vty_init(void) +{ + install_element_ve(&show_llc_cmd); + + return 0; +} diff --git a/src/gprs/gprs_sgsn.c b/src/gprs/gprs_sgsn.c new file mode 100644 index 000000000..84bf512ae --- /dev/null +++ b/src/gprs/gprs_sgsn.c @@ -0,0 +1,404 @@ +/* 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 + +extern struct sgsn_instance *sgsn; + +LLIST_HEAD(sgsn_mm_ctxts); +LLIST_HEAD(sgsn_ggsn_ctxts); +LLIST_HEAD(sgsn_apn_ctxts); +LLIST_HEAD(sgsn_pdp_ctxts); + +static const struct rate_ctr_desc mmctx_ctr_description[] = { + { "sign.packets.in", "Signalling Messages ( In)" }, + { "sign.packets.out", "Signalling Messages (Out)" }, + { "udata.packets.in", "User Data Messages ( In)" }, + { "udata.packets.out", "User Data Messages (Out)" }, + { "udata.bytes.in", "User Data Bytes ( In)" }, + { "udata.bytes.out", "User Data Bytes (Out)" }, + { "pdp_ctx_act", "PDP Context Activations " }, + { "suspend", "SUSPEND Count " }, + { "paging.ps", "Paging Packet Switched " }, + { "paging.cs", "Paging Circuit Switched " }, + { "ra_update", "Routing Area Update " }, +}; + +static const struct rate_ctr_group_desc mmctx_ctrg_desc = { + .group_name_prefix = "sgsn.mmctx", + .group_description = "SGSN MM Context Statistics", + .num_ctr = ARRAY_SIZE(mmctx_ctr_description), + .ctr_desc = mmctx_ctr_description, +}; + +static const struct rate_ctr_desc pdpctx_ctr_description[] = { + { "udata.packets.in", "User Data Messages ( In)" }, + { "udata.packets.out", "User Data Messages (Out)" }, + { "udata.bytes.in", "User Data Bytes ( In)" }, + { "udata.bytes.out", "User Data Bytes (Out)" }, +}; + +static const struct rate_ctr_group_desc pdpctx_ctrg_desc = { + .group_name_prefix = "sgsn.pdpctx", + .group_description = "SGSN PDP Context Statistics", + .num_ctr = ARRAY_SIZE(pdpctx_ctr_description), + .ctr_desc = pdpctx_ctr_description, +}; + +static int ra_id_equals(const struct gprs_ra_id *id1, + const struct gprs_ra_id *id2) +{ + return (id1->mcc == id2->mcc && id1->mnc == id2->mnc && + id1->lac == id2->lac && id1->rac == id2->rac); +} + +/* See 03.02 Chapter 2.6 */ +static inline uint32_t tlli_foreign(uint32_t tlli) +{ + return ((tlli | 0x80000000) & ~0x40000000); +} + +/* look-up a SGSN MM context based on TLLI + RAI */ +struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli, + const struct gprs_ra_id *raid) +{ + struct sgsn_mm_ctx *ctx; + int tlli_type; + + llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + if (tlli == ctx->tlli && + ra_id_equals(raid, &ctx->ra)) + return ctx; + } + + tlli_type = gprs_tlli_type(tlli); + switch (tlli_type) { + case TLLI_LOCAL: + llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + if ((ctx->p_tmsi | 0xC0000000) == tlli || + (ctx->p_tmsi_old && (ctx->p_tmsi_old | 0xC0000000) == tlli)) { + ctx->tlli = tlli; + return ctx; + } + } + break; + case TLLI_FOREIGN: + llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + if (tlli == tlli_foreign(ctx->tlli) && + ra_id_equals(raid, &ctx->ra)) + return ctx; + } + break; + default: + break; + } + + return NULL; +} + +struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t p_tmsi) +{ + struct sgsn_mm_ctx *ctx; + + llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + if (p_tmsi == ctx->p_tmsi || + (ctx->p_tmsi_old && ctx->p_tmsi_old == p_tmsi)) + return ctx; + } + return NULL; +} + +struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi) +{ + struct sgsn_mm_ctx *ctx; + + llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { + if (!strcmp(imsi, ctx->imsi)) + return ctx; + } + return NULL; + +} + +/* Allocate a new SGSN MM context */ +struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli, + const struct gprs_ra_id *raid) +{ + struct sgsn_mm_ctx *ctx; + + ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx); + if (!ctx) + return NULL; + + memcpy(&ctx->ra, raid, sizeof(ctx->ra)); + ctx->tlli = tlli; + ctx->mm_state = GMM_DEREGISTERED; + ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli); + INIT_LLIST_HEAD(&ctx->pdp_list); + + llist_add(&ctx->list, &sgsn_mm_ctxts); + + return ctx; +} + +/* this is a hard _free_ function, it doesn't clean up the PDP contexts + * in libgtp! */ +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); +} + +/* look up PDP context by MM context and NSAPI */ +struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm, + uint8_t nsapi) +{ + struct sgsn_pdp_ctx *pdp; + + llist_for_each_entry(pdp, &mm->pdp_list, list) { + if (pdp->nsapi == nsapi) + return pdp; + } + return NULL; +} + +/* look up PDP context by MM context and transaction ID */ +struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm, + uint8_t tid) +{ + struct sgsn_pdp_ctx *pdp; + + llist_for_each_entry(pdp, &mm->pdp_list, list) { + if (pdp->ti == tid) + return pdp; + } + return NULL; +} + +/* you don't want to use this directly, call sgsn_create_pdp_ctx() */ +struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm, + uint8_t nsapi) +{ + struct sgsn_pdp_ctx *pdp; + + pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi); + if (pdp) + return NULL; + + pdp = talloc_zero(tall_bsc_ctx, struct sgsn_pdp_ctx); + if (!pdp) + return NULL; + + pdp->mm = mm; + pdp->nsapi = nsapi; + pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi); + llist_add(&pdp->list, &mm->pdp_list); + llist_add(&pdp->g_list, &sgsn_pdp_ctxts); + + return pdp; +} + +#include +/* you probably want to call sgsn_delete_pdp_ctx() instead */ +void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp) +{ + rate_ctr_group_free(pdp->ctrg); + 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; + LOGP(DGPRS, LOGL_NOTICE, "freeing PDP context that still " + "has a libgtp handle attached to it, this shouldn't " + "happen!\n"); + osmo_generate_backtrace(); + lib->priv = NULL; + } + + 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; +} + +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 */ + +#if 0 +struct apn_ctx *apn_ctx_alloc(const char *ap_name) +{ + struct apn_ctx *actx; + + actx = talloc_zero(talloc_bsc_ctx, struct apn_ctx); + if (!actx) + return NULL; + actx->name = talloc_strdup(actx, ap_name); + + return actx; +} + +struct apn_ctx *apn_ctx_by_name(const char *name) +{ + struct apn_ctx *actx; + + llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { + if (!strcmp(name, actx->name)) + return actx; + } + return NULL; +} + +struct apn_ctx *apn_ctx_find_alloc(const char *name) +{ + struct apn_ctx *actx; + + actx = apn_ctx_by_name(name); + if (!actx) + actx = apn_ctx_alloc(name); + + return actx; +} +#endif + +uint32_t sgsn_alloc_ptmsi(void) +{ + struct sgsn_mm_ctx *mm; + uint32_t ptmsi; + +restart: + ptmsi = rand(); + llist_for_each_entry(mm, &sgsn_mm_ctxts, list) { + if (mm->p_tmsi == ptmsi) + goto restart; + } + + return ptmsi; +} + +static void drop_one_pdp(struct sgsn_pdp_ctx *pdp) +{ + if (pdp->mm->mm_state == GMM_REGISTERED_NORMAL) + gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL); + else { + /* FIXME: GPRS paging in case MS is SUSPENDED */ + LOGP(DGPRS, LOGL_NOTICE, "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 + * ottherwise 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; +} diff --git a/src/gprs/gprs_sndcp.c b/src/gprs/gprs_sndcp.c new file mode 100644 index 000000000..e901ceb96 --- /dev/null +++ b/src/gprs/gprs_sndcp.c @@ -0,0 +1,620 @@ +/* GPRS SNDCP protocol implementation as per 3GPP TS 04.65 */ + +/* (C) 2010 by Harald Welte + * (C) 2010 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gprs_sndcp.h" + +/* Chapter 7.2: SN-PDU Formats */ +struct sndcp_common_hdr { + /* octet 1 */ + uint8_t nsapi:4; + uint8_t more:1; + uint8_t type:1; + uint8_t first:1; + uint8_t spare:1; +} __attribute__((packed)); + +/* PCOMP / DCOMP only exist in first fragment */ +struct sndcp_comp_hdr { + /* octet 2 */ + uint8_t pcomp:4; + uint8_t dcomp:4; +} __attribute__((packed)); + +struct sndcp_udata_hdr { + /* octet 3 */ + uint8_t npdu_high:4; + uint8_t seg_nr:4; + /* octet 4 */ + uint8_t npdu_low; +} __attribute__((packed)); + + +static void *tall_sndcp_ctx; + +/* A fragment queue entry, containing one framgent of a N-PDU */ +struct defrag_queue_entry { + struct llist_head list; + /* segment number of this fragment */ + uint32_t seg_nr; + /* length of the data area of this fragment */ + uint32_t data_len; + /* pointer to the data of this fragment */ + uint8_t *data; +}; + +LLIST_HEAD(gprs_sndcp_entities); + +/* Enqueue a fragment into the defragment queue */ +static int defrag_enqueue(struct gprs_sndcp_entity *sne, uint8_t seg_nr, + uint8_t *data, uint32_t data_len) +{ + struct defrag_queue_entry *dqe; + + dqe = talloc_zero(tall_sndcp_ctx, struct defrag_queue_entry); + if (!dqe) + return -ENOMEM; + dqe->data = talloc_zero_size(dqe, data_len); + if (!dqe->data) { + talloc_free(dqe); + return -ENOMEM; + } + dqe->seg_nr = seg_nr; + dqe->data_len = data_len; + + llist_add(&dqe->list, &sne->defrag.frag_list); + + if (seg_nr > sne->defrag.highest_seg) + sne->defrag.highest_seg = seg_nr; + + sne->defrag.seg_have |= (1 << seg_nr); + sne->defrag.tot_len += data_len; + + memcpy(dqe->data, data, data_len); + + return 0; +} + +/* return if we have all segments of this N-PDU */ +static int defrag_have_all_segments(struct gprs_sndcp_entity *sne) +{ + uint32_t seg_needed = 0; + unsigned int i; + + /* create a bitmask of needed segments */ + for (i = 0; i <= sne->defrag.highest_seg; i++) + seg_needed |= (1 << i); + + if (seg_needed == sne->defrag.seg_have) + return 1; + + return 0; +} + +static struct defrag_queue_entry *defrag_get_seg(struct gprs_sndcp_entity *sne, + uint32_t seg_nr) +{ + struct defrag_queue_entry *dqe; + + llist_for_each_entry(dqe, &sne->defrag.frag_list, list) { + if (dqe->seg_nr == seg_nr) { + llist_del(&dqe->list); + return dqe; + } + } + return NULL; +} + +/* Perform actual defragmentation and create an output packet */ +static int defrag_segments(struct gprs_sndcp_entity *sne) +{ + struct msgb *msg; + unsigned int seg_nr; + uint8_t *npdu; + + LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Defragment output PDU %u " + "num_seg=%u tot_len=%u\n", sne->lle->llme->tlli, sne->nsapi, + sne->defrag.npdu, sne->defrag.highest_seg, sne->defrag.tot_len); + msg = msgb_alloc_headroom(sne->defrag.tot_len+256, 128, "SNDCP Defrag"); + if (!msg) + return -ENOMEM; + + /* FIXME: message headers + identifiers */ + + npdu = msg->data; + + for (seg_nr = 0; seg_nr <= sne->defrag.highest_seg; seg_nr++) { + struct defrag_queue_entry *dqe; + uint8_t *data; + + dqe = defrag_get_seg(sne, seg_nr); + if (!dqe) { + LOGP(DSNDCP, LOGL_ERROR, "Segment %u missing\n", seg_nr); + msgb_free(msg); + return -EIO; + } + /* actually append the segment to the N-PDU */ + data = msgb_put(msg, dqe->data_len); + memcpy(data, dqe->data, dqe->data_len); + + /* release memory for the fragment queue entry */ + talloc_free(dqe); + } + + /* FIXME: cancel timer */ + + /* actually send the N-PDU to the SGSN core code, which then + * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ + return sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli, + sne->nsapi, msg, sne->defrag.tot_len, npdu); +} + +static int defrag_input(struct gprs_sndcp_entity *sne, struct msgb *msg, uint8_t *hdr, + unsigned int len) +{ + struct sndcp_common_hdr *sch; + struct sndcp_comp_hdr *scomph = NULL; + struct sndcp_udata_hdr *suh; + uint16_t npdu_num; + uint8_t *data; + int rc; + + 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)); + + data = (uint8_t *)suh + sizeof(struct sndcp_udata_hdr); + + npdu_num = (suh->npdu_high << 8) | suh->npdu_low; + + LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Input PDU %u Segment %u " + "Length %u %s %s\n", sne->lle->llme->tlli, sne->nsapi, npdu_num, + suh->seg_nr, len, sch->first ? "F " : "", sch->more ? "M" : ""); + + if (sch->first) { + /* first segment of a new packet. Discard all leftover fragments of + * previous packet */ + if (!llist_empty(&sne->defrag.frag_list)) { + struct defrag_queue_entry *dqe, *dqe2; + LOGP(DSNDCP, LOGL_INFO, "TLLI=0x%08x NSAPI=%u: Dropping " + "SN-PDU %u due to insufficient segments (%04x)\n", + sne->lle->llme->tlli, sne->nsapi, sne->defrag.npdu, + sne->defrag.seg_have); + llist_for_each_entry_safe(dqe, dqe2, &sne->defrag.frag_list, list) { + llist_del(&dqe->list); + talloc_free(dqe); + } + } + /* store the currently de-fragmented PDU number */ + sne->defrag.npdu = npdu_num; + + /* Re-set fragmentation state */ + sne->defrag.no_more = sne->defrag.highest_seg = sne->defrag.seg_have = 0; + sne->defrag.tot_len = 0; + /* FIXME: (re)start timer */ + } + + if (sne->defrag.npdu != npdu_num) { + LOGP(DSNDCP, LOGL_INFO, "Segment for different SN-PDU " + "(%u != %u)\n", npdu_num, sne->defrag.npdu); + /* FIXME */ + } + + /* FIXME: check if seg_nr already exists */ + /* make sure to subtract length of SNDCP header from 'len' */ + rc = defrag_enqueue(sne, suh->seg_nr, data, len - (data - hdr)); + if (rc < 0) + return rc; + + if (!sch->more) { + /* this is suppsed to be the last segment of the N-PDU, but it + * might well be not the last to arrive */ + sne->defrag.no_more = 1; + } + + if (sne->defrag.no_more) { + /* we have already received the last segment before, let's check + * if all the previous segments exist */ + if (defrag_have_all_segments(sne)) + return defrag_segments(sne); + } + + return 0; +} + +static struct gprs_sndcp_entity *gprs_sndcp_entity_by_lle(const struct gprs_llc_lle *lle, + uint8_t nsapi) +{ + struct gprs_sndcp_entity *sne; + + llist_for_each_entry(sne, &gprs_sndcp_entities, list) { + if (sne->lle == lle && sne->nsapi == nsapi) + return sne; + } + return NULL; +} + +static struct gprs_sndcp_entity *gprs_sndcp_entity_alloc(struct gprs_llc_lle *lle, + uint8_t nsapi) +{ + struct gprs_sndcp_entity *sne; + + sne = talloc_zero(tall_sndcp_ctx, struct gprs_sndcp_entity); + if (!sne) + return NULL; + + sne->lle = lle; + sne->nsapi = nsapi; + sne->defrag.timer.data = sne; + //sne->fqueue.timer.cb = FIXME; + sne->rx_state = SNDCP_RX_S_FIRST; + INIT_LLIST_HEAD(&sne->defrag.frag_list); + + llist_add(&sne->list, &gprs_sndcp_entities); + + return sne; +} + +/* Entry point for the SNSM-ACTIVATE.indication */ +int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) +{ + LOGP(DSNDCP, LOGL_INFO, "SNSM-ACTIVATE.ind (lle=%p TLLI=%08x, " + "SAPI=%u, NSAPI=%u)\n", lle, lle->llme->tlli, lle->sapi, nsapi); + + if (gprs_sndcp_entity_by_lle(lle, nsapi)) { + LOGP(DSNDCP, LOGL_ERROR, "Trying to ACTIVATE " + "already-existing entity (TLLI=%08x, NSAPI=%u)\n", + lle->llme->tlli, nsapi); + return -EEXIST; + } + + if (!gprs_sndcp_entity_alloc(lle, nsapi)) { + LOGP(DSNDCP, LOGL_ERROR, "Out of memory during ACTIVATE\n"); + return -ENOMEM; + } + + return 0; +} + +/* Entry point for the SNSM-DEACTIVATE.indication */ +int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) +{ + struct gprs_sndcp_entity *sne; + + LOGP(DSNDCP, LOGL_INFO, "SNSM-DEACTIVATE.ind (lle=%p, TLLI=%08x, " + "SAPI=%u, NSAPI=%u)\n", lle, lle->llme->tlli, lle->sapi, nsapi); + + sne = gprs_sndcp_entity_by_lle(lle, nsapi); + if (!sne) { + LOGP(DSNDCP, LOGL_ERROR, "SNSM-DEACTIVATE.ind for non-" + "existing TLLI=%08x SAPI=%u NSAPI=%u\n", lle->llme->tlli, + lle->sapi, nsapi); + return -ENOENT; + } + llist_del(&sne->list); + /* frag queue entries are hierarchically allocated, so no need to + * free them explicitly here */ + talloc_free(sne); + + return 0; +} + +/* Fragmenter state */ +struct sndcp_frag_state { + uint8_t frag_nr; + struct msgb *msg; /* original message */ + uint8_t *next_byte; /* first byte of next fragment */ + + struct gprs_sndcp_entity *sne; + void *mmcontext; +}; + +/* returns '1' if there are more fragments to send, '0' if none */ +static int sndcp_send_ud_frag(struct sndcp_frag_state *fs) +{ + struct gprs_sndcp_entity *sne = fs->sne; + struct gprs_llc_lle *lle = sne->lle; + struct sndcp_common_hdr *sch; + struct sndcp_comp_hdr *scomph; + struct sndcp_udata_hdr *suh; + struct msgb *fmsg; + unsigned int max_payload_len; + unsigned int len; + uint8_t *data; + int rc, more; + + fmsg = msgb_alloc_headroom(fs->sne->lle->params.n201_u+256, 128, + "SNDCP Frag"); + if (!fmsg) + return -ENOMEM; + + /* make sure lower layers route the fragment like the original */ + msgb_tlli(fmsg) = msgb_tlli(fs->msg); + msgb_bvci(fmsg) = msgb_bvci(fs->msg); + msgb_nsei(fmsg) = msgb_nsei(fs->msg); + + /* prepend common SNDCP header */ + sch = (struct sndcp_common_hdr *) msgb_put(fmsg, sizeof(*sch)); + sch->nsapi = sne->nsapi; + /* Set FIRST bit if we are the first fragment in a series */ + if (fs->frag_nr == 0) + sch->first = 1; + sch->type = 1; + + /* append the compression header for first fragment */ + if (sch->first) { + scomph = (struct sndcp_comp_hdr *) + msgb_put(fmsg, sizeof(*scomph)); + scomph->pcomp = 0; + scomph->dcomp = 0; + } + + /* append the user-data header */ + suh = (struct sndcp_udata_hdr *) msgb_put(fmsg, sizeof(*suh)); + suh->npdu_low = sne->tx_npdu_nr & 0xff; + suh->npdu_high = (sne->tx_npdu_nr >> 8) & 0xf; + suh->seg_nr = fs->frag_nr % 0xf; + + /* calculate remaining length to be sent */ + len = (fs->msg->data + fs->msg->len) - fs->next_byte; + /* how much payload can we actually send via LLC? */ + max_payload_len = lle->params.n201_u - (sizeof(*sch) + sizeof(*suh)); + if (sch->first) + max_payload_len -= sizeof(*scomph); + /* check if we're exceeding the max */ + if (len > max_payload_len) + len = max_payload_len; + + /* copy the actual fragment data into our fmsg */ + data = msgb_put(fmsg, len); + memcpy(data, fs->next_byte, len); + + /* Increment fragment number and data pointer to next fragment */ + fs->frag_nr++; + fs->next_byte += len; + + /* determine if we have more fragemnts to send */ + if ((fs->msg->data + fs->msg->len) <= fs->next_byte) + more = 0; + else + more = 1; + + /* set the MORE bit of the SNDCP header accordingly */ + sch->more = more; + + rc = gprs_llc_tx_ui(fmsg, lle->sapi, 0, fs->mmcontext); + if (rc < 0) { + /* abort in case of error, do not advance frag_nr / next_byte */ + msgb_free(fmsg); + return rc; + } + + if (!more) { + /* we've sent all fragments */ + msgb_free(fs->msg); + memset(fs, 0, sizeof(*fs)); + /* increment NPDU number for next frame */ + sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff; + return 0; + } + + /* default: more fragments to send */ + return 1; +} + +/* Request transmission of a SN-PDU over specified LLC Entity + SAPI */ +int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi, + void *mmcontext) +{ + struct gprs_sndcp_entity *sne; + struct sndcp_common_hdr *sch; + struct sndcp_comp_hdr *scomph; + struct sndcp_udata_hdr *suh; + struct sndcp_frag_state fs; + + /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ + + sne = gprs_sndcp_entity_by_lle(lle, nsapi); + if (!sne) { + LOGP(DSNDCP, LOGL_ERROR, "Cannot find SNDCP Entity\n"); + return -EIO; + } + + /* Check if we need to fragment this N-PDU into multiple SN-PDUs */ + if (msg->len > lle->params.n201_u - + (sizeof(*sch) + sizeof(*suh) + sizeof(*scomph))) { + /* initialize the fragmenter state */ + fs.msg = msg; + fs.frag_nr = 0; + fs.next_byte = msg->data; + fs.sne = sne; + fs.mmcontext = mmcontext; + + /* call function to generate and send fragments until all + * of the N-PDU has been sent */ + while (1) { + int rc = sndcp_send_ud_frag(&fs); + if (rc == 0) + return 0; + if (rc < 0) + return rc; + } + /* not reached */ + return 0; + } + + /* this is the non-fragmenting case where we only build 1 SN-PDU */ + + /* prepend the user-data header */ + suh = (struct sndcp_udata_hdr *) msgb_push(msg, sizeof(*suh)); + suh->npdu_low = sne->tx_npdu_nr & 0xff; + suh->npdu_high = (sne->tx_npdu_nr >> 8) & 0xf; + suh->seg_nr = 0; + sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff; + + scomph = (struct sndcp_comp_hdr *) msgb_push(msg, sizeof(*scomph)); + scomph->pcomp = 0; + scomph->dcomp = 0; + + /* prepend common SNDCP header */ + sch = (struct sndcp_common_hdr *) msgb_push(msg, sizeof(*sch)); + sch->first = 1; + sch->type = 1; + sch->nsapi = nsapi; + + return gprs_llc_tx_ui(msg, lle->sapi, 0, mmcontext); +} + +/* Section 5.1.2.17 LL-UNITDATA.ind */ +int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, + uint8_t *hdr, uint16_t len) +{ + struct gprs_sndcp_entity *sne; + struct sndcp_common_hdr *sch = (struct sndcp_common_hdr *)hdr; + struct sndcp_comp_hdr *scomph = NULL; + struct sndcp_udata_hdr *suh; + uint8_t *npdu; + uint16_t npdu_num; + int npdu_len; + + sch = (struct sndcp_common_hdr *) hdr; + if (sch->first) { + scomph = (struct sndcp_comp_hdr *) (hdr + 1); + suh = (struct sndcp_udata_hdr *) (hdr + 1 + sizeof(struct sndcp_common_hdr)); + } else + suh = (struct sndcp_udata_hdr *) (hdr + sizeof(struct sndcp_common_hdr)); + + if (sch->type == 0) { + LOGP(DSNDCP, LOGL_ERROR, "SN-DATA PDU at unitdata_ind() function\n"); + return -EINVAL; + } + + if (len < sizeof(*sch) + sizeof(*suh)) { + LOGP(DSNDCP, LOGL_ERROR, "SN-UNITDATA PDU too short (%u)\n", len); + return -EIO; + } + + sne = gprs_sndcp_entity_by_lle(lle, sch->nsapi); + if (!sne) { + LOGP(DSNDCP, LOGL_ERROR, "Message for non-existing SNDCP Entity " + "(lle=%p, TLLI=%08x, SAPI=%u, NSAPI=%u)\n", lle, + lle->llme->tlli, lle->sapi, sch->nsapi); + return -EIO; + } + /* FIXME: move this RA_ID up to the LLME or even higher */ + bssgp_parse_cell_id(&sne->ra_id, msgb_bcid(msg)); + + /* any non-first segment is by definition something to defragment + * as is any segment that tells us there are more segments */ + if (!sch->first || sch->more) + return defrag_input(sne, msg, hdr, len); + + if (scomph && (scomph->pcomp || scomph->dcomp)) { + LOGP(DSNDCP, LOGL_ERROR, "We don't support compression yet\n"); + return -EIO; + } + + npdu_num = (suh->npdu_high << 8) | suh->npdu_low; + npdu = (uint8_t *)suh + sizeof(*suh); + npdu_len = (msg->data + msg->len) - npdu; + if (npdu_len <= 0) { + LOGP(DSNDCP, LOGL_ERROR, "Short SNDCP N-PDU: %d\n", npdu_len); + return -EIO; + } + /* actually send the N-PDU to the SGSN core code, which then + * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ + return sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli, sne->nsapi, msg, npdu_len, npdu); +} + +#if 0 +/* Section 5.1.2.1 LL-RESET.ind */ +static int sndcp_ll_reset_ind(struct gprs_sndcp_entity *se) +{ + /* treat all outstanding SNDCP-LLC request type primitives as not sent */ + /* reset all SNDCP XID parameters to default values */ + LOGP(DSNDCP, LOGL_NOTICE, "not implemented.\n"); + return 0; +} + +static int sndcp_ll_status_ind() +{ + /* inform the SM sub-layer by means of SNSM-STATUS.req */ + LOGP(DSNDCP, LOGL_NOTICE, "not implemented.\n"); + return 0; +} + +static struct sndcp_state_list {{ + uint32_t states; + unsigned int type; + int (*rout)(struct gprs_sndcp_entity *se, struct msgb *msg); +} sndcp_state_list[] = { + { ALL_STATES, + LL_RESET_IND, sndcp_ll_reset_ind }, + { ALL_STATES, + LL_ESTABLISH_IND, sndcp_ll_est_ind }, + { SBIT(SNDCP_S_EST_RQD), + LL_ESTABLISH_RESP, sndcp_ll_est_ind }, + { SBIT(SNDCP_S_EST_RQD), + LL_ESTABLISH_CONF, sndcp_ll_est_conf }, + { SBIT(SNDCP_S_ +}; + +static int sndcp_rx_llc_prim() +{ + case LL_ESTABLISH_REQ: + case LL_RELEASE_REQ: + case LL_XID_REQ: + case LL_DATA_REQ: + LL_UNITDATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ + + switch (prim) { + case LL_RESET_IND: + case LL_ESTABLISH_IND: + case LL_ESTABLISH_RESP: + case LL_ESTABLISH_CONF: + case LL_RELEASE_IND: + case LL_RELEASE_CONF: + case LL_XID_IND: + case LL_XID_RESP: + case LL_XID_CONF: + case LL_DATA_IND: + case LL_DATA_CONF: + case LL_UNITDATA_IND: + case LL_STATUS_IND: +} +#endif diff --git a/src/gprs/gprs_sndcp.h b/src/gprs/gprs_sndcp.h new file mode 100644 index 000000000..fef871aa1 --- /dev/null +++ b/src/gprs/gprs_sndcp.h @@ -0,0 +1,53 @@ +#ifndef _INT_SNDCP_H +#define _INT_SNDCP_H + +#include +#include + +/* A fragment queue header, maintaining list of fragments for one N-PDU */ +struct defrag_state { + /* PDU number for which the defragmentation state applies */ + uint16_t npdu; + /* highest segment number we have received so far */ + uint8_t highest_seg; + /* bitmask of the segments we already have */ + uint32_t seg_have; + /* do we still expect more segments? */ + unsigned int no_more; + /* total length of all segments together */ + unsigned int tot_len; + + /* linked list of defrag_queue_entry: one for each fragment */ + struct llist_head frag_list; + + struct osmo_timer_list timer; +}; + +/* See 6.7.1.2 Reassembly */ +enum sndcp_rx_state { + SNDCP_RX_S_FIRST, + SNDCP_RX_S_SUBSEQ, + SNDCP_RX_S_DISCARD, +}; + +struct gprs_sndcp_entity { + struct llist_head list; + + /* FIXME: move this RA_ID up to the LLME or even higher */ + struct gprs_ra_id ra_id; + /* reference to the LLC Entity below this SNDCP entity */ + struct gprs_llc_lle *lle; + /* The NSAPI we shall use on top of LLC */ + uint8_t nsapi; + + /* NPDU number for the GTP->SNDCP side */ + uint16_t tx_npdu_nr; + /* SNDCP eeceiver state */ + enum sndcp_rx_state rx_state; + /* The defragmentation queue */ + struct defrag_state defrag; +}; + +extern struct llist_head gprs_sndcp_entities; + +#endif /* INT_SNDCP_H */ diff --git a/src/gprs/gprs_sndcp_vty.c b/src/gprs/gprs_sndcp_vty.c new file mode 100644 index 000000000..deeef071a --- /dev/null +++ b/src/gprs/gprs_sndcp_vty.c @@ -0,0 +1,72 @@ +/* VTY interface for our GPRS SNDCP implementation */ + +/* (C) 2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gprs_sndcp.h" + +#include +#include + +static void vty_dump_sne(struct vty *vty, struct gprs_sndcp_entity *sne) +{ + vty_out(vty, " TLLI %08x SAPI=%u NSAPI=%u:%s", + sne->lle->llme->tlli, sne->lle->sapi, sne->nsapi, VTY_NEWLINE); + vty_out(vty, " Defrag: npdu=%u highest_seg=%u seg_have=0x%08x tot_len=%u%s", + sne->defrag.npdu, sne->defrag.highest_seg, sne->defrag.seg_have, + sne->defrag.tot_len, VTY_NEWLINE); +} + + +DEFUN(show_sndcp, show_sndcp_cmd, + "show sndcp", + SHOW_STR "Display information about the SNDCP protocol") +{ + struct gprs_sndcp_entity *sne; + + vty_out(vty, "State of SNDCP Entities%s", VTY_NEWLINE); + llist_for_each_entry(sne, &gprs_sndcp_entities, list) + vty_dump_sne(vty, sne); + + return CMD_SUCCESS; +} + +int gprs_sndcp_vty_init(void) +{ + install_element_ve(&show_sndcp_cmd); + + return 0; +} diff --git a/src/gprs/osmo_sgsn.cfg b/src/gprs/osmo_sgsn.cfg new file mode 100644 index 000000000..c4c9ec1cf --- /dev/null +++ b/src/gprs/osmo_sgsn.cfg @@ -0,0 +1,23 @@ +! +! Osmocom SGSN (0.9.0.474-0ede2) configuration saved from vty +!! +! +line vty + no login +! +sgsn + gtp local-ip 192.168.100.11 + ggsn 0 remote-ip 192.168.100.239 + ggsn 0 gtp-version 1 +ns + timer tns-block 3 + timer tns-block-retries 3 + timer tns-reset 3 + timer tns-reset-retries 3 + timer tns-test 30 + timer tns-alive 3 + timer tns-alive-retries 10 + encapsulation udp local-ip 192.168.100.11 + encapsulation udp local-port 23000 + encapsulation framerelay-gre enabled 0 +bssgp diff --git a/src/gprs/sgsn_libgtp.c b/src/gprs/sgsn_libgtp.c new file mode 100644 index 000000000..f2eb35d2c --- /dev/null +++ b/src/gprs/sgsn_libgtp.c @@ -0,0 +1,622 @@ +/* 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 + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a 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 + +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 sgsn_pdp_ctx *pctx; + struct pdp_t *pdp; + uint64_t imsi_ui64; + 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 not verified */ + pdp->selmode = 0x01; + + /* IMSI, TEID/TEIC, FLLU/FLLC, TID, NSAPI set in pdp_newpdp */ + + /* FIXME: MSISDN in BCD format from mmctx */ + //pdp->msisdn.l/.v + + /* 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 */ + pdp->qos_req.l = TLVP_LEN(tp, OSMO_IE_GSM_REQ_QOS); + if (pdp->qos_req.l > sizeof(pdp->qos_req.v)) + pdp->qos_req.l = sizeof(pdp->qos_req.v); + memcpy(pdp->qos_req.v, TLVP_VAL(tp, OSMO_IE_GSM_REQ_QOS), + pdp->qos_req.l); + + /* SGSN address for control plane */ + pdp->gsnlc.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr); + memcpy(pdp->gsnlc.v, &sgsn->cfg.gtp_listenaddr.sin_addr, + sizeof(sgsn->cfg.gtp_listenaddr.sin_addr)); + + /* SGSN address for user plane */ + pdp->gsnlu.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr); + memcpy(pdp->gsnlu.v, &sgsn->cfg.gtp_listenaddr.sin_addr, + sizeof(sgsn->cfg.gtp_listenaddr.sin_addr)); + + /* 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) +{ + LOGP(DGPRS, LOGL_ERROR, "Delete PDP Context\n"); + + /* FIXME: decide if we need teardown or not ! */ + return gtp_delete_context_req(pctx->ggsn->gsn, pctx->lib, pctx, 1); +} + +struct cause_map { + uint8_t cause_in; + uint8_t cause_out; +}; + +static uint8_t cause_map(const struct cause_map *map, uint8_t in, uint8_t deflt) +{ + const struct cause_map *m; + + for (m = map; m->cause_in && m->cause_out; m++) { + if (m->cause_in == in) + return m->cause_out; + } + return deflt; +} + +/* how do we map from gtp cause to SM cause */ +static const struct cause_map gtp2sm_cause_map[] = { + { GTPCAUSE_NO_RESOURCES, GSM_CAUSE_INSUFF_RSRC }, + { GTPCAUSE_NOT_SUPPORTED, GSM_CAUSE_SERV_OPT_NOTSUPP }, + { GTPCAUSE_MAN_IE_INCORRECT, GSM_CAUSE_INV_MAND_INFO }, + { GTPCAUSE_MAN_IE_MISSING, GSM_CAUSE_INV_MAND_INFO }, + { GTPCAUSE_OPT_IE_INCORRECT, GSM_CAUSE_PROTO_ERR_UNSPEC }, + { GTPCAUSE_SYS_FAIL, GSM_CAUSE_NET_FAIL }, + { GTPCAUSE_ROAMING_REST, GSM_CAUSE_REQ_SERV_OPT_NOTSUB }, + { GTPCAUSE_PTIMSI_MISMATCH, GSM_CAUSE_PROTO_ERR_UNSPEC }, + { GTPCAUSE_CONN_SUSP, GSM_CAUSE_PROTO_ERR_UNSPEC }, + { GTPCAUSE_AUTH_FAIL, GSM_CAUSE_AUTH_FAILED }, + { GTPCAUSE_USER_AUTH_FAIL, GSM_CAUSE_ACT_REJ_GGSN }, + { GTPCAUSE_CONTEXT_NOT_FOUND, GSM_CAUSE_PROTO_ERR_UNSPEC }, + { GTPCAUSE_ADDR_OCCUPIED, GSM_CAUSE_INSUFF_RSRC }, + { GTPCAUSE_NO_MEMORY, GSM_CAUSE_INSUFF_RSRC }, + { GTPCAUSE_RELOC_FAIL, GSM_CAUSE_PROTO_ERR_UNSPEC }, + { GTPCAUSE_UNKNOWN_MAN_EXTHEADER, GSM_CAUSE_PROTO_ERR_UNSPEC }, + { GTPCAUSE_MISSING_APN, GSM_CAUSE_MISSING_APN }, + { GTPCAUSE_UNKNOWN_PDP, GSM_CAUSE_UNKNOWN_PDP }, + { 0, 0 } +}; + +/* The GGSN has confirmed the creation of a PDP Context */ +static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) +{ + struct sgsn_pdp_ctx *pctx = cbp; + uint8_t reject_cause; + int rc; + + DEBUGP(DGPRS, "Received CREATE PDP CTX CONF, cause=%d(%s)\n", + cause, get_value_string(gtp_cause_strs, cause)); + + /* Check for cause value if it was really successful */ + if (cause < 0) { + LOGP(DGPRS, LOGL_NOTICE, "Create PDP ctx req timed out\n"); + if (pdp && pdp->version == 1) { + pdp->version = 0; + gtp_create_context_req(sgsn->gsn, pdp, cbp); + return 0; + } else { + reject_cause = GSM_CAUSE_NET_FAIL; + goto reject; + } + } + + /* Check for cause value if it was really successful */ + if (cause != GTPCAUSE_ACC_REQ) { + reject_cause = cause_map(gtp2sm_cause_map, cause, + GSM_CAUSE_ACT_REJ_GGSN); + goto reject; + } + + /* Activate the SNDCP layer */ + sndcp_sm_activate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi); + + /* Send PDP CTX ACT to MS */ + return gsm48_tx_gsm_act_pdp_acc(pctx); + +reject: + pctx->state = PDP_STATE_NONE; + if (pdp) + pdp_freepdp(pdp); + /* Send PDP CTX ACT REJ to MS */ + rc = gsm48_tx_gsm_act_pdp_rej(pctx->mm, pctx->ti, reject_cause, + 0, NULL); + sgsn_pdp_ctx_free(pctx); + + return EOF; +} + +/* Confirmation of a PDP Context Delete */ +static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) +{ + struct sgsn_pdp_ctx *pctx = cbp; + int rc; + + DEBUGP(DGPRS, "Received DELETE PDP CTX CONF, cause=%d(%s)\n", + cause, get_value_string(gtp_cause_strs, cause)); + + /* Deactivate the SNDCP layer */ + sndcp_sm_deactivate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi); + + /* Confirm deactivation of PDP context to MS */ + rc = gsm48_tx_gsm_deact_pdp_acc(pctx); + + /* 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) { + DEBUGP(DGPRS, "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) { + DEBUGP(DGPRS, "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; + int rc; + + DEBUGP(DGPRS, "GTP DATA IND from GGSN, length=%u\n", len); + + pdp = lib->priv; + if (!pdp) { + LOGP(DGPRS, LOGL_NOTICE, + "GTP DATA IND from GGSN for unknown PDP\n"); + return -EIO; + } + mm = pdp->mm; + if (!mm) { + LOGP(DGPRS, LOGL_ERROR, + "PDP context (imsi=%s) without MM context!\n", mm->imsi); + return -EIO; + } + + msg = msgb_alloc_headroom(len+256, 128, "GTP->SNDCP"); + ud = msgb_put(msg, len); + memcpy(ud, packet, len); + + msgb_tlli(msg) = mm->tlli; + msgb_bvci(msg) = mm->bvci; + msgb_nsei(msg) = mm->nsei; + + switch (mm->mm_state) { + case GMM_REGISTERED_SUSPENDED: + /* initiate PS PAGING procedure */ + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.mode = BSSGP_PAGING_PS; + pinfo.scope = BSSGP_PAGING_BVCI; + pinfo.bvci = mm->bvci; + pinfo.imsi = mm->imsi; + pinfo.ptmsi = &mm->p_tmsi; + pinfo.drx_params = mm->drx_parms; + pinfo.qos[0] = 0; // FIXME + rc = bssgp_tx_paging(mm->nsei, 0, &pinfo); + rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PAGING_PS]); + /* FIXME: queue the packet we received from GTP */ + break; + case GMM_REGISTERED_NORMAL: + break; + default: + LOGP(DGPRS, LOGL_ERROR, "GTP DATA IND for TLLI %08X in state " + "%u\n", mm->tlli, mm->mm_state); + msgb_free(msg); + return -1; + } + + rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_OUT]); + rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_OUT], len); + rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_UDATA_OUT]); + rate_ctr_add(&mm->ctrg->ctr[GMM_CTR_BYTES_UDATA_OUT], len); + + return sndcp_unitdata_req(msg, &mm->llme->lle[pdp->sapi], + pdp->nsapi, mm); +} + +/* Called by SNDCP when it has received/re-assembled a N-PDU */ +int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi, + struct msgb *msg, uint32_t npdu_len, uint8_t *npdu) +{ + struct sgsn_mm_ctx *mmctx; + struct sgsn_pdp_ctx *pdp; + + /* look-up the MM context for this message */ + mmctx = sgsn_mm_ctx_by_tlli(tlli, ra_id); + if (!mmctx) { + LOGP(DGPRS, LOGL_ERROR, + "Cannot find MM CTX for TLLI %08x\n", tlli); + return -EIO; + } + /* look-up the PDP context for this message */ + pdp = sgsn_pdp_ctx_by_nsapi(mmctx, nsapi); + if (!pdp) { + LOGP(DGPRS, LOGL_ERROR, "Cannot find PDP CTX for " + "TLLI=%08x, NSAPI=%u\n", tlli, nsapi); + return -EIO; + } + if (!pdp->lib) { + LOGP(DGPRS, LOGL_ERROR, "PDP CTX without libgtp\n"); + return -EIO; + } + + rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_IN]); + rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_IN], npdu_len); + rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_UDATA_IN]); + rate_ctr_add(&mmctx->ctrg->ctr[GMM_CTR_BYTES_UDATA_IN], npdu_len); + + return gtp_data_req(pdp->ggsn->gsn, pdp->lib, npdu, npdu_len); +} + +/* libgtp select loop integration */ +static int sgsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what) +{ + struct sgsn_instance *sgi = fd->data; + int rc; + + if (!(what & BSC_FD_READ)) + return 0; + + switch (fd->priv_nr) { + case 0: + rc = gtp_decaps0(sgi->gsn); + break; + case 1: + rc = gtp_decaps1c(sgi->gsn); + break; + case 2: + rc = gtp_decaps1u(sgi->gsn); + break; + default: + rc = -EINVAL; + break; + } + return rc; +} + +static void sgsn_gtp_tmr_start(struct sgsn_instance *sgi) +{ + struct timeval next; + + /* Retrieve next retransmission as struct timeval */ + gtp_retranstimeout(sgi->gsn, &next); + + /* re-schedule the timer */ + osmo_timer_schedule(&sgi->gtp_timer, next.tv_sec, next.tv_usec/1000); +} + +/* timer callback for libgtp retransmissions and ping */ +static void sgsn_gtp_tmr_cb(void *data) +{ + struct sgsn_instance *sgi = data; + + /* Do all the retransmissions as needed */ + gtp_retrans(sgi->gsn); + + sgsn_gtp_tmr_start(sgi); +} + +int sgsn_gtp_init(struct sgsn_instance *sgi) +{ + int rc; + struct gsn_t *gsn; + + rc = gtp_new(&sgi->gsn, sgi->cfg.gtp_statedir, + &sgi->cfg.gtp_listenaddr.sin_addr, GTP_MODE_SGSN); + if (rc) { + LOGP(DGPRS, LOGL_ERROR, "Failed to create GTP: %d\n", rc); + return rc; + } + gsn = sgi->gsn; + + sgi->gtp_fd0.fd = gsn->fd0; + sgi->gtp_fd0.priv_nr = 0; + sgi->gtp_fd0.data = sgi; + sgi->gtp_fd0.when = BSC_FD_READ; + sgi->gtp_fd0.cb = sgsn_gtp_fd_cb; + rc = osmo_fd_register(&sgi->gtp_fd0); + if (rc < 0) + return rc; + + sgi->gtp_fd1c.fd = gsn->fd1c; + sgi->gtp_fd1c.priv_nr = 1; + sgi->gtp_fd1c.data = sgi; + sgi->gtp_fd1c.when = BSC_FD_READ; + sgi->gtp_fd1c.cb = sgsn_gtp_fd_cb; + rc = osmo_fd_register(&sgi->gtp_fd1c); + if (rc < 0) { + osmo_fd_unregister(&sgi->gtp_fd0); + return rc; + } + + sgi->gtp_fd1u.fd = gsn->fd1u; + sgi->gtp_fd1u.priv_nr = 2; + sgi->gtp_fd1u.data = sgi; + sgi->gtp_fd1u.when = BSC_FD_READ; + sgi->gtp_fd1u.cb = sgsn_gtp_fd_cb; + rc = osmo_fd_register(&sgi->gtp_fd1u); + if (rc < 0) { + osmo_fd_unregister(&sgi->gtp_fd0); + osmo_fd_unregister(&sgi->gtp_fd1c); + return rc; + } + + /* Start GTP re-transmission timer */ + sgi->gtp_timer.cb = sgsn_gtp_tmr_cb; + sgi->gtp_timer.data = sgi; + sgsn_gtp_tmr_start(sgi); + + /* Register callbackcs with libgtp */ + gtp_set_cb_delete_context(gsn, cb_delete_context); + gtp_set_cb_conf(gsn, cb_conf); + gtp_set_cb_recovery(gsn, cb_recovery); + gtp_set_cb_data_ind(gsn, cb_data_ind); + gtp_set_cb_unsup_ind(gsn, cb_unsup_ind); + gtp_set_cb_extheader_ind(gsn, cb_extheader_ind); + + return 0; +} diff --git a/src/gprs/sgsn_main.c b/src/gprs/sgsn_main.c new file mode 100644 index 000000000..040e1d713 --- /dev/null +++ b/src/gprs/sgsn_main.c @@ -0,0 +1,380 @@ +/* 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 "../../bscconfig.h" + +/* FIXME: put into seperate header file */ +int gprs_sndcp_vty_init(void); +int sgsn_gtp_init(struct sgsn_instance *sgi); + +/* this is here for the vty... it will never be called */ +void subscr_put() { abort(); } + +#define _GNU_SOURCE +#include + +void *tall_bsc_ctx; + +struct gprs_ns_inst *sgsn_nsi; +static int daemonize = 0; +const char *openbsc_copyright = + "Copyright (C) 2010 Harald Welte and On-Waves\r\n" + "License AGPLv3+: GNU AGPL version 2 or later \r\n" + "This is free software: you are free to change and redistribute it.\r\n" + "There is NO WARRANTY, to the extent permitted by law.\r\n"; + +static struct sgsn_instance sgsn_inst = { + .config_file = "osmo_sgsn.cfg", + .cfg = { + .gtp_statedir = "./", + .acl_enabled = 1, + }, +}; +struct sgsn_instance *sgsn = &sgsn_inst; + +/* call-back function for the NS protocol */ +static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, + struct msgb *msg, uint16_t bvci) +{ + int rc = 0; + + switch (event) { + case GPRS_NS_EVT_UNIT_DATA: + /* hand the message into the BSSGP implementation */ + rc = bssgp_rcvmsg(msg); + break; + default: + LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event); + if (msg) + msgb_free(msg); + rc = -EIO; + break; + } + return rc; +} + +/* call-back function for the BSSGP protocol */ +int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) +{ + struct osmo_bssgp_prim *bp; + bp = container_of(oph, struct osmo_bssgp_prim, oph); + + switch (oph->sap) { + case SAP_BSSGP_LL: + switch (oph->primitive) { + case PRIM_BSSGP_UL_UD: + return gprs_llc_rcvmsg(oph->msg, bp->tp); + } + break; + case SAP_BSSGP_GMM: + switch (oph->primitive) { + case PRIM_BSSGP_GMM_SUSPEND: + return gprs_gmm_rx_suspend(bp->ra_id, bp->tlli); + case PRIM_BSSGP_GMM_RESUME: + return gprs_gmm_rx_resume(bp->ra_id, bp->tlli, + bp->u.resume.suspend_ref); + } + break; + case SAP_BSSGP_NM: + break; + } + return 0; +} + +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); + sleep(1); + exit(0); + break; + case SIGABRT: + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + talloc_report(tall_vty_ctx, stderr); + talloc_report_full(tall_bsc_ctx, stderr); + break; + case SIGUSR2: + talloc_report_full(tall_vty_ctx, stderr); + break; + default: + break; + } +} + +/* NSI that BSSGP uses when transmitting on NS */ +extern struct gprs_ns_inst *bssgp_nsi; +extern void *tall_msgb_ctx; + +extern enum node_type 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\n"); + printf(" -e --log-level number\tSet a global log level\n"); +} + +static void handle_options(int argc, char **argv) +{ + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"debug", 1, 0, 'd'}, + {"daemonize", 0, 0, 'D'}, + {"config-file", 1, 0, 'c'}, + {"disable-color", 0, 0, 's'}, + {"timestamp", 0, 0, 'T'}, + {"log-level", 1, 0, 'e'}, + {NULL, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "hd:Dc:sTe:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + //print_usage(); + print_help(); + exit(0); + case 's': + log_set_use_color(osmo_stderr_target, 0); + break; + case 'd': + log_parse_category_mask(osmo_stderr_target, optarg); + break; + case 'D': + daemonize = 1; + break; + case 'c': + sgsn_inst.config_file = strdup(optarg); + break; + case 'T': + log_set_print_timestamp(osmo_stderr_target, 1); + break; + case 'e': + log_set_log_level(osmo_stderr_target, atoi(optarg)); + break; + default: + /* ignore */ + break; + } + } +} + +/* default categories */ +static struct log_info_cat gprs_categories[] = { + [DMM] = { + .name = "DMM", + .description = "Layer3 Mobility Management (MM)", + .color = "\033[1;33m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DPAG] = { + .name = "DPAG", + .description = "Paging Subsystem", + .color = "\033[1;38m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DMEAS] = { + .name = "DMEAS", + .description = "Radio Measurement Processing", + .enabled = 0, .loglevel = LOGL_NOTICE, + }, + [DREF] = { + .name = "DREF", + .description = "Reference Counting", + .enabled = 0, .loglevel = LOGL_NOTICE, + }, + [DGPRS] = { + .name = "DGPRS", + .description = "GPRS Packet Service", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DNS] = { + .name = "DNS", + .description = "GPRS Network Service (NS)", + .enabled = 1, .loglevel = LOGL_INFO, + }, + [DBSSGP] = { + .name = "DBSSGP", + .description = "GPRS BSS Gateway Protocol (BSSGP)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DLLC] = { + .name = "DLLC", + .description = "GPRS Logical Link Control Protocol (LLC)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DSNDCP] = { + .name = "DSNDCP", + .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, +}; + +static const struct log_info gprs_log_info = { + .filter_fn = gprs_log_filter_fn, + .cat = gprs_categories, + .num_cat = ARRAY_SIZE(gprs_categories), +}; + + +int main(int argc, char **argv) +{ + struct gsm_network dummy_network; + int rc; + + tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn"); + tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); + + signal(SIGINT, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); + + osmo_init_ignore_signals(); + osmo_init_logging(&gprs_log_info); + + vty_info.copyright = openbsc_copyright; + vty_init(&vty_info); + logging_vty_add_cmds(&gprs_log_info); + sgsn_vty_init(); + + handle_options(argc, argv); + + rate_ctr_init(tall_bsc_ctx); + rc = telnet_init(tall_bsc_ctx, &dummy_network, 4245); + if (rc < 0) + exit(1); + + gprs_ns_set_log_ss(DNS); + bssgp_set_log_ss(DBSSGP); + + sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_bsc_ctx); + if (!sgsn_nsi) { + LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); + exit(1); + } + bssgp_nsi = sgsn_inst.cfg.nsi = sgsn_nsi; + + gprs_llc_init("/usr/local/lib/osmocom/crypt/"); + + gprs_ns_vty_init(bssgp_nsi); + bssgp_vty_init(); + gprs_llc_vty_init(); + gprs_sndcp_vty_init(); + /* FIXME: register signal handler for SS_L_NS */ + + rc = sgsn_parse_config(sgsn_inst.config_file, &sgsn_inst.cfg); + if (rc < 0) { + LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n"); + exit(2); + } + + rc = sgsn_gtp_init(&sgsn_inst); + if (rc) { + LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on GTP socket\n"); + exit(2); + } + + rc = gprs_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 (daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + perror("Error during daemonize"); + exit(1); + } + } + + while (1) { + rc = osmo_select_main(0); + if (rc < 0) + exit(3); + } + + /* not reached */ + exit(0); +} diff --git a/src/gprs/sgsn_vty.c b/src/gprs/sgsn_vty.c new file mode 100644 index 000000000..a4ba2802e --- /dev/null +++ b/src/gprs/sgsn_vty.c @@ -0,0 +1,448 @@ +/* + * (C) 2010-2013 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 sgsn_config *g_cfg = NULL; + +struct imsi_acl_entry { + struct llist_head list; + char imsi[16+1]; +}; + +#define GSM48_MAX_APN_LEN 102 /* 10.5.6.1 */ +static char *gprs_apn2str(uint8_t *apn, unsigned int len) +{ + static char apnbuf[GSM48_MAX_APN_LEN+1]; + unsigned int i = 0; + + if (!apn) + return ""; + + if (len > sizeof(apnbuf)-1) + len = sizeof(apnbuf)-1; + + memcpy(apnbuf, apn, len); + apnbuf[len] = '\0'; + + /* replace the domain name step sizes with dots */ + while (i < len) { + unsigned int step = apnbuf[i]; + apnbuf[i] = '.'; + i += step+1; + } + + return apnbuf+1; +} + +static 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; + + 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) { + 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); + } + + vty_out(vty, " auth-policy %s%s", + g_cfg->acl_enabled ? "closed" : "accept-all", VTY_NEWLINE); + llist_for_each_entry(acl, &g_cfg->imsi_acl, list) + vty_out(vty, " imsi-acl add %s%s", acl->imsi, 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; +} + +#if 0 +DEFUN(cfg_apn_ggsn, cfg_apn_ggsn_cmd, + "apn APNAME ggsn <0-255>", + "") +{ + struct apn_ctx ** +} +#endif + +const struct value_string gprs_mm_st_strs[] = { + { GMM_DEREGISTERED, "DEREGISTERED" }, + { GMM_COMMON_PROC_INIT, "COMMON PROCEDURE (INIT)" }, + { GMM_REGISTERED_NORMAL, "REGISTERED (NORMAL)" }, + { GMM_REGISTERED_SUSPENDED, "REGISTERED (SUSPENDED)" }, + { GMM_DEREGISTERED_INIT, "DEREGISTERED (INIT)" }, + { 0, NULL } +}; + +static void vty_dump_pdp(struct vty *vty, const char *pfx, + struct sgsn_pdp_ctx *pdp) +{ + vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u%s", + pfx, pdp->mm->imsi, pdp->sapi, pdp->nsapi, VTY_NEWLINE); + vty_out(vty, "%s APN: %s%s", pfx, + gprs_apn2str(pdp->lib->apn_use.v, pdp->lib->apn_use.l), + VTY_NEWLINE); + vty_out(vty, "%s PDP Address: %s%s", pfx, + gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l), + VTY_NEWLINE); + vty_out_rate_ctr_group(vty, " ", pdp->ctrg); +} + +static void vty_dump_mmctx(struct vty *vty, const char *pfx, + struct sgsn_mm_ctx *mm, int pdp) +{ + vty_out(vty, "%sMM Context for IMSI %s, IMEI %s, P-TMSI %08x%s", + pfx, mm->imsi, mm->imei, mm->p_tmsi, VTY_NEWLINE); + vty_out(vty, "%s MSISDN: %s, TLLI: %08x%s", pfx, mm->msisdn, + mm->tlli, VTY_NEWLINE); + vty_out(vty, "%s MM State: %s, Routeing Area: %u-%u-%u-%u, " + "Cell ID: %u%s", pfx, + get_value_string(gprs_mm_st_strs, mm->mm_state), + mm->ra.mcc, mm->ra.mnc, mm->ra.lac, mm->ra.rac, + mm->cell_id, VTY_NEWLINE); + + vty_out_rate_ctr_group(vty, " ", mm->ctrg); + + if (pdp) { + struct sgsn_pdp_ctx *pdp; + + llist_for_each_entry(pdp, &mm->pdp_list, list) + vty_dump_pdp(vty, " ", pdp); + } +} + +DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn", + SHOW_STR "Display information about the SGSN") +{ + /* 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; +} + +/* temporary IMSI ACL hack */ +struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi) +{ + struct imsi_acl_entry *acl; + llist_for_each_entry(acl, &g_cfg->imsi_acl, list) { + if (!strcmp(imsi, acl->imsi)) + return acl; + } + return NULL; +} + +int sgsn_acl_add(const char *imsi) +{ + struct imsi_acl_entry *acl; + + if (sgsn_acl_lookup(imsi)) + return -EEXIST; + + acl = talloc_zero(NULL, struct imsi_acl_entry); + if (!acl) + return -ENOMEM; + strncpy(acl->imsi, imsi, sizeof(acl->imsi)); + + llist_add(&acl->list, &g_cfg->imsi_acl); + + return 0; +} + +int sgsn_acl_del(const char *imsi) +{ + struct imsi_acl_entry *acl; + + acl = sgsn_acl_lookup(imsi); + if (!acl) + return -ENODEV; + + llist_del(&acl->list); + talloc_free(acl); + + return 0; +} + + +DEFUN(imsi_acl, cfg_imsi_acl_cmd, + "imsi-acl (add|del) IMSI", + "Access Control List of foreign IMSIs\n" + "Add IMSI to ACL\n" + "Remove IMSI from ACL\n" + "IMSI of subscriber\n") +{ + const char *op = argv[0]; + const char *imsi = argv[1]; + int rc; + + if (!strcmp(op, "add")) + rc = sgsn_acl_add(imsi); + else + rc = sgsn_acl_del(imsi); + + if (rc < 0) { + vty_out(vty, "%% unable to %s ACL\n", op); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_auth_policy, cfg_auth_policy_cmd, + "auth-policy (accept-all|closed)", + "Autorization Policy of SGSN\n" + "Accept all IMSIs (DANGEROUS\n" + "Accept only home network subscribers or those in ACL\n") +{ + if (!strcmp(argv[0], "accept-all")) + g_cfg->acl_enabled = 0; + else + g_cfg->acl_enabled = 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(CONFIG_NODE, &cfg_sgsn_cmd); + install_node(&sgsn_node, config_write_sgsn); + install_default(SGSN_NODE); + install_element(SGSN_NODE, &ournode_exit_cmd); + install_element(SGSN_NODE, &ournode_end_cmd); + 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); + + return 0; +} + +int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg) +{ + int rc; + + g_cfg = cfg; + INIT_LLIST_HEAD(&g_cfg->imsi_acl); + + rc = vty_read_config_file(config_file, NULL); + if (rc < 0) { + fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); + return rc; + } + + return 0; +} diff --git a/src/ipaccess/Makefile.am b/src/ipaccess/Makefile.am new file mode 100644 index 000000000..308215b5d --- /dev/null +++ b/src/ipaccess/Makefile.am @@ -0,0 +1,32 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) +OSMO_LIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOABIS_LIBS) + +bin_PROGRAMS = ipaccess-find ipaccess-config ipaccess-proxy + +ipaccess_find_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(OSMO_LIBS) +ipaccess_find_SOURCES = ipaccess-find.c + +ipaccess_config_SOURCES = ipaccess-config.c ipaccess-firmware.c network_listen.c + +# FIXME: resolve the bogus dependencies patched around here: +ipaccess_config_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + -ldl -ldbi $(LIBCRYPT) $(OSMO_LIBS) + +ipaccess_proxy_SOURCES = ipaccess-proxy.c +ipaccess_proxy_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(OSMO_LIBS) diff --git a/src/ipaccess/ipaccess-config.c b/src/ipaccess/ipaccess-config.c new file mode 100644 index 000000000..e66771a1a --- /dev/null +++ b/src/ipaccess/ipaccess-config.c @@ -0,0 +1,1024 @@ +/* ip.access nanoBTS configuration tool */ + +/* (C) 2009-2010 by Harald Welte + * (C) 2009-2011 by Holger Hans Peter Freyther + * (C) 2009-2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gsm_network *bsc_gsmnet; + +static int net_listen_testnr; +static int restart; +static char *prim_oml_ip; +static char *bts_ip_addr, *bts_ip_mask, *bts_ip_gw; +static char *unit_id; +static uint16_t nv_flags; +static uint16_t nv_mask; +static char *software = NULL; +static int sw_load_state = 0; +static int oml_state = 0; +static int dump_files = 0; +static char *firmware_analysis = NULL; +static int found_trx = 0; +static int loop_tests = 0; + +struct sw_load { + uint8_t file_id[255]; + uint8_t file_id_len; + + uint8_t file_version[255]; + uint8_t file_version_len; +}; + +static void *tall_ctx_config = NULL; +static struct sw_load *sw_load1 = NULL; +static struct sw_load *sw_load2 = NULL; + +/* +static uint8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 }; +static uint8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 }; +*/ + +extern int ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what); +extern struct e1inp_line_ops ipaccess_e1inp_line_ops; + +/* Actively connect to a BTS. Currently used by ipaccess-config.c */ +static int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa) +{ + struct e1inp_ts *e1i_ts = &line->ts[0]; + struct osmo_fd *bfd = &e1i_ts->driver.ipaccess.fd; + int ret, on = 1; + + bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + bfd->cb = ipaccess_fd_cb; + bfd->when = BSC_FD_READ | BSC_FD_WRITE; + bfd->data = line; + bfd->priv_nr = E1INP_SIGN_OML; + + if (bfd->fd < 0) { + LOGP(DLINP, LOGL_ERROR, "could not create TCP socket.\n"); + return -EIO; + } + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); + if (ret < 0) { + LOGP(DLINP, LOGL_ERROR, "could not connect socket\n"); + close(bfd->fd); + return ret; + } + + ret = osmo_fd_register(bfd); + if (ret < 0) { + close(bfd->fd); + return ret; + } + return ret; + //return e1inp_line_register(line); +} + +/* configure pseudo E1 line in ip.access style and connect to BTS */ +static int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin) +{ + struct e1inp_line *line; + struct e1inp_ts *sign_ts, *rsl_ts; + struct e1inp_sign_link *oml_link, *rsl_link; + + line = talloc_zero(tall_bsc_ctx, struct e1inp_line); + if (!line) + return -ENOMEM; + + line->driver = e1inp_driver_find("ipa"); + if (!line->driver) { + fprintf(stderr, "cannot `ipa' driver, giving up.\n"); + return -EINVAL; + } + line->ops = &ipaccess_e1inp_line_ops; + + /* create E1 timeslots for signalling and TRAU frames */ + e1inp_ts_config_sign(&line->ts[1-1], line); + e1inp_ts_config_sign(&line->ts[2-1], line); + + /* create signalling links for TS1 */ + sign_ts = &line->ts[1-1]; + rsl_ts = &line->ts[2-1]; + oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, + bts->c0, 0xff, 0); + rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL, + bts->c0, 0, 0); + + /* create back-links from bts/trx */ + bts->oml_link = oml_link; + bts->c0->rsl_link = rsl_link; + + /* default port at BTS for incoming connections is 3006 */ + if (sin->sin_port == 0) + sin->sin_port = htons(3006); + + return ipaccess_connect(line, sin); +} + +/* + * Callback function for NACK on the OML NM + * + * Currently we send the config requests but don't check the + * result. The nanoBTS will send us a NACK when we did something the + * BTS didn't like. + */ +static int ipacc_msg_nack(uint8_t mt) +{ + fprintf(stderr, "Failure to set attribute. This seems fatal\n"); + exit(-1); + return 0; +} + +static void check_restart_or_exit(struct gsm_bts_trx *trx) +{ + if (restart) { + abis_nm_ipaccess_restart(trx); + } else { + exit(0); + } +} + +static int ipacc_msg_ack(uint8_t mt, struct gsm_bts_trx *trx) +{ + if (sw_load_state == 1) { + fprintf(stderr, "The new software is activaed.\n"); + check_restart_or_exit(trx); + } else if (oml_state == 1) { + fprintf(stderr, "Set the NV Attributes.\n"); + check_restart_or_exit(trx); + } + + return 0; +} + +static const uint8_t phys_conf_min[] = { 0x02 }; + +static uint16_t build_physconf(uint8_t *physconf_buf, const struct rxlev_stats *st) +{ + uint16_t *whitelist = (uint16_t *) (physconf_buf + 4); + int num_arfcn; + unsigned int arfcnlist_size; + + /* Create whitelist from rxlevels */ + physconf_buf[0] = phys_conf_min[0]; + physconf_buf[1] = NM_IPAC_EIE_ARFCN_WHITE; + num_arfcn = ipac_rxlevstat2whitelist(whitelist, st, 0, 100); + arfcnlist_size = num_arfcn * 2; + *((uint16_t *) (physconf_buf+2)) = htons(arfcnlist_size); + DEBUGP(DNM, "physconf_buf (%s)\n", osmo_hexdump(physconf_buf, arfcnlist_size+4)); + return arfcnlist_size+4; +} + +static int nwl_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_bts_trx *trx; + uint8_t physconf_buf[2*NUM_ARFCNS+16]; + uint16_t physconf_len; + + switch (signal) { + case S_IPAC_NWL_COMPLETE: + trx = signal_data; + DEBUGP(DNM, "received S_IPAC_NWL_COMPLETE signal\n"); + switch (trx->ipaccess.test_nr) { + case NM_IPACC_TESTNO_CHAN_USAGE: + /* Dump RxLev results */ + //rxlev_stat_dump(&trx->ipaccess.rxlev_stat); + /* Create whitelist from results */ + physconf_len = build_physconf(physconf_buf, + &trx->ipaccess.rxlev_stat); + /* Start next test abbout BCCH channel usage */ + ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_CHAN_USAGE, + physconf_buf, physconf_len); + break; + case NM_IPACC_TESTNO_BCCH_CHAN_USAGE: + /* Dump BCCH RxLev results */ + //rxlev_stat_dump(&trx->ipaccess.rxlev_stat); + /* Create whitelist from results */ + physconf_len = build_physconf(physconf_buf, + &trx->ipaccess.rxlev_stat); + /* Start next test about BCCH info */ + ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_INFO, + physconf_buf, physconf_len); + break; + case NM_IPACC_TESTNO_BCCH_INFO: + /* re-start full process with CHAN_USAGE */ + if (loop_tests) { + DEBUGP(DNM, "starting next test cycle\n"); + ipac_nwl_test_start(trx, net_listen_testnr, phys_conf_min, + sizeof(phys_conf_min)); + } else { + exit(0); + } + break; + } + break; + } + return 0; +} + +static int nm_state_event(int evt, uint8_t obj_class, void *obj, + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, + struct abis_om_obj_inst *obj_inst); + +static int nm_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct ipacc_ack_signal_data *ipacc_data; + struct nm_statechg_signal_data *nsd; + + switch (signal) { + case S_NM_IPACC_NACK: + ipacc_data = signal_data; + return ipacc_msg_nack(ipacc_data->msg_type); + case S_NM_IPACC_ACK: + ipacc_data = signal_data; + return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->trx); + case S_NM_IPACC_RESTART_ACK: + printf("The BTS has acked the restart. Exiting.\n"); + exit(0); + break; + case S_NM_IPACC_RESTART_NACK: + printf("The BTS has nacked the restart. Exiting.\n"); + exit(0); + break; + case S_NM_STATECHG_OPER: + case S_NM_STATECHG_ADM: + nsd = signal_data; + nm_state_event(signal, nsd->obj_class, nsd->obj, nsd->old_state, + nsd->new_state, nsd->obj_inst); + break; + default: + break; + } + + return 0; +} + +/* callback function passed to the ABIS OML code */ +static int percent; +static int percent_old; +static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg, + void *data, void *param) +{ + struct msgb *msg; + struct gsm_bts_trx *trx; + + if (hook != GSM_HOOK_NM_SWLOAD) + return 0; + + trx = (struct gsm_bts_trx *) data; + + switch (event) { + case NM_MT_LOAD_INIT_ACK: + fprintf(stdout, "Software Load Initiate ACK\n"); + break; + case NM_MT_LOAD_INIT_NACK: + fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); + exit(5); + break; + case NM_MT_LOAD_END_ACK: + fprintf(stderr, "LOAD END ACK..."); + /* now make it the default */ + sw_load_state = 1; + + msg = msgb_alloc(1024, "sw: nvattr"); + msg->l2h = msgb_put(msg, 3); + msg->l3h = &msg->l2h[3]; + + /* activate software */ + if (sw_load1) { + msgb_v_put(msg, NM_ATT_SW_DESCR); + msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load1->file_id_len, sw_load1->file_id); + msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load1->file_version_len, + sw_load1->file_version); + } + + if (sw_load2) { + msgb_v_put(msg, NM_ATT_SW_DESCR); + msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load2->file_id_len, sw_load2->file_id); + msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load2->file_version_len, + sw_load2->file_version); + } + + /* fill in the data */ + msg->l2h[0] = NM_ATT_IPACC_CUR_SW_CFG; + msg->l2h[1] = msgb_l3len(msg) >> 8; + msg->l2h[2] = msgb_l3len(msg) & 0xff; + printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg)); + abis_nm_ipaccess_set_nvattr(trx, msg->l2h, msgb_l2len(msg)); + msgb_free(msg); + break; + case NM_MT_LOAD_END_NACK: + fprintf(stderr, "ERROR: Software Load End NACK\n"); + exit(3); + break; + case NM_MT_ACTIVATE_SW_NACK: + fprintf(stderr, "ERROR: Activate Software NACK\n"); + exit(4); + break; + case NM_MT_ACTIVATE_SW_ACK: + break; + case NM_MT_LOAD_SEG_ACK: + percent = abis_nm_software_load_status(trx->bts); + if (percent > percent_old) + printf("Software Download Progress: %d%%\n", percent); + percent_old = percent; + break; + case NM_MT_LOAD_ABORT: + fprintf(stderr, "ERROR: Load aborted by the BTS.\n"); + exit(6); + break; + } + return 0; +} + +static void nv_put_ip_if_cfg(struct msgb *nmsg, uint32_t ip, uint32_t mask) +{ + msgb_put_u8(nmsg, NM_ATT_IPACC_IP_IF_CFG); + + msgb_put_u32(nmsg, ip); + msgb_put_u32(nmsg, mask); +} + +static void nv_put_gw_cfg(struct msgb *nmsg, uint32_t addr, uint32_t mask, uint32_t gw) +{ + msgb_put_u8(nmsg, NM_ATT_IPACC_IP_GW_CFG); + msgb_put_u32(nmsg, addr); + msgb_put_u32(nmsg, mask); + msgb_put_u32(nmsg, gw); +} + +static void nv_put_unit_id(struct msgb *nmsg, const char *unit_id) +{ + msgb_tl16v_put(nmsg, NM_ATT_IPACC_UNIT_ID, strlen(unit_id)+1, + (const uint8_t *)unit_id); +} + +static void nv_put_prim_oml(struct msgb *nmsg, uint32_t ip, uint16_t port) +{ + int len; + + /* 0x88 + IP + port */ + len = 1 + sizeof(ip) + sizeof(port); + + msgb_put_u8(nmsg, NM_ATT_IPACC_PRIM_OML_CFG_LIST); + msgb_put_u16(nmsg, len); + + msgb_put_u8(nmsg, 0x88); + + /* IP address */ + msgb_put_u32(nmsg, ip); + + /* port number */ + msgb_put_u16(nmsg, port); +} + +static void nv_put_flags(struct msgb *nmsg, uint16_t nv_flags, uint16_t nv_mask) +{ + msgb_put_u8(nmsg, NM_ATT_IPACC_NV_FLAGS); + msgb_put_u16(nmsg, sizeof(nv_flags) + sizeof(nv_mask)); + msgb_put_u8(nmsg, nv_flags & 0xff); + msgb_put_u8(nmsg, nv_mask & 0xff); + msgb_put_u8(nmsg, nv_flags >> 8); + msgb_put_u8(nmsg, nv_mask >> 8); +} + +/* human-readable test names for the ip.access tests */ +static const struct value_string ipa_test_strs[] = { + { 64, "ccch-usage" }, + { 65, "bcch-usage" }, + { 66, "freq-sync" }, + { 67, "rtp-usage" }, + { 68, "rtp-perf" }, + { 69, "gprs-ccch" }, + { 70, "pccch-usage" }, + { 71, "gprs-usage" }, + { 72, "esta-mf" }, + { 73, "uplink-mf" }, + { 74, "dolink-mf" }, + { 75, "tbf-details" }, + { 76, "tbf-usage" }, + { 77, "llc-data" }, + { 78, "pdch-usage" }, + { 79, "power-control" }, + { 80, "link-adaption" }, + { 81, "tch-usage" }, + { 82, "amr-mf" }, + { 83, "rtp-multiplex-perf" }, + { 84, "rtp-multiplex-usage" }, + { 85, "srtp-multiplex-usage" }, + { 86, "abis-traffic" }, + { 89, "gprs-multiplex-perf" }, + { 90, "gprs-multiplex-usage" }, + { 0, NULL }, +}; + +/* human-readable names for the ip.access nanoBTS NVRAM Flags */ +static const struct value_string ipa_nvflag_strs[] = { + { 0x0001, "static-ip" }, + { 0x0002, "static-gw" }, + { 0x0004, "no-dhcp-vsi" }, + { 0x0008, "dhcp-enabled" }, + { 0x0040, "led-disabled" }, + { 0x0100, "secondary-oml-enabled" }, + { 0x0200, "diag-enabled" }, + { 0x0400, "cli-enabled" }, + { 0x0800, "http-enabled" }, + { 0x1000, "post-enabled" }, + { 0x2000, "snmp-enabled" }, + { 0, NULL } +}; + +/* set the flags in flags/mask according to a string-identified flag and 'enable' */ +static int ipa_nvflag_set(uint16_t *flags, uint16_t *mask, const char *name, int en) +{ + int rc; + rc = get_string_value(ipa_nvflag_strs, name); + if (rc < 0) + return rc; + + *mask |= rc; + if (en) + *flags |= rc; + else + *flags &= ~rc; + + return 0; +} + +static void bootstrap_om(struct gsm_bts_trx *trx) +{ + struct msgb *nmsg = msgb_alloc(1024, "nested msgb"); + int need_to_set_attr = 0; + int len; + + printf("OML link established using TRX %d\n", trx->nr); + + if (unit_id) { + len = strlen(unit_id); + if (len > nmsg->data_len-10) + goto out_err; + printf("setting Unit ID to '%s'\n", unit_id); + nv_put_unit_id(nmsg, unit_id); + need_to_set_attr = 1; + } + if (prim_oml_ip) { + struct in_addr ia; + + if (!inet_aton(prim_oml_ip, &ia)) { + fprintf(stderr, "invalid IP address: %s\n", + prim_oml_ip); + goto out_err; + } + + printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia)); + nv_put_prim_oml(nmsg, ntohl(ia.s_addr), 0); + need_to_set_attr = 1; + } + if (nv_mask) { + printf("setting NV Flags/Mask to 0x%04x/0x%04x\n", + nv_flags, nv_mask); + nv_put_flags(nmsg, nv_flags, nv_mask); + need_to_set_attr = 1; + } + if (bts_ip_addr && bts_ip_mask) { + struct in_addr ia_addr, ia_mask; + + if (!inet_aton(bts_ip_addr, &ia_addr)) { + fprintf(stderr, "invalid IP address: %s\n", + bts_ip_addr); + goto out_err; + } + + if (!inet_aton(bts_ip_mask, &ia_mask)) { + fprintf(stderr, "invalid IP address: %s\n", + bts_ip_mask); + goto out_err; + } + + printf("setting static IP Address/Mask\n"); + nv_put_ip_if_cfg(nmsg, ntohl(ia_addr.s_addr), ntohl(ia_mask.s_addr)); + need_to_set_attr = 1; + } + if (bts_ip_gw) { + struct in_addr ia_gw; + + if (!inet_aton(bts_ip_gw, &ia_gw)) { + fprintf(stderr, "invalid IP address: %s\n", + bts_ip_gw); + goto out_err; + } + + printf("setting static IP Gateway\n"); + /* we only set the default gateway with zero addr/mask */ + nv_put_gw_cfg(nmsg, 0, 0, ntohl(ia_gw.s_addr)); + need_to_set_attr = 1; + } + + if (need_to_set_attr) { + abis_nm_ipaccess_set_nvattr(trx, nmsg->head, nmsg->len); + oml_state = 1; + } + + if (restart && !prim_oml_ip && !software) { + printf("restarting BTS\n"); + abis_nm_ipaccess_restart(trx); + } + +out_err: + msgb_free(nmsg); +} + +static int nm_state_event(int evt, uint8_t obj_class, void *obj, + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, + struct abis_om_obj_inst *obj_inst) +{ + if (obj_class == NM_OC_BASEB_TRANSC) { + if (!found_trx && obj_inst->trx_nr != 0xff) { + struct gsm_bts_trx *trx = container_of(obj, struct gsm_bts_trx, bb_transc); + bootstrap_om(trx); + found_trx = 1; + } + } else if (evt == S_NM_STATECHG_OPER && + obj_class == NM_OC_RADIO_CARRIER && + new_state->availability == 3) { + struct gsm_bts_trx *trx = obj; + + if (net_listen_testnr) + ipac_nwl_test_start(trx, net_listen_testnr, + phys_conf_min, sizeof(phys_conf_min)); + else if (software) { + int rc; + printf("Attempting software upload with '%s'\n", software); + rc = abis_nm_software_load(trx->bts, trx->nr, software, 19, 0, swload_cbfn, trx); + if (rc < 0) { + fprintf(stderr, "Failed to start software load\n"); + exit(-3); + } + } + } + return 0; +} + +static struct sw_load *create_swload(struct sdp_header *header) +{ + struct sw_load *load; + + load = talloc_zero(tall_ctx_config, struct sw_load); + + strncpy((char *)load->file_id, header->firmware_info.sw_part, 20); + load->file_id_len = strlen(header->firmware_info.sw_part) + 1; + + strncpy((char *)load->file_version, header->firmware_info.version, 20); + load->file_version_len = strlen(header->firmware_info.version) + 1; + + return load; +} + +static int find_sw_load_params(const char *filename) +{ + struct stat stat; + struct sdp_header *header; + struct llist_head *entry; + int fd; + void *tall_firm_ctx = 0; + + entry = talloc_zero(tall_firm_ctx, struct llist_head); + INIT_LLIST_HEAD(entry); + + fd = open(filename, O_RDONLY); + if (!fd) { + perror("nada"); + return -1; + } + + /* verify the file */ + if (fstat(fd, &stat) == -1) { + perror("Can not stat the file"); + close(fd); + return -1; + } + + ipaccess_analyze_file(fd, stat.st_size, 0, entry); + if (close(fd) != 0) { + perror("Close failed.\n"); + return -1; + } + + /* try to find what we are looking for */ + llist_for_each_entry(header, entry, entry) { + if (ntohs(header->firmware_info.more_more_magic) == 0x1000) { + sw_load1 = create_swload(header); + } else if (ntohs(header->firmware_info.more_more_magic) == 0x2001) { + sw_load2 = create_swload(header); + } + } + + if (!sw_load1 || !sw_load2) { + fprintf(stderr, "Did not find data.\n"); + talloc_free(tall_firm_ctx); + return -1; + } + + talloc_free(tall_firm_ctx); + return 0; +} + +static void dump_entry(struct sdp_header_item *sub_entry, int part, int fd) +{ + int out_fd; + int copied; + char filename[4096]; + off_t target; + + if (!dump_files) + return; + + if (sub_entry->header_entry.something1 == 0) + return; + + snprintf(filename, sizeof(filename), "part.%d", part++); + out_fd = open(filename, O_WRONLY | O_CREAT, 0660); + if (out_fd < 0) { + perror("Can not dump firmware"); + return; + } + + target = sub_entry->absolute_offset + ntohl(sub_entry->header_entry.start) + 4; + if (lseek(fd, target, SEEK_SET) != target) { + perror("seek failed"); + close(out_fd); + return; + } + + for (copied = 0; copied < ntohl(sub_entry->header_entry.length); ++copied) { + char c; + if (read(fd, &c, sizeof(c)) != sizeof(c)) { + perror("copy failed"); + break; + } + + if (write(out_fd, &c, sizeof(c)) != sizeof(c)) { + perror("write failed"); + break; + } + } + + close(out_fd); +} + +static void analyze_firmware(const char *filename) +{ + struct stat stat; + struct sdp_header *header; + struct sdp_header_item *sub_entry; + struct llist_head *entry; + int fd; + void *tall_firm_ctx = 0; + int part = 0; + + entry = talloc_zero(tall_firm_ctx, struct llist_head); + INIT_LLIST_HEAD(entry); + + printf("Opening possible firmware '%s'\n", filename); + fd = open(filename, O_RDONLY); + if (!fd) { + perror("nada"); + return; + } + + /* verify the file */ + if (fstat(fd, &stat) == -1) { + perror("Can not stat the file"); + close(fd); + return; + } + + ipaccess_analyze_file(fd, stat.st_size, 0, entry); + + llist_for_each_entry(header, entry, entry) { + printf("Printing header information:\n"); + printf("more_more_magic: 0x%x\n", ntohs(header->firmware_info.more_more_magic)); + printf("header_length: %u\n", ntohl(header->firmware_info.header_length)); + printf("file_length: %u\n", ntohl(header->firmware_info.file_length)); + printf("sw_part: %.20s\n", header->firmware_info.sw_part); + printf("text1: %.64s\n", header->firmware_info.text1); + printf("time: %.12s\n", header->firmware_info.time); + printf("date: %.14s\n", header->firmware_info.date); + printf("text2: %.10s\n", header->firmware_info.text2); + printf("version: %.20s\n", header->firmware_info.version); + printf("subitems...\n"); + + llist_for_each_entry(sub_entry, &header->header_list, entry) { + printf("\tsomething1: %u\n", sub_entry->header_entry.something1); + printf("\ttext1: %.64s\n", sub_entry->header_entry.text1); + printf("\ttime: %.12s\n", sub_entry->header_entry.time); + printf("\tdate: %.14s\n", sub_entry->header_entry.date); + printf("\ttext2: %.10s\n", sub_entry->header_entry.text2); + printf("\tversion: %.20s\n", sub_entry->header_entry.version); + printf("\tlength: %u\n", ntohl(sub_entry->header_entry.length)); + printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1)); + printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2)); + printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start)); + printf("\tabs. offset: 0x%lx\n", sub_entry->absolute_offset); + printf("\n\n"); + + dump_entry(sub_entry, part++, fd); + } + printf("\n\n"); + } + + if (close(fd) != 0) { + perror("Close failed.\n"); + return; + } + + talloc_free(tall_firm_ctx); +} + +static void print_usage(void) +{ + printf("Usage: ipaccess-config\n"); +} + +static void print_help(void) +{ +#if 0 + printf("Commmands for reading from the BTS:\n"); + printf(" -D --dump\t\t\tDump the BTS configuration\n"); + printf("\n"); +#endif + printf("Commmands for writing to the BTS:\n"); + printf(" -u --unit-id UNIT_ID\t\tSet the Unit ID of the BTS\n"); + printf(" -o --oml-ip IP\t\tSet primary OML IP (IP of your BSC)\n"); + printf(" -i --ip-address IP/MASK\tSet static IP address + netmask of BTS\n"); + printf(" -g --ip-gateway IP\t\tSet static IP gateway of BTS\n"); + printf(" -r --restart\t\t\tRestart the BTS (after other operations)\n"); + printf(" -n --nvram-flags FLAGS/MASK\tSet NVRAM attributes\n"); + printf(" -S --nvattr-set FLAG\tSet one additional NVRAM attribute\n"); + printf(" -U --nvattr-unset FLAG\tSet one additional NVRAM attribute\n"); + printf(" -l --listen TESTNR\t\tPerform specified test number\n"); + printf(" -L --Listen TEST_NAME\t\tPerform specified test\n"); + printf(" -s --stream-id ID\t\tSet the IPA Stream Identifier for OML\n"); + printf(" -d --software FIRMWARE\tDownload firmware into BTS\n"); + printf("\n"); + printf("Miscellaneous commands:\n"); + printf(" -h --help\t\t\tthis text\n"); + printf(" -H --HELP\t\t\tPrint parameter details.\n"); + printf(" -f --firmware FIRMWARE\tProvide firmware information\n"); + printf(" -w --write-firmware\t\tThis will dump the firmware parts to the filesystem. Use with -f.\n"); + printf(" -p --loop\t\t\tLoop the tests executed with the --listen command.\n"); +} + +static void print_value_string(const struct value_string *val, int size) +{ + int i; + + for (i = 0; i < size - 1; ++i) { + char sep = val[i + 1].str == NULL ? '.' : ','; + printf("%s%c ", val[i].str, sep); + } + printf("\n"); +} + +static void print_options(void) +{ + + printf("Options for NVRAM (-S,-U):\n "); + print_value_string(&ipa_nvflag_strs[0], ARRAY_SIZE(ipa_nvflag_strs)); + + printf("Options for Tests (-L):\n "); + print_value_string(&ipa_test_strs[0], ARRAY_SIZE(ipa_test_strs)); +} + +extern void bts_model_nanobts_init(); + +int main(int argc, char **argv) +{ + struct gsm_bts *bts; + struct sockaddr_in sin; + int rc, option_index = 0, stream_id = 0xff; + + osmo_init_logging(&log_info); + log_parse_category_mask(osmo_stderr_target, "DNM,0"); + bts_model_nanobts_init(); + + printf("ipaccess-config (C) 2009-2010 by Harald Welte and others\n"); + printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); + + while (1) { + int c; + unsigned long ul; + char *slash; + static struct option long_options[] = { + { "unit-id", 1, 0, 'u' }, + { "oml-ip", 1, 0, 'o' }, + { "ip-address", 1, 0, 'i' }, + { "ip-gateway", 1, 0, 'g' }, + { "restart", 0, 0, 'r' }, + { "nvram-flags", 1, 0, 'n' }, + { "nvattr-set", 1, 0, 'S' }, + { "nvattr-unset", 1, 0, 'U' }, + { "help", 0, 0, 'h' }, + { "HELP", 0, 0, 'H' }, + { "listen", 1, 0, 'l' }, + { "Listen", 1, 0, 'L' }, + { "stream-id", 1, 0, 's' }, + { "software", 1, 0, 'd' }, + { "firmware", 1, 0, 'f' }, + { "write-firmware", 0, 0, 'w' }, + { "disable-color", 0, 0, 'c'}, + { "loop", 0, 0, 'p' }, + { 0, 0, 0, 0 }, + }; + + c = getopt_long(argc, argv, "u:o:i:g:rn:S:U:l:L:hs:d:f:wcpH", long_options, + &option_index); + + if (c == -1) + break; + + switch (c) { + case 'u': + unit_id = optarg; + break; + case 'o': + prim_oml_ip = optarg; + break; + case 'i': + slash = strchr(optarg, '/'); + if (!slash) + exit(2); + bts_ip_addr = optarg; + *slash = 0; + bts_ip_mask = slash+1; + break; + case 'g': + bts_ip_gw = optarg; + break; + case 'r': + restart = 1; + break; + case 'n': + slash = strchr(optarg, '/'); + if (!slash) + exit(2); + ul = strtoul(optarg, NULL, 16); + nv_flags = ul & 0xffff; + ul = strtoul(slash+1, NULL, 16); + nv_mask = ul & 0xffff; + break; + case 'S': + if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 1) < 0) + exit(2); + break; + case 'U': + if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 0) < 0) + exit(2); + break; + case 'l': + net_listen_testnr = atoi(optarg); + break; + case 'L': + net_listen_testnr = get_string_value(ipa_test_strs, + optarg); + if (net_listen_testnr < 0) { + fprintf(stderr, + "The test '%s' is not known. Use -H to" + " see available tests.\n", optarg); + exit(2); + } + break; + case 's': + stream_id = atoi(optarg); + break; + case 'd': + software = strdup(optarg); + if (find_sw_load_params(optarg) != 0) + exit(0); + break; + case 'f': + firmware_analysis = optarg; + break; + case 'w': + dump_files = 1; + break; + case 'c': + log_set_use_color(osmo_stderr_target, 0); + break; + case 'p': + loop_tests = 1; + break; + case 'h': + print_usage(); + print_help(); + exit(0); + case 'H': + print_options(); + exit(0); + } + }; + + if (firmware_analysis) + analyze_firmware(firmware_analysis); + + if (optind >= argc) { + /* only warn if we have not done anything else */ + if (!firmware_analysis) + fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n"); + exit(2); + } + libosmo_abis_init(tall_ctx_config); + + bsc_gsmnet = gsm_network_init(1, 1, NULL); + if (!bsc_gsmnet) + exit(1); + + bts = gsm_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_NANOBTS, HARDCODED_TSC, + HARDCODED_BSIC); + /* ip.access supports up to 4 chained TRX */ + gsm_bts_trx_alloc(bts); + gsm_bts_trx_alloc(bts); + gsm_bts_trx_alloc(bts); + bts->oml_tei = stream_id; + + osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); + osmo_signal_register_handler(SS_IPAC_NWL, nwl_sig_cb, NULL); + + ipac_nwl_init(); + + printf("Trying to connect to ip.access BTS ...\n"); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(argv[optind], &sin.sin_addr); + rc = ia_config_connect(bts, &sin); + if (rc < 0) { + perror("Error connecting to the BTS"); + exit(1); + } + + bts->oml_link->ts->sign.delay = 10; + bts->c0->rsl_link->ts->sign.delay = 10; + while (1) { + rc = osmo_select_main(0); + if (rc < 0) + exit(3); + } + + exit(0); +} + diff --git a/src/ipaccess/ipaccess-find.c b/src/ipaccess/ipaccess-find.c new file mode 100644 index 000000000..bb9819ed1 --- /dev/null +++ b/src/ipaccess/ipaccess-find.c @@ -0,0 +1,208 @@ +/* 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 + +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) { + rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, + strlen(ifname)); + 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' ", ipaccess_idtag_name(t_tag), cur); + + cur += t_len; + } + printf("\n"); + return 0; +} + +static int read_response(int fd) +{ + unsigned char buf[255]; + struct sockaddr_in sa; + int len; + socklen_t sa_len = sizeof(sa); + + len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len); + if (len < 0) + return len; + + /* 2 bytes length, 1 byte protocol (0xfe) */ + if (buf[2] != 0xfe) + return 0; + + if (buf[4] != IPAC_MSGT_ID_RESP) + return 0; + + return parse_response(buf+6, len-6); +} + +static int bfd_cb(struct osmo_fd *bfd, unsigned int flags) +{ + if (flags & BSC_FD_READ) + return read_response(bfd->fd); + if (flags & BSC_FD_WRITE) { + bfd->when &= ~BSC_FD_WRITE; + return bcast_find(bfd->fd); + } + return 0; +} + +static struct osmo_timer_list timer; + +static void timer_cb(void *_data) +{ + struct osmo_fd *bfd = _data; + + bfd->when |= BSC_FD_WRITE; + + osmo_timer_schedule(&timer, 5, 0); +} + +int main(int argc, char **argv) +{ + struct osmo_fd bfd; + char *ifname = NULL; + int rc; + + printf("ipaccess-find (C) 2009 by Harald Welte\n"); + printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); + + if (argc < 2) { + fprintf(stdout, "you might need to specify the outgoing\n" + " network interface, e.g. ``%s eth0''\n", argv[0]); + } else { + ifname = argv[1]; + } + + bfd.cb = bfd_cb; + bfd.when = BSC_FD_READ | BSC_FD_WRITE; + bfd.fd = udp_sock(ifname); + if (bfd.fd < 0) { + perror("Cannot create local socket for broadcast udp"); + exit(1); + } + + osmo_fd_register(&bfd); + + timer.cb = timer_cb; + timer.data = &bfd; + + osmo_timer_schedule(&timer, 5, 0); + + printf("Trying to find ip.access BTS by broadcast UDP...\n"); + + while (1) { + rc = osmo_select_main(0); + if (rc < 0) + exit(3); + } + + exit(0); +} + diff --git a/src/ipaccess/ipaccess-firmware.c b/src/ipaccess/ipaccess-firmware.c new file mode 100644 index 000000000..5f55bb526 --- /dev/null +++ b/src/ipaccess/ipaccess-firmware.c @@ -0,0 +1,135 @@ +/* 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/src/ipaccess/ipaccess-proxy.c b/src/ipaccess/ipaccess-proxy.c new file mode 100644 index 000000000..9ab4ebbf4 --- /dev/null +++ b/src/ipaccess/ipaccess-proxy.c @@ -0,0 +1,1202 @@ +/* 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 + +/* 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); + 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); + uint16_t site_id, bts_id, trx_id; + struct ipa_bts_conn *ipbc; + int ret = 0; + + switch (msg_type) { + case IPAC_MSGT_PING: + ret = ipaccess_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 */ + ipaccess_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, ... */ + site_id = bts_id = trx_id = 0; + ipaccess_parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT), + &site_id, &bts_id, &trx_id); + ipbc = find_bts_by_unitid(ipp, site_id, 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, site_id, bts_id, + 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", + site_id, bts_id, trx_id); + + if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) { + LOGP(DLINP, LOGL_ERROR, "Second OML connection from " + "same BTS ?!?\n"); + return 0; + } + + if (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 |= trx_id << 8; + ipbc->rsl_conn[trx_id] = ipc; + + /* Create RSL TCP connection towards BSC */ + sin.sin_port = htons(IPA_TCP_PORT_RSL); + ipbc->bsc_rsl_conn[trx_id] = + connect_bsc(&sin, RSL_TO_BSC | (trx_id << 8), ipbc); + if (!ipbc->bsc_oml_conn) + return -EIO; + DEBUGP(DLINP, "(%u/%u/%u) Connected RSL to BSC\n", + site_id, bts_id, trx_id); + } + break; + case IPAC_MSGT_ID_GET: + DEBUGP(DLMI, "ID_GET\n"); + if ((bfd->priv_nr & 0xff) != OML_TO_BSC && + (bfd->priv_nr & 0xff) != RSL_TO_BSC) { + DEBUGP(DLINP, "IDentity REQuest from BTS ?!?\n"); + return -EIO; + } + ipbc = ipc->bts_conn; + if (!ipbc) { + DEBUGP(DLINP, "ID_GET from BSC before we have ID_RESP from BTS\n"); + return -EIO; + } + ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len); + break; + case IPAC_MSGT_ID_ACK: + DEBUGP(DLMI, "ID_ACK? -> ACK!\n"); + ret = ipaccess_send_id_ack(bfd->fd); + break; + default: + LOGP(DLMI, LOGL_ERROR, "Unhandled IPA type; %d\n", msg_type); + return 1; + break; + } + return 0; +} + +struct msgb *ipaccess_proxy_read_msg(struct osmo_fd *bfd, int *error) +{ + struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP"); + struct ipaccess_head *hh; + int len, ret = 0; + + if (!msg) { + *error = -ENOMEM; + return NULL; + } + + /* first read our 3-byte header */ + hh = (struct ipaccess_head *) msg->data; + ret = recv(bfd->fd, msg->data, 3, 0); + if (ret < 0) { + if (errno != EAGAIN) + LOGP(DLINP, LOGL_ERROR, "recv error: %s\n", strerror(errno)); + msgb_free(msg); + *error = ret; + return NULL; + } else if (ret == 0) { + msgb_free(msg); + *error = ret; + return NULL; + } + + msgb_put(msg, ret); + + /* then read te length as specified in header */ + msg->l2h = msg->data + sizeof(*hh); + len = ntohs(hh->len); + ret = recv(bfd->fd, msg->l2h, len, 0); + if (ret < len) { + LOGP(DLINP, LOGL_ERROR, "short read!\n"); + msgb_free(msg); + *error = -EIO; + return NULL; + } + msgb_put(msg, ret); + + return msg; +} + +static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc, + unsigned int priv_nr) +{ + struct ipa_proxy_conn *bsc_conn; + unsigned int trx_id = priv_nr >> 8; + + switch (priv_nr & 0xff) { + case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ + bsc_conn = ipbc->bsc_oml_conn; + break; + case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ + bsc_conn = ipbc->bsc_rsl_conn[trx_id]; + break; + case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ + bsc_conn = ipbc->oml_conn; + break; + case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ + bsc_conn = ipbc->rsl_conn[trx_id]; + break; + default: + bsc_conn = NULL; + break; + } + return bsc_conn; +} + +static void reconn_tmr_cb(void *data) +{ + struct ipa_proxy *ipp = data; + struct ipa_bts_conn *ipbc; + struct sockaddr_in sin; + int i; + + DEBUGP(DLINP, "Running reconnect timer\n"); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(bsc_ipaddr, &sin.sin_addr); + + llist_for_each_entry(ipbc, &ipp->bts_list, list) { + /* if OML to BSC is dead, try to restore it */ + if (ipbc->oml_conn && !ipbc->bsc_oml_conn) { + sin.sin_port = htons(IPA_TCP_PORT_OML); + logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0); + LOGPC(DLINP, LOGL_NOTICE, "OML Trying to reconnect\n"); + ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); + if (!ipbc->bsc_oml_conn) + goto reschedule; + logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0); + LOGPC(DLINP, LOGL_NOTICE, "OML Reconnected\n"); + } + /* if we (still) don't have a OML connection, skip RSL */ + if (!ipbc->oml_conn || !ipbc->bsc_oml_conn) + continue; + + for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) { + unsigned int priv_nr; + /* don't establish RSL links which we don't have */ + if (!ipbc->rsl_conn[i]) + continue; + if (ipbc->bsc_rsl_conn[i]) + continue; + priv_nr = ipbc->rsl_conn[i]->fd.priv_nr; + priv_nr &= ~0xff; + priv_nr |= RSL_TO_BSC; + sin.sin_port = htons(IPA_TCP_PORT_RSL); + logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8); + LOGPC(DLINP, LOGL_NOTICE, "RSL Trying to reconnect\n"); + ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc); + if (!ipbc->bsc_rsl_conn[i]) + goto reschedule; + logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8); + LOGPC(DLINP, LOGL_NOTICE, "RSL Reconnected\n"); + } + } + return; + +reschedule: + osmo_timer_schedule(&ipp->reconn_timer, 5, 0); +} + +static void handle_dead_socket(struct osmo_fd *bfd) +{ + struct ipa_proxy_conn *ipc = bfd->data; /* local conn */ + struct ipa_proxy_conn *bsc_conn; /* remote conn */ + struct ipa_bts_conn *ipbc = ipc->bts_conn; + unsigned int trx_id = bfd->priv_nr >> 8; + struct msgb *msg, *msg2; + + osmo_fd_unregister(bfd); + close(bfd->fd); + bfd->fd = -1; + + /* FIXME: clear tx_queue, remove all references, etc. */ + llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list) + msgb_free(msg); + + switch (bfd->priv_nr & 0xff) { + case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ + /* The BTS started a connection with us but we got no + * IPAC_MSGT_ID_RESP message yet, in that scenario we did not + * allocate the ipa_bts_conn structure. */ + if (ipbc == NULL) + break; + ipbc->oml_conn = NULL; + bsc_conn = ipbc->bsc_oml_conn; + /* close the connection to the BSC */ + osmo_fd_unregister(&bsc_conn->fd); + close(bsc_conn->fd.fd); + llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) + msgb_free(msg); + talloc_free(bsc_conn); + ipbc->bsc_oml_conn = NULL; + /* FIXME: do we need to delete the entire ipbc ? */ + break; + case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ + ipbc->rsl_conn[trx_id] = NULL; + bsc_conn = ipbc->bsc_rsl_conn[trx_id]; + /* close the connection to the BSC */ + osmo_fd_unregister(&bsc_conn->fd); + close(bsc_conn->fd.fd); + llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) + msgb_free(msg); + talloc_free(bsc_conn); + ipbc->bsc_rsl_conn[trx_id] = NULL; + break; + case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ + ipbc->bsc_oml_conn = NULL; + bsc_conn = ipbc->oml_conn; + /* start reconnect timer */ + osmo_timer_schedule(&ipp->reconn_timer, 5, 0); + break; + case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ + ipbc->bsc_rsl_conn[trx_id] = NULL; + bsc_conn = ipbc->rsl_conn[trx_id]; + /* start reconnect timer */ + osmo_timer_schedule(&ipp->reconn_timer, 5, 0); + break; + default: + bsc_conn = NULL; + break; + } + + talloc_free(ipc); +} + +static void patch_gprs_msg(struct ipa_bts_conn *ipbc, int priv_nr, struct msgb *msg) +{ + uint8_t *nsvci; + + if ((priv_nr & 0xff) != OML_FROM_BTS && (priv_nr & 0xff) != OML_TO_BSC) + return; + + if (msgb_l2len(msg) != 39) + return; + + /* + * Check if this is a IPA Set Attribute or IPA Set Attribute ACK + * and if the FOM Class is GPRS NSVC0 and then we will patch it. + * + * The patch assumes the message looks like the one from the trace + * but we only match messages with a specific size anyway... So + * this hack should work just fine. + */ + + if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 && + msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 && + msg->l2h[18] == 0xf5 && msg->l2h[19] == 0xf2) { + nsvci = &msg->l2h[23]; + ipbc->gprs_orig_port = *(uint16_t *)(nsvci+8); + ipbc->gprs_orig_ip = *(uint32_t *)(nsvci+10); + *(uint16_t *)(nsvci+8) = htons(ipbc->gprs_local_port); + *(uint32_t *)(nsvci+10) = ipbc->ipp->listen_addr.s_addr; + } else if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 && + msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 && + msg->l2h[18] == 0xf6 && msg->l2h[19] == 0xf2) { + nsvci = &msg->l2h[23]; + *(uint16_t *)(nsvci+8) = ipbc->gprs_orig_port; + *(uint32_t *)(nsvci+10) = ipbc->gprs_orig_ip; + } +} + +static int handle_tcp_read(struct osmo_fd *bfd) +{ + struct ipa_proxy_conn *ipc = bfd->data; + struct ipa_bts_conn *ipbc = ipc->bts_conn; + struct ipa_proxy_conn *bsc_conn; + struct msgb *msg; + struct ipaccess_head *hh; + int ret = 0; + char *btsbsc; + + if ((bfd->priv_nr & 0xff) <= 2) + btsbsc = "BTS"; + else + btsbsc = "BSC"; + + msg = ipaccess_proxy_read_msg(bfd, &ret); + if (!msg) { + if (ret == 0) { + logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); + LOGPC(DLINP, LOGL_NOTICE, "%s disappeared, " + "dead socket\n", btsbsc); + handle_dead_socket(bfd); + } + return ret; + } + + msgb_put(msg, ret); + logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); + DEBUGPC(DLMI, "RX<-%s: %s\n", btsbsc, osmo_hexdump(msg->data, msg->len)); + + hh = (struct ipaccess_head *) msg->data; + if (hh->proto == IPAC_PROTO_IPACCESS) { + ret = ipaccess_rcvmsg(ipc, msg, bfd); + if (ret < 0) { + osmo_fd_unregister(bfd); + close(bfd->fd); + bfd->fd = -1; + talloc_free(bfd); + msgb_free(msg); + return ret; + } else if (ret == 0) { + /* we do not forward parts of the CCM protocol + * through the proxy but rather terminate it ourselves. */ + msgb_free(msg); + return ret; + } + } + + if (!ipbc) { + LOGP(DLINP, LOGL_ERROR, + "received %s packet but no ipc->bts_conn?!?\n", btsbsc); + msgb_free(msg); + return -EIO; + } + + bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr); + if (bsc_conn) { + if (gprs_ns_ipaddr) + patch_gprs_msg(ipbc, bfd->priv_nr, msg); + /* enqueue packet towards BSC */ + msgb_enqueue(&bsc_conn->tx_queue, msg); + /* mark respective filedescriptor as 'we want to write' */ + bsc_conn->fd.when |= BSC_FD_WRITE; + } else { + logp_ipbc_uid(DLINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8); + LOGPC(DLINP, LOGL_INFO, "Dropping packet from %s, " + "since remote connection is dead\n", btsbsc); + msgb_free(msg); + } + + return ret; +} + +/* a TCP socket is ready to be written to */ +static int handle_tcp_write(struct osmo_fd *bfd) +{ + struct ipa_proxy_conn *ipc = bfd->data; + struct ipa_bts_conn *ipbc = ipc->bts_conn; + struct llist_head *lh; + struct msgb *msg; + char *btsbsc; + int ret; + + if ((bfd->priv_nr & 0xff) <= 2) + btsbsc = "BTS"; + else + btsbsc = "BSC"; + + + /* get the next msg for this timeslot */ + if (llist_empty(&ipc->tx_queue)) { + bfd->when &= ~BSC_FD_WRITE; + return 0; + } + lh = ipc->tx_queue.next; + llist_del(lh); + msg = llist_entry(lh, struct msgb, list); + + logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); + DEBUGPC(DLMI, "TX %04x: %s\n", bfd->priv_nr, + osmo_hexdump(msg->data, msg->len)); + + ret = send(bfd->fd, msg->data, msg->len, 0); + msgb_free(msg); + + if (ret == 0) { + logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); + LOGP(DLINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc); + handle_dead_socket(bfd); + } + + return ret; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int 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 = 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 = ipaccess_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; + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); + if (ret < 0) { + LOGP(DLINP, LOGL_ERROR, "Could not connect socket: %s\n", + inet_ntoa(sa->sin_addr)); + close(bfd->fd); + talloc_free(ipc); + return NULL; + } + + /* pre-fill tx_queue with identity request */ + ret = osmo_fd_register(bfd); + if (ret < 0) { + close(bfd->fd); + talloc_free(ipc); + return NULL; + } + + return ipc; +} + +static int ipaccess_proxy_setup(void) +{ + int ret; + + ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy); + if (!ipp) + return -ENOMEM; + INIT_LLIST_HEAD(&ipp->bts_list); + ipp->reconn_timer.cb = reconn_tmr_cb; + ipp->reconn_timer.data = ipp; + + /* Listen for OML connections */ + ret = make_sock(&ipp->oml_listen_fd, IPPROTO_TCP, INADDR_ANY, + IPA_TCP_PORT_OML, OML_FROM_BTS, listen_fd_cb, NULL); + if (ret < 0) + return ret; + + /* Listen for RSL connections */ + ret = make_sock(&ipp->rsl_listen_fd, IPPROTO_TCP, INADDR_ANY, + IPA_TCP_PORT_RSL, RSL_FROM_BTS, listen_fd_cb, NULL); + + if (ret < 0) + return ret; + + /* Connect the GPRS NS Socket */ + if (gprs_ns_ipaddr) { + inet_aton(gprs_ns_ipaddr, &ipp->gprs_addr); + inet_aton(listen_ipaddr, &ipp->listen_addr); + } + + return ret; +} + +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGABRT: + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + talloc_report_full(tall_bsc_ctx, stderr); + break; + default: + break; + } +} + +static void print_help(void) +{ + printf(" ipaccess-proxy is a proxy BTS.\n"); + printf(" -h --help. This help text.\n"); + printf(" -l --listen IP. The ip to listen to.\n"); + printf(" -b --bsc IP. The BSC IP address.\n"); + printf(" -g --gprs IP. Take GPRS NS from that IP.\n"); + printf("\n"); + printf(" -s --disable-color. Disable the color inside the logging message.\n"); + printf(" -e --log-level number. Set the global loglevel.\n"); + printf(" -T --timestamp. Prefix every log message with a timestamp.\n"); + printf(" -V --version. Print the version of OpenBSC.\n"); +} + +static void print_usage(void) +{ + printf("Usage: ipaccess-proxy [options]\n"); +} + +enum { + IPA_PROXY_OPT_LISTEN_NONE = 0, + IPA_PROXY_OPT_LISTEN_IP = (1 << 0), + IPA_PROXY_OPT_BSC_IP = (1 << 1), +}; + +static void handle_options(int argc, char** argv) +{ + int options_mask = 0; + + /* disable explicit missing arguments error output from getopt_long */ + opterr = 0; + + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"disable-color", 0, 0, 's'}, + {"timestamp", 0, 0, 'T'}, + {"log-level", 1, 0, 'e'}, + {"listen", 1, 0, 'l'}, + {"bsc", 1, 0, 'b'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "hsTe:l:b:g:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + print_help(); + exit(0); + case 'l': + listen_ipaddr = optarg; + options_mask |= IPA_PROXY_OPT_LISTEN_IP; + break; + case 'b': + bsc_ipaddr = optarg; + options_mask |= IPA_PROXY_OPT_BSC_IP; + break; + case 'g': + gprs_ns_ipaddr = optarg; + break; + case 's': + log_set_use_color(osmo_stderr_target, 0); + break; + case 'T': + log_set_print_timestamp(osmo_stderr_target, 1); + break; + case 'e': + log_set_log_level(osmo_stderr_target, atoi(optarg)); + break; + case '?': + if (optopt) { + printf("ERROR: missing mandatory argument " + "for `%s' option\n", argv[optind-1]); + } else { + printf("ERROR: unknown option `%s'\n", + argv[optind-1]); + } + print_usage(); + print_help(); + exit(EXIT_FAILURE); + break; + default: + /* ignore */ + break; + } + } + if ((options_mask & (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP)) + != (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP)) { + printf("ERROR: You have to specify `--listen' and `--bsc' " + "options at least.\n"); + print_usage(); + print_help(); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char **argv) +{ + int rc; + + tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy"); + + osmo_init_logging(&log_info); + log_parse_category_mask(osmo_stderr_target, "DLINP:DLMI"); + + handle_options(argc, argv); + + rc = ipaccess_proxy_setup(); + if (rc < 0) + exit(1); + + signal(SIGUSR1, &signal_handler); + signal(SIGABRT, &signal_handler); + osmo_init_ignore_signals(); + + while (1) { + osmo_select_main(0); + } +} diff --git a/src/ipaccess/network_listen.c b/src/ipaccess/network_listen.c new file mode 100644 index 000000000..6749c4aa3 --- /dev/null +++ b/src/ipaccess/network_listen.c @@ -0,0 +1,254 @@ +/* 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 */ + test_rep_len = ntohs(*(uint16_t *) &foh->data[3]); + /* 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 */ + ferr_list_len = ntohs(*(uint16_t *) &foh->data[7]); + + /* 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 */ + ferr_list_len = ntohs(*(uint16_t *) &foh->data[7]); + + /* 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/src/libbsc/Makefile.am b/src/libbsc/Makefile.am new file mode 100644 index 000000000..42fabab6d --- /dev/null +++ b/src/libbsc/Makefile.am @@ -0,0 +1,26 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) + +noinst_LIBRARIES = libbsc.a + +libbsc_a_SOURCES = abis_nm.c abis_nm_vty.c \ + abis_om2000.c abis_om2000_vty.c \ + abis_rsl.c bsc_rll.c \ + paging.c \ + bts_ericsson_rbs2000.c \ + bts_ipaccess_nanobts.c \ + bts_siemens_bs11.c \ + bts_nokia_site.c \ + bts_unknown.c \ + bts_sysmobts.c \ + chan_alloc.c \ + gsm_subscriber_base.c \ + handover_decision.c handover_logic.c meas_rep.c \ + rest_octets.c system_information.c \ + e1_config.c \ + bsc_api.c bsc_msc.c bsc_vty.c \ + gsm_04_08_utils.c \ + bsc_init.c bts_init.c bsc_rf_ctrl.c \ + arfcn_range_encode.c + diff --git a/src/libbsc/abis_nm.c b/src/libbsc/abis_nm.c new file mode 100644 index 000000000..a2e9f0d5d --- /dev/null +++ b/src/libbsc/abis_nm.c @@ -0,0 +1,2741 @@ +/* GSM Network Management (OML) messages on the A-bis interface + * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ + +/* (C) 2008-2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OM_ALLOC_SIZE 1024 +#define OM_HEADROOM_SIZE 128 +#define IPACC_SEGMENT_SIZE 245 + +int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const uint8_t *buf, int len) +{ + if (!bts->model) + return -EIO; + return tlv_parse(tp, &bts->model->nm_att_tlvdef, buf, len, 0, 0); +} + +static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (arr[i] == mt) + return 1; + } + + return 0; +} + +#if 0 +/* is this msgtype the usual ACK/NACK type ? */ +static int is_ack_nack(enum abis_nm_msgtype mt) +{ + return !is_in_arr(mt, no_ack_nack, ARRAY_SIZE(no_ack_nack)); +} +#endif + +/* is this msgtype a report ? */ +static int is_report(enum abis_nm_msgtype mt) +{ + return is_in_arr(mt, abis_nm_reports, ARRAY_SIZE(abis_nm_reports)); +} + +#define MT_ACK(x) (x+1) +#define MT_NACK(x) (x+2) + +static void fill_om_hdr(struct abis_om_hdr *oh, uint8_t len) +{ + oh->mdisc = ABIS_OM_MDISC_FOM; + oh->placement = ABIS_OM_PLACEMENT_ONLY; + oh->sequence = 0; + oh->length = len; +} + +static void 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; +} + +static struct msgb *nm_msgb_alloc(void) +{ + return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, + "OML"); +} + +int _abis_nm_sendmsg(struct msgb *msg) +{ + msg->l2h = msg->data; + + if (!msg->dst) { + LOGP(DNM, LOGL_ERROR, "%s: msg->dst == NULL\n", __func__); + return -EINVAL; + } + + return abis_sendmsg(msg); +} + +/* Send a OML NM Message from BSC to BTS */ +static int abis_nm_queue_msg(struct gsm_bts *bts, struct msgb *msg) +{ + msg->dst = bts->oml_link; + + /* queue OML messages */ + if (llist_empty(&bts->abis_queue) && !bts->abis_nm_pend) { + bts->abis_nm_pend = OBSC_NM_W_ACK_CB(msg); + return _abis_nm_sendmsg(msg); + } else { + msgb_enqueue(&bts->abis_queue, msg); + return 0; + } + +} + +int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg) +{ + OBSC_NM_W_ACK_CB(msg) = 1; + return abis_nm_queue_msg(bts, msg); +} + +static int abis_nm_sendmsg_direct(struct gsm_bts *bts, struct msgb *msg) +{ + OBSC_NM_W_ACK_CB(msg) = 0; + return abis_nm_queue_msg(bts, msg); +} + +static int abis_nm_rcvmsg_sw(struct msgb *mb); + +int nm_is_running(struct gsm_nm_state *s) { + return (s->operational == NM_OPSTATE_ENABLED) && ( + (s->availability == NM_AVSTATE_OK) || + (s->availability == 0xff) + ); +} + +/* Update the administrative state of a given object in our in-memory data + * structures and send an event to the higher layer */ +static int update_admstate(struct gsm_bts *bts, uint8_t obj_class, + struct abis_om_obj_inst *obj_inst, uint8_t adm_state) +{ + struct gsm_nm_state *nm_state, new_state; + struct nm_statechg_signal_data nsd; + + memset(&nsd, 0, sizeof(nsd)); + + nsd.obj = gsm_objclass2obj(bts, obj_class, obj_inst); + if (!nsd.obj) + return -EINVAL; + nm_state = gsm_objclass2nmstate(bts, obj_class, obj_inst); + if (!nm_state) + return -1; + + new_state = *nm_state; + new_state.administrative = adm_state; + + nsd.bts = bts; + nsd.obj_class = obj_class; + nsd.old_state = nm_state; + nsd.new_state = &new_state; + nsd.obj_inst = obj_inst; + osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); + + nm_state->administrative = adm_state; + + return 0; +} + +static int abis_nm_rx_statechg_rep(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct e1inp_sign_link *sign_link = mb->dst; + struct gsm_bts *bts = sign_link->trx->bts; + struct tlv_parsed tp; + struct gsm_nm_state *nm_state, new_state; + + DEBUGPC(DNM, "STATE CHG: "); + + memset(&new_state, 0, sizeof(new_state)); + + nm_state = gsm_objclass2nmstate(bts, foh->obj_class, &foh->obj_inst); + if (!nm_state) { + DEBUGPC(DNM, "unknown object class\n"); + return -EINVAL; + } + + new_state = *nm_state; + + abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); + if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) { + new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE); + DEBUGPC(DNM, "OP_STATE=%s ", + abis_nm_opstate_name(new_state.operational)); + } + if (TLVP_PRESENT(&tp, NM_ATT_AVAIL_STATUS)) { + if (TLVP_LEN(&tp, NM_ATT_AVAIL_STATUS) == 0) + new_state.availability = 0xff; + else + new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS); + DEBUGPC(DNM, "AVAIL=%s(%02x) ", + abis_nm_avail_name(new_state.availability), + new_state.availability); + } else + new_state.availability = 0xff; + if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) { + new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); + DEBUGPC(DNM, "ADM=%2s ", + get_value_string(abis_nm_adm_state_names, + new_state.administrative)); + } + DEBUGPC(DNM, "\n"); + + if ((new_state.administrative != 0 && nm_state->administrative == 0) || + new_state.operational != nm_state->operational || + new_state.availability != nm_state->availability) { + /* Update the operational state of a given object in our in-memory data + * structures and send an event to the higher layer */ + struct nm_statechg_signal_data nsd; + nsd.obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst); + nsd.obj_class = foh->obj_class; + nsd.old_state = nm_state; + nsd.new_state = &new_state; + nsd.obj_inst = &foh->obj_inst; + nsd.bts = bts; + osmo_signal_dispatch(SS_NM, S_NM_STATECHG_OPER, &nsd); + nm_state->operational = new_state.operational; + nm_state->availability = new_state.availability; + if (nm_state->administrative == 0) + nm_state->administrative = new_state.administrative; + } +#if 0 + if (op_state == 1) { + /* try to enable objects that are disabled */ + abis_nm_opstart(bts, foh->obj_class, + foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, + foh->obj_inst.ts_nr); + } +#endif + return 0; +} + +static int rx_fail_evt_rep(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct e1inp_sign_link *sign_link = mb->dst; + struct tlv_parsed tp; + const uint8_t *p_val; + char *p_text; + + LOGPC(DNM, LOGL_ERROR, "Failure Event Report "); + + abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); + + if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE)) + LOGPC(DNM, LOGL_ERROR, "Type=%s ", + abis_nm_event_type_name(*TLVP_VAL(&tp, NM_ATT_EVENT_TYPE))); + if (TLVP_PRESENT(&tp, NM_ATT_SEVERITY)) + LOGPC(DNM, LOGL_ERROR, "Severity=%s ", + abis_nm_severity_name(*TLVP_VAL(&tp, NM_ATT_SEVERITY))); + if (TLVP_PRESENT(&tp, NM_ATT_PROB_CAUSE)) { + p_val = TLVP_VAL(&tp, NM_ATT_PROB_CAUSE); + LOGPC(DNM, LOGL_ERROR, "Probable cause= %02X %02X %02X ", p_val[0], p_val[1], p_val[2]); + } + if (TLVP_PRESENT(&tp, NM_ATT_ADD_TEXT)) { + p_val = TLVP_VAL(&tp, NM_ATT_ADD_TEXT); + p_text = talloc_strndup(tall_bsc_ctx, (const char *) p_val, TLVP_LEN(&tp, NM_ATT_ADD_TEXT)); + if (p_text) { + LOGPC(DNM, LOGL_ERROR, "Additional Text=%s ", p_text); + talloc_free(p_text); + } + } + + LOGPC(DNM, LOGL_ERROR, "\n"); + + return 0; +} + +static int abis_nm_rcvmsg_report(struct msgb *mb) +{ + struct abis_om_fom_hdr *foh = msgb_l3(mb); + uint8_t mt = foh->msg_type; + + abis_nm_debugp_foh(DNM, foh); + + //nmh->cfg->report_cb(mb, foh); + + switch (mt) { + case NM_MT_STATECHG_EVENT_REP: + return abis_nm_rx_statechg_rep(mb); + break; + case NM_MT_SW_ACTIVATED_REP: + DEBUGPC(DNM, "Software Activated Report\n"); + osmo_signal_dispatch(SS_NM, S_NM_SW_ACTIV_REP, mb); + break; + case NM_MT_FAILURE_EVENT_REP: + rx_fail_evt_rep(mb); + osmo_signal_dispatch(SS_NM, S_NM_FAIL_REP, mb); + break; + case NM_MT_TEST_REP: + DEBUGPC(DNM, "Test Report\n"); + osmo_signal_dispatch(SS_NM, S_NM_TEST_REP, mb); + break; + default: + DEBUGPC(DNM, "reporting NM MT 0x%02x\n", mt); + break; + + }; + + return 0; +} + +/* Activate the specified software into the BTS */ +static int ipacc_sw_activate(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, + uint8_t i2, const uint8_t *sw_desc, uint8_t swdesc_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t len = swdesc_len; + uint8_t *trailer; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2); + + trailer = msgb_put(msg, swdesc_len); + memcpy(trailer, sw_desc, swdesc_len); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_parse_sw_config(const uint8_t *sw_descr, const size_t sw_descr_len, + struct abis_nm_sw_descr *desc, const int res_len) +{ + static const struct tlv_definition sw_descr_def = { + .def = { + [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V, }, + [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V, }, + }, + }; + + size_t pos = 0; + int desc_pos = 0; + + for (pos = 0; pos < sw_descr_len && desc_pos < res_len; ++desc_pos) { + uint8_t tag; + uint16_t tag_len; + const uint8_t *val; + int len; + + memset(&desc[desc_pos], 0, sizeof(desc[desc_pos])); + desc[desc_pos].start = &sw_descr[pos]; + + /* Classic TLV parsing doesn't work well with SW_DESCR because of it's + * nested nature and the fact you have to assume it contains only two sub + * tags NM_ATT_FILE_VERSION & NM_ATT_FILE_ID to parse it */ + if (sw_descr[pos] != NM_ATT_SW_DESCR) { + LOGP(DNM, LOGL_ERROR, + "SW_DESCR attribute identifier not found!\n"); + return -1; + } + + pos += 1; + len = tlv_parse_one(&tag, &tag_len, &val, + &sw_descr_def, &sw_descr[pos], sw_descr_len - pos); + if (len < 0 || (tag != NM_ATT_FILE_ID)) { + LOGP(DNM, LOGL_ERROR, + "FILE_ID attribute identifier not found!\n"); + return -2; + } + desc[desc_pos].file_id = val; + desc[desc_pos].file_id_len = tag_len; + pos += len; + + + len = tlv_parse_one(&tag, &tag_len, &val, + &sw_descr_def, &sw_descr[pos], sw_descr_len - pos); + if (len < 0 || (tag != NM_ATT_FILE_VERSION)) { + LOGP(DNM, LOGL_ERROR, + "FILE_VERSION attribute identifier not found!\n"); + return -3; + } + desc[desc_pos].file_ver = val; + desc[desc_pos].file_ver_len = tag_len; + pos += len; + + /* final size */ + desc[desc_pos].len = &sw_descr[pos] - desc[desc_pos].start; + } + + return desc_pos; +} + +int abis_nm_select_newest_sw(const struct abis_nm_sw_descr *sw_descr, + const size_t size) +{ + int res = 0; + int i; + + for (i = 1; i < size; ++i) { + if (memcmp(sw_descr[res].file_ver, sw_descr[i].file_ver, + OSMO_MIN(sw_descr[i].file_ver_len, sw_descr[res].file_ver_len)) < 0) { + res = i; + } + } + + return res; +} + +static int abis_nm_rx_sw_act_req(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct e1inp_sign_link *sign_link = mb->dst; + struct tlv_parsed tp; + const uint8_t *sw_config; + int ret, sw_config_len, len; + struct abis_nm_sw_descr sw_descr[5]; + + abis_nm_debugp_foh(DNM, foh); + + DEBUGPC(DNM, "SW Activate Request: "); + + DEBUGP(DNM, "Software Activate Request, ACKing and Activating\n"); + + ret = abis_nm_sw_act_req_ack(sign_link->trx->bts, foh->obj_class, + foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, + foh->obj_inst.ts_nr, 0, + foh->data, oh->length-sizeof(*foh)); + if (ret != 0) { + LOGP(DNM, LOGL_ERROR, + "Sending SW ActReq ACK failed: %d\n", ret); + return ret; + } + + abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); + sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG); + sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG); + if (!TLVP_PRESENT(&tp, NM_ATT_SW_CONFIG)) { + LOGP(DNM, LOGL_ERROR, + "SW config not found! Can't continue.\n"); + return -EINVAL; + } else { + DEBUGP(DNM, "Found SW config: %s\n", osmo_hexdump(sw_config, sw_config_len)); + } + + /* Parse up to two sw descriptions from the data */ + len = abis_nm_parse_sw_config(sw_config, sw_config_len, + &sw_descr[0], ARRAY_SIZE(sw_descr)); + if (len <= 0) { + LOGP(DNM, LOGL_ERROR, "Failed to parse SW Config.\n"); + return -EINVAL; + } + + ret = abis_nm_select_newest_sw(&sw_descr[0], len); + DEBUGP(DNM, "Selected sw description %d of %d\n", ret, len); + + return ipacc_sw_activate(sign_link->trx->bts, foh->obj_class, + foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, + foh->obj_inst.ts_nr, + sw_descr[ret].start, sw_descr[ret].len); +} + +/* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */ +static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct e1inp_sign_link *sign_link = mb->dst; + struct tlv_parsed tp; + uint8_t adm_state; + + abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); + if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) + return -EINVAL; + + adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); + + return update_admstate(sign_link->trx->bts, foh->obj_class, &foh->obj_inst, adm_state); +} + +static int abis_nm_rx_lmt_event(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct e1inp_sign_link *sign_link = mb->dst; + struct tlv_parsed tp; + + DEBUGP(DNM, "LMT Event "); + abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); + if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) && + TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) { + uint8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION); + DEBUGPC(DNM, "LOG%s ", onoff ? "ON" : "OFF"); + } + if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) && + TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) >= 1) { + uint8_t level = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV); + DEBUGPC(DNM, "Level=%u ", level); + } + if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_NAME) && + TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_NAME) >= 1) { + char *name = (char *) TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_NAME); + DEBUGPC(DNM, "Username=%s ", name); + } + DEBUGPC(DNM, "\n"); + /* FIXME: parse LMT LOGON TIME */ + return 0; +} + +void abis_nm_queue_send_next(struct gsm_bts *bts) +{ + int wait = 0; + struct msgb *msg; + /* the queue is empty */ + while (!llist_empty(&bts->abis_queue)) { + msg = msgb_dequeue(&bts->abis_queue); + wait = OBSC_NM_W_ACK_CB(msg); + _abis_nm_sendmsg(msg); + + if (wait) + break; + } + + bts->abis_nm_pend = wait; +} + +/* Receive a OML NM Message from BTS */ +static int abis_nm_rcvmsg_fom(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct e1inp_sign_link *sign_link = mb->dst; + uint8_t mt = foh->msg_type; + int ret = 0; + + /* check for unsolicited message */ + if (is_report(mt)) + return abis_nm_rcvmsg_report(mb); + + if (is_in_arr(mt, abis_nm_sw_load_msgs, ARRAY_SIZE(abis_nm_sw_load_msgs))) + return abis_nm_rcvmsg_sw(mb); + + if (is_in_arr(mt, abis_nm_nacks, ARRAY_SIZE(abis_nm_nacks))) { + struct nm_nack_signal_data nack_data; + struct tlv_parsed tp; + + abis_nm_debugp_foh(DNM, foh); + + DEBUGPC(DNM, "%s NACK ", abis_nm_nack_name(mt)); + + abis_nm_tlv_parse(&tp, sign_link->trx->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 = sign_link->trx->bts; + osmo_signal_dispatch(SS_NM, S_NM_NACK, &nack_data); + abis_nm_queue_send_next(sign_link->trx->bts); + return 0; + } +#if 0 + /* check if last message is to be acked */ + if (is_ack_nack(nmh->last_msgtype)) { + if (mt == MT_ACK(nmh->last_msgtype)) { + DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type); + /* we got our ACK, continue sending the next msg */ + } else if (mt == MT_NACK(nmh->last_msgtype)) { + /* we got a NACK, signal this to the caller */ + DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type); + /* FIXME: somehow signal this to the caller */ + } else { + /* really strange things happen */ + return -EINVAL; + } + } +#endif + + switch (mt) { + case NM_MT_CHG_ADM_STATE_ACK: + ret = abis_nm_rx_chg_adm_state_ack(mb); + break; + case NM_MT_SW_ACT_REQ: + ret = abis_nm_rx_sw_act_req(mb); + break; + case NM_MT_BS11_LMT_SESSION: + ret = abis_nm_rx_lmt_event(mb); + break; + case NM_MT_CONN_MDROP_LINK_ACK: + DEBUGP(DNM, "CONN MDROP LINK ACK\n"); + break; + case NM_MT_IPACC_RESTART_ACK: + osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_ACK, NULL); + break; + case NM_MT_IPACC_RESTART_NACK: + osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_NACK, NULL); + break; + case NM_MT_SET_BTS_ATTR_ACK: + break; + } + + abis_nm_queue_send_next(sign_link->trx->bts); + return ret; +} + +static int abis_nm_rx_ipacc(struct msgb *mb); + +static int abis_nm_rcvmsg_manuf(struct msgb *mb) +{ + int rc; + struct e1inp_sign_link *sign_link = mb->dst; + int bts_type = sign_link->trx->bts->type; + + switch (bts_type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_OSMO_SYSMO: + rc = abis_nm_rx_ipacc(mb); + abis_nm_queue_send_next(sign_link->trx->bts); + break; + default: + LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this " + "BTS type (%u)\n", bts_type); + rc = 0; + break; + } + + return rc; +} + +/* High-Level API */ +/* Entry-point where L2 OML from BTS enters the NM code */ +int abis_nm_rcvmsg(struct msgb *msg) +{ + struct abis_om_hdr *oh = msgb_l2(msg); + int rc = 0; + + /* Various consistency checks */ + if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { + LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", + oh->placement); + if (oh->placement != ABIS_OM_PLACEMENT_FIRST) { + rc = -EINVAL; + goto err; + } + } + if (oh->sequence != 0) { + LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", + oh->sequence); + rc = -EINVAL; + goto err; + } +#if 0 + unsigned int l2_len = msg->tail - (uint8_t *)msgb_l2(msg); + unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr); + if (oh->length + hlen > l2_len) { + LOGP(DNM, LOGL_ERROR, "ABIS OML truncated message (%u > %u)\n", + oh->length + sizeof(*oh), l2_len); + return -EINVAL; + } + if (oh->length + hlen < l2_len) + LOGP(DNM, LOGL_ERROR, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len); +#endif + msg->l3h = (unsigned char *)oh + sizeof(*oh); + + switch (oh->mdisc) { + case ABIS_OM_MDISC_FOM: + rc = abis_nm_rcvmsg_fom(msg); + break; + case ABIS_OM_MDISC_MANUF: + rc = abis_nm_rcvmsg_manuf(msg); + break; + case ABIS_OM_MDISC_MMI: + case ABIS_OM_MDISC_TRAU: + LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n", + oh->mdisc); + break; + default: + LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n", + oh->mdisc); + rc = -EINVAL; + break; + } +err: + msgb_free(msg); + return rc; +} + +#if 0 +/* initialized all resources */ +struct abis_nm_h *abis_nm_init(struct abis_nm_cfg *cfg) +{ + struct abis_nm_h *nmh; + + nmh = malloc(sizeof(*nmh)); + if (!nmh) + return NULL; + + nmh->cfg = cfg; + + return nmh; +} + +/* free all resources */ +void abis_nm_fini(struct abis_nm_h *nmh) +{ + free(nmh); +} +#endif + +/* Here we are trying to define a high-level API that can be used by + * the actual BSC implementation. However, the architecture is currently + * still under design. Ideally the calls to this API would be synchronous, + * while the underlying stack behind the APi runs in a traditional select + * based state machine. + */ + +/* 6.2 Software Load: */ +enum sw_state { + SW_STATE_NONE, + SW_STATE_WAIT_INITACK, + SW_STATE_WAIT_SEGACK, + SW_STATE_WAIT_ENDACK, + SW_STATE_WAIT_ACTACK, + SW_STATE_ERROR, +}; + +struct abis_nm_sw { + struct gsm_bts *bts; + int trx_nr; + gsm_cbfn *cbfn; + void *cb_data; + int forced; + + /* this will become part of the SW LOAD INITIATE */ + uint8_t obj_class; + uint8_t obj_instance[3]; + + uint8_t file_id[255]; + uint8_t file_id_len; + + uint8_t file_version[255]; + uint8_t file_version_len; + + uint8_t window_size; + uint8_t seg_in_window; + + int fd; + FILE *stream; + enum sw_state state; + int last_seg; +}; + +static struct abis_nm_sw g_sw; + +static void sw_add_file_id_and_ver(struct abis_nm_sw *sw, struct msgb *msg) +{ + if (sw->bts->type == GSM_BTS_TYPE_NANOBTS) { + msgb_v_put(msg, NM_ATT_SW_DESCR); + msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); + msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, + sw->file_version); + } else if (sw->bts->type == GSM_BTS_TYPE_BS11) { + msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); + msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, + sw->file_version); + } else { + LOGP(DNM, LOGL_ERROR, "Please implement this for the BTS.\n"); + } +} + +/* 6.2.1 / 8.3.1: Load Data Initiate */ +static int sw_load_init(struct abis_nm_sw *sw) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t len = 3*2 + sw->file_id_len + sw->file_version_len; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class, + sw->obj_instance[0], sw->obj_instance[1], + sw->obj_instance[2]); + + sw_add_file_id_and_ver(sw, msg); + msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size); + + return abis_nm_sendmsg(sw->bts, msg); +} + +static int is_last_line(FILE *stream) +{ + char next_seg_buf[256]; + long pos; + + /* check if we're sending the last line */ + pos = ftell(stream); + if (!fgets(next_seg_buf, sizeof(next_seg_buf)-2, stream)) { + fseek(stream, pos, SEEK_SET); + return 1; + } + + fseek(stream, pos, SEEK_SET); + return 0; +} + +/* 6.2.2 / 8.3.2 Load Data Segment */ +static int sw_load_segment(struct abis_nm_sw *sw) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + char seg_buf[256]; + char *line_buf = seg_buf+2; + unsigned char *tlv; + int len; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + + switch (sw->bts->type) { + case GSM_BTS_TYPE_BS11: + if (fgets(line_buf, sizeof(seg_buf)-2, sw->stream) == NULL) { + perror("fgets reading segment"); + return -EINVAL; + } + seg_buf[0] = 0x00; + + /* check if we're sending the last line */ + sw->last_seg = is_last_line(sw->stream); + if (sw->last_seg) + seg_buf[1] = 0; + else + seg_buf[1] = 1 + sw->seg_in_window++; + + len = strlen(line_buf) + 2; + tlv = msgb_put(msg, TLV_GROSS_LEN(len)); + tlv_put(tlv, NM_ATT_BS11_FILE_DATA, len, (uint8_t *)seg_buf); + /* BS11 wants CR + LF in excess of the TLV length !?! */ + tlv[1] -= 2; + + /* we only now know the exact length for the OM hdr */ + len = strlen(line_buf)+2; + break; + case GSM_BTS_TYPE_NANOBTS: { + osmo_static_assert(sizeof(seg_buf) >= IPACC_SEGMENT_SIZE, buffer_big_enough); + len = read(sw->fd, &seg_buf, IPACC_SEGMENT_SIZE); + if (len < 0) { + perror("read failed"); + return -EINVAL; + } + + if (len != IPACC_SEGMENT_SIZE) + sw->last_seg = 1; + + ++sw->seg_in_window; + msgb_tl16v_put(msg, NM_ATT_IPACC_FILE_DATA, len, (const uint8_t *) seg_buf); + len += 3; + break; + } + default: + LOGP(DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n"); + /* FIXME: Other BTS types */ + return -1; + } + + fill_om_fom_hdr(oh, len, NM_MT_LOAD_SEG, sw->obj_class, + sw->obj_instance[0], sw->obj_instance[1], + sw->obj_instance[2]); + + return abis_nm_sendmsg_direct(sw->bts, msg); +} + +/* 6.2.4 / 8.3.4 Load Data End */ +static int sw_load_end(struct abis_nm_sw *sw) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_LOAD_END, sw->obj_class, + sw->obj_instance[0], sw->obj_instance[1], + sw->obj_instance[2]); + + sw_add_file_id_and_ver(sw, msg); + return abis_nm_sendmsg(sw->bts, msg); +} + +/* Activate the specified software into the BTS */ +static int sw_activate(struct abis_nm_sw *sw) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, sw->obj_class, + sw->obj_instance[0], sw->obj_instance[1], + sw->obj_instance[2]); + + /* FIXME: this is BS11 specific format */ + msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); + msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, + sw->file_version); + + return abis_nm_sendmsg(sw->bts, msg); +} + +struct sdp_firmware { + char magic[4]; + char more_magic[4]; + unsigned int header_length; + unsigned int file_length; +} __attribute__ ((packed)); + +static int parse_sdp_header(struct abis_nm_sw *sw) +{ + struct sdp_firmware firmware_header; + int rc; + struct stat stat; + + rc = read(sw->fd, &firmware_header, sizeof(firmware_header)); + if (rc != sizeof(firmware_header)) { + LOGP(DNM, LOGL_ERROR, "Could not read SDP file header.\n"); + return -1; + } + + if (strncmp(firmware_header.magic, " SDP", 4) != 0) { + LOGP(DNM, LOGL_ERROR, "The magic number1 is wrong.\n"); + return -1; + } + + if (firmware_header.more_magic[0] != 0x10 || + firmware_header.more_magic[1] != 0x02 || + firmware_header.more_magic[2] != 0x00 || + firmware_header.more_magic[3] != 0x00) { + LOGP(DNM, LOGL_ERROR, "The more magic number is wrong.\n"); + return -1; + } + + + if (fstat(sw->fd, &stat) == -1) { + LOGP(DNM, LOGL_ERROR, "Could not stat the file.\n"); + return -1; + } + + if (ntohl(firmware_header.file_length) != stat.st_size) { + LOGP(DNM, LOGL_ERROR, "The filesizes do not match.\n"); + return -1; + } + + /* go back to the start as we checked the whole filesize.. */ + lseek(sw->fd, 0l, SEEK_SET); + LOGP(DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood.\n" + "There might be checksums in the file that are not\n" + "verified and incomplete firmware might be flashed.\n" + "There is absolutely no WARRANTY that flashing will\n" + "work.\n"); + return 0; +} + +static int sw_open_file(struct abis_nm_sw *sw, const char *fname) +{ + char file_id[12+1]; + char file_version[80+1]; + int rc; + + sw->fd = open(fname, O_RDONLY); + if (sw->fd < 0) + return sw->fd; + + switch (sw->bts->type) { + case GSM_BTS_TYPE_BS11: + sw->stream = fdopen(sw->fd, "r"); + if (!sw->stream) { + perror("fdopen"); + return -1; + } + /* read first line and parse file ID and VERSION */ + rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n", + file_id, file_version); + if (rc != 2) { + perror("parsing header line of software file"); + return -1; + } + strcpy((char *)sw->file_id, file_id); + sw->file_id_len = strlen(file_id); + strcpy((char *)sw->file_version, file_version); + sw->file_version_len = strlen(file_version); + /* rewind to start of file */ + rewind(sw->stream); + break; + case GSM_BTS_TYPE_NANOBTS: + /* TODO: extract that from the filename or content */ + rc = parse_sdp_header(sw); + if (rc < 0) { + fprintf(stderr, "Could not parse the ipaccess SDP header\n"); + return -1; + } + + strcpy((char *)sw->file_id, "id"); + sw->file_id_len = 3; + strcpy((char *)sw->file_version, "version"); + sw->file_version_len = 8; + break; + default: + /* We don't know how to treat them yet */ + close(sw->fd); + return -EINVAL; + } + + return 0; +} + +static void sw_close_file(struct abis_nm_sw *sw) +{ + switch (sw->bts->type) { + case GSM_BTS_TYPE_BS11: + fclose(sw->stream); + break; + default: + close(sw->fd); + break; + } +} + +/* Fill the window */ +static int sw_fill_window(struct abis_nm_sw *sw) +{ + int rc; + + while (sw->seg_in_window < sw->window_size) { + rc = sw_load_segment(sw); + if (rc < 0) + return rc; + if (sw->last_seg) + break; + } + return 0; +} + +/* callback function from abis_nm_rcvmsg() handler */ +static int abis_nm_rcvmsg_sw(struct msgb *mb) +{ + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct e1inp_sign_link *sign_link = mb->dst; + int rc = -1; + struct abis_nm_sw *sw = &g_sw; + enum sw_state old_state = sw->state; + + //DEBUGP(DNM, "state %u, NM MT 0x%02x\n", sw->state, foh->msg_type); + + switch (sw->state) { + case SW_STATE_WAIT_INITACK: + switch (foh->msg_type) { + case NM_MT_LOAD_INIT_ACK: + /* fill window with segments */ + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_INIT_ACK, mb, + sw->cb_data, NULL); + rc = sw_fill_window(sw); + sw->state = SW_STATE_WAIT_SEGACK; + abis_nm_queue_send_next(sign_link->trx->bts); + break; + case NM_MT_LOAD_INIT_NACK: + if (sw->forced) { + DEBUGP(DNM, "FORCED: Ignoring Software Load " + "Init NACK\n"); + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_INIT_ACK, mb, + sw->cb_data, NULL); + rc = sw_fill_window(sw); + sw->state = SW_STATE_WAIT_SEGACK; + } else { + DEBUGP(DNM, "Software Load Init NACK\n"); + /* FIXME: cause */ + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_INIT_NACK, mb, + sw->cb_data, NULL); + sw->state = SW_STATE_ERROR; + } + abis_nm_queue_send_next(sign_link->trx->bts); + break; + } + break; + case SW_STATE_WAIT_SEGACK: + switch (foh->msg_type) { + case NM_MT_LOAD_SEG_ACK: + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_SEG_ACK, mb, + sw->cb_data, NULL); + sw->seg_in_window = 0; + if (!sw->last_seg) { + /* fill window with more segments */ + rc = sw_fill_window(sw); + sw->state = SW_STATE_WAIT_SEGACK; + } else { + /* end the transfer */ + sw->state = SW_STATE_WAIT_ENDACK; + rc = sw_load_end(sw); + } + abis_nm_queue_send_next(sign_link->trx->bts); + break; + case NM_MT_LOAD_ABORT: + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_ABORT, mb, + sw->cb_data, NULL); + break; + } + break; + case SW_STATE_WAIT_ENDACK: + switch (foh->msg_type) { + case NM_MT_LOAD_END_ACK: + sw_close_file(sw); + DEBUGP(DNM, "Software Load End (BTS %u)\n", + sw->bts->nr); + sw->state = SW_STATE_NONE; + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_END_ACK, mb, + sw->cb_data, NULL); + rc = 0; + abis_nm_queue_send_next(sign_link->trx->bts); + break; + case NM_MT_LOAD_END_NACK: + if (sw->forced) { + DEBUGP(DNM, "FORCED: Ignoring Software Load" + "End NACK\n"); + sw->state = SW_STATE_NONE; + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_END_ACK, mb, + sw->cb_data, NULL); + } else { + DEBUGP(DNM, "Software Load End NACK\n"); + /* FIXME: cause */ + sw->state = SW_STATE_ERROR; + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_LOAD_END_NACK, mb, + sw->cb_data, NULL); + } + abis_nm_queue_send_next(sign_link->trx->bts); + break; + } + case SW_STATE_WAIT_ACTACK: + switch (foh->msg_type) { + case NM_MT_ACTIVATE_SW_ACK: + /* we're done */ + DEBUGP(DNM, "Activate Software DONE!\n"); + sw->state = SW_STATE_NONE; + rc = 0; + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_ACTIVATE_SW_ACK, mb, + sw->cb_data, NULL); + abis_nm_queue_send_next(sign_link->trx->bts); + break; + case NM_MT_ACTIVATE_SW_NACK: + DEBUGP(DNM, "Activate Software NACK\n"); + /* FIXME: cause */ + sw->state = SW_STATE_ERROR; + if (sw->cbfn) + sw->cbfn(GSM_HOOK_NM_SWLOAD, + NM_MT_ACTIVATE_SW_NACK, mb, + sw->cb_data, NULL); + abis_nm_queue_send_next(sign_link->trx->bts); + break; + } + case SW_STATE_NONE: + switch (foh->msg_type) { + case NM_MT_ACTIVATE_SW_ACK: + rc = 0; + break; + } + break; + case SW_STATE_ERROR: + break; + } + + if (rc) + DEBUGP(DNM, "unexpected NM MT 0x%02x in state %u -> %u\n", + foh->msg_type, old_state, sw->state); + + return rc; +} + +/* Load the specified software into the BTS */ +int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, + uint8_t win_size, int forced, + gsm_cbfn *cbfn, void *cb_data) +{ + struct abis_nm_sw *sw = &g_sw; + int rc; + + DEBUGP(DNM, "Software Load (BTS %u, File \"%s\")\n", + bts->nr, fname); + + if (sw->state != SW_STATE_NONE) + return -EBUSY; + + sw->bts = bts; + sw->trx_nr = trx_nr; + + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + sw->obj_class = NM_OC_SITE_MANAGER; + sw->obj_instance[0] = 0xff; + sw->obj_instance[1] = 0xff; + sw->obj_instance[2] = 0xff; + break; + case GSM_BTS_TYPE_NANOBTS: + sw->obj_class = NM_OC_BASEB_TRANSC; + sw->obj_instance[0] = sw->bts->nr; + sw->obj_instance[1] = sw->trx_nr; + sw->obj_instance[2] = 0xff; + break; + case GSM_BTS_TYPE_UNKNOWN: + default: + LOGPC(DNM, LOGL_ERROR, "Software Load not properly implemented.\n"); + return -1; + break; + } + sw->window_size = win_size; + sw->state = SW_STATE_WAIT_INITACK; + sw->cbfn = cbfn; + sw->cb_data = cb_data; + sw->forced = forced; + + rc = sw_open_file(sw, fname); + if (rc < 0) { + sw->state = SW_STATE_NONE; + return rc; + } + + return sw_load_init(sw); +} + +int abis_nm_software_load_status(struct gsm_bts *bts) +{ + struct abis_nm_sw *sw = &g_sw; + struct stat st; + int rc, percent; + + rc = fstat(sw->fd, &st); + if (rc < 0) { + perror("ERROR during stat"); + return rc; + } + + if (sw->stream) + percent = (ftell(sw->stream) * 100) / st.st_size; + else + percent = (lseek(sw->fd, 0, SEEK_CUR) * 100) / st.st_size; + return percent; +} + +/* Activate the specified software into the BTS */ +int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, + gsm_cbfn *cbfn, void *cb_data) +{ + struct abis_nm_sw *sw = &g_sw; + int rc; + + DEBUGP(DNM, "Activating Software (BTS %u, File \"%s\")\n", + bts->nr, fname); + + if (sw->state != SW_STATE_NONE) + return -EBUSY; + + sw->bts = bts; + sw->obj_class = NM_OC_SITE_MANAGER; + sw->obj_instance[0] = 0xff; + sw->obj_instance[1] = 0xff; + sw->obj_instance[2] = 0xff; + sw->state = SW_STATE_WAIT_ACTACK; + sw->cbfn = cbfn; + sw->cb_data = cb_data; + + /* Open the file in order to fill some sw struct members */ + rc = sw_open_file(sw, fname); + if (rc < 0) { + sw->state = SW_STATE_NONE; + return rc; + } + sw_close_file(sw); + + return sw_activate(sw); +} + +static void fill_nm_channel(struct abis_nm_channel *ch, uint8_t bts_port, + uint8_t ts_nr, uint8_t subslot_nr) +{ + ch->attrib = NM_ATT_ABIS_CHANNEL; + ch->bts_port = bts_port; + ch->timeslot = ts_nr; + ch->subslot = subslot_nr; +} + +int abis_nm_establish_tei(struct gsm_bts *bts, uint8_t trx_nr, + uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, + uint8_t tei) +{ + struct abis_om_hdr *oh; + struct abis_nm_channel *ch; + uint8_t len = sizeof(*ch) + 2; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_ESTABLISH_TEI, NM_OC_RADIO_CARRIER, + bts->bts_nr, trx_nr, 0xff); + + msgb_tv_put(msg, NM_ATT_TEI, tei); + + ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); + fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); + + return abis_nm_sendmsg(bts, msg); +} + +/* connect signalling of one (BTS,TRX) to a particular timeslot on the E1 */ +int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, + uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot) +{ + struct gsm_bts *bts = trx->bts; + struct abis_om_hdr *oh; + struct abis_nm_channel *ch; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_SIGN, + NM_OC_RADIO_CARRIER, bts->bts_nr, trx->nr, 0xff); + + ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); + fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); + + return abis_nm_sendmsg(bts, msg); +} + +#if 0 +int abis_nm_disc_terr_sign(struct abis_nm_h *h, struct abis_om_obj_inst *inst, + struct abis_nm_abis_channel *chan) +{ +} +#endif + +int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, + uint8_t e1_port, uint8_t e1_timeslot, + uint8_t e1_subslot) +{ + struct gsm_bts *bts = ts->trx->bts; + struct abis_om_hdr *oh; + struct abis_nm_channel *ch; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_TRAF, + NM_OC_CHANNEL, bts->bts_nr, ts->trx->nr, ts->nr); + + ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); + fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); + + DEBUGP(DNM, "CONNECT TERR TRAF Um=%s E1=(%u,%u,%u)\n", + gsm_ts_name(ts), + e1_port, e1_timeslot, e1_subslot); + + return abis_nm_sendmsg(bts, msg); +} + +#if 0 +int abis_nm_disc_terr_traf(struct abis_nm_h *h, struct abis_om_obj_inst *inst, + struct abis_nm_abis_channel *chan, + uint8_t subchan) +{ +} +#endif + +/* Chapter 8.11.1 */ +int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, + uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, + uint8_t *attr, uint8_t attr_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + DEBUGP(DNM, "Get Attr (bts=%d)\n", bts->nr); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, attr_len, NM_MT_GET_ATTR, obj_class, + bts_nr, trx_nr, ts_nr); + msgb_tl16v_put(msg, NM_ATT_LIST_REQ_ATTR, attr_len, attr); + + return abis_nm_sendmsg(bts, msg); +} + +/* Chapter 8.6.1 */ +int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t *cur; + + DEBUGP(DNM, "Set BTS Attr (bts=%d)\n", bts->nr); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, attr_len, NM_MT_SET_BTS_ATTR, NM_OC_BTS, bts->bts_nr, 0xff, 0xff); + cur = msgb_put(msg, attr_len); + memcpy(cur, attr, attr_len); + + return abis_nm_sendmsg(bts, msg); +} + +/* Chapter 8.6.2 */ +int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t *cur; + + DEBUGP(DNM, "Set TRX Attr (bts=%d,trx=%d)\n", trx->bts->nr, trx->nr); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, attr_len, NM_MT_SET_RADIO_ATTR, NM_OC_RADIO_CARRIER, + trx->bts->bts_nr, trx->nr, 0xff); + cur = msgb_put(msg, attr_len); + memcpy(cur, attr, attr_len); + + return abis_nm_sendmsg(trx->bts, msg); +} + +static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb, + const char **reason) +{ + int i; + + *reason = "Reason unknown"; + + /* As it turns out, the BS-11 has some very peculiar restrictions + * on the channel combinations it allows */ + switch (ts->trx->bts->type) { + case GSM_BTS_TYPE_BS11: + switch (chan_comb) { + case NM_CHANC_TCHHalf: + case NM_CHANC_TCHHalf2: + /* not supported */ + *reason = "TCH/H is not supported."; + return -EINVAL; + case NM_CHANC_SDCCH: + /* only one SDCCH/8 per TRX */ + for (i = 0; i < TRX_NR_TS; i++) { + if (i == ts->nr) + continue; + if (ts->trx->ts[i].nm_chan_comb == + NM_CHANC_SDCCH) { + *reason = "Only one SDCCH/8 per TRX allowed."; + return -EINVAL; + } + } + /* not allowed for TS0 of BCCH-TRX */ + if (ts->trx == ts->trx->bts->c0 && + ts->nr == 0) { + *reason = "SDCCH/8 must be on TS0."; + return -EINVAL; + } + + /* not on the same TRX that has a BCCH+SDCCH4 + * combination */ + if (ts->trx != ts->trx->bts->c0 && + (ts->trx->ts[0].nm_chan_comb == 5 || + ts->trx->ts[0].nm_chan_comb == 8)) { + *reason = "SDCCH/8 and BCCH must be on the same TRX."; + return -EINVAL; + } + break; + case NM_CHANC_mainBCCH: + case NM_CHANC_BCCHComb: + /* allowed only for TS0 of C0 */ + if (ts->trx != ts->trx->bts->c0 || ts->nr != 0) { + *reason = "Main BCCH must be on TS0."; + return -EINVAL; + } + break; + case NM_CHANC_BCCH: + /* allowed only for TS 2/4/6 of C0 */ + if (ts->trx != ts->trx->bts->c0) { + *reason = "BCCH must be on C0."; + return -EINVAL; + } + if (ts->nr != 2 && ts->nr != 4 && ts->nr != 6) { + *reason = "BCCH must be on TS 2/4/6."; + return -EINVAL; + } + break; + case 8: /* this is not like 08.58, but in fact + * FCCH+SCH+BCCH+CCCH+SDCCH/4+SACCH/C4+CBCH */ + /* FIXME: only one CBCH allowed per cell */ + break; + } + break; + case GSM_BTS_TYPE_NANOBTS: + switch (ts->nr) { + case 0: + if (ts->trx->nr == 0) { + /* only on TRX0 */ + switch (chan_comb) { + case NM_CHANC_BCCH: + case NM_CHANC_mainBCCH: + case NM_CHANC_BCCHComb: + return 0; + break; + default: + *reason = "TS0 of TRX0 must carry a BCCH."; + return -EINVAL; + } + } else { + switch (chan_comb) { + case NM_CHANC_TCHFull: + case NM_CHANC_TCHHalf: + case NM_CHANC_IPAC_TCHFull_TCHHalf: + return 0; + default: + *reason = "TS0 must carry a TCH/F or TCH/H."; + return -EINVAL; + } + } + break; + case 1: + if (ts->trx->nr == 0) { + switch (chan_comb) { + case NM_CHANC_SDCCH_CBCH: + if (ts->trx->ts[0].nm_chan_comb == + NM_CHANC_mainBCCH) + return 0; + *reason = "TS0 must be the main BCCH for CBCH."; + return -EINVAL; + case NM_CHANC_SDCCH: + case NM_CHANC_TCHFull: + case NM_CHANC_TCHHalf: + case NM_CHANC_IPAC_TCHFull_TCHHalf: + case NM_CHANC_IPAC_TCHFull_PDCH: + return 0; + default: + *reason = "TS1 must carry a CBCH, SDCCH or TCH."; + return -EINVAL; + } + } else { + switch (chan_comb) { + case NM_CHANC_SDCCH: + case NM_CHANC_TCHFull: + case NM_CHANC_TCHHalf: + case NM_CHANC_IPAC_TCHFull_TCHHalf: + return 0; + default: + *reason = "TS1 must carry a SDCCH or TCH."; + return -EINVAL; + } + } + break; + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + switch (chan_comb) { + case NM_CHANC_TCHFull: + case NM_CHANC_TCHHalf: + case NM_CHANC_IPAC_TCHFull_TCHHalf: + return 0; + case NM_CHANC_IPAC_PDCH: + case NM_CHANC_IPAC_TCHFull_PDCH: + if (ts->trx->nr == 0) + return 0; + else { + *reason = "PDCH must be on TRX0."; + return -EINVAL; + } + } + break; + } + *reason = "Unknown combination"; + return -EINVAL; + case GSM_BTS_TYPE_OSMO_SYSMO: + /* no known restrictions */ + return 0; + default: + /* unknown BTS type */ + return 0; + } + return 0; +} + +/* Chapter 8.6.3 */ +int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb) +{ + struct gsm_bts *bts = ts->trx->bts; + struct abis_om_hdr *oh; + uint8_t zero = 0x00; + struct msgb *msg = nm_msgb_alloc(); + uint8_t len = 2 + 2; + const char *reason = NULL; + + if (bts->type == GSM_BTS_TYPE_BS11) + len += 4 + 2 + 2 + 3; + + DEBUGP(DNM, "Set Chan Attr %s\n", gsm_ts_name(ts)); + if (verify_chan_comb(ts, chan_comb, &reason) < 0) { + msgb_free(msg); + LOGP(DNM, LOGL_ERROR, + "Invalid Channel Combination %d on %s. Reason: %s\n", + chan_comb, gsm_ts_name(ts), reason); + return -EINVAL; + } + ts->nm_chan_comb = chan_comb; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_SET_CHAN_ATTR, + NM_OC_CHANNEL, bts->bts_nr, + ts->trx->nr, ts->nr); + msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb); + if (ts->hopping.enabled) { + unsigned int i; + uint8_t *len; + + msgb_tv_put(msg, NM_ATT_HSN, ts->hopping.hsn); + msgb_tv_put(msg, NM_ATT_MAIO, ts->hopping.maio); + + /* build the ARFCN list */ + msgb_put_u8(msg, NM_ATT_ARFCN_LIST); + len = msgb_put(msg, 1); + *len = 0; + for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { + if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) { + msgb_put_u16(msg, i); + /* At least BS-11 wants a TLV16 here */ + if (bts->type == GSM_BTS_TYPE_BS11) + *len += 1; + else + *len += sizeof(uint16_t); + } + } + } + if (ts->tsc == -1) + msgb_tv_put(msg, NM_ATT_TSC, bts->tsc); /* training sequence */ + else + msgb_tv_put(msg, NM_ATT_TSC, ts->tsc); /* training sequence */ + if (bts->type == GSM_BTS_TYPE_BS11) + msgb_tlv_put(msg, 0x59, 1, &zero); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_sw_act_req_ack(struct gsm_bts *bts, 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 = (struct abis_om_fom_hdr *) oh->data; + fill_om_fom_hdr(oh, 0, NM_MT_OPSTART, obj_class, i0, i1, i2); + + abis_nm_debugp_foh(DNM, foh); + DEBUGPC(DNM, "Sending OPSTART\n"); + + return abis_nm_sendmsg(bts, msg); +} + +/* Chapter 8.8.5 */ +int abis_nm_chg_adm_state(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, + uint8_t i1, uint8_t i2, enum abis_nm_adm_state adm_state) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2, NM_MT_CHG_ADM_STATE, obj_class, i0, i1, i2); + msgb_tv_put(msg, NM_ATT_ADM_STATE, adm_state); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_conn_mdrop_link(struct gsm_bts *bts, uint8_t e1_port0, uint8_t ts0, + uint8_t e1_port1, uint8_t ts1) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t *attr; + + DEBUGP(DNM, "CONNECT MDROP LINK E1=(%u,%u) -> E1=(%u, %u)\n", + e1_port0, ts0, e1_port1, ts1); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 6, NM_MT_CONN_MDROP_LINK, + NM_OC_SITE_MANAGER, 0x00, 0x00, 0x00); + + attr = msgb_put(msg, 3); + attr[0] = NM_ATT_MDROP_LINK; + attr[1] = e1_port0; + attr[2] = ts0; + + attr = msgb_put(msg, 3); + attr[0] = NM_ATT_MDROP_NEXT; + attr[1] = e1_port1; + attr[2] = ts1; + + return abis_nm_sendmsg(bts, msg); +} + +/* Chapter 8.7.1 */ +int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class, + uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, + uint8_t test_nr, uint8_t auton_report, struct msgb *msg) +{ + struct abis_om_hdr *oh; + + DEBUGP(DNM, "PEFORM TEST %s\n", abis_nm_test_name(test_nr)); + + if (!msg) + msg = nm_msgb_alloc(); + + msgb_tv_push(msg, NM_ATT_AUTON_REPORT, auton_report); + msgb_tv_push(msg, NM_ATT_TEST_NO, test_nr); + oh = (struct abis_om_hdr *) msgb_push(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, msgb_l3len(msg), NM_MT_PERF_TEST, + obj_class, bts_nr, trx_nr, ts_nr); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_event_reports(struct gsm_bts *bts, int on) +{ + if (on == 0) + return __simple_cmd(bts, NM_MT_STOP_EVENT_REP); + else + return __simple_cmd(bts, NM_MT_REST_EVENT_REP); +} + +/* Siemens (or BS-11) specific commands */ + +int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect) +{ + if (reconnect == 0) + return __simple_cmd(bts, NM_MT_BS11_DISCONNECT); + else + return __simple_cmd(bts, NM_MT_BS11_RECONNECT); +} + +int abis_nm_bs11_restart(struct gsm_bts *bts) +{ + return __simple_cmd(bts, NM_MT_BS11_RESTART); +} + + +struct bs11_date_time { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t min; + uint8_t sec; +} __attribute__((packed)); + + +void get_bs11_date_time(struct bs11_date_time *aet) +{ + time_t t; + struct tm *tm; + + t = time(NULL); + tm = localtime(&t); + aet->sec = tm->tm_sec; + aet->min = tm->tm_min; + aet->hour = tm->tm_hour; + aet->day = tm->tm_mday; + aet->month = tm->tm_mon; + aet->year = htons(1900 + tm->tm_year); +} + +int abis_nm_bs11_reset_resource(struct gsm_bts *bts) +{ + return __simple_cmd(bts, NM_MT_BS11_RESET_RESOURCE); +} + +int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin) +{ + if (begin) + return __simple_cmd(bts, NM_MT_BS11_BEGIN_DB_TX); + else + return __simple_cmd(bts, NM_MT_BS11_END_DB_TX); +} + +int abis_nm_bs11_create_object(struct gsm_bts *bts, + enum abis_bs11_objtype type, uint8_t idx, + uint8_t attr_len, const uint8_t *attr) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t *cur; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, attr_len, NM_MT_BS11_CREATE_OBJ, + NM_OC_BS11, type, 0, idx); + cur = msgb_put(msg, attr_len); + memcpy(cur, attr, attr_len); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_delete_object(struct gsm_bts *bts, + enum abis_bs11_objtype type, uint8_t idx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, + NM_OC_BS11, type, 0, idx); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, uint8_t idx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t zero = 0x00; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 3, NM_MT_BS11_CREATE_OBJ, + NM_OC_BS11_ENVABTSE, 0, idx, 0xff); + msgb_tlv_put(msg, 0x99, 1, &zero); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_create_bport(struct gsm_bts *bts, uint8_t idx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 0, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11_BPORT, + idx, 0xff, 0xff); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_delete_bport(struct gsm_bts *bts, uint8_t idx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, NM_OC_BS11_BPORT, + idx, 0xff, 0xff); + + return abis_nm_sendmsg(bts, msg); +} + +static const uint8_t sm_attr[] = { NM_ATT_TEI, NM_ATT_ABIS_CHANNEL }; +int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2+sizeof(sm_attr), NM_MT_GET_ATTR, NM_OC_SITE_MANAGER, + 0xff, 0xff, 0xff); + msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(sm_attr), sm_attr); + + return abis_nm_sendmsg(bts, msg); +} + +/* like abis_nm_conn_terr_traf + set_tei */ +int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, uint8_t e1_port, + uint8_t e1_timeslot, uint8_t e1_subslot, + uint8_t tei) +{ + struct abis_om_hdr *oh; + struct abis_nm_channel *ch; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, sizeof(*ch)+2, NM_MT_BS11_SET_ATTR, + NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); + + ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); + fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); + msgb_tv_put(msg, NM_ATT_TEI, tei); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, uint8_t level) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, + NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); + msgb_tlv_put(msg, NM_ATT_BS11_TXPWR, 1, &level); + + return abis_nm_sendmsg(trx->bts, msg); +} + +int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t attr = NM_ATT_BS11_TXPWR; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, + NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); + msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); + + return abis_nm_sendmsg(trx->bts, msg); +} + +int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t attr[] = { NM_ATT_BS11_PLL_MODE }; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, + NM_OC_BS11, BS11_OBJ_LI, 0x00, 0x00); + msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_get_cclk(struct gsm_bts *bts) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t attr[] = { NM_ATT_BS11_CCLK_ACCURACY, + NM_ATT_BS11_CCLK_TYPE }; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, + NM_OC_BS11, BS11_OBJ_CCLK, 0x00, 0x00); + msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); + + return abis_nm_sendmsg(bts, msg); + +} + +//static const uint8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 }; + +int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on) +{ + return abis_nm_bs11_logon(bts, 0x02, "FACTORY", on); +} + +int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on) +{ + return abis_nm_bs11_logon(bts, 0x03, "FIELD ", on); +} + +int abis_nm_bs11_logon(struct gsm_bts *bts, uint8_t level, const char *name, int on) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + struct bs11_date_time bdt; + + get_bs11_date_time(&bdt); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + if (on) { + uint8_t len = 3*2 + sizeof(bdt) + + 1 + strlen(name); + fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON, + NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); + msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME, + sizeof(bdt), (uint8_t *) &bdt); + msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV, + 1, &level); + msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME, + strlen(name), (uint8_t *)name); + } else { + fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF, + NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); + } + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password) +{ + struct abis_om_hdr *oh; + struct msgb *msg; + + if (strlen(password) != 10) + return -EINVAL; + + msg = nm_msgb_alloc(); + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2+strlen(password), NM_MT_BS11_SET_ATTR, + NM_OC_BS11, BS11_OBJ_TRX1, 0x00, 0x00); + msgb_tlv_put(msg, NM_ATT_BS11_PASSWORD, 10, (const uint8_t *)password); + + return abis_nm_sendmsg(bts, msg); +} + +/* change the BS-11 PLL Mode to either locked (E1 derived) or standalone */ +int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked) +{ + struct abis_om_hdr *oh; + struct msgb *msg; + uint8_t tlv_value; + + msg = nm_msgb_alloc(); + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, + BS11_OBJ_LI, 0x00, 0x00); + + if (locked) + tlv_value = BS11_LI_PLL_LOCKED; + else + tlv_value = BS11_LI_PLL_STANDALONE; + + msgb_tlv_put(msg, NM_ATT_BS11_PLL_MODE, 1, &tlv_value); + + return abis_nm_sendmsg(bts, msg); +} + +/* Set the calibration value of the PLL (work value/set value) + * It depends on the login which one is changed */ +int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value) +{ + struct abis_om_hdr *oh; + struct msgb *msg; + uint8_t tlv_value[2]; + + msg = nm_msgb_alloc(); + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, + BS11_OBJ_TRX1, 0x00, 0x00); + + tlv_value[0] = value>>8; + tlv_value[1] = value&0xff; + + msgb_tlv_put(msg, NM_ATT_BS11_PLL, 2, tlv_value); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_get_state(struct gsm_bts *bts) +{ + return __simple_cmd(bts, NM_MT_BS11_GET_STATE); +} + +/* BS11 SWL */ + +void *tall_fle_ctx; + +struct abis_nm_bs11_sw { + struct gsm_bts *bts; + char swl_fname[PATH_MAX]; + uint8_t win_size; + int forced; + struct llist_head file_list; + gsm_cbfn *user_cb; /* specified by the user */ +}; +static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw; + +struct file_list_entry { + struct llist_head list; + char fname[PATH_MAX]; +}; + +struct file_list_entry *fl_dequeue(struct llist_head *queue) +{ + struct llist_head *lh; + + if (llist_empty(queue)) + return NULL; + + lh = queue->next; + llist_del(lh); + + return llist_entry(lh, struct file_list_entry, list); +} + +static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw) +{ + char linebuf[255]; + struct llist_head *lh, *lh2; + FILE *swl; + int rc = 0; + + swl = fopen(bs11_sw->swl_fname, "r"); + if (!swl) + return -ENODEV; + + /* zero the stale file list, if any */ + llist_for_each_safe(lh, lh2, &bs11_sw->file_list) { + llist_del(lh); + talloc_free(lh); + } + + while (fgets(linebuf, sizeof(linebuf), swl)) { + char file_id[12+1]; + char file_version[80+1]; + struct file_list_entry *fle; + static char dir[PATH_MAX]; + + if (strlen(linebuf) < 4) + continue; + + rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version); + if (rc < 0) { + perror("ERR parsing SWL file"); + rc = -EINVAL; + goto out; + } + if (rc < 2) + continue; + + fle = talloc_zero(tall_fle_ctx, struct file_list_entry); + if (!fle) { + rc = -ENOMEM; + goto out; + } + + /* construct new filename */ + strncpy(dir, bs11_sw->swl_fname, sizeof(dir)); + strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1); + strcat(fle->fname, "/"); + strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname)); + + llist_add_tail(&fle->list, &bs11_sw->file_list); + } + +out: + fclose(swl); + return rc; +} + +/* bs11 swload specific callback, passed to abis_nm core swload */ +static int bs11_swload_cbfn(unsigned int hook, unsigned int event, + struct msgb *msg, void *data, void *param) +{ + struct abis_nm_bs11_sw *bs11_sw = data; + struct file_list_entry *fle; + int rc = 0; + + switch (event) { + case NM_MT_LOAD_END_ACK: + fle = fl_dequeue(&bs11_sw->file_list); + if (fle) { + /* start download the next file of our file list */ + rc = abis_nm_software_load(bs11_sw->bts, 0xff, fle->fname, + bs11_sw->win_size, + bs11_sw->forced, + &bs11_swload_cbfn, bs11_sw); + talloc_free(fle); + } else { + /* activate the SWL */ + rc = abis_nm_software_activate(bs11_sw->bts, + bs11_sw->swl_fname, + bs11_swload_cbfn, + bs11_sw); + } + break; + case NM_MT_LOAD_SEG_ACK: + case NM_MT_LOAD_END_NACK: + case NM_MT_LOAD_INIT_ACK: + case NM_MT_LOAD_INIT_NACK: + case NM_MT_ACTIVATE_SW_NACK: + case NM_MT_ACTIVATE_SW_ACK: + default: + /* fallthrough to the user callback */ + if (bs11_sw->user_cb) + rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL); + break; + } + + return rc; +} + +/* Siemens provides a SWL file that is a mere listing of all the other + * files that are part of a software release. We need to upload first + * the list file, and then each file that is listed in the list file */ +int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, + uint8_t win_size, int forced, gsm_cbfn *cbfn) +{ + struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw; + struct file_list_entry *fle; + int rc = 0; + + INIT_LLIST_HEAD(&bs11_sw->file_list); + bs11_sw->bts = bts; + bs11_sw->win_size = win_size; + bs11_sw->user_cb = cbfn; + bs11_sw->forced = forced; + + strncpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname)); + rc = bs11_read_swl_file(bs11_sw); + if (rc < 0) + return rc; + + /* dequeue next item in file list */ + fle = fl_dequeue(&bs11_sw->file_list); + if (!fle) + return -EINVAL; + + /* start download the next file of our file list */ + rc = abis_nm_software_load(bts, 0xff, fle->fname, win_size, forced, + bs11_swload_cbfn, bs11_sw); + talloc_free(fle); + return rc; +} + +#if 0 +static uint8_t req_attr_btse[] = { + NM_ATT_ADM_STATE, NM_ATT_BS11_LMT_LOGON_SESSION, + NM_ATT_BS11_LMT_LOGIN_TIME, NM_ATT_BS11_LMT_USER_ACC_LEV, + NM_ATT_BS11_LMT_USER_NAME, + + 0xaf, NM_ATT_BS11_RX_OFFSET, NM_ATT_BS11_VENDOR_NAME, + + NM_ATT_BS11_SW_LOAD_INTENDED, NM_ATT_BS11_SW_LOAD_SAFETY, + + NM_ATT_BS11_SW_LOAD_STORED }; + +static uint8_t req_attr_btsm[] = { + NM_ATT_ABIS_CHANNEL, NM_ATT_TEI, NM_ATT_BS11_ABIS_EXT_TIME, + NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xce, NM_ATT_FILE_ID, + NM_ATT_FILE_VERSION, NM_ATT_OPER_STATE, 0xe8, NM_ATT_BS11_ALL_TEST_CATG, + NM_ATT_SW_DESCR, NM_ATT_GET_ARI }; +#endif + +static uint8_t req_attr[] = { + NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE, + 0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO, + 0x42, NM_ATT_BS11_ESN_PCB_SERIAL, NM_ATT_BS11_PLL }; + +int abis_nm_bs11_get_serno(struct gsm_bts *bts) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + /* SiemensHW CCTRL object */ + fill_om_fom_hdr(oh, 2+sizeof(req_attr), NM_MT_GET_ATTR, NM_OC_BS11, + 0x03, 0x00, 0x00); + msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(req_attr), req_attr); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_set_ext_time(struct gsm_bts *bts) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + struct bs11_date_time aet; + + get_bs11_date_time(&aet); + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + /* SiemensHW CCTRL object */ + fill_om_fom_hdr(oh, 2+sizeof(aet), NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, + 0xff, 0xff, 0xff); + msgb_tlv_put(msg, NM_ATT_BS11_ABIS_EXT_TIME, sizeof(aet), (uint8_t *) &aet); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, uint8_t bport) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + uint8_t attr = NM_ATT_BS11_LINE_CFG; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, + NM_OC_BS11_BPORT, bport, 0xff, 0x02); + msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, uint8_t bport, enum abis_bs11_line_cfg line_cfg) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + struct bs11_date_time aet; + + get_bs11_date_time(&aet); + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 2, NM_MT_BS11_SET_ATTR, NM_OC_BS11_BPORT, + bport, 0xff, 0x02); + msgb_tv_put(msg, NM_ATT_BS11_LINE_CFG, line_cfg); + + return abis_nm_sendmsg(bts, msg); +} + +/* ip.access nanoBTS specific commands */ +static const char ipaccess_magic[] = "com.ipaccess"; + + +static int abis_nm_rx_ipacc(struct msgb *msg) +{ + struct in_addr addr; + struct abis_om_hdr *oh = msgb_l2(msg); + struct abis_om_fom_hdr *foh; + uint8_t idstrlen = oh->data[0]; + struct tlv_parsed tp; + struct ipacc_ack_signal_data signal; + struct e1inp_sign_link *sign_link = msg->dst; + + if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) { + LOGP(DNM, LOGL_ERROR, "id string is not com.ipaccess !?!\n"); + return -EINVAL; + } + + foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen); + abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); + + abis_nm_debugp_foh(DNM, foh); + + DEBUGPC(DNM, "IPACCESS(0x%02x): ", foh->msg_type); + + switch (foh->msg_type) { + case NM_MT_IPACC_RSL_CONNECT_ACK: + DEBUGPC(DNM, "RSL CONNECT ACK "); + if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP)) { + memcpy(&addr, + TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP), sizeof(addr)); + + DEBUGPC(DNM, "IP=%s ", inet_ntoa(addr)); + } + if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP_PORT)) + DEBUGPC(DNM, "PORT=%u ", + ntohs(*((uint16_t *) + TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP_PORT)))); + if (TLVP_PRESENT(&tp, NM_ATT_IPACC_STREAM_ID)) + DEBUGPC(DNM, "STREAM=0x%02x ", + *TLVP_VAL(&tp, NM_ATT_IPACC_STREAM_ID)); + DEBUGPC(DNM, "\n"); + break; + case NM_MT_IPACC_RSL_CONNECT_NACK: + LOGP(DNM, LOGL_ERROR, "RSL CONNECT NACK "); + if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) + DEBUGPC(DNM, " CAUSE=%s\n", + abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); + else + DEBUGPC(DNM, "\n"); + break; + case NM_MT_IPACC_SET_NVATTR_ACK: + DEBUGPC(DNM, "SET NVATTR ACK\n"); + /* FIXME: decode and show the actual attributes */ + break; + case NM_MT_IPACC_SET_NVATTR_NACK: + LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK "); + if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) + LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", + abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); + else + LOGPC(DNM, LOGL_ERROR, "\n"); + break; + case NM_MT_IPACC_GET_NVATTR_ACK: + DEBUGPC(DNM, "GET NVATTR ACK\n"); + /* FIXME: decode and show the actual attributes */ + break; + case NM_MT_IPACC_GET_NVATTR_NACK: + LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK "); + if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) + LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", + abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); + else + LOGPC(DNM, LOGL_ERROR, "\n"); + break; + case NM_MT_IPACC_SET_ATTR_ACK: + DEBUGPC(DNM, "SET ATTR ACK\n"); + break; + case NM_MT_IPACC_SET_ATTR_NACK: + LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK "); + if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) + LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", + abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); + else + LOGPC(DNM, LOGL_ERROR, "\n"); + break; + default: + DEBUGPC(DNM, "unknown\n"); + break; + } + + /* signal handling */ + switch (foh->msg_type) { + case NM_MT_IPACC_RSL_CONNECT_NACK: + case NM_MT_IPACC_SET_NVATTR_NACK: + case NM_MT_IPACC_GET_NVATTR_NACK: + signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr); + signal.msg_type = foh->msg_type; + osmo_signal_dispatch(SS_NM, S_NM_IPACC_NACK, &signal); + break; + case NM_MT_IPACC_SET_NVATTR_ACK: + signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr); + signal.msg_type = foh->msg_type; + osmo_signal_dispatch(SS_NM, S_NM_IPACC_ACK, &signal); + break; + default: + break; + } + + return 0; +} + +/* send an ip-access manufacturer specific message */ +int abis_nm_ipaccess_msg(struct gsm_bts *bts, uint8_t msg_type, + uint8_t obj_class, uint8_t bts_nr, + uint8_t trx_nr, uint8_t ts_nr, + uint8_t *attr, int attr_len) +{ + struct msgb *msg = nm_msgb_alloc(); + struct abis_om_hdr *oh; + struct abis_om_fom_hdr *foh; + uint8_t *data; + + /* construct the 12.21 OM header, observe the erroneous length */ + oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); + fill_om_hdr(oh, sizeof(*foh) + attr_len); + oh->mdisc = ABIS_OM_MDISC_MANUF; + + /* add the ip.access magic */ + data = msgb_put(msg, sizeof(ipaccess_magic)+1); + *data++ = sizeof(ipaccess_magic); + memcpy(data, ipaccess_magic, sizeof(ipaccess_magic)); + + /* fill the 12.21 FOM header */ + foh = (struct abis_om_fom_hdr *) msgb_put(msg, sizeof(*foh)); + foh->msg_type = msg_type; + foh->obj_class = obj_class; + foh->obj_inst.bts_nr = bts_nr; + foh->obj_inst.trx_nr = trx_nr; + foh->obj_inst.ts_nr = ts_nr; + + if (attr && attr_len) { + data = msgb_put(msg, attr_len); + memcpy(data, attr, attr_len); + } + + return abis_nm_sendmsg(bts, msg); +} + +/* set some attributes in NVRAM */ +int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, uint8_t *attr, + int attr_len) +{ + return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_SET_NVATTR, + NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff, attr, + attr_len); +} + +int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, + uint32_t ip, uint16_t port, uint8_t stream) +{ + struct in_addr ia; + uint8_t attr[] = { NM_ATT_IPACC_STREAM_ID, 0, + NM_ATT_IPACC_DST_IP_PORT, 0, 0, + NM_ATT_IPACC_DST_IP, 0, 0, 0, 0 }; + + int attr_len = sizeof(attr); + + ia.s_addr = htonl(ip); + attr[1] = stream; + attr[3] = port >> 8; + attr[4] = port & 0xff; + *(uint32_t *)(attr+6) = ia.s_addr; + + /* if ip == 0, we use the default IP */ + if (ip == 0) + attr_len -= 5; + + DEBUGP(DNM, "ip.access RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n", + inet_ntoa(ia), port, stream); + + return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT, + NM_OC_BASEB_TRANSC, trx->bts->bts_nr, + trx->nr, 0xff, attr, attr_len); +} + +/* restart / reboot an ip.access nanoBTS */ +int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC, + trx->bts->nr, trx->nr, 0xff); + + return abis_nm_sendmsg(trx->bts, msg); +} + +int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class, + uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, + uint8_t *attr, uint8_t attr_len) +{ + return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_ATTR, + obj_class, bts_nr, trx_nr, ts_nr, + attr, attr_len); +} + +void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts) +{ + /* we simply reuse the GSM48 function and overwrite the RAC + * with the Cell ID */ + gsm48_ra_id_by_bts(buf, bts); + *((uint16_t *)(buf + 5)) = htons(bts->cell_identity); +} + +void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked) +{ + int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED; + + trx->mo.nm_state.administrative = new_state; + if (!trx->bts || !trx->bts->oml_link) + return; + + abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER, + trx->bts->bts_nr, trx->nr, 0xff, + new_state); +} + +static const struct value_string ipacc_testres_names[] = { + { NM_IPACC_TESTRES_SUCCESS, "SUCCESS" }, + { NM_IPACC_TESTRES_TIMEOUT, "TIMEOUT" }, + { NM_IPACC_TESTRES_NO_CHANS, "NO CHANNELS" }, + { NM_IPACC_TESTRES_PARTIAL, "PARTIAL" }, + { NM_IPACC_TESTRES_STOPPED, "STOPPED" }, + { 0, NULL } +}; + +const char *ipacc_testres_name(uint8_t res) +{ + return get_value_string(ipacc_testres_names, res); +} + +void ipac_parse_cgi(struct cell_global_id *cid, const uint8_t *buf) +{ + cid->mcc = (buf[0] & 0xf) * 100; + cid->mcc += (buf[0] >> 4) * 10; + cid->mcc += (buf[1] & 0xf) * 1; + + if (buf[1] >> 4 == 0xf) { + cid->mnc = (buf[2] & 0xf) * 10; + cid->mnc += (buf[2] >> 4) * 1; + } else { + cid->mnc = (buf[2] & 0xf) * 100; + cid->mnc += (buf[2] >> 4) * 10; + cid->mnc += (buf[1] >> 4) * 1; + } + + cid->lac = ntohs(*((uint16_t *)&buf[3])); + cid->ci = ntohs(*((uint16_t *)&buf[5])); +} + +/* parse BCCH information IEI from wire format to struct ipac_bcch_info */ +int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf) +{ + uint8_t *cur = buf; + uint16_t len __attribute__((unused)); + + memset(binf, 0, sizeof(*binf)); + + if (cur[0] != NM_IPAC_EIE_BCCH_INFO) + return -EINVAL; + cur++; + + len = ntohs(*(uint16_t *)cur); + cur += 2; + + binf->info_type = ntohs(*(uint16_t *)cur); + cur += 2; + + if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) + binf->freq_qual = *cur >> 2; + + binf->arfcn = (*cur++ & 3) << 8; + binf->arfcn |= *cur++; + + if (binf->info_type & IPAC_BINF_RXLEV) + binf->rx_lev = *cur & 0x3f; + cur++; + + if (binf->info_type & IPAC_BINF_RXQUAL) + binf->rx_qual = *cur & 0x7; + cur++; + + if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) + binf->freq_err = ntohs(*(uint16_t *)cur); + cur += 2; + + if (binf->info_type & IPAC_BINF_FRAME_OFFSET) + binf->frame_offset = ntohs(*(uint16_t *)cur); + cur += 2; + + if (binf->info_type & IPAC_BINF_FRAME_NR_OFFSET) + binf->frame_nr_offset = ntohl(*(uint32_t *)cur); + cur += 4; + +#if 0 + /* Somehow this is not set correctly */ + if (binf->info_type & IPAC_BINF_BSIC) +#endif + binf->bsic = *cur & 0x3f; + cur++; + + ipac_parse_cgi(&binf->cgi, cur); + cur += 7; + + if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2) { + memcpy(binf->ba_list_si2, cur, sizeof(binf->ba_list_si2)); + cur += sizeof(binf->ba_list_si2); + } + + if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2bis) { + memcpy(binf->ba_list_si2bis, cur, + sizeof(binf->ba_list_si2bis)); + cur += sizeof(binf->ba_list_si2bis); + } + + if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2ter) { + memcpy(binf->ba_list_si2ter, cur, + sizeof(binf->ba_list_si2ter)); + cur += sizeof(binf->ba_list_si2ter); + } + + return 0; +} + +void abis_nm_clear_queue(struct gsm_bts *bts) +{ + struct msgb *msg; + + while (!llist_empty(&bts->abis_queue)) { + msg = msgb_dequeue(&bts->abis_queue); + msgb_free(msg); + } + + bts->abis_nm_pend = 0; +} diff --git a/src/libbsc/abis_nm_ipaccess.c b/src/libbsc/abis_nm_ipaccess.c new file mode 100644 index 000000000..b8225383a --- /dev/null +++ b/src/libbsc/abis_nm_ipaccess.c @@ -0,0 +1,87 @@ +/* GSM Network Management (OML) messages on the A-bis interface + * Extensions for the ip.access A-bis over IP protocol*/ + +/* (C) 2008-2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/* A list of all the 'embedded' attributes of ip.access */ +enum ipa_embedded_att { + IPA_ATT_ARFCN_WHITELIST = 0x01, + IPA_ATT_ARFCN_BLACKLIST = 0x02, + IPA_ATT_FREQ_ERR_LIST = 0x03, + IPA_ATT_CHAN_USAGE_LIST = 0x04, + IPA_ATT_BCCH_INF_TYPE = 0x05, + IPA_ATT_BCCH_INF = 0x06, + IPA_ATT_CONFIG = 0x07, + IPA_ATT_RESULT_DETAILS = 0x08, + IPA_ATT_RXLEV_THRESH = 0x09, + IPA_ATT_FREQ_SYNC_OPT = 0x0a, + IPA_ATT_MAC_ADDR = 0x0b, + IPA_ATT_HW_SW_COMPAT_NR = 0x0c, + IPA_ATT_MANUF_SER_NR = 0x0d, + IPA_ATT_OEM_ID = 0x0e, + IPA_ATT_DATETIME_MANUF = 0x0f, + IPA_ATT_DATETIME_CALIB = 0x10, + IPA_ATT_BEACON_INF = 0x11, + IPA_ATT_FREQ_ERR = 0x12, + IPA_ATT_SNMP_COMM_STRING = 0x13, + IPA_ATT_SNMP_TRAP_ADDR = 0x14, + IPA_ATT_SNMP_TRAP_PORT = 0x15, + IPA_ATT_SNMP_MAN_ADDR = 0x16, + IPA_ATT_SNMP_SYS_CONTACT = 0x17, + IPA_ATT_FACTORY_ID = 0x18, + IPA_ATT_FACTORY_SERIAL = 0x19, + IPA_ATT_LOGGED_EVT_IND = 0x1a, + IPA_ATT_LOCAL_ADD_TEXT = 0x1b, + IPA_ATT_FREQ_BANDS = 0x1c, + IPA_ATT_MAX_TA = 0x1d, + IPA_ATT_CIPH_ALG = 0x1e, + IPA_ATT_CHAN_TYPES = 0x1f, + IPA_ATT_CHAN_MODES = 0x20, + IPA_ATT_GPRS_CODING_SCHEMES = 0x21, + IPA_ATT_RTP_FEATURES = 0x22, + IPA_ATT_RSL_FEATURES = 0x23, + IPA_ATT_BTS_HW_CLASS = 0x24, + IPA_ATT_BTS_ID = 0x25, + IPA_ATT_BCAST_L2_MSG = 0x26, +}; + +/* append an ip.access channel list to the given msgb */ +static int ipa_chan_list_append(struct msgb *msg, 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/src/libbsc/abis_nm_vty.c b/src/libbsc/abis_nm_vty.c new file mode 100644 index 000000000..fd6021008 --- /dev/null +++ b/src/libbsc/abis_nm_vty.c @@ -0,0 +1,192 @@ +/* VTY interface for A-bis OML (Netowrk Management) */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern struct gsm_network *bsc_gsmnet; + +static struct cmd_node oml_node = { + OML_NODE, + "%s(oml)# ", + 1, +}; + +struct oml_node_state { + struct gsm_bts *bts; + uint8_t obj_class; + uint8_t obj_inst[3]; +}; + +static int dummy_config_write(struct vty *v) +{ + return CMD_SUCCESS; +} + +/* FIXME: auto-generate those strings from the value_string lists */ +#define NM_OBJCLASS_VTY "(site-manager|bts|radio-carrier|baseband-transceiver|channel|adjc|handover|power-contorl|btse|rack|test|envabtse|bport|gprs-nse|gprs-cell|gprs-nsvc|siemenshw)" +#define NM_OBJCLASS_VTY_HELP "Site Manager Object\n" \ + "BTS Object\n" \ + "Radio Carrier Object\n" \ + "Baseband Transceiver Object\n" \ + "Channel (Timeslot) Object\n" \ + "Adjacent Object (Siemens)\n" \ + "Handover Object (Siemens)\n" \ + "Power Control Object (Siemens)\n" \ + "BTSE Object (Siemens)\n" \ + "Rack Object (Siemens)\n" \ + "Test Object (Siemens)\n" \ + "ENVABTSE Object (Siemens)\n" \ + "BPORT Object (Siemens)\n" \ + "GPRS NSE Object (ip.access/osmo-bts)\n" \ + "GPRS Cell Object (ip.acecss/osmo-bts)\n" \ + "GPRS NSVC Object (ip.acecss/osmo-bts)\n" \ + "SIEMENSHW Object (Siemens)\n" + + +DEFUN(oml_class_inst, oml_class_inst_cmd, + "bts <0-255> oml class " NM_OBJCLASS_VTY + " instance <0-255> <0-255> <0-255>", + "BTS related commands\n" "BTS Number\n" + "Manipulate the OML managed objects\n" + "Object Class\n" NM_OBJCLASS_VTY_HELP + "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") +{ + struct gsm_bts *bts; + struct oml_node_state *oms; + int bts_nr = atoi(argv[0]); + + bts = gsm_bts_num(bsc_gsmnet, bts_nr); + if (!bts) { + vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); + if (!oms) + return CMD_WARNING; + + oms->bts = bts; + oms->obj_class = get_string_value(abis_nm_obj_class_names, argv[1]); + oms->obj_inst[0] = atoi(argv[2]); + oms->obj_inst[1] = atoi(argv[3]); + oms->obj_inst[2] = atoi(argv[4]); + + vty->index = oms; + vty->node = OML_NODE; + + return CMD_SUCCESS; + +} + +DEFUN(oml_classnum_inst, oml_classnum_inst_cmd, + "bts <0-255> oml class <0-255> instance <0-255> <0-255> <0-255>", + "BTS related commands\n" "BTS Number\n" + "Manipulate the OML managed objects\n" + "Object Class\n" "Object Class\n" + "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") +{ + struct gsm_bts *bts; + struct oml_node_state *oms; + int bts_nr = atoi(argv[0]); + + bts = gsm_bts_num(bsc_gsmnet, bts_nr); + if (!bts) { + vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); + if (!oms) + return CMD_WARNING; + + oms->bts = bts; + oms->obj_class = atoi(argv[1]); + oms->obj_inst[0] = atoi(argv[2]); + oms->obj_inst[1] = atoi(argv[3]); + oms->obj_inst[2] = atoi(argv[4]); + + vty->index = oms; + vty->node = OML_NODE; + + return CMD_SUCCESS; +} + +DEFUN(oml_chg_adm_state, oml_chg_adm_state_cmd, + "change-adm-state (locked|unlocked|shutdown|null)", + "Change the Administrative State\n" + "Locked\n" "Unlocked\n" "Shutdown\n" "NULL\n") +{ + struct oml_node_state *oms = vty->index; + enum abis_nm_adm_state state; + + state = get_string_value(abis_nm_adm_state_names, argv[0]); + + abis_nm_chg_adm_state(oms->bts, oms->obj_class, oms->obj_inst[0], + oms->obj_inst[1], oms->obj_inst[2], state); + + return CMD_SUCCESS; +} + +DEFUN(oml_opstart, oml_opstart_cmd, + "opstart", "Send an OPSTART message to the object") +{ + struct oml_node_state *oms = vty->index; + + abis_nm_opstart(oms->bts, oms->obj_class, oms->obj_inst[0], + oms->obj_inst[1], oms->obj_inst[2]); + + return CMD_SUCCESS; +} + +int abis_nm_vty_init(void) +{ + install_element(ENABLE_NODE, &oml_class_inst_cmd); + install_element(ENABLE_NODE, &oml_classnum_inst_cmd); + install_node(&oml_node, dummy_config_write); + + install_default(OML_NODE); + install_element(OML_NODE, &ournode_exit_cmd); + install_element(OML_NODE, &oml_chg_adm_state_cmd); + install_element(OML_NODE, &oml_opstart_cmd); + + return 0; +} diff --git a/src/libbsc/abis_om2000.c b/src/libbsc/abis_om2000.c new file mode 100644 index 000000000..d8350e5c1 --- /dev/null +++ b/src/libbsc/abis_om2000.c @@ -0,0 +1,1609 @@ +/* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface + * implemented based on protocol trace analysis, no formal documentation */ + +/* (C) 2010-2011 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define OM_ALLOC_SIZE 1024 +#define OM_HEADROOM_SIZE 128 + +/* use following functions from abis_nm.c: + * om2k_msgb_alloc() + * abis_om2k_sendmsg() + */ + +struct abis_om2k_hdr { + struct abis_om_hdr om; + uint16_t msg_type; + struct abis_om2k_mo mo; + uint8_t data[0]; +} __attribute__ ((packed)); + +enum abis_om2k_msgtype { + OM2K_MSGT_ABORT_SP_CMD = 0x0000, + OM2K_MSGT_ABORT_SP_COMPL = 0x0002, + OM2K_MSGT_ALARM_REP_ACK = 0x0004, + OM2K_MSGT_ALARM_REP_NACK = 0x0005, + OM2K_MSGT_ALARM_REP = 0x0006, + OM2K_MSGT_ALARM_STATUS_REQ = 0x0008, + OM2K_MSGT_ALARM_STATUS_REQ_ACK = 0x000a, + OM2K_MSGT_ALARM_STATUS_REQ_REJ = 0x000b, + OM2K_MSGT_ALARM_STATUS_RES_ACK = 0x000c, + OM2K_MSGT_ALARM_STATUS_RES_NACK = 0x000d, + OM2K_MSGT_ALARM_STATUS_RES = 0x000e, + OM2K_MSGT_CAL_TIME_RESP = 0x0010, + OM2K_MSGT_CAL_TIME_REJ = 0x0011, + OM2K_MSGT_CAL_TIME_REQ = 0x0012, + + OM2K_MSGT_CON_CONF_REQ = 0x0014, + OM2K_MSGT_CON_CONF_REQ_ACK = 0x0016, + OM2K_MSGT_CON_CONF_REQ_REJ = 0x0017, + OM2K_MSGT_CON_CONF_RES_ACK = 0x0018, + OM2K_MSGT_CON_CONF_RES_NACK = 0x0019, + OM2K_MSGT_CON_CONF_RES = 0x001a, + + OM2K_MSGT_CONNECT_CMD = 0x001c, + OM2K_MSGT_CONNECT_COMPL = 0x001e, + OM2K_MSGT_CONNECT_REJ = 0x001f, + + OM2K_MSGT_DISABLE_REQ = 0x0028, + OM2K_MSGT_DISABLE_REQ_ACK = 0x002a, + OM2K_MSGT_DISABLE_REQ_REJ = 0x002b, + OM2K_MSGT_DISABLE_RES_ACK = 0x002c, + OM2K_MSGT_DISABLE_RES_NACK = 0x002d, + OM2K_MSGT_DISABLE_RES = 0x002e, + OM2K_MSGT_DISCONNECT_CMD = 0x0030, + OM2K_MSGT_DISCONNECT_COMPL = 0x0032, + OM2K_MSGT_DISCONNECT_REJ = 0x0033, + OM2K_MSGT_ENABLE_REQ = 0x0034, + OM2K_MSGT_ENABLE_REQ_ACK = 0x0036, + OM2K_MSGT_ENABLE_REQ_REJ = 0x0037, + OM2K_MSGT_ENABLE_RES_ACK = 0x0038, + OM2K_MSGT_ENABLE_RES_NACK = 0x0039, + OM2K_MSGT_ENABLE_RES = 0x003a, + + OM2K_MSGT_FAULT_REP_ACK = 0x0040, + OM2K_MSGT_FAULT_REP_NACK = 0x0041, + OM2K_MSGT_FAULT_REP = 0x0042, + + OM2K_MSGT_IS_CONF_REQ = 0x0060, + OM2K_MSGT_IS_CONF_REQ_ACK = 0x0062, + OM2K_MSGT_IS_CONF_REQ_REJ = 0x0063, + OM2K_MSGT_IS_CONF_RES_ACK = 0x0064, + OM2K_MSGT_IS_CONF_RES_NACK = 0x0065, + OM2K_MSGT_IS_CONF_RES = 0x0066, + + OM2K_MSGT_OP_INFO = 0x0074, + OM2K_MSGT_OP_INFO_ACK = 0x0076, + OM2K_MSGT_OP_INFO_REJ = 0x0077, + OM2K_MSGT_RESET_CMD = 0x0078, + OM2K_MSGT_RESET_COMPL = 0x007a, + OM2K_MSGT_RESET_REJ = 0x007b, + OM2K_MSGT_RX_CONF_REQ = 0x007c, + OM2K_MSGT_RX_CONF_REQ_ACK = 0x007e, + OM2K_MSGT_RX_CONF_REQ_REJ = 0x007f, + OM2K_MSGT_RX_CONF_RES_ACK = 0x0080, + OM2K_MSGT_RX_CONF_RES_NACK = 0x0081, + OM2K_MSGT_RX_CONF_RES = 0x0082, + OM2K_MSGT_START_REQ = 0x0084, + OM2K_MSGT_START_REQ_ACK = 0x0086, + OM2K_MSGT_START_REQ_REJ = 0x0087, + OM2K_MSGT_START_RES_ACK = 0x0088, + OM2K_MSGT_START_RES_NACK = 0x0089, + OM2K_MSGT_START_RES = 0x008a, + OM2K_MSGT_STATUS_REQ = 0x008c, + OM2K_MSGT_STATUS_RESP = 0x008e, + OM2K_MSGT_STATUS_REJ = 0x008f, + + OM2K_MSGT_TEST_REQ = 0x0094, + OM2K_MSGT_TEST_REQ_ACK = 0x0096, + OM2K_MSGT_TEST_REQ_REJ = 0x0097, + OM2K_MSGT_TEST_RES_ACK = 0x0098, + OM2K_MSGT_TEST_RES_NACK = 0x0099, + OM2K_MSGT_TEST_RES = 0x009a, + + OM2K_MSGT_TF_CONF_REQ = 0x00a0, + OM2K_MSGT_TF_CONF_REQ_ACK = 0x00a2, + OM2K_MSGT_TF_CONF_REQ_REJ = 0x00a3, + OM2K_MSGT_TF_CONF_RES_ACK = 0x00a4, + OM2K_MSGT_TF_CONF_RES_NACK = 0x00a5, + OM2K_MSGT_TF_CONF_RES = 0x00a6, + OM2K_MSGT_TS_CONF_REQ = 0x00a8, + OM2K_MSGT_TS_CONF_REQ_ACK = 0x00aa, + OM2K_MSGT_TS_CONF_REQ_REJ = 0x00ab, + OM2K_MSGT_TS_CONF_RES_ACK = 0x00ac, + OM2K_MSGT_TS_CONF_RES_NACK = 0x00ad, + OM2K_MSGT_TS_CONF_RES = 0x00ae, + OM2K_MSGT_TX_CONF_REQ = 0x00b0, + OM2K_MSGT_TX_CONF_REQ_ACK = 0x00b2, + OM2K_MSGT_TX_CONF_REQ_REJ = 0x00b3, + OM2K_MSGT_TX_CONF_RES_ACK = 0x00b4, + OM2K_MSGT_TX_CONF_RES_NACK = 0x00b5, + OM2K_MSGT_TX_CONF_RES = 0x00b6, + + OM2K_MSGT_NEGOT_REQ_ACK = 0x0104, + OM2K_MSGT_NEGOT_REQ_NACK = 0x0105, + OM2K_MSGT_NEGOT_REQ = 0x0106, +}; + +enum abis_om2k_dei { + OM2K_DEI_ACCORDANCE_IND = 0x00, + OM2K_DEI_BCC = 0x06, + OM2K_DEI_BS_AG_BKS_RES = 0x07, + OM2K_DEI_BSIC = 0x09, + OM2K_DEI_BA_PA_MFRMS = 0x0a, + OM2K_DEI_CBCH_INDICATOR = 0x0b, + OM2K_DEI_CCCH_OPTIONS = 0x0c, + OM2K_DEI_CAL_TIME = 0x0d, + OM2K_DEI_COMBINATION = 0x0f, + OM2K_DEI_CON_CONN_LIST = 0x10, + OM2K_DEI_DRX_DEV_MAX = 0x12, + OM2K_DEI_END_LIST_NR = 0x13, + OM2K_DEI_EXT_COND_MAP_1 = 0x14, + OM2K_DEI_EXT_COND_MAP_2 = 0x15, + OM2K_DEI_FILLING_MARKER = 0x1c, + OM2K_DEI_FN_OFFSET = 0x1d, + OM2K_DEI_FREQ_LIST = 0x1e, + OM2K_DEI_FREQ_SPEC_RX = 0x1f, + OM2K_DEI_FREQ_SPEC_TX = 0x20, + OM2K_DEI_HSN = 0x21, + OM2K_DEI_ICM_INDICATOR = 0x22, + OM2K_DEI_INT_FAULT_MAP_1A = 0x23, + OM2K_DEI_INT_FAULT_MAP_1B = 0x24, + OM2K_DEI_INT_FAULT_MAP_2A = 0x25, + OM2K_DEI_INT_FAULT_MAP_2A_EXT = 0x26, + OM2K_DEI_IS_CONN_LIST = 0x27, + OM2K_DEI_LIST_NR = 0x28, + OM2K_DEI_LOCAL_ACCESS = 0x2a, + OM2K_DEI_MAIO = 0x2b, + OM2K_DEI_MO_STATE = 0x2c, + OM2K_DEI_NY1 = 0x2d, + OM2K_DEI_OP_INFO = 0x2e, + OM2K_DEI_POWER = 0x2f, + OM2K_DEI_REASON_CODE = 0x32, + OM2K_DEI_RX_DIVERSITY = 0x33, + OM2K_DEI_RESULT_CODE = 0x35, + OM2K_DEI_T3105 = 0x38, + OM2K_DEI_TF_MODE = 0x3a, + OM2K_DEI_TS_NR = 0x3c, + OM2K_DEI_TSC = 0x3d, + OM2K_DEI_BTS_VERSION = 0x40, + OM2K_DEI_OML_IWD_VERSION = 0x41, + OM2K_DEI_RSL_IWD_VERSION = 0x42, + OM2K_DEI_OML_FUNC_MAP_1 = 0x43, + OM2K_DEI_OML_FUNC_MAP_2 = 0x44, + OM2K_DEI_RSL_FUNC_MAP_1 = 0x45, + OM2K_DEI_RSL_FUNC_MAP_2 = 0x46, + OM2K_DEI_EXT_RANGE = 0x47, + OM2K_DEI_REQ_IND = 0x48, + OM2K_DEI_REPL_UNIT_MAP = 0x50, + OM2K_DEI_ICM_BOUND_PARAMS = 0x74, + OM2K_DEI_LSC = 0x79, + OM2K_DEI_LSC_FILT_TIME = 0x7a, + OM2K_DEI_CALL_SUPV_TIME = 0x7b, + OM2K_DEI_ICM_CHAN_RATE = 0x7e, + OM2K_DEI_HW_INFO_SIG = 0x84, + OM2K_DEI_TF_SYNC_SRC = 0x86, + OM2K_DEI_TTA = 0x87, + OM2K_DEI_CAPA_SIG = 0x8a, + OM2K_DEI_NEGOT_REC1 = 0x90, + OM2K_DEI_NEGOT_REC2 = 0x91, + OM2K_DEI_ENCR_ALG = 0x92, + OM2K_DEI_INTERF_REJ_COMB = 0x94, + OM2K_DEI_FS_OFFSET = 0x98, + OM2K_DEI_EXT_COND_MAP_2_EXT = 0x9c, +}; + +const struct tlv_definition om2k_att_tlvdef = { + .def = { + [OM2K_DEI_ACCORDANCE_IND] = { TLV_TYPE_TV }, + [OM2K_DEI_BCC] = { TLV_TYPE_TV }, + [OM2K_DEI_BS_AG_BKS_RES] = { TLV_TYPE_TV }, + [OM2K_DEI_BSIC] = { TLV_TYPE_TV }, + [OM2K_DEI_BA_PA_MFRMS] = { TLV_TYPE_TV }, + [OM2K_DEI_CBCH_INDICATOR] = { TLV_TYPE_TV }, + [OM2K_DEI_INT_FAULT_MAP_1A] = { TLV_TYPE_FIXED, 6 }, + [OM2K_DEI_INT_FAULT_MAP_1B] = { TLV_TYPE_FIXED, 6 }, + [OM2K_DEI_INT_FAULT_MAP_2A] = { TLV_TYPE_FIXED, 6 }, + [OM2K_DEI_INT_FAULT_MAP_2A_EXT]={ TLV_TYPE_FIXED, 6 }, + [OM2K_DEI_CCCH_OPTIONS] = { TLV_TYPE_TV }, + [OM2K_DEI_CAL_TIME] = { TLV_TYPE_FIXED, 6 }, + [OM2K_DEI_COMBINATION] = { TLV_TYPE_TV }, + [OM2K_DEI_CON_CONN_LIST] = { TLV_TYPE_TLV }, + [OM2K_DEI_DRX_DEV_MAX] = { TLV_TYPE_TV }, + [OM2K_DEI_END_LIST_NR] = { TLV_TYPE_TV }, + [OM2K_DEI_EXT_COND_MAP_1] = { TLV_TYPE_FIXED, 2 }, + [OM2K_DEI_EXT_COND_MAP_2] = { TLV_TYPE_FIXED, 2 }, + [OM2K_DEI_FILLING_MARKER] = { TLV_TYPE_TV }, + [OM2K_DEI_FN_OFFSET] = { TLV_TYPE_FIXED, 2 }, + [OM2K_DEI_FREQ_LIST] = { TLV_TYPE_TLV }, + [OM2K_DEI_FREQ_SPEC_RX] = { TLV_TYPE_FIXED, 2 }, + [OM2K_DEI_FREQ_SPEC_TX] = { TLV_TYPE_FIXED, 2 }, + [OM2K_DEI_HSN] = { TLV_TYPE_TV }, + [OM2K_DEI_ICM_INDICATOR] = { TLV_TYPE_TV }, + [OM2K_DEI_IS_CONN_LIST] = { TLV_TYPE_TLV }, + [OM2K_DEI_LIST_NR] = { TLV_TYPE_TV }, + [OM2K_DEI_LOCAL_ACCESS] = { TLV_TYPE_TV }, + [OM2K_DEI_MAIO] = { TLV_TYPE_TV }, + [OM2K_DEI_MO_STATE] = { TLV_TYPE_TV }, + [OM2K_DEI_NY1] = { TLV_TYPE_TV }, + [OM2K_DEI_OP_INFO] = { TLV_TYPE_TV }, + [OM2K_DEI_POWER] = { TLV_TYPE_TV }, + [OM2K_DEI_REASON_CODE] = { TLV_TYPE_TV }, + [OM2K_DEI_RX_DIVERSITY] = { TLV_TYPE_TV }, + [OM2K_DEI_RESULT_CODE] = { TLV_TYPE_TV }, + [OM2K_DEI_T3105] = { TLV_TYPE_TV }, + [OM2K_DEI_TF_MODE] = { TLV_TYPE_TV }, + [OM2K_DEI_TS_NR] = { TLV_TYPE_TV }, + [OM2K_DEI_TSC] = { TLV_TYPE_TV }, + [OM2K_DEI_BTS_VERSION] = { TLV_TYPE_FIXED, 12 }, + [OM2K_DEI_OML_IWD_VERSION] = { TLV_TYPE_FIXED, 6 }, + [OM2K_DEI_RSL_IWD_VERSION] = { TLV_TYPE_FIXED, 6 }, + [OM2K_DEI_OML_FUNC_MAP_1] = { TLV_TYPE_TLV }, + [OM2K_DEI_OML_FUNC_MAP_2] = { TLV_TYPE_TLV }, + [OM2K_DEI_RSL_FUNC_MAP_1] = { TLV_TYPE_TLV }, + [OM2K_DEI_RSL_FUNC_MAP_2] = { TLV_TYPE_TLV }, + [OM2K_DEI_EXT_RANGE] = { TLV_TYPE_TV }, + [OM2K_DEI_REQ_IND] = { TLV_TYPE_TV }, + [OM2K_DEI_REPL_UNIT_MAP] = { TLV_TYPE_FIXED, 6 }, + [OM2K_DEI_ICM_BOUND_PARAMS] = { TLV_TYPE_FIXED, 5 }, + [OM2K_DEI_LSC] = { TLV_TYPE_TV }, + [OM2K_DEI_LSC_FILT_TIME] = { TLV_TYPE_TV }, + [OM2K_DEI_CALL_SUPV_TIME] = { TLV_TYPE_TV }, + [OM2K_DEI_ICM_CHAN_RATE] = { TLV_TYPE_TV }, + [OM2K_DEI_HW_INFO_SIG] = { TLV_TYPE_FIXED, 2 }, + [OM2K_DEI_TF_SYNC_SRC] = { TLV_TYPE_TV }, + [OM2K_DEI_TTA] = { TLV_TYPE_TV }, + [OM2K_DEI_CAPA_SIG] = { TLV_TYPE_FIXED, 2 }, + [OM2K_DEI_NEGOT_REC1] = { TLV_TYPE_TLV }, + [OM2K_DEI_NEGOT_REC2] = { TLV_TYPE_TLV }, + [OM2K_DEI_ENCR_ALG] = { TLV_TYPE_TV }, + [OM2K_DEI_INTERF_REJ_COMB] = { TLV_TYPE_TV }, + [OM2K_DEI_FS_OFFSET] = { TLV_TYPE_FIXED, 5 }, + [OM2K_DEI_EXT_COND_MAP_2_EXT] = { TLV_TYPE_FIXED, 4 }, + }, +}; + +static const struct value_string om2k_msgcode_vals[] = { + { 0x0000, "Abort SP Command" }, + { 0x0002, "Abort SP Complete" }, + { 0x0004, "Alarm Report ACK" }, + { 0x0005, "Alarm Report NACK" }, + { 0x0006, "Alarm Report" }, + { 0x0008, "Alarm Status Request" }, + { 0x000a, "Alarm Status Request Accept" }, + { 0x000b, "Alarm Status Request Reject" }, + { 0x000c, "Alarm Status Result ACK" }, + { 0x000d, "Alarm Status Result NACK" }, + { 0x000e, "Alarm Status Result" }, + { 0x0010, "Calendar Time Response" }, + { 0x0011, "Calendar Time Reject" }, + { 0x0012, "Calendar Time Request" }, + { 0x0014, "CON Configuration Request" }, + { 0x0016, "CON Configuration Request Accept" }, + { 0x0017, "CON Configuration Request Reject" }, + { 0x0018, "CON Configuration Result ACK" }, + { 0x0019, "CON Configuration Result NACK" }, + { 0x001a, "CON Configuration Result" }, + { 0x001c, "Connect Command" }, + { 0x001e, "Connect Complete" }, + { 0x001f, "Connect Reject" }, + { 0x0028, "Disable Request" }, + { 0x002a, "Disable Request Accept" }, + { 0x002b, "Disable Request Reject" }, + { 0x002c, "Disable Result ACK" }, + { 0x002d, "Disable Result NACK" }, + { 0x002e, "Disable Result" }, + { 0x0030, "Disconnect Command" }, + { 0x0032, "Disconnect Complete" }, + { 0x0033, "Disconnect Reject" }, + { 0x0034, "Enable Request" }, + { 0x0036, "Enable Request Accept" }, + { 0x0037, "Enable Request Reject" }, + { 0x0038, "Enable Result ACK" }, + { 0x0039, "Enable Result NACK" }, + { 0x003a, "Enable Result" }, + { 0x003c, "Escape Downlink Normal" }, + { 0x003d, "Escape Downlink NACK" }, + { 0x003e, "Escape Uplink Normal" }, + { 0x003f, "Escape Uplink NACK" }, + { 0x0040, "Fault Report ACK" }, + { 0x0041, "Fault Report NACK" }, + { 0x0042, "Fault Report" }, + { 0x0044, "File Package End Command" }, + { 0x0046, "File Package End Result" }, + { 0x0047, "File Package End Reject" }, + { 0x0048, "File Relation Request" }, + { 0x004a, "File Relation Response" }, + { 0x004b, "File Relation Request Reject" }, + { 0x004c, "File Segment Transfer" }, + { 0x004e, "File Segment Transfer Complete" }, + { 0x004f, "File Segment Transfer Reject" }, + { 0x0050, "HW Information Request" }, + { 0x0052, "HW Information Request Accept" }, + { 0x0053, "HW Information Request Reject" }, + { 0x0054, "HW Information Result ACK" }, + { 0x0055, "HW Information Result NACK" }, + { 0x0056, "HW Information Result" }, + { 0x0060, "IS Configuration Request" }, + { 0x0062, "IS Configuration Request Accept" }, + { 0x0063, "IS Configuration Request Reject" }, + { 0x0064, "IS Configuration Result ACK" }, + { 0x0065, "IS Configuration Result NACK" }, + { 0x0066, "IS Configuration Result" }, + { 0x0068, "Load Data End" }, + { 0x006a, "Load Data End Result" }, + { 0x006b, "Load Data End Reject" }, + { 0x006c, "Load Data Init" }, + { 0x006e, "Load Data Init Accept" }, + { 0x006f, "Load Data Init Reject" }, + { 0x0070, "Loop Control Command" }, + { 0x0072, "Loop Control Complete" }, + { 0x0073, "Loop Control Reject" }, + { 0x0074, "Operational Information" }, + { 0x0076, "Operational Information Accept" }, + { 0x0077, "Operational Information Reject" }, + { 0x0078, "Reset Command" }, + { 0x007a, "Reset Complete" }, + { 0x007b, "Reset Reject" }, + { 0x007c, "RX Configuration Request" }, + { 0x007e, "RX Configuration Request Accept" }, + { 0x007f, "RX Configuration Request Reject" }, + { 0x0080, "RX Configuration Result ACK" }, + { 0x0081, "RX Configuration Result NACK" }, + { 0x0082, "RX Configuration Result" }, + { 0x0084, "Start Request" }, + { 0x0086, "Start Request Accept" }, + { 0x0087, "Start Request Reject" }, + { 0x0088, "Start Result ACK" }, + { 0x0089, "Start Result NACK" }, + { 0x008a, "Start Result" }, + { 0x008c, "Status Request" }, + { 0x008e, "Status Response" }, + { 0x008f, "Status Reject" }, + { 0x0094, "Test Request" }, + { 0x0096, "Test Request Accept" }, + { 0x0097, "Test Request Reject" }, + { 0x0098, "Test Result ACK" }, + { 0x0099, "Test Result NACK" }, + { 0x009a, "Test Result" }, + { 0x00a0, "TF Configuration Request" }, + { 0x00a2, "TF Configuration Request Accept" }, + { 0x00a3, "TF Configuration Request Reject" }, + { 0x00a4, "TF Configuration Result ACK" }, + { 0x00a5, "TF Configuration Result NACK" }, + { 0x00a6, "TF Configuration Result" }, + { 0x00a8, "TS Configuration Request" }, + { 0x00aa, "TS Configuration Request Accept" }, + { 0x00ab, "TS Configuration Request Reject" }, + { 0x00ac, "TS Configuration Result ACK" }, + { 0x00ad, "TS Configuration Result NACK" }, + { 0x00ae, "TS Configuration Result" }, + { 0x00b0, "TX Configuration Request" }, + { 0x00b2, "TX Configuration Request Accept" }, + { 0x00b3, "TX Configuration Request Reject" }, + { 0x00b4, "TX Configuration Result ACK" }, + { 0x00b5, "TX Configuration Result NACK" }, + { 0x00b6, "TX Configuration Result" }, + { 0x00bc, "DIP Alarm Report ACK" }, + { 0x00bd, "DIP Alarm Report NACK" }, + { 0x00be, "DIP Alarm Report" }, + { 0x00c0, "DIP Alarm Status Request" }, + { 0x00c2, "DIP Alarm Status Response" }, + { 0x00c3, "DIP Alarm Status Reject" }, + { 0x00c4, "DIP Quality Report I ACK" }, + { 0x00c5, "DIP Quality Report I NACK" }, + { 0x00c6, "DIP Quality Report I" }, + { 0x00c8, "DIP Quality Report II ACK" }, + { 0x00c9, "DIP Quality Report II NACK" }, + { 0x00ca, "DIP Quality Report II" }, + { 0x00dc, "DP Configuration Request" }, + { 0x00de, "DP Configuration Request Accept" }, + { 0x00df, "DP Configuration Request Reject" }, + { 0x00e0, "DP Configuration Result ACK" }, + { 0x00e1, "DP Configuration Result NACK" }, + { 0x00e2, "DP Configuration Result" }, + { 0x00e4, "Capabilities HW Info Report ACK" }, + { 0x00e5, "Capabilities HW Info Report NACK" }, + { 0x00e6, "Capabilities HW Info Report" }, + { 0x00e8, "Capabilities Request" }, + { 0x00ea, "Capabilities Request Accept" }, + { 0x00eb, "Capabilities Request Reject" }, + { 0x00ec, "Capabilities Result ACK" }, + { 0x00ed, "Capabilities Result NACK" }, + { 0x00ee, "Capabilities Result" }, + { 0x00f0, "FM Configuration Request" }, + { 0x00f2, "FM Configuration Request Accept" }, + { 0x00f3, "FM Configuration Request Reject" }, + { 0x00f4, "FM Configuration Result ACK" }, + { 0x00f5, "FM Configuration Result NACK" }, + { 0x00f6, "FM Configuration Result" }, + { 0x00f8, "FM Report Request" }, + { 0x00fa, "FM Report Response" }, + { 0x00fb, "FM Report Reject" }, + { 0x00fc, "FM Start Command" }, + { 0x00fe, "FM Start Complete" }, + { 0x00ff, "FM Start Reject" }, + { 0x0100, "FM Stop Command" }, + { 0x0102, "FM Stop Complete" }, + { 0x0103, "FM Stop Reject" }, + { 0x0104, "Negotiation Request ACK" }, + { 0x0105, "Negotiation Request NACK" }, + { 0x0106, "Negotiation Request" }, + { 0x0108, "BTS Initiated Request ACK" }, + { 0x0109, "BTS Initiated Request NACK" }, + { 0x010a, "BTS Initiated Request" }, + { 0x010c, "Radio Channels Release Command" }, + { 0x010e, "Radio Channels Release Complete" }, + { 0x010f, "Radio Channels Release Reject" }, + { 0x0118, "Feature Control Command" }, + { 0x011a, "Feature Control Complete" }, + { 0x011b, "Feature Control Reject" }, + + { 0, NULL } +}; + +/* TS 12.21 Section 9.4: Attributes */ +static const struct value_string om2k_attr_vals[] = { + { 0x00, "Accordance indication" }, + { 0x01, "Alarm Id" }, + { 0x02, "Alarm Data" }, + { 0x03, "Alarm Severity" }, + { 0x04, "Alarm Status" }, + { 0x05, "Alarm Status Type" }, + { 0x06, "BCC" }, + { 0x07, "BS_AG_BKS_RES" }, + { 0x09, "BSIC" }, + { 0x0a, "BA_PA_MFRMS" }, + { 0x0b, "CBCH Indicator" }, + { 0x0c, "CCCH Options" }, + { 0x0d, "Calendar Time" }, + { 0x0f, "Channel Combination" }, + { 0x10, "CON Connection List" }, + { 0x11, "Data End Indication" }, + { 0x12, "DRX_DEV_MAX" }, + { 0x13, "End List Number" }, + { 0x14, "External Condition Map Class 1" }, + { 0x15, "External Condition Map Class 2" }, + { 0x16, "File Relation Indication" }, + { 0x17, "File Revision" }, + { 0x18, "File Segment Data" }, + { 0x19, "File Segment Length" }, + { 0x1a, "File Segment Sequence Number" }, + { 0x1b, "File Size" }, + { 0x1c, "Filling Marker" }, + { 0x1d, "FN Offset" }, + { 0x1e, "Frequency List" }, + { 0x1f, "Frequency Specifier RX" }, + { 0x20, "Frequency Specifier TX" }, + { 0x21, "HSN" }, + { 0x22, "ICM Indicator" }, + { 0x23, "Internal Fault Map Class 1A" }, + { 0x24, "Internal Fault Map Class 1B" }, + { 0x25, "Internal Fault Map Class 2A" }, + { 0x26, "Internal Fault Map Class 2A Extension" }, + { 0x27, "IS Connection List" }, + { 0x28, "List Number" }, + { 0x29, "File Package State Indication" }, + { 0x2a, "Local Access State" }, + { 0x2b, "MAIO" }, + { 0x2c, "MO State" }, + { 0x2d, "Ny1" }, + { 0x2e, "Operational Information" }, + { 0x2f, "Power" }, + { 0x30, "RU Position Data" }, + { 0x31, "Protocol Error" }, + { 0x32, "Reason Code" }, + { 0x33, "Receiver Diversity" }, + { 0x34, "Replacement Unit Map" }, + { 0x35, "Result Code" }, + { 0x36, "RU Revision Data" }, + { 0x38, "T3105" }, + { 0x39, "Test Loop Setting" }, + { 0x3a, "TF Mode" }, + { 0x3b, "TF Compensation Value" }, + { 0x3c, "Time Slot Number" }, + { 0x3d, "TSC" }, + { 0x3e, "RU Logical Id" }, + { 0x3f, "RU Serial Number Data" }, + { 0x40, "BTS Version" }, + { 0x41, "OML IWD Version" }, + { 0x42, "RWL IWD Version" }, + { 0x43, "OML Function Map 1" }, + { 0x44, "OML Function Map 2" }, + { 0x45, "RSL Function Map 1" }, + { 0x46, "RSL Function Map 2" }, + { 0x47, "Extended Range Indicator" }, + { 0x48, "Request Indicators" }, + { 0x49, "DIP Alarm Condition Map" }, + { 0x4a, "ES Incoming" }, + { 0x4b, "ES Outgoing" }, + { 0x4e, "SES Incoming" }, + { 0x4f, "SES Outgoing" }, + { 0x50, "Replacement Unit Map Extension" }, + { 0x52, "UAS Incoming" }, + { 0x53, "UAS Outgoing" }, + { 0x58, "DF Incoming" }, + { 0x5a, "DF Outgoing" }, + { 0x5c, "SF" }, + { 0x60, "S Bits Setting" }, + { 0x61, "CRC-4 Use Option" }, + { 0x62, "T Parameter" }, + { 0x63, "N Parameter" }, + { 0x64, "N1 Parameter" }, + { 0x65, "N3 Parameter" }, + { 0x66, "N4 Parameter" }, + { 0x67, "P Parameter" }, + { 0x68, "Q Parameter" }, + { 0x69, "BI_Q1" }, + { 0x6a, "BI_Q2" }, + { 0x74, "ICM Boundary Parameters" }, + { 0x77, "AFT" }, + { 0x78, "AFT RAI" }, + { 0x79, "Link Supervision Control" }, + { 0x7a, "Link Supervision Filtering Time" }, + { 0x7b, "Call Supervision Time" }, + { 0x7c, "Interval Length UAS Incoming" }, + { 0x7d, "Interval Length UAS Outgoing" }, + { 0x7e, "ICM Channel Rate" }, + { 0x7f, "Attribute Identifier" }, + { 0x80, "FM Frequency List" }, + { 0x81, "FM Frequency Report" }, + { 0x82, "FM Percentile" }, + { 0x83, "FM Clear Indication" }, + { 0x84, "HW Info Signature" }, + { 0x85, "MO Record" }, + { 0x86, "TF Synchronisation Source" }, + { 0x87, "TTA" }, + { 0x88, "End Segment Number" }, + { 0x89, "Segment Number" }, + { 0x8a, "Capabilities Signature" }, + { 0x8c, "File Relation List" }, + { 0x90, "Negotiation Record I" }, + { 0x91, "Negotiation Record II" }, + { 0x92, "Encryption Algorithm" }, + { 0x94, "Interference Rejection Combining" }, + { 0x95, "Dedication Information" }, + { 0x97, "Feature Code" }, + { 0x98, "FS Offset" }, + { 0x99, "ESB Timeslot" }, + { 0x9a, "Master TG Instance" }, + { 0x9b, "Master TX Chain Delay" }, + { 0x9c, "External Condition Class 2 Extension" }, + { 0x9d, "TSs MO State" }, + { 0, NULL } +}; + +const struct value_string om2k_mo_class_short_vals[] = { + { 0x01, "TRXC" }, + { 0x03, "TS" }, + { 0x04, "TF" }, + { 0x05, "IS" }, + { 0x06, "CON" }, + { 0x07, "DP" }, + { 0x0a, "CF" }, + { 0x0b, "TX" }, + { 0x0c, "RX" }, + { 0, NULL } +}; + +static struct msgb *om2k_msgb_alloc(void) +{ + return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, + "OM2000"); +} + +static int abis_om2k_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len) +{ + return tlv_parse(tp, &om2k_att_tlvdef, buf, len, 0, 0); +} + +static int abis_om2k_msg_tlv_parse(struct tlv_parsed *tp, struct abis_om2k_hdr *oh) +{ + return abis_om2k_tlv_parse(tp, oh->data, oh->om.length - 6); +} + +static char *om2k_mo_name(const struct abis_om2k_mo *mo) +{ + static char mo_buf[64]; + + memset(mo_buf, 0, sizeof(mo_buf)); + snprintf(mo_buf, sizeof(mo_buf), "%s/%02x/%02x/%02x", + get_value_string(om2k_mo_class_short_vals, mo->class), + mo->bts, mo->assoc_so, mo->inst); + return mo_buf; +} + +/* resolve the gsm_nm_state data structure for a given MO */ +static struct gsm_nm_state * +mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + struct gsm_bts_trx *trx; + struct gsm_nm_state *nm_state = NULL; + + switch (mo->class) { + case OM2K_MO_CLS_TRXC: + trx = gsm_bts_trx_num(bts, mo->assoc_so); + if (!trx) + return NULL; + nm_state = &trx->mo.nm_state; + break; + case OM2K_MO_CLS_TS: + trx = gsm_bts_trx_num(bts, mo->assoc_so); + if (!trx) + return NULL; + if (mo->inst >= ARRAY_SIZE(trx->ts)) + return NULL; + nm_state = &trx->ts[mo->inst].mo.nm_state; + break; + case OM2K_MO_CLS_TF: + nm_state = &bts->rbs2000.tf.mo.nm_state; + break; + case OM2K_MO_CLS_IS: + nm_state = &bts->rbs2000.is.mo.nm_state; + break; + case OM2K_MO_CLS_CON: + nm_state = &bts->rbs2000.con.mo.nm_state; + break; + case OM2K_MO_CLS_DP: + nm_state = &bts->rbs2000.con.mo.nm_state; + break; + case OM2K_MO_CLS_CF: + nm_state = &bts->mo.nm_state; + break; + case OM2K_MO_CLS_TX: + trx = gsm_bts_trx_num(bts, mo->assoc_so); + if (!trx) + return NULL; + break; + case OM2K_MO_CLS_RX: + trx = gsm_bts_trx_num(bts, mo->assoc_so); + if (!trx) + return NULL; + break; + } + + return nm_state; +} + +static void *mo2obj(struct gsm_bts *bts, struct abis_om2k_mo *mo) +{ + struct gsm_bts_trx *trx; + + switch (mo->class) { + case OM2K_MO_CLS_TX: + case OM2K_MO_CLS_RX: + case OM2K_MO_CLS_TRXC: + return gsm_bts_trx_num(bts, mo->assoc_so); + case OM2K_MO_CLS_TS: + trx = gsm_bts_trx_num(bts, mo->assoc_so); + if (!trx) + return NULL; + if (mo->inst >= ARRAY_SIZE(trx->ts)) + return NULL; + return &trx->ts[mo->inst]; + case OM2K_MO_CLS_TF: + case OM2K_MO_CLS_IS: + case OM2K_MO_CLS_CON: + case OM2K_MO_CLS_DP: + case OM2K_MO_CLS_CF: + return bts; + } + + return NULL; +} + +static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, + uint8_t mo_state) +{ + struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); + struct gsm_nm_state new_state; + struct nm_statechg_signal_data nsd; + + if (!nm_state) + return; + + new_state = *nm_state; + /* NOTICE: 12.21 Availability state values != OM2000 */ + new_state.availability = mo_state; + + memset(&nsd, 0, sizeof(nsd)); + + nsd.bts = bts; + nsd.obj = mo2obj(bts, mo); + nsd.old_state = nm_state; + nsd.new_state = &new_state; + nsd.om2k_mo = mo; + + osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); + + nm_state->availability = new_state.availability; +} + +static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo, + uint8_t op_state) +{ + struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); + struct gsm_nm_state new_state; + + if (!nm_state) + return; + + new_state = *nm_state; + switch (op_state) { + case 1: + new_state.operational = NM_OPSTATE_ENABLED; + break; + case 0: + new_state.operational = NM_OPSTATE_DISABLED; + break; + default: + new_state.operational = NM_OPSTATE_NULL; + break; + } + + nm_state->operational = new_state.operational; +} + +static void signal_op_state(struct gsm_bts *bts, struct abis_om2k_mo *mo) +{ + struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); + struct nm_statechg_signal_data nsd; + + if (!nm_state) + return; + + nsd.bts = bts; + nsd.obj = mo2obj(bts, mo); + nsd.old_state = nm_state; + nsd.new_state = nm_state; + nsd.om2k_mo = mo; + + osmo_signal_dispatch(SS_NM, S_NM_STATECHG_OPER, &nsd); +} + +static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) +{ + struct abis_om2k_hdr *o2h; + struct gsm_bts_trx *trx; + + msg->l2h = msg->data; + o2h = (struct abis_om2k_hdr *) msg->l2h; + + /* Compute the length in the OML header */ + o2h->om.length = 6 + msgb_l2len(msg)-sizeof(*o2h); + + switch (o2h->mo.class) { + case OM2K_MO_CLS_TRXC: + case OM2K_MO_CLS_TX: + case OM2K_MO_CLS_RX: + /* Route through per-TRX OML Link to the appropriate TRX */ + trx = gsm_bts_trx_by_nr(bts, o2h->mo.inst); + if (!trx) { + LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " + "non-existing TRX\n", om2k_mo_name(&o2h->mo)); + return -ENODEV; + } + msg->dst = trx->oml_link; + break; + case OM2K_MO_CLS_TS: + /* Route through per-TRX OML Link to the appropriate TRX */ + trx = gsm_bts_trx_by_nr(bts, o2h->mo.assoc_so); + if (!trx) { + LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " + "non-existing TRX\n", om2k_mo_name(&o2h->mo)); + return -ENODEV; + } + msg->dst = trx->oml_link; + break; + default: + /* Route through the IXU/DXU OML Link */ + msg->dst = bts->oml_link; + break; + } + + return _abis_nm_sendmsg(msg); +} + +static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo, + uint16_t msg_type) +{ + o2h->om.mdisc = ABIS_OM_MDISC_FOM; + o2h->om.placement = ABIS_OM_PLACEMENT_ONLY; + o2h->om.sequence = 0; + /* We fill o2h->om.length later during om2k_sendmsg() */ + o2h->msg_type = htons(msg_type); + memcpy(&o2h->mo, mo, sizeof(o2h->mo)); +} + +const struct abis_om2k_mo om2k_mo_cf = { OM2K_MO_CLS_CF, 0, 0xFF, 0 }; +const struct abis_om2k_mo om2k_mo_is = { OM2K_MO_CLS_IS, 0, 0xFF, 0 }; +const struct abis_om2k_mo om2k_mo_con = { OM2K_MO_CLS_CON, 0, 0xFF, 0 }; +const struct abis_om2k_mo om2k_mo_tf = { OM2K_MO_CLS_TF, 0, 0xFF, 0 }; + +static int abis_om2k_cal_time_resp(struct gsm_bts *bts) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + time_t tm_t; + struct tm *tm; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &om2k_mo_cf, OM2K_MSGT_CAL_TIME_RESP); + + tm_t = time(NULL); + tm = localtime(&tm_t); + + msgb_put_u8(msg, OM2K_DEI_CAL_TIME); + msgb_put_u8(msg, tm->tm_year % 100); + msgb_put_u8(msg, tm->tm_mon + 1); + msgb_put_u8(msg, tm->tm_mday); + msgb_put_u8(msg, tm->tm_hour); + msgb_put_u8(msg, tm->tm_min); + msgb_put_u8(msg, tm->tm_sec); + + return abis_om2k_sendmsg(bts, msg); +} + +static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *mo, + uint8_t msg_type) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, mo, msg_type); + + DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), + get_value_string(om2k_msgcode_vals, msg_type)); + + return abis_om2k_sendmsg(bts, msg); +} + +int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_RESET_CMD); +} + +int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_START_REQ); +} + +int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_STATUS_REQ); +} + +int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_CONNECT_CMD); +} + +int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISCONNECT_CMD); +} + +int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_TEST_REQ); +} + +int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_ENABLE_REQ); +} + +int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +{ + return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISABLE_REQ); +} + +int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, + uint8_t operational) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, mo, OM2K_MSGT_OP_INFO); + + msgb_tv_put(msg, OM2K_DEI_OP_INFO, operational); + + DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), + get_value_string(om2k_msgcode_vals, OM2K_MSGT_OP_INFO)); + + /* we update the state here... and send the signal at ACK */ + update_op_state(bts, mo, operational); + + return abis_om2k_sendmsg(bts, msg); +} + +static void om2k_fill_is_conn_grp(struct om2k_is_conn_grp *grp, uint16_t icp1, + uint16_t icp2, uint8_t cont_idx) +{ + grp->icp1 = htons(icp1); + grp->icp2 = htons(icp2); + grp->cont_idx = cont_idx; +} + +int abis_om2k_tx_is_conf_req(struct gsm_bts *bts) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + struct is_conn_group *grp; + unsigned int num_grps = 0, i = 0; + struct om2k_is_conn_grp *cg; + + /* count number of groups in linked list */ + llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) + num_grps++; + + if (!num_grps) + return -EINVAL; + + /* allocate buffer for oml group array */ + cg = talloc_zero_array(bts, struct om2k_is_conn_grp, num_grps); + + /* fill array with data from linked list */ + llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) + om2k_fill_is_conn_grp(&cg[i++], grp->icp1, grp->icp2, grp->ci); + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &om2k_mo_is, OM2K_MSGT_IS_CONF_REQ); + + msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); + msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); + + msgb_tlv_put(msg, OM2K_DEI_IS_CONN_LIST, + num_grps * sizeof(*cg), (uint8_t *)cg); + + talloc_free(cg); + + DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&om2k_mo_is), + get_value_string(om2k_msgcode_vals, OM2K_MSGT_IS_CONF_REQ)); + + return abis_om2k_sendmsg(bts, msg); +} + +int abis_om2k_tx_con_conf_req(struct gsm_bts *bts, uint8_t *data, + unsigned int len) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &om2k_mo_con, OM2K_MSGT_CON_CONF_REQ); + + msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); + msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); + + msgb_tlv_put(msg, OM2K_DEI_CON_CONN_LIST, len, data); + + DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&om2k_mo_con), + get_value_string(om2k_msgcode_vals, OM2K_MSGT_CON_CONF_REQ)); + + return abis_om2k_sendmsg(bts, msg); +} + +static void om2k_trx_to_mo(struct abis_om2k_mo *mo, + const struct gsm_bts_trx *trx, + enum abis_om2k_mo_cls cls) +{ + mo->class = cls; + mo->bts = 0; + mo->inst = trx->nr; + mo->assoc_so = 255; +} + +static void om2k_ts_to_mo(struct abis_om2k_mo *mo, + const struct gsm_bts_trx_ts *ts) +{ + mo->class = OM2K_MO_CLS_TS; + mo->bts = 0; + mo->inst = ts->nr; + mo->assoc_so = ts->trx->nr; +} + +/* Configure a Receiver MO */ +int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + struct abis_om2k_mo mo; + + om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_RX); + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &mo, OM2K_MSGT_RX_CONF_REQ); + + msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_RX, trx->arfcn); + msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ + + return abis_om2k_sendmsg(trx->bts, msg); +} + +/* Configure a Transmitter MO */ +int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + struct abis_om2k_mo mo; + + om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_TX); + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TX_CONF_REQ); + + msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_TX, trx->arfcn); + msgb_tv_put(msg, OM2K_DEI_POWER, trx->nominal_power-trx->max_power_red); + msgb_tv_put(msg, OM2K_DEI_FILLING_MARKER, 0); /* Filling enabled */ + msgb_tv_put(msg, OM2K_DEI_BCC, trx->bts->bsic & 0x7); + /* Dedication Information is optional */ + + return abis_om2k_sendmsg(trx->bts, msg); +} + +enum abis_om2k_tf_mode { + OM2K_TF_MODE_MASTER = 0x00, + OM2K_TF_MODE_STANDALONE = 0x01, + OM2K_TF_MODE_SLAVE = 0x02, + OM2K_TF_MODE_UNDEFINED = 0xff, +}; + +static const uint8_t fs_offset_undef[5] = { 0xff, 0xff, 0xff, 0xff, 0xff }; + +int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &om2k_mo_tf, OM2K_MSGT_TF_CONF_REQ); + + msgb_tv_put(msg, OM2K_DEI_TF_MODE, OM2K_TF_MODE_STANDALONE); + msgb_tv_put(msg, OM2K_DEI_TF_SYNC_SRC, 0x00); + msgb_tv_fixed_put(msg, OM2K_DEI_FS_OFFSET, + sizeof(fs_offset_undef), fs_offset_undef); + + DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&om2k_mo_tf), + get_value_string(om2k_msgcode_vals, OM2K_MSGT_TF_CONF_REQ)); + + return abis_om2k_sendmsg(bts, msg); +} + +static uint8_t pchan2comb(enum gsm_phys_chan_config pchan) +{ + switch (pchan) { + case GSM_PCHAN_CCCH: + return 4; + case GSM_PCHAN_CCCH_SDCCH4: + return 5; + case GSM_PCHAN_SDCCH8_SACCH8C: + return 3; + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + case GSM_PCHAN_PDCH: + case GSM_PCHAN_TCH_F_PDCH: + return 8; + default: + return 0; + } +} + +static int put_freq_list(uint8_t *buf, uint16_t arfcn) +{ + buf[0] = 0x00; /* TX/RX address */ + buf[1] = (arfcn >> 8); + buf[2] = (arfcn & 0xff); + + return 3; +} + +/* Compute a frequency list in OM2000 fomrmat */ +static int om2k_gen_freq_list(uint8_t *list, struct gsm_bts_trx_ts *ts) +{ + uint8_t *cur = list; + int len; + + if (ts->hopping.enabled) { + unsigned int i; + for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { + if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) + cur += put_freq_list(cur, i); + } + } else + cur += put_freq_list(cur, ts->trx->arfcn); + + len = cur - list; + + return len; +} + +const uint8_t icm_bound_params[] = { 0x02, 0x06, 0x0c, 0x16, 0x06 }; + +int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + struct abis_om2k_mo mo; + uint8_t freq_list[64*3]; /* BA max size: 64 ARFCN */ + int freq_list_len; + + om2k_ts_to_mo(&mo, ts); + + memset(freq_list, 0, sizeof(freq_list)); + freq_list_len = om2k_gen_freq_list(freq_list, ts); + if (freq_list_len < 0) + return freq_list_len; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TS_CONF_REQ); + + msgb_tv_put(msg, OM2K_DEI_COMBINATION, pchan2comb(ts->pchan)); + msgb_tv_put(msg, OM2K_DEI_TS_NR, ts->nr); + msgb_tlv_put(msg, OM2K_DEI_FREQ_LIST, freq_list_len, freq_list); + msgb_tv_put(msg, OM2K_DEI_HSN, ts->hopping.hsn); + msgb_tv_put(msg, OM2K_DEI_MAIO, ts->hopping.maio); + msgb_tv_put(msg, OM2K_DEI_BSIC, ts->trx->bts->bsic); + msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ + msgb_tv16_put(msg, OM2K_DEI_FN_OFFSET, 0); + msgb_tv_put(msg, OM2K_DEI_EXT_RANGE, 0); /* Off */ + /* Optional: Interference Rejection Combining */ + msgb_tv_put(msg, OM2K_DEI_INTERF_REJ_COMB, 0x00); + switch (ts->pchan) { + case GSM_PCHAN_CCCH: + msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); + msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01); + msgb_tv_put(msg, OM2K_DEI_DRX_DEV_MAX, 0x05); + /* Repeat Paging/IMM.ASS: True, Allow Paging Type 3: Yes, Page for 5 seconds (default) */ + msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01); + break; + case GSM_PCHAN_CCCH_SDCCH4: + msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); + msgb_tv_put(msg, OM2K_DEI_NY1, 35); + msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); + msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0); + msgb_tv_put(msg, OM2K_DEI_TSC, ts->trx->bts->tsc); + 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, ts->trx->bts->tsc); + /* 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, ts->trx->bts->tsc); + /* Disable RF RESOURCE INDICATION on idle channels */ + msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); + msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, + sizeof(icm_bound_params), icm_bound_params); + msgb_tv_put(msg, OM2K_DEI_TTA, 10); /* Timer for Time Alignment */ + if (ts->pchan == GSM_PCHAN_TCH_H) + msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 1); /* TCH/H */ + else + msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 0); /* TCH/F */ + msgb_tv_put(msg, OM2K_DEI_LSC, 1); /* enabled */ + msgb_tv_put(msg, OM2K_DEI_LSC_FILT_TIME, 10); /* units of 100ms */ + msgb_tv_put(msg, OM2K_DEI_CALL_SUPV_TIME, 8); + msgb_tv_put(msg, OM2K_DEI_ENCR_ALG, 0x00); + break; + } + + return abis_om2k_sendmsg(ts->trx->bts, msg); +} + +static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2k_mo *mo, + uint8_t *data, unsigned int len) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, mo, OM2K_MSGT_NEGOT_REQ_ACK); + + msgb_tlv_put(msg, OM2K_DEI_NEGOT_REC2, len, data); + + DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), + get_value_string(om2k_msgcode_vals, OM2K_MSGT_NEGOT_REQ_ACK)); + + return abis_om2k_sendmsg(bts, msg); +} + +struct iwd_version { + uint8_t gen_char[3+1]; + uint8_t rev_char[3+1]; +}; + +struct iwd_type { + uint8_t num_vers; + struct iwd_version v[8]; +}; + +static int om2k_rx_negot_req(struct msgb *msg) +{ + struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; + struct abis_om2k_hdr *o2h = msgb_l2(msg); + struct iwd_type iwd_types[16]; + uint8_t num_iwd_types = o2h->data[2]; + uint8_t *cur = o2h->data+3; + unsigned int i, v; + + uint8_t out_buf[1024]; + uint8_t *out_cur = out_buf+1; + uint8_t out_num_types = 0; + + memset(iwd_types, 0, sizeof(iwd_types)); + + /* Parse the RBS-supported IWD versions into iwd_types array */ + for (i = 0; i < num_iwd_types; i++) { + uint8_t num_versions = *cur++; + uint8_t iwd_type = *cur++; + + iwd_types[iwd_type].num_vers = num_versions; + + for (v = 0; v < num_versions; v++) { + struct iwd_version *iwd_v = &iwd_types[iwd_type].v[v]; + + memcpy(iwd_v->gen_char, cur, 3); + cur += 3; + memcpy(iwd_v->rev_char, cur, 3); + cur += 3; + + DEBUGP(DNM, "\tIWD Type %u Gen %s Rev %s\n", iwd_type, + iwd_v->gen_char, iwd_v->rev_char); + } + } + + /* Select the last version for each IWD type */ + for (i = 0; i < ARRAY_SIZE(iwd_types); i++) { + struct iwd_type *type = &iwd_types[i]; + struct iwd_version *last_v; + + if (type->num_vers == 0) + continue; + + out_num_types++; + + last_v = &type->v[type->num_vers-1]; + + *out_cur++ = i; + memcpy(out_cur, last_v->gen_char, 3); + out_cur += 3; + memcpy(out_cur, last_v->rev_char, 3); + out_cur += 3; + } + + out_buf[0] = out_num_types; + + return abis_om2k_tx_negot_req_ack(sign_link->trx->bts, &o2h->mo, out_buf, out_cur - out_buf); +} + +static int om2k_rx_start_res(struct msgb *msg) +{ + struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; + struct abis_om2k_hdr *o2h = msgb_l2(msg); + int rc; + + rc = abis_om2k_tx_simple(sign_link->trx->bts, &o2h->mo, OM2K_MSGT_START_RES_ACK); + rc = abis_om2k_tx_op_info(sign_link->trx->bts, &o2h->mo, 1); + + return rc; +} + +static int om2k_rx_op_info_ack(struct msgb *msg) +{ + struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; + struct abis_om2k_hdr *o2h = msgb_l2(msg); + + /* This Acknowledgement does not contain the actual operational state, + * so we signal whatever state we saved when we sent the Op Info + * request */ + + signal_op_state(sign_link->trx->bts, &o2h->mo); + + return 0; +} + +const struct value_string om2k_result_strings[] = { + { 0x02, "Wrong state or out of sequence" }, + { 0x03, "File error" }, + { 0x04, "Fault, unspecified" }, + { 0x05, "Tuning fault" }, + { 0x06, "Protocol error" }, + { 0x07, "MO not connected" }, + { 0x08, "Parameter error" }, + { 0x09, "Optional function not supported" }, + { 0x0a, "Local access state LOCALLY DISCONNECTED" }, + { 0, NULL } +}; + +const struct value_string om2k_accordance_strings[] = { + { 0x00, "Data according to request" }, + { 0x01, "Data not according to request" }, + { 0x02, "Inconsistent MO data" }, + { 0x03, "Capability constraint violation" }, + { 0, NULL } +}; + +const struct value_string om2k_mostate_vals[] = { + { 0x00, "RESET" }, + { 0x01, "STARTED" }, + { 0x02, "ENABLED" }, + { 0x03, "DISABLED" }, + { 0, NULL } +}; + +static int om2k_rx_nack(struct msgb *msg) +{ + struct abis_om2k_hdr *o2h = msgb_l2(msg); + uint16_t msg_type = ntohs(o2h->msg_type); + struct tlv_parsed tp; + + LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s", om2k_mo_name(&o2h->mo), + get_value_string(om2k_msgcode_vals, msg_type)); + + abis_om2k_msg_tlv_parse(&tp, o2h); + if (TLVP_PRESENT(&tp, OM2K_DEI_REASON_CODE)) + LOGPC(DNM, LOGL_ERROR, ", Reason 0x%02x", + *TLVP_VAL(&tp, OM2K_DEI_REASON_CODE)); + + if (TLVP_PRESENT(&tp, OM2K_DEI_RESULT_CODE)) + LOGPC(DNM, LOGL_ERROR, ", Result %s", + get_value_string(om2k_result_strings, + *TLVP_VAL(&tp, OM2K_DEI_RESULT_CODE))); + LOGPC(DNM, LOGL_ERROR, "\n"); + + return 0; +} + +/* Process a Configuration Result message */ +static int process_conf_res(struct gsm_bts *bts, struct msgb *msg) +{ + struct abis_om2k_hdr *o2h = msgb_l2(msg); + uint16_t msg_type = ntohs(o2h->msg_type); + struct nm_om2k_signal_data nsd; + struct tlv_parsed tp; + uint8_t acc; + unsigned int log_level; + int ret; + + memset(&nsd, 0, sizeof(nsd)); + + abis_om2k_msg_tlv_parse(&tp, o2h); + if (!TLVP_PRESENT(&tp, OM2K_DEI_ACCORDANCE_IND)) + return -EIO; + + acc = *TLVP_VAL(&tp, OM2K_DEI_ACCORDANCE_IND); + + switch (acc) { + case 0: + log_level = LOGL_DEBUG; + ret = 0; + break; + default: + log_level = LOGL_ERROR; + ret = -EINVAL; + break; + } + + LOGP(DNM, log_level, "Rx MO=%s %s, Accordance: %s\n", + om2k_mo_name(&o2h->mo), + get_value_string(om2k_msgcode_vals, msg_type), + get_value_string(om2k_accordance_strings, acc)); + + nsd.bts = bts; + nsd.obj = mo2obj(bts, &o2h->mo); + nsd.om2k_mo = &o2h->mo; + nsd.accordance_ind = acc; + osmo_signal_dispatch(SS_NM, S_NM_OM2K_CONF_RES, &nsd); + + return ret; +} + +static int process_mo_state(struct gsm_bts *bts, struct msgb *msg) +{ + struct abis_om2k_hdr *o2h = msgb_l2(msg); + uint16_t msg_type = ntohs(o2h->msg_type); + struct tlv_parsed tp; + uint8_t mo_state; + + abis_om2k_msg_tlv_parse(&tp, o2h); + if (!TLVP_PRESENT(&tp, OM2K_DEI_MO_STATE)) + return -EIO; + mo_state = *TLVP_VAL(&tp, OM2K_DEI_MO_STATE); + + LOGP(DNM, LOGL_DEBUG, "Rx MO=%s %s, MO State: %s\n", + om2k_mo_name(&o2h->mo), + get_value_string(om2k_msgcode_vals, msg_type), + get_value_string(om2k_mostate_vals, mo_state)); + + update_mo_state(bts, &o2h->mo, mo_state); + + return 0; +} + +int abis_om2k_rcvmsg(struct msgb *msg) +{ + struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; + struct gsm_bts *bts = sign_link->trx->bts; + struct abis_om2k_hdr *o2h = msgb_l2(msg); + struct abis_om_hdr *oh = &o2h->om; + uint16_t msg_type = ntohs(o2h->msg_type); + int rc = 0; + + /* Various consistency checks */ + if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { + LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", + oh->placement); + if (oh->placement != ABIS_OM_PLACEMENT_FIRST) + return -EINVAL; + } + if (oh->sequence != 0) { + LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", + oh->sequence); + return -EINVAL; + } + + msg->l3h = (unsigned char *)o2h + sizeof(*o2h); + + if (oh->mdisc != ABIS_OM_MDISC_FOM) { + LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n", + oh->mdisc); + return -EINVAL; + } + + DEBUGP(DNM, "Rx MO=%s %s (%s)\n", om2k_mo_name(&o2h->mo), + get_value_string(om2k_msgcode_vals, msg_type), + osmo_hexdump(msg->l2h, msgb_l2len(msg))); + + switch (msg_type) { + case OM2K_MSGT_CAL_TIME_REQ: + rc = abis_om2k_cal_time_resp(bts); + break; + case OM2K_MSGT_FAULT_REP: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK); + process_mo_state(bts, msg); + break; + case OM2K_MSGT_NEGOT_REQ: + rc = om2k_rx_negot_req(msg); + break; + case OM2K_MSGT_START_RES: + rc = om2k_rx_start_res(msg); + process_mo_state(bts, msg); + break; + case OM2K_MSGT_OP_INFO_ACK: + rc = om2k_rx_op_info_ack(msg); + break; + case OM2K_MSGT_IS_CONF_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_IS_CONF_RES_ACK); + process_conf_res(bts, msg); + break; + case OM2K_MSGT_CON_CONF_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CON_CONF_RES_ACK); + process_conf_res(bts, msg); + break; + case OM2K_MSGT_TX_CONF_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TX_CONF_RES_ACK); + process_conf_res(bts, msg); + break; + case OM2K_MSGT_RX_CONF_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RX_CONF_RES_ACK); + process_conf_res(bts, msg); + break; + case OM2K_MSGT_TS_CONF_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TS_CONF_RES_ACK); + process_conf_res(bts, msg); + break; + case OM2K_MSGT_TF_CONF_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TF_CONF_RES_ACK); + process_conf_res(bts, msg); + break; + case OM2K_MSGT_CONNECT_COMPL: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RESET_CMD); + break; + case OM2K_MSGT_RESET_COMPL: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_REQ); + break; + case OM2K_MSGT_ENABLE_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_ENABLE_RES_ACK); + process_mo_state(bts, msg); + break; + case OM2K_MSGT_DISABLE_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_DISABLE_RES_ACK); + process_mo_state(bts, msg); + break; + case OM2K_MSGT_TEST_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TEST_RES_ACK); + process_mo_state(bts, msg); + break; + case OM2K_MSGT_STATUS_RESP: + process_mo_state(bts, msg); + break; + case OM2K_MSGT_START_REQ_ACK: + case OM2K_MSGT_CON_CONF_REQ_ACK: + case OM2K_MSGT_IS_CONF_REQ_ACK: + case OM2K_MSGT_TX_CONF_REQ_ACK: + case OM2K_MSGT_RX_CONF_REQ_ACK: + case OM2K_MSGT_TS_CONF_REQ_ACK: + case OM2K_MSGT_TF_CONF_REQ_ACK: + case OM2K_MSGT_ENABLE_REQ_ACK: + case OM2K_MSGT_ALARM_STATUS_REQ_ACK: + case OM2K_MSGT_DISABLE_REQ_ACK: + break; + case OM2K_MSGT_START_REQ_REJ: + case OM2K_MSGT_CONNECT_REJ: + case OM2K_MSGT_OP_INFO_REJ: + case OM2K_MSGT_DISCONNECT_REJ: + case OM2K_MSGT_TEST_REQ_REJ: + case OM2K_MSGT_CON_CONF_REQ_REJ: + case OM2K_MSGT_IS_CONF_REQ_REJ: + case OM2K_MSGT_TX_CONF_REQ_REJ: + case OM2K_MSGT_RX_CONF_REQ_REJ: + case OM2K_MSGT_TS_CONF_REQ_REJ: + case OM2K_MSGT_TF_CONF_REQ_REJ: + case OM2K_MSGT_ENABLE_REQ_REJ: + case OM2K_MSGT_ALARM_STATUS_REQ_REJ: + case OM2K_MSGT_DISABLE_REQ_REJ: + rc = om2k_rx_nack(msg); + break; + default: + LOGP(DNM, LOGL_NOTICE, "Rx unhandled OM2000 msg %s\n", + get_value_string(om2k_msgcode_vals, msg_type)); + } + + msgb_free(msg); + return rc; +} diff --git a/src/libbsc/abis_om2000_vty.c b/src/libbsc/abis_om2000_vty.c new file mode 100644 index 000000000..3df005ba0 --- /dev/null +++ b/src/libbsc/abis_om2000_vty.c @@ -0,0 +1,466 @@ +/* VTY interface for A-bis OM2000 */ + +/* (C) 2010-2011 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern struct gsm_network *bsc_gsmnet; + +static struct cmd_node om2k_node = { + OM2K_NODE, + "%s(om2k)# ", + 1, +}; + +struct oml_node_state { + struct gsm_bts *bts; + struct abis_om2k_mo mo; +}; + +static int dummy_config_write(struct vty *v) +{ + return CMD_SUCCESS; +} + +/* FIXME: auto-generate those strings from the value_string lists */ +#define OM2K_OBJCLASS_VTY "(trxc|ts|tf|is|con|dp|cf|tx|rx)" +#define OM2K_OBJCLASS_VTY_HELP "TRX Controller\n" \ + "Timeslot\n" \ + "Timing Function\n" \ + "Interface Switch\n" \ + "Abis Concentrator\n" \ + "Digital Path\n" \ + "Central Function\n" \ + "Transmitter\n" \ + "Receiver\n" + +DEFUN(om2k_class_inst, om2k_class_inst_cmd, + "bts <0-255> om2000 class " OM2K_OBJCLASS_VTY + " <0-255> <0-255> <0-255>", + "BTS related commands\n" "BTS Number\n" + "Manipulate the OM2000 managed objects\n" + "Object Class\n" OM2K_OBJCLASS_VTY_HELP + "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") +{ + struct gsm_bts *bts; + struct oml_node_state *oms; + int bts_nr = atoi(argv[0]); + + bts = gsm_bts_num(bsc_gsmnet, bts_nr); + if (!bts) { + vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + if (bts->type != GSM_BTS_TYPE_RBS2000) { + vty_out(vty, "%% BTS %d not an Ericsson RBS%s", + bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); + if (!oms) + return CMD_WARNING; + + oms->bts = bts; + oms->mo.class = get_string_value(om2k_mo_class_short_vals, argv[1]); + oms->mo.bts = atoi(argv[2]); + oms->mo.assoc_so = atoi(argv[3]); + oms->mo.inst = atoi(argv[4]); + + vty->index = oms; + vty->node = OM2K_NODE; + + return CMD_SUCCESS; + +} + +DEFUN(om2k_classnum_inst, om2k_classnum_inst_cmd, + "bts <0-255> om2000 class <0-255> <0-255> <0-255> <0-255>", + "BTS related commands\n" "BTS Number\n" + "Manipulate the OML managed objects\n" + "Object Class\n" "Object Class\n" + "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") +{ + struct gsm_bts *bts; + struct oml_node_state *oms; + int bts_nr = atoi(argv[0]); + + bts = gsm_bts_num(bsc_gsmnet, bts_nr); + if (!bts) { + vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); + if (!oms) + return CMD_WARNING; + + oms->bts = bts; + oms->mo.class = atoi(argv[1]); + oms->mo.bts = atoi(argv[2]); + oms->mo.assoc_so = atoi(argv[3]); + oms->mo.inst = atoi(argv[4]); + + vty->index = oms; + vty->node = OM2K_NODE; + + return CMD_SUCCESS; +} + +DEFUN(om2k_reset, om2k_reset_cmd, + "reset-command", + "Reset the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_reset_cmd(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_start, om2k_start_cmd, + "start-request", + "Start the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_start_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_status, om2k_status_cmd, + "status-request", + "Get the MO Status\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_status_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_connect, om2k_connect_cmd, + "connect-command", + "Connect the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_connect_cmd(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_disconnect, om2k_disconnect_cmd, + "disconnect-command", + "Disconnect the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_disconnect_cmd(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_enable, om2k_enable_cmd, + "enable-request", + "Enable the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_enable_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_disable, om2k_disable_cmd, + "disable-request", + "Disable the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_disable_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_op_info, om2k_op_info_cmd, + "operational-info <0-1>", + "Set operational information\n" + "Set operational info to 0 or 1\n") +{ + struct oml_node_state *oms = vty->index; + int oper = atoi(argv[0]); + + abis_om2k_tx_op_info(oms->bts, &oms->mo, oper); + return CMD_SUCCESS; +} + +DEFUN(om2k_test, om2k_test_cmd, + "test-request", + "Test the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_test_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +struct con_conn_group { + struct llist_head list; + + uint8_t cg; + uint16_t ccp; + uint8_t tag; + uint8_t tei; +}; + +static void add_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp, + uint8_t tag, uint8_t tei) +{ + struct con_conn_group *ent = talloc_zero(bts, struct con_conn_group); + + ent->cg = cg; + ent->ccp = ccp; + ent->tag = tag; + ent->tei = tei; + + llist_add_tail(&ent->list, &bts->rbs2000.con.conn_groups); +} + +static int del_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp, + uint8_t tag, uint8_t tei) +{ + struct con_conn_group *grp, *grp2; + + llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.con.conn_groups, list) { + if (grp->cg == cg && grp->ccp == ccp && grp->tag == tag + && grp->tei == tei) { + llist_del(&grp->list); + talloc_free(grp); + return 0; + } + } + return -ENOENT; +} + +#define CON_LIST_HELP "CON connetiton list\n" \ + "Add entry to CON list\n" \ + "Delete entry from CON list\n" \ + "Connection Group Number\n" \ + "CON Connection Point\n" \ + +DEFUN(om2k_con_list_dec, om2k_con_list_dec_cmd, + "con-connection-list (add|del) <1-255> <0-1023> deconcentrated", + CON_LIST_HELP "De-concentrated in/outlet\n") +{ + struct oml_node_state *oms = vty->index; + struct gsm_bts *bts = oms->bts; + uint8_t cg = atoi(argv[1]); + uint16_t ccp = atoi(argv[2]); + + if (!strcmp(argv[0], "add")) + add_con_list(bts, cg, ccp, 0, 0xff); + else { + if (del_con_list(bts, cg, ccp, 0, 0xff) < 0) { + vty_out(vty, "%% No matching CON list entry%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +DEFUN(om2k_con_list_tei, om2k_con_list_tei_cmd, + "con-connection-list (add|del) <1-255> <0-1023> tei <0-63>", + CON_LIST_HELP "Concentrated in/outlet with TEI\n" "TEI Number\n") +{ + struct oml_node_state *oms = vty->index; + struct gsm_bts *bts = oms->bts; + uint8_t cg = atoi(argv[1]); + uint16_t ccp = atoi(argv[2]); + uint8_t tei = atoi(argv[3]); + + if (!strcmp(argv[0], "add")) + add_con_list(bts, cg, ccp, cg, tei); + else { + if (del_con_list(bts, cg, ccp, cg, tei) < 0) { + vty_out(vty, "%% No matching CON list entry%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd, + "is-connection-list (add|del) <0-2047> <0-2047> <0-255>", + "Interface Switch Connnection List\n" + "Add to IS list\n" "Delete from IS list\n" + "ICP1\n" "ICP2\n" "Contiguity Index\n") +{ + struct gsm_bts *bts = vty->index; + uint16_t icp1 = atoi(argv[1]); + uint16_t icp2 = atoi(argv[2]); + uint8_t ci = atoi(argv[3]); + struct is_conn_group *grp, *grp2; + + if (!strcmp(argv[0], "add")) { + grp = talloc_zero(bts, struct is_conn_group); + grp->icp1 = icp1; + grp->icp2 = icp2; + grp->ci = ci; + llist_add_tail(&grp->list, &bts->rbs2000.is.conn_groups); + } else { + llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.is.conn_groups, list) { + if (grp->icp1 == icp1 && grp->icp2 == icp2 + && grp->ci == ci) { + llist_del(&grp->list); + talloc_free(grp); + return CMD_SUCCESS; + } + } + vty_out(vty, "%% No matching IS Conn Group found!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + + +DEFUN(om2k_conf_req, om2k_conf_req_cmd, + "configuration-request", + "Send the configuration request for current MO\n") +{ + struct oml_node_state *oms = vty->index; + struct gsm_bts *bts = oms->bts; + struct gsm_bts_trx *trx = NULL; + struct gsm_bts_trx_ts *ts = NULL; + + switch (oms->mo.class) { + case OM2K_MO_CLS_IS: + abis_om2k_tx_is_conf_req(bts); + break; + case OM2K_MO_CLS_TS: + trx = gsm_bts_trx_by_nr(bts, oms->mo.assoc_so); + if (!trx) { + vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr, + oms->mo.assoc_so, VTY_NEWLINE); + return CMD_WARNING; + } + if (oms->mo.inst >= ARRAY_SIZE(trx->ts)) { + vty_out(vty, "%% Timeslot %u out of range%s", + oms->mo.inst, VTY_NEWLINE); + return CMD_WARNING; + } + ts = &trx->ts[oms->mo.inst]; + abis_om2k_tx_ts_conf_req(ts); + break; + case OM2K_MO_CLS_RX: + case OM2K_MO_CLS_TX: + case OM2K_MO_CLS_TRXC: + trx = gsm_bts_trx_by_nr(bts, oms->mo.inst); + if (!trx) { + vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr, + oms->mo.inst, VTY_NEWLINE); + return CMD_WARNING; + } + switch (oms->mo.class) { + case OM2K_MO_CLS_RX: + abis_om2k_tx_rx_conf_req(trx); + break; + case OM2K_MO_CLS_TX: + abis_om2k_tx_tx_conf_req(trx); + break; + default: + break; + } + break; + case OM2K_MO_CLS_TF: + abis_om2k_tx_tf_conf_req(bts); + break; + default: + vty_out(vty, "%% Don't know how to configure MO%s", + VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts) +{ + struct is_conn_group *igrp; + struct con_conn_group *cgrp; + + llist_for_each_entry(igrp, &bts->rbs2000.is.conn_groups, list) + vty_out(vty, " is-connection-list add %u %u %u%s", + igrp->icp1, igrp->icp2, igrp->ci, VTY_NEWLINE); + + llist_for_each_entry(cgrp, &bts->rbs2000.con.conn_groups, list) { + vty_out(vty, " con-connection-list add %u %u ", + cgrp->cg, cgrp->ccp); + if (cgrp->tei == 0xff) + vty_out(vty, "deconcentrated%s", VTY_NEWLINE); + else + vty_out(vty, "tei %u%s", cgrp->tei, VTY_NEWLINE); + } +} + +int abis_om2k_vty_init(void) +{ + install_element(ENABLE_NODE, &om2k_class_inst_cmd); + install_element(ENABLE_NODE, &om2k_classnum_inst_cmd); + install_node(&om2k_node, dummy_config_write); + + install_default(OM2K_NODE); + install_element(OM2K_NODE, &ournode_exit_cmd); + install_element(OM2K_NODE, &om2k_reset_cmd); + install_element(OM2K_NODE, &om2k_start_cmd); + install_element(OM2K_NODE, &om2k_status_cmd); + install_element(OM2K_NODE, &om2k_connect_cmd); + install_element(OM2K_NODE, &om2k_disconnect_cmd); + install_element(OM2K_NODE, &om2k_enable_cmd); + install_element(OM2K_NODE, &om2k_disable_cmd); + install_element(OM2K_NODE, &om2k_op_info_cmd); + install_element(OM2K_NODE, &om2k_test_cmd); + install_element(OM2K_NODE, &om2k_conf_req_cmd); + install_element(OM2K_NODE, &om2k_con_list_dec_cmd); + install_element(OM2K_NODE, &om2k_con_list_tei_cmd); + + install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd); + + return 0; +} diff --git a/src/libbsc/abis_rsl.c b/src/libbsc/abis_rsl.c new file mode 100644 index 000000000..41bfcdc92 --- /dev/null +++ b/src/libbsc/abis_rsl.c @@ -0,0 +1,2204 @@ +/* GSM Radio Signalling Link messages on the A-bis interface + * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ + +/* (C) 2008-2010 by Harald Welte + * (C) 2012 by Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RSL_ALLOC_SIZE 1024 +#define RSL_ALLOC_HEADROOM 128 + +enum sacch_deact { + SACCH_NONE, + SACCH_DEACTIVATE, +}; + +static int rsl_send_imm_assignment(struct gsm_lchan *lchan); + +static void 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 have an error timer pending to release that */ + if (lchan->state != LCHAN_S_REL_ERR) + rsl_lchan_set_state(lchan, LCHAN_S_NONE); + lchan_free(lchan); +} + +static uint8_t mdisc_by_msgtype(uint8_t msg_type) +{ + /* mask off the transparent bit ? */ + msg_type &= 0xfe; + + if ((msg_type & 0xf0) == 0x00) + return ABIS_RSL_MDISC_RLL; + if ((msg_type & 0xf0) == 0x10) { + if (msg_type >= 0x19 && msg_type <= 0x22) + return ABIS_RSL_MDISC_TRX; + else + return ABIS_RSL_MDISC_COM_CHAN; + } + if ((msg_type & 0xe0) == 0x20) + return ABIS_RSL_MDISC_DED_CHAN; + + return ABIS_RSL_MDISC_LOC; +} + +static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh, + uint8_t msg_type) +{ + dh->c.msg_discr = mdisc_by_msgtype(msg_type); + dh->c.msg_type = msg_type; + dh->ie_chan = RSL_IE_CHAN_NR; +} + +/* determine logical channel based on TRX and channel number IE */ +struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr) +{ + struct gsm_lchan *lchan; + uint8_t ts_nr = chan_nr & 0x07; + uint8_t cbits = chan_nr >> 3; + uint8_t lch_idx; + struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; + + if (cbits == 0x01) { + lch_idx = 0; /* TCH/F */ + if (ts->pchan != GSM_PCHAN_TCH_F && + ts->pchan != GSM_PCHAN_PDCH && + ts->pchan != GSM_PCHAN_TCH_F_PDCH) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if ((cbits & 0x1e) == 0x02) { + lch_idx = cbits & 0x1; /* TCH/H */ + if (ts->pchan != GSM_PCHAN_TCH_H) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if ((cbits & 0x1c) == 0x04) { + lch_idx = cbits & 0x3; /* SDCCH/4 */ + if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if ((cbits & 0x18) == 0x08) { + lch_idx = cbits & 0x7; /* SDCCH/8 */ + if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { + lch_idx = 0; + if (ts->pchan != GSM_PCHAN_CCCH && + ts->pchan != GSM_PCHAN_CCCH_SDCCH4) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + /* FIXME: we should not return first sdcch4 !!! */ + } else { + LOGP(DRSL, LOGL_ERROR, "unknown chan_nr=0x%02x\n", chan_nr); + return NULL; + } + + lchan = &ts->lchan[lch_idx]; + log_set_context(BSC_CTX_LCHAN, lchan); + if (lchan->conn) + log_set_context(BSC_CTX_SUBSCR, lchan->conn->subscr); + + return lchan; +} + +/* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */ +uint64_t str_to_imsi(const char *imsi_str) +{ + uint64_t ret; + + ret = strtoull(imsi_str, NULL, 10); + + return ret; +} + +static struct msgb *rsl_msgb_alloc(void) +{ + return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, + "RSL"); +} + +#define MACBLOCK_SIZE 23 +static void pad_macblock(uint8_t *out, const uint8_t *in, int len) +{ + memcpy(out, in, len); + + if (len < MACBLOCK_SIZE) + memset(out+len, 0x2b, MACBLOCK_SIZE-len); +} + +/* Chapter 9.3.7: Encryption Information */ +static int build_encr_info(uint8_t *out, struct gsm_lchan *lchan) +{ + *out++ = lchan->encr.alg_id & 0xff; + if (lchan->encr.key_len) + memcpy(out, lchan->encr.key, lchan->encr.key_len); + return lchan->encr.key_len + 1; +} + +static void print_rsl_cause(int lvl, const uint8_t *cause_v, uint8_t cause_len) +{ + int i; + + LOGPC(DRSL, lvl, "CAUSE=0x%02x(%s) ", + cause_v[0], rsl_err_name(cause_v[0])); + for (i = 1; i < cause_len-1; i++) + LOGPC(DRSL, lvl, "%02x ", cause_v[i]); +} + +static void lchan_act_tmr_cb(void *data) +{ + struct gsm_lchan *lchan = data; + + LOGP(DRSL, LOGL_ERROR, + "%s Timeout during activation. Marked as broken.\n", + gsm_lchan_name(lchan)); + + rsl_lchan_set_state(lchan, LCHAN_S_BROKEN); + lchan_free(lchan); +} + +static void lchan_deact_tmr_cb(void *data) +{ + struct gsm_lchan *lchan = data; + + LOGP(DRSL, LOGL_ERROR, + "%s Timeout during deactivation! Marked as broken.\n", + gsm_lchan_name(lchan)); + + rsl_lchan_set_state(lchan, LCHAN_S_BROKEN); + lchan_free(lchan); +} + + +/* Send a BCCH_INFO message as per Chapter 8.5.1 */ +int rsl_bcch_info(struct gsm_bts_trx *trx, uint8_t type, + const uint8_t *data, int len) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh); + init_dchan_hdr(dh, RSL_MT_BCCH_INFO); + dh->chan_nr = RSL_CHAN_BCCH; + + msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); + msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); + + msg->dst = trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, + const uint8_t *data, int len) +{ + struct abis_rsl_common_hdr *ch; + struct msgb *msg = rsl_msgb_alloc(); + + ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); + ch->msg_discr = ABIS_RSL_MDISC_TRX; + ch->msg_type = RSL_MT_SACCH_FILL; + + msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); + msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); + + msg->dst = trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +int rsl_sacch_info_modify(struct gsm_lchan *lchan, uint8_t type, + const uint8_t *data, int len) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_SACCH_INFO_MODIFY); + dh->chan_nr = chan_nr; + + msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); + msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); + + msg->dst = lchan->ts->trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg; + uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + + db = abs(db); + if (db > 30) + return -EINVAL; + + msg = rsl_msgb_alloc(); + + lchan->bs_power = db/2; + if (fpc) + lchan->bs_power |= 0x10; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_BS_POWER_CONTROL); + dh->chan_nr = chan_nr; + + msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); + + msg->dst = lchan->ts->trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg; + uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + int ctl_lvl; + + ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, dbm); + if (ctl_lvl < 0) + return ctl_lvl; + + msg = rsl_msgb_alloc(); + + lchan->ms_power = ctl_lvl; + + if (fpc) + lchan->ms_power |= 0x20; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_MS_POWER_CONTROL); + dh->chan_nr = chan_nr; + + msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); + + msg->dst = lchan->ts->trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm, + struct gsm_lchan *lchan) +{ + memset(cm, 0, sizeof(*cm)); + + /* FIXME: what to do with data calls ? */ + if (lchan->ts->trx->bts->network->dtx_enabled) + cm->dtx_dtu = 0x03; + else + cm->dtx_dtu = 0x00; + + /* set TCH Speech/Data */ + cm->spd_ind = lchan->rsl_cmode; + + if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && + lchan->tch_mode != GSM48_CMODE_SIGN) + LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, " + "but tch_mode != signalling\n"); + + switch (lchan->type) { + case GSM_LCHAN_SDCCH: + cm->chan_rt = RSL_CMOD_CRT_SDCCH; + break; + case GSM_LCHAN_TCH_F: + cm->chan_rt = RSL_CMOD_CRT_TCH_Bm; + break; + case GSM_LCHAN_TCH_H: + cm->chan_rt = RSL_CMOD_CRT_TCH_Lm; + break; + case GSM_LCHAN_NONE: + case GSM_LCHAN_UNKNOWN: + default: + return -EINVAL; + } + + switch (lchan->tch_mode) { + case GSM48_CMODE_SIGN: + cm->chan_rate = 0; + break; + case GSM48_CMODE_SPEECH_V1: + cm->chan_rate = RSL_CMOD_SP_GSM1; + break; + case GSM48_CMODE_SPEECH_EFR: + cm->chan_rate = RSL_CMOD_SP_GSM2; + break; + case GSM48_CMODE_SPEECH_AMR: + cm->chan_rate = RSL_CMOD_SP_GSM3; + break; + case GSM48_CMODE_DATA_14k5: + case GSM48_CMODE_DATA_12k0: + case GSM48_CMODE_DATA_6k0: + switch (lchan->csd_mode) { + case LCHAN_CSD_M_NT: + /* non-transparent CSD with RLP */ + switch (lchan->tch_mode) { + case GSM48_CMODE_DATA_14k5: + cm->chan_rate = RSL_CMOD_SP_NT_14k5; + break; + case GSM48_CMODE_DATA_12k0: + cm->chan_rate = RSL_CMOD_SP_NT_12k0; + break; + case GSM48_CMODE_DATA_6k0: + cm->chan_rate = RSL_CMOD_SP_NT_6k0; + break; + default: + return -EINVAL; + } + break; + /* transparent data services below */ + case LCHAN_CSD_M_T_1200_75: + cm->chan_rate = RSL_CMOD_CSD_T_1200_75; + break; + case LCHAN_CSD_M_T_600: + cm->chan_rate = RSL_CMOD_CSD_T_600; + break; + case LCHAN_CSD_M_T_1200: + cm->chan_rate = RSL_CMOD_CSD_T_1200; + break; + case LCHAN_CSD_M_T_2400: + cm->chan_rate = RSL_CMOD_CSD_T_2400; + break; + case LCHAN_CSD_M_T_9600: + cm->chan_rate = RSL_CMOD_CSD_T_9600; + break; + case LCHAN_CSD_M_T_14400: + cm->chan_rate = RSL_CMOD_CSD_T_14400; + break; + case LCHAN_CSD_M_T_29000: + cm->chan_rate = RSL_CMOD_CSD_T_29000; + break; + case LCHAN_CSD_M_T_32000: + cm->chan_rate = RSL_CMOD_CSD_T_32000; + break; + default: + return -EINVAL; + } + default: + return -EINVAL; + } + + return 0; +} + +/* Chapter 8.4.1 */ +#if 0 +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) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); + dh->chan_nr = chan_nr; + + msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); + /* For compatibility with Phase 1 */ + msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(*chan_mode), + (uint8_t *) chan_mode); + msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4, + (uint8_t *) chan_ident); +#if 0 + msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1, + (uint8_t *) &encr_info); +#endif + msgb_tv_put(msg, RSL_IE_BS_POWER, bs_power); + msgb_tv_put(msg, RSL_IE_MS_POWER, ms_power); + msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); + + msg->dst = trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} +#endif + +int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type, + uint8_t ta, uint8_t ho_ref) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg; + int rc; + uint8_t *len; + + uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + struct rsl_ie_chan_mode cm; + struct gsm48_chan_desc cd; + + rc = channel_mode_from_lchan(&cm, lchan); + if (rc < 0) + return rc; + + memset(&cd, 0, sizeof(cd)); + gsm48_lchan2chan_desc(&cd, lchan); + + msg = rsl_msgb_alloc(); + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); + dh->chan_nr = chan_nr; + + msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); + msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), + (uint8_t *) &cm); + + /* + * The Channel Identification is needed for Phase1 phones + * and it contains the GSM48 Channel Description and the + * Mobile Allocation. The GSM 08.58 asks for the Mobile + * Allocation to have a length of zero. We are using the + * msgb_l3len to calculate the length of both messages. + */ + msgb_v_put(msg, RSL_IE_CHAN_IDENT); + len = msgb_put(msg, 1); + msgb_tv_fixed_put(msg, GSM48_IE_CHANDESC_2, sizeof(cd), (const uint8_t *) &cd); + + if (lchan->ts->hopping.enabled) + msgb_tlv_put(msg, GSM48_IE_MA_AFTER, lchan->ts->hopping.ma_len, + lchan->ts->hopping.ma_data); + else + msgb_tlv_put(msg, GSM48_IE_MA_AFTER, 0, NULL); + + /* update the calculated size */ + msg->l3h = len + 1; + *len = msgb_l3len(msg); + + if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { + uint8_t encr_info[MAX_A5_KEY_LEN+2]; + rc = build_encr_info(encr_info, lchan); + if (rc > 0) + msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); + } + + switch (act_type) { + case RSL_ACT_INTER_ASYNC: + case RSL_ACT_INTER_SYNC: + msgb_tv_put(msg, RSL_IE_HANDO_REF, ho_ref); + break; + default: + break; + } + + msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); + msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); + msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); + + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) + msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf), + (uint8_t *) &lchan->mr_conf); + + 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); + } + + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { + msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf), + (uint8_t *) &lchan->mr_conf); + } + + msg->dst = lchan->ts->trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +/* Chapter 8.4.6: Send the encryption command with given L3 info */ +int rsl_encryption_cmd(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh; + struct gsm_lchan *lchan = msg->lchan; + uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t encr_info[MAX_A5_KEY_LEN+2]; + uint8_t l3_len = msg->len; + int rc; + + /* First push the L3 IE tag and length */ + msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); + + /* then the link identifier (SAPI0, main sign link) */ + msgb_tv_push(msg, RSL_IE_LINK_IDENT, 0); + + /* then encryption information */ + rc = build_encr_info(encr_info, lchan); + if (rc <= 0) + return rc; + msgb_tlv_push(msg, RSL_IE_ENCR_INFO, rc, encr_info); + + /* and finally the DCHAN header */ + dh = (struct abis_rsl_dchan_hdr *) msgb_push(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_ENCR_CMD); + dh->chan_nr = chan_nr; + + msg->dst = lchan->ts->trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +/* Chapter 8.4.5 / 4.6: Deactivate the SACCH after 04.08 RR CHAN RELEASE */ +int rsl_deact_sacch(struct gsm_lchan *lchan) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_DEACTIVATE_SACCH); + dh->chan_nr = gsm_lchan2chan_nr(lchan); + + msg->lchan = lchan; + msg->dst = lchan->ts->trx->rsl_link; + + DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan)); + + return abis_rsl_sendmsg(msg); +} + +static void error_timeout_cb(void *data) +{ + struct gsm_lchan *lchan = data; + if (lchan->state != LCHAN_S_REL_ERR) { + LOGP(DRSL, LOGL_ERROR, "%s error timeout but not in error state: %d\n", + gsm_lchan_name(lchan), lchan->state); + return; + } + + /* go back to the none state */ + LOGP(DRSL, LOGL_NOTICE, "%s is back in operation.\n", gsm_lchan_name(lchan)); + rsl_lchan_set_state(lchan, LCHAN_S_NONE); +} + +static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan); + +/* Chapter 8.4.14 / 4.7: Tell BTS to release the radio channel */ +static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error, + enum sacch_deact deact_sacch) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg; + int rc; + + /* Stop timers that should lead to a channel release */ + osmo_timer_del(&lchan->T3109); + + if (lchan->state == LCHAN_S_REL_ERR) { + LOGP(DRSL, LOGL_NOTICE, "%s is in error state not sending release.\n", + gsm_lchan_name(lchan)); + return -1; + } + + msg = rsl_msgb_alloc(); + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL); + dh->chan_nr = gsm_lchan2chan_nr(lchan); + + msg->lchan = lchan; + msg->dst = lchan->ts->trx->rsl_link; + + DEBUGP(DRSL, "%s RF Channel Release CMD due error %d\n", gsm_lchan_name(lchan), error); + + if (error) { + struct e1inp_sign_link *sign_link = msg->dst; + + /* + * 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); + lchan->error_timer.data = lchan; + lchan->error_timer.cb = error_timeout_cb; + osmo_timer_schedule(&lchan->error_timer, + sign_link->trx->bts->network->T3111 + 2, 0); + } + + /* Start another timer or assume the BTS sends a ACK/NACK? */ + lchan->act_timer.cb = lchan_deact_tmr_cb; + lchan->act_timer.data = lchan; + osmo_timer_schedule(&lchan->act_timer, 4, 0); + + rc = abis_rsl_sendmsg(msg); + + /* BTS will respond by RF CHAN REL ACK */ + return rc; +} + +static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan) +{ + + DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", gsm_lchan_name(lchan)); + + /* Stop all pending timers */ + osmo_timer_del(&lchan->act_timer); + osmo_timer_del(&lchan->T3111); + + if (lchan->state == LCHAN_S_BROKEN) { + /* we are leaving this channel broken for now */ + LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK for broken channel.\n", + gsm_lchan_name(lchan)); + return 0; + } + + if (lchan->state != LCHAN_S_REL_REQ && lchan->state != LCHAN_S_REL_ERR) + LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n", + gsm_lchan_name(lchan), + gsm_lchans_name(lchan->state)); + do_lchan_free(lchan); + + return 0; +} + +int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len, + uint8_t *ms_ident, uint8_t chan_needed) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_PAGING_CMD); + dh->chan_nr = RSL_CHAN_PCH_AGCH; + + msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group); + msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len-2, ms_ident+2); + msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed); + + msg->dst = bts->c0->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +int imsi_str2bcd(uint8_t *bcd_out, const char *str_in) +{ + int i, len = strlen(str_in); + + for (i = 0; i < len; i++) { + int num = str_in[i] - 0x30; + if (num < 0 || num > 9) + return -1; + if (i % 2 == 0) + bcd_out[i/2] = num; + else + bcd_out[i/2] |= (num << 4); + } + + return 0; +} + +/* Chapter 8.5.6 */ +int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val) +{ + struct msgb *msg = rsl_msgb_alloc(); + struct abis_rsl_dchan_hdr *dh; + uint8_t buf[MACBLOCK_SIZE]; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD); + dh->chan_nr = RSL_CHAN_PCH_AGCH; + + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val); + break; + default: + /* If phase 2, construct a FULL_IMM_ASS_INFO */ + pad_macblock(buf, val, len); + msgb_tlv_put(msg, RSL_IE_FULL_IMM_ASS_INFO, MACBLOCK_SIZE, buf); + break; + } + + msg->dst = bts->c0->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +/* Send Siemens specific MS RF Power Capability Indication */ +int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci) +{ + struct msgb *msg = rsl_msgb_alloc(); + struct abis_rsl_dchan_hdr *dh; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_SIEMENS_MRPCI); + dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; + dh->chan_nr = gsm_lchan2chan_nr(lchan); + msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(uint8_t *)mrpci); + + DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n", + gsm_lchan_name(lchan), *(uint8_t *)mrpci); + + msg->dst = lchan->ts->trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + + +/* Send "DATA REQUEST" message with given L3 Info payload */ +/* Chapter 8.3.1 */ +int rsl_data_request(struct msgb *msg, uint8_t link_id) +{ + if (msg->lchan == NULL) { + LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n"); + return -EINVAL; + } + + rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, gsm_lchan2chan_nr(msg->lchan), + link_id, 1); + + msg->dst = msg->lchan->ts->trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +/* Send "ESTABLISH REQUEST" message with given L3 Info payload */ +/* Chapter 8.3.1 */ +int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id) +{ + struct msgb *msg; + + msg = rsl_rll_simple(RSL_MT_EST_REQ, gsm_lchan2chan_nr(lchan), + link_id, 0); + msg->dst = lchan->ts->trx->rsl_link; + + DEBUGP(DRLL, "%s RSL RLL ESTABLISH REQ (link_id=0x%02x)\n", + gsm_lchan_name(lchan), link_id); + + return abis_rsl_sendmsg(msg); +} + +/* 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); + + return abis_rsl_sendmsg(msg); +} + +int rsl_lchan_set_state(struct gsm_lchan *lchan, int state) +{ + lchan->state = state; + return 0; +} + +/* Chapter 8.4.2: Channel Activate Acknowledge */ +static int rsl_rx_chan_act_ack(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); + + /* BTS has confirmed channel activation, we now need + * to assign the activated channel to the MS */ + if (rslh->ie_chan != RSL_IE_CHAN_NR) + return -EINVAL; + + osmo_timer_del(&msg->lchan->act_timer); + + if (msg->lchan->state == LCHAN_S_BROKEN) { + LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK for broken channel.\n", + gsm_lchan_name(msg->lchan)); + return 0; + } + + if (msg->lchan->state != LCHAN_S_ACT_REQ) + LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n", + gsm_lchan_name(msg->lchan), + gsm_lchans_name(msg->lchan->state)); + rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE); + + if (msg->lchan->rqd_ref) { + rsl_send_imm_assignment(msg->lchan); + talloc_free(msg->lchan->rqd_ref); + msg->lchan->rqd_ref = NULL; + msg->lchan->rqd_ta = 0; + } + + send_lchan_signal(S_LCHAN_ACTIVATE_ACK, msg->lchan, NULL); + + return 0; +} + +/* Chapter 8.4.3: Channel Activate NACK */ +static int rsl_rx_chan_act_nack(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tp; + + osmo_timer_del(&msg->lchan->act_timer); + + if (msg->lchan->state == LCHAN_S_BROKEN) { + LOGP(DRSL, LOGL_ERROR, + "%s CHANNEL ACTIVATE NACK for broken channel.\n", + gsm_lchan_name(msg->lchan)); + return -1; + } + + LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK ", + gsm_lchan_name(msg->lchan)); + + /* BTS has rejected channel activation ?!? */ + if (dh->ie_chan != RSL_IE_CHAN_NR) + return -EINVAL; + + rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); + if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) { + const uint8_t *cause = TLVP_VAL(&tp, RSL_IE_CAUSE); + print_rsl_cause(LOGL_ERROR, cause, + TLVP_LEN(&tp, RSL_IE_CAUSE)); + msg->lchan->error_cause = *cause; + if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC) + rsl_lchan_set_state(msg->lchan, LCHAN_S_BROKEN); + else + rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE); + + } else + rsl_lchan_set_state(msg->lchan, LCHAN_S_BROKEN); + + 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; + + /* FIXME: print which channel */ + LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL: RELEASING ", + gsm_lchan_name(msg->lchan)); + + rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); + + if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) + print_rsl_cause(LOGL_NOTICE, TLVP_VAL(&tp, RSL_IE_CAUSE), + TLVP_LEN(&tp, RSL_IE_CAUSE)); + + LOGPC(DRSL, LOGL_NOTICE, "\n"); + /* FIXME: only free it after channel release ACK */ + osmo_counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rf_fail); + return rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE); +} + +static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru, + const char *prefix) +{ + DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ", + prefix, rxlev2dbm(mru->full.rx_lev), + prefix, rxlev2dbm(mru->sub.rx_lev)); + DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ", + prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual); +} + +static void print_meas_rep(struct gsm_lchan *lchan, struct gsm_meas_rep *mr) +{ + int i; + char *name = ""; + + if (lchan && lchan->conn && lchan->conn->subscr) + name = subscr_name(lchan->conn->subscr); + + DEBUGP(DMEAS, "[%s] MEASUREMENT RESULT NR=%d ", name, mr->nr); + + if (mr->flags & MEAS_REP_F_DL_DTX) + DEBUGPC(DMEAS, "DTXd "); + + print_meas_rep_uni(&mr->ul, "ul"); + DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power); + if (mr->flags & MEAS_REP_F_MS_TO) + DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset); + + if (mr->flags & MEAS_REP_F_MS_L1) { + DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr); + DEBUGPC(DMEAS, "L1_FPC=%u ", + mr->flags & MEAS_REP_F_FPC ? 1 : 0); + DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta); + } + + if (mr->flags & MEAS_REP_F_UL_DTX) + DEBUGPC(DMEAS, "DTXu "); + if (mr->flags & MEAS_REP_F_BA1) + DEBUGPC(DMEAS, "BA1 "); + if (!(mr->flags & MEAS_REP_F_DL_VALID)) + DEBUGPC(DMEAS, "NOT VALID "); + else + print_meas_rep_uni(&mr->dl, "dl"); + + DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell); + if (mr->num_cell == 7) + return; + for (i = 0; i < mr->num_cell; i++) { + struct gsm_meas_rep_cell *mrc = &mr->cell[i]; + DEBUGP(DMEAS, "IDX=%u ARFCN=%u BSIC=%u => %d dBm\n", + mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev)); + } +} + +static int rsl_rx_meas_res(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tp; + struct gsm_meas_rep *mr = lchan_next_meas_rep(msg->lchan); + uint8_t len; + const uint8_t *val; + int rc; + + /* check if this channel is actually active */ + /* FIXME: maybe this check should be way more generic/centralized */ + if (msg->lchan->state != LCHAN_S_ACTIVE) { + LOGP(DRSL, LOGL_DEBUG, "%s: MEAS RES for inactive channel\n", + gsm_lchan_name(msg->lchan)); + return 0; + } + + memset(mr, 0, sizeof(*mr)); + mr->lchan = msg->lchan; + + rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); + + if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) || + !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) || + !TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) + return -EIO; + + /* Mandatory Parts */ + mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR); + + len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS); + val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS); + if (len >= 3) { + if (val[0] & 0x40) + mr->flags |= MEAS_REP_F_DL_DTX; + mr->ul.full.rx_lev = val[0] & 0x3f; + mr->ul.sub.rx_lev = val[1] & 0x3f; + mr->ul.full.rx_qual = val[2]>>3 & 0x7; + mr->ul.sub.rx_qual = val[2] & 0x7; + } + + mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); + + /* Optional Parts */ + if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) + mr->ms_timing_offset = + *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET); + + if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) { + struct e1inp_sign_link *sign_link = msg->dst; + + val = TLVP_VAL(&tp, RSL_IE_L1_INFO); + mr->flags |= MEAS_REP_F_MS_L1; + mr->ms_l1.pwr = ms_pwr_dbm(sign_link->trx->bts->band, val[0] >> 3); + if (val[0] & 0x04) + mr->flags |= MEAS_REP_F_FPC; + mr->ms_l1.ta = val[1]; + /* BS11 and Nokia reports TA shifted by 2 bits */ + if (msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11 + || msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) + mr->ms_l1.ta >>= 2; + } + if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { + msg->l3h = (uint8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO); + rc = gsm48_parse_meas_rep(mr, msg); + if (rc < 0) + return rc; + } + + print_meas_rep(msg->lchan, mr); + + send_lchan_signal(S_LCHAN_MEAS_REP, msg->lchan, mr); + + return 0; +} + +/* Chapter 8.4.7 */ +static int rsl_rx_hando_det(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tp; + + DEBUGP(DRSL, "%s HANDOVER DETECT ", gsm_lchan_name(msg->lchan)); + + rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); + + if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY)) + DEBUGPC(DRSL, "access delay = %u\n", + *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY)); + else + DEBUGPC(DRSL, "\n"); + + send_lchan_signal(S_LCHAN_HANDOVER_DETECT, msg->lchan, NULL); + + return 0; +} + +static int abis_rsl_rx_dchan(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); + int rc = 0; + char *ts_name; + struct e1inp_sign_link *sign_link = msg->dst; + + msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr); + ts_name = gsm_lchan_name(msg->lchan); + + switch (rslh->c.msg_type) { + case RSL_MT_CHAN_ACTIV_ACK: + DEBUGP(DRSL, "%s CHANNEL ACTIVATE ACK\n", ts_name); + rc = rsl_rx_chan_act_ack(msg); + break; + case RSL_MT_CHAN_ACTIV_NACK: + rc = rsl_rx_chan_act_nack(msg); + break; + case RSL_MT_CONN_FAIL: + rc = rsl_rx_conn_fail(msg); + break; + case RSL_MT_MEAS_RES: + rc = rsl_rx_meas_res(msg); + break; + case RSL_MT_HANDO_DET: + rc = rsl_rx_hando_det(msg); + break; + case RSL_MT_RF_CHAN_REL_ACK: + rc = rsl_rx_rf_chan_rel_ack(msg->lchan); + break; + case RSL_MT_MODE_MODIFY_ACK: + DEBUGP(DRSL, "%s CHANNEL MODE MODIFY ACK\n", ts_name); + break; + case RSL_MT_MODE_MODIFY_NACK: + LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name); + break; + case RSL_MT_IPAC_PDCH_ACT_ACK: + DEBUGPC(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name); + msg->lchan->ts->flags |= TS_F_PDCH_MODE; + break; + case RSL_MT_IPAC_PDCH_ACT_NACK: + LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name); + break; + case RSL_MT_IPAC_PDCH_DEACT_ACK: + DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name); + msg->lchan->ts->flags &= ~TS_F_PDCH_MODE; + break; + case RSL_MT_IPAC_PDCH_DEACT_NACK: + LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name); + break; + case RSL_MT_PHY_CONTEXT_CONF: + case RSL_MT_PREPROC_MEAS_RES: + case RSL_MT_TALKER_DET: + case RSL_MT_LISTENER_DET: + case RSL_MT_REMOTE_CODEC_CONF_REP: + case RSL_MT_MR_CODEC_MOD_ACK: + case RSL_MT_MR_CODEC_MOD_NACK: + case RSL_MT_MR_CODEC_MOD_PER: + LOGP(DRSL, LOGL_NOTICE, "%s Unimplemented Abis RSL DChan " + "msg 0x%02x\n", ts_name, rslh->c.msg_type); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "%s unknown Abis RSL DChan msg 0x%02x\n", + ts_name, rslh->c.msg_type); + return -EINVAL; + } + + return rc; +} + +static int rsl_rx_error_rep(struct msgb *msg) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + struct tlv_parsed tp; + struct e1inp_sign_link *sign_link = msg->dst; + + LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT ", gsm_trx_name(sign_link->trx)); + + rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh)); + + if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) + print_rsl_cause(LOGL_ERROR, TLVP_VAL(&tp, RSL_IE_CAUSE), + TLVP_LEN(&tp, RSL_IE_CAUSE)); + + LOGPC(DRSL, LOGL_ERROR, "\n"); + + return 0; +} + +static int abis_rsl_rx_trx(struct msgb *msg) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + struct e1inp_sign_link *sign_link = msg->dst; + int rc = 0; + + switch (rslh->msg_type) { + case RSL_MT_ERROR_REPORT: + rc = rsl_rx_error_rep(msg); + break; + case RSL_MT_RF_RES_IND: + /* interference on idle channels of TRX */ + //DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(sign_link->trx)); + break; + case RSL_MT_OVERLOAD: + /* indicate CCCH / ACCH / processor overload */ + LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n", + gsm_trx_name(sign_link->trx)); + break; + case 0x42: /* Nokia specific: SI End ACK */ + LOGP(DRSL, LOGL_INFO, "Nokia SI End ACK\n"); + break; + case 0x43: /* Nokia specific: SI End NACK */ + LOGP(DRSL, LOGL_INFO, "Nokia SI End NACK\n"); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message " + "type 0x%02x\n", gsm_trx_name(sign_link->trx), rslh->msg_type); + return -EINVAL; + } + return rc; +} + +/* If T3101 expires, we never received a response to IMMEDIATE ASSIGN */ +static void t3101_expired(void *data) +{ + struct gsm_lchan *lchan = data; + + rsl_rf_chan_release(lchan, 1, SACCH_DEACTIVATE); +} + +/* If T3111 expires, we will send the RF Channel Request */ +static void t3111_expired(void *data) +{ + struct gsm_lchan *lchan = data; + + rsl_rf_chan_release(lchan, 0, SACCH_NONE); +} + +/* If T3109 expires the MS has not send a UA/UM do the error release */ +static void t3109_expired(void *data) +{ + struct gsm_lchan *lchan = data; + + LOGP(DRSL, LOGL_ERROR, + "%s SACCH deactivation timeout.\n", gsm_lchan_name(lchan)); + rsl_rf_chan_release(lchan, 1, SACCH_NONE); +} + +#define GSM48_LEN2PLEN(a) (((a) << 2) | 1) + +/* Format an IMM ASS REJ according to 04.08 Chapter 9.1.20 */ +static int rsl_send_imm_ass_rej(struct gsm_bts *bts, + unsigned int num_req_refs, + struct gsm48_req_ref *rqd_refs, + uint8_t wait_ind) +{ + uint8_t buf[GSM_MACBLOCK_LEN]; + struct gsm48_imm_ass_rej *iar = (struct gsm48_imm_ass_rej *)buf; + + /* create IMMEDIATE ASSIGN REJECT 04.08 message */ + memset(iar, 0, sizeof(*iar)); + iar->proto_discr = GSM48_PDISC_RR; + iar->msg_type = GSM48_MT_RR_IMM_ASS_REJ; + iar->page_mode = GSM48_PM_SAME; + + memcpy(&iar->req_ref1, &rqd_refs[0], sizeof(iar->req_ref1)); + iar->wait_ind1 = wait_ind; + + if (num_req_refs >= 2) + memcpy(&iar->req_ref2, &rqd_refs[1], sizeof(iar->req_ref2)); + else + memcpy(&iar->req_ref2, &rqd_refs[0], sizeof(iar->req_ref2)); + iar->wait_ind2 = wait_ind; + + if (num_req_refs >= 3) + memcpy(&iar->req_ref3, &rqd_refs[2], sizeof(iar->req_ref3)); + else + memcpy(&iar->req_ref3, &rqd_refs[0], sizeof(iar->req_ref3)); + iar->wait_ind3 = wait_ind; + + if (num_req_refs >= 4) + memcpy(&iar->req_ref4, &rqd_refs[3], sizeof(iar->req_ref4)); + else + memcpy(&iar->req_ref4, &rqd_refs[0], sizeof(iar->req_ref4)); + iar->wait_ind4 = wait_ind; + + /* we need to subtract 1 byte from sizeof(*iar) since ia includes the l2_plen field */ + iar->l2_plen = GSM48_LEN2PLEN((sizeof(*iar)-1)); + + return rsl_imm_assign_cmd(bts, sizeof(*iar), (uint8_t *) iar); +} + +/* MS has requested a channel on the RACH */ +static int rsl_rx_chan_rqd(struct msgb *msg) +{ + struct e1inp_sign_link *sign_link = msg->dst; + struct gsm_bts *bts = sign_link->trx->bts; + struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); + struct gsm48_req_ref *rqd_ref; + enum gsm_chan_t lctype; + enum gsm_chreq_reason_t chreq_reason; + struct gsm_lchan *lchan; + uint8_t rqd_ta; + int is_lu; + + uint16_t arfcn; + uint8_t subch; + + /* parse request reference to be used in immediate assign */ + if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) + return -EINVAL; + + rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; + + /* parse access delay and use as TA */ + if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) + return -EINVAL; + rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; + + /* determine channel type (SDCCH/TCH_F/TCH_H) based on + * request reference RA */ + lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra); + chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci); + + osmo_counter_inc(bts->network->stats.chreq.total); + + /* + * We want LOCATION UPDATES to succeed and will assign a TCH + * if we have no SDCCH available. + */ + is_lu = !!(chreq_reason == GSM_CHREQ_REASON_LOCATION_UPD); + + /* check availability / allocate channel */ + lchan = lchan_alloc(bts, lctype, is_lu); + if (!lchan) { + LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n", + msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra); + osmo_counter_inc(bts->network->stats.chreq.no_channel); + /* FIXME gather multiple CHAN RQD and reject up to 4 at the same time */ + if (bts->network->T3122) + rsl_send_imm_ass_rej(bts, 1, rqd_ref, bts->network->T3122 & 0xff); + return -ENOMEM; + } + + if (lchan->state != LCHAN_S_NONE) + LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel " + "in state %s\n", gsm_lchan_name(lchan), + gsm_lchans_name(lchan->state)); + + /* save the RACH data as we need it after the CHAN ACT ACK */ + lchan->rqd_ref = talloc_zero(bts, struct gsm48_req_ref); + if (!lchan->rqd_ref) { + LOGP(DRSL, LOGL_ERROR, "Failed to allocate gsm48_req_ref.\n"); + lchan_free(lchan); + return -ENOMEM; + } + + rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); + memcpy(lchan->rqd_ref, rqd_ref, sizeof(*rqd_ref)); + lchan->rqd_ta = rqd_ta; + + arfcn = lchan->ts->trx->arfcn; + subch = lchan->nr; + + lchan->encr.alg_id = RSL_ENC_ALG_A5(0); /* no encryption */ + lchan->ms_power = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); + lchan->bs_power = 0; /* 0dB reduction, output power = Pn */ + lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; + lchan->tch_mode = GSM48_CMODE_SIGN; + + /* Start another timer or assume the BTS sends a ACK/NACK? */ + lchan->act_timer.cb = lchan_act_tmr_cb; + lchan->act_timer.data = lchan; + osmo_timer_schedule(&lchan->act_timer, 4, 0); + + DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s " + "r=%s ra=0x%02x ta=%d\n", gsm_lchan_name(lchan), arfcn, subch, + gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason), + rqd_ref->ra, rqd_ta); + + /* BS11 requires TA shifted by 2 bits */ + if (bts->type == GSM_BTS_TYPE_BS11) + rqd_ta <<= 2; + rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, 0); + + return 0; +} + +static int rsl_send_imm_assignment(struct gsm_lchan *lchan) +{ + struct gsm_bts *bts = lchan->ts->trx->bts; + uint8_t buf[GSM_MACBLOCK_LEN]; + struct gsm48_imm_ass *ia = (struct gsm48_imm_ass *) buf; + + /* create IMMEDIATE ASSIGN 04.08 messge */ + memset(ia, 0, sizeof(*ia)); + /* we set ia->l2_plen once we know the length of the MA below */ + ia->proto_discr = GSM48_PDISC_RR; + ia->msg_type = GSM48_MT_RR_IMM_ASS; + ia->page_mode = GSM48_PM_SAME; + gsm48_lchan2chan_desc(&ia->chan_desc, lchan); + + /* use request reference extracted from CHAN_RQD */ + memcpy(&ia->req_ref, lchan->rqd_ref, sizeof(ia->req_ref)); + ia->timing_advance = lchan->rqd_ta; + if (!lchan->ts->hopping.enabled) { + ia->mob_alloc_len = 0; + } else { + ia->mob_alloc_len = lchan->ts->hopping.ma_len; + memcpy(ia->mob_alloc, lchan->ts->hopping.ma_data, ia->mob_alloc_len); + } + /* we need to subtract 1 byte from sizeof(*ia) since ia includes the l2_plen field */ + ia->l2_plen = GSM48_LEN2PLEN((sizeof(*ia)-1) + ia->mob_alloc_len); + + /* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */ + lchan->T3101.cb = t3101_expired; + lchan->T3101.data = lchan; + osmo_timer_schedule(&lchan->T3101, bts->network->T3101, 0); + + /* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */ + return rsl_imm_assign_cmd(bts, sizeof(*ia)+ia->mob_alloc_len, (uint8_t *) ia); +} + +/* current load on the CCCH */ +static int rsl_rx_ccch_load(struct msgb *msg) +{ + struct e1inp_sign_link *sign_link = msg->dst; + struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); + struct ccch_signal_data sd; + + sd.bts = sign_link->trx->bts; + sd.rach_slot_count = -1; + sd.rach_busy_count = -1; + sd.rach_access_count = -1; + + switch (rslh->data[0]) { + case RSL_IE_PAGING_LOAD: + sd.pg_buf_space = rslh->data[1] << 8 | rslh->data[2]; + if (is_ipaccess_bts(sign_link->trx->bts) && sd.pg_buf_space == 0xffff) { + /* paging load below configured threshold, use 50 as default */ + sd.pg_buf_space = 50; + } + paging_update_buffer_space(sign_link->trx->bts, sd.pg_buf_space); + osmo_signal_dispatch(SS_CCCH, S_CCCH_PAGING_LOAD, &sd); + break; + case RSL_IE_RACH_LOAD: + if (msg->data_len >= 7) { + sd.rach_slot_count = rslh->data[2] << 8 | rslh->data[3]; + sd.rach_busy_count = rslh->data[4] << 8 | rslh->data[5]; + sd.rach_access_count = rslh->data[6] << 8 | rslh->data[7]; + osmo_signal_dispatch(SS_CCCH, S_CCCH_RACH_LOAD, &sd); + } + break; + default: + break; + } + + return 0; +} + +static int abis_rsl_rx_cchan(struct msgb *msg) +{ + struct e1inp_sign_link *sign_link = msg->dst; + struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); + int rc = 0; + + msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr); + + switch (rslh->c.msg_type) { + case RSL_MT_CHAN_RQD: + /* MS has requested a channel on the RACH */ + rc = rsl_rx_chan_rqd(msg); + break; + case RSL_MT_CCCH_LOAD_IND: + /* current load on the CCCH */ + rc = rsl_rx_ccch_load(msg); + break; + case RSL_MT_DELETE_IND: + /* CCCH overloaded, IMM_ASSIGN was dropped */ + case RSL_MT_CBCH_LOAD_IND: + /* current load on the CBCH */ + LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message " + "type 0x%02x\n", rslh->c.msg_type); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type " + "0x%02x\n", rslh->c.msg_type); + return -EINVAL; + } + + return rc; +} + +static int rsl_rx_rll_err_ind(struct msgb *msg) +{ + struct tlv_parsed tp; + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t rlm_cause; + + rsl_tlv_parse(&tp, rllh->data, msgb_l2len(msg) - sizeof(*rllh)); + if (!TLVP_PRESENT(&tp, RSL_IE_RLM_CAUSE)) { + LOGP(DRLL, LOGL_ERROR, + "%s ERROR INDICATION without mandantory cause.\n", + gsm_lchan_name(msg->lchan)); + return -1; + } + + rlm_cause = *TLVP_VAL(&tp, RSL_IE_RLM_CAUSE); + LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s\n", + gsm_lchan_name(msg->lchan), + rsl_rlm_cause_name(rlm_cause)); + + rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND); + + if (rlm_cause == RLL_CAUSE_T200_EXPIRED) { + osmo_counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rll_err); + return rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE); + } + + return 0; +} + +static void rsl_handle_release(struct gsm_lchan *lchan) +{ + int sapi; + struct gsm_bts *bts; + + /* + * Maybe only one link/SAPI was releasd or the error handling + * was activated. Just return now and let the other code handle + * it. + */ + if (lchan->state != LCHAN_S_REL_REQ) + return; + + for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { + if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) + continue; + LOGP(DRSL, LOGL_DEBUG, "%s waiting for SAPI=%d to be released.\n", + gsm_lchan_name(lchan), sapi); + return; + } + + + /* Stop T3109 and wait for T3111 before re-using the channel */ + osmo_timer_del(&lchan->T3109); + lchan->T3111.cb = t3111_expired; + lchan->T3111.data = lchan; + bts = lchan->ts->trx->bts; + osmo_timer_schedule(&lchan->T3111, bts->network->T3111, 0); +} + +/* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST + 0x02, 0x06, + 0x01, 0x20, + 0x02, 0x00, + 0x0b, 0x00, 0x0f, 0x05, 0x08, ... */ + +static int abis_rsl_rx_rll(struct msgb *msg) +{ + struct e1inp_sign_link *sign_link = msg->dst; + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + int rc = 0; + char *ts_name; + uint8_t sapi = rllh->link_id & 7; + + msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr); + ts_name = gsm_lchan_name(msg->lchan); + DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi); + + switch (rllh->c.msg_type) { + case RSL_MT_DATA_IND: + DEBUGPC(DRLL, "DATA INDICATION\n"); + if (msgb_l2len(msg) > + sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && + rllh->data[0] == RSL_IE_L3_INFO) { + msg->l3h = &rllh->data[3]; + return gsm0408_rcvmsg(msg, rllh->link_id); + } + break; + case RSL_MT_EST_IND: + DEBUGPC(DRLL, "ESTABLISH INDICATION\n"); + /* lchan is established, stop T3101 */ + msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS; + osmo_timer_del(&msg->lchan->T3101); + if (msgb_l2len(msg) > + sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && + rllh->data[0] == RSL_IE_L3_INFO) { + msg->l3h = &rllh->data[3]; + return gsm0408_rcvmsg(msg, rllh->link_id); + } + break; + case RSL_MT_EST_CONF: + DEBUGPC(DRLL, "ESTABLISH CONFIRM\n"); + msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_NET; + rll_indication(msg->lchan, rllh->link_id, + BSC_RLLR_IND_EST_CONF); + break; + case RSL_MT_REL_IND: + /* BTS informs us of having received DISC from MS */ + DEBUGPC(DRLL, "RELEASE INDICATION\n"); + msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; + rll_indication(msg->lchan, rllh->link_id, + BSC_RLLR_IND_REL_IND); + rsl_handle_release(msg->lchan); + break; + case RSL_MT_REL_CONF: + /* BTS informs us of having received UA from MS, + * in response to DISC that we've sent earlier */ + DEBUGPC(DRLL, "RELEASE CONFIRMATION\n"); + msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; + rsl_handle_release(msg->lchan); + break; + case RSL_MT_ERROR_IND: + rc = rsl_rx_rll_err_ind(msg); + break; + case RSL_MT_UNIT_DATA_IND: + LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message " + "type 0x%02x\n", rllh->c.msg_type); + break; + default: + LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message " + "type 0x%02x\n", rllh->c.msg_type); + } + return rc; +} + +static uint8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan) +{ + switch (lchan->tch_mode) { + case GSM48_CMODE_SPEECH_V1: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return 0x00; + case GSM_LCHAN_TCH_H: + return 0x03; + default: + break; + } + case GSM48_CMODE_SPEECH_EFR: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return 0x01; + /* there's no half-rate EFR */ + default: + break; + } + case GSM48_CMODE_SPEECH_AMR: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return 0x02; + case GSM_LCHAN_TCH_H: + return 0x05; + default: + break; + } + default: + break; + } + LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for " + "tch_mode == 0x%02x\n", lchan->tch_mode); + return 0; +} + +static 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; + } + case GSM48_CMODE_SPEECH_EFR: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return RTP_PT_GSM_EFR; + /* there's no half-rate EFR */ + default: + break; + } + case GSM48_CMODE_SPEECH_AMR: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + case GSM_LCHAN_TCH_H: + return RTP_PT_AMR; + default: + break; + } + default: + break; + } + LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access rtp payload type for " + "tch_mode == 0x%02x\n & lchan_type == %d", + lchan->tch_mode, lchan->type); + return 0; +} + +/* ip.access specific RSL extensions */ +static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv) +{ + struct in_addr ip; + uint16_t port, conn_id; + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) { + ip.s_addr = *((uint32_t *) TLVP_VAL(tv, RSL_IE_IPAC_LOCAL_IP)); + DEBUGPC(DRSL, "LOCAL_IP=%s ", inet_ntoa(ip)); + lchan->abis_ip.bound_ip = ntohl(ip.s_addr); + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_PORT)) { + port = *((uint16_t *) TLVP_VAL(tv, RSL_IE_IPAC_LOCAL_PORT)); + port = ntohs(port); + DEBUGPC(DRSL, "LOCAL_PORT=%u ", port); + lchan->abis_ip.bound_port = port; + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_CONN_ID)) { + conn_id = *((uint16_t *) TLVP_VAL(tv, RSL_IE_IPAC_CONN_ID)); + conn_id = ntohs(conn_id); + DEBUGPC(DRSL, "CON_ID=%u ", conn_id); + lchan->abis_ip.conn_id = conn_id; + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_RTP_PAYLOAD2)) { + lchan->abis_ip.rtp_payload2 = + *TLVP_VAL(tv, RSL_IE_IPAC_RTP_PAYLOAD2); + DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ", + lchan->abis_ip.rtp_payload2); + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_SPEECH_MODE)) { + lchan->abis_ip.speech_mode = + *TLVP_VAL(tv, RSL_IE_IPAC_SPEECH_MODE); + DEBUGPC(DRSL, "speech_mode=0x%02x ", + lchan->abis_ip.speech_mode); + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_IP)) { + ip.s_addr = *((uint32_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_IP)); + DEBUGPC(DRSL, "REMOTE_IP=%s ", inet_ntoa(ip)); + lchan->abis_ip.connect_ip = ntohl(ip.s_addr); + } + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_PORT)) { + port = *((uint16_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_PORT)); + port = ntohs(port); + DEBUGPC(DRSL, "REMOTE_PORT=%u ", port); + lchan->abis_ip.connect_port = port; + } +} + +/*! \brief Issue IPA RSL CRCX to configure RTP on BTS side + * \param[in] lchan Logical Channel for which we issue CRCX + */ +int rsl_ipacc_crcx(struct gsm_lchan *lchan) +{ + struct msgb *msg = rsl_msgb_alloc(); + struct abis_rsl_dchan_hdr *dh; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_IPAC_CRCX); + dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; + dh->chan_nr = gsm_lchan2chan_nr(lchan); + + /* 0x1- == receive-only, 0x-1 == EFR codec */ + lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan); + lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); + msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); + msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); + + DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x RTP_PAYLOAD=%d\n", + gsm_lchan_name(lchan), lchan->abis_ip.speech_mode, + lchan->abis_ip.rtp_payload); + + msg->dst = lchan->ts->trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +/*! \brief Issue IPA RSL MDCX to configure MGW-side of RTP + * \param[in] lchan Logical Channel for which we issue MDCX + * \param[in] ip Remote (MGW) IP address for RTP + * \param[in] port Remote (MGW) UDP port number for RTP + * \param[in] rtp_payload2 Contents of RTP PAYLOAD 2 IE + */ +int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip, uint16_t port, + uint8_t rtp_payload2) +{ + struct msgb *msg = rsl_msgb_alloc(); + struct abis_rsl_dchan_hdr *dh; + uint32_t *att_ip; + struct in_addr ia; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_IPAC_MDCX); + dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; + dh->chan_nr = gsm_lchan2chan_nr(lchan); + + /* we need to store these now as MDCX_ACK does not return them :( */ + lchan->abis_ip.rtp_payload2 = rtp_payload2; + lchan->abis_ip.connect_port = port; + lchan->abis_ip.connect_ip = ip; + + /* 0x0- == both directions, 0x-1 == EFR codec */ + lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan); + lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); + + ia.s_addr = htonl(ip); + DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD=%d RTP_PAYLOAD2=%d " + "CONN_ID=%d speech_mode=0x%02x\n", gsm_lchan_name(lchan), + inet_ntoa(ia), port, lchan->abis_ip.rtp_payload, rtp_payload2, + lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode); + + msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id); + msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP); + att_ip = (uint32_t *) msgb_put(msg, sizeof(ip)); + *att_ip = ia.s_addr; + msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port); + msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); + msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); + if (rtp_payload2) + msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2); + + msg->dst = lchan->ts->trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +/* tell BTS to connect RTP stream to our local RTP socket */ +int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan) +{ + struct rtp_socket *rs = lchan->abis_ip.rtp_socket; + int rc; + + rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr), + ntohs(rs->rtp.sin_local.sin_port), + /* FIXME: use RTP payload of bound socket, not BTS*/ + lchan->abis_ip.rtp_payload2); + + return rc; +} + +int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act) +{ + struct msgb *msg = rsl_msgb_alloc(); + struct abis_rsl_dchan_hdr *dh; + uint8_t msg_type; + + if (act) + msg_type = RSL_MT_IPAC_PDCH_ACT; + else + msg_type = RSL_MT_IPAC_PDCH_DEACT; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, msg_type); + dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; + dh->chan_nr = gsm_ts2chan_nr(ts, 0); + + DEBUGP(DRSL, "%s IPAC_PDCH_%sACT\n", gsm_ts_name(ts), + act ? "" : "DE"); + + msg->dst = ts->trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tv; + struct gsm_lchan *lchan = msg->lchan; + + /* the BTS has acknowledged a local bind, it now tells us the IP + * address and port number to which it has bound the given logical + * channel */ + + rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); + if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) || + !TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) || + !TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) { + LOGP(DRSL, LOGL_NOTICE, "mandatory IE missing"); + return -EINVAL; + } + + ipac_parse_rtp(lchan, &tv); + + osmo_signal_dispatch(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan); + + return 0; +} + +static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tv; + struct gsm_lchan *lchan = msg->lchan; + + /* the BTS has acknowledged a remote connect request and + * it now tells us the IP address and port number to which it has + * connected the given logical channel */ + + rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); + ipac_parse_rtp(lchan, &tv); + osmo_signal_dispatch(SS_ABISIP, S_ABISIP_MDCX_ACK, msg->lchan); + + return 0; +} + +static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tv; + + rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); + + if (TLVP_PRESENT(&tv, RSL_IE_CAUSE)) + print_rsl_cause(LOGL_DEBUG, TLVP_VAL(&tv, RSL_IE_CAUSE), + TLVP_LEN(&tv, RSL_IE_CAUSE)); + + osmo_signal_dispatch(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan); + + return 0; +} + +static int abis_rsl_rx_ipacc(struct msgb *msg) +{ + struct e1inp_sign_link *sign_link = msg->dst; + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + char *ts_name; + int rc = 0; + + msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr); + ts_name = gsm_lchan_name(msg->lchan); + + switch (rllh->c.msg_type) { + case RSL_MT_IPAC_CRCX_ACK: + DEBUGP(DRSL, "%s IPAC_CRCX_ACK ", ts_name); + rc = abis_rsl_rx_ipacc_crcx_ack(msg); + break; + case RSL_MT_IPAC_CRCX_NACK: + /* somehow the BTS was unable to bind the lchan to its local + * port?!? */ + LOGP(DRSL, LOGL_ERROR, "%s IPAC_CRCX_NACK\n", ts_name); + break; + case RSL_MT_IPAC_MDCX_ACK: + /* the BTS tells us that a connect operation was successful */ + DEBUGP(DRSL, "%s IPAC_MDCX_ACK ", ts_name); + rc = abis_rsl_rx_ipacc_mdcx_ack(msg); + break; + case RSL_MT_IPAC_MDCX_NACK: + /* somehow the BTS was unable to connect the lchan to a remote + * port */ + LOGP(DRSL, LOGL_ERROR, "%s IPAC_MDCX_NACK\n", ts_name); + break; + case RSL_MT_IPAC_DLCX_IND: + DEBUGP(DRSL, "%s IPAC_DLCX_IND ", ts_name); + rc = abis_rsl_rx_ipacc_dlcx_ind(msg); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n", + rllh->c.msg_type); + break; + } + DEBUGPC(DRSL, "\n"); + + return rc; +} + + +/* Entry-point where L2 RSL from BTS enters */ +int abis_rsl_rcvmsg(struct msgb *msg) +{ + struct abis_rsl_common_hdr *rslh; + int rc = 0; + + if (!msg) { + DEBUGP(DRSL, "Empty RSL msg?..\n"); + return -1; + } + + if (msgb_l2len(msg) < sizeof(*rslh)) { + DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg)); + msgb_free(msg); + return -1; + } + + rslh = msgb_l2(msg); + + switch (rslh->msg_discr & 0xfe) { + case ABIS_RSL_MDISC_RLL: + rc = abis_rsl_rx_rll(msg); + break; + case ABIS_RSL_MDISC_DED_CHAN: + rc = abis_rsl_rx_dchan(msg); + break; + case ABIS_RSL_MDISC_COM_CHAN: + rc = abis_rsl_rx_cchan(msg); + break; + case ABIS_RSL_MDISC_TRX: + rc = abis_rsl_rx_trx(msg); + break; + case ABIS_RSL_MDISC_LOC: + LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL msg disc 0x%02x\n", + rslh->msg_discr); + break; + case ABIS_RSL_MDISC_IPACCESS: + rc = abis_rsl_rx_ipacc(msg); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator " + "0x%02x\n", rslh->msg_discr); + rc = -EINVAL; + } + msgb_free(msg); + return rc; +} + +int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number, + uint8_t cb_command, const uint8_t *data, int len) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *cb_cmd; + + cb_cmd = rsl_msgb_alloc(); + if (!cb_cmd) + return -1; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(cb_cmd, sizeof*dh); + init_dchan_hdr(dh, RSL_MT_SMS_BC_CMD); + dh->chan_nr = RSL_CHAN_SDCCH4_ACCH; /* TODO: check the chan config */ + + msgb_tv_put(cb_cmd, RSL_IE_CB_CMD_TYPE, cb_command); + msgb_tlv_put(cb_cmd, RSL_IE_SMSCB_MSG, len, data); + + cb_cmd->trx = bts->c0; + + return abis_rsl_sendmsg(cb_cmd); +} + +int rsl_nokia_si_begin(struct gsm_bts_trx *trx) +{ + struct abis_rsl_common_hdr *ch; + struct msgb *msg = rsl_msgb_alloc(); + + ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); + ch->msg_discr = ABIS_RSL_MDISC_TRX; + ch->msg_type = 0x40; /* Nokia SI Begin */ + + msg->dst = trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +int rsl_nokia_si_end(struct gsm_bts_trx *trx) +{ + struct abis_rsl_common_hdr *ch; + struct msgb *msg = rsl_msgb_alloc(); + + ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); + ch->msg_discr = ABIS_RSL_MDISC_TRX; + ch->msg_type = 0x41; /* Nokia SI End */ + + msgb_tv_put(msg, 0xFD, 0x00); /* Nokia Pagemode Info, No paging reorganisation required */ + + msg->dst = trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction) +{ + struct abis_rsl_common_hdr *ch; + struct msgb *msg = rsl_msgb_alloc(); + + ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); + ch->msg_discr = ABIS_RSL_MDISC_DED_CHAN; + ch->msg_type = RSL_MT_BS_POWER_CONTROL; + + msgb_tv_put(msg, RSL_IE_CHAN_NR, channel); + msgb_tv_put(msg, RSL_IE_BS_POWER, reduction); /* reduction in 2dB steps */ + + msg->dst = trx->rsl_link; + + return abis_rsl_sendmsg(msg); +} + +/** + * Release all allocated SAPIs starting from @param start and + * release them with the given release mode. Once the release + * confirmation arrives it will be attempted to release the + * the RF channel. + */ +int rsl_release_sapis_from(struct gsm_lchan *lchan, int start, + enum rsl_rel_mode release_mode) +{ + int no_sapi = 1; + int sapi; + + for (sapi = start; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { + uint8_t link_id; + if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) + continue; + + link_id = sapi; + if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H) + link_id |= 0x40; + rsl_release_request(lchan, link_id, release_mode); + no_sapi = 0; + } + + return no_sapi; +} + +int rsl_start_t3109(struct gsm_lchan *lchan) +{ + struct gsm_bts *bts = lchan->ts->trx->bts; + + /* Disabled, mostly legacy code */ + if (bts->network->T3109 == 0) + return -1; + + lchan->T3109.cb = t3109_expired; + lchan->T3109.data = lchan; + osmo_timer_schedule(&lchan->T3109, bts->network->T3109, 0); + return 0; +} + +/** + * \brief directly RF Channel Release the lchan + * + * When no SAPI was allocated, directly release the logical channel. This + * should only be called from chan_alloc.c on channel release handling. In + * case no SAPI was established the RF Channel can be directly released, + */ +int rsl_direct_rf_release(struct gsm_lchan *lchan) +{ + int i; + for (i = 0; i < ARRAY_SIZE(lchan->sapis); ++i) { + if (lchan->sapis[i] != LCHAN_SAPI_UNUSED) { + LOGP(DRSL, LOGL_ERROR, "%s SAPI(%d) still allocated.\n", + gsm_lchan_name(lchan), i); + return -1; + } + } + + /* Now release it */ + return rsl_rf_chan_release(lchan, 0, SACCH_NONE); +} diff --git a/src/libbsc/arfcn_range_encode.c b/src/libbsc/arfcn_range_encode.c new file mode 100644 index 000000000..02a75a53c --- /dev/null +++ b/src/libbsc/arfcn_range_encode.c @@ -0,0 +1,305 @@ +/* gsm 04.08 system information (si) encoding and decoding + * 3gpp ts 04.08 version 7.21.0 release 1998 / etsi ts 100 940 v7.21.0 */ + +/* + * (C) 2012 Holger Hans Peter Freyther + * (C) 2012 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include + +#include + +int greatest_power_of_2_lesser_or_equal_to(int index) +{ + int power_of_2 = 1; + + do { + power_of_2 *= 2; + } while (power_of_2 <= index); + + /* now go back one step */ + return power_of_2 / 2; +} + +static inline int mod(int data, int range) +{ + int res = data % range; + while (res < 0) + res += range; + return res; +} + +/** + * Determine at which index to split the ARFCNs to create an + * equally size partition for the given range. Return -1 if + * no such partition exists. + */ +int range_enc_find_index(const int range, const int *freqs, const int size) +{ + int i, j, n; + + const int RANGE_DELTA = (range - 1) / 2; + + for (i = 0; i < size; ++i) { + n = 0; + for (j = 0; j < size; ++j) { + if (mod(freqs[j] - freqs[i], range) <= RANGE_DELTA) + n += 1; + } + + if (n - 1 == (size - 1) / 2) + return i; + } + + return -1; +} + +/** + * Range encode the ARFCN list. + * \param range The range to use. + * \param arfcns The list of ARFCNs + * \param size The size of the list of ARFCNs + * \param out Place to store the W(i) output. + */ +int range_enc_arfcns(const int range, + const int *arfcns, int size, int *out, + const int index) +{ + int split_at; + int i; + + /* + * The below is a GNU extension and we can remove it when + * we move to a quicksort like in-situ swap with the pivot. + */ + int arfcns_left[size / 2]; + int arfcns_right[size / 2]; + int l_size; + int r_size; + int l_origin; + int r_origin; + + + /* Test the two recursion anchors and stop processing */ + if (size == 0) + return 0; + + if (size == 1) { + out[index] = 1 + arfcns[0]; + return 0; + } + + /* Now do the processing */ + split_at = range_enc_find_index(range, arfcns, size); + + /* we now know where to split */ + out[index] = 1 + arfcns[split_at]; + + /* calculate the work that needs to be done for the leafs */ + l_origin = mod(arfcns[split_at] + ((range - 1) / 2) + 1, range); + r_origin = mod(arfcns[split_at] + 1, range); + for (i = 0, l_size = 0, r_size = 0; i < size; ++i) { + if (mod(arfcns[i] - l_origin, range) < range / 2) + arfcns_left[l_size++] = mod(arfcns[i] - l_origin, range); + if (mod(arfcns[i] - r_origin, range) < range / 2) + arfcns_right[r_size++] = mod(arfcns[i] - r_origin, range); + } + + /* + * Now recurse and we need to make this iterative... but as the + * tree is balanced the stack will not be too deep. + */ + range_enc_arfcns(range / 2, arfcns_left, l_size, + out, index + greatest_power_of_2_lesser_or_equal_to(index + 1)); + range_enc_arfcns((range -1 ) / 2, arfcns_right, r_size, + out, index + (2 * greatest_power_of_2_lesser_or_equal_to(index + 1))); + return 0; +} + +/* + * The easiest is to use f0 == arfcns[0]. This means that under certain + * circumstances we can encode less ARFCNs than possible with an optimal f0. + * + * TODO: Solve the optimisation problem and pick f0 so that the max distance + * is the smallest. Taking into account the modulo operation. I think picking + * size/2 will be the optimal arfcn. + */ +/** + * This implements the range determination as described in GSM 04.08 J4. The + * result will be a base frequency f0 and the range to use. + * + * \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) + return ARFCN_RANGE_1024; + + return ARFCN_RANGE_INVALID; +} + +/* + * The below is easier is to write in four methods than + * to use the max_bits. The encoding is so screwed.. as + * the bits need to be put in place in the wrong order.. + */ +#define HIGH_BITS(w, index, bits, offset) \ + (w[index - 1] >> (bits - offset)) +#define LOW_BITS(w, index, bits, offset) \ + (w[index - 1]) + +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; +} + +int range_enc_range128(uint8_t *chan_list, int f0, int *w) +{ + chan_list[0] = 0x8C; + write_orig_arfcn(chan_list, f0); + + LOGP(DRR, LOGL_ERROR, "Range128 encoding is not implemented.\n"); + return -1; +} + +int range_enc_range256(uint8_t *chan_list, int f0, int *w) +{ + chan_list[0] = 0x8A; + write_orig_arfcn(chan_list, f0); + + LOGP(DRR, LOGL_ERROR, "Range256 encoding is not implemented.\n"); + return -1; +} + +int range_enc_range512(uint8_t *chan_list, int f0, int *w) +{ + struct gsm48_range_512 *range512; + write_orig_arfcn(chan_list, f0); + + range512 = (struct gsm48_range_512 *) &chan_list[0]; + range512->form_id = chan_list[0] = 0x44; + + /* W(1) */ + range512->w1_hi = HIGH_BITS(w, 1, 9, 7); + range512->w1_lo = LOW_BITS (w, 1, 9, 2); + /* W(2) */ + range512->w2_hi = HIGH_BITS(w, 2, 8, 6); + range512->w2_lo = LOW_BITS (w, 2, 8, 2); + /* W(3) */ + range512->w3_hi = HIGH_BITS(w, 3, 8, 6); + range512->w3_lo = LOW_BITS (w, 3, 8, 2); + /* W(4) */ + range512->w4_hi = HIGH_BITS(w, 4, 7, 6); + range512->w4_lo = LOW_BITS (w, 4, 7, 1); + /* W(5) */ + range512->w5 = HIGH_BITS(w, 5, 7, 7); + /* W(6) */ + range512->w6 = HIGH_BITS(w, 6, 7, 7); + /* W(7) */ + range512->w7_hi = HIGH_BITS(w, 7, 7, 1); + range512->w7_lo = LOW_BITS (w, 7, 7, 6); + /* W(8) */ + range512->w8_hi = HIGH_BITS(w, 8, 6, 2); + range512->w8_lo = LOW_BITS (w, 8, 6, 4); + /* W(9) */ + range512->w9_hi = HIGH_BITS(w, 9, 6, 4); + range512->w9_lo = LOW_BITS(w, 9, 6, 2); + /* W(10) */ + range512->w10 = HIGH_BITS(w, 10, 6, 6); + /* W(11) */ + range512->w11 = HIGH_BITS(w, 11, 6, 6); + /* W(12) */ + range512->w12_hi = HIGH_BITS(w, 12, 6, 2); + range512->w12_lo = LOW_BITS (w, 12, 6, 4); + /* W(13) */ + range512->w13_hi = HIGH_BITS(w, 13, 6, 4); + range512->w13_lo = LOW_BITS(w, 13, 6, 2); + /* W(14) */ + range512->w14 = HIGH_BITS(w, 14, 6, 6); + /* W(15) */ + range512->w15 = HIGH_BITS(w, 15, 6, 6); + /* W(16) */ + range512->w16_hi = HIGH_BITS(w, 16, 5, 2); + range512->w16_lo = HIGH_BITS(w, 16, 5, 3); + /* W(17) */ + range512->w17 = HIGH_BITS(w, 17, 5, 5); + + return 0; +} + +int range_enc_range1024(uint8_t *chan_list, int f0, int f0_included, int *w) +{ + chan_list[0] = 0x80 | (f0_included << 2); + + LOGP(DRR, LOGL_ERROR, "Range1024 encoding is not implemented.\n"); + return -1; +} + +int range_enc_filter_arfcns(const int range, int *arfcns, + const int size, const int f0, int *f0_included) +{ + int i, j = 0; + *f0_included = 0; + + if (range == ARFCN_RANGE_1024) { + for (i = 0; i < size; ++i) { + if (arfcns[i] == f0) { + *f0_included = 1; + continue; + } + + /* copy and subtract */ + arfcns[j++] = mod(arfcns[i] - 1, 1024); + } + } else { + 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) + continue; + + arfcns[j++] = mod(arfcns[i] - (f0 + 1), 1024); + } + } + + return j; +} diff --git a/src/libbsc/bsc_api.c b/src/libbsc/bsc_api.c new file mode 100644 index 000000000..bde13b929 --- /dev/null +++ b/src/libbsc/bsc_api.c @@ -0,0 +1,854 @@ +/* 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 + +#define GSM0808_T10_VALUE 6, 0 + +static LLIST_HEAD(sub_connections); + +static void rll_ind_cb(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind); +static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id); +static void handle_release(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); +static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); +static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); + +/* GSM 08.08 3.2.2.33 */ +static uint8_t lchan_to_chosen_channel(struct gsm_lchan *lchan) +{ + uint8_t channel_mode = 0, channel = 0; + + switch (lchan->tch_mode) { + case GSM48_CMODE_SPEECH_V1: + case GSM48_CMODE_SPEECH_EFR: + case GSM48_CMODE_SPEECH_AMR: + channel_mode = 0x9; + break; + case GSM48_CMODE_SIGN: + channel_mode = 0x8; + break; + case GSM48_CMODE_DATA_14k5: + channel_mode = 0xe; + break; + case GSM48_CMODE_DATA_12k0: + channel_mode = 0xb; + break; + case GSM48_CMODE_DATA_6k0: + channel_mode = 0xc; + break; + case GSM48_CMODE_DATA_3k6: + channel_mode = 0xd; + break; + } + + switch (lchan->type) { + case GSM_LCHAN_NONE: + channel = 0x0; + break; + case GSM_LCHAN_SDCCH: + channel = 0x1; + break; + case GSM_LCHAN_TCH_F: + channel = 0x8; + break; + case GSM_LCHAN_TCH_H: + channel = 0x9; + break; + case GSM_LCHAN_UNKNOWN: + default: + LOGP(DMSC, LOGL_ERROR, "Unknown lchan type: %u\n", lchan->type); + break; + } + + return channel_mode << 4 | channel; +} + +static uint8_t chan_mode_to_speech(struct gsm_lchan *lchan) +{ + int mode = 0; + + switch (lchan->tch_mode) { + case GSM48_CMODE_SPEECH_V1: + mode = 1; + break; + case GSM48_CMODE_SPEECH_EFR: + mode = 0x11; + break; + case GSM48_CMODE_SPEECH_AMR: + mode = 0x21; + break; + case GSM48_CMODE_SIGN: + case GSM48_CMODE_DATA_14k5: + case GSM48_CMODE_DATA_12k0: + case GSM48_CMODE_DATA_6k0: + case GSM48_CMODE_DATA_3k6: + default: + LOGP(DMSC, LOGL_ERROR, "Using non speech mode: %d\n", mode); + return 0; + break; + } + + /* assume to always do AMR HR on any TCH type */ + if (lchan->type == GSM_LCHAN_TCH_H || + lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) + mode |= 0x4; + + return mode; +} + +static void assignment_t10_timeout(void *_conn) +{ + struct bsc_api *api; + struct gsm_subscriber_connection *conn = + (struct gsm_subscriber_connection *) _conn; + + LOGP(DMSC, LOGL_ERROR, "Assigment T10 timeout on %p\n", conn); + + /* + * normal release on the secondary channel but only if the + * secondary_channel has not been released by the handle_chan_nack. + */ + if (conn->secondary_lchan) + lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); + conn->secondary_lchan = NULL; + + /* inform them about the failure */ + api = conn->bts->network->bsc_api; + api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); +} + +/** + * Handle the multirate config + */ +static void handle_mr_config(struct gsm_subscriber_connection *conn, + struct gsm_lchan *lchan) +{ + struct bsc_api *api; + api = conn->bts->network->bsc_api; + + if (api->mr_config) + return api->mr_config(conn, &lchan->mr_conf); + + lchan->mr_conf.ver = 1; + lchan->mr_conf.icmi = 1; + lchan->mr_conf.m5_90 = 1; +} + +/* + * Start a new assignment and make sure that it is completed within T10 either + * positively, negatively or by the timeout. + * + * 1.) allocate a new lchan + * 2.) copy the encryption key and other data from the + * old to the new channel. + * 3.) RSL Channel Activate this channel and wait + * + * -> Signal handler for the LCHAN + * 4.) Send GSM 04.08 assignment command to the MS + * + * -> Assignment Complete/Assignment Failure + * 5.) Release the SDCCH, continue signalling on the new link + */ +static int handle_new_assignment(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) +{ + struct gsm_lchan *new_lchan; + int chan_type; + + chan_type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H; + + new_lchan = lchan_alloc(conn->bts, chan_type, 0); + + if (!new_lchan) { + LOGP(DMSC, LOGL_NOTICE, "No free channel.\n"); + return -1; + } + + /* copy old data to the new channel */ + memcpy(&new_lchan->encr, &conn->lchan->encr, sizeof(new_lchan->encr)); + new_lchan->ms_power = conn->lchan->ms_power; + new_lchan->bs_power = conn->lchan->bs_power; + + /* copy new data to it */ + new_lchan->tch_mode = chan_mode; + new_lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; + + /* handle AMR correctly */ + if (chan_mode == GSM48_CMODE_SPEECH_AMR) + handle_mr_config(conn, new_lchan); + + if (rsl_chan_activate_lchan(new_lchan, 0x1, 0, 0) < 0) { + LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); + lchan_free(new_lchan); + return -1; + } + + /* remember that we have the channel */ + conn->secondary_lchan = new_lchan; + new_lchan->conn = conn; + + rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); + return 0; +} + +struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan) +{ + struct gsm_subscriber_connection *conn; + + conn = talloc_zero(lchan->ts->trx->bts->network, struct gsm_subscriber_connection); + if (!conn) + return NULL; + + /* Configure the time and start it so it will be closed */ + conn->lchan = lchan; + conn->bts = lchan->ts->trx->bts; + lchan->conn = conn; + llist_add_tail(&conn->entry, &sub_connections); + return conn; +} + +/* TODO: move subscriber put here... */ +void subscr_con_free(struct gsm_subscriber_connection *conn) +{ + if (!conn) + return; + + + if (conn->subscr) { + subscr_put(conn->subscr); + conn->subscr = NULL; + } + + + if (conn->ho_lchan) { + LOGP(DNM, LOGL_ERROR, "The ho_lchan should have been cleared.\n"); + conn->ho_lchan->conn = NULL; + } + + if (conn->lchan) { + LOGP(DNM, LOGL_ERROR, "The lchan should have been cleared.\n"); + conn->lchan->conn = NULL; + } + + if (conn->secondary_lchan) { + LOGP(DNM, LOGL_ERROR, "The secondary_lchan should have been cleared.\n"); + conn->secondary_lchan->conn = NULL; + } + + llist_del(&conn->entry); + talloc_free(conn); +} + +int bsc_api_init(struct gsm_network *network, struct bsc_api *api) +{ + network->bsc_api = api; + return 0; +} + +/*! \brief process incoming 08.08 DTAP from MSC (send via BTS to MS) */ +int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, + struct msgb *msg, int link_id, int allow_sacch) +{ + uint8_t sapi; + + + if (!conn->lchan) { + LOGP(DMSC, LOGL_ERROR, + "Called submit dtap without an lchan.\n"); + msgb_free(msg); + return -1; + } + + sapi = link_id & 0x7; + msg->lchan = conn->lchan; + msg->dst = msg->lchan->ts->trx->rsl_link; + + /* If we are on a TCH and need to submit a SMS (on SAPI=3) we need to use the SACH */ + if (allow_sacch && sapi != 0) { + if (conn->lchan->type == GSM_LCHAN_TCH_F || conn->lchan->type == GSM_LCHAN_TCH_H) + link_id |= 0x40; + } + + msg->l3h = msg->data; + /* is requested SAPI already up? */ + if (conn->lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) { + /* Establish L2 for additional SAPI */ + OBSC_LINKID_CB(msg) = link_id; + if (rll_establish(msg->lchan, sapi, rll_ind_cb, msg) != 0) { + msgb_free(msg); + send_sapi_reject(conn, link_id); + return -1; + } + return 0; + } else { + /* Directly forward via RLL/RSL to BTS */ + return rsl_data_request(msg, link_id); + } +} + +/* + * \brief Check if the given channel is compatible with the mode/fullrate + */ +static int chan_compat_with_mode(struct gsm_lchan *lchan, int chan_mode, int full_rate) +{ + switch (chan_mode) { + case GSM48_CMODE_SIGN: + /* signalling is always possible */ + return 1; + case GSM48_CMODE_SPEECH_V1: + case GSM48_CMODE_SPEECH_AMR: + case GSM48_CMODE_DATA_3k6: + case GSM48_CMODE_DATA_6k0: + /* these services can all run on TCH/H, but we may have + * an explicit override by the 'full_rate' argument */ + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return 1; + case GSM_LCHAN_TCH_H: + if (full_rate) + return 0; + else + return 1; + break; + default: + return 0; + } + break; + case GSM48_CMODE_DATA_12k0: + case GSM48_CMODE_DATA_14k5: + case GSM48_CMODE_SPEECH_EFR: + /* these services all explicitly require a TCH/F */ + if (lchan->type == GSM_LCHAN_TCH_F) + return 1; + else + return 0; + break; + } + + return 0; +} + +/** + * Send a GSM08.08 Assignment Request. Right now this does not contain the + * audio codec type or the allowed rates for the config. It is assumed that + * this is for audio handling only. In case the current channel does not allow + * the selected mode a new one will be allocated. + * + * TODO: Add multirate configuration, make it work for more than audio. + */ +int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) +{ + struct bsc_api *api; + api = conn->bts->network->bsc_api; + + if (!chan_compat_with_mode(conn->lchan, chan_mode, full_rate)) { + if (handle_new_assignment(conn, chan_mode, full_rate) != 0) + goto error; + } else { + LOGP(DMSC, LOGL_NOTICE, + "Sending ChanModify for speech %d %d\n", chan_mode, full_rate); + if (chan_mode == GSM48_CMODE_SPEECH_AMR) + handle_mr_config(conn, conn->lchan); + + gsm48_lchan_modify(conn->lchan, chan_mode); + } + + /* we will now start the timer to complete the assignment */ + conn->T10.cb = assignment_t10_timeout; + conn->T10.data = conn; + osmo_timer_schedule(&conn->T10, GSM0808_T10_VALUE); + return 0; + +error: + api->assign_fail(conn, 0, NULL); + return -1; +} + +int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len, + uint8_t *mi, int chan_type) +{ + return rsl_paging_cmd(bts, page_group, mi_len, mi, chan_type); +} + +static void handle_ass_compl(struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct gsm48_hdr *gh; + struct bsc_api *api = conn->bts->network->bsc_api; + + if (conn->secondary_lchan != msg->lchan) { + LOGP(DMSC, LOGL_ERROR, "Assignment Compl should occur on second lchan.\n"); + return; + } + + gh = msgb_l3(msg); + if (msgb_l3len(msg) - sizeof(*gh) != 1) { + LOGP(DMSC, LOGL_ERROR, "Assignment Compl invalid: %u\n", + msgb_l3len(msg) - sizeof(*gh)); + return; + } + + /* swap channels */ + osmo_timer_del(&conn->T10); + + lchan_release(conn->lchan, 0, RSL_REL_LOCAL_END); + conn->lchan = conn->secondary_lchan; + conn->secondary_lchan = NULL; + + if (is_ipaccess_bts(conn->bts) && conn->lchan->tch_mode != GSM48_CMODE_SIGN) + rsl_ipacc_crcx(conn->lchan); + + api->assign_compl(conn, gh->data[0], + lchan_to_chosen_channel(conn->lchan), + conn->lchan->encr.alg_id, + chan_mode_to_speech(conn->lchan)); +} + +static void handle_ass_fail(struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct bsc_api *api = conn->bts->network->bsc_api; + uint8_t *rr_failure; + struct gsm48_hdr *gh; + + + if (conn->lchan != msg->lchan) { + LOGP(DMSC, LOGL_ERROR, "Assignment failure should occur on primary lchan.\n"); + return; + } + + /* stop the timer and release it */ + osmo_timer_del(&conn->T10); + lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); + conn->secondary_lchan = NULL; + + gh = msgb_l3(msg); + if (msgb_l3len(msg) - sizeof(*gh) != 1) { + LOGP(DMSC, LOGL_ERROR, "assignemnt failure unhandled: %u\n", + msgb_l3len(msg) - sizeof(*gh)); + rr_failure = NULL; + } else { + rr_failure = &gh->data[0]; + } + + api->assign_fail(conn, + GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE, + rr_failure); +} + +static void handle_classmark_chg(struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + uint8_t cm2_len, cm3_len = 0; + uint8_t *cm2, *cm3 = NULL; + + DEBUGP(DRR, "CLASSMARK CHANGE "); + + /* classmark 2 */ + cm2_len = gh->data[0]; + cm2 = &gh->data[1]; + DEBUGPC(DRR, "CM2(len=%u) ", cm2_len); + + if (payload_len > cm2_len + 1) { + /* we must have a classmark3 */ + if (gh->data[cm2_len+1] != 0x20) { + DEBUGPC(DRR, "ERR CM3 TAG\n"); + return; + } + if (cm2_len > 3) { + DEBUGPC(DRR, "CM2 too long!\n"); + return; + } + + cm3_len = gh->data[cm2_len+2]; + cm3 = &gh->data[cm2_len+3]; + if (cm3_len > 14) { + DEBUGPC(DRR, "CM3 len %u too long!\n", cm3_len); + return; + } + DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len); + } + api->classmark_chg(conn, cm2, cm2_len, cm3, cm3_len); +} + +/* Chapter 9.1.16 Handover complete */ +static void handle_rr_ho_compl(struct msgb *msg) +{ + struct lchan_signal_data sig; + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DRR, "HANDOVER COMPLETE cause = %s\n", + rr_cause_name(gh->data[0])); + + sig.lchan = msg->lchan; + sig.mr = NULL; + osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, &sig); + /* FIXME: release old channel */ +} + +/* Chapter 9.1.17 Handover Failure */ +static void handle_rr_ho_fail(struct msgb *msg) +{ + struct lchan_signal_data sig; + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DRR, "HANDOVER FAILED cause = %s\n", + rr_cause_name(gh->data[0])); + + sig.lchan = msg->lchan; + sig.mr = NULL; + osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, &sig); + /* FIXME: release allocated new channel */ +} + + +static void dispatch_dtap(struct gsm_subscriber_connection *conn, + uint8_t link_id, struct msgb *msg) +{ + struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; + struct gsm48_hdr *gh; + uint8_t pdisc; + int rc; + + if (msgb_l3len(msg) < sizeof(*gh)) { + LOGP(DMSC, LOGL_ERROR, "Message too short for a GSM48 header.\n"); + return; + } + + gh = msgb_l3(msg); + pdisc = gh->proto_discr & 0x0f; + + /* the idea is to handle all RR messages here, and only hand + * MM/CC/SMS-CP/LCS up to the MSC. Some messages like PAGING + * RESPONSE or CM SERVICE REQUEST will not be covered here, as + * they are only possible in the first L3 message of each L2 + * channel, i.e. 'conn' will not exist and gsm0408_rcvmsg() + * will call api->compl_l3() for it */ + switch (pdisc) { + case GSM48_PDISC_RR: + switch (gh->msg_type) { + case GSM48_MT_RR_GPRS_SUSP_REQ: + DEBUGP(DRR, "GRPS SUSPEND REQUEST\n"); + break; + case GSM48_MT_RR_STATUS: + LOGP(DRR, LOGL_NOTICE, "RR STATUS (cause: %s)\n", + rr_cause_name(gh->data[0])); + break; + case GSM48_MT_RR_MEAS_REP: + /* This shouldn't actually end up here, as RSL treats + * L3 Info of 08.58 MEASUREMENT REPORT different by calling + * directly into gsm48_parse_meas_rep */ + LOGP(DMEAS, LOGL_ERROR, "DIRECT GSM48 MEASUREMENT REPORT ?!? "); + break; + case GSM48_MT_RR_HANDO_COMPL: + handle_rr_ho_compl(msg); + break; + case GSM48_MT_RR_HANDO_FAIL: + handle_rr_ho_fail(msg); + break; + case GSM48_MT_RR_CIPH_M_COMPL: + if (api->cipher_mode_compl) + api->cipher_mode_compl(conn, msg, + conn->lchan->encr.alg_id); + break; + case GSM48_MT_RR_ASS_COMPL: + handle_ass_compl(conn, msg); + break; + case GSM48_MT_RR_ASS_FAIL: + handle_ass_fail(conn, msg); + break; + case GSM48_MT_RR_CHAN_MODE_MODIF_ACK: + osmo_timer_del(&conn->T10); + rc = gsm48_rx_rr_modif_ack(msg); + if (rc < 0) { + api->assign_fail(conn, + GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, + NULL); + } else if (rc >= 0) { + api->assign_compl(conn, 0, + lchan_to_chosen_channel(conn->lchan), + conn->lchan->encr.alg_id, + chan_mode_to_speech(conn->lchan)); + } + break; + case GSM48_MT_RR_CLSM_CHG: + handle_classmark_chg(conn, msg); + break; + case GSM48_MT_RR_APP_INFO: + /* Passing RR APP INFO to MSC, not quite + * according to spec */ + if (api->dtap) + api->dtap(conn, link_id, msg); + break; + default: + /* Normally, a MSC should never receive RR + * messages, but we'd rather forward what we + * don't know than drop it... */ + LOGP(DRR, LOGL_NOTICE, "BSC: Passing unknown 04.08 " + "RR message type 0x%02x to MSC\n", gh->msg_type); + if (api->dtap) + api->dtap(conn, link_id, msg); + } + break; + default: + if (api->dtap) + api->dtap(conn, link_id, msg); + break; + } +} + +/*! \brief RSL has received a DATA INDICATION with L3 from MS */ +int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id) +{ + int rc; + struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; + struct gsm_lchan *lchan; + + lchan = msg->lchan; + if (lchan->state != LCHAN_S_ACTIVE) { + LOGP(DRSL, LOGL_INFO, "Got data in non active state(%s), " + "discarding.\n", gsm_lchans_name(lchan->state)); + return -1; + } + + + if (lchan->conn) { + /* if we already have a connection, forward via DTAP to + * MSC */ + dispatch_dtap(lchan->conn, link_id, msg); + } else { + /* allocate a new connection */ + rc = BSC_API_CONN_POL_REJECT; + lchan->conn = subscr_con_allocate(msg->lchan); + if (!lchan->conn) { + lchan_release(lchan, 1, RSL_REL_NORMAL); + return -1; + } + + /* fwd via bsc_api to send COMPLETE L3 INFO to MSC */ + rc = api->compl_l3(lchan->conn, msg, 0); + + if (rc != BSC_API_CONN_POL_ACCEPT) { + lchan->conn->lchan = NULL; + subscr_con_free(lchan->conn); + lchan_release(lchan, 1, RSL_REL_NORMAL); + } + } + + return 0; +} + +/*! \brief We received a GSM 08.08 CIPHER MODE from the MSC */ +int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher, + const uint8_t *key, int len, int include_imeisv) +{ + if (cipher > 0 && key == NULL) { + LOGP(DRSL, LOGL_ERROR, "Need to have an encrytpion key.\n"); + return -1; + } + + if (len > MAX_A5_KEY_LEN) { + LOGP(DRSL, LOGL_ERROR, "The key is too long: %d\n", len); + return -1; + } + + conn->lchan->encr.alg_id = RSL_ENC_ALG_A5(cipher); + if (key) { + conn->lchan->encr.key_len = len; + memcpy(conn->lchan->encr.key, key, len); + } + + return gsm48_send_rr_ciph_mode(conn->lchan, include_imeisv); +} + +/* + * Release all occupied RF Channels but stay around for more. + */ +int gsm0808_clear(struct gsm_subscriber_connection *conn) +{ + if (conn->ho_lchan) + bsc_clear_handover(conn, 1); + + if (conn->secondary_lchan) + lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); + + if (conn->lchan) + lchan_release(conn->lchan, 1, RSL_REL_NORMAL); + + conn->lchan = NULL; + conn->secondary_lchan = NULL; + conn->ho_lchan = NULL; + conn->bts = NULL; + + osmo_timer_del(&conn->T10); + + return 0; +} + +static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id) +{ + struct bsc_api *api; + + if (!conn) + return; + + api = conn->bts->network->bsc_api; + if (!api || !api->sapi_n_reject) + return; + + api->sapi_n_reject(conn, link_id); +} + +static void rll_ind_cb(struct gsm_lchan *lchan, uint8_t link_id, void *_data, enum bsc_rllr_ind rllr_ind) +{ + struct msgb *msg = _data; + + /* + * There seems to be a small window that the RLL timer can + * fire after a lchan_release call and before the S_CHALLOC_FREED + * is called. Check if a conn is set before proceeding. + */ + if (!lchan->conn) + return; + + switch (rllr_ind) { + case BSC_RLLR_IND_EST_CONF: + rsl_data_request(msg, OBSC_LINKID_CB(msg)); + break; + case BSC_RLLR_IND_REL_IND: + case BSC_RLLR_IND_ERR_IND: + case BSC_RLLR_IND_TIMEOUT: + send_sapi_reject(lchan->conn, OBSC_LINKID_CB(msg)); + msgb_free(msg); + break; + } +} + +static int bsc_handle_lchan_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct bsc_api *bsc; + struct gsm_lchan *lchan; + struct lchan_signal_data *lchan_data; + + if (subsys != SS_LCHAN) + return 0; + + + lchan_data = signal_data; + if (!lchan_data->lchan || !lchan_data->lchan->conn) + return 0; + + lchan = lchan_data->lchan; + bsc = lchan->ts->trx->bts->network->bsc_api; + if (!bsc) + return 0; + + switch (signal) { + case S_LCHAN_UNEXPECTED_RELEASE: + handle_release(lchan->conn, bsc, lchan); + break; + case S_LCHAN_ACTIVATE_ACK: + handle_chan_ack(lchan->conn, bsc, lchan); + break; + case S_LCHAN_ACTIVATE_NACK: + handle_chan_nack(lchan->conn, bsc, lchan); + break; + } + + return 0; +} + +static void handle_release(struct gsm_subscriber_connection *conn, + struct bsc_api *bsc, struct gsm_lchan *lchan) +{ + int destruct = 1; + + if (conn->secondary_lchan == lchan) { + osmo_timer_del(&conn->T10); + conn->secondary_lchan = NULL; + + bsc->assign_fail(conn, + GSM0808_CAUSE_RADIO_INTERFACE_FAILURE, + NULL); + } + + /* clear the connection now */ + if (bsc->clear_request) + destruct = bsc->clear_request(conn, 0); + + /* now give up all channels */ + if (conn->lchan == lchan) + conn->lchan = NULL; + if (conn->ho_lchan == lchan) { + bsc_clear_handover(conn, 0); + conn->ho_lchan = NULL; + } + lchan->conn = NULL; + + gsm0808_clear(conn); + + if (destruct) + subscr_con_free(conn); +} + +static void handle_chan_ack(struct gsm_subscriber_connection *conn, + struct bsc_api *api, struct gsm_lchan *lchan) +{ + if (conn->secondary_lchan != lchan) + return; + + LOGP(DMSC, LOGL_NOTICE, "Sending assignment on chan: %p\n", lchan); + gsm48_send_rr_ass_cmd(conn->lchan, lchan, lchan->ms_power); +} + +static void handle_chan_nack(struct gsm_subscriber_connection *conn, + struct bsc_api *api, struct gsm_lchan *lchan) +{ + if (conn->secondary_lchan != lchan) + return; + + LOGP(DMSC, LOGL_ERROR, "Channel activation failed. Waiting for timeout now\n"); + conn->secondary_lchan->conn = NULL; + conn->secondary_lchan = NULL; +} + +static __attribute__((constructor)) void on_dso_load_bsc(void) +{ + osmo_signal_register_handler(SS_LCHAN, bsc_handle_lchan_signal, NULL); +} diff --git a/src/libbsc/bsc_init.c b/src/libbsc/bsc_init.c new file mode 100644 index 000000000..8fd72cf46 --- /dev/null +++ b/src/libbsc/bsc_init.c @@ -0,0 +1,501 @@ +/* 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 + +/* global pointer to the gsm network data structure */ +extern struct gsm_network *bsc_gsmnet; + +/* Callback function for NACK on the OML NM */ +static int oml_msg_nack(struct nm_nack_signal_data *nack) +{ + if (nack->mt == NM_MT_SET_BTS_ATTR_NACK) { + + LOGP(DNM, LOGL_ERROR, "Failed to set BTS attributes. That is fatal. " + "Was the bts type and frequency properly specified?\n"); + goto drop_bts; + } else { + LOGP(DNM, LOGL_ERROR, "Got a NACK going to drop the OML links.\n"); + goto drop_bts; + } + + return 0; + +drop_bts: + if (!nack->bts) { + LOGP(DNM, LOGL_ERROR, "Unknown bts. Can not drop it.\n"); + return 0; + } + + if (is_ipaccess_bts(nack->bts)) + ipaccess_drop_oml(nack->bts); + + return 0; +} + +/* Callback function to be called every time we receive a signal from NM */ +static int nm_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct nm_nack_signal_data *nack; + + switch (signal) { + case S_NM_NACK: + nack = signal_data; + return oml_msg_nack(nack); + default: + break; + } + return 0; +} + +int bsc_shutdown_net(struct gsm_network *net) +{ + struct gsm_bts *bts; + + llist_for_each_entry(bts, &net->bts_list, list) { + LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr); + osmo_signal_dispatch(SS_L_GLOBAL, S_GLOBAL_BTS_CLOSE_OM, bts); + } + + return 0; +} + +static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len) +{ + struct gsm_bts *bts = trx->bts; + int rc; + + DEBUGP(DRR, "SI%s: %s\n", get_value_string(osmo_sitype_strs, i), + osmo_hexdump(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN)); + + switch (i) { + case SYSINFO_TYPE_5: + case SYSINFO_TYPE_5bis: + case SYSINFO_TYPE_5ter: + case SYSINFO_TYPE_6: + rc = rsl_sacch_filling(trx, osmo_sitype2rsl(i), + GSM_BTS_SI(bts, i), si_len); + break; + default: + rc = rsl_bcch_info(trx, osmo_sitype2rsl(i), + GSM_BTS_SI(bts, i), si_len); + break; + } + + return rc; +} + +/* set all system information types */ +static int set_system_infos(struct gsm_bts_trx *trx) +{ + int i, rc; + struct gsm_bts *bts = trx->bts; + uint8_t gen_si[_MAX_SYSINFO_TYPE], n_si = 0, n; + int si_len[_MAX_SYSINFO_TYPE]; + + bts->si_common.cell_sel_par.ms_txpwr_max_ccch = + ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); + bts->si_common.cell_sel_par.neci = bts->network->neci; + + /* First, we determine which of the SI messages we actually need */ + + if (trx == bts->c0) { + /* 1...4 are always present on a C0 TRX */ + gen_si[n_si++] = SYSINFO_TYPE_1; + gen_si[n_si++] = SYSINFO_TYPE_2; + gen_si[n_si++] = SYSINFO_TYPE_2bis; + gen_si[n_si++] = SYSINFO_TYPE_2ter; + gen_si[n_si++] = SYSINFO_TYPE_3; + gen_si[n_si++] = SYSINFO_TYPE_4; + + /* 13 is always present on a C0 TRX of a GPRS BTS */ + if (bts->gprs.mode != BTS_GPRS_NONE) + gen_si[n_si++] = SYSINFO_TYPE_13; + } + + /* 5 and 6 are always present on every TRX */ + gen_si[n_si++] = SYSINFO_TYPE_5; + gen_si[n_si++] = SYSINFO_TYPE_5bis; + gen_si[n_si++] = SYSINFO_TYPE_5ter; + gen_si[n_si++] = SYSINFO_TYPE_6; + + /* Second, we generate the selected SI via RSL */ + + for (n = 0; n < n_si; n++) { + i = gen_si[n]; + /* Only generate SI if this SI is not in "static" (user-defined) mode */ + if (!(bts->si_mode_static & (1 << i))) { + /* Set SI as being valid. gsm_generate_si() might unset + * it, if SI is not required. */ + bts->si_valid |= (1 << i); + rc = gsm_generate_si(bts, i); + if (rc < 0) + goto err_out; + si_len[i] = rc; + } else { + if (i == SYSINFO_TYPE_5 || i == SYSINFO_TYPE_5bis + || i == SYSINFO_TYPE_5ter) + si_len[i] = 18; + else if (i == SYSINFO_TYPE_6) + si_len[i] = 11; + else + si_len[i] = 23; + } + } + + /* Third, we send the selected SI via RSL */ + + for (n = 0; n < n_si; n++) { + i = gen_si[n]; + if (!(bts->si_valid & (1 << i))) + continue; + rc = rsl_si(trx, i, si_len[i]); + if (rc < 0) + return rc; + } + + return 0; +err_out: + LOGP(DRR, LOGL_ERROR, "Cannot generate SI%s for BTS %u, most likely " + "a problem with neighbor cell list generation\n", + get_value_string(osmo_sitype_strs, i), bts->nr); + return rc; +} + +/* Produce a MA as specified in 10.5.2.21 */ +static int generate_ma_for_ts(struct gsm_bts_trx_ts *ts) +{ + /* we have three bitvecs: the per-timeslot ARFCNs, the cell chan ARFCNs + * and the MA */ + struct bitvec *cell_chan = &ts->trx->bts->si_common.cell_alloc; + struct bitvec *ts_arfcn = &ts->hopping.arfcns; + struct bitvec *ma = &ts->hopping.ma; + unsigned int num_cell_arfcns, bitnum, n_chan; + int i; + + /* re-set the MA to all-zero */ + ma->cur_bit = 0; + ts->hopping.ma_len = 0; + memset(ma->data, 0, ma->data_len); + + if (!ts->hopping.enabled) + return 0; + + /* count the number of ARFCNs in the cell channel allocation */ + num_cell_arfcns = 0; + for (i = 0; i < 1024; i++) { + if (bitvec_get_bit_pos(cell_chan, i)) + num_cell_arfcns++; + } + + /* pad it to octet-aligned number of bits */ + ts->hopping.ma_len = num_cell_arfcns / 8; + if (num_cell_arfcns % 8) + ts->hopping.ma_len++; + + n_chan = 0; + for (i = 0; i < 1024; i++) { + if (!bitvec_get_bit_pos(cell_chan, i)) + continue; + /* set the corresponding bit in the MA */ + bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; + if (bitvec_get_bit_pos(ts_arfcn, i)) + bitvec_set_bit_pos(ma, bitnum, 1); + else + bitvec_set_bit_pos(ma, bitnum, 0); + n_chan++; + } + + /* ARFCN 0 is special: It is coded last in the bitmask */ + if (bitvec_get_bit_pos(cell_chan, 0)) { + n_chan++; + /* set the corresponding bit in the MA */ + bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; + if (bitvec_get_bit_pos(ts_arfcn, 0)) + bitvec_set_bit_pos(ma, bitnum, 1); + else + bitvec_set_bit_pos(ma, bitnum, 0); + } + + return 0; +} + +static void bootstrap_rsl(struct gsm_bts_trx *trx) +{ + unsigned int i; + + LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) " + "on ARFCN %u using MCC=%u MNC=%u LAC=%u CID=%u BSIC=%u TSC=%u\n", + trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code, + bsc_gsmnet->network_code, trx->bts->location_area_code, + trx->bts->cell_identity, trx->bts->bsic, trx->bts->tsc); + + if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) { + rsl_nokia_si_begin(trx); + } + + set_system_infos(trx); + + if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) { + /* channel unspecific, power reduction in 2 dB steps */ + rsl_bs_power_control(trx, 0xFF, trx->max_power_red / 2); + rsl_nokia_si_end(trx); + } + + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) + generate_ma_for_ts(&trx->ts[i]); +} + +/* Callback function to be called every time we receive a signal from INPUT */ +static int inp_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct input_signal_data *isd = signal_data; + struct gsm_bts_trx *trx = isd->trx; + int ts_no, lchan_no; + + if (subsys != SS_L_INPUT) + return -EINVAL; + + switch (signal) { + case S_L_INP_TEI_UP: + if (isd->link_type == E1INP_SIGN_OML) { + /* TODO: this is required for the Nokia BTS, hopping is configured + during OML, other MA is not set. */ + struct gsm_bts_trx *cur_trx; + /* was static in system_information.c */ + extern int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts); + uint8_t ca[20]; + /* has to be called before generate_ma_for_ts to + set bts->si_common.cell_alloc */ + generate_cell_chan_list(ca, trx->bts); + + llist_for_each_entry(cur_trx, &trx->bts->trx_list, list) { + int i; + + for (i = 0; i < ARRAY_SIZE(cur_trx->ts); i++) + generate_ma_for_ts(&cur_trx->ts[i]); + } + } + if (isd->link_type == E1INP_SIGN_RSL) + bootstrap_rsl(trx); + break; + case S_L_INP_TEI_DN: + LOGP(DLMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx); + + if (isd->link_type == E1INP_SIGN_OML) + osmo_counter_inc(trx->bts->network->stats.bts.oml_fail); + else if (isd->link_type == E1INP_SIGN_RSL) + osmo_counter_inc(trx->bts->network->stats.bts.rsl_fail); + + /* + * free all allocated channels. change the nm_state so the + * trx and trx_ts becomes unusable and chan_alloc.c can not + * allocate from it. + */ + for (ts_no = 0; ts_no < ARRAY_SIZE(trx->ts); ++ts_no) { + struct gsm_bts_trx_ts *ts = &trx->ts[ts_no]; + + for (lchan_no = 0; lchan_no < ARRAY_SIZE(ts->lchan); ++lchan_no) { + if (ts->lchan[lchan_no].state != LCHAN_S_NONE) + lchan_free(&ts->lchan[lchan_no]); + lchan_reset(&ts->lchan[lchan_no]); + } + } + + gsm_bts_mo_reset(trx->bts); + + abis_nm_clear_queue(trx->bts); + break; + default: + break; + } + + return 0; +} + +static int bootstrap_bts(struct gsm_bts *bts) +{ + int i, n; + + if (bts->model->start && !bts->model->started) { + int ret = bts->model->start(bts->network); + if (ret < 0) + return ret; + + bts->model->started = true; + } + + /* FIXME: What about secondary TRX of a BTS? What about a BTS that has TRX + * in different bands? Why is 'band' a parameter of the BTS and not of the TRX? */ + switch (bts->band) { + case GSM_BAND_1800: + if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) { + LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n"); + return -EINVAL; + } + break; + case GSM_BAND_1900: + if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) { + LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n"); + return -EINVAL; + } + break; + case GSM_BAND_900: + if (bts->c0->arfcn < 1 || + (bts->c0->arfcn > 124 && bts->c0->arfcn < 955) || + bts->c0->arfcn > 1023) { + LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 1-124, 955-1023.\n"); + return -EINVAL; + } + break; + case GSM_BAND_850: + if (bts->c0->arfcn < 128 || bts->c0->arfcn > 251) { + LOGP(DNM, LOGL_ERROR, "GSM850 channel must be between 128-251.\n"); + return -EINVAL; + } + break; + default: + LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n"); + return -EINVAL; + } + + if (bts->network->auth_policy == GSM_AUTH_POLICY_ACCEPT_ALL && + !bts->si_common.rach_control.cell_bar) + LOGP(DNM, LOGL_ERROR, "\nWARNING: You are running an 'accept-all' " + "network on a BTS that is not barred. This " + "configuration is likely to interfere with production " + "GSM networks and should only be used in a RF " + "shielded environment such as a faraday cage!\n\n"); + + /* Control Channel Description is set from vty/config */ + + /* T3212 is set from vty/config */ + + /* Set ccch config by looking at ts config */ + for (n=0, i=0; i<8; i++) + n += bts->c0->ts[i].pchan == GSM_PCHAN_CCCH ? 1 : 0; + + switch (n) { + case 0: + bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C; + /* Limit reserved block to 2 on combined channel */ + if (bts->si_common.chan_desc.bs_ag_blks_res > 2) + bts->si_common.chan_desc.bs_ag_blks_res = 2; + break; + case 1: + bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_NC; + break; + case 2: + bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_2_NC; + break; + case 3: + bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_3_NC; + break; + case 4: + bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_4_NC; + break; + default: + LOGP(DNM, LOGL_ERROR, "Unsupported CCCH timeslot configuration\n"); + return -EINVAL; + } + + /* some defaults for our system information */ + bts->si_common.cell_options.radio_link_timeout = 7; /* 12 */ + + /* allow/disallow DTXu */ + if (bts->network->dtx_enabled) + bts->si_common.cell_options.dtx = 0; + else + bts->si_common.cell_options.dtx = 2; + + bts->si_common.cell_options.pwrc = 0; /* PWRC not set */ + + bts->si_common.cell_sel_par.acs = 0; + + bts->si_common.ncc_permitted = 0xff; + + /* Initialize the BTS state */ + gsm_bts_mo_reset(bts); + + return 0; +} + +int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *), + const char *config_file) +{ + struct telnet_connection dummy_conn; + struct gsm_bts *bts; + int rc; + + /* initialize our data structures */ + bsc_gsmnet = gsm_network_init(1, 1, mncc_recv); + if (!bsc_gsmnet) + return -ENOMEM; + + bsc_gsmnet->name_long = talloc_strdup(bsc_gsmnet, "OpenBSC"); + bsc_gsmnet->name_short = talloc_strdup(bsc_gsmnet, "OpenBSC"); + + /* our vty command code expects vty->priv to point to a telnet_connection */ + dummy_conn.priv = bsc_gsmnet; + rc = vty_read_config_file(config_file, &dummy_conn); + if (rc < 0) { + LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file); + return rc; + } + + rc = telnet_init(tall_bsc_ctx, bsc_gsmnet, 4242); + 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/src/libbsc/bsc_msc.c b/src/libbsc/bsc_msc.c new file mode 100644 index 000000000..66288a316 --- /dev/null +++ b/src/libbsc/bsc_msc.c @@ -0,0 +1,273 @@ +/* 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; + + 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 Connection timeout.\n"); + 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); + + if ((what & BSC_FD_WRITE) == 0) { + LOGP(DMSC, LOGL_ERROR, "Callback but not writable.\n"); + return -1; + } + + queue = container_of(fd, struct osmo_wqueue, bfd); + con = container_of(queue, struct bsc_msc_connection, write_queue); + + /* 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 socket failed.\n"); + goto error; + } + if (val != 0) { + LOGP(DMSC, LOGL_ERROR, "Not connected to the MSC: %d\n", val); + goto error; + } + + + /* go to full operation */ + fd->cb = 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.\n"); + 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 connections configured.\n"); + connection_loss(con); + return -1; + } + + /* move to the next connection */ + dest = (struct bsc_msc_dest *) con->dests->next; + llist_del(&dest->list); + llist_add_tail(&dest->list, con->dests); + + LOGP(DMSC, LOGL_NOTICE, "Attempting to connect MSC at %s:%d\n", + dest->ip, dest->port); + + con->is_connected = 0; + + fd = &con->write_queue.bfd; + fd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + fd->priv_nr = 1; + + if (fd->fd < 0) { + perror("Creating TCP socket failed"); + return fd->fd; + } + + /* make it non blocking */ + setnonblocking(fd); + + /* set the socket priority */ + ret = setsockopt(fd->fd, IPPROTO_IP, IP_TOS, + &dest->dscp, sizeof(dest->dscp)); + if (ret != 0) + LOGP(DMSC, LOGL_ERROR, "Failed to set DSCP to %d. %s\n", + dest->dscp, strerror(errno)); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(dest->port); + inet_aton(dest->ip, &sin.sin_addr); + + setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin)); + + if (ret == -1 && errno == EINPROGRESS) { + LOGP(DMSC, LOGL_ERROR, "MSC Connection in progress\n"); + fd->when = BSC_FD_WRITE; + fd->cb = msc_connection_connect; + con->timeout_timer.cb = msc_con_timeout; + con->timeout_timer.data = con; + 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; + 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); + + 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.\n"); + bsc_msc_connect(con); +} + +void bsc_msc_schedule_connect(struct bsc_msc_connection *con) +{ + LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC.\n"); + con->reconnect_timer.cb = reconnect_msc; + con->reconnect_timer.data = con; + osmo_timer_schedule(&con->reconnect_timer, 5, 0); +} + +struct msgb *bsc_msc_id_get_resp(const char *token) +{ + struct msgb *msg; + + if (!token) { + LOGP(DMSC, LOGL_ERROR, "No token specified.\n"); + return NULL; + } + + msg = msgb_alloc_headroom(4096, 128, "id resp"); + if (!msg) { + LOGP(DMSC, LOGL_ERROR, "Failed to create the message.\n"); + return NULL; + } + + msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP); + msgb_l16tv_put(msg, strlen(token) + 1, + IPAC_IDTAG_UNITNAME, (uint8_t *) token); + return msg; +} diff --git a/src/libbsc/bsc_rf_ctrl.c b/src/libbsc/bsc_rf_ctrl.c new file mode 100644 index 000000000..bd36e1884 --- /dev/null +++ b/src/libbsc/bsc_rf_ctrl.c @@ -0,0 +1,530 @@ +/* RF Ctl handling socket */ + +/* (C) 2010 by Harald Welte + * (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 + +#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 || !bsc_data->rf_ctrl) + return OSMO_BSC_RF_POLICY_UNKNOWN; + + switch (bsc_data->rf_ctrl->policy) { + case S_RF_ON: + return OSMO_BSC_RF_POLICY_ON; + case S_RF_OFF: + return OSMO_BSC_RF_POLICY_OFF; + case S_RF_GRACE: + return OSMO_BSC_RF_POLICY_GRACE; + default: + return OSMO_BSC_RF_POLICY_UNKNOWN; + } +} + +static int lock_each_trx(struct gsm_network *net, int lock) +{ + struct gsm_bts *bts; + + llist_for_each_entry(bts, &net->bts_list, list) { + struct gsm_bts_trx *trx; + + /* Exclude the BTS from the global lock */ + if (bts->excl_from_rf_lock) { + LOGP(DLINP, LOGL_DEBUG, + "Excluding BTS(%d) from trx lock.\n", bts->nr); + continue; + } + + llist_for_each_entry(trx, &bts->trx_list, list) { + gsm_trx_lock_rf(trx, lock); + } + } + + return 0; +} + +static void send_resp(struct osmo_bsc_rf_conn *conn, char send) +{ + struct msgb *msg; + + msg = msgb_alloc(10, "RF Query"); + if (!msg) { + LOGP(DLINP, LOGL_ERROR, "Failed to allocate response msg.\n"); + return; + } + + msg->l2h = msgb_put(msg, 1); + msg->l2h[0] = send; + + if (osmo_wqueue_enqueue(&conn->queue, msg) != 0) { + LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the answer.\n"); + msgb_free(msg); + return; + } + + return; +} + + +/* + * Send a + * 'g' when we are in grace mode + * '1' when one TRX is online, + * '0' otherwise + */ +static void handle_query(struct osmo_bsc_rf_conn *conn) +{ + struct gsm_bts *bts; + char send = RF_CMD_OFF; + + if (conn->rf->policy == S_RF_GRACE) + return send_resp(conn, RF_CMD_ON_G); + + llist_for_each_entry(bts, &conn->rf->gsm_network->bts_list, list) { + struct gsm_bts_trx *trx; + + /* Exclude the BTS from the global lock */ + if (bts->excl_from_rf_lock) { + LOGP(DLINP, LOGL_DEBUG, + "Excluding BTS(%d) from query.\n", bts->nr); + continue; + } + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->mo.nm_state.availability == NM_AVSTATE_OK && + trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) { + send = RF_CMD_ON; + break; + } + } + } + + send_resp(conn, send); +} + +static void rf_check_cb(void *_data) +{ + struct gsm_bts *bts; + struct osmo_bsc_rf *rf = _data; + + llist_for_each_entry(bts, &rf->gsm_network->bts_list, list) { + struct gsm_bts_trx *trx; + + /* don't bother to check a booting or missing BTS */ + if (!bts->oml_link || !is_ipaccess_bts(bts)) + continue; + + /* Exclude the BTS from the global lock */ + if (bts->excl_from_rf_lock) { + LOGP(DLINP, LOGL_DEBUG, + "Excluding BTS(%d) from query.\n", bts->nr); + continue; + } + + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->mo.nm_state.availability != NM_AVSTATE_OK || + trx->mo.nm_state.operational != NM_OPSTATE_ENABLED || + trx->mo.nm_state.administrative != NM_STATE_UNLOCKED) { + LOGP(DNM, LOGL_ERROR, "RF activation failed. Starting again.\n"); + ipaccess_drop_oml(bts); + break; + } + } + } +} + +static void send_signal(struct osmo_bsc_rf *rf, int val) +{ + struct rf_signal_data sig; + sig.net = rf->gsm_network; + + rf->policy = val; + osmo_signal_dispatch(SS_RF, val, &sig); +} + +static int switch_rf_off(struct osmo_bsc_rf *rf) +{ + lock_each_trx(rf->gsm_network, 1); + send_signal(rf, S_RF_OFF); + + return 0; +} + +static void grace_timeout(void *_data) +{ + struct osmo_bsc_rf *rf = (struct osmo_bsc_rf *) _data; + + LOGP(DLINP, LOGL_NOTICE, "Grace timeout. Going to disable all BTS/TRX.\n"); + switch_rf_off(rf); +} + +static int enter_grace(struct osmo_bsc_rf *rf) +{ + if (osmo_timer_pending(&rf->grace_timeout)) { + LOGP(DLINP, LOGL_NOTICE, "RF Grace timer is pending. Not restarting.\n"); + return 0; + } + + rf->grace_timeout.cb = grace_timeout; + rf->grace_timeout.data = rf; + osmo_timer_schedule(&rf->grace_timeout, rf->gsm_network->bsc_data->mid_call_timeout, 0); + LOGP(DLINP, LOGL_NOTICE, "Going to switch RF off in %d seconds.\n", + rf->gsm_network->bsc_data->mid_call_timeout); + + send_signal(rf, S_RF_GRACE); + return 0; +} + +static void rf_delay_cmd_cb(void *data) +{ + struct osmo_bsc_rf *rf = data; + + switch (rf->last_request) { + case RF_CMD_D_OFF: + rf->last_state_command = "RF Direct Off"; + osmo_timer_del(&rf->rf_check); + osmo_timer_del(&rf->grace_timeout); + switch_rf_off(rf); + break; + case RF_CMD_ON: + rf->last_state_command = "RF Direct On"; + osmo_timer_del(&rf->grace_timeout); + lock_each_trx(rf->gsm_network, 0); + send_signal(rf, S_RF_ON); + osmo_timer_schedule(&rf->rf_check, 3, 0); + break; + case RF_CMD_OFF: + rf->last_state_command = "RF Scheduled Off"; + osmo_timer_del(&rf->rf_check); + enter_grace(rf); + break; + } +} + +static int rf_read_cmd(struct osmo_fd *fd) +{ + struct osmo_bsc_rf_conn *conn = fd->data; + char buf[1]; + int rc; + + rc = read(fd->fd, buf, sizeof(buf)); + if (rc != sizeof(buf)) { + LOGP(DLINP, LOGL_ERROR, "Short read %d/%s\n", errno, strerror(errno)); + osmo_fd_unregister(fd); + close(fd->fd); + osmo_wqueue_clear(&conn->queue); + talloc_free(conn); + return -1; + } + + switch (buf[0]) { + case RF_CMD_QUERY: + handle_query(conn); + break; + case RF_CMD_D_OFF: + case RF_CMD_ON: + case RF_CMD_OFF: + osmo_bsc_rf_schedule_lock(conn->rf, buf[0]); + break; + default: + conn->rf->last_state_command = "Unknown command"; + LOGP(DLINP, LOGL_ERROR, "Unknown command %d\n", buf[0]); + break; + } + + return 0; +} + +static int rf_write_cmd(struct osmo_fd *fd, struct msgb *msg) +{ + int rc; + + rc = write(fd->fd, msg->data, msg->len); + if (rc != msg->len) { + LOGP(DLINP, LOGL_ERROR, "Short write %d/%s\n", errno, strerror(errno)); + return -1; + } + + return 0; +} + +static int rf_ctrl_accept(struct osmo_fd *bfd, unsigned int what) +{ + struct osmo_bsc_rf_conn *conn; + struct osmo_bsc_rf *rf = bfd->data; + struct sockaddr_un addr; + socklen_t len = sizeof(addr); + int fd; + + fd = accept(bfd->fd, (struct sockaddr *) &addr, &len); + if (fd < 0) { + LOGP(DLINP, LOGL_ERROR, "Failed to accept. errno: %d/%s\n", + errno, strerror(errno)); + return -1; + } + + conn = talloc_zero(rf, struct osmo_bsc_rf_conn); + if (!conn) { + LOGP(DLINP, LOGL_ERROR, "Failed to allocate mem.\n"); + close(fd); + return -1; + } + + osmo_wqueue_init(&conn->queue, 10); + conn->queue.bfd.data = conn; + conn->queue.bfd.fd = fd; + conn->queue.bfd.when = BSC_FD_READ | BSC_FD_WRITE; + conn->queue.read_cb = rf_read_cmd; + conn->queue.write_cb = rf_write_cmd; + conn->rf = rf; + + if (osmo_fd_register(&conn->queue.bfd) != 0) { + close(fd); + talloc_free(conn); + return -1; + } + + return 0; +} + +static void rf_auto_off_cb(void *_timer) +{ + struct osmo_bsc_rf *rf = _timer; + + LOGP(DLINP, LOGL_NOTICE, + "Going to switch off RF due lack of a MSC connection.\n"); + osmo_bsc_rf_schedule_lock(rf, RF_CMD_D_OFF); +} + +static int msc_signal_handler(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_network *net; + struct msc_signal_data *msc; + struct osmo_bsc_rf *rf; + + /* check if we want to handle this signal */ + if (subsys != SS_MSC) + return 0; + + net = handler_data; + msc = signal_data; + + /* check if we have the needed information */ + if (!net->bsc_data || !net->bsc_data->rf_ctrl) + 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; +} + +struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net) +{ + unsigned int namelen; + struct sockaddr_un local; + struct osmo_fd *bfd; + struct osmo_bsc_rf *rf; + int rc; + + rf = talloc_zero(NULL, struct osmo_bsc_rf); + if (!rf) { + LOGP(DLINP, LOGL_ERROR, "Failed to create osmo_bsc_rf.\n"); + return NULL; + } + + 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 NULL; + } + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + unlink(local.sun_path); + + /* we use the same magic that X11 uses in Xtranssock.c for + * calculating the proper length of the sockaddr */ +#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) + local.sun_len = strlen(local.sun_path); +#endif +#if defined(BSD44SOCKETS) || defined(SUN_LEN) + namelen = SUN_LEN(&local); +#else + namelen = strlen(local.sun_path) + + offsetof(struct sockaddr_un, sun_path); +#endif + + rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); + if (rc != 0) { + LOGP(DLINP, LOGL_ERROR, "Failed to bind '%s' errno: %d/%s\n", + local.sun_path, errno, strerror(errno)); + close(bfd->fd); + talloc_free(rf); + return NULL; + } + + if (listen(bfd->fd, 0) != 0) { + LOGP(DLINP, LOGL_ERROR, "Failed to listen: %d/%s\n", errno, strerror(errno)); + close(bfd->fd); + talloc_free(rf); + return NULL; + } + + 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); + talloc_free(rf); + return NULL; + } + + rf->gsm_network = net; + rf->policy = S_RF_ON; + rf->last_state_command = ""; + + /* check the rf state */ + rf->rf_check.data = rf; + rf->rf_check.cb = rf_check_cb; + + /* delay cmd handling */ + rf->delay_cmd.data = rf; + rf->delay_cmd.cb = rf_delay_cmd_cb; + + rf->auto_off_timer.data = rf; + rf->auto_off_timer.cb = rf_auto_off_cb; + + /* listen to RF signals */ + osmo_signal_register_handler(SS_MSC, msc_signal_handler, net); + + return rf; +} + +void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd) +{ + rf->last_request = cmd; + if (!osmo_timer_pending(&rf->delay_cmd)) + osmo_timer_schedule(&rf->delay_cmd, 1, 0); +} diff --git a/src/libbsc/bsc_rll.c b/src/libbsc/bsc_rll.c new file mode 100644 index 000000000..d12aeeec9 --- /dev/null +++ b/src/libbsc/bsc_rll.c @@ -0,0 +1,141 @@ +/* GSM BSC Radio Link Layer API + * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct bsc_rll_req { + struct llist_head list; + struct osmo_timer_list timer; + + struct gsm_lchan *lchan; + uint8_t link_id; + + void (*cb)(struct gsm_lchan *lchan, uint8_t link_id, + void *data, enum bsc_rllr_ind); + void *data; +}; + +/* we only compare C1, C2 and SAPI */ +#define LINKID_MASK 0xC7 + +static LLIST_HEAD(bsc_rll_reqs); + +static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type) +{ + llist_del(&rllr->list); + rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type); + talloc_free(rllr); +} + +static void timer_cb(void *_rllr) +{ + struct bsc_rll_req *rllr = _rllr; + + complete_rllr(rllr, BSC_RLLR_IND_TIMEOUT); +} + +/* establish a RLL connection with given SAPI / priority */ +int rll_establish(struct gsm_lchan *lchan, uint8_t sapi, + void (*cb)(struct gsm_lchan *, uint8_t, void *, + enum bsc_rllr_ind), + void *data) +{ + struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req); + uint8_t link_id; + if (!rllr) + return -ENOMEM; + + link_id = sapi; + + /* If we are a TCH and not in signalling mode, we need to + * indicate that the new RLL connection is to be made on the SACCH */ + if ((lchan->type == GSM_LCHAN_TCH_F || + lchan->type == GSM_LCHAN_TCH_H) && sapi != 0) + link_id |= 0x40; + + rllr->lchan = lchan; + rllr->link_id = link_id; + rllr->cb = cb; + rllr->data = data; + + llist_add(&rllr->list, &bsc_rll_reqs); + + rllr->timer.cb = &timer_cb; + rllr->timer.data = rllr; + + osmo_timer_schedule(&rllr->timer, 10, 0); + + /* send the RSL RLL ESTablish REQuest */ + return rsl_establish_request(rllr->lchan, rllr->link_id); +} + +/* Called from RSL code in case we have received an indication regarding + * any RLL link */ +void rll_indication(struct gsm_lchan *lchan, 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/src/libbsc/bsc_vty.c b/src/libbsc/bsc_vty.c new file mode 100644 index 000000000..45df90f5f --- /dev/null +++ b/src/libbsc/bsc_vty.c @@ -0,0 +1,3142 @@ +/* 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 "../../bscconfig.h" + + +#define NETWORK_STR "Configure the GSM network\n" +#define CODE_CMD_STR "Code commands\n" +#define NAME_CMD_STR "Name Commands\n" +#define NAME_STR "Name to use\n" +#define LCHAN_NR_STR "Logical Channel Number\n" + + +/* FIXME: this should go to some common file */ +static const struct value_string gprs_ns_timer_strs[] = { + { 0, "tns-block" }, + { 1, "tns-block-retries" }, + { 2, "tns-reset" }, + { 3, "tns-reset-retries" }, + { 4, "tns-test" }, + { 5, "tns-alive" }, + { 6, "tns-alive-retries" }, + { 0, NULL } +}; + +static const struct value_string gprs_bssgp_cfg_strs[] = { + { 0, "blocking-timer" }, + { 1, "blocking-retries" }, + { 2, "unblocking-retries" }, + { 3, "reset-timer" }, + { 4, "reset-retries" }, + { 5, "suspend-timer" }, + { 6, "suspend-retries" }, + { 7, "resume-timer" }, + { 8, "resume-retries" }, + { 9, "capability-update-timer" }, + { 10, "capability-update-retries" }, + { 0, NULL } +}; + +static const struct value_string bts_neigh_mode_strs[] = { + { NL_MODE_AUTOMATIC, "automatic" }, + { NL_MODE_MANUAL, "manual" }, + { NL_MODE_MANUAL_SI5SEP, "manual-si5" }, + { 0, NULL } +}; + +const struct value_string bts_loc_fix_names[] = { + { BTS_LOC_FIX_INVALID, "invalid" }, + { BTS_LOC_FIX_2D, "fix2d" }, + { BTS_LOC_FIX_3D, "fix3d" }, + { 0, NULL } +}; + +struct cmd_node net_node = { + GSMNET_NODE, + "%s(config-net)# ", + 1, +}; + +struct cmd_node bts_node = { + BTS_NODE, + "%s(config-net-bts)# ", + 1, +}; + +struct cmd_node trx_node = { + TRX_NODE, + "%s(config-net-bts-trx)# ", + 1, +}; + +struct cmd_node ts_node = { + TS_NODE, + "%s(config-net-bts-trx-ts)# ", + 1, +}; + +extern struct gsm_network *bsc_gsmnet; + +struct gsm_network *gsmnet_from_vty(struct vty *v) +{ + /* In case we read from the config file, the vty->priv cannot + * point to a struct telnet_connection, and thus conn->priv + * will not point to the gsm_network structure */ +#if 0 + struct telnet_connection *conn = v->priv; + return (struct gsm_network *) conn->priv; +#else + return bsc_gsmnet; +#endif +} + +static int dummy_config_write(struct vty *v) +{ + return CMD_SUCCESS; +} + +static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms) +{ + vty_out(vty,"Oper '%s', Admin '%s', Avail '%s'%s", + abis_nm_opstate_name(nms->operational), + get_value_string(abis_nm_adm_state_names, nms->administrative), + abis_nm_avail_name(nms->availability), VTY_NEWLINE); +} + +static void dump_pchan_load_vty(struct vty *vty, char *prefix, + const struct pchan_load *pl) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) { + const struct load_counter *lc = &pl->pchan[i]; + unsigned int percent; + + if (lc->total == 0) + continue; + + percent = (lc->used * 100) / lc->total; + + vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix, + gsm_pchan_name(i), percent, lc->used, lc->total, + VTY_NEWLINE); + } +} + +static void net_dump_vty(struct vty *vty, struct gsm_network *net) +{ + struct pchan_load pl; + + vty_out(vty, "BSC is on Country Code %u, Network Code %u " + "and has %u BTS%s", net->country_code, net->network_code, + net->num_bts, VTY_NEWLINE); + vty_out(vty, " Long network name: '%s'%s", + net->name_long, VTY_NEWLINE); + vty_out(vty, " Short network name: '%s'%s", + net->name_short, VTY_NEWLINE); + vty_out(vty, " Authentication policy: %s%s", + gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE); + vty_out(vty, " Location updating reject cause: %u%s", + net->reject_cause, VTY_NEWLINE); + vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption, + VTY_NEWLINE); + vty_out(vty, " NECI (TCH/H): %u%s", net->neci, + VTY_NEWLINE); + vty_out(vty, " Use TCH for Paging any: %d%s", net->pag_any_tch, + VTY_NEWLINE); + vty_out(vty, " RRLP Mode: %s%s", rrlp_mode_name(net->rrlp.mode), + VTY_NEWLINE); + vty_out(vty, " MM Info: %s%s", net->send_mm_info ? "On" : "Off", + VTY_NEWLINE); + vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off", + VTY_NEWLINE); + network_chan_load(&pl, net); + vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); + dump_pchan_load_vty(vty, " ", &pl); + + /* show rf */ + if (net->bsc_data && net->bsc_data->rf_ctrl) + vty_out(vty, " Last RF Command: %s%s", + net->bsc_data->rf_ctrl->last_state_command, + VTY_NEWLINE); +} + +DEFUN(show_net, show_net_cmd, "show network", + SHOW_STR "Display information about a GSM NETWORK\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + net_dump_vty(vty, net); + + return CMD_SUCCESS; +} + +static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l) +{ + struct e1inp_line *line; + + if (!e1l) { + vty_out(vty, " None%s", VTY_NEWLINE); + return; + } + + line = e1l->ts->line; + + vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s", + line->num, line->driver->name, e1l->ts->num, + e1inp_signtype_name(e1l->type), VTY_NEWLINE); + vty_out(vty, " E1 TEI %u, SAPI %u%s", + e1l->tei, e1l->sapi, VTY_NEWLINE); +} + +static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) +{ + struct pchan_load pl; + + vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, " + "BSIC %u, TSC %u and %u TRX%s", + bts->nr, btstype2str(bts->type), gsm_band_name(bts->band), + bts->cell_identity, + bts->location_area_code, bts->bsic, bts->tsc, + bts->num_trx, VTY_NEWLINE); + vty_out(vty, "Description: %s%s", + bts->description ? bts->description : "(null)", VTY_NEWLINE); + vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE); + vty_out(vty, "Minimum Rx Level for Access: %i dBm%s", + rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min), + VTY_NEWLINE); + vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s", + bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); + vty_out(vty, "RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer, + VTY_NEWLINE); + vty_out(vty, "RACH Max transmissions: %u%s", + rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), + VTY_NEWLINE); + if (bts->si_common.rach_control.cell_bar) + vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE); + vty_out(vty, "Channel Description Attachment: %s%s", + (bts->si_common.chan_desc.att) ? "yes" : "no", VTY_NEWLINE); + vty_out(vty, "Channel Description BS-PA-MFRMS: %u%s", + bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE); + vty_out(vty, "Channel Description BS-AG_BLKS-RES: %u%s", + bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE); + vty_out(vty, "System Information present: 0x%08x, static: 0x%08x%s", + bts->si_valid, bts->si_mode_static, VTY_NEWLINE); + if (is_ipaccess_bts(bts)) + vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s", + bts->ip_access.site_id, bts->ip_access.bts_id, + bts->oml_tei, VTY_NEWLINE); + else if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) + vty_out(vty, " Skip Reset: %d%s", + bts->nokia.skip_reset, VTY_NEWLINE); + vty_out(vty, " NM State: "); + net_dump_nmstate(vty, &bts->mo.nm_state); + vty_out(vty, " Site Mgr NM State: "); + net_dump_nmstate(vty, &bts->site_mgr.mo.nm_state); + vty_out(vty, " GPRS NSE: "); + net_dump_nmstate(vty, &bts->gprs.nse.mo.nm_state); + vty_out(vty, " GPRS CELL: "); + net_dump_nmstate(vty, &bts->gprs.cell.mo.nm_state); + vty_out(vty, " GPRS NSVC0: "); + net_dump_nmstate(vty, &bts->gprs.nsvc[0].mo.nm_state); + vty_out(vty, " GPRS NSVC1: "); + net_dump_nmstate(vty, &bts->gprs.nsvc[1].mo.nm_state); + vty_out(vty, " Paging: %u pending requests, %u free slots%s", + paging_pending_requests_nr(bts), + bts->paging.available_slots, VTY_NEWLINE); + if (is_ipaccess_bts(bts)) { + vty_out(vty, " OML Link state: %s.%s", + bts->oml_link ? "connected" : "disconnected", VTY_NEWLINE); + } else { + vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); + e1isl_dump_vty(vty, bts->oml_link); + } + + /* FIXME: chan_desc */ + memset(&pl, 0, sizeof(pl)); + bts_chan_load(&pl, bts); + vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); + dump_pchan_load_vty(vty, " ", &pl); +} + +DEFUN(show_bts, show_bts_cmd, "show bts [<0-255>]", + SHOW_STR "Display information about a BTS\n" + "BTS number") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + int bts_nr; + + if (argc != 0) { + /* use the BTS number that the user has specified */ + bts_nr = atoi(argv[0]); + if (bts_nr >= net->num_bts) { + vty_out(vty, "%% can't find BTS '%s'%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); + return CMD_SUCCESS; + } + /* print all BTS's */ + for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) + bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); + + return CMD_SUCCESS; +} + +/* utility functions */ +static void parse_e1_link(struct gsm_e1_subslot *e1_link, const char *line, + const char *ts, const char *ss) +{ + e1_link->e1_nr = atoi(line); + e1_link->e1_ts = atoi(ts); + if (!strcmp(ss, "full")) + e1_link->e1_ts_ss = 255; + else + e1_link->e1_ts_ss = atoi(ss); +} + +static void config_write_e1_link(struct vty *vty, struct gsm_e1_subslot *e1_link, + const char *prefix) +{ + if (!e1_link->e1_ts) + return; + + if (e1_link->e1_ts_ss == 255) + vty_out(vty, "%se1 line %u timeslot %u sub-slot full%s", + prefix, e1_link->e1_nr, e1_link->e1_ts, VTY_NEWLINE); + else + vty_out(vty, "%se1 line %u timeslot %u sub-slot %u%s", + prefix, e1_link->e1_nr, e1_link->e1_ts, + e1_link->e1_ts_ss, VTY_NEWLINE); +} + + +static void config_write_ts_single(struct vty *vty, struct gsm_bts_trx_ts *ts) +{ + vty_out(vty, " timeslot %u%s", ts->nr, VTY_NEWLINE); + if (ts->tsc != -1 && ts->tsc != ts->trx->bts->tsc) + vty_out(vty, " training_sequence_code %u%s", ts->tsc, VTY_NEWLINE); + if (ts->pchan != GSM_PCHAN_NONE) + vty_out(vty, " phys_chan_config %s%s", + gsm_pchan_name(ts->pchan), VTY_NEWLINE); + vty_out(vty, " hopping enabled %u%s", + ts->hopping.enabled, VTY_NEWLINE); + if (ts->hopping.enabled) { + unsigned int i; + vty_out(vty, " hopping sequence-number %u%s", + ts->hopping.hsn, VTY_NEWLINE); + vty_out(vty, " hopping maio %u%s", + ts->hopping.maio, VTY_NEWLINE); + for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { + if (!bitvec_get_bit_pos(&ts->hopping.arfcns, i)) + continue; + vty_out(vty, " hopping arfcn add %u%s", + i, VTY_NEWLINE); + } + } + config_write_e1_link(vty, &ts->e1_link, " "); + + if (ts->trx->bts->model->config_write_ts) + ts->trx->bts->model->config_write_ts(vty, ts); +} + +static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx) +{ + int i; + + vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE); + if (trx->description) + vty_out(vty, " description %s%s", trx->description, + VTY_NEWLINE); + vty_out(vty, " rf_locked %u%s", + trx->mo.nm_state.administrative == NM_STATE_LOCKED ? 1 : 0, + VTY_NEWLINE); + vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE); + vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE); + vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE); + config_write_e1_link(vty, &trx->rsl_e1_link, " rsl "); + vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE); + + if (trx->bts->model->config_write_trx) + trx->bts->model->config_write_trx(vty, trx); + + for (i = 0; i < TRX_NR_TS; i++) + config_write_ts_single(vty, &trx->ts[i]); +} + +static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts) +{ + unsigned int i; + vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode), + VTY_NEWLINE); + if (bts->gprs.mode == BTS_GPRS_NONE) + return; + + vty_out(vty, " gprs routing area %u%s", bts->gprs.rac, + VTY_NEWLINE); + vty_out(vty, " gprs network-control-order nc%u%s", + bts->gprs.net_ctrl_ord, VTY_NEWLINE); + vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci, + VTY_NEWLINE); + for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++) + vty_out(vty, " gprs cell timer %s %u%s", + get_value_string(gprs_bssgp_cfg_strs, i), + bts->gprs.cell.timer[i], VTY_NEWLINE); + vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei, + VTY_NEWLINE); + for (i = 0; i < ARRAY_SIZE(bts->gprs.nse.timer); i++) + vty_out(vty, " gprs ns timer %s %u%s", + get_value_string(gprs_ns_timer_strs, i), + bts->gprs.nse.timer[i], VTY_NEWLINE); + for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { + struct gsm_bts_gprs_nsvc *nsvc = + &bts->gprs.nsvc[i]; + struct in_addr ia; + + ia.s_addr = htonl(nsvc->remote_ip); + vty_out(vty, " gprs nsvc %u nsvci %u%s", i, + nsvc->nsvci, VTY_NEWLINE); + vty_out(vty, " gprs nsvc %u local udp port %u%s", i, + nsvc->local_port, VTY_NEWLINE); + vty_out(vty, " gprs nsvc %u remote udp port %u%s", i, + nsvc->remote_port, VTY_NEWLINE); + vty_out(vty, " gprs nsvc %u remote ip %s%s", i, + inet_ntoa(ia), VTY_NEWLINE); + } +} + +static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + int i; + + vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE); + vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE); + if (bts->description) + vty_out(vty, " description %s%s", bts->description, VTY_NEWLINE); + vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE); + vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE); + vty_out(vty, " location_area_code %u%s", bts->location_area_code, + VTY_NEWLINE); + vty_out(vty, " training_sequence_code %u%s", bts->tsc, VTY_NEWLINE); + vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE); + if (bts->tz.override != 0) + vty_out(vty, " timezone %d %d%s", bts->tz.hr, bts->tz.mn, VTY_NEWLINE); + vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE); + vty_out(vty, " cell reselection hysteresis %u%s", + bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); + vty_out(vty, " rxlev access min %u%s", + bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE); + + if (bts->si_common.cell_ro_sel_par.present) { + struct gsm48_si_selection_params *sp; + sp = &bts->si_common.cell_ro_sel_par; + + if (sp->cbq) + vty_out(vty, " cell bar qualify %u%s", + sp->cbq, VTY_NEWLINE); + + if (sp->cell_resel_off) + vty_out(vty, " cell reselection offset %u%s", + sp->cell_resel_off*2, VTY_NEWLINE); + + if (sp->temp_offs == 7) + vty_out(vty, " temporary offset infinite%s", + VTY_NEWLINE); + else if (sp->temp_offs) + vty_out(vty, " temporary offset %u%s", + sp->temp_offs*10, VTY_NEWLINE); + + if (sp->penalty_time == 31) + vty_out(vty, " penalty time reserved%s", + VTY_NEWLINE); + else if (sp->penalty_time) + vty_out(vty, " penalty time %u%s", + (sp->penalty_time*20)+20, VTY_NEWLINE); + } + + /* Is periodic LU enabled or disabled? */ + if (bts->si_common.chan_desc.t3212 == 0) + vty_out(vty, " no periodic location update%s", VTY_NEWLINE); + else + vty_out(vty, " periodic location update %u%s", + bts->si_common.chan_desc.t3212 * 6, VTY_NEWLINE); + + vty_out(vty, " 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); + for (i = SYSINFO_TYPE_1; i < _MAX_SYSINFO_TYPE; i++) { + if (bts->si_mode_static & (1 << i)) { + vty_out(vty, " system-information %s mode static%s", + get_value_string(osmo_sitype_strs, i), VTY_NEWLINE); + vty_out(vty, " system-information %s static %s%s", + get_value_string(osmo_sitype_strs, i), + osmo_hexdump_nospc(bts->si_buf[i], sizeof(bts->si_buf[i])), + VTY_NEWLINE); + } + } + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_OSMO_SYSMO: + vty_out(vty, " ip.access unit_id %u %u%s", + bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE); + if (bts->ip_access.rsl_ip) { + struct in_addr ia; + ia.s_addr = htonl(bts->ip_access.rsl_ip); + vty_out(vty, " ip.access rsl-ip %s%s", inet_ntoa(ia), + VTY_NEWLINE); + } + vty_out(vty, " oml ip.access stream_id %u line %u%s", + bts->oml_tei, bts->oml_e1_link.e1_nr, VTY_NEWLINE); + break; + case GSM_BTS_TYPE_NOKIA_SITE: + vty_out(vty, " nokia_site skip-reset %d%s", bts->nokia.skip_reset, VTY_NEWLINE); + break; + 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); + } + } + + config_write_bts_gprs(vty, bts); + + if (bts->excl_from_rf_lock) + vty_out(vty, " rf-lock-exclude%s", VTY_NEWLINE); + + if (bts->model->config_write_bts) + bts->model->config_write_bts(vty, bts); + + llist_for_each_entry(trx, &bts->trx_list, list) + config_write_trx_single(vty, trx); +} + +static int config_write_bts(struct vty *v) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(v); + struct gsm_bts *bts; + + llist_for_each_entry(bts, &gsmnet->bts_list, list) + config_write_bts_single(v, bts); + + return CMD_SUCCESS; +} + +static int config_write_net(struct vty *vty) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + vty_out(vty, "network%s", VTY_NEWLINE); + vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE); + vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE); + vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE); + vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE); + vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE); + vty_out(vty, " location updating reject cause %u%s", + gsmnet->reject_cause, VTY_NEWLINE); + vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE); + vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE); + vty_out(vty, " paging any use tch %d%s", gsmnet->pag_any_tch, VTY_NEWLINE); + vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode), + VTY_NEWLINE); + vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE); + vty_out(vty, " handover %u%s", gsmnet->handover.active, VTY_NEWLINE); + vty_out(vty, " handover window rxlev averaging %u%s", + gsmnet->handover.win_rxlev_avg, VTY_NEWLINE); + vty_out(vty, " handover window rxqual averaging %u%s", + gsmnet->handover.win_rxqual_avg, VTY_NEWLINE); + vty_out(vty, " handover window rxlev neighbor averaging %u%s", + gsmnet->handover.win_rxlev_avg_neigh, VTY_NEWLINE); + vty_out(vty, " handover power budget interval %u%s", + gsmnet->handover.pwr_interval, VTY_NEWLINE); + vty_out(vty, " handover power budget hysteresis %u%s", + gsmnet->handover.pwr_hysteresis, VTY_NEWLINE); + vty_out(vty, " handover maximum distance %u%s", + gsmnet->handover.max_distance, VTY_NEWLINE); + vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE); + vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE); + vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE); + vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE); + vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE); + vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE); + vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE); + vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE); + vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE); + vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE); + vty_out(vty, " timer t3122 %u%s", gsmnet->T3122, VTY_NEWLINE); + vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE); + vty_out(vty, " dtx-used %u%s", gsmnet->dtx_enabled, VTY_NEWLINE); + vty_out(vty, " subscriber-keep-in-ram %d%s", + gsmnet->keep_subscr, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx) +{ + vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s", + trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE); + vty_out(vty, "Description: %s%s", + trx->description ? trx->description : "(null)", VTY_NEWLINE); + vty_out(vty, " RF Nominal Power: %d dBm, reduced by %u dB, " + "resulting BS power: %d dBm%s", + trx->nominal_power, trx->max_power_red, + trx->nominal_power - trx->max_power_red, VTY_NEWLINE); + vty_out(vty, " NM State: "); + net_dump_nmstate(vty, &trx->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), + ts->tsc == -1 ? ts->trx->bts->tsc : ts->tsc); + if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) + vty_out(vty, " (%s mode)", + ts->flags & TS_F_PDCH_MODE ? "PDCH" : "TCH/F"); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, " NM State: "); + net_dump_nmstate(vty, &ts->mo.nm_state); + if (!is_ipaccess_bts(ts->trx->bts)) + vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s", + ts->e1_link.e1_nr, ts->e1_link.e1_ts, + ts->e1_link.e1_ts_ss, VTY_NEWLINE); +} + +DEFUN(show_ts, + show_ts_cmd, + "show timeslot [<0-255>] [<0-255>] [<0-7>]", + SHOW_STR "Display information about a TS\n" + "BTS Number\n" "TRX Number\n" "Timeslot Number\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + struct gsm_bts *bts = NULL; + struct gsm_bts_trx *trx = NULL; + struct gsm_bts_trx_ts *ts = NULL; + int bts_nr, trx_nr, ts_nr; + + if (argc >= 1) { + /* use the BTS number that the user has specified */ + bts_nr = atoi(argv[0]); + if (bts_nr >= net->num_bts) { + vty_out(vty, "%% can't find BTS '%s'%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + bts = gsm_bts_num(net, bts_nr); + } + if (argc >= 2) { + trx_nr = atoi(argv[1]); + if (trx_nr >= bts->num_trx) { + vty_out(vty, "%% can't find TRX '%s'%s", argv[1], + VTY_NEWLINE); + return CMD_WARNING; + } + trx = gsm_bts_trx_num(bts, trx_nr); + } + if (argc >= 3) { + ts_nr = atoi(argv[2]); + if (ts_nr >= TRX_NR_TS) { + vty_out(vty, "%% can't find TS '%s'%s", argv[2], + VTY_NEWLINE); + return CMD_WARNING; + } + /* Fully Specified: print and exit */ + ts = &trx->ts[ts_nr]; + ts_dump_vty(vty, ts); + return CMD_SUCCESS; + } + + if (bts && trx) { + /* Iterate over all TS in this TRX */ + for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { + ts = &trx->ts[ts_nr]; + ts_dump_vty(vty, ts); + } + } else if (bts) { + /* Iterate over all TRX in this BTS, TS in each TRX */ + for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { + trx = gsm_bts_trx_num(bts, trx_nr); + for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { + ts = &trx->ts[ts_nr]; + ts_dump_vty(vty, ts); + } + } + } else { + /* Iterate over all BTS, TRX in each BTS, TS in each TRX */ + for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { + bts = gsm_bts_num(net, bts_nr); + for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { + trx = gsm_bts_trx_num(bts, trx_nr); + for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { + ts = &trx->ts[ts_nr]; + ts_dump_vty(vty, ts); + } + } + } + } + + return CMD_SUCCESS; +} + +static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr) +{ + vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, + subscr->authorized, VTY_NEWLINE); + if (subscr->name) + vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); + if (subscr->extension) + vty_out(vty, " Extension: %s%s", subscr->extension, + VTY_NEWLINE); + vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); + if (subscr->tmsi != GSM_RESERVED_TMSI) + vty_out(vty, " TMSI: %08X%s", subscr->tmsi, + VTY_NEWLINE); + + vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); +} + +static void meas_rep_dump_uni_vty(struct vty *vty, + struct gsm_meas_rep_unidir *mru, + const char *prefix, + const char *dir) +{ + vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ", + prefix, dir, rxlev2dbm(mru->full.rx_lev), + dir, rxlev2dbm(mru->sub.rx_lev)); + vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s", + dir, mru->full.rx_qual, dir, mru->sub.rx_qual, + VTY_NEWLINE); +} + +static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr, + const char *prefix) +{ + vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE); + vty_out(vty, "%s Flags: %s%s%s%s%s", prefix, + mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "", + mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "", + mr->flags & MEAS_REP_F_FPC ? "FPC " : "", + mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ", + VTY_NEWLINE); + if (mr->flags & MEAS_REP_F_MS_TO) + vty_out(vty, "%s MS Timing Offset: %u%s", prefix, + mr->ms_timing_offset, VTY_NEWLINE); + if (mr->flags & MEAS_REP_F_MS_L1) + vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s", + prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE); + if (mr->flags & MEAS_REP_F_DL_VALID) + meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl"); + meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul"); +} + +static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan) +{ + int idx; + + vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u: Type %s%s", + lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, + lchan->nr, gsm_lchant_name(lchan->type), VTY_NEWLINE); + vty_out(vty, " Connection: %u, State: %s%s", + lchan->conn ? 1: 0, + gsm_lchans_name(lchan->state), VTY_NEWLINE); + vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s", + lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red + - lchan->bs_power*2, + ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power), + VTY_NEWLINE); + if (lchan->conn && lchan->conn->subscr) { + vty_out(vty, " Subscriber:%s", VTY_NEWLINE); + subscr_dump_vty(vty, lchan->conn->subscr); + } else + vty_out(vty, " No Subscriber%s", VTY_NEWLINE); + if (is_ipaccess_bts(lchan->ts->trx->bts)) { + struct in_addr ia; + ia.s_addr = htonl(lchan->abis_ip.bound_ip); + vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s", + inet_ntoa(ia), lchan->abis_ip.bound_port, + lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id, + VTY_NEWLINE); + } + + /* we want to report the last measurement report */ + idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), + lchan->meas_rep_idx, 1); + meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " "); +} + +static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan) +{ + struct gsm_meas_rep *mr; + int idx; + + /* we want to report the last measurement report */ + idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), + lchan->meas_rep_idx, 1); + mr = &lchan->meas_rep[idx]; + + vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u, Type %s - " + "L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s", + lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, + lchan->nr, gsm_lchant_name(lchan->type), mr->ms_l1.pwr, + rxlev2dbm(mr->dl.full.rx_lev), + rxlev2dbm(mr->ul.full.rx_lev), + VTY_NEWLINE); +} + + +static int dump_lchan_trx_ts(struct gsm_bts_trx_ts *ts, struct vty *vty, + void (*dump_cb)(struct vty *, struct gsm_lchan *)) +{ + int lchan_nr; + for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; lchan_nr++) { + struct gsm_lchan *lchan = &ts->lchan[lchan_nr]; + if ((lchan->type == GSM_LCHAN_NONE) && (lchan->state == LCHAN_S_NONE)) + continue; + dump_cb(vty, lchan); + } + + return CMD_SUCCESS; +} + +static int dump_lchan_trx(struct gsm_bts_trx *trx, struct vty *vty, + void (*dump_cb)(struct vty *, struct gsm_lchan *)) +{ + int ts_nr; + + for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { + struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; + dump_lchan_trx_ts(ts, vty, dump_cb); + } + + return CMD_SUCCESS; +} + +static int dump_lchan_bts(struct gsm_bts *bts, struct vty *vty, + void (*dump_cb)(struct vty *, struct gsm_lchan *)) +{ + int trx_nr; + + for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { + struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_nr); + dump_lchan_trx(trx, vty, dump_cb); + } + + return CMD_SUCCESS; +} + +static int lchan_summary(struct vty *vty, int argc, const char **argv, + void (*dump_cb)(struct vty *, struct gsm_lchan *)) +{ + struct gsm_network *net = gsmnet_from_vty(vty); + struct gsm_bts *bts; + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + struct gsm_lchan *lchan; + int bts_nr, trx_nr, ts_nr, lchan_nr; + + if (argc >= 1) { + /* use the BTS number that the user has specified */ + bts_nr = atoi(argv[0]); + if (bts_nr >= net->num_bts) { + vty_out(vty, "%% can't find BTS %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + bts = gsm_bts_num(net, bts_nr); + + if (argc == 1) + return dump_lchan_bts(bts, vty, dump_cb); + } + if (argc >= 2) { + trx_nr = atoi(argv[1]); + if (trx_nr >= bts->num_trx) { + vty_out(vty, "%% can't find TRX %s%s", argv[1], + VTY_NEWLINE); + return CMD_WARNING; + } + trx = gsm_bts_trx_num(bts, trx_nr); + + if (argc == 2) + return dump_lchan_trx(trx, vty, dump_cb); + } + if (argc >= 3) { + ts_nr = atoi(argv[2]); + if (ts_nr >= TRX_NR_TS) { + vty_out(vty, "%% can't find TS %s%s", argv[2], + VTY_NEWLINE); + return CMD_WARNING; + } + ts = &trx->ts[ts_nr]; + + if (argc == 3) + return dump_lchan_trx_ts(ts, vty, dump_cb); + } + if (argc >= 4) { + lchan_nr = atoi(argv[3]); + if (lchan_nr >= TS_MAX_LCHAN) { + vty_out(vty, "%% can't find LCHAN %s%s", argv[3], + VTY_NEWLINE); + return CMD_WARNING; + } + lchan = &ts->lchan[lchan_nr]; + dump_cb(vty, lchan); + return CMD_SUCCESS; + } + + + for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { + bts = gsm_bts_num(net, bts_nr); + dump_lchan_bts(bts, vty, dump_cb); + } + + return CMD_SUCCESS; +} + + +DEFUN(show_lchan, + show_lchan_cmd, + "show lchan [<0-255>] [<0-255>] [<0-7>] [lchan_nr]", + SHOW_STR "Display information about a logical channel\n" + "BTS Number\n" "TRX Number\n" "Timeslot Number\n" + LCHAN_NR_STR) + +{ + return lchan_summary(vty, argc, argv, lchan_dump_full_vty); +} + +DEFUN(show_lchan_summary, + show_lchan_summary_cmd, + "show lchan summary [<0-255>] [<0-255>] [<0-7>] [lchan_nr]", + SHOW_STR "Display information about a logical channel\n" + "Short summary\n" + "BTS Number\n" "TRX Number\n" "Timeslot Number\n" + LCHAN_NR_STR) +{ + return lchan_summary(vty, argc, argv, lchan_dump_short_vty); +} + +static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag) +{ + vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE); + subscr_dump_vty(vty, pag->subscr); +} + +static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts) +{ + struct gsm_paging_request *pag; + + if (!bts->paging.bts) + return; + + llist_for_each_entry(pag, &bts->paging.pending_requests, entry) + paging_dump_vty(vty, pag); +} + +DEFUN(show_paging, + show_paging_cmd, + "show paging [<0-255>]", + SHOW_STR "Display information about paging reuqests of a BTS\n" + "BTS Number\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + struct gsm_bts *bts; + int bts_nr; + + if (argc >= 1) { + /* use the BTS number that the user has specified */ + bts_nr = atoi(argv[0]); + if (bts_nr >= net->num_bts) { + vty_out(vty, "%% can't find BTS %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + bts = gsm_bts_num(net, bts_nr); + bts_paging_dump_vty(vty, bts); + + return CMD_SUCCESS; + } + for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { + bts = gsm_bts_num(net, bts_nr); + bts_paging_dump_vty(vty, bts); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_net, + cfg_net_cmd, + "network", NETWORK_STR) +{ + vty->index = gsmnet_from_vty(vty); + vty->node = GSMNET_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ncc, + cfg_net_ncc_cmd, + "network country code <1-999>", + "Set the GSM network country code\n" + "Country commands\n" + CODE_CMD_STR + "Network Country Code to use\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->country_code = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_mnc, + cfg_net_mnc_cmd, + "mobile network code <0-999>", + "Set the GSM mobile network code\n" + "Network Commands\n" + CODE_CMD_STR + "Mobile Network Code to use\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->network_code = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_name_short, + cfg_net_name_short_cmd, + "short name NAME", + "Set the short GSM network name\n" NAME_CMD_STR NAME_STR) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + bsc_replace_string(gsmnet, &gsmnet->name_short, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_name_long, + cfg_net_name_long_cmd, + "long name NAME", + "Set the long GSM network name\n" NAME_CMD_STR NAME_STR) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + bsc_replace_string(gsmnet, &gsmnet->name_long, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_auth_policy, + cfg_net_auth_policy_cmd, + "auth policy (closed|accept-all|token)", + "Authentication (not cryptographic)\n" + "Set the GSM network authentication policy\n" + "Require the MS to be activated in HLR\n" + "Accept all MS, whether in HLR or not\n" + "Use SMS-token based authentication\n") +{ + enum gsm_auth_policy policy = gsm_auth_policy_parse(argv[0]); + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->auth_policy = policy; + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_reject_cause, + cfg_net_reject_cause_cmd, + "location updating reject cause <2-111>", + "Set the reject cause of location updating reject\n" + "Set the reject cause of location updating reject\n" + "Set the reject cause of location updating reject\n" + "Set the reject cause of location updating reject\n" + "Cause Value as Per GSM TS 04.08\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->reject_cause = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_encryption, + cfg_net_encryption_cmd, + "encryption a5 (0|1|2|3)", + "Encryption options\n" + "A5 encryption\n" "A5/0: No encryption\n" + "A5/1: Encryption\n" "A5/2: Export-grade Encryption\n" + "A5/3: 'New' Secure Encryption\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->a5_encryption= atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_neci, + cfg_net_neci_cmd, + "neci (0|1)", + "New Establish Cause Indication\n" + "Don't set the NECI bit\n" "Set the NECI bit\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->neci = atoi(argv[0]); + gsm_net_update_ctype(gsmnet); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd, + "rrlp mode (none|ms-based|ms-preferred|ass-preferred)", + "Radio Resource Location Protocol\n" + "Set the Radio Resource Location Protocol Mode\n" + "Don't send RRLP request\n" + "Request MS-based location\n" + "Request any location, prefer MS-based\n" + "Request any location, prefer MS-assisted\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd, + "mm info (0|1)", + "Mobility Management\n" + "Send MM INFO after LOC UPD ACCEPT\n" + "Disable\n" "Enable\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->send_mm_info = atoi(argv[0]); + + return CMD_SUCCESS; +} + +#define HANDOVER_STR "Handover Options\n" + +DEFUN(cfg_net_handover, cfg_net_handover_cmd, + "handover (0|1)", + HANDOVER_STR + "Don't perform in-call handover\n" + "Perform in-call handover\n") +{ + int enable = atoi(argv[0]); + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + if (enable && ipacc_rtp_direct) { + vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode " + "is enabled by using the -P command line option%s", + VTY_NEWLINE); + return CMD_WARNING; + } + gsmnet->handover.active = enable; + + return CMD_SUCCESS; +} + +#define HO_WIN_STR HANDOVER_STR "Measurement Window\n" +#define HO_WIN_RXLEV_STR HO_WIN_STR "Received Level Averaging\n" +#define HO_WIN_RXQUAL_STR HO_WIN_STR "Received Quality Averaging\n" +#define HO_PBUDGET_STR HANDOVER_STR "Power Budget\n" +#define HO_AVG_COUNT_STR "Amount to use for Averaging\n" + +DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd, + "handover window rxlev averaging <1-10>", + HO_WIN_RXLEV_STR + "How many RxLev measurements are used for averaging\n" + HO_AVG_COUNT_STR) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->handover.win_rxlev_avg = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd, + "handover window rxqual averaging <1-10>", + HO_WIN_RXQUAL_STR + "How many RxQual measurements are used for averaging\n" + HO_AVG_COUNT_STR) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->handover.win_rxqual_avg = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd, + "handover window rxlev neighbor averaging <1-10>", + HO_WIN_RXLEV_STR "Neighbor\n" + "How many RxQual measurements are used for averaging\n" + HO_AVG_COUNT_STR) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd, + "handover power budget interval <1-99>", + HO_PBUDGET_STR + "How often to check if we have a better cell (SACCH frames)\n" + "Interval\n" "Number\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->handover.pwr_interval = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd, + "handover power budget hysteresis <0-999>", + HO_PBUDGET_STR + "How many dB does a neighbor to be stronger to become a HO candidate\n" + "Hysteresis\n" "Number\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->handover.pwr_hysteresis = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd, + "handover maximum distance <0-9999>", + HANDOVER_STR + "How big is the maximum timing advance before HO is forced\n" + "Distance\n" "Number\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->handover.max_distance = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_pag_any_tch, + cfg_net_pag_any_tch_cmd, + "paging any use tch (0|1)", + "Assign a TCH when receiving a Paging Any request\n" + "Any Channel\n" "Use\n" "TCH\n" + "Do not use TCH for Paging Request Any\n" + "Do use TCH for Paging Request Any\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->pag_any_tch = atoi(argv[0]); + gsm_net_update_ctype(gsmnet); + return CMD_SUCCESS; +} + +#define DECLARE_TIMER(number, doc) \ + DEFUN(cfg_net_T##number, \ + cfg_net_T##number##_cmd, \ + "timer t" #number " <0-65535>", \ + "Configure GSM Timers\n" \ + doc "Timer Value\n") \ +{ \ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); \ + int value = atoi(argv[0]); \ + \ + if (value < 0 || value > 65535) { \ + vty_out(vty, "Timer value %s out of range.%s", \ + argv[0], VTY_NEWLINE); \ + return CMD_WARNING; \ + } \ + \ + gsmnet->T##number = value; \ + return CMD_SUCCESS; \ +} + +DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT.\n") +DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.\n") +DECLARE_TIMER(3105, "Set the timer for repetition of PHYSICAL INFORMATION.\n") +DECLARE_TIMER(3107, "Currently not used.\n") +DECLARE_TIMER(3109, "Set the RSL SACCH deactivation timeout.\n") +DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF Channel.\n") +DECLARE_TIMER(3113, "Set the time to try paging a subscriber.\n") +DECLARE_TIMER(3115, "Currently not used.\n") +DECLARE_TIMER(3117, "Currently not used.\n") +DECLARE_TIMER(3119, "Currently not used.\n") +DECLARE_TIMER(3122, "Waiting time (seconds) after IMM ASS REJECT\n") +DECLARE_TIMER(3141, "Currently not used.\n") + +DEFUN(cfg_net_dtx, + cfg_net_dtx_cmd, + "dtx-used (0|1)", + "Enable the usage of DTX.\n" + "DTX is disabled\n" "DTX is enabled\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->dtx_enabled = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_subscr_keep, + cfg_net_subscr_keep_cmd, + "subscriber-keep-in-ram (0|1)", + "Keep unused subscribers in RAM.\n" + "Delete unused subscribers\n" "Keep unused subscribers\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->keep_subscr = atoi(argv[0]); + return CMD_SUCCESS; +} + +/* per-BTS configuration */ +DEFUN(cfg_bts, + cfg_bts_cmd, + "bts <0-255>", + "Select a BTS to configure\n" + "BTS Number\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + int bts_nr = atoi(argv[0]); + struct gsm_bts *bts; + + if (bts_nr > gsmnet->num_bts) { + vty_out(vty, "%% The next unused BTS number is %u%s", + gsmnet->num_bts, VTY_NEWLINE); + return CMD_WARNING; + } else if (bts_nr == gsmnet->num_bts) { + /* allocate a new one */ + bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_UNKNOWN, + HARDCODED_TSC, HARDCODED_BSIC); + } else + bts = gsm_bts_num(gsmnet, bts_nr); + + if (!bts) { + vty_out(vty, "%% Unable to allocate BTS %u%s", + gsmnet->num_bts, VTY_NEWLINE); + return CMD_WARNING; + } + + vty->index = bts; + vty->index_sub = &bts->description; + vty->node = BTS_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_type, + cfg_bts_type_cmd, + "type TYPE", /* dynamically created */ + "Set the BTS type\n" "Type\n") +{ + struct gsm_bts *bts = vty->index; + int rc; + + rc = gsm_set_bts_type(bts, parse_btstype(argv[0])); + if (rc < 0) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_band, + cfg_bts_band_cmd, + "band BAND", + "Set the frequency band of this BTS\n" "Frequency band\n") +{ + struct gsm_bts *bts = vty->index; + int band = gsm_band_parse(argv[0]); + + if (band < 0) { + vty_out(vty, "%% BAND %d is not a valid GSM band%s", + band, VTY_NEWLINE); + return CMD_WARNING; + } + + bts->band = band; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ci, + cfg_bts_ci_cmd, + "cell_identity <0-65535>", + "Set the Cell identity of this BTS\n" "Cell Identity\n") +{ + struct gsm_bts *bts = vty->index; + int ci = atoi(argv[0]); + + if (ci < 0 || ci > 0xffff) { + vty_out(vty, "%% CI %d is not in the valid range (0-65535)%s", + ci, VTY_NEWLINE); + return CMD_WARNING; + } + bts->cell_identity = ci; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_lac, + cfg_bts_lac_cmd, + "location_area_code <0-65535>", + "Set the Location Area Code (LAC) of this BTS\n" "LAC\n") +{ + struct gsm_bts *bts = vty->index; + int lac = atoi(argv[0]); + + if (lac < 0 || lac > 0xffff) { + vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s", + lac, VTY_NEWLINE); + return CMD_WARNING; + } + + if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { + vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", + lac, VTY_NEWLINE); + return CMD_WARNING; + } + + bts->location_area_code = lac; + + return CMD_SUCCESS; +} + + +DEFUN(cfg_bts_tsc, + cfg_bts_tsc_cmd, + "training_sequence_code <0-7>", + "Set the Training Sequence Code (TSC) of this BTS\n" "TSC\n") +{ + struct gsm_bts *bts = vty->index; + int tsc = atoi(argv[0]); + + bts->tsc = tsc; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_bsic, + cfg_bts_bsic_cmd, + "base_station_id_code <0-63>", + "Set the Base Station Identity Code (BSIC) of this BTS\n" + "BSIC of this BTS\n") +{ + struct gsm_bts *bts = vty->index; + int bsic = atoi(argv[0]); + + if (bsic < 0 || bsic > 0x3f) { + vty_out(vty, "%% BSIC %d is not in the valid range (0-255)%s", + bsic, VTY_NEWLINE); + return CMD_WARNING; + } + bts->bsic = bsic; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_timezone, + cfg_bts_timezone_cmd, + "timezone <-19-19> (0|15|30|45)", + "Set the Timezone Offset of this BTS\n" + "Timezone offset (hours)\n" + "Timezone offset (00 minutes)\n" + "Timezone offset (15 minutes)\n" + "Timezone offset (30 minutes)\n" + "Timezone offset (45 minutes)\n" + ) +{ + struct gsm_bts *bts = vty->index; + int tzhr = atoi(argv[0]); + int tzmn = atoi(argv[1]); + + bts->tz.hr = tzhr; + bts->tz.mn = tzmn; + bts->tz.override = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_no_timezone, + cfg_bts_no_timezone_cmd, + "no timezone", + NO_STR + "Disable BTS specific timezone\n") +{ + struct gsm_bts *bts = vty->index; + + bts->tz.override = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_unit_id, + cfg_bts_unit_id_cmd, + "ip.access unit_id <0-65534> <0-255>", + "Abis/IP specific options\n" + "Set the IPA BTS Unit ID\n" + "Unit ID (Site)\n" + "Unit ID (BTS)\n") +{ + struct gsm_bts *bts = vty->index; + int site_id = atoi(argv[0]); + int bts_id = atoi(argv[1]); + + if (!is_ipaccess_bts(bts)) { + vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->ip_access.site_id = site_id; + bts->ip_access.bts_id = bts_id; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_rsl_ip, + cfg_bts_rsl_ip_cmd, + "ip.access rsl-ip A.B.C.D", + "Abis/IP specific options\n" + "Set the IPA RSL IP Address of the BSC\n" + "Destination IP address for RSL connection\n") +{ + struct gsm_bts *bts = vty->index; + struct in_addr ia; + + if (!is_ipaccess_bts(bts)) { + vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + inet_aton(argv[0], &ia); + bts->ip_access.rsl_ip = ntohl(ia.s_addr); + + return CMD_SUCCESS; +} + + +DEFUN(cfg_bts_nokia_site_skip_reset, + cfg_bts_nokia_site_skip_reset_cmd, + "nokia_site skip-reset (0|1)", + "Nokia *Site related commands\n" + "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; +} + +#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_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; +} + +#define GPRS_TEXT "GPRS Packet Network\n" + +DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd, + "gprs cell bvci <2-65535>", + GPRS_TEXT + "GPRS Cell Settings\n" + "GPRS BSSGP VC Identifier\n" + "GPRS BSSGP VC Identifier") +{ + struct gsm_bts *bts = vty->index; + + if (bts->gprs.mode == BTS_GPRS_NONE) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.cell.bvci = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd, + "gprs nsei <0-65535>", + GPRS_TEXT + "GPRS NS Entity Identifier\n" + "GPRS NS Entity Identifier") +{ + struct gsm_bts *bts = vty->index; + + if (bts->gprs.mode == BTS_GPRS_NONE) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.nse.nsei = atoi(argv[0]); + + return CMD_SUCCESS; +} + +#define NSVC_TEXT "Network Service Virtual Connection (NS-VC)\n" \ + "NSVC Logical Number\n" + +DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd, + "gprs nsvc <0-1> nsvci <0-65535>", + GPRS_TEXT NSVC_TEXT + "NS Virtual Connection Identifier\n" + "GPRS NS VC Identifier") +{ + struct gsm_bts *bts = vty->index; + int idx = atoi(argv[0]); + + if (bts->gprs.mode == BTS_GPRS_NONE) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.nsvc[idx].nsvci = atoi(argv[1]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd, + "gprs nsvc <0-1> local udp port <0-65535>", + GPRS_TEXT NSVC_TEXT + "GPRS NS Local UDP Port\n" + "GPRS NS Local UDP Port\n" + "GPRS NS Local UDP Port\n" + "GPRS NS Local UDP Port Number\n") +{ + struct gsm_bts *bts = vty->index; + int idx = atoi(argv[0]); + + if (bts->gprs.mode == BTS_GPRS_NONE) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.nsvc[idx].local_port = atoi(argv[1]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd, + "gprs nsvc <0-1> remote udp port <0-65535>", + GPRS_TEXT NSVC_TEXT + "GPRS NS Remote UDP Port\n" + "GPRS NS Remote UDP Port\n" + "GPRS NS Remote UDP Port\n" + "GPRS NS Remote UDP Port Number\n") +{ + struct gsm_bts *bts = vty->index; + int idx = atoi(argv[0]); + + if (bts->gprs.mode == BTS_GPRS_NONE) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.nsvc[idx].remote_port = atoi(argv[1]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd, + "gprs nsvc <0-1> remote ip A.B.C.D", + GPRS_TEXT NSVC_TEXT + "GPRS NS Remote IP Address\n" + "GPRS NS Remote IP Address\n" + "GPRS NS Remote IP Address\n") +{ + struct gsm_bts *bts = vty->index; + int idx = atoi(argv[0]); + struct in_addr ia; + + if (bts->gprs.mode == BTS_GPRS_NONE) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + inet_aton(argv[1], &ia); + bts->gprs.nsvc[idx].remote_ip = ntohl(ia.s_addr); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_pag_free, cfg_bts_pag_free_cmd, + "paging free <-1-1024>", + "Paging options\n" + "Only page when having a certain amount of free slots\n" + "amount of required free paging slots. -1 to disable\n") +{ + struct gsm_bts *bts = vty->index; + + bts->paging.free_chans_need = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_ns_timer, cfg_bts_gprs_ns_timer_cmd, + "gprs ns timer " NS_TIMERS " <0-255>", + GPRS_TEXT "Network Service\n" + "Network Service Timer\n" + NS_TIMERS_HELP "Timer Value\n") +{ + struct gsm_bts *bts = vty->index; + int idx = get_string_value(gprs_ns_timer_strs, argv[0]); + int val = atoi(argv[1]); + + if (bts->gprs.mode == BTS_GPRS_NONE) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.nse.timer)) + return CMD_WARNING; + + bts->gprs.nse.timer[idx] = val; + + return CMD_SUCCESS; +} + +#define BSSGP_TIMERS "(blocking-timer|blocking-retries|unblocking-retries|reset-timer|reset-retries|suspend-timer|suspend-retries|resume-timer|resume-retries|capability-update-timer|capability-update-retries)" +#define BSSGP_TIMERS_HELP \ + "Tbvc-block timeout\n" \ + "Tbvc-block retries\n" \ + "Tbvc-unblock retries\n" \ + "Tbvcc-reset timeout\n" \ + "Tbvc-reset retries\n" \ + "Tbvc-suspend timeout\n" \ + "Tbvc-suspend retries\n" \ + "Tbvc-resume timeout\n" \ + "Tbvc-resume retries\n" \ + "Tbvc-capa-update timeout\n" \ + "Tbvc-capa-update retries\n" + +DEFUN(cfg_bts_gprs_cell_timer, cfg_bts_gprs_cell_timer_cmd, + "gprs cell timer " BSSGP_TIMERS " <0-255>", + GPRS_TEXT "Cell / BSSGP\n" + "Cell/BSSGP Timer\n" + BSSGP_TIMERS_HELP "Timer Value\n") +{ + struct gsm_bts *bts = vty->index; + int idx = get_string_value(gprs_bssgp_cfg_strs, argv[0]); + int val = atoi(argv[1]); + + if (bts->gprs.mode == BTS_GPRS_NONE) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.cell.timer)) + return CMD_WARNING; + + bts->gprs.cell.timer[idx] = val; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd, + "gprs routing area <0-255>", + GPRS_TEXT + "GPRS Routing Area Code\n" + "GPRS Routing Area Code\n" + "GPRS Routing Area Code\n") +{ + struct gsm_bts *bts = vty->index; + + if (bts->gprs.mode == BTS_GPRS_NONE) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.rac = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_net_ctrl_ord, cfg_bts_gprs_net_ctrl_ord_cmd, + "gprs network-control-order (nc0|nc1|nc2)", + GPRS_TEXT + "GPRS Network Control Order\n" + "MS controlled cell re-selection, no measurement reporting\n" + "MS controlled cell re-selection, MS sends measurement reports\n" + "Network controlled cell re-selection, MS sends measurement reports\n") +{ + struct gsm_bts *bts = vty->index; + + if (bts->gprs.mode == BTS_GPRS_NONE) { + vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.net_ctrl_ord = atoi(argv[0] + 2); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_gprs_mode, cfg_bts_gprs_mode_cmd, + "gprs mode (none|gprs|egprs)", + GPRS_TEXT + "GPRS Mode for this BTS\n" + "GPRS Disabled on this BTS\n" + "GPRS Enabled on this BTS\n" + "EGPRS (EDGE) Enabled on this BTS\n") +{ + struct gsm_bts *bts = vty->index; + enum bts_gprs_mode mode = bts_gprs_mode_parse(argv[0]); + + if (mode != BTS_GPRS_NONE && + !gsm_bts_has_feature(bts, BTS_FEAT_GPRS)) { + vty_out(vty, "This BTS type does not support %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + if (mode == BTS_GPRS_EGPRS && + !gsm_bts_has_feature(bts, BTS_FEAT_EGPRS)) { + vty_out(vty, "This BTS type does not support %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.mode = mode; + + return CMD_SUCCESS; +} + +#define SI_TEXT "System Information Messages\n" +#define SI_TYPE_TEXT "(1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter)" +#define SI_TYPE_HELP "System Information Type 1\n" \ + "System Information Type 2\n" \ + "System Information Type 3\n" \ + "System Information Type 4\n" \ + "System Information Type 5\n" \ + "System Information Type 6\n" \ + "System Information Type 7\n" \ + "System Information Type 8\n" \ + "System Information Type 9\n" \ + "System Information Type 10\n" \ + "System Information Type 13\n" \ + "System Information Type 16\n" \ + "System Information Type 17\n" \ + "System Information Type 18\n" \ + "System Information Type 19\n" \ + "System Information Type 20\n" \ + "System Information Type 2bis\n" \ + "System Information Type 2ter\n" \ + "System Information Type 2quater\n" \ + "System Information Type 5bis\n" \ + "System Information Type 5ter\n" + +DEFUN(cfg_bts_si_mode, cfg_bts_si_mode_cmd, + "system-information " SI_TYPE_TEXT " mode (static|computed)", + SI_TEXT SI_TYPE_HELP + "System Information Mode\n" + "Static user-specified\n" + "Dynamic, BSC-computed\n") +{ + struct gsm_bts *bts = vty->index; + int type; + + type = get_string_value(osmo_sitype_strs, argv[0]); + if (type < 0) { + vty_out(vty, "Error SI Type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[1], "static")) + bts->si_mode_static |= (1 << type); + else + bts->si_mode_static &= ~(1 << type); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_si_static, cfg_bts_si_static_cmd, + "system-information " SI_TYPE_TEXT " static HEXSTRING", + SI_TEXT SI_TYPE_HELP + "Static System Information filling\n" + "Static user-specified SI content in HEX notation\n") +{ + struct gsm_bts *bts = vty->index; + int rc, type; + + type = get_string_value(osmo_sitype_strs, argv[0]); + if (type < 0) { + vty_out(vty, "Error SI Type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!(bts->si_mode_static & (1 << type))) { + vty_out(vty, "SI Type %s is not configured in static mode%s", + get_value_string(osmo_sitype_strs, type), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Fill buffer with padding pattern */ + memset(bts->si_buf[type], 0x2b, sizeof(bts->si_buf[type])); + + /* Parse the user-specified SI in hex format, [partially] overwriting padding */ + rc = osmo_hexparse(argv[1], bts->si_buf[type], sizeof(bts->si_buf[0])); + if (rc < 0 || rc > sizeof(bts->si_buf[0])) { + vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Mark this SI as present */ + bts->si_valid |= (1 << type); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_neigh_mode, cfg_bts_neigh_mode_cmd, + "neighbor-list mode (automatic|manual|manual-si5)", + "Neighbor List\n" "Mode of Neighbor List generation\n" + "Automatically from all BTS in this OpenBSC\n" "Manual\n" + "Manual with different lists for SI2 and SI5\n") +{ + struct gsm_bts *bts = vty->index; + int mode = get_string_value(bts_neigh_mode_strs, argv[0]); + + switch (mode) { + case NL_MODE_MANUAL_SI5SEP: + case NL_MODE_MANUAL: + /* make sure we clear the current list when switching to + * manual mode */ + if (bts->neigh_list_manual_mode == 0) + memset(&bts->si_common.data.neigh_list, 0, + sizeof(bts->si_common.data.neigh_list)); + break; + default: + break; + } + + bts->neigh_list_manual_mode = mode; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_neigh, cfg_bts_neigh_cmd, + "neighbor-list (add|del) arfcn <0-1023>", + "Neighbor List\n" "Add to manual neighbor list\n" + "Delete from manual neighbor list\n" "ARFCN of neighbor\n" + "ARFCN of neighbor\n") +{ + struct gsm_bts *bts = vty->index; + struct bitvec *bv = &bts->si_common.neigh_list; + uint16_t arfcn = atoi(argv[1]); + + if (!bts->neigh_list_manual_mode) { + vty_out(vty, "%% Cannot configure neighbor list in " + "automatic mode%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[0], "add")) + bitvec_set_bit_pos(bv, arfcn, 1); + else + bitvec_set_bit_pos(bv, arfcn, 0); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_si5_neigh, cfg_bts_si5_neigh_cmd, + "si5 neighbor-list (add|del) arfcn <0-1023>", + "SI5 Neighbor List\n" + "SI5 Neighbor List\n" "Add to manual SI5 neighbor list\n" + "Delete from SI5 manual neighbor list\n" "ARFCN of neighbor\n" + "ARFCN of neighbor\n") +{ + struct gsm_bts *bts = vty->index; + struct bitvec *bv = &bts->si_common.si5_neigh_list; + uint16_t arfcn = atoi(argv[1]); + + if (!bts->neigh_list_manual_mode) { + vty_out(vty, "%% Cannot configure neighbor list in " + "automatic mode%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[0], "add")) + bitvec_set_bit_pos(bv, arfcn, 1); + else + bitvec_set_bit_pos(bv, arfcn, 0); + + return CMD_SUCCESS; +} + +#define EXCL_RFLOCK_STR "Exclude this BTS from the global RF Lock\n" + +DEFUN(cfg_bts_excl_rf_lock, + cfg_bts_excl_rf_lock_cmd, + "rf-lock-exclude", + EXCL_RFLOCK_STR) +{ + struct gsm_bts *bts = vty->index; + bts->excl_from_rf_lock = 1; + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_no_excl_rf_lock, + cfg_bts_no_excl_rf_lock_cmd, + "no rf-lock-exclude", + NO_STR EXCL_RFLOCK_STR) +{ + struct gsm_bts *bts = vty->index; + bts->excl_from_rf_lock = 0; + return CMD_SUCCESS; +} + +#define 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; + + ts->tsc = atoi(argv[0]); + + return CMD_SUCCESS; +} + +#define HOPPING_STR "Configure frequency hopping\n" + +DEFUN(cfg_ts_hopping, + cfg_ts_hopping_cmd, + "hopping enabled (0|1)", + HOPPING_STR "Enable or disable frequency hopping\n" + "Disable frequency hopping\n" "Enable frequency hopping\n") +{ + struct gsm_bts_trx_ts *ts = vty->index; + int enabled = atoi(argv[0]); + + if (enabled && !gsm_bts_has_feature(ts->trx->bts, BTS_FEAT_HOPPING)) { + vty_out(vty, "BTS model does not support hopping%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ts->hopping.enabled = enabled; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ts_hsn, + cfg_ts_hsn_cmd, + "hopping sequence-number <0-63>", + HOPPING_STR + "Which hopping sequence to use for this channel\n" + "Hopping Sequence Number (HSN)\n") +{ + struct gsm_bts_trx_ts *ts = vty->index; + + ts->hopping.hsn = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ts_maio, + cfg_ts_maio_cmd, + "hopping maio <0-63>", + HOPPING_STR + "Which hopping MAIO to use for this channel\n" + "Mobile Allocation Index Offset (MAIO)\n") +{ + struct gsm_bts_trx_ts *ts = vty->index; + + ts->hopping.maio = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ts_arfcn_add, + cfg_ts_arfcn_add_cmd, + "hopping arfcn add <0-1023>", + HOPPING_STR "Configure hopping ARFCN list\n" + "Add an entry to the hopping ARFCN list\n" "ARFCN\n") +{ + struct gsm_bts_trx_ts *ts = vty->index; + int arfcn = atoi(argv[0]); + + bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 1); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ts_arfcn_del, + cfg_ts_arfcn_del_cmd, + "hopping arfcn del <0-1023>", + HOPPING_STR "Configure hopping ARFCN list\n" + "Delete an entry to the hopping ARFCN list\n" "ARFCN\n") +{ + struct gsm_bts_trx_ts *ts = vty->index; + int arfcn = atoi(argv[0]); + + bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 0); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ts_e1_subslot, + cfg_ts_e1_subslot_cmd, + "e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", + "E1/T1 channel connected to this on-air timeslot\n" + "E1/T1 channel connected to this on-air timeslot\n" + "E1/T1 line connected to this on-air timeslot\n" + "E1/T1 timeslot connected to this on-air timeslot\n" + "E1/T1 timeslot connected to this on-air timeslot\n" + "E1/T1 sub-slot connected to this on-air timeslot\n" + "E1/T1 sub-slot 0 connected to this on-air timeslot\n" + "E1/T1 sub-slot 1 connected to this on-air timeslot\n" + "E1/T1 sub-slot 2 connected to this on-air timeslot\n" + "E1/T1 sub-slot 3 connected to this on-air timeslot\n" + "Full E1/T1 timeslot connected to this on-air timeslot\n") +{ + struct gsm_bts_trx_ts *ts = vty->index; + + parse_e1_link(&ts->e1_link, argv[0], argv[1], argv[2]); + + return CMD_SUCCESS; +} + +void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net) +{ + vty_out(vty, "Channel Requests : %lu total, %lu no channel%s", + osmo_counter_get(net->stats.chreq.total), + osmo_counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); + vty_out(vty, "Channel Failures : %lu rf_failures, %lu rll failures%s", + osmo_counter_get(net->stats.chan.rf_fail), + osmo_counter_get(net->stats.chan.rll_err), VTY_NEWLINE); + vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s", + osmo_counter_get(net->stats.paging.attempted), + osmo_counter_get(net->stats.paging.completed), + osmo_counter_get(net->stats.paging.expired), VTY_NEWLINE); + vty_out(vty, "BTS failures : %lu OML, %lu RSL%s", + osmo_counter_get(net->stats.bts.oml_fail), + osmo_counter_get(net->stats.bts.rsl_fail), VTY_NEWLINE); +} + +DEFUN(logging_fltr_imsi, + logging_fltr_imsi_cmd, + "logging filter imsi IMSI", + LOGGING_STR FILTER_STR + "Filter log messages by IMSI\n" "IMSI to be used as filter\n") +{ + struct log_target *tgt = osmo_log_vty2tgt(vty); + + if (!tgt) + return CMD_WARNING; + + log_set_imsi_filter(tgt, argv[0]); + return CMD_SUCCESS; +} + + +DEFUN(drop_bts, + drop_bts_cmd, + "drop bts connection <0-65535> (oml|rsl)", + "Debug/Simulation command to drop 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(pdch_act, pdch_act_cmd, + "bts <0-255> trx <0-255> timeslot <0-7> pdch (activate|deactivate)", + "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n" + "TRX Timeslot\n" "Timeslot Number\n" "Packet Data Channel\n" + "Activate Dynamic PDCH/TCH (-> PDCH mode)\n" + "Deactivate Dynamic PDCH/TCH (-> TCH mode)\n") +{ + struct gsm_bts *bts; + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + int bts_nr = atoi(argv[0]); + int trx_nr = atoi(argv[1]); + int ts_nr = atoi(argv[2]); + int activate; + + bts = gsm_bts_num(bsc_gsmnet, bts_nr); + if (!bts) { + vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + if (!is_ipaccess_bts(bts)) { + vty_out(vty, "%% This command only works for ipaccess BTS%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + trx = gsm_bts_trx_num(bts, trx_nr); + if (!trx) { + vty_out(vty, "%% No such TRX (%d)%s", trx_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + ts = &trx->ts[ts_nr]; + if (ts->pchan != GSM_PCHAN_TCH_F_PDCH) { + vty_out(vty, "%% Timeslot %u is not in dynamic TCH_F/PDCH " + "mode%s", ts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[3], "activate")) + activate = 1; + else + activate = 0; + + rsl_ipacc_pdch_activate(ts, activate); + + return CMD_SUCCESS; + +} + +extern int bsc_vty_init_extra(void); +extern const char *openbsc_copyright; + +int bsc_vty_init(const struct log_info *cat) +{ + cfg_ts_pchan_cmd.string = + vty_cmd_string_from_valstr(tall_bsc_ctx, + gsm_pchant_names, + "phys_chan_config (", "|", ")", + VTY_DO_LOWER); + cfg_ts_pchan_cmd.doc = + vty_cmd_string_from_valstr(tall_bsc_ctx, + gsm_pchant_descs, + "Physical Channel Combination\n", + "\n", "", 0); + + cfg_bts_type_cmd.string = + vty_cmd_string_from_valstr(tall_bsc_ctx, + bts_type_names, + "type (", "|", ")", + VTY_DO_LOWER); + cfg_bts_type_cmd.doc = + vty_cmd_string_from_valstr(tall_bsc_ctx, + bts_type_descs, + "BTS Vendor/Type\n", + "\n", "", 0); + + + install_element_ve(&show_net_cmd); + install_element_ve(&show_bts_cmd); + install_element_ve(&show_trx_cmd); + install_element_ve(&show_ts_cmd); + install_element_ve(&show_lchan_cmd); + install_element_ve(&show_lchan_summary_cmd); + install_element_ve(&logging_fltr_imsi_cmd); + + install_element_ve(&show_paging_cmd); + + logging_vty_add_cmds(cat); + install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd); + + install_element(CONFIG_NODE, &cfg_net_cmd); + install_node(&net_node, config_write_net); + install_default(GSMNET_NODE); + install_element(GSMNET_NODE, &ournode_exit_cmd); + install_element(GSMNET_NODE, &ournode_end_cmd); + install_element(GSMNET_NODE, &cfg_net_ncc_cmd); + install_element(GSMNET_NODE, &cfg_net_mnc_cmd); + install_element(GSMNET_NODE, &cfg_net_name_short_cmd); + install_element(GSMNET_NODE, &cfg_net_name_long_cmd); + install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd); + install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd); + install_element(GSMNET_NODE, &cfg_net_encryption_cmd); + install_element(GSMNET_NODE, &cfg_net_neci_cmd); + install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd); + install_element(GSMNET_NODE, &cfg_net_mm_info_cmd); + install_element(GSMNET_NODE, &cfg_net_handover_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd); + install_element(GSMNET_NODE, &cfg_net_T3101_cmd); + install_element(GSMNET_NODE, &cfg_net_T3103_cmd); + install_element(GSMNET_NODE, &cfg_net_T3105_cmd); + install_element(GSMNET_NODE, &cfg_net_T3107_cmd); + install_element(GSMNET_NODE, &cfg_net_T3109_cmd); + install_element(GSMNET_NODE, &cfg_net_T3111_cmd); + install_element(GSMNET_NODE, &cfg_net_T3113_cmd); + install_element(GSMNET_NODE, &cfg_net_T3115_cmd); + install_element(GSMNET_NODE, &cfg_net_T3117_cmd); + install_element(GSMNET_NODE, &cfg_net_T3119_cmd); + install_element(GSMNET_NODE, &cfg_net_T3122_cmd); + install_element(GSMNET_NODE, &cfg_net_T3141_cmd); + install_element(GSMNET_NODE, &cfg_net_dtx_cmd); + install_element(GSMNET_NODE, &cfg_net_subscr_keep_cmd); + install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd); + + install_element(GSMNET_NODE, &cfg_bts_cmd); + install_node(&bts_node, config_write_bts); + install_default(BTS_NODE); + install_element(BTS_NODE, &ournode_exit_cmd); + install_element(BTS_NODE, &ournode_end_cmd); + install_element(BTS_NODE, &cfg_bts_type_cmd); + install_element(BTS_NODE, &cfg_description_cmd); + install_element(BTS_NODE, &cfg_no_description_cmd); + install_element(BTS_NODE, &cfg_bts_band_cmd); + install_element(BTS_NODE, &cfg_bts_ci_cmd); + install_element(BTS_NODE, &cfg_bts_lac_cmd); + install_element(BTS_NODE, &cfg_bts_tsc_cmd); + install_element(BTS_NODE, &cfg_bts_bsic_cmd); + install_element(BTS_NODE, &cfg_bts_unit_id_cmd); + install_element(BTS_NODE, &cfg_bts_rsl_ip_cmd); + install_element(BTS_NODE, &cfg_bts_timezone_cmd); + install_element(BTS_NODE, &cfg_bts_no_timezone_cmd); + install_element(BTS_NODE, &cfg_bts_nokia_site_skip_reset_cmd); + install_element(BTS_NODE, &cfg_bts_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_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_gprs_mode_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_net_ctrl_ord_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd); + install_element(BTS_NODE, &cfg_bts_pag_free_cmd); + install_element(BTS_NODE, &cfg_bts_si_mode_cmd); + install_element(BTS_NODE, &cfg_bts_si_static_cmd); + install_element(BTS_NODE, &cfg_bts_neigh_mode_cmd); + install_element(BTS_NODE, &cfg_bts_neigh_cmd); + install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd); + install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd); + install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd); + + install_element(BTS_NODE, &cfg_trx_cmd); + install_node(&trx_node, dummy_config_write); + install_default(TRX_NODE); + install_element(TRX_NODE, &ournode_exit_cmd); + install_element(TRX_NODE, &ournode_end_cmd); + install_element(TRX_NODE, &cfg_trx_arfcn_cmd); + install_element(TRX_NODE, &cfg_description_cmd); + install_element(TRX_NODE, &cfg_no_description_cmd); + install_element(TRX_NODE, &cfg_trx_nominal_power_cmd); + install_element(TRX_NODE, &cfg_trx_max_power_red_cmd); + install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd); + install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd); + install_element(TRX_NODE, &cfg_trx_rf_locked_cmd); + + install_element(TRX_NODE, &cfg_ts_cmd); + install_node(&ts_node, dummy_config_write); + install_default(TS_NODE); + install_element(TS_NODE, &ournode_exit_cmd); + install_element(TS_NODE, &ournode_end_cmd); + install_element(TS_NODE, &cfg_ts_pchan_cmd); + install_element(TS_NODE, &cfg_ts_pchan_compat_cmd); + install_element(TS_NODE, &cfg_ts_tsc_cmd); + install_element(TS_NODE, &cfg_ts_hopping_cmd); + install_element(TS_NODE, &cfg_ts_hsn_cmd); + install_element(TS_NODE, &cfg_ts_maio_cmd); + install_element(TS_NODE, &cfg_ts_arfcn_add_cmd); + install_element(TS_NODE, &cfg_ts_arfcn_del_cmd); + install_element(TS_NODE, &cfg_ts_e1_subslot_cmd); + + install_element(ENABLE_NODE, &drop_bts_cmd); + install_element(ENABLE_NODE, &pdch_act_cmd); + + abis_nm_vty_init(); + abis_om2k_vty_init(); + e1inp_vty_init(); + + bsc_vty_init_extra(); + + return 0; +} diff --git a/src/libbsc/bts_ericsson_rbs2000.c b/src/libbsc/bts_ericsson_rbs2000.c new file mode 100644 index 000000000..ba5ebe576 --- /dev/null +++ b/src/libbsc/bts_ericsson_rbs2000.c @@ -0,0 +1,276 @@ +/* Ericsson RBS-2xxx specific code */ + +/* (C) 2011 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include + +#include +#include +#include +#include +#include +#include + +#include + +static void bootstrap_om_bts(struct gsm_bts *bts) +{ + LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); + abis_om2k_tx_start_req(bts, &om2k_mo_cf); + /* FIXME */ +} + +static void bootstrap_om_trx(struct gsm_bts_trx *trx) +{ + struct abis_om2k_mo trx_mo = { OM2K_MO_CLS_TRXC, 0, 255, trx->nr }; + + LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", + trx->bts->nr, trx->nr); + + abis_om2k_tx_reset_cmd(trx->bts, &trx_mo); +} + +static int shutdown_om(struct gsm_bts *bts) +{ + /* FIXME */ + return 0; +} + + +/* Tell LAPD to start start the SAP (send SABM requests) for all signalling + * timeslots in this line */ +static void start_sabm_in_line(struct e1inp_line *line, int start) +{ + struct e1inp_sign_link *link; + int i; + + for (i = 0; i < ARRAY_SIZE(line->ts); i++) { + struct e1inp_ts *ts = &line->ts[i]; + + if (ts->type != E1INP_TS_TYPE_SIGN) + continue; + + llist_for_each_entry(link, &ts->sign.sign_links, list) { + if (start) + lapd_sap_start(ts->lapd, link->tei, link->sapi); + else + lapd_sap_stop(ts->lapd, link->tei, link->sapi); + } + } +} + +/* Callback function to be called every time we receive a signal from INPUT */ +static int gbl_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_bts *bts; + + if (subsys != SS_L_GLOBAL) + return 0; + + switch (signal) { + case S_GLOBAL_BTS_CLOSE_OM: + bts = signal_data; + if (bts->type == GSM_BTS_TYPE_RBS2000) + shutdown_om(signal_data); + break; + } + + return 0; +} + +/* Callback function to be called every time we receive a signal from INPUT */ +static int inp_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct input_signal_data *isd = signal_data; + + if (subsys != SS_L_INPUT) + return 0; + + switch (signal) { + case S_L_INP_TEI_UP: + switch (isd->link_type) { + case E1INP_SIGN_OML: + if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000) + break; + if (isd->tei == isd->trx->bts->oml_tei) + bootstrap_om_bts(isd->trx->bts); + else + bootstrap_om_trx(isd->trx); + break; + } + break; + case S_L_INP_LINE_INIT: + case S_L_INP_LINE_NOALARM: + if (strcasecmp(isd->line->driver->name, "DAHDI") + && strcasecmp(isd->line->driver->name, "MISDN_LAPD")) + break; + start_sabm_in_line(isd->line, 1); + break; + case S_L_INP_LINE_ALARM: + if (strcasecmp(isd->line->driver->name, "DAHDI") + && strcasecmp(isd->line->driver->name, "MISDN_LAPD")) + break; + start_sabm_in_line(isd->line, 0); + break; + } + + return 0; +} + +static void nm_statechg_evt(unsigned int signal, + struct nm_statechg_signal_data *nsd) +{ + struct abis_om2k_mo mo; + + if (nsd->bts->type != GSM_BTS_TYPE_RBS2000) + return; + + switch (nsd->om2k_mo->class) { + case OM2K_MO_CLS_CF: + if (nsd->new_state->operational != NM_OPSTATE_ENABLED || + nsd->new_state->availability != OM2K_MO_S_STARTED) + break; + /* CF has started, we can trigger IS and TF start */ + abis_om2k_tx_connect_cmd(nsd->bts, &om2k_mo_is); + abis_om2k_tx_connect_cmd(nsd->bts, &om2k_mo_tf); + break; + case OM2K_MO_CLS_IS: + if (nsd->new_state->availability == OM2K_MO_S_ENABLED) { + /* IS is enabled, we can proceed with TRXC/RX/TX/TS */ + break; + } + if (nsd->new_state->operational != NM_OPSTATE_ENABLED) + break; + /* IS has started, we can configure + enable it */ + abis_om2k_tx_is_conf_req(nsd->bts); + break; + case OM2K_MO_CLS_TF: + if (nsd->new_state->operational != NM_OPSTATE_ENABLED || + nsd->new_state->availability == OM2K_MO_S_DISABLED) + break; + if (nsd->new_state->availability == OM2K_MO_S_STARTED) { + /* TF has started, configure + enable it */ + abis_om2k_tx_tf_conf_req(nsd->bts); + } + break; + case OM2K_MO_CLS_TRXC: + if (nsd->new_state->availability != OM2K_MO_S_STARTED) + break; + /* TRXC is started, connect the TX and RX objects */ + memcpy(&mo, nsd->om2k_mo, sizeof(mo)); + mo.class = OM2K_MO_CLS_TX; + abis_om2k_tx_connect_cmd(nsd->bts, &mo); + mo.class = OM2K_MO_CLS_RX; + abis_om2k_tx_connect_cmd(nsd->bts, &mo); + break; + case OM2K_MO_CLS_RX: + if (nsd->new_state->operational != NM_OPSTATE_ENABLED || + nsd->new_state->availability != OM2K_MO_S_STARTED) + break; + /* RX is started, configure + enable it */ + abis_om2k_tx_rx_conf_req(nsd->obj); + break; + case OM2K_MO_CLS_TX: + if (nsd->new_state->operational != NM_OPSTATE_ENABLED || + nsd->new_state->availability != OM2K_MO_S_STARTED) + break; + /* RX is started, configure + enable it */ + abis_om2k_tx_tx_conf_req(nsd->obj); + break; + } +} + +static void nm_conf_res(struct nm_om2k_signal_data *nsd) +{ + switch (nsd->om2k_mo->class) { + case OM2K_MO_CLS_IS: + case OM2K_MO_CLS_TF: + case OM2K_MO_CLS_RX: + case OM2K_MO_CLS_TX: + /* If configuration was a success, enable it */ + abis_om2k_tx_enable_req(nsd->bts, nsd->om2k_mo); + break; + } +} + +static int nm_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + if (subsys != SS_NM) + return 0; + + switch (signal) { + case S_NM_STATECHG_OPER: + case S_NM_STATECHG_ADM: + nm_statechg_evt(signal, signal_data); + break; + case S_NM_OM2K_CONF_RES: + nm_conf_res(signal_data); + break; + default: + break; + } + + return 0; +} + +static void config_write_bts(struct vty *vty, struct gsm_bts *bts) +{ + abis_om2k_config_write_bts(vty, bts); +} + +static int bts_model_rbs2k_start(struct gsm_network *net); + +static void bts_model_rbs2k_e1line_bind_ops(struct e1inp_line *line) +{ + e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); +} + +static struct gsm_bts_model model_rbs2k = { + .type = GSM_BTS_TYPE_RBS2000, + .name = "rbs2000", + .start = bts_model_rbs2k_start, + .oml_rcvmsg = &abis_om2k_rcvmsg, + .config_write_bts = &config_write_bts, + .e1line_bind_ops = &bts_model_rbs2k_e1line_bind_ops, +}; + +static int bts_model_rbs2k_start(struct gsm_network *net) +{ + model_rbs2k.features.data = &model_rbs2k._features_data[0]; + model_rbs2k.features.data_len = sizeof(model_rbs2k._features_data); + + gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HOPPING); + gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HSCSD); + + osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); + osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); + osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); + + return 0; +} + +int bts_model_rbs2k_init(void) +{ + return gsm_bts_model_register(&model_rbs2k); +} diff --git a/src/libbsc/bts_init.c b/src/libbsc/bts_init.c new file mode 100644 index 000000000..d6b152a79 --- /dev/null +++ b/src/libbsc/bts_init.c @@ -0,0 +1,30 @@ +/* (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/src/libbsc/bts_ipaccess_nanobts.c b/src/libbsc/bts_ipaccess_nanobts.c new file mode 100644 index 000000000..c8b235d56 --- /dev/null +++ b/src/libbsc/bts_ipaccess_nanobts.c @@ -0,0 +1,650 @@ +/* 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 + +extern struct gsm_network *bsc_gsmnet; + +static int bts_model_nanobts_start(struct gsm_network *net); +static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line); + +struct gsm_bts_model bts_model_nanobts = { + .type = GSM_BTS_TYPE_NANOBTS, + .name = "nanobts", + .start = bts_model_nanobts_start, + .oml_rcvmsg = &abis_nm_rcvmsg, + .e1line_bind_ops = bts_model_nanobts_e1line_bind_ops, + .nm_att_tlvdef = { + .def = { + /* ip.access specifics */ + [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, + [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, }, + [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, + [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, + [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, + [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 }, + [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V }, + }, + }, +}; + +static unsigned char nanobts_attr_bts[] = { + NM_ATT_INTERF_BOUND, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73, + /* interference avg. period in numbers of SACCH multifr */ + NM_ATT_INTAVE_PARAM, 0x06, + /* conn fail based on SACCH error rate */ + NM_ATT_CONN_FAIL_CRIT, 0x00, 0x02, 0x01, 0x10, + NM_ATT_T200, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, 0xa8, + NM_ATT_MAX_TA, 0x3f, + NM_ATT_OVERL_PERIOD, 0x00, 0x01, 10, /* seconds */ + NM_ATT_CCCH_L_T, 10, /* percent */ + NM_ATT_CCCH_L_I_P, 1, /* seconds */ + NM_ATT_RACH_B_THRESH, 10, /* busy threshold in - dBm */ + NM_ATT_LDAVG_SLOTS, 0x03, 0xe8, /* rach load averaging 1000 slots */ + NM_ATT_BTS_AIR_TIMER, 128, /* miliseconds */ + NM_ATT_NY1, 10, /* 10 retransmissions of physical config */ + NM_ATT_BCCH_ARFCN, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, + NM_ATT_BSIC, HARDCODED_BSIC, + NM_ATT_IPACC_CGI, 0, 7, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x00, +}; + +static unsigned char nanobts_attr_radio[] = { + NM_ATT_RF_MAXPOWR_R, 0x0c, /* number of -2dB reduction steps / Pn */ + NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, +}; + +static unsigned char nanobts_attr_nse[] = { + NM_ATT_IPACC_NSEI, 0, 2, 0x03, 0x9d, /* NSEI 925 */ + /* all timers in seconds */ + NM_ATT_IPACC_NS_CFG, 0, 7, 3, /* (un)blocking timer (Tns-block) */ + 3, /* (un)blocking retries */ + 3, /* reset timer (Tns-reset) */ + 3, /* reset retries */ + 30, /* test timer (Tns-test) */ + 3, /* alive timer (Tns-alive) */ + 10, /* alive retrires */ + /* all timers in seconds, unless otherwise stated */ + NM_ATT_IPACC_BSSGP_CFG, 0, 11, + 3, /* blockimg timer (T1) */ + 3, /* blocking retries */ + 3, /* unblocking retries */ + 3, /* reset timer (T2) */ + 3, /* reset retries */ + 10, /* suspend timer (T3) in 100ms */ + 3, /* suspend retries */ + 10, /* resume timer (T4) in 100ms */ + 3, /* resume retries */ + 10, /* capability update timer (T5) */ + 3, /* capability update retries */ +}; + +static unsigned char nanobts_attr_cell[] = { + NM_ATT_IPACC_RAC, 0, 1, 1, /* routing area code */ + NM_ATT_IPACC_GPRS_PAGING_CFG, 0, 2, + 5, /* repeat time (50ms) */ + 3, /* repeat count */ + NM_ATT_IPACC_BVCI, 0, 2, 0x03, 0x9d, /* BVCI 925 */ + /* all timers in seconds, unless otherwise stated */ + NM_ATT_IPACC_RLC_CFG, 0, 9, + 20, /* T3142 */ + 5, /* T3169 */ + 5, /* T3191 */ + 160, /* T3193 (units of 10ms) */ + 5, /* T3195 */ + 10, /* N3101 */ + 4, /* N3103 */ + 8, /* N3105 */ + 15, /* RLC CV countdown */ + NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00, /* CS1..CS4 */ + NM_ATT_IPACC_RLC_CFG_2, 0, 5, + 0x00, 250, /* T downlink TBF extension (0..500) */ + 0x00, 250, /* T uplink TBF extension (0..500) */ + 2, /* CS2 */ +#if 0 + /* EDGE model only, breaks older models. + * Should inquire the BTS capabilities */ + NM_ATT_IPACC_RLC_CFG_3, 0, 1, + 2, /* MCS2 */ +#endif +}; + +static unsigned char nanobts_attr_nsvc0[] = { + NM_ATT_IPACC_NSVCI, 0, 2, 0x03, 0x9d, /* 925 */ + NM_ATT_IPACC_NS_LINK_CFG, 0, 8, + 0x59, 0xd8, /* remote udp port (23000) */ + 192, 168, 100, 11, /* remote ip address */ + 0x59, 0xd8, /* local udp port (23000) */ +}; + +static void patch_16(uint8_t *data, const uint16_t val) +{ + memcpy(data, &val, sizeof(val)); +} + +static void patch_32(uint8_t *data, const uint32_t val) +{ + memcpy(data, &val, sizeof(val)); +} + +/* + * Patch the various SYSTEM INFORMATION tables to update + * the LAI + */ +static void patch_nm_tables(struct gsm_bts *bts) +{ + uint8_t arfcn_low = bts->c0->arfcn & 0xff; + uint8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; + + /* patch ARFCN into BTS Attributes */ + nanobts_attr_bts[42] &= 0xf0; + nanobts_attr_bts[42] |= arfcn_high; + nanobts_attr_bts[43] = arfcn_low; + + /* patch the RACH attributes */ + if (bts->rach_b_thresh != -1) { + nanobts_attr_bts[33] = bts->rach_b_thresh & 0xff; + } + + if (bts->rach_ldavg_slots != -1) { + uint8_t avg_high = bts->rach_ldavg_slots & 0xff; + uint8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; + + nanobts_attr_bts[35] = avg_high; + nanobts_attr_bts[36] = avg_low; + } + + /* patch BSIC */ + nanobts_attr_bts[sizeof(nanobts_attr_bts)-11] = bts->bsic; + + /* patch CGI */ + abis_nm_ipaccess_cgi(nanobts_attr_bts+sizeof(nanobts_attr_bts)-7, bts); + + /* patch the power reduction */ + nanobts_attr_radio[1] = bts->c0->max_power_red / 2; + + /* patch NSEI */ + nanobts_attr_nse[3] = bts->gprs.nse.nsei >> 8; + nanobts_attr_nse[4] = bts->gprs.nse.nsei & 0xff; + memcpy(nanobts_attr_nse+8, bts->gprs.nse.timer, + ARRAY_SIZE(bts->gprs.nse.timer)); + memcpy(nanobts_attr_nse+18, bts->gprs.cell.timer, + ARRAY_SIZE(bts->gprs.cell.timer)); + + /* patch NSVCI */ + nanobts_attr_nsvc0[3] = bts->gprs.nsvc[0].nsvci >> 8; + nanobts_attr_nsvc0[4] = bts->gprs.nsvc[0].nsvci & 0xff; + + /* patch IP address as SGSN IP */ + patch_16(nanobts_attr_nsvc0 + 8, + htons(bts->gprs.nsvc[0].remote_port)); + patch_32(nanobts_attr_nsvc0 + 10, + htonl(bts->gprs.nsvc[0].remote_ip)); + patch_16(nanobts_attr_nsvc0 + 14, + htons(bts->gprs.nsvc[0].local_port)); + + /* patch BVCI */ + nanobts_attr_cell[12] = bts->gprs.cell.bvci >> 8; + nanobts_attr_cell[13] = bts->gprs.cell.bvci & 0xff; + /* patch RAC */ + nanobts_attr_cell[3] = bts->gprs.rac; + + if (bts->gprs.mode == BTS_GPRS_EGPRS) { + /* patch EGPRS coding schemes MCS 1..9 */ + nanobts_attr_cell[29] = 0x8f; + nanobts_attr_cell[30] = 0xff; + } +} + + +/* Callback function to be called whenever we get a GSM 12.21 state change event */ +static int nm_statechg_event(int evt, struct nm_statechg_signal_data *nsd) +{ + 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; + + 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) { + patch_nm_tables(bts); + abis_nm_set_bts_attr(bts, nanobts_attr_bts, + sizeof(nanobts_attr_bts)); + abis_nm_chg_adm_state(bts, obj_class, + bts->bts_nr, 0xff, 0xff, + NM_STATE_UNLOCKED); + abis_nm_opstart(bts, obj_class, + bts->bts_nr, 0xff, 0xff); + } + break; + case NM_OC_CHANNEL: + ts = obj; + trx = ts->trx; + if (new_state->operational == NM_OPSTATE_DISABLED && + new_state->availability == NM_AVSTATE_DEPENDENCY) { + patch_nm_tables(trx->bts); + enum abis_nm_chan_comb ccomb = + abis_nm_chcomb4pchan(ts->pchan); + abis_nm_set_channel_attr(ts, ccomb); + abis_nm_chg_adm_state(trx->bts, obj_class, + trx->bts->bts_nr, trx->nr, ts->nr, + NM_STATE_UNLOCKED); + abis_nm_opstart(trx->bts, obj_class, + trx->bts->bts_nr, trx->nr, ts->nr); + } + break; + case NM_OC_RADIO_CARRIER: + trx = obj; + if (new_state->operational == NM_OPSTATE_DISABLED && + new_state->availability == NM_AVSTATE_OK) + abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, + trx->nr, 0xff); + break; + case NM_OC_GPRS_NSE: + bts = container_of(obj, struct gsm_bts, gprs.nse); + if (bts->gprs.mode == BTS_GPRS_NONE) + break; + if (new_state->availability == NM_AVSTATE_DEPENDENCY) { + abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, + 0xff, 0xff, nanobts_attr_nse, + sizeof(nanobts_attr_nse)); + abis_nm_opstart(bts, obj_class, bts->bts_nr, + 0xff, 0xff); + } + break; + case NM_OC_GPRS_CELL: + bts = container_of(obj, struct gsm_bts, gprs.cell); + if (bts->gprs.mode == BTS_GPRS_NONE) + break; + if (new_state->availability == NM_AVSTATE_DEPENDENCY) { + abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, + 0, 0xff, nanobts_attr_cell, + sizeof(nanobts_attr_cell)); + abis_nm_opstart(bts, obj_class, bts->bts_nr, + 0, 0xff); + abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, + 0, 0xff, NM_STATE_UNLOCKED); + abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE, bts->bts_nr, + 0xff, 0xff, NM_STATE_UNLOCKED); + } + break; + case NM_OC_GPRS_NSVC: + nsvc = obj; + bts = nsvc->bts; + if (bts->gprs.mode == BTS_GPRS_NONE) + break; + /* We skip NSVC1 since we only use NSVC0 */ + if (nsvc->id == 1) + break; + if ((new_state->availability == NM_AVSTATE_OFF_LINE) || + (new_state->availability == NM_AVSTATE_DEPENDENCY)) { + abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, + nsvc->id, 0xff, + nanobts_attr_nsvc0, + sizeof(nanobts_attr_nsvc0)); + abis_nm_opstart(bts, obj_class, bts->bts_nr, + nsvc->id, 0xff); + abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, + nsvc->id, 0xff, + NM_STATE_UNLOCKED); + } + default: + break; + } + return 0; +} + +/* Callback function to be called every time we receive a 12.21 SW activated report */ +static int sw_activ_rep(struct msgb *mb) +{ + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct 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 */ + nanobts_attr_radio[5] &= 0xf0; + nanobts_attr_radio[5] |= trx->arfcn >> 8; + nanobts_attr_radio[6] = trx->arfcn & 0xff; + abis_nm_set_radio_attr(trx, nanobts_attr_radio, + sizeof(nanobts_attr_radio)); + abis_nm_chg_adm_state(trx->bts, foh->obj_class, + trx->bts->bts_nr, trx->nr, 0xff, + rc_state); + abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr, + trx->nr, 0xff); + break; + } + } + return 0; +} + +/* Callback function to be called every time we receive a signal from NM */ +static int 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); + + 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_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; +} + +/* 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); + + /* 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/src/libbsc/bts_nokia_site.c b/src/libbsc/bts_nokia_site.c new file mode 100644 index 000000000..36e3fac4f --- /dev/null +++ b/src/libbsc/bts_nokia_site.c @@ -0,0 +1,1740 @@ +/* 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 ? */ + +#define RESET_INTERVAL 15, 0 /* 15 seconds */ + +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); + + return abis_nm_send(bts, NOKIA_MSG_RESET_REQ, ref, data, len_data); +} + +/* TODO: put in a separate file ? */ + +static int abis_nm_send_multi_segments(struct gsm_bts *bts, uint8_t msg_type, + uint16_t ref, uint8_t * data, int len) +{ + int len_remain, len_to_send, max_send; + int seq = 0; + int ret; + + len_remain = len; + + while (len_remain) { + struct abis_om_hdr *oh; + struct abis_om_nokia_hdr *noh; + struct msgb *msg = nm_msgb_alloc(); + + if (seq == 0) + max_send = 256 - sizeof(struct abis_om_nokia_hdr); + else + max_send = 256; + + if (len_remain > max_send) { + len_to_send = max_send; + + if (seq == 0) { + /* first segment */ + oh = (struct abis_om_hdr *)msgb_put(msg, + ABIS_OM_NOKIA_HDR_SIZE + + + len_to_send); + + oh->mdisc = ABIS_OM_MDISC_FOM; + oh->placement = ABIS_OM_PLACEMENT_FIRST; /* first segment of multi-segment message */ + oh->sequence = seq; + oh->length = 0; /* 256 bytes */ + + noh = (struct abis_om_nokia_hdr *)oh->data; + + noh->msg_type = msg_type; + noh->spare = 0; + noh->reference = htons(ref); + memcpy(noh->data, data, len_to_send); + } else { + /* segment in between */ + oh = (struct abis_om_hdr *)msgb_put(msg, + sizeof + (struct + abis_om_hdr) + + + len_to_send); + + oh->mdisc = ABIS_OM_MDISC_FOM; + oh->placement = ABIS_OM_PLACEMENT_MIDDLE; /* segment of multi-segment message */ + oh->sequence = seq; + oh->length = 0; /* 256 bytes */ + + memcpy(oh->data, data, len_to_send); + } + } else { + + len_to_send = len_remain; + + /* check if message fits in a single segment */ + + if (seq == 0) + return abis_nm_send(bts, msg_type, ref, data, + len_to_send); + + /* last segment */ + + oh = (struct abis_om_hdr *)msgb_put(msg, + sizeof(struct + abis_om_hdr) + + len_to_send); + + oh->mdisc = ABIS_OM_MDISC_FOM; + oh->placement = ABIS_OM_PLACEMENT_LAST; /* last segment of multi-segment message */ + oh->sequence = seq; + oh->length = len_to_send; + + memcpy(oh->data, data, len_to_send); + } + + DEBUGPC(DNM, "Sending multi-segment %d\n", seq); + + ret = abis_nm_sendmsg(bts, msg); + if (ret < 0) + return ret; + + nokia_abis_nm_queue_send_next(bts); + + /* next segment */ + len_remain -= len_to_send; + data += len_to_send; + seq++; + } + return ret; +} + +/* TODO: put in a separate file ? */ + +static int abis_nm_send_config(struct gsm_bts *bts, uint8_t bts_type) +{ + struct gsm_bts_trx *trx; + uint8_t config[2048]; /* TODO: might be too small if lots of TRX are used */ + int len = 0; + int idx = 0; + int ret; + int hopping = 0; + int need_hopping = 0; + + memset(config, 0, sizeof(config)); + + llist_for_each_entry(trx, &bts->trx_list, list) { +#if 0 /* debugging */ + printf("TRX\n"); + printf(" arfcn: %d\n", trx->arfcn); + printf(" bsic: %d\n", trx->bts->bsic); + uint8_t ca[20]; + memset(ca, 0xFF, sizeof(ca)); + ret = generate_cell_chan_list(ca, trx->bts); + printf(" ca (%d): %s\n", ret, osmo_hexdump(ca, sizeof(ca))); + int i; + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + + printf(" pchan %d: %d\n", i, ts->pchan); + } +#endif + ret = make_fu_config(trx, idx + 1, config + len, &hopping); + need_hopping |= hopping; + len += ret; + + idx++; + } + + ret = make_bts_config(bts_type, idx, config + len, need_hopping); + len += ret; + +#if 0 /* debugging */ + dump_elements(config, len); +#endif + + return abis_nm_send_multi_segments(bts, NOKIA_MSG_CONF_DATA, 1, config, + len); +} + +#define GET_NEXT_BYTE if(idx >= len) return 0; \ + ub = data[idx++]; + +static int find_element(uint8_t * data, int len, uint16_t id, uint8_t * value, + int max_value) +{ + uint8_t ub; + int idx = 0; + int found = 0; + int constructed __attribute__((unused)); + uint16_t id_value; + + for (;;) { + + GET_NEXT_BYTE; + + /* encoding bit, construced means that other elements are contained */ + constructed = ((ub & 0x20) ? 1 : 0); + + if ((ub & 0x1F) == 0x1F) { + /* fixed pattern, ID follows */ + GET_NEXT_BYTE; /* ID */ + id_value = ub & 0x7F; + if (ub & 0x80) { + /* extension bit */ + GET_NEXT_BYTE; /* ID low part */ + id_value = (id_value << 7) | (ub & 0x7F); + } + if (id_value == id) + found = 1; + } else { + id_value = (ub & 0x3F); + if (id_value == id) + found = 1; + } + + GET_NEXT_BYTE; /* length */ + + if (found) { + /* get data */ + uint8_t n = ub; + uint8_t i; + for (i = 0; i < n; i++) { + GET_NEXT_BYTE; + if (max_value <= 0) + return -1; /* buffer too small */ + *value = ub; + value++; + max_value--; + } + return n; /* length */ + } else { + /* skip data */ + uint8_t n = ub; + uint8_t i; + for (i = 0; i < n; i++) { + GET_NEXT_BYTE; + } + } + } + return 0; /* not found */ +} + +static int dump_elements(uint8_t * data, int len) +{ + uint8_t ub; + int idx = 0; + int constructed; + uint16_t id_value; + static char indent[100] = ""; /* TODO: move static to BTS context */ + + for (;;) { + + GET_NEXT_BYTE; + + /* encoding bit, construced means that other elements are contained */ + constructed = ((ub & 0x20) ? 1 : 0); + + if ((ub & 0x1F) == 0x1F) { + /* fixed pattern, ID follows */ + GET_NEXT_BYTE; /* ID */ + id_value = ub & 0x7F; + if (ub & 0x80) { + /* extension bit */ + GET_NEXT_BYTE; /* ID low part */ + id_value = (id_value << 7) | (ub & 0x7F); + } + + } else { + id_value = (ub & 0x3F); + } + + GET_NEXT_BYTE; /* length */ + + printf("%s--ID = 0x%02X (%s) %s\n", indent, id_value, + get_element_name_string(id_value), + constructed ? "** constructed **" : ""); + printf("%s length = %d\n", indent, ub); + printf("%s %s\n", indent, osmo_hexdump(data + idx, ub)); + + if (constructed) { + int indent_len = strlen(indent); + strcat(indent, " "); + + dump_elements(data + idx, ub); + + indent[indent_len] = 0; + } + /* skip data */ + uint8_t n = ub; + uint8_t i; + for (i = 0; i < n; i++) { + GET_NEXT_BYTE; + } + } + return 0; +} + +/* TODO: put in a separate file ? */ + +/* taken from abis_nm.c */ + +static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts) +{ + int wait = 0; + struct msgb *msg; + /* the queue is empty */ + while (!llist_empty(&bts->abis_queue)) { + msg = msgb_dequeue(&bts->abis_queue); + wait = OBSC_NM_W_ACK_CB(msg); + abis_sendmsg(msg); + + if (wait) + break; + } + + bts->abis_nm_pend = wait; +} + +/* TODO: put in a separate file ? */ + +/* timer for restarting OML after BTS reset */ + +static void reset_timer_cb(void *_bts) +{ + struct gsm_bts *bts = _bts; + struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; + struct e1inp_line *line; + + bts->nokia.wait_reset = 0; + + /* OML link */ + line = e1inp_line_find(e1_link->e1_nr); + if (!line) { + LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " + "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); + return; + } + + start_sabm_in_line(line, 0, -1); /* stop all first */ + start_sabm_in_line(line, 1, SAPI_OML); /* start only OML */ +} + +/* TODO: put in a separate file ? */ + +/* + This is how the configuration is done: + - start OML link + - reset BTS + - receive ACK, wait some time and restart OML link + - receive OMU STARTED message, send START DOWNLOAD REQ + - receive CNF REQ message, send CONF DATA + - receive ACK, start RSL link(s) + ACK some other messages received from the BTS. + + Probably its also possible to configure the BTS without a reset, this + has not been tested yet. +*/ + +static int abis_nm_rcvmsg_fom(struct msgb *mb) +{ + struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)mb->dst; + struct gsm_bts *bts = sign_link->trx->bts; + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_nokia_hdr *noh = msgb_l3(mb); + uint8_t mt = noh->msg_type; + int ret = 0; + uint16_t ref = ntohs(noh->reference); + uint8_t info[256]; + uint8_t ack = 0xFF; + uint8_t severity = 0xFF; + int str_len; + int len_data; + + if (bts->nokia.wait_reset) { + LOGP(DNM, LOGL_INFO, + "Ignore message while waiting for reset\n"); + return ret; + } + + if (oh->length < sizeof(struct abis_om_nokia_hdr)) { + LOGP(DNM, LOGL_ERROR, "Message too short\n"); + return -EINVAL; + } + + len_data = oh->length - sizeof(struct abis_om_nokia_hdr); + LOGP(DNM, LOGL_INFO, "(0x%02X) %s\n", mt, get_msg_type_name_string(mt)); +#if 0 /* debugging */ + dump_elements(noh->data, len_data); +#endif + + switch (mt) { + case NOKIA_MSG_OMU_STARTED: + if (find_element(noh->data, len_data, + NOKIA_EI_BTS_TYPE, &bts->nokia.bts_type, + sizeof(uint8_t)) == sizeof(uint8_t)) + LOGP(DNM, LOGL_INFO, "BTS type = %d (%s)\n", + bts->nokia.bts_type, + get_bts_type_string(bts->nokia.bts_type)); + else + LOGP(DNM, LOGL_ERROR, "BTS type not found\n"); + /* send START_DOWNLOAD_REQ */ + abis_nm_download_req(bts, ref); + break; + case NOKIA_MSG_MF_REQ: + break; + case NOKIA_MSG_CONF_REQ: + /* send ACK */ + abis_nm_ack(bts, ref); + nokia_abis_nm_queue_send_next(bts); + /* send CONF_DATA */ + abis_nm_send_config(bts, bts->nokia.bts_type); + bts->nokia.configured = 1; + break; + case NOKIA_MSG_ACK: + if (find_element + (noh->data, len_data, NOKIA_EI_ACK, &ack, + sizeof(uint8_t)) == sizeof(uint8_t)) { + LOGP(DNM, LOGL_INFO, "ACK = %d\n", ack); + if (ack != 1) { + LOGP(DNM, LOGL_ERROR, "No ACK received (%d)\n", + ack); + /* TODO: properly handle failures (NACK) */ + } + } else + LOGP(DNM, LOGL_ERROR, "ACK not found\n"); + + /* TODO: the assumption for the following is that no NACK was received */ + + /* ACK for reset message ? */ + if (!bts->nokia.did_reset) { + bts->nokia.did_reset = 1; + + /* + TODO: For the InSite processing the received data is + blocked in the driver during reset. + Otherwise the LAPD module might assert because the InSite + sends garbage on the E1 line during reset. + This is done by looking at "wait_reset" in the driver + (function handle_ts1_read()) and ignoring the received data. + It seems to be necessary for the MetroSite too. + */ + bts->nokia.wait_reset = 1; + + bts->nokia.reset_timer.cb = &reset_timer_cb; + bts->nokia.reset_timer.data = bts; + osmo_timer_schedule(&bts->nokia.reset_timer, RESET_INTERVAL); + + 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); + + 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/src/libbsc/bts_siemens_bs11.c b/src/libbsc/bts_siemens_bs11.c new file mode 100644 index 000000000..101e996dd --- /dev/null +++ b/src/libbsc/bts_siemens_bs11.c @@ -0,0 +1,607 @@ +/* Siemens BS-11 specific code */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include + +#include +#include +#include +#include +#include + +static int bts_model_bs11_start(struct gsm_network *net); + +static void bts_model_bs11_e1line_bind_ops(struct e1inp_line *line) +{ + e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); +} + +static struct gsm_bts_model model_bs11 = { + .type = GSM_BTS_TYPE_BS11, + .name = "bs11", + .start = bts_model_bs11_start, + .oml_rcvmsg = &abis_nm_rcvmsg, + .e1line_bind_ops = bts_model_bs11_e1line_bind_ops, + .nm_att_tlvdef = { + .def = { + [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TLV }, + /* BS11 specifics */ + [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV }, + [0xd5] = { TLV_TYPE_TLV }, + [0xa8] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV }, + [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV }, + [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV }, + [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV }, + [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV }, + [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV }, + [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV }, + [0x95] = { TLV_TYPE_FIXED, 2 }, + }, + }, +}; + +/* The following definitions are for OM and NM packets that we cannot yet + * generate by code but we just pass on */ + +// BTS Site Manager, SET ATTRIBUTES + +/* + Object Class: BTS Site Manager + Instance 1: FF + Instance 2: FF + Instance 3: FF +SET ATTRIBUTES + sAbisExternalTime: 2007/09/08 14:36:11 + omLAPDRelTimer: 30sec + shortLAPDIntTimer: 5sec + emergencyTimer1: 10 minutes + emergencyTimer2: 0 minutes +*/ + +unsigned char msg_1[] = +{ + NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, 0xFF, 0xFF, 0xFF, + NM_ATT_BS11_ABIS_EXT_TIME, 0x07, + 0xD7, 0x09, 0x08, 0x0E, 0x24, 0x0B, 0xCE, + 0x02, + 0x00, 0x1E, + NM_ATT_BS11_SH_LAPD_INT_TIMER, + 0x01, 0x05, + 0x42, 0x02, 0x00, 0x0A, + 0x44, 0x02, 0x00, 0x00 +}; + +// BTS, SET BTS ATTRIBUTES + +/* + Object Class: BTS + BTS relat. Number: 0 + Instance 2: FF + Instance 3: FF +SET BTS ATTRIBUTES + bsIdentityCode / BSIC: + PLMN_colour_code: 7h + BS_colour_code: 7h + BTS Air Timer T3105: 4 ,unit 10 ms + btsIsHopping: FALSE + periodCCCHLoadIndication: 1sec + thresholdCCCHLoadIndication: 0% + cellAllocationNumber: 00h = GSM 900 + enableInterferenceClass: 00h = Disabled + fACCHQual: 6 (FACCH stealing flags minus 1) + intaveParameter: 31 SACCH multiframes + interferenceLevelBoundaries: + Interference Boundary 1: 0Ah + Interference Boundary 2: 0Fh + Interference Boundary 3: 14h + Interference Boundary 4: 19h + Interference Boundary 5: 1Eh + mSTxPwrMax: 11 + GSM range: 2=39dBm, 15=13dBm, stepsize 2 dBm + DCS1800 range: 0=30dBm, 15=0dBm, stepsize 2 dBm + PCS1900 range: 0=30dBm, 15=0dBm, stepsize 2 dBm + 30=33dBm, 31=32dBm + ny1: + Maximum number of repetitions for PHYSICAL INFORMATION message (GSM 04.08): 20 + powerOutputThresholds: + Out Power Fault Threshold: -10 dB + Red Out Power Threshold: - 6 dB + Excessive Out Power Threshold: 5 dB + rACHBusyThreshold: -127 dBm + rACHLoadAveragingSlots: 250 ,number of RACH burst periods + rfResourceIndicationPeriod: 125 SACCH multiframes + T200: + SDCCH: 044 in 5 ms + FACCH/Full rate: 031 in 5 ms + FACCH/Half rate: 041 in 5 ms + SACCH with TCH SAPI0: 090 in 10 ms + SACCH with SDCCH: 090 in 10 ms + SDCCH with SAPI3: 090 in 5 ms + SACCH with TCH SAPI3: 135 in 10 ms + tSync: 9000 units of 10 msec + tTrau: 9000 units of 10 msec + enableUmLoopTest: 00h = disabled + enableExcessiveDistance: 00h = Disabled + excessiveDistance: 64km + hoppingMode: 00h = baseband hopping + cellType: 00h = Standard Cell + BCCH ARFCN / bCCHFrequency: 1 +*/ + +static unsigned char bs11_attr_bts[] = +{ + NM_ATT_BSIC, HARDCODED_BSIC, + NM_ATT_BTS_AIR_TIMER, 0x04, + NM_ATT_BS11_BTSLS_HOPPING, 0x00, + NM_ATT_CCCH_L_I_P, 0x01, + NM_ATT_CCCH_L_T, 0x00, + NM_ATT_BS11_CELL_ALLOC_NR, NM_BS11_CANR_GSM, + NM_ATT_BS11_ENA_INTERF_CLASS, 0x01, + NM_ATT_BS11_FACCH_QUAL, 0x06, + /* interference avg. period in numbers of SACCH multifr */ + NM_ATT_INTAVE_PARAM, 0x1F, + NM_ATT_INTERF_BOUND, 0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x7B, + NM_ATT_CCCH_L_T, 0x23, + NM_ATT_GSM_TIME, 0x28, 0x00, + NM_ATT_ADM_STATE, 0x03, + NM_ATT_RACH_B_THRESH, 0x7F, + NM_ATT_LDAVG_SLOTS, 0x00, 0xFA, + NM_ATT_BS11_RF_RES_IND_PER, 0x7D, + NM_ATT_T200, 0x2C, 0x1F, 0x29, 0x5A, 0x5A, 0x5A, 0x87, + NM_ATT_BS11_TSYNC, 0x23, 0x28, + NM_ATT_BS11_TTRAU, 0x23, 0x28, + NM_ATT_TEST_DUR, 0x01, 0x00, + NM_ATT_OUTST_ALARM, 0x01, 0x00, + NM_ATT_BS11_EXCESSIVE_DISTANCE, 0x01, 0x40, + NM_ATT_BS11_HOPPING_MODE, 0x01, 0x00, + NM_ATT_BS11_PLL, 0x01, 0x00, + NM_ATT_BCCH_ARFCN, 0x00, HARDCODED_ARFCN/*0x01*/, +}; + +// Handover Recognition, SET ATTRIBUTES + +/* +Illegal Contents GSM Formatted O&M Msg + Object Class: Handover Recognition + BTS relat. Number: 0 + Instance 2: FF + Instance 3: FF +SET ATTRIBUTES + enableDelayPowerBudgetHO: 00h = Disabled + enableDistanceHO: 00h = Disabled + enableInternalInterCellHandover: 00h = Disabled + enableInternalIntraCellHandover: 00h = Disabled + enablePowerBudgetHO: 00h = Disabled + enableRXLEVHO: 00h = Disabled + enableRXQUALHO: 00h = Disabled + hoAveragingDistance: 8 SACCH multiframes + hoAveragingLev: + A_LEV_HO: 8 SACCH multiframes + W_LEV_HO: 1 SACCH multiframes + hoAveragingPowerBudget: 16 SACCH multiframes + hoAveragingQual: + A_QUAL_HO: 8 SACCH multiframes + W_QUAL_HO: 2 SACCH multiframes + hoLowerThresholdLevDL: (10 - 110) dBm + hoLowerThresholdLevUL: (5 - 110) dBm + hoLowerThresholdQualDL: 06h = 6.4% < BER < 12.8% + hoLowerThresholdQualUL: 06h = 6.4% < BER < 12.8% + hoThresholdLevDLintra : (20 - 110) dBm + hoThresholdLevULintra: (20 - 110) dBm + hoThresholdMsRangeMax: 20 km + nCell: 06h + timerHORequest: 3 ,unit 2 SACCH multiframes +*/ + +unsigned char msg_3[] = +{ + NM_MT_BS11_SET_ATTR, NM_OC_BS11_HANDOVER, 0x00, 0xFF, 0xFF, + 0xD0, 0x00, /* enableDelayPowerBudgetHO */ + 0x64, 0x00, /* enableDistanceHO */ + 0x67, 0x00, /* enableInternalInterCellHandover */ + 0x68, 0x00, /* enableInternalInterCellHandover */ + 0x6A, 0x00, /* enablePowerBudgetHO */ + 0x6C, 0x00, /* enableRXLEVHO */ + 0x6D, 0x00, /* enableRXQUALHO */ + 0x6F, 0x08, /* hoAveragingDistance */ + 0x70, 0x08, 0x01, /* hoAveragingLev */ + 0x71, 0x10, 0x10, 0x10, + 0x72, 0x08, 0x02, /* hoAveragingQual */ + 0x73, 0x0A, /* hoLowerThresholdLevDL */ + 0x74, 0x05, /* hoLowerThresholdLevUL */ + 0x75, 0x06, /* hoLowerThresholdQualDL */ + 0x76, 0x06, /* hoLowerThresholdQualUL */ + 0x78, 0x14, /* hoThresholdLevDLintra */ + 0x79, 0x14, /* hoThresholdLevULintra */ + 0x7A, 0x14, /* hoThresholdMsRangeMax */ + 0x7D, 0x06, /* nCell */ + NM_ATT_BS11_TIMER_HO_REQUEST, 0x03, + 0x20, 0x01, 0x00, + 0x45, 0x01, 0x00, + 0x48, 0x01, 0x00, + 0x5A, 0x01, 0x00, + 0x5B, 0x01, 0x05, + 0x5E, 0x01, 0x1A, + 0x5F, 0x01, 0x20, + 0x9D, 0x01, 0x00, + 0x47, 0x01, 0x00, + 0x5C, 0x01, 0x64, + 0x5D, 0x01, 0x1E, + 0x97, 0x01, 0x20, + 0xF7, 0x01, 0x3C, +}; + +// Power Control, SET ATTRIBUTES + +/* + Object Class: Power Control + BTS relat. Number: 0 + Instance 2: FF + Instance 3: FF +SET ATTRIBUTES + enableMsPowerControl: 00h = Disabled + enablePowerControlRLFW: 00h = Disabled + pcAveragingLev: + A_LEV_PC: 4 SACCH multiframes + W_LEV_PC: 1 SACCH multiframes + pcAveragingQual: + A_QUAL_PC: 4 SACCH multiframes + W_QUAL_PC: 2 SACCH multiframes + pcLowerThresholdLevDL: 0Fh + pcLowerThresholdLevUL: 0Ah + pcLowerThresholdQualDL: 05h = 3.2% < BER < 6.4% + pcLowerThresholdQualUL: 05h = 3.2% < BER < 6.4% + pcRLFThreshold: 0Ch + pcUpperThresholdLevDL: 14h + pcUpperThresholdLevUL: 0Fh + pcUpperThresholdQualDL: 04h = 1.6% < BER < 3.2% + pcUpperThresholdQualUL: 04h = 1.6% < BER < 3.2% + powerConfirm: 2 ,unit 2 SACCH multiframes + powerControlInterval: 2 ,unit 2 SACCH multiframes + powerIncrStepSize: 02h = 4 dB + powerRedStepSize: 01h = 2 dB + radioLinkTimeoutBs: 64 SACCH multiframes + enableBSPowerControl: 00h = disabled +*/ + +unsigned char msg_4[] = +{ + NM_MT_BS11_SET_ATTR, NM_OC_BS11_PWR_CTRL, 0x00, 0xFF, 0xFF, + NM_ATT_BS11_ENA_MS_PWR_CTRL, 0x00, + NM_ATT_BS11_ENA_PWR_CTRL_RLFW, 0x00, + 0x7E, 0x04, 0x01, /* pcAveragingLev */ + 0x7F, 0x04, 0x02, /* pcAveragingQual */ + 0x80, 0x0F, /* pcLowerThresholdLevDL */ + 0x81, 0x0A, /* pcLowerThresholdLevUL */ + 0x82, 0x05, /* pcLowerThresholdQualDL */ + 0x83, 0x05, /* pcLowerThresholdQualUL */ + 0x84, 0x0C, /* pcRLFThreshold */ + 0x85, 0x14, /* pcUpperThresholdLevDL */ + 0x86, 0x0F, /* pcUpperThresholdLevUL */ + 0x87, 0x04, /* pcUpperThresholdQualDL */ + 0x88, 0x04, /* pcUpperThresholdQualUL */ + 0x89, 0x02, /* powerConfirm */ + 0x8A, 0x02, /* powerConfirmInterval */ + 0x8B, 0x02, /* powerIncrStepSize */ + 0x8C, 0x01, /* powerRedStepSize */ + 0x8D, 0x40, /* radioLinkTimeoutBs */ + 0x65, 0x01, 0x00 // set to 0x01 to enable BSPowerControl +}; + + +// Transceiver, SET TRX ATTRIBUTES (TRX 0) + +/* + Object Class: Transceiver + BTS relat. Number: 0 + Tranceiver number: 0 + Instance 3: FF +SET TRX ATTRIBUTES + aRFCNList (HEX): 0001 + txPwrMaxReduction: 00h = 30dB + radioMeasGran: 254 SACCH multiframes + radioMeasRep: 01h = enabled + memberOfEmergencyConfig: 01h = TRUE + trxArea: 00h = TRX doesn't belong to a concentric cell +*/ + +static unsigned char bs11_attr_radio[] = +{ + NM_ATT_ARFCN_LIST, 0x01, 0x00, HARDCODED_ARFCN /*0x01*/, + NM_ATT_RF_MAXPOWR_R, 0x00, + NM_ATT_BS11_RADIO_MEAS_GRAN, 0x01, 0x05, + NM_ATT_BS11_RADIO_MEAS_REP, 0x01, 0x01, + NM_ATT_BS11_EMRG_CFG_MEMBER, 0x01, 0x01, + NM_ATT_BS11_TRX_AREA, 0x01, 0x00, +}; + +/* + * Patch the various SYSTEM INFORMATION tables to update + * the LAI + */ +static void patch_nm_tables(struct gsm_bts *bts) +{ + uint8_t arfcn_low = bts->c0->arfcn & 0xff; + uint8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; + + /* T3105 attribute in units of 10ms */ + bs11_attr_bts[2] = bts->network->T3105 / 10; + + /* patch ARFCN into BTS Attributes */ + bs11_attr_bts[69] &= 0xf0; + bs11_attr_bts[69] |= arfcn_high; + bs11_attr_bts[70] = arfcn_low; + + /* patch ARFCN into TRX Attributes */ + bs11_attr_radio[2] &= 0xf0; + bs11_attr_radio[2] |= arfcn_high; + bs11_attr_radio[3] = arfcn_low; + + /* patch the RACH attributes */ + if (bts->rach_b_thresh != -1) + bs11_attr_bts[33] = bts->rach_b_thresh & 0xff; + + if (bts->rach_ldavg_slots != -1) { + uint8_t avg_high = bts->rach_ldavg_slots & 0xff; + uint8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; + + bs11_attr_bts[35] = avg_high; + bs11_attr_bts[36] = avg_low; + } + + /* patch BSIC */ + bs11_attr_bts[1] = bts->bsic; + + /* patch the power reduction */ + bs11_attr_radio[5] = bts->c0->max_power_red / 2; +} + + +static void nm_reconfig_ts(struct gsm_bts_trx_ts *ts) +{ + enum abis_nm_chan_comb ccomb = abis_nm_chcomb4pchan(ts->pchan); + struct gsm_e1_subslot *e1l = &ts->e1_link; + + abis_nm_set_channel_attr(ts, ccomb); + + if (is_ipaccess_bts(ts->trx->bts)) + return; + + switch (ts->pchan) { + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts, + e1l->e1_ts_ss); + break; + default: + break; + } +} + +static void nm_reconfig_trx(struct gsm_bts_trx *trx) +{ + struct gsm_e1_subslot *e1l = &trx->rsl_e1_link; + int i; + + patch_nm_tables(trx->bts); + + switch (trx->bts->type) { + case GSM_BTS_TYPE_BS11: + /* FIXME: discover this by fetching an attribute */ +#if 0 + trx->nominal_power = 15; /* 15dBm == 30mW PA configuration */ +#else + trx->nominal_power = 24; /* 24dBm == 250mW PA configuration */ +#endif + abis_nm_conn_terr_sign(trx, e1l->e1_nr, e1l->e1_ts, + e1l->e1_ts_ss); + abis_nm_establish_tei(trx->bts, trx->nr, e1l->e1_nr, + e1l->e1_ts, e1l->e1_ts_ss, trx->rsl_tei); + + /* Set Radio Attributes */ + if (trx == trx->bts->c0) + abis_nm_set_radio_attr(trx, bs11_attr_radio, + sizeof(bs11_attr_radio)); + else { + uint8_t trx1_attr_radio[sizeof(bs11_attr_radio)]; + uint8_t arfcn_low = trx->arfcn & 0xff; + uint8_t arfcn_high = (trx->arfcn >> 8) & 0x0f; + memcpy(trx1_attr_radio, bs11_attr_radio, + sizeof(trx1_attr_radio)); + + /* patch ARFCN into TRX Attributes */ + trx1_attr_radio[2] &= 0xf0; + trx1_attr_radio[2] |= arfcn_high; + trx1_attr_radio[3] = arfcn_low; + + abis_nm_set_radio_attr(trx, trx1_attr_radio, + sizeof(trx1_attr_radio)); + } + break; + case GSM_BTS_TYPE_NANOBTS: + switch (trx->bts->band) { + case GSM_BAND_850: + case GSM_BAND_900: + trx->nominal_power = 20; + break; + case GSM_BAND_1800: + case GSM_BAND_1900: + trx->nominal_power = 23; + break; + default: + LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n", + gsm_band_name(trx->bts->band)); + break; + } + break; + default: + break; + } + + for (i = 0; i < TRX_NR_TS; i++) + nm_reconfig_ts(&trx->ts[i]); +} + +static void nm_reconfig_bts(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + patch_nm_tables(bts); + abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/ + abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts)); + abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */ + abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */ + break; + default: + break; + } + + llist_for_each_entry(trx, &bts->trx_list, list) + nm_reconfig_trx(trx); +} + + +static void bootstrap_om_bs11(struct gsm_bts *bts) +{ + LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); + + /* stop sending event reports */ + abis_nm_event_reports(bts, 0); + + /* begin DB transmission */ + abis_nm_bs11_db_transmission(bts, 1); + + /* end DB transmission */ + abis_nm_bs11_db_transmission(bts, 0); + + /* Reset BTS Site manager resource */ + abis_nm_bs11_reset_resource(bts); + + /* begin DB transmission */ + abis_nm_bs11_db_transmission(bts, 1); + + /* reconfigure BTS with all TRX and all TS */ + nm_reconfig_bts(bts); + + /* end DB transmission */ + abis_nm_bs11_db_transmission(bts, 0); + + /* Reset BTS Site manager resource */ + abis_nm_bs11_reset_resource(bts); + + /* restart sending event reports */ + abis_nm_event_reports(bts, 1); +} + +static int shutdown_om(struct gsm_bts *bts) +{ + /* stop sending event reports */ + abis_nm_event_reports(bts, 0); + + /* begin DB transmission */ + abis_nm_bs11_db_transmission(bts, 1); + + /* end DB transmission */ + abis_nm_bs11_db_transmission(bts, 0); + + /* Reset BTS Site manager resource */ + abis_nm_bs11_reset_resource(bts); + + return 0; +} + +/* Callback function to be called every time we receive a signal from INPUT */ +static int gbl_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_bts *bts; + + if (subsys != SS_L_GLOBAL) + return 0; + + switch (signal) { + case S_GLOBAL_BTS_CLOSE_OM: + bts = signal_data; + if (bts->type == GSM_BTS_TYPE_BS11) + shutdown_om(signal_data); + break; + } + + return 0; +} + +/* Callback function to be called every time we receive a signal from INPUT */ +static int inp_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct input_signal_data *isd = signal_data; + + if (subsys != SS_L_INPUT) + return 0; + + switch (signal) { + case S_L_INP_TEI_UP: + switch (isd->link_type) { + case E1INP_SIGN_OML: + if (isd->trx->bts->type == GSM_BTS_TYPE_BS11) + bootstrap_om_bs11(isd->trx->bts); + break; + } + } + + return 0; +} + +static int bts_model_bs11_start(struct gsm_network *net) +{ + model_bs11.features.data = &model_bs11._features_data[0]; + model_bs11.features.data_len = sizeof(model_bs11._features_data); + + gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HOPPING); + gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HSCSD); + + 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/src/libbsc/bts_sysmobts.c b/src/libbsc/bts_sysmobts.c new file mode 100644 index 000000000..754e277e5 --- /dev/null +++ b/src/libbsc/bts_sysmobts.c @@ -0,0 +1,59 @@ +/* sysmocom sysmoBTS specific code */ + +/* (C) 2010-2012 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct gsm_bts_model bts_model_nanobts; + +static struct gsm_bts_model model_sysmobts; + +int bts_model_sysmobts_init(void) +{ + model_sysmobts = bts_model_nanobts; + model_sysmobts.name = "sysmobts"; + model_sysmobts.type = GSM_BTS_TYPE_OSMO_SYSMO; + + model_sysmobts.features.data = &model_sysmobts._features_data[0]; + model_sysmobts.features.data_len = + sizeof(model_sysmobts._features_data); + + 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/src/libbsc/bts_unknown.c b/src/libbsc/bts_unknown.c new file mode 100644 index 000000000..f1135294f --- /dev/null +++ b/src/libbsc/bts_unknown.c @@ -0,0 +1,40 @@ +/* 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/src/libbsc/chan_alloc.c b/src/libbsc/chan_alloc.c new file mode 100644 index 000000000..9b74329f4 --- /dev/null +++ b/src/libbsc/chan_alloc.c @@ -0,0 +1,493 @@ +/* GSM Channel allocation routines + * + * (C) 2008 by Harald Welte + * (C) 2008, 2009 by Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +static int ts_is_usable(struct gsm_bts_trx_ts *ts) +{ + /* FIXME: How does this behave for BS-11 ? */ + if (is_ipaccess_bts(ts->trx->bts)) { + if (!nm_is_running(&ts->mo.nm_state)) + return 0; + } + + return 1; +} + +int trx_is_usable(struct gsm_bts_trx *trx) +{ + /* FIXME: How does this behave for BS-11 ? */ + if (is_ipaccess_bts(trx->bts)) { + if (!nm_is_running(&trx->mo.nm_state) || + !nm_is_running(&trx->bb_transc.mo.nm_state)) + return 0; + } + + return 1; +} + +struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts, + enum gsm_phys_chan_config pchan) +{ + struct gsm_bts_trx *trx = bts->c0; + struct gsm_bts_trx_ts *ts = &trx->ts[0]; + + if (pchan != GSM_PCHAN_CCCH && + pchan != GSM_PCHAN_CCCH_SDCCH4) + return NULL; + + if (ts->pchan != GSM_PCHAN_NONE) + return NULL; + + ts->pchan = pchan; + + return ts; +} + +/* Allocate a physical channel (TS) */ +struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts, + enum gsm_phys_chan_config pchan) +{ + int j; + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + int from, to; + + if (!trx_is_usable(trx)) + continue; + + /* the following constraints are pure policy, + * no requirement to put this restriction in place */ + if (trx == bts->c0) { + /* On the first TRX we run one CCCH and one SDCCH8 */ + switch (pchan) { + case GSM_PCHAN_CCCH: + case GSM_PCHAN_CCCH_SDCCH4: + from = 0; to = 0; + break; + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + from = 1; to = 7; + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + default: + return NULL; + } + } else { + /* Every secondary TRX is configured for TCH/F + * and TCH/H only */ + switch (pchan) { + case GSM_PCHAN_SDCCH8_SACCH8C: + from = 1; to = 1; + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + from = 1; to = 7; + break; + default: + return NULL; + } + } + + for (j = from; j <= to; j++) { + struct gsm_bts_trx_ts *ts = &trx->ts[j]; + + if (!ts_is_usable(ts)) + continue; + + if (ts->pchan == GSM_PCHAN_NONE) { + ts->pchan = pchan; + /* set channel attribute on OML */ + abis_nm_set_channel_attr(ts, abis_nm_chcomb4pchan(pchan)); + return ts; + } + } + } + return NULL; +} + +/* Free a physical channel (TS) */ +void ts_free(struct gsm_bts_trx_ts *ts) +{ + ts->pchan = GSM_PCHAN_NONE; +} + +static const uint8_t subslots_per_pchan[] = { + [GSM_PCHAN_NONE] = 0, + [GSM_PCHAN_CCCH] = 0, + [GSM_PCHAN_CCCH_SDCCH4] = 4, + [GSM_PCHAN_TCH_F] = 1, + [GSM_PCHAN_TCH_H] = 2, + [GSM_PCHAN_SDCCH8_SACCH8C] = 8, + /* FIXME: what about dynamic TCH_F_TCH_H ? */ + [GSM_PCHAN_TCH_F_PDCH] = 1, +}; + +static struct gsm_lchan * +_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan) +{ + struct gsm_bts_trx_ts *ts; + int j, ss; + + if (!trx_is_usable(trx)) + return NULL; + + for (j = 0; j < 8; j++) { + ts = &trx->ts[j]; + if (!ts_is_usable(ts)) + continue; + /* ip.access dynamic TCH/F + PDCH combination */ + if (ts->pchan == GSM_PCHAN_TCH_F_PDCH && + pchan == GSM_PCHAN_TCH_F) { + /* we can only consider such a dynamic channel + * if the PDCH is currently inactive */ + if (ts->flags & TS_F_PDCH_MODE) + continue; + } else if (ts->pchan != pchan) + continue; + /* check if all sub-slots are allocated yet */ + for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) { + struct gsm_lchan *lc = &ts->lchan[ss]; + if (lc->type == GSM_LCHAN_NONE && + lc->state == LCHAN_S_NONE) + return lc; + } + } + + return NULL; +} + +static struct gsm_lchan * +_lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) +{ + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + struct gsm_lchan *lc; + + if (bts->chan_alloc_reverse) { + llist_for_each_entry_reverse(trx, &bts->trx_list, list) { + lc = _lc_find_trx(trx, pchan); + if (lc) + return lc; + } + } else { + llist_for_each_entry(trx, &bts->trx_list, list) { + lc = _lc_find_trx(trx, pchan); + if (lc) + return lc; + } + } + + /* we cannot allocate more of these */ + if (pchan == GSM_PCHAN_CCCH_SDCCH4) + return NULL; + + /* if we've reached here, we need to allocate a new physical + * channel for the logical channel type requested */ + ts = ts_alloc(bts, pchan); + if (!ts) { + /* no more radio resources */ + return NULL; + } + return &ts->lchan[0]; +} + +/* Allocate a logical channel */ +struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, + int allow_bigger) +{ + struct gsm_lchan *lchan = NULL; + enum gsm_phys_chan_config first, second; + + switch (type) { + case GSM_LCHAN_SDCCH: + if (bts->chan_alloc_reverse) { + first = GSM_PCHAN_SDCCH8_SACCH8C; + second = GSM_PCHAN_CCCH_SDCCH4; + } else { + first = GSM_PCHAN_CCCH_SDCCH4; + second = GSM_PCHAN_SDCCH8_SACCH8C; + } + + lchan = _lc_find_bts(bts, first); + if (lchan == NULL) + lchan = _lc_find_bts(bts, second); + + /* allow to assign bigger channels */ + if (allow_bigger) { + if (lchan == NULL) { + lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); + type = GSM_LCHAN_TCH_H; + } + + if (lchan == NULL) { + lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); + type = GSM_LCHAN_TCH_F; + } + } + break; + case GSM_LCHAN_TCH_F: + lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); + break; + case GSM_LCHAN_TCH_H: + lchan =_lc_find_bts(bts, GSM_PCHAN_TCH_H); + /* If we don't have TCH/H available, fall-back to TCH/F */ + if (!lchan) { + lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); + type = GSM_LCHAN_TCH_F; + } + break; + default: + LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type); + } + + if (lchan) { + lchan->type = type; + + /* clear sapis */ + memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis)); + + /* clear multi rate config */ + memset(&lchan->mr_conf, 0, sizeof(lchan->mr_conf)); + } else { + struct challoc_signal_data sig; + sig.bts = bts; + sig.type = type; + osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_ALLOC_FAIL, &sig); + } + + return lchan; +} + +/* Free a logical channel */ +void lchan_free(struct gsm_lchan *lchan) +{ + struct challoc_signal_data sig; + int i; + + sig.type = lchan->type; + lchan->type = GSM_LCHAN_NONE; + + + if (lchan->conn) { + struct lchan_signal_data sig; + + /* We might kill an active channel... */ + sig.lchan = lchan; + sig.mr = NULL; + osmo_signal_dispatch(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, &sig); + } + + if (lchan->abis_ip.rtp_socket) { + LOGP(DRLL, LOGL_ERROR, "%s RTP Proxy Socket remained open.\n", + gsm_lchan_name(lchan)); + rtp_socket_free(lchan->abis_ip.rtp_socket); + lchan->abis_ip.rtp_socket = NULL; + } + + /* stop the timer */ + osmo_timer_del(&lchan->T3101); + + /* clear cached measuement reports */ + lchan->meas_rep_idx = 0; + for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) { + lchan->meas_rep[i].flags = 0; + lchan->meas_rep[i].nr = 0; + } + for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++) + lchan->neigh_meas[i].arfcn = 0; + + if (lchan->rqd_ref) { + talloc_free(lchan->rqd_ref); + lchan->rqd_ref = NULL; + lchan->rqd_ta = 0; + } + + sig.lchan = lchan; + sig.bts = lchan->ts->trx->bts; + osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_FREED, &sig); + + if (lchan->conn) { + LOGP(DRLL, LOGL_ERROR, "the subscriber connection should be gone.\n"); + lchan->conn = NULL; + } + + /* FIXME: ts_free() the timeslot, if we're the last logical + * channel using it */ +} + +/* + * There was an error with the TRX and we need to forget + * any state so that a lchan can be allocated again after + * the trx is fully usable. + * + * This should be called after lchan_free to force a channel + * be available for allocation again. This means that this + * method will stop the "delay after error"-timer and set the + * state to LCHAN_S_NONE. + */ +void lchan_reset(struct gsm_lchan *lchan) +{ + osmo_timer_del(&lchan->T3101); + osmo_timer_del(&lchan->T3109); + osmo_timer_del(&lchan->T3111); + osmo_timer_del(&lchan->error_timer); + + lchan->type = GSM_LCHAN_NONE; + lchan->state = LCHAN_S_NONE; + + if (lchan->abis_ip.rtp_socket) { + rtp_socket_free(lchan->abis_ip.rtp_socket); + lchan->abis_ip.rtp_socket = NULL; + } +} + +/* Drive the release process of the lchan */ +static void _lchan_handle_release(struct gsm_lchan *lchan, + int sacch_deact, int mode) +{ + /* Release all SAPIs on the local end and continue */ + rsl_release_sapis_from(lchan, 1, RSL_REL_LOCAL_END); + + /* + * Shall we send a RR Release, start T3109 and wait for the + * release indication from the BTS or just take it down (e.g. + * on assignment requests) + */ + if (sacch_deact) { + gsm48_send_rr_release(lchan); + + /* Deactivate the SACCH on the BTS side */ + rsl_deact_sacch(lchan); + rsl_start_t3109(lchan); + } else if (lchan->sapis[0] == LCHAN_SAPI_UNUSED) { + rsl_direct_rf_release(lchan); + } else { + rsl_release_request(lchan, 0, mode); + } +} + +/* Consider releasing the channel now */ +int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode mode) +{ + DEBUGP(DRLL, "%s starting release sequence\n", gsm_lchan_name(lchan)); + rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ); + + lchan->conn = NULL; + _lchan_handle_release(lchan, sacch_deact, mode); + return 1; +} + +static struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) { + struct gsm_bts_trx *trx; + int ts_no, lchan_no; + + llist_for_each_entry(trx, &bts->trx_list, list) { + for (ts_no = 0; ts_no < 8; ++ts_no) { + for (lchan_no = 0; lchan_no < TS_MAX_LCHAN; ++lchan_no) { + struct gsm_lchan *lchan = + &trx->ts[ts_no].lchan[lchan_no]; + if (lchan->conn && subscr == lchan->conn->subscr) + return lchan; + } + } + } + + return NULL; +} + +struct gsm_subscriber_connection *connection_for_subscr(struct gsm_subscriber *subscr) +{ + struct gsm_bts *bts; + struct gsm_network *net = subscr->net; + struct gsm_lchan *lchan; + + llist_for_each_entry(bts, &net->bts_list, list) { + lchan = lchan_find(bts, subscr); + if (lchan) + return lchan->conn; + } + + return NULL; +} + +void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + int i; + + /* skip administratively deactivated tranxsceivers */ + if (!nm_is_running(&trx->mo.nm_state) || + !nm_is_running(&trx->bb_transc.mo.nm_state)) + continue; + + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + struct load_counter *pl = &cl->pchan[ts->pchan]; + int j; + + /* skip administratively deactivated timeslots */ + if (!nm_is_running(&ts->mo.nm_state)) + continue; + + for (j = 0; j < subslots_per_pchan[ts->pchan]; j++) { + struct gsm_lchan *lchan = &ts->lchan[j]; + + pl->total++; + + switch (lchan->state) { + case LCHAN_S_NONE: + break; + default: + pl->used++; + break; + } + } + } + } +} + +void network_chan_load(struct pchan_load *pl, struct gsm_network *net) +{ + struct gsm_bts *bts; + + memset(pl, 0, sizeof(*pl)); + + llist_for_each_entry(bts, &net->bts_list, list) + bts_chan_load(pl, bts); +} + diff --git a/src/libbsc/e1_config.c b/src/libbsc/e1_config.c new file mode 100644 index 000000000..d82b009c7 --- /dev/null +++ b/src/libbsc/e1_config.c @@ -0,0 +1,303 @@ +/* OpenBSC E1 Input code */ + +/* (C) 2008-2010 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SAPI_L2ML 0 +#define SAPI_OML 62 +#define SAPI_RSL 0 /* 63 ? */ + +/* The e1_reconfig_*() functions below tale the configuration present in the + * bts/trx/ts data structures and ensure the E1 configuration reflects the + * timeslot/subslot/TEI configuration */ + +int e1_reconfig_ts(struct gsm_bts_trx_ts *ts) +{ + struct gsm_e1_subslot *e1_link = &ts->e1_link; + struct e1inp_line *line; + struct e1inp_ts *e1_ts; + + DEBUGP(DLMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr); + + if (!e1_link->e1_ts) { + LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) without E1 timeslot?\n", + ts->nr, ts->trx->nr, ts->trx->bts->nr); + return 0; + } + + line = e1inp_line_find(e1_link->e1_nr); + if (!line) { + LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) referring to " + "non-existing E1 line %u\n", ts->nr, ts->trx->nr, + ts->trx->bts->nr, e1_link->e1_nr); + return -ENOMEM; + } + + switch (ts->pchan) { + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + e1_ts = &line->ts[e1_link->e1_ts-1]; + e1inp_ts_config_trau(e1_ts, line, subch_cb); + subch_demux_activate(&e1_ts->trau.demux, e1_link->e1_ts_ss); + break; + default: + break; + } + + return 0; +} + +int e1_reconfig_trx(struct gsm_bts_trx *trx) +{ + struct gsm_e1_subslot *e1_link = &trx->rsl_e1_link; + struct e1inp_ts *sign_ts; + struct e1inp_line *line; + struct e1inp_sign_link *rsl_link; + int i; + + if (!e1_link->e1_ts) { + LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link without " + "timeslot?\n", trx->bts->nr, trx->nr); + return -EINVAL; + } + + /* RSL Link */ + line = e1inp_line_find(e1_link->e1_nr); + if (!line) { + LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link referring " + "to non-existing E1 line %u\n", trx->bts->nr, + trx->nr, e1_link->e1_nr); + return -ENOMEM; + } + sign_ts = &line->ts[e1_link->e1_ts-1]; + e1inp_ts_config_sign(sign_ts, line); + /* Ericsson RBS have a per-TRX OML link in parallel to RSL */ + if (trx->bts->type == GSM_BTS_TYPE_RBS2000) { + struct e1inp_sign_link *oml_link; + oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, trx, + trx->rsl_tei, SAPI_OML); + if (!oml_link) { + LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) OML link creation " + "failed\n", trx->bts->nr, trx->nr); + return -ENOMEM; + } + if (trx->oml_link) + e1inp_sign_link_destroy(trx->oml_link); + trx->oml_link = oml_link; + } + rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, + trx, trx->rsl_tei, SAPI_RSL); + if (!rsl_link) { + LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link creation " + "failed\n", trx->bts->nr, trx->nr); + return -ENOMEM; + } + if (trx->rsl_link) + e1inp_sign_link_destroy(trx->rsl_link); + trx->rsl_link = rsl_link; + + for (i = 0; i < TRX_NR_TS; i++) + e1_reconfig_ts(&trx->ts[i]); + + return 0; +} + +/* this is the generic callback for all ISDN-based BTS. */ +static int bts_isdn_sign_link(struct msgb *msg) +{ + int ret = -EINVAL; + struct e1inp_sign_link *link = msg->dst; + struct gsm_bts *bts; + + log_set_context(BSC_CTX_BTS, link->trx->bts); + switch (link->type) { + case E1INP_SIGN_OML: + bts = link->trx->bts; + ret = bts->model->oml_rcvmsg(msg); + break; + case E1INP_SIGN_RSL: + ret = abis_rsl_rcvmsg(msg); + break; + default: + LOGP(DLMI, LOGL_ERROR, "unknown link type %u\n", link->type); + break; + } + return ret; +} + +struct e1inp_line_ops bts_isdn_e1inp_line_ops = { + .sign_link = bts_isdn_sign_link, +}; + +int e1_reconfig_bts(struct gsm_bts *bts) +{ + struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; + struct e1inp_ts *sign_ts; + struct e1inp_line *line; + struct e1inp_sign_link *oml_link; + struct gsm_bts_trx *trx; + + DEBUGP(DLMI, "e1_reconfig_bts(%u)\n", bts->nr); + + line = e1inp_line_find(e1_link->e1_nr); + if (!line) { + LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " + "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); + return -ENOMEM; + } + + if (!bts->model->e1line_bind_ops) { + LOGP(DLINP, LOGL_ERROR, "no callback to bind E1 line operations\n"); + return -EINVAL; + } + if (!line->ops) + bts->model->e1line_bind_ops(line); + + /* skip signal link initialization, this is done later for these BTS. */ + if (bts->type == GSM_BTS_TYPE_NANOBTS || + bts->type == GSM_BTS_TYPE_OSMO_SYSMO) + return e1inp_line_update(line); + + /* OML link */ + if (!e1_link->e1_ts) { + LOGP(DLINP, LOGL_ERROR, "BTS %u OML link without timeslot?\n", + bts->nr); + return -EINVAL; + } + + sign_ts = &line->ts[e1_link->e1_ts-1]; + e1inp_ts_config_sign(sign_ts, line); + oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, + bts->c0, bts->oml_tei, SAPI_OML); + if (!oml_link) { + LOGP(DLINP, LOGL_ERROR, "BTS %u OML link creation failed\n", + bts->nr); + return -ENOMEM; + } + if (bts->oml_link) + e1inp_sign_link_destroy(bts->oml_link); + bts->oml_link = oml_link; + + llist_for_each_entry(trx, &bts->trx_list, list) + e1_reconfig_trx(trx); + + /* notify E1 input something has changed */ + return e1inp_line_update(line); +} + +#if 0 +/* do some compiled-in configuration for our BTS/E1 setup */ +int e1_config(struct gsm_bts *bts, int cardnr, int release_l2) +{ + struct e1inp_line *line; + struct e1inp_ts *sign_ts; + struct e1inp_sign_link *oml_link, *rsl_link; + struct gsm_bts_trx *trx = bts->c0; + int base_ts; + + switch (bts->nr) { + case 0: + /* First BTS uses E1 TS 01,02,03,04,05 */ + base_ts = HARDCODED_BTS0_TS - 1; + break; + case 1: + /* Second BTS uses E1 TS 06,07,08,09,10 */ + base_ts = HARDCODED_BTS1_TS - 1; + break; + case 2: + /* Third BTS uses E1 TS 11,12,13,14,15 */ + base_ts = HARDCODED_BTS2_TS - 1; + default: + return -EINVAL; + } + + line = talloc_zero(tall_bsc_ctx, struct e1inp_line); + if (!line) + return -ENOMEM; + + /* create E1 timeslots for signalling and TRAU frames */ + e1inp_ts_config(&line->ts[base_ts+1-1], line, E1INP_TS_TYPE_SIGN); + e1inp_ts_config(&line->ts[base_ts+2-1], line, E1INP_TS_TYPE_TRAU); + e1inp_ts_config(&line->ts[base_ts+3-1], line, E1INP_TS_TYPE_TRAU); + + /* create signalling links for TS1 */ + sign_ts = &line->ts[base_ts+1-1]; + oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, + trx, TEI_OML, SAPI_OML); + rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, + trx, TEI_RSL, SAPI_RSL); + + /* create back-links from bts/trx */ + bts->oml_link = oml_link; + trx->rsl_link = rsl_link; + + /* enable subchannel demuxer on TS2 */ + subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 1); + subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 2); + subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 3); + + /* enable subchannel demuxer on TS3 */ + subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 0); + subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 1); + subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 2); + subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 3); + + trx = gsm_bts_trx_num(bts, 1); + if (trx) { + /* create E1 timeslots for TRAU frames of TRX1 */ + e1inp_ts_config(&line->ts[base_ts+4-1], line, E1INP_TS_TYPE_TRAU); + e1inp_ts_config(&line->ts[base_ts+5-1], line, E1INP_TS_TYPE_TRAU); + + /* create RSL signalling link for TRX1 */ + sign_ts = &line->ts[base_ts+1-1]; + rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, + trx, TEI_RSL+1, SAPI_RSL); + /* create back-links from trx */ + trx->rsl_link = rsl_link; + + /* enable subchannel demuxer on TS2 */ + subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 0); + subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 1); + subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 2); + subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 3); + + /* enable subchannel demuxer on TS3 */ + subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 0); + subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 1); + subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 2); + subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 3); + } + + return mi_setup(cardnr, line, release_l2); +} +#endif diff --git a/src/libbsc/gsm_04_08_utils.c b/src/libbsc/gsm_04_08_utils.c new file mode 100644 index 000000000..8ccefd740 --- /dev/null +++ b/src/libbsc/gsm_04_08_utils.c @@ -0,0 +1,651 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 + * utility functions + */ + +/* (C) 2008-2009 by Harald Welte + * (C) 2008, 2009 by Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +/* should ip.access BTS use direct RTP streams between each other (1), + * or should OpenBSC always act as RTP relay/proxy in between (0) ? */ +int ipacc_rtp_direct = 1; + +static int gsm48_sendmsg(struct msgb *msg) +{ + if (msg->lchan) + msg->dst = msg->lchan->ts->trx->rsl_link; + + msg->l3h = msg->data; + return rsl_data_request(msg, 0); +} + +/* Section 9.1.8 / Table 9.9 */ +struct chreq { + uint8_t val; + uint8_t mask; + enum chreq_type type; +}; + +/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */ +static const struct chreq chreq_type_neci1[] = { + { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, + { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F }, + { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H }, + { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL }, + { 0xe0, 0xe0, CHREQ_T_TCH_F }, + { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H }, + { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, + { 0x00, 0xf0, CHREQ_T_LOCATION_UPD }, + { 0x10, 0xf0, CHREQ_T_SDCCH }, + { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 }, + { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, + { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, + { 0x67, 0xff, CHREQ_T_LMU }, + { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, + { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, + { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, + { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, +}; + +/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */ +static const struct chreq chreq_type_neci0[] = { + { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, + { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H }, + { 0xe0, 0xe0, CHREQ_T_TCH_F }, + { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, + { 0x00, 0xe0, CHREQ_T_LOCATION_UPD }, + { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 }, + { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, + { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, + { 0x67, 0xff, CHREQ_T_LMU }, + { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, + { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, + { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, + { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, +}; + +static const enum gsm_chan_t ctype_by_chreq[] = { + [CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F, + [CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F, + [CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H, + [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H, + [CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH, + [CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F, + [CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H, + [CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H, + [CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH, + [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_SDCCH, + [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH, + [CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F, + [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F, + [CHREQ_T_LMU] = GSM_LCHAN_SDCCH, + [CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH, + [CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN, +}; + +static const enum gsm_chreq_reason_t reason_by_chreq[] = { + [CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG, + [CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL, + [CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL, + [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL, + [CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_CALL, + [CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD, + [CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG, + [CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG, + [CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG, + [CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG, + [CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER, +}; + +/* verify that the two tables match */ +osmo_static_assert(sizeof(ctype_by_chreq) == + sizeof(((struct gsm_network *) NULL)->ctype_by_chreq), assert_size); + +/* + * Update channel types for request based on policy. E.g. in the + * case of a TCH/H network/bsc use TCH/H for the emergency calls, + * for early assignment assign a SDCCH and some other options. + */ +void gsm_net_update_ctype(struct gsm_network *network) +{ + /* copy over the data */ + memcpy(network->ctype_by_chreq, ctype_by_chreq, sizeof(ctype_by_chreq)); + + /* + * Use TCH/H for emergency calls when this cell allows TCH/H. Maybe it + * is better to iterate over the BTS/TRX and check if no TCH/F is available + * and then set it to TCH/H. + */ + if (network->neci) + network->ctype_by_chreq[CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_H; + + if (network->pag_any_tch) { + if (network->neci) { + network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_H; + network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_H; + } else { + network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_F; + network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_F; + } + } +} + +enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, uint8_t ra) +{ + int i; + int length; + const struct chreq *chreq; + + if (network->neci) { + chreq = chreq_type_neci1; + length = ARRAY_SIZE(chreq_type_neci1); + } else { + chreq = chreq_type_neci0; + length = ARRAY_SIZE(chreq_type_neci0); + } + + + for (i = 0; i < length; i++) { + const struct chreq *chr = &chreq[i]; + if ((ra & chr->mask) == chr->val) + return network->ctype_by_chreq[chr->type]; + } + LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra); + return GSM_LCHAN_SDCCH; +} + +int get_reason_by_chreq(uint8_t ra, int neci) +{ + int i; + int length; + const struct chreq *chreq; + + if (neci) { + chreq = chreq_type_neci1; + length = ARRAY_SIZE(chreq_type_neci1); + } else { + chreq = chreq_type_neci0; + length = ARRAY_SIZE(chreq_type_neci0); + } + + for (i = 0; i < length; i++) { + const struct chreq *chr = &chreq[i]; + if ((ra & chr->mask) == chr->val) + return reason_by_chreq[chr->type]; + } + LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra); + return GSM_CHREQ_REASON_OTHER; +} + +/* 7.1.7 and 9.1.7: RR CHANnel RELease */ +int gsm48_send_rr_release(struct gsm_lchan *lchan) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + uint8_t *cause; + + msg->lchan = lchan; + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CHAN_REL; + + cause = msgb_put(msg, 1); + cause[0] = GSM48_RR_CAUSE_NORMAL; + + DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n", + lchan->nr, lchan->type); + + /* Send actual release request to MS */ + return gsm48_sendmsg(msg); +} + +int send_siemens_mrpci(struct gsm_lchan *lchan, + uint8_t *classmark2_lv) +{ + struct rsl_mrpci mrpci; + + if (classmark2_lv[0] < 2) + return -EINVAL; + + mrpci.power_class = classmark2_lv[1] & 0x7; + mrpci.vgcs_capable = classmark2_lv[2] & (1 << 1); + mrpci.vbs_capable = classmark2_lv[2] & (1 <<2); + mrpci.gsm_phase = (classmark2_lv[1]) >> 5 & 0x3; + + return rsl_siemens_mrpci(lchan, &mrpci); +} + +int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type) +{ + /* Check the size for the classmark */ + if (length < 1 + *classmark2_lv) + return -1; + + uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1; + if (length < 2 + *classmark2_lv + mi_lv[0]) + return -2; + + *mi_type = mi_lv[1] & GSM_MI_TYPE_MASK; + return gsm48_mi_to_string(mi_string, GSM48_MI_SIZE, mi_lv+1, *mi_lv); +} + +int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length, + char *mi_string, uint8_t *mi_type) +{ + static const uint32_t classmark_offset = + offsetof(struct gsm48_pag_resp, classmark2); + uint8_t *classmark2_lv = (uint8_t *) &resp->classmark2; + return gsm48_extract_mi(classmark2_lv, length - classmark_offset, + mi_string, mi_type); +} + +int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, + struct msgb *msg, struct gsm_subscriber *subscr) +{ + struct gsm_bts *bts = msg->lchan->ts->trx->bts; + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t *classmark2_lv = gh->data + 1; + + if (is_siemens_bts(bts)) + send_siemens_mrpci(msg->lchan, classmark2_lv); + + if (!conn->subscr) { + conn->subscr = subscr; + } else if (conn->subscr != subscr) { + LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n"); + subscr_put(subscr); + return -EINVAL; + } else { + DEBUGP(DRR, "<- Channel already owned by us\n"); + subscr_put(subscr); + subscr = conn->subscr; + } + + osmo_counter_inc(bts->network->stats.paging.completed); + + /* Stop paging on the bts we received the paging response */ + paging_request_stop(conn->bts, subscr, conn, msg); + return 0; +} + +/* Chapter 9.1.9: Ciphering Mode Command */ +int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + uint8_t ciph_mod_set; + + msg->lchan = lchan; + + DEBUGP(DRR, "TX CIPHERING MODE CMD\n"); + + if (lchan->encr.alg_id <= RSL_ENC_ALG_A5(0)) + ciph_mod_set = 0; + else + ciph_mod_set = (lchan->encr.alg_id-2)<<1 | 1; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CIPH_M_CMD; + gh->data[0] = (want_imeisv & 0x1) << 4 | (ciph_mod_set & 0xf); + + return rsl_encryption_cmd(msg); +} + +static void gsm48_cell_desc(struct gsm48_cell_desc *cd, + const struct gsm_bts *bts) +{ + cd->ncc = (bts->bsic >> 3 & 0x7); + cd->bcc = (bts->bsic & 0x7); + cd->arfcn_hi = bts->c0->arfcn >> 8; + cd->arfcn_lo = bts->c0->arfcn & 0xff; +} + +void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, + const struct gsm_lchan *lchan) +{ + uint16_t arfcn = lchan->ts->trx->arfcn & 0x3ff; + + cd->chan_nr = gsm_lchan2chan_nr(lchan); + if (!lchan->ts->hopping.enabled) { + cd->h0.tsc = lchan->ts->trx->bts->tsc; + cd->h0.h = 0; + cd->h0.arfcn_high = arfcn >> 8; + cd->h0.arfcn_low = arfcn & 0xff; + } else { + cd->h1.tsc = lchan->ts->trx->bts->tsc; + cd->h1.h = 1; + cd->h1.maio_high = lchan->ts->hopping.maio >> 2; + cd->h1.maio_low = lchan->ts->hopping.maio & 0x03; + cd->h1.hsn = lchan->ts->hopping.hsn; + } +} + +#define GSM48_HOCMD_CCHDESC_LEN 16 + +/* Chapter 9.1.15: Handover Command */ +int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, + uint8_t power_command, uint8_t ho_ref) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_ho_cmd *ho = + (struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho)); + + msg->lchan = old_lchan; + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_HANDO_CMD; + + /* mandatory bits */ + gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts); + gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan); + ho->ho_ref = ho_ref; + ho->power_command = power_command; + + if (new_lchan->ts->hopping.enabled) { + struct gsm_bts *bts = new_lchan->ts->trx->bts; + struct gsm48_system_information_type_1 *si1; + uint8_t *cur; + + si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1); + /* Copy the Cell Chan Desc (ARFCNS in this cell) */ + msgb_put_u8(msg, GSM48_IE_CELL_CH_DESC); + cur = msgb_put(msg, GSM48_HOCMD_CCHDESC_LEN); + memcpy(cur, si1->cell_channel_description, + GSM48_HOCMD_CCHDESC_LEN); + /* Copy the Mobile Allocation */ + msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, + new_lchan->ts->hopping.ma_len, + new_lchan->ts->hopping.ma_data); + } + /* FIXME: optional bits for type of synchronization? */ + + return gsm48_sendmsg(msg); +} + +/* Chapter 9.1.2: Assignment Command */ +int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_ass_cmd *ass = + (struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass)); + + DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode); + + msg->lchan = dest_lchan; + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_ASS_CMD; + + /* + * fill the channel information element, this code + * should probably be shared with rsl_rx_chan_rqd(), + * gsm48_tx_chan_mode_modify. But beware that 10.5.2.5 + * 10.5.2.5.a have slightly different semantic for + * the chan_desc. But as long as multi-slot configurations + * are not used we seem to be fine. + */ + gsm48_lchan2chan_desc(&ass->chan_desc, lchan); + ass->power_command = power_command; + + /* optional: cell channel description */ + + msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode); + + /* mobile allocation in case of hopping */ + if (lchan->ts->hopping.enabled) { + msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, lchan->ts->hopping.ma_len, + lchan->ts->hopping.ma_data); + } + + /* in case of multi rate we need to attach a config */ + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { + if (lchan->mr_conf.ver == 0) { + LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " + "without multirate config.\n"); + } else { + uint8_t *data = msgb_put(msg, 4); + data[0] = GSM48_IE_MUL_RATE_CFG; + data[1] = 0x2; + memcpy(&data[2], &lchan->mr_conf, 2); + } + } + + return gsm48_sendmsg(msg); +} + +/* 9.1.5 Channel mode modify: Modify the mode on the MS side */ +int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, uint8_t mode) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_chan_mode_modify *cmm = + (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm)); + + DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode); + + lchan->tch_mode = mode; + msg->lchan = lchan; + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF; + + /* fill the channel information element, this code + * should probably be shared with rsl_rx_chan_rqd() */ + gsm48_lchan2chan_desc(&cmm->chan_desc, lchan); + cmm->mode = mode; + + /* in case of multi rate we need to attach a config */ + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { + if (lchan->mr_conf.ver == 0) { + LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " + "without multirate config.\n"); + } else { + uint8_t *data = msgb_put(msg, 4); + data[0] = GSM48_IE_MUL_RATE_CFG; + data[1] = 0x2; + memcpy(&data[2], &lchan->mr_conf, 2); + } + } + + return gsm48_sendmsg(msg); +} + +int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t lchan_mode) +{ + int rc; + + rc = gsm48_tx_chan_mode_modify(lchan, lchan_mode); + if (rc < 0) + return rc; + + return rc; +} + +int gsm48_rx_rr_modif_ack(struct msgb *msg) +{ + int rc; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_chan_mode_modify *mod = + (struct gsm48_chan_mode_modify *) gh->data; + + DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n"); + + if (mod->mode != msg->lchan->tch_mode) { + LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n", + msg->lchan->tch_mode, mod->mode); + return -1; + } + + /* update the channel type */ + switch (mod->mode) { + case GSM48_CMODE_SIGN: + msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; + break; + case GSM48_CMODE_SPEECH_V1: + case GSM48_CMODE_SPEECH_EFR: + case GSM48_CMODE_SPEECH_AMR: + msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; + break; + case GSM48_CMODE_DATA_14k5: + case GSM48_CMODE_DATA_12k0: + case GSM48_CMODE_DATA_6k0: + case GSM48_CMODE_DATA_3k6: + msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA; + break; + } + + /* We've successfully modified the MS side of the channel, + * now go on to modify the BTS side of the channel */ + rc = rsl_chan_mode_modify_req(msg->lchan); + + /* FIXME: we not only need to do this after mode modify, but + * also after channel activation */ + if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN) + rsl_ipacc_crcx(msg->lchan); + return rc; +} + +int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t *data = gh->data; + struct gsm_bts *bts = msg->lchan->ts->trx->bts; + struct bitvec *nbv = &bts->si_common.neigh_list; + struct gsm_meas_rep_cell *mrc; + + if (gh->msg_type != GSM48_MT_RR_MEAS_REP) + return -EINVAL; + + if (data[0] & 0x80) + rep->flags |= MEAS_REP_F_BA1; + if (data[0] & 0x40) + rep->flags |= MEAS_REP_F_UL_DTX; + if ((data[1] & 0x40) == 0x00) + rep->flags |= MEAS_REP_F_DL_VALID; + + rep->dl.full.rx_lev = data[0] & 0x3f; + rep->dl.sub.rx_lev = data[1] & 0x3f; + rep->dl.full.rx_qual = (data[2] >> 4) & 0x7; + rep->dl.sub.rx_qual = (data[2] >> 1) & 0x7; + + rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2); + if (rep->num_cell < 1 || rep->num_cell > 6) + return 0; + + /* an encoding nightmare in perfection */ + mrc = &rep->cell[0]; + mrc->rxlev = data[3] & 0x3f; + mrc->neigh_idx = data[4] >> 3; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5); + if (rep->num_cell < 2) + return 0; + + mrc = &rep->cell[1]; + mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7); + mrc->neigh_idx = (data[6] >> 2) & 0x1f; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4); + if (rep->num_cell < 3) + return 0; + + mrc = &rep->cell[2]; + mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6); + mrc->neigh_idx = (data[8] >> 1) & 0x1f; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3); + if (rep->num_cell < 4) + return 0; + + mrc = &rep->cell[3]; + mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5); + mrc->neigh_idx = data[10] & 0x1f; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = data[11] >> 2; + if (rep->num_cell < 5) + return 0; + + mrc = &rep->cell[4]; + mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4); + mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7); + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = (data[13] >> 1) & 0x3f; + if (rep->num_cell < 6) + return 0; + + mrc = &rep->cell[5]; + mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3); + mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6); + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = data[15] & 0x3f; + + return 0; +} + +struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value) +{ + struct msgb *msg; + struct gsm48_hdr *gh; + + msg = gsm48_msgb_alloc(); + if (!msg) + return NULL; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_CM_SERV_REJ; + gh->data[0] = value; + + return msg; +} + +struct msgb *gsm48_create_loc_upd_rej(uint8_t cause) +{ + struct gsm48_hdr *gh; + struct msgb *msg; + + msg = gsm48_msgb_alloc(); + if (!msg) + return NULL; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT; + gh->data[0] = cause; + return msg; +} diff --git a/src/libbsc/gsm_subscriber_base.c b/src/libbsc/gsm_subscriber_base.c new file mode 100644 index 000000000..747a699f9 --- /dev/null +++ b/src/libbsc/gsm_subscriber_base.c @@ -0,0 +1,149 @@ +/* The concept of a subscriber as seen by the BSC */ + +/* (C) 2008 by Harald Welte + * (C) 2009-2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +LLIST_HEAD(active_subscribers); +void *tall_subscr_ctx; + +/* for the gsm_subscriber.c */ +struct llist_head *subscr_bsc_active_subscribers(void) +{ + return &active_subscribers; +} + + +char *subscr_name(struct gsm_subscriber *subscr) +{ + if (strlen(subscr->name)) + return subscr->name; + + return subscr->imsi; +} + +struct gsm_subscriber *subscr_alloc(void) +{ + struct gsm_subscriber *s; + + s = talloc_zero(tall_subscr_ctx, struct gsm_subscriber); + if (!s) + return NULL; + + llist_add_tail(&s->entry, &active_subscribers); + s->use_count = 1; + s->tmsi = GSM_RESERVED_TMSI; + + INIT_LLIST_HEAD(&s->requests); + + return s; +} + +static void subscr_free(struct gsm_subscriber *subscr) +{ + llist_del(&subscr->entry); + talloc_free(subscr); +} + +struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr) +{ + subscr->use_count++; + DEBUGP(DREF, "subscr %s usage increases usage to: %d\n", + subscr->extension, subscr->use_count); + return subscr; +} + +struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr) +{ + subscr->use_count--; + DEBUGP(DREF, "subscr %s usage decreased usage to: %d\n", + subscr->extension, subscr->use_count); + if (subscr->use_count <= 0 && !subscr->net->keep_subscr) + subscr_free(subscr); + return NULL; +} + +struct gsm_subscriber *subscr_get_or_create(struct gsm_network *net, + const char *imsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { + if (strcmp(subscr->imsi, imsi) == 0 && subscr->net == net) + return subscr_get(subscr); + } + + subscr = subscr_alloc(); + if (!subscr) + return NULL; + + strcpy(subscr->imsi, imsi); + subscr->net = net; + return subscr; +} + +struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_network *net, uint32_t tmsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { + if (subscr->tmsi == tmsi && subscr->net == net) + return subscr_get(subscr); + } + + return NULL; +} + +struct gsm_subscriber *subscr_active_by_imsi(struct gsm_network *net, const char *imsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { + if (strcmp(subscr->imsi, imsi) == 0 && subscr->net == net) + return subscr_get(subscr); + } + + return NULL; +} + +int subscr_purge_inactive(struct gsm_network *net) +{ + struct gsm_subscriber *subscr, *tmp; + int purged = 0; + + llist_for_each_entry_safe(subscr, tmp, subscr_bsc_active_subscribers(), entry) { + if (subscr->net == net && subscr->use_count <= 0) { + subscr_free(subscr); + purged += 1; + } + } + + return purged; +} diff --git a/src/libbsc/handover_decision.c b/src/libbsc/handover_decision.c new file mode 100644 index 000000000..24c0f79cb --- /dev/null +++ b/src/libbsc/handover_decision.c @@ -0,0 +1,297 @@ +/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This + * only implements the handover algorithm/decision, but not execution + * of it */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* issue handover to a cell identified by ARFCN and BSIC */ +static int handover_to_arfcn_bsic(struct gsm_lchan *lchan, + uint16_t arfcn, uint8_t bsic) +{ + struct gsm_bts *new_bts; + + /* resolve the gsm_bts structure for the best neighbor */ + new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic); + if (!new_bts) { + LOGP(DHO, LOGL_NOTICE, "unable to determine neighbor BTS " + "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic); + return -EINVAL; + } + + /* and actually try to handover to that cell */ + return bsc_handover_start(lchan, new_bts); +} + +/* did we get a RXLEV for a given cell in the given report? */ +static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr, + uint16_t arfcn, uint8_t bsic) +{ + int i; + + for (i = 0; i < mr->num_cell; i++) { + struct gsm_meas_rep_cell *mrc = &mr->cell[i]; + + /* search for matching report */ + if (!(mrc->arfcn == arfcn && mrc->bsic == bsic)) + continue; + + mrc->flags |= MRC_F_PROCESSED; + return mrc->rxlev; + } + return -ENODEV; +} + +/* obtain averaged rxlev for given neighbor */ +static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window) +{ + unsigned int i, idx; + int avg = 0; + + idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev), + nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev), + window); + + for (i = 0; i < window; i++) { + int j = (idx+i) % ARRAY_SIZE(nmp->rxlev); + + avg += nmp->rxlev[j]; + } + + return avg / window; +} + +/* find empty or evict bad neighbor */ +static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan) +{ + int j, worst = 999999; + struct neigh_meas_proc *nmp_worst = NULL; + + /* first try to find an empty/unused slot */ + for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { + struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; + if (!nmp->arfcn) + return nmp; + } + + /* no empty slot found. evict worst neighbor from list */ + for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { + struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; + int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG); + if (!nmp_worst || avg < worst) { + worst = avg; + nmp_worst = nmp; + } + } + + return nmp_worst; +} + +/* process neighbor cell measurement reports */ +static void process_meas_neigh(struct gsm_meas_rep *mr) +{ + int i, j, idx; + + /* for each reported cell, try to update global state */ + for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) { + struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j]; + unsigned int idx; + int rxlev; + + /* skip unused entries */ + if (!nmp->arfcn) + continue; + + rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic); + idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); + if (rxlev >= 0) { + nmp->rxlev[idx] = rxlev; + nmp->last_seen_nr = mr->nr; + } else + nmp->rxlev[idx] = 0; + nmp->rxlev_cnt++; + } + + /* iterate over list of reported cells, check if we did not + * process all of them */ + for (i = 0; i < mr->num_cell; i++) { + struct gsm_meas_rep_cell *mrc = &mr->cell[i]; + struct neigh_meas_proc *nmp; + + if (mrc->flags & MRC_F_PROCESSED) + continue; + + nmp = find_evict_neigh(mr->lchan); + + nmp->arfcn = mrc->arfcn; + nmp->bsic = mrc->bsic; + + idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); + nmp->rxlev[idx] = mrc->rxlev; + nmp->rxlev_cnt++; + nmp->last_seen_nr = mr->nr; + + mrc->flags |= MRC_F_PROCESSED; + } +} + +/* attempt to do a handover */ +static int attempt_handover(struct gsm_meas_rep *mr) +{ + struct gsm_network *net = mr->lchan->ts->trx->bts->network; + struct neigh_meas_proc *best_cell = NULL; + unsigned int best_better_db = 0; + int i, rc; + + /* find the best cell in this report that is at least RXLEV_HYST + * better than the current serving cell */ + + for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) { + struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i]; + int avg, better; + + /* skip empty slots */ + if (nmp->arfcn == 0) + continue; + + /* caculate average rxlev for this cell over the window */ + avg = neigh_meas_avg(nmp, net->handover.win_rxlev_avg_neigh); + + /* check if hysteresis is fulfilled */ + if (avg < mr->dl.full.rx_lev + net->handover.pwr_hysteresis) + continue; + + better = avg - mr->dl.full.rx_lev; + if (better > best_better_db) { + best_cell = nmp; + best_better_db = better; + } + } + + if (!best_cell) + return 0; + + LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ", + gsm_ts_name(mr->lchan->ts), best_cell->arfcn); + if (!net->handover.active) { + LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n"); + return 0; + } + + rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic); + switch (rc) { + case 0: + LOGPC(DHO, LOGL_INFO, "Starting handover\n"); + break; + case -ENOSPC: + LOGPC(DHO, LOGL_INFO, "No channel available\n"); + break; + case -EBUSY: + LOGPC(DHO, LOGL_INFO, "Handover already active\n"); + break; + default: + LOGPC(DHO, LOGL_ERROR, "Unknown error\n"); + } + return rc; +} + +/* process an already parsed measurement report and decide if we want to + * attempt a handover */ +static int process_meas_rep(struct gsm_meas_rep *mr) +{ + struct gsm_network *net = mr->lchan->ts->trx->bts->network; + int av_rxlev; + + /* we currently only do handover for TCH channels */ + switch (mr->lchan->type) { + case GSM_LCHAN_TCH_F: + case GSM_LCHAN_TCH_H: + break; + default: + return 0; + } + + /* parse actual neighbor cell info */ + if (mr->num_cell > 0 && mr->num_cell < 7) + process_meas_neigh(mr); + + av_rxlev = get_meas_rep_avg(mr->lchan, MEAS_REP_DL_RXLEV_FULL, + net->handover.win_rxlev_avg); + + /* Interference HO */ + if (rxlev2dbm(av_rxlev) > -85 && + meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, + 3, 4, 5)) + return attempt_handover(mr); + + /* Bad Quality */ + if (meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, + 3, 4, 5)) + return attempt_handover(mr); + + /* Low Level */ + if (rxlev2dbm(av_rxlev) <= -110) + return attempt_handover(mr); + + /* Distance */ + if (mr->ms_l1.ta > net->handover.max_distance) + return attempt_handover(mr); + + /* Power Budget AKA Better Cell */ + if ((mr->nr % net->handover.pwr_interval) == 0) + return attempt_handover(mr); + + return 0; + +} + +static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct lchan_signal_data *lchan_data; + + if (subsys != SS_LCHAN) + return 0; + + lchan_data = signal_data; + switch (signal) { + case S_LCHAN_MEAS_REP: + process_meas_rep(lchan_data->mr); + break; + } + + return 0; +} + +void on_dso_load_ho_dec(void) +{ + osmo_signal_register_handler(SS_LCHAN, ho_dec_sig_cb, NULL); +} diff --git a/src/libbsc/handover_logic.c b/src/libbsc/handover_logic.c new file mode 100644 index 000000000..9cf26af9d --- /dev/null +++ b/src/libbsc/handover_logic.c @@ -0,0 +1,374 @@ +/* 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 + +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 struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan) +{ + struct bsc_handover *ho; + + llist_for_each_entry(ho, &bsc_handovers, list) { + if (ho->new_lchan == new_lchan) + return ho; + } + + return NULL; +} + +static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan) +{ + struct bsc_handover *ho; + + llist_for_each_entry(ho, &bsc_handovers, list) { + if (ho->old_lchan == old_lchan) + return ho; + } + + return NULL; +} + +/* Hand over the specified logical channel to the specified new BTS. + * This is the main entry point for the actual handover algorithm, + * after it has decided it wants to initiate HO to a specific BTS */ +int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) +{ + struct gsm_lchan *new_lchan; + struct bsc_handover *ho; + static uint8_t ho_ref; + int rc; + + /* don't attempt multiple handovers for the same lchan at + * the same time */ + if (bsc_ho_by_old_lchan(old_lchan)) + return -EBUSY; + + DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n", + old_lchan->ts->trx->bts->nr, bts->nr); + + osmo_counter_inc(bts->network->stats.handover.attempted); + + if (!old_lchan->conn) { + LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n"); + return -ENOSPC; + } + + new_lchan = lchan_alloc(bts, old_lchan->type, 0); + if (!new_lchan) { + LOGP(DHO, LOGL_NOTICE, "No free channel\n"); + osmo_counter_inc(bts->network->stats.handover.no_channel); + return -ENOSPC; + } + + ho = talloc_zero(tall_bsc_ctx, struct bsc_handover); + if (!ho) { + LOGP(DHO, LOGL_FATAL, "Out of Memory\n"); + lchan_free(new_lchan); + return -ENOMEM; + } + ho->old_lchan = old_lchan; + ho->new_lchan = new_lchan; + ho->ho_ref = ho_ref++; + + /* copy some parameters from old lchan */ + memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr)); + new_lchan->ms_power = old_lchan->ms_power; + new_lchan->bs_power = old_lchan->bs_power; + new_lchan->rsl_cmode = old_lchan->rsl_cmode; + new_lchan->tch_mode = old_lchan->tch_mode; + + new_lchan->conn = old_lchan->conn; + new_lchan->conn->ho_lchan = new_lchan; + + /* FIXME: do we have a better idea of the timing advance? */ + rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0, + ho->ho_ref); + if (rc < 0) { + LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); + new_lchan->conn->ho_lchan = NULL; + new_lchan->conn = NULL; + talloc_free(ho); + lchan_free(new_lchan); + return rc; + } + + rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); + llist_add(&ho->list, &bsc_handovers); + /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */ + + return 0; +} + +void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan) +{ + struct bsc_handover *ho; + + ho = bsc_ho_by_new_lchan(conn->ho_lchan); + + + if (!ho && conn->ho_lchan) + LOGP(DHO, LOGL_ERROR, "BUG: We lost some state.\n"); + + if (!ho) { + LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); + return; + } + + conn->ho_lchan->conn = NULL; + conn->ho_lchan = NULL; + + if (free_lchan) + lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); + + osmo_timer_del(&ho->T3103); + llist_del(&ho->list); + talloc_free(ho); +} + +/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */ +static void ho_T3103_cb(void *_ho) +{ + struct bsc_handover *ho = _ho; + struct gsm_network *net = ho->new_lchan->ts->trx->bts->network; + + DEBUGP(DHO, "HO T3103 expired\n"); + osmo_counter_inc(net->stats.handover.timeout); + + ho->new_lchan->conn->ho_lchan = NULL; + ho->new_lchan->conn = NULL; + lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); + llist_del(&ho->list); + talloc_free(ho); +} + +/* RSL has acknowledged activation of the new lchan */ +static int ho_chan_activ_ack(struct gsm_lchan *new_lchan) +{ + struct bsc_handover *ho; + + /* we need to check if this channel activation is related to + * a handover at all (and if, which particular handover) */ + ho = bsc_ho_by_new_lchan(new_lchan); + if (!ho) + return -ENODEV; + + DEBUGP(DHO, "handover activate ack, send HO Command\n"); + + /* we can now send the 04.08 HANDOVER COMMAND to the MS + * using the old lchan */ + + gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref); + + /* start T3103. We can continue either with T3103 expiration, + * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */ + ho->T3103.cb = ho_T3103_cb; + ho->T3103.data = ho; + osmo_timer_schedule(&ho->T3103, 10, 0); + + /* create a RTP connection */ + if (is_ipaccess_bts(new_lchan->ts->trx->bts)) + rsl_ipacc_crcx(new_lchan); + + return 0; +} + +/* RSL has not acknowledged activation of the new lchan */ +static int ho_chan_activ_nack(struct gsm_lchan *new_lchan) +{ + struct bsc_handover *ho; + + ho = bsc_ho_by_new_lchan(new_lchan); + if (!ho) { + LOGP(DHO, LOGL_INFO, "ACT NACK: unable to find HO record\n"); + return -ENODEV; + } + + new_lchan->conn->ho_lchan = NULL; + new_lchan->conn = NULL; + llist_del(&ho->list); + talloc_free(ho); + + /* FIXME: maybe we should try to allocate a new LCHAN here? */ + + return 0; +} + +/* GSM 04.08 HANDOVER COMPLETE has been received on new channel */ +static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) +{ + struct gsm_network *net; + struct bsc_handover *ho; + + ho = bsc_ho_by_new_lchan(new_lchan); + if (!ho) { + LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); + return -ENODEV; + } + + net = new_lchan->ts->trx->bts->network; + LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN " + "%u->%u\n", subscr_name(ho->old_lchan->conn->subscr), + ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr, + ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn); + + osmo_counter_inc(net->stats.handover.completed); + + osmo_timer_del(&ho->T3103); + + /* Replace the ho lchan with the primary one */ + if (ho->old_lchan != new_lchan->conn->lchan) + LOGP(DHO, LOGL_ERROR, "Primary lchan changed during handover.\n"); + + if (new_lchan != new_lchan->conn->ho_lchan) + LOGP(DHO, LOGL_ERROR, "Handover channel changed during this handover.\n"); + + new_lchan->conn->ho_lchan = NULL; + new_lchan->conn->lchan = new_lchan; + ho->old_lchan->conn = NULL; + + rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE); + lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END); + + /* do something to re-route the actual speech frames ! */ + + llist_del(&ho->list); + talloc_free(ho); + + return 0; +} + +/* GSM 04.08 HANDOVER FAIL has been received */ +static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan) +{ + struct gsm_network *net = old_lchan->ts->trx->bts->network; + struct bsc_handover *ho; + + ho = bsc_ho_by_old_lchan(old_lchan); + if (!ho) { + LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); + return -ENODEV; + } + + osmo_counter_inc(net->stats.handover.failed); + + osmo_timer_del(&ho->T3103); + llist_del(&ho->list); + + /* release the channel and forget about it */ + ho->new_lchan->conn->ho_lchan = NULL; + ho->new_lchan->conn = NULL; + lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); + + talloc_free(ho); + + return 0; +} + +/* GSM 08.58 HANDOVER DETECT has been received */ +static int ho_rsl_detect(struct gsm_lchan *new_lchan) +{ + struct bsc_handover *ho; + + ho = bsc_ho_by_new_lchan(new_lchan); + if (!ho) { + LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); + return -ENODEV; + } + + /* FIXME: do we actually want to do something here ? */ + + return 0; +} + +static int ho_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/src/libbsc/meas_proc.c b/src/libbsc/meas_proc.c new file mode 100644 index 000000000..5b97e74ee --- /dev/null +++ b/src/libbsc/meas_proc.c @@ -0,0 +1,84 @@ +/* Measurement Processing */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +/* process an already parsed measurement report */ +static int process_meas_rep(struct gsm_meas_rep *mr) +{ + struct gsm_meas_rep_cell *mr_cell = NULL; + unsigned int best_better_db; + int i; + + /* FIXME: implement actual averaging over multiple measurement + * reports */ + + /* find the best cell in this report that is at least RXLEV_HYST + * better than the current serving cell */ + for (i = 0; i < mr->num_cell; i++) { + unsigned int better; + if (mr->cell[i].rxlev < mr->dl.full.rx_lev + RXLEV_HYST) + continue; + + better = mr->cell[i].rxlev - mr->dl.full.rx_lev; + if (better > best_better_db) { + mr_cell = &mr->cell[i]; + best_better_db = better; + } + } + + if (mr_cell) + return handover_to_arfcn_bsic(mr->lchan, mr_cell->arfcn, + mr_cell->bsic); + return 0; +} + +static int meas_proc_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_lchan *lchan; + struct gsm_meas_rep *mr; + + if (subsys != SS_LCHAN) + return 0; + + switch (signal) { + case S_LCHAN_MEAS_REP: + mr = signal_data; + process_meas_rep(mr); + break; + } + + return 0; +} + +static __attribute__((constructor)) void on_dso_load_meas(void) +{ + osmo_signal_register_handler(SS_LCHAN, meas_proc_sig_cb, NULL); +} diff --git a/src/libbsc/meas_rep.c b/src/libbsc/meas_rep.c new file mode 100644 index 000000000..808103d28 --- /dev/null +++ b/src/libbsc/meas_rep.c @@ -0,0 +1,115 @@ +/* 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/src/libbsc/paging.c b/src/libbsc/paging.c new file mode 100644 index 000000000..286c57ba9 --- /dev/null +++ b/src/libbsc/paging.c @@ -0,0 +1,436 @@ +/* Paging helper and manager.... */ +/* (C) 2009,2013 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/* + * Relevant specs: + * 12.21: + * - 9.4.12 for CCCH Local Threshold + * + * 05.58: + * - 8.5.2 CCCH Load indication + * - 9.3.15 Paging Load + * + * Approach: + * - Send paging command to subscriber + * - On Channel Request we will remember the reason + * - After the ACK we will request the identity + * - Then we will send assign the gsm_subscriber and + * - and call a callback + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +void *tall_paging_ctx; + +#define PAGING_TIMER 0, 500000 + +/* + * Kill one paging request update the internal list... + */ +static void paging_remove_request(struct gsm_bts_paging_state *paging_bts, + struct gsm_paging_request *to_be_deleted) +{ + osmo_timer_del(&to_be_deleted->T3113); + llist_del(&to_be_deleted->entry); + subscr_put(to_be_deleted->subscr); + talloc_free(to_be_deleted); +} + +static void page_ms(struct gsm_paging_request *request) +{ + uint8_t mi[128]; + unsigned int mi_len; + unsigned int page_group; + struct gsm_bts *bts = request->bts; + + /* the bts is down.. we will just wait for the paging to expire */ + if (!bts->oml_link) + return; + + LOGP(DPAG, LOGL_INFO, "Going to send paging commands: imsi: '%s' tmsi: '0x%x'\n", + request->subscr->imsi, request->subscr->tmsi); + + if (request->subscr->tmsi == GSM_RESERVED_TMSI) + mi_len = gsm48_generate_mid_from_imsi(mi, request->subscr->imsi); + else + mi_len = gsm48_generate_mid_from_tmsi(mi, request->subscr->tmsi); + + page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc, + str_to_imsi(request->subscr->imsi)); + gsm0808_page(bts, page_group, mi_len, mi, request->chan_type); +} + +static void paging_schedule_if_needed(struct gsm_bts_paging_state *paging_bts) +{ + if (llist_empty(&paging_bts->pending_requests)) + return; + + if (!osmo_timer_pending(&paging_bts->work_timer)) + osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER); +} + + +static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts); +static void paging_give_credit(void *data) +{ + struct gsm_bts_paging_state *paging_bts = data; + + LOGP(DPAG, LOGL_NOTICE, "No slots available on bts nr %d\n", paging_bts->bts->nr); + paging_bts->available_slots = 20; + paging_handle_pending_requests(paging_bts); +} + +static int can_send_pag_req(struct gsm_bts *bts, int rsl_type) +{ + struct pchan_load pl; + int count; + + memset(&pl, 0, sizeof(pl)); + bts_chan_load(&pl, bts); + + switch (rsl_type) { + case RSL_CHANNEED_TCH_F: + case RSL_CHANNEED_TCH_ForH: + goto count_tch; + break; + case RSL_CHANNEED_SDCCH: + goto count_sdcch; + break; + case RSL_CHANNEED_ANY: + default: + if (bts->network->pag_any_tch) + goto count_tch; + else + goto count_sdcch; + break; + } + + return 0; + + /* could available SDCCH */ +count_sdcch: + count = 0; + count += pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].total + - pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].used; + count += pl.pchan[GSM_PCHAN_CCCH_SDCCH4].total + - pl.pchan[GSM_PCHAN_CCCH_SDCCH4].used; + return bts->paging.free_chans_need > count; + +count_tch: + count = 0; + count += pl.pchan[GSM_PCHAN_TCH_F].total + - pl.pchan[GSM_PCHAN_TCH_F].used; + if (bts->network->neci) + count += pl.pchan[GSM_PCHAN_TCH_H].total + - pl.pchan[GSM_PCHAN_TCH_H].used; + return bts->paging.free_chans_need > count; +} + +/* + * This is kicked by the periodic PAGING LOAD Indicator + * coming from abis_rsl.c + * + * We attempt to iterate once over the list of items but + * only upto available_slots. + */ +static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts) +{ + struct gsm_paging_request *request = NULL; + + /* + * Determine if the pending_requests list is empty and + * return then. + */ + if (llist_empty(&paging_bts->pending_requests)) { + /* since the list is empty, no need to reschedule the timer */ + return; + } + + /* + * In case the BTS does not provide us with load indication and we + * ran out of slots, call an autofill routine. It might be that the + * BTS did not like our paging messages and then we have counted down + * to zero and we do not get any messages. + */ + if (paging_bts->available_slots == 0) { + paging_bts->credit_timer.cb = paging_give_credit; + paging_bts->credit_timer.data = paging_bts; + osmo_timer_schedule(&paging_bts->credit_timer, 5, 0); + return; + } + + request = llist_entry(paging_bts->pending_requests.next, + struct gsm_paging_request, entry); + + /* we need to determine the number of free channels */ + if (paging_bts->free_chans_need != -1) { + if (can_send_pag_req(request->bts, request->chan_type) != 0) + goto skip_paging; + } + + /* handle the paging request now */ + page_ms(request); + paging_bts->available_slots--; + request->attempts++; + + /* take the current and add it to the back */ + llist_del(&request->entry); + llist_add_tail(&request->entry, &paging_bts->pending_requests); + +skip_paging: + osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER); +} + +static void paging_worker(void *data) +{ + struct gsm_bts_paging_state *paging_bts = data; + + paging_handle_pending_requests(paging_bts); +} + +static void paging_init_if_needed(struct gsm_bts *bts) +{ + if (bts->paging.bts) + return; + + bts->paging.bts = bts; + INIT_LLIST_HEAD(&bts->paging.pending_requests); + bts->paging.work_timer.cb = paging_worker; + bts->paging.work_timer.data = &bts->paging; + + /* Large number, until we get a proper message */ + bts->paging.available_slots = 20; +} + +static int paging_pending_request(struct gsm_bts_paging_state *bts, + struct gsm_subscriber *subscr) { + struct gsm_paging_request *req; + + llist_for_each_entry(req, &bts->pending_requests, entry) { + if (subscr == req->subscr) + return 1; + } + + return 0; +} + +static void paging_T3113_expired(void *data) +{ + struct gsm_paging_request *req = (struct gsm_paging_request *)data; + void *cbfn_param; + gsm_cbfn *cbfn; + int msg; + + LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n", + req, req->subscr->imsi); + + /* must be destroyed before calling cbfn, to prevent double free */ + osmo_counter_inc(req->bts->network->stats.paging.expired); + cbfn_param = req->cbfn_param; + cbfn = req->cbfn; + + /* did we ever manage to page the subscriber */ + msg = req->attempts > 0 ? GSM_PAGING_EXPIRED : GSM_PAGING_BUSY; + + /* destroy it now. Do not access req afterwards */ + paging_remove_request(&req->bts->paging, req); + + if (cbfn) + cbfn(GSM_HOOK_RR_PAGING, msg, NULL, NULL, + cbfn_param); +} + +static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *data) +{ + struct gsm_bts_paging_state *bts_entry = &bts->paging; + struct gsm_paging_request *req; + + if (paging_pending_request(bts_entry, subscr)) { + LOGP(DPAG, LOGL_INFO, "Paging request already pending for %s\n", subscr->imsi); + return -EEXIST; + } + + LOGP(DPAG, LOGL_DEBUG, "Start paging of subscriber %llu on bts %d.\n", + subscr->id, bts->nr); + req = talloc_zero(tall_paging_ctx, struct gsm_paging_request); + req->subscr = subscr_get(subscr); + req->bts = bts; + req->chan_type = type; + req->cbfn = cbfn; + req->cbfn_param = data; + req->T3113.cb = paging_T3113_expired; + req->T3113.data = req; + osmo_timer_schedule(&req->T3113, bts->network->T3113, 0); + llist_add_tail(&req->entry, &bts_entry->pending_requests); + paging_schedule_if_needed(bts_entry); + + return 0; +} + +int paging_request_bts(struct gsm_bts *bts, struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *data) +{ + int rc; + + /* skip all currently inactive TRX */ + if (!trx_is_usable(bts->c0)) + return 0; + + /* maybe it is the first time we use it */ + paging_init_if_needed(bts); + + + /* Trigger paging, pass any error to the caller */ + rc = _paging_request(bts, subscr, type, cbfn, data); + if (rc < 0) + return rc; + return 1; +} + +int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *data) +{ + struct gsm_bts *bts = NULL; + int num_pages = 0; + + osmo_counter_inc(network->stats.paging.attempted); + + /* start paging subscriber on all BTS within Location Area */ + do { + int rc; + + bts = gsm_bts_by_lac(network, subscr->lac, bts); + if (!bts) + break; + + rc = paging_request_bts(bts, subscr, type, cbfn, data); + if (rc < 0) + return rc; + num_pages += rc; + } while (1); + + if (num_pages == 0) + osmo_counter_inc(network->stats.paging.detached); + + return num_pages; +} + + +/* we consciously ignore the type of the request here */ +static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr, + struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct gsm_bts_paging_state *bts_entry = &bts->paging; + struct gsm_paging_request *req, *req2; + + paging_init_if_needed(bts); + + llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests, + entry) { + if (req->subscr == subscr) { + if (conn && req->cbfn) { + LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d, calling cbfn.\n", bts->nr); + req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, + msg, conn, req->cbfn_param); + } else + LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d silently.\n", bts->nr); + paging_remove_request(&bts->paging, req); + break; + } + } +} + +/* Stop paging on all other bts' */ +void paging_request_stop(struct gsm_bts *_bts, struct gsm_subscriber *subscr, + struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct gsm_bts *bts = NULL; + + if (_bts) + _paging_request_stop(_bts, subscr, conn, msg); + + do { + /* + * FIXME: Don't use the lac of the subscriber... + * as it might have magically changed the lac.. use the + * location area of the _bts as reconfiguration of the + * network is probably happening less often. + */ + bts = gsm_bts_by_lac(subscr->net, subscr->lac, bts); + if (!bts) + break; + + /* Stop paging */ + if (bts != _bts) + _paging_request_stop(bts, subscr, NULL, NULL); + } while (1); +} + +void paging_update_buffer_space(struct gsm_bts *bts, uint16_t free_slots) +{ + paging_init_if_needed(bts); + + osmo_timer_del(&bts->paging.credit_timer); + bts->paging.available_slots = free_slots; + paging_schedule_if_needed(&bts->paging); +} + +unsigned int paging_pending_requests_nr(struct gsm_bts *bts) +{ + unsigned int requests = 0; + struct gsm_paging_request *req; + + paging_init_if_needed(bts); + + llist_for_each_entry(req, &bts->paging.pending_requests, entry) + ++requests; + + return requests; +} + +/** + * Find any paging data for the given subscriber at the given BTS. + */ +void *paging_get_data(struct gsm_bts *bts, struct gsm_subscriber *subscr) +{ + struct gsm_paging_request *req; + + llist_for_each_entry(req, &bts->paging.pending_requests, entry) + if (req->subscr == subscr) + return req->cbfn_param; + + return NULL; +} diff --git a/src/libbsc/rest_octets.c b/src/libbsc/rest_octets.c new file mode 100644 index 000000000..41bf5050b --- /dev/null +++ b/src/libbsc/rest_octets.c @@ -0,0 +1,437 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface, + * rest octet handling according to + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include +#include + +/* generate SI1 rest octets */ +int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net) +{ + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data = data; + bv.data_len = 1; + + if (nch_pos) { + bitvec_set_bit(&bv, H); + bitvec_set_uint(&bv, *nch_pos, 5); + } else + bitvec_set_bit(&bv, L); + + if (is1800_net) + bitvec_set_bit(&bv, L); + else + bitvec_set_bit(&bv, H); + + bitvec_spare_padding(&bv, 6); + return bv.data_len; +} + +/* Append selection parameters to bitvec */ +static void append_selection_params(struct bitvec *bv, + const struct gsm48_si_selection_params *sp) +{ + if (sp->present) { + bitvec_set_bit(bv, H); + bitvec_set_bit(bv, sp->cbq); + bitvec_set_uint(bv, sp->cell_resel_off, 6); + bitvec_set_uint(bv, sp->temp_offs, 3); + bitvec_set_uint(bv, sp->penalty_time, 5); + } else + bitvec_set_bit(bv, L); +} + +/* Append power offset to bitvec */ +static void append_power_offset(struct bitvec *bv, + const struct gsm48_si_power_offset *po) +{ + if (po->present) { + bitvec_set_bit(bv, H); + bitvec_set_uint(bv, po->power_offset, 2); + } else + bitvec_set_bit(bv, L); +} + +/* Append GPRS indicator to bitvec */ +static void append_gprs_ind(struct bitvec *bv, + const struct gsm48_si3_gprs_ind *gi) +{ + if (gi->present) { + bitvec_set_bit(bv, H); + bitvec_set_uint(bv, gi->ra_colour, 3); + /* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */ + bitvec_set_bit(bv, gi->si13_position); + } else + bitvec_set_bit(bv, L); +} + + +/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ +int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3) +{ + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data = data; + bv.data_len = 4; + + /* Optional Selection Parameters */ + append_selection_params(&bv, &si3->selection_params); + + /* Optional Power Offset */ + append_power_offset(&bv, &si3->power_offset); + + /* Do we have a SI2ter on the BCCH? */ + if (si3->si2ter_indicator) + bitvec_set_bit(&bv, H); + else + bitvec_set_bit(&bv, L); + + /* Early Classmark Sending Control */ + if (si3->early_cm_ctrl) + bitvec_set_bit(&bv, H); + else + bitvec_set_bit(&bv, L); + + /* Do we have a SI Type 9 on the BCCH? */ + if (si3->scheduling.present) { + bitvec_set_bit(&bv, H); + bitvec_set_uint(&bv, si3->scheduling.where, 3); + } else + bitvec_set_bit(&bv, L); + + /* GPRS Indicator */ + append_gprs_ind(&bv, &si3->gprs_ind); + + bitvec_spare_padding(&bv, (bv.data_len*8)-1); + return bv.data_len; +} + +static int append_lsa_params(struct bitvec *bv, + const struct gsm48_lsa_params *lsa_params) +{ + /* FIXME */ + return -1; +} + +/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ +int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4) +{ + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data = data; + bv.data_len = 10; /* FIXME: up to ? */ + + /* SI4 Rest Octets O */ + append_selection_params(&bv, &si4->selection_params); + append_power_offset(&bv, &si4->power_offset); + append_gprs_ind(&bv, &si4->gprs_ind); + + if (0 /* FIXME */) { + /* H and SI4 Rest Octets S */ + bitvec_set_bit(&bv, H); + + /* LSA Parameters */ + if (si4->lsa_params.present) { + bitvec_set_bit(&bv, H); + append_lsa_params(&bv, &si4->lsa_params); + } else + bitvec_set_bit(&bv, L); + + /* Cell Identity */ + if (1) { + bitvec_set_bit(&bv, H); + bitvec_set_uint(&bv, si4->cell_id, 16); + } else + bitvec_set_bit(&bv, L); + + /* LSA ID Information */ + if (0) { + bitvec_set_bit(&bv, H); + /* FIXME */ + } else + bitvec_set_bit(&bv, L); + } else { + /* L and break indicator */ + bitvec_set_bit(&bv, L); + bitvec_set_bit(&bv, si4->break_ind ? H : L); + } + + return bv.data_len; +} + +/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a: + < GPRS Mobile Allocation IE > ::= + < HSN : bit (6) > + { 0 | 1 < RFL number list : < RFL number list struct > > } + { 0 < MA_LENGTH : bit (6) > + < MA_BITMAP: bit (val(MA_LENGTH) + 1) > + | 1 { 0 | 1 > } } ; + + < RFL number list struct > :: = + < RFL_NUMBER : bit (4) > + { 0 | 1 < RFL number list struct > } ; + < ARFCN index list struct > ::= + < ARFCN_INDEX : bit(6) > + { 0 | 1 < ARFCN index list struct > } ; + */ +static int append_gprs_mobile_alloc(struct bitvec *bv) +{ + /* Hopping Sequence Number */ + bitvec_set_uint(bv, 0, 6); + + if (0) { + /* We want to use a RFL number list */ + bitvec_set_bit(bv, 1); + /* FIXME: RFL number list */ + } else + bitvec_set_bit(bv, 0); + + if (0) { + /* We want to use a MA_BITMAP */ + bitvec_set_bit(bv, 0); + /* FIXME: MA_LENGTH, MA_BITMAP, ... */ + } else { + bitvec_set_bit(bv, 1); + if (0) { + /* We want to provide an ARFCN index list */ + bitvec_set_bit(bv, 1); + /* FIXME */ + } else + bitvec_set_bit(bv, 0); + } + return 0; +} + +static int encode_t3192(unsigned int t3192) +{ + if (t3192 == 0) + return 3; + else if (t3192 <= 80) + return 4; + else if (t3192 <= 120) + return 5; + else if (t3192 <= 160) + return 6; + else if (t3192 <= 200) + return 7; + else if (t3192 <= 500) + return 0; + else if (t3192 <= 1000) + return 1; + else if (t3192 <= 1500) + return 2; + else + return -EINVAL; +} + +static int encode_drx_timer(unsigned int drx) +{ + if (drx == 0) + return 0; + else if (drx == 1) + return 1; + else if (drx == 2) + return 2; + else if (drx <= 4) + return 3; + else if (drx <= 8) + return 4; + else if (drx <= 16) + return 5; + else if (drx <= 32) + return 6; + else if (drx <= 64) + return 7; + else + return -EINVAL; +} + +/* GPRS Cell Options as per TS 04.60 Chapter 12.24 + < GPRS Cell Options IE > ::= + < NMO : bit(2) > + < T3168 : bit(3) > + < T3192 : bit(3) > + < DRX_TIMER_MAX: bit(3) > + < ACCESS_BURST_TYPE: bit > + < CONTROL_ACK_TYPE : bit > + < BS_CV_MAX: bit(4) > + { 0 | 1 < PAN_DEC : bit(3) > + < PAN_INC : bit(3) > + < PAN_MAX : bit(3) > + { 0 | 1 < Extension Length : bit(6) > + < bit (val(Extension Length) + 1 + & { < Extension Information > ! { bit ** = } } ; + < Extension Information > ::= + { 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit > + < BEP_PERIOD : bit(4) > } + < PFC_FEATURE_MODE : bit > + < DTM_SUPPORT : bit > + + ** ; + */ +static int append_gprs_cell_opt(struct bitvec *bv, + const struct gprs_cell_options *gco) +{ + int t3192, drx_timer_max; + + t3192 = encode_t3192(gco->t3192); + if (t3192 < 0) + return t3192; + + drx_timer_max = encode_drx_timer(gco->drx_timer_max); + if (drx_timer_max < 0) + return drx_timer_max; + + bitvec_set_uint(bv, gco->nmo, 2); + bitvec_set_uint(bv, gco->t3168 / 500, 3); + bitvec_set_uint(bv, t3192, 3); + bitvec_set_uint(bv, drx_timer_max, 3); + /* ACCESS_BURST_TYPE: Hard-code 8bit */ + bitvec_set_bit(bv, 0); + /* CONTROL_ACK_TYPE: Hard-code to RLC/MAC control block */ + bitvec_set_bit(bv, 1); + bitvec_set_uint(bv, gco->bs_cv_max, 4); + + if (0) { + /* hard-code no PAN_{DEC,INC,MAX} */ + bitvec_set_bit(bv, 0); + } else { + /* copied from ip.access BSC protocol trace */ + bitvec_set_bit(bv, 1); + bitvec_set_uint(bv, 1, 3); /* DEC */ + bitvec_set_uint(bv, 1, 3); /* INC */ + bitvec_set_uint(bv, 15, 3); /* MAX */ + } + + if (!gco->ext_info_present) { + /* no extension information */ + bitvec_set_bit(bv, 0); + } else { + /* extension information */ + bitvec_set_bit(bv, 1); + if (!gco->ext_info.egprs_supported) { + /* 6bit length of extension */ + bitvec_set_uint(bv, (1 + 3)-1, 6); + /* EGPRS supported in the cell */ + bitvec_set_bit(bv, 0); + } else { + /* 6bit length of extension */ + bitvec_set_uint(bv, (1 + 5 + 3)-1, 6); + /* EGPRS supported in the cell */ + bitvec_set_bit(bv, 1); + /* 1bit EGPRS PACKET CHANNEL REQUEST */ + bitvec_set_bit(bv, gco->ext_info.use_egprs_p_ch_req); + /* 4bit BEP PERIOD */ + bitvec_set_uint(bv, gco->ext_info.bep_period, 4); + } + bitvec_set_bit(bv, gco->ext_info.pfc_supported); + bitvec_set_bit(bv, gco->ext_info.dtm_supported); + bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination); + } + + return 0; +} + +static void append_gprs_pwr_ctrl_pars(struct bitvec *bv, + const struct gprs_power_ctrl_pars *pcp) +{ + bitvec_set_uint(bv, pcp->alpha, 4); + bitvec_set_uint(bv, pcp->t_avg_w, 5); + bitvec_set_uint(bv, pcp->t_avg_t, 5); + bitvec_set_uint(bv, pcp->pc_meas_chan, 1); + bitvec_set_uint(bv, pcp->n_avg_i, 4); +} + +/* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */ +int rest_octets_si13(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/src/libbsc/system_information.c b/src/libbsc/system_information.c new file mode 100644 index 000000000..c901a4ae6 --- /dev/null +++ b/src/libbsc/system_information.c @@ -0,0 +1,844 @@ +/* GSM 04.08 System Information (SI) encoding and decoding + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-2010 by Harald Welte + * (C) 2012 Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * DCS1800 and PCS1900 have overlapping ARFCNs. We would need to set the + * ARFCN_PCS flag on the 1900 ARFCNs but this would increase cell_alloc + * and other arrays to make sure (ARFCN_PCS + 1024)/8 ARFCNs fit into the + * array. DCS1800 and PCS1900 can not be used at the same time so conserve + * memory and do the below. + */ +static int band_compatible(const struct gsm_bts *bts, int arfcn) +{ + enum gsm_band band = gsm_arfcn2band(arfcn); + + /* normal case */ + if (band == bts->band) + return 1; + /* deal with ARFCN_PCS not set */ + if (band == GSM_BAND_1800 && bts->band == GSM_BAND_1900) + return 1; + + return 0; +} + +static int is_dcs_net(const struct gsm_bts *bts) +{ + if (bts->band == GSM_BAND_850) + return 0; + if (bts->band == GSM_BAND_1900) + return 0; + return 1; +} + +static int use_arfcn(const struct gsm_bts *bts, const int bis, const int ter, + const int pgsm, const int arfcn) +{ + if (!bis && !ter && band_compatible(bts, arfcn)) + return 1; + if (bis && pgsm && band_compatible(bts, arfcn) && (arfcn < 1 || arfcn > 124)) + return 1; + if (ter && !band_compatible(bts, arfcn)) + return 1; + return 0; +} + +/* Frequency Lists as per TS 04.08 10.5.2.13 */ + +/* 10.5.2.13.2: Bit map 0 format */ +static int freq_list_bm0_set_arfcn(uint8_t *chan_list, unsigned int arfcn) +{ + unsigned int byte, bit; + + if (arfcn > 124 || arfcn < 1) { + LOGP(DRR, LOGL_ERROR, "Bitmap 0 only supports ARFCN 1...124\n"); + return -EINVAL; + } + + /* the bitmask is from 1..124, not from 0..123 */ + arfcn--; + + byte = arfcn / 8; + bit = arfcn % 8; + + chan_list[GSM48_CELL_CHAN_DESC_SIZE-1-byte] |= (1 << bit); + + return 0; +} + +/* 10.5.2.13.7: Variable bit map format */ +static int freq_list_bmrel_set_arfcn(uint8_t *chan_list, unsigned int arfcn) +{ + unsigned int byte, bit; + unsigned int min_arfcn; + unsigned int bitno; + + min_arfcn = (chan_list[0] & 1) << 9; + min_arfcn |= chan_list[1] << 1; + min_arfcn |= (chan_list[2] >> 7) & 1; + + /* The lower end of our bitmaks is always implicitly included */ + if (arfcn == min_arfcn) + return 0; + + if (((arfcn - min_arfcn) & 1023) > 111) { + LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn); + return -EINVAL; + } + + bitno = (arfcn - min_arfcn) & 1023; + byte = bitno / 8; + bit = bitno % 8; + + chan_list[2 + byte] |= 1 << (7 - bit); + + return 0; +} + +/* generate a variable bitmap */ +static int enc_freq_lst_var_bitmap(uint8_t *chan_list, + struct bitvec *bv, const struct gsm_bts *bts, + int bis, int ter, int min, int pgsm) +{ + int i; + + /* set it to 'Variable bitmap format' */ + chan_list[0] = 0x8e; + + chan_list[0] |= (min >> 9) & 1; + chan_list[1] = (min >> 1); + chan_list[2] = (min & 1) << 7; + + for (i = 0; i < bv->data_len*8; i++) { + /* see notes in bitvec2freq_list */ + if (bitvec_get_bit_pos(bv, i) + && ((!bis && !ter && gsm_arfcn2band(i) == bts->band) + || (bis && pgsm && gsm_arfcn2band(i) == bts->band && (i < 1 || i > 124)) + || (ter && gsm_arfcn2band(i) != bts->band))) { + int rc = freq_list_bmrel_set_arfcn(chan_list, i); + if (rc < 0) + return rc; + } + } + + return 0; +} + +/* generate a frequency list with the range 512 format */ +static int enc_freq_lst_range(uint8_t *chan_list, + struct bitvec *bv, const struct gsm_bts *bts, + int bis, int ter, int pgsm) +{ + int arfcns[RANGE_ENC_MAX_ARFCNS]; + int w[RANGE_ENC_MAX_ARFCNS]; + int f0_included = 0; + int arfcns_used = 0; + int i, rc, range, f0; + + /* + * Select ARFCNs according to the rules in bitvec2freq_list + */ + for (i = 0; i < bv->data_len * 8; ++i) { + /* More ARFCNs than the maximum */ + if (arfcns_used > ARRAY_SIZE(arfcns)) + return -1; + /* Check if we can select it? */ + if (bitvec_get_bit_pos(bv, i) && use_arfcn(bts, bis, ter, pgsm, i)) + arfcns[arfcns_used++] = i; + } + + /* + * Check if the given list of ARFCNs can be encoded. + */ + range = range_enc_determine_range(arfcns, arfcns_used, &f0); + if (range == ARFCN_RANGE_INVALID) + return -2; + + /* + * Manipulate the ARFCN list according to the rules in J4 depending + * on the selected range. + */ + arfcns_used = range_enc_filter_arfcns(range, arfcns, arfcns_used, + f0, &f0_included); + + memset(w, 0, sizeof(w)); + rc = range_enc_arfcns(range, arfcns, arfcns_used, w, 0); + if (rc != 0) + return -3; + + /* Select the range and the amount of bits needed */ + switch (range) { + case ARFCN_RANGE_128: + return range_enc_range128(chan_list, f0, w); + break; + case ARFCN_RANGE_256: + return range_enc_range256(chan_list, f0, w); + break; + case ARFCN_RANGE_512: + return range_enc_range512(chan_list, f0, w); + break; + case ARFCN_RANGE_1024: + return range_enc_range1024(chan_list, f0, f0_included, w); + break; + default: + return -4; + }; +} + +/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ +static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv, + const struct gsm_bts *bts, int bis, int ter) +{ + int i, rc, min = -1, max = -1, pgsm = 0, arfcns = 0; + + memset(chan_list, 0, 16); + + if (bts->band == GSM_BAND_900 + && bts->c0->arfcn >= 1 && bts->c0->arfcn <= 124) + pgsm = 1; + /* P-GSM-only handsets only support 'bit map 0 format' */ + if (!bis && !ter && pgsm) { + chan_list[0] = 0; + + for (i = 0; i < bv->data_len*8; i++) { + if (i >= 1 && i <= 124 + && bitvec_get_bit_pos(bv, i)) { + rc = freq_list_bm0_set_arfcn(chan_list, i); + if (rc < 0) + return rc; + } + } + return 0; + } + + for (i = 0; i < bv->data_len*8; i++) { + /* in case of SI2 or SI5 allow all neighbours in same band + * in case of SI*bis, allow neighbours in same band ouside pgsm + * in case of SI*ter, allow neighbours in different bands + */ + if (!bitvec_get_bit_pos(bv, i)) + continue; + if (!use_arfcn(bts, bis, ter, pgsm, i)) + continue; + /* count the arfcns we want to carry */ + arfcns += 1; + + /* 955..1023 < 0..885 */ + if (min < 0) + min = i; + if (i >= 955 && min < 955) + min = i; + if (i >= 955 && min >= 955 && i < min) + min = i; + if (i < 955 && min < 955 && i < min) + min = i; + if (max < 0) + max = i; + if (i < 955 && max >= 955) + max = i; + if (i >= 955 && max >= 955 && i > max) + max = i; + if (i < 955 && max < 955 && i > max) + max = i; + } + + if (max == -1) { + /* Empty set, use 'bit map 0 format' */ + chan_list[0] = 0; + return 0; + } + + /* Now find the best encoding */ + if (((max - min) & 1023) <= 111) + return enc_freq_lst_var_bitmap(chan_list, bv, bts, bis, + ter, min, pgsm); + + /* Attempt to do the range encoding */ + rc = enc_freq_lst_range(chan_list, bv, bts, bis, ter, pgsm); + if (rc == 0) + return 0; + + LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, arfcns=%d " + "can not generate ARFCN list", min, max, arfcns); + return -EINVAL; +} + +/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ +/* static*/ int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + struct bitvec *bv = &bts->si_common.cell_alloc; + + /* Zero-initialize the bit-vector */ + memset(bv->data, 0, bv->data_len); + + /* first we generate a bitvec of all TRX ARFCN's in our BTS */ + llist_for_each_entry(trx, &bts->trx_list, list) { + unsigned int i, j; + /* Always add the TRX's ARFCN */ + bitvec_set_bit_pos(bv, trx->arfcn, 1); + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + /* Add any ARFCNs present in hopping channels */ + for (j = 0; j < 1024; j++) { + if (bitvec_get_bit_pos(&ts->hopping.arfcns, j)) + bitvec_set_bit_pos(bv, j, 1); + } + } + } + + /* then we generate a GSM 04.08 frequency list from the bitvec */ + return bitvec2freq_list(chan_list, bv, bts, 0, 0); +} + +/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ +static int generate_bcch_chan_list(uint8_t *chan_list, struct gsm_bts *bts, + int si5, int bis, int ter) +{ + struct gsm_bts *cur_bts; + struct bitvec *bv; + + if (si5 && bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) + bv = &bts->si_common.si5_neigh_list; + else + bv = &bts->si_common.neigh_list; + + /* Generate list of neighbor cells if we are in automatic mode */ + if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) { + /* Zero-initialize the bit-vector */ + memset(bv->data, 0, bv->data_len); + + /* first we generate a bitvec of the BCCH ARFCN's in our BSC */ + llist_for_each_entry(cur_bts, &bts->network->bts_list, list) { + if (cur_bts == bts) + continue; + bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1); + } + } + + /* then we generate a GSM 04.08 frequency list from the bitvec */ + return bitvec2freq_list(chan_list, bv, bts, bis, ter); +} + +static int list_arfcn(uint8_t *chan_list, uint8_t mask, char *text) +{ + int n = 0, i; + struct gsm_sysinfo_freq freq[1024]; + + memset(freq, 0, sizeof(freq)); + gsm48_decode_freq_list(freq, chan_list, 16, 0xce, 1); + for (i = 0; i < 1024; i++) { + if (freq[i].mask) { + if (!n) + LOGP(DRR, LOGL_INFO, "%s", text); + LOGPC(DRR, LOGL_INFO, " %d", i); + n++; + } + } + if (n) + LOGPC(DRR, LOGL_INFO, "\n"); + + return n; +} + +static int generate_si1(uint8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_1 *si1 = + (struct gsm48_system_information_type_1 *) output; + + memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si1->header.l2_plen = (21 << 2) | 1; + si1->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si1->header.skip_indicator = 0; + si1->header.system_information = GSM48_MT_RR_SYSINFO_1; + + rc = generate_cell_chan_list(si1->cell_channel_description, bts); + if (rc < 0) + return rc; + list_arfcn(si1->cell_channel_description, 0xce, "Serving cell:"); + + si1->rach_control = bts->si_common.rach_control; + + /* + * SI1 Rest Octets (10.5.2.32), contains NCH position and band + * indicator but that is not in the 04.08. + */ + rc = rest_octets_si1(si1->rest_octets, NULL, is_dcs_net(bts)); + + return sizeof(*si1) + rc; +} + +static int generate_si2(uint8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_2 *si2 = + (struct gsm48_system_information_type_2 *) output; + + memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si2->header.l2_plen = (22 << 2) | 1; + si2->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si2->header.skip_indicator = 0; + si2->header.system_information = GSM48_MT_RR_SYSINFO_2; + + rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts, 0, 0, 0); + if (rc < 0) + return rc; + list_arfcn(si2->bcch_frequency_list, 0xce, + "Neighbour cells in same band:"); + + si2->ncc_permitted = bts->si_common.ncc_permitted; + si2->rach_control = bts->si_common.rach_control; + + return sizeof(*si2); +} + +static int generate_si2bis(uint8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_2bis *si2b = + (struct gsm48_system_information_type_2bis *) output; + int n; + + memset(si2b, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si2b->header.l2_plen = (22 << 2) | 1; + si2b->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si2b->header.skip_indicator = 0; + si2b->header.system_information = GSM48_MT_RR_SYSINFO_2bis; + + rc = generate_bcch_chan_list(si2b->bcch_frequency_list, bts, 0, 1, 0); + if (rc < 0) + return rc; + n = list_arfcn(si2b->bcch_frequency_list, 0xce, + "Neighbour cells in same band, but outside P-GSM:"); + if (n) { + /* indicate in SI2 and SI2bis: there is an extension */ + struct gsm48_system_information_type_2 *si2 = + (struct gsm48_system_information_type_2 *) + bts->si_buf[SYSINFO_TYPE_2]; + si2->bcch_frequency_list[0] |= 0x20; + si2b->bcch_frequency_list[0] |= 0x20; + } else + bts->si_valid &= ~(1 << SYSINFO_TYPE_2bis); + + si2b->rach_control = bts->si_common.rach_control; + + return sizeof(*si2b); +} + +static int generate_si2ter(uint8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_2ter *si2t = + (struct gsm48_system_information_type_2ter *) output; + int n; + + memset(si2t, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si2t->header.l2_plen = (22 << 2) | 1; + si2t->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si2t->header.skip_indicator = 0; + si2t->header.system_information = GSM48_MT_RR_SYSINFO_2ter; + + rc = generate_bcch_chan_list(si2t->ext_bcch_frequency_list, bts, 0, 0, 1); + if (rc < 0) + return rc; + n = list_arfcn(si2t->ext_bcch_frequency_list, 0x8e, + "Neighbour cells in different band:"); + if (!n) + bts->si_valid &= ~(1 << SYSINFO_TYPE_2ter); + + return sizeof(*si2t); +} + +static struct gsm48_si_ro_info si_info = { + .selection_params = { + .present = 0, + }, + .power_offset = { + .present = 0, + }, + .si2ter_indicator = 0, + .early_cm_ctrl = 1, + .scheduling = { + .present = 0, + }, + .gprs_ind = { + .si13_position = 0, + .ra_colour = 0, + .present = 1, + }, + .lsa_params = { + .present = 0, + }, + .cell_id = 0, /* FIXME: doesn't the bts have this? */ + .break_ind = 0, +}; + +static int generate_si3(uint8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_3 *si3 = + (struct gsm48_system_information_type_3 *) output; + + memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si3->header.l2_plen = (18 << 2) | 1; + si3->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si3->header.skip_indicator = 0; + si3->header.system_information = GSM48_MT_RR_SYSINFO_3; + + si3->cell_identity = htons(bts->cell_identity); + gsm48_generate_lai(&si3->lai, bts->network->country_code, + bts->network->network_code, + bts->location_area_code); + si3->control_channel_desc = bts->si_common.chan_desc; + si3->cell_options = bts->si_common.cell_options; + si3->cell_sel_par = bts->si_common.cell_sel_par; + si3->rach_control = bts->si_common.rach_control; + + if ((bts->si_valid & (1 << SYSINFO_TYPE_2ter))) { + LOGP(DRR, LOGL_INFO, "SI 2ter is included.\n"); + si_info.si2ter_indicator = 1; + } else { + si_info.si2ter_indicator = 0; + } + + /* SI3 Rest Octets (10.5.2.34), containing + CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME + Power Offset, 2ter Indicator, Early Classmark Sending, + Scheduling if and WHERE, GPRS Indicator, SI13 position */ + rc = rest_octets_si3(si3->rest_octets, &si_info); + + return sizeof(*si3) + rc; +} + +static int generate_si4(uint8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_4 *si4 = + (struct gsm48_system_information_type_4 *) output; + + /* length of all IEs present except SI4 rest octets and l2_plen */ + int l2_plen = sizeof(*si4) - 1; + + memset(si4, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si4->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si4->header.skip_indicator = 0; + si4->header.system_information = GSM48_MT_RR_SYSINFO_4; + + gsm48_generate_lai(&si4->lai, bts->network->country_code, + bts->network->network_code, + bts->location_area_code); + si4->cell_sel_par = bts->si_common.cell_sel_par; + si4->rach_control = bts->si_common.rach_control; + + /* Optional: CBCH Channel Description + CBCH Mobile Allocation */ + + si4->header.l2_plen = (l2_plen << 2) | 1; + + /* SI4 Rest Octets (10.5.2.35), containing + Optional Power offset, GPRS Indicator, + Cell Identity, LSA ID, Selection Parameter */ + rc = rest_octets_si4(si4->data, &si_info); + + return sizeof(*si4) + rc; +} + +static int generate_si5(uint8_t *output, struct gsm_bts *bts) +{ + struct gsm48_system_information_type_5 *si5; + int rc, l2_plen = 18; + + memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + /* ip.access nanoBTS needs l2_plen!! */ + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_OSMO_SYSMO: + *output++ = (l2_plen << 2) | 1; + l2_plen++; + break; + default: + break; + } + + si5 = (struct gsm48_system_information_type_5 *) output; + + /* l2 pseudo length, not part of msg: 18 */ + si5->rr_protocol_discriminator = GSM48_PDISC_RR; + si5->skip_indicator = 0; + si5->system_information = GSM48_MT_RR_SYSINFO_5; + rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts, 1, 0, 0); + if (rc < 0) + return rc; + list_arfcn(si5->bcch_frequency_list, 0xce, + "Neighbour cells in same band:"); + + /* 04.08 9.1.37: L2 Pseudo Length of 18 */ + return l2_plen; +} + +static int generate_si5bis(uint8_t *output, struct gsm_bts *bts) +{ + struct gsm48_system_information_type_5bis *si5b; + int rc, l2_plen = 18; + int n; + + memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + /* ip.access nanoBTS needs l2_plen!! */ + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_OSMO_SYSMO: + *output++ = (l2_plen << 2) | 1; + l2_plen++; + break; + default: + break; + } + + si5b = (struct gsm48_system_information_type_5bis *) output; + + /* l2 pseudo length, not part of msg: 18 */ + si5b->rr_protocol_discriminator = GSM48_PDISC_RR; + si5b->skip_indicator = 0; + si5b->system_information = GSM48_MT_RR_SYSINFO_5bis; + rc = generate_bcch_chan_list(si5b->bcch_frequency_list, bts, 1, 1, 0); + if (rc < 0) + return rc; + n = list_arfcn(si5b->bcch_frequency_list, 0xce, + "Neighbour cells in same band, but outside P-GSM:"); + if (n) { + /* indicate in SI5 and SI5bis: there is an extension */ + struct gsm48_system_information_type_5 *si5 = + (struct gsm48_system_information_type_5 *) + bts->si_buf[SYSINFO_TYPE_5]; + si5->bcch_frequency_list[0] |= 0x20; + si5b->bcch_frequency_list[0] |= 0x20; + } else + bts->si_valid &= ~(1 << SYSINFO_TYPE_5bis); + + /* 04.08 9.1.37: L2 Pseudo Length of 18 */ + return l2_plen; +} + +static int generate_si5ter(uint8_t *output, struct gsm_bts *bts) +{ + struct gsm48_system_information_type_5ter *si5t; + int rc, l2_plen = 18; + int n; + + memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + /* ip.access nanoBTS needs l2_plen!! */ + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_OSMO_SYSMO: + *output++ = (l2_plen << 2) | 1; + l2_plen++; + break; + default: + break; + } + + si5t = (struct gsm48_system_information_type_5ter *) output; + + /* l2 pseudo length, not part of msg: 18 */ + si5t->rr_protocol_discriminator = GSM48_PDISC_RR; + si5t->skip_indicator = 0; + si5t->system_information = GSM48_MT_RR_SYSINFO_5ter; + rc = generate_bcch_chan_list(si5t->bcch_frequency_list, bts, 1, 0, 1); + if (rc < 0) + return rc; + n = list_arfcn(si5t->bcch_frequency_list, 0x8e, + "Neighbour cells in different band:"); + if (!n) + bts->si_valid &= ~(1 << SYSINFO_TYPE_5ter); + + /* 04.08 9.1.37: L2 Pseudo Length of 18 */ + return l2_plen; +} + +static int generate_si6(uint8_t *output, struct gsm_bts *bts) +{ + struct gsm48_system_information_type_6 *si6; + int l2_plen = 11; + + memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + /* ip.access nanoBTS needs l2_plen!! */ + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_OSMO_SYSMO: + *output++ = (l2_plen << 2) | 1; + l2_plen++; + break; + default: + break; + } + + si6 = (struct gsm48_system_information_type_6 *) output; + + /* l2 pseudo length, not part of msg: 11 */ + si6->rr_protocol_discriminator = GSM48_PDISC_RR; + si6->skip_indicator = 0; + si6->system_information = GSM48_MT_RR_SYSINFO_6; + si6->cell_identity = htons(bts->cell_identity); + gsm48_generate_lai(&si6->lai, bts->network->country_code, + bts->network->network_code, + bts->location_area_code); + si6->cell_options = bts->si_common.cell_options; + si6->ncc_permitted = bts->si_common.ncc_permitted; + + /* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */ + + return l2_plen; +} + +static struct gsm48_si13_info si13_default = { + .cell_opts = { + .nmo = GPRS_NMO_II, + .t3168 = 2000, + .t3192 = 1500, + .drx_timer_max = 3, + .bs_cv_max = 15, + .ext_info_present = 0, + .ext_info = { + /* The values below are just guesses ! */ + .egprs_supported = 0, + .use_egprs_p_ch_req = 1, + .bep_period = 5, + .pfc_supported = 0, + .dtm_supported = 0, + .bss_paging_coordination = 0, + }, + }, + .pwr_ctrl_pars = { + .alpha = 0, /* a = 0.0 */ + .t_avg_w = 16, + .t_avg_t = 16, + .pc_meas_chan = 0, /* downling measured on CCCH */ + .n_avg_i = 8, + }, + .bcch_change_mark = 1, + .si_change_field = 0, + .pbcch_present = 0, + { + .no_pbcch = { + .rac = 0, /* needs to be patched */ + .spgc_ccch_sup = 0, + .net_ctrl_ord = 0, + .prio_acc_thr = 6, + }, + }, +}; + +static int generate_si13(uint8_t *output, struct gsm_bts *bts) +{ + struct gsm48_system_information_type_13 *si13 = + (struct gsm48_system_information_type_13 *) output; + int ret; + + memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si13->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si13->header.skip_indicator = 0; + si13->header.system_information = GSM48_MT_RR_SYSINFO_13; + + si13_default.no_pbcch.rac = bts->gprs.rac; + si13_default.no_pbcch.net_ctrl_ord = bts->gprs.net_ctrl_ord; + + ret = rest_octets_si13(si13->rest_octets, &si13_default); + if (ret < 0) + return ret; + + /* length is coded in bit 2 an up */ + si13->header.l2_plen = 0x01; + + return sizeof (*si13) + ret; +} + +typedef int (*gen_si_fn_t)(uint8_t *output, struct gsm_bts *bts); + +static const gen_si_fn_t gen_si_fn[_MAX_SYSINFO_TYPE] = { + [SYSINFO_TYPE_1] = &generate_si1, + [SYSINFO_TYPE_2] = &generate_si2, + [SYSINFO_TYPE_2bis] = &generate_si2bis, + [SYSINFO_TYPE_2ter] = &generate_si2ter, + [SYSINFO_TYPE_3] = &generate_si3, + [SYSINFO_TYPE_4] = &generate_si4, + [SYSINFO_TYPE_5] = &generate_si5, + [SYSINFO_TYPE_5bis] = &generate_si5bis, + [SYSINFO_TYPE_5ter] = &generate_si5ter, + [SYSINFO_TYPE_6] = &generate_si6, + [SYSINFO_TYPE_13] = &generate_si13, +}; + +int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) +{ + gen_si_fn_t gen_si; + + switch (bts->gprs.mode) { + case BTS_GPRS_EGPRS: + si13_default.cell_opts.ext_info_present = 1; + si13_default.cell_opts.ext_info.egprs_supported = 1; + /* fallthrough */ + case BTS_GPRS_GPRS: + si_info.gprs_ind.present = 1; + break; + case BTS_GPRS_NONE: + si_info.gprs_ind.present = 0; + break; + } + + memcpy(&si_info.selection_params, + &bts->si_common.cell_ro_sel_par, + sizeof(struct gsm48_si_selection_params)); + + gen_si = gen_si_fn[si_type]; + if (!gen_si) + return -EINVAL; + + return gen_si(bts->si_buf[si_type], bts); +} diff --git a/src/libcommon/Makefile.am b/src/libcommon/Makefile.am new file mode 100644 index 000000000..e41326f12 --- /dev/null +++ b/src/libcommon/Makefile.am @@ -0,0 +1,7 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) + +noinst_LIBRARIES = libcommon.a + +libcommon_a_SOURCES = bsc_version.c common_vty.c debug.c gsm_data.c gsm_data_shared.c socket.c talloc_ctx.c diff --git a/src/libcommon/bsc_version.c b/src/libcommon/bsc_version.c new file mode 100644 index 000000000..04acf0412 --- /dev/null +++ b/src/libcommon/bsc_version.c @@ -0,0 +1,30 @@ +/* Hold the copyright and version string */ +/* (C) 2010 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include "../../bscconfig.h" + +const char *openbsc_copyright = + "Copyright (C) 2008-2012 Harald Welte, Holger Freyther\r\n" + "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" + "Dieter Spaar, Andreas Eversberg, Sylvain Munaut\r\n\r\n" + "License AGPLv3+: GNU AGPL version 3 or later \r\n" + "This is free software: you are free to change and redistribute it.\r\n" + "There is NO WARRANTY, to the extent permitted by law.\r\n"; + + diff --git a/src/libcommon/common_vty.c b/src/libcommon/common_vty.c new file mode 100644 index 000000000..7342f14c7 --- /dev/null +++ b/src/libcommon/common_vty.c @@ -0,0 +1,244 @@ +/* OpenBSC VTY common helpers */ +/* (C) 2009-2010 by Harald Welte + * (C) 2009-2010 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +enum node_type bsc_vty_go_parent(struct vty *vty) +{ + switch (vty->node) { + case GSMNET_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case BTS_NODE: + vty->node = GSMNET_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts *bts = vty->index; + vty->index = bts->network; + } + break; + case TRX_NODE: + vty->node = BTS_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts_trx *trx = vty->index; + vty->index = trx->bts; + } + break; + case TS_NODE: + vty->node = TRX_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts_trx_ts *ts = vty->index; + vty->index = ts->trx; + } + break; + case OML_NODE: + case OM2K_NODE: + vty->node = ENABLE_NODE; + talloc_free(vty->index); + vty->index = NULL; + break; + case NAT_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case NAT_BSC_NODE: + vty->node = NAT_NODE; + { + struct bsc_config *bsc_config = vty->index; + vty->index = bsc_config->nat; + } + break; + case PGROUP_NODE: + vty->node = NAT_NODE; + break; + case TRUNK_NODE: + vty->node = MGCP_NODE; + break; + case SMPP_ESME_NODE: + vty->node = SMPP_NODE; + vty->index = NULL; + break; + case SMPP_NODE: + case MSC_NODE: + case MNCC_INT_NODE: + default: + vty->node = CONFIG_NODE; + } + + return vty->node; +} + +/* Down vty node level. */ +gDEFUN(ournode_exit, + ournode_exit_cmd, "exit", "Exit current mode and down to previous mode\n") +{ + switch (vty->node) { + case GSMNET_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case BTS_NODE: + vty->node = GSMNET_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts *bts = vty->index; + vty->index = bts->network; + vty->index_sub = NULL; + } + break; + case TRX_NODE: + vty->node = BTS_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts_trx *trx = vty->index; + vty->index = trx->bts; + vty->index_sub = &trx->bts->description; + } + break; + case TS_NODE: + vty->node = TRX_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts_trx_ts *ts = vty->index; + vty->index = ts->trx; + vty->index_sub = &ts->trx->description; + } + break; + case NAT_BSC_NODE: + vty->node = NAT_NODE; + { + struct bsc_config *bsc_config = vty->index; + vty->index = bsc_config->nat; + } + break; + case PGROUP_NODE: + vty->node = NAT_NODE; + 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: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case OML_NODE: + case OM2K_NODE: + vty->node = ENABLE_NODE; + talloc_free(vty->index); + vty->index = NULL; + break; + case MSC_NODE: + case MNCC_INT_NODE: + vty->node = CONFIG_NODE; + break; + case TRUNK_NODE: + vty->node = MGCP_NODE; + vty->index = NULL; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* End of configuration. */ +gDEFUN(ournode_end, + ournode_end_cmd, "end", "End current mode and change to enable mode.") +{ + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case GSMNET_NODE: + case BTS_NODE: + case TRX_NODE: + case TS_NODE: + case MGCP_NODE: + case TRUNK_NODE: + case GBPROXY_NODE: + case SGSN_NODE: + case VTY_NODE: + case NAT_NODE: + case NAT_BSC_NODE: + case PGROUP_NODE: + case MSC_NODE: + case MNCC_INT_NODE: + case SMPP_NODE: + case SMPP_ESME_NODE: + case BSC_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + vty->index = NULL; + vty->index_sub = NULL; + break; + default: + break; + } + return CMD_SUCCESS; +} + +int bsc_vty_is_config_node(struct vty *vty, int node) +{ + switch (node) { + /* add items that are not config */ + case OML_NODE: + case OM2K_NODE: + case SUBSCR_NODE: + case CONFIG_NODE: + return 0; + + default: + return 1; + } +} + +/* a talloc string replace routine */ +void bsc_replace_string(void *ctx, char **dst, const char *newstr) +{ + if (*dst) + talloc_free(*dst); + *dst = talloc_strdup(ctx, newstr); +} diff --git a/src/libcommon/debug.c b/src/libcommon/debug.c new file mode 100644 index 000000000..b3685e6d2 --- /dev/null +++ b/src/libcommon/debug.c @@ -0,0 +1,212 @@ +/* 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, + }, +}; + +enum log_filter { + _FLT_ALL = LOG_FILTER_ALL, /* libosmocore */ + FLT_IMSI = 1, + FLT_NSVC = 2, + FLT_BVC = 3, +}; + +static int filter_fn(const struct log_context *ctx, + struct log_target *tar) +{ + struct gsm_subscriber *subscr = ctx->ctx[BSC_CTX_SUBSCR]; + const struct gprs_nsvc *nsvc = ctx->ctx[GPRS_CTX_NSVC]; + const struct gprs_nsvc *bvc = ctx->ctx[GPRS_CTX_BVC]; + + if ((tar->filter_map & (1 << FLT_IMSI)) != 0 + && subscr && strcmp(subscr->imsi, tar->filter_data[FLT_IMSI]) == 0) + return 1; + + /* Filter on the NS Virtual Connection */ + if ((tar->filter_map & (1 << FLT_NSVC)) != 0 + && nsvc && (nsvc == tar->filter_data[FLT_NSVC])) + return 1; + + /* Filter on the NS Virtual Connection */ + if ((tar->filter_map & (1 << FLT_BVC)) != 0 + && bvc && (bvc == tar->filter_data[FLT_BVC])) + return 1; + + return 0; +} + +const struct log_info log_info = { + .filter_fn = filter_fn, + .cat = default_categories, + .num_cat = ARRAY_SIZE(default_categories), +}; + +void log_set_imsi_filter(struct log_target *target, const char *imsi) +{ + if (imsi) { + target->filter_map |= (1 << FLT_IMSI); + target->filter_data[FLT_IMSI] = talloc_strdup(target, imsi); + } else if (target->filter_data[FLT_IMSI]) { + target->filter_map &= ~(1 << FLT_IMSI); + talloc_free(target->filter_data[FLT_IMSI]); + target->filter_data[FLT_IMSI] = NULL; + } +} diff --git a/src/libcommon/gsm_data.c b/src/libcommon/gsm_data.c new file mode 100644 index 000000000..5f7e32e73 --- /dev/null +++ b/src/libcommon/gsm_data.c @@ -0,0 +1,461 @@ +/* (C) 2008-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +void *tall_bsc_ctx; + +static LLIST_HEAD(bts_models); + +void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr, + uint8_t e1_ts, uint8_t e1_ts_ss) +{ + ts->e1_link.e1_nr = e1_nr; + ts->e1_link.e1_ts = e1_ts; + ts->e1_link.e1_ts_ss = e1_ts_ss; +} + +static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type) +{ + struct gsm_bts_model *model; + + llist_for_each_entry(model, &bts_models, list) { + if (model->type == type) + return model; + } + + return NULL; +} + +int gsm_bts_model_register(struct gsm_bts_model *model) +{ + if (bts_model_find(model->type)) + return -EEXIST; + + tlv_def_patch(&model->nm_att_tlvdef, &abis_nm_att_tlvdef); + llist_add_tail(&model->list, &bts_models); + return 0; +} + +struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_code, + int (*mncc_recv)(struct gsm_network *, struct msgb *)) +{ + struct gsm_network *net; + + net = talloc_zero(tall_bsc_ctx, struct gsm_network); + if (!net) + return NULL; + + net->bsc_data = talloc_zero(net, struct osmo_bsc_data); + if (!net->bsc_data) { + talloc_free(net); + return NULL; + } + + /* Init back pointer */ + net->bsc_data->auto_off_timeout = -1; + net->bsc_data->network = net; + INIT_LLIST_HEAD(&net->bsc_data->mscs); + + net->country_code = country_code; + net->network_code = network_code; + net->num_bts = 0; + net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED; + net->T3101 = GSM_T3101_DEFAULT; + net->T3105 = GSM_T3105_DEFAULT; + net->T3113 = GSM_T3113_DEFAULT; + net->T3122 = GSM_T3122_DEFAULT; + /* FIXME: initialize all other timers! */ + + /* default set of handover parameters */ + net->handover.win_rxlev_avg = 10; + net->handover.win_rxqual_avg = 1; + net->handover.win_rxlev_avg_neigh = 10; + net->handover.pwr_interval = 6; + net->handover.pwr_hysteresis = 3; + net->handover.max_distance = 9999; + + INIT_LLIST_HEAD(&net->trans_list); + INIT_LLIST_HEAD(&net->upqueue); + INIT_LLIST_HEAD(&net->bts_list); + + net->stats.chreq.total = osmo_counter_alloc("net.chreq.total"); + net->stats.chreq.no_channel = osmo_counter_alloc("net.chreq.no_channel"); + net->stats.handover.attempted = osmo_counter_alloc("net.handover.attempted"); + net->stats.handover.no_channel = osmo_counter_alloc("net.handover.no_channel"); + net->stats.handover.timeout = osmo_counter_alloc("net.handover.timeout"); + net->stats.handover.completed = osmo_counter_alloc("net.handover.completed"); + net->stats.handover.failed = osmo_counter_alloc("net.handover.failed"); + net->stats.loc_upd_type.attach = osmo_counter_alloc("net.loc_upd_type.attach"); + net->stats.loc_upd_type.normal = osmo_counter_alloc("net.loc_upd_type.normal"); + net->stats.loc_upd_type.periodic = osmo_counter_alloc("net.loc_upd_type.periodic"); + net->stats.loc_upd_type.detach = osmo_counter_alloc("net.imsi_detach.count"); + net->stats.loc_upd_resp.reject = osmo_counter_alloc("net.loc_upd_resp.reject"); + net->stats.loc_upd_resp.accept = osmo_counter_alloc("net.loc_upd_resp.accept"); + net->stats.paging.attempted = osmo_counter_alloc("net.paging.attempted"); + net->stats.paging.detached = osmo_counter_alloc("net.paging.detached"); + net->stats.paging.completed = osmo_counter_alloc("net.paging.completed"); + net->stats.paging.expired = osmo_counter_alloc("net.paging.expired"); + net->stats.sms.submitted = osmo_counter_alloc("net.sms.submitted"); + net->stats.sms.no_receiver = osmo_counter_alloc("net.sms.no_receiver"); + net->stats.sms.delivered = osmo_counter_alloc("net.sms.delivered"); + net->stats.sms.rp_err_mem = osmo_counter_alloc("net.sms.rp_err_mem"); + net->stats.sms.rp_err_other = osmo_counter_alloc("net.sms.rp_err_other"); + net->stats.call.mo_setup = osmo_counter_alloc("net.call.mo_setup"); + net->stats.call.mo_connect_ack = osmo_counter_alloc("net.call.mo_connect_ack"); + net->stats.call.mt_setup = osmo_counter_alloc("net.call.mt_setup"); + net->stats.call.mt_connect = osmo_counter_alloc("net.call.mt_connect"); + net->stats.chan.rf_fail = osmo_counter_alloc("net.chan.rf_fail"); + net->stats.chan.rll_err = osmo_counter_alloc("net.chan.rll_err"); + net->stats.bts.oml_fail = osmo_counter_alloc("net.bts.oml_fail"); + net->stats.bts.rsl_fail = osmo_counter_alloc("net.bts.rsl_fail"); + + net->mncc_recv = mncc_recv; + + gsm_net_update_ctype(net); + + return net; +} + +struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num) +{ + struct gsm_bts *bts; + + if (num >= net->num_bts) + return NULL; + + llist_for_each_entry(bts, &net->bts_list, list) { + if (bts->nr == num) + return bts; + } + + return NULL; +} + +/* Get reference to a neighbor cell on a given BCCH ARFCN */ +struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, + uint16_t arfcn, uint8_t bsic) +{ + struct gsm_bts *neigh; + /* FIXME: use some better heuristics here to determine which cell + * using this ARFCN really is closest to the target cell. For + * now we simply assume that each ARFCN will only be used by one + * cell */ + + llist_for_each_entry(neigh, &bts->network->bts_list, list) { + if (neigh->c0->arfcn == arfcn && + neigh->bsic == bsic) + return neigh; + } + + return NULL; +} + +const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE+1] = { + { GSM_BTS_TYPE_UNKNOWN, "unknown" }, + { GSM_BTS_TYPE_BS11, "bs11" }, + { GSM_BTS_TYPE_NANOBTS, "nanobts" }, + { GSM_BTS_TYPE_RBS2000, "rbs2000" }, + { GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" }, + { GSM_BTS_TYPE_OSMO_SYSMO, "sysmobts" }, + { 0, NULL } +}; + +const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = { + { GSM_BTS_TYPE_UNKNOWN, "Unknown BTS Type" }, + { GSM_BTS_TYPE_BS11, "Siemens BTS (BS-11 or compatible)" }, + { GSM_BTS_TYPE_NANOBTS, "ip.access nanoBTS or compatible" }, + { GSM_BTS_TYPE_RBS2000, "Ericsson RBS2000 Series" }, + { GSM_BTS_TYPE_NOKIA_SITE, "Nokia {Metro,Ultra,In}Site" }, + { GSM_BTS_TYPE_OSMO_SYSMO, "sysmocom sysmoBTS" }, + { 0, NULL } +}; + +enum gsm_bts_type parse_btstype(const char *arg) +{ + return get_string_value(bts_type_names, arg); +} + +const char *btstype2str(enum gsm_bts_type type) +{ + return get_value_string(bts_type_names, type); +} + +struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr) +{ + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->nr == nr) + return trx; + } + return NULL; +} + +/* Search for a BTS in the given Location Area; optionally start searching + * with start_bts (for continuing to search after the first result) */ +struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, + struct gsm_bts *start_bts) +{ + int i; + struct gsm_bts *bts; + int skip = 0; + + if (start_bts) + skip = 1; + + for (i = 0; i < net->num_bts; i++) { + bts = gsm_bts_num(net, i); + + if (skip) { + if (start_bts == bts) + skip = 0; + continue; + } + + if (lac == GSM_LAC_RESERVED_ALL_BTS || bts->location_area_code == lac) + return bts; + } + return NULL; +} + +static const struct value_string auth_policy_names[] = { + { GSM_AUTH_POLICY_CLOSED, "closed" }, + { GSM_AUTH_POLICY_ACCEPT_ALL, "accept-all" }, + { GSM_AUTH_POLICY_TOKEN, "token" }, + { 0, NULL } +}; + +enum gsm_auth_policy gsm_auth_policy_parse(const char *arg) +{ + return get_string_value(auth_policy_names, arg); +} + +const char *gsm_auth_policy_name(enum gsm_auth_policy policy) +{ + return get_value_string(auth_policy_names, policy); +} + +static const struct value_string rrlp_mode_names[] = { + { RRLP_MODE_NONE, "none" }, + { RRLP_MODE_MS_BASED, "ms-based" }, + { RRLP_MODE_MS_PREF, "ms-preferred" }, + { RRLP_MODE_ASS_PREF, "ass-preferred" }, + { 0, NULL } +}; + +enum rrlp_mode rrlp_mode_parse(const char *arg) +{ + return get_string_value(rrlp_mode_names, arg); +} + +const char *rrlp_mode_name(enum rrlp_mode mode) +{ + return get_value_string(rrlp_mode_names, mode); +} + +static const struct value_string bts_gprs_mode_names[] = { + { BTS_GPRS_NONE, "none" }, + { BTS_GPRS_GPRS, "gprs" }, + { BTS_GPRS_EGPRS, "egprs" }, + { 0, NULL } +}; + +enum bts_gprs_mode bts_gprs_mode_parse(const char *arg) +{ + return get_string_value(bts_gprs_mode_names, arg); +} + +const char *bts_gprs_mode_name(enum bts_gprs_mode mode) +{ + return get_value_string(bts_gprs_mode_names, mode); +} + +struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan) +{ + struct gsm_meas_rep *meas_rep; + + meas_rep = &lchan->meas_rep[lchan->meas_rep_idx]; + memset(meas_rep, 0, sizeof(*meas_rep)); + meas_rep->lchan = lchan; + lchan->meas_rep_idx = (lchan->meas_rep_idx + 1) + % ARRAY_SIZE(lchan->meas_rep); + + return meas_rep; +} + +int gsm_btsmodel_set_feature(struct gsm_bts_model *bts, enum gsm_bts_features feat) +{ + return bitvec_set_bit_pos(&bts->features, feat, 1); +} + +int gsm_bts_has_feature(struct gsm_bts *bts, enum gsm_bts_features feat) +{ + return bitvec_get_bit_pos(&bts->model->features, feat); +} + +int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) +{ + struct gsm_bts_model *model; + + model = bts_model_find(type); + if (!model) + return -EINVAL; + + bts->type = type; + bts->model = model; + + if (model->start && !model->started) { + int ret = model->start(bts->network); + if (ret < 0) + return ret; + + model->started = true; + } + + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_OSMO_SYSMO: + /* Set the default OML Stream ID to 0xff */ + bts->oml_tei = 0xff; + bts->c0->nominal_power = 23; + break; + case GSM_BTS_TYPE_RBS2000: + INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups); + INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups); + break; + case GSM_BTS_TYPE_BS11: + case GSM_BTS_TYPE_UNKNOWN: + case GSM_BTS_TYPE_NOKIA_SITE: + case _NUM_GSM_BTS_TYPE: + break; + } + + return 0; +} + +struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type, + uint8_t tsc, uint8_t bsic) +{ + struct gsm_bts_model *model = bts_model_find(type); + struct gsm_bts *bts; + + if (!model && type != GSM_BTS_TYPE_UNKNOWN) + return NULL; + + bts = gsm_bts_alloc(net); + if (!bts) + return NULL; + + bts->network = net; + bts->nr = net->num_bts++; + bts->type = type; + bts->model = model; + bts->tsc = tsc; + bts->bsic = bsic; + + bts->neigh_list_manual_mode = 0; + bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */ + bts->si_common.cell_sel_par.rxlev_acc_min = 0; + bts->si_common.neigh_list.data = bts->si_common.data.neigh_list; + bts->si_common.neigh_list.data_len = + sizeof(bts->si_common.data.neigh_list); + bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list; + bts->si_common.si5_neigh_list.data_len = + sizeof(bts->si_common.data.si5_neigh_list); + bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc; + bts->si_common.cell_alloc.data_len = + sizeof(bts->si_common.data.cell_alloc); + bts->si_common.rach_control.re = 1; /* no re-establishment */ + bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */ + bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */ + bts->si_common.rach_control.t2 = 4; /* no emergency calls */ + bts->si_common.chan_desc.att = 1; /* attachment required */ + bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */ + bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */ + bts->si_common.chan_desc.t3212 = 5; /* Use 30 min periodic update interval as sane default */ + + 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; +} + diff --git a/src/libcommon/gsm_data_shared.c b/src/libcommon/gsm_data_shared.c new file mode 100644 index 000000000..1b0814c49 --- /dev/null +++ b/src/libcommon/gsm_data_shared.c @@ -0,0 +1,521 @@ +/* (C) 2008-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +void gsm_abis_mo_reset(struct gsm_abis_mo *mo) +{ + mo->nm_state.operational = NM_OPSTATE_NULL; + mo->nm_state.availability = NM_AVSTATE_POWER_OFF; +} + +static void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts, + uint8_t obj_class, uint8_t p1, uint8_t p2, uint8_t p3) +{ + mo->bts = bts; + mo->obj_class = obj_class; + mo->obj_inst.bts_nr = p1; + mo->obj_inst.trx_nr = p2; + mo->obj_inst.ts_nr = p3; + gsm_abis_mo_reset(mo); +} + +const struct value_string gsm_pchant_names[10] = { + { GSM_PCHAN_NONE, "NONE" }, + { GSM_PCHAN_CCCH, "CCCH" }, + { GSM_PCHAN_CCCH_SDCCH4,"CCCH+SDCCH4" }, + { GSM_PCHAN_TCH_F, "TCH/F" }, + { GSM_PCHAN_TCH_H, "TCH/H" }, + { GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH8" }, + { GSM_PCHAN_PDCH, "PDCH" }, + { GSM_PCHAN_TCH_F_PDCH, "TCH/F_PDCH" }, + { GSM_PCHAN_UNKNOWN, "UNKNOWN" }, + { 0, NULL } +}; + +const struct value_string gsm_pchant_descs[10] = { + { 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" }, + { 0, NULL } +}; + +const char *gsm_pchan_name(enum gsm_phys_chan_config c) +{ + return get_value_string(gsm_pchant_names, c); +} + +enum gsm_phys_chan_config gsm_pchan_parse(const char *name) +{ + return get_string_value(gsm_pchant_names, name); +} + +const struct value_string gsm_lchant_names[6] = { + { GSM_LCHAN_NONE, "NONE" }, + { GSM_LCHAN_SDCCH, "SDCCH" }, + { GSM_LCHAN_TCH_F, "TCH/F" }, + { GSM_LCHAN_TCH_H, "TCH/H" }, + { GSM_LCHAN_UNKNOWN, "UNKNOWN" }, + { 0, NULL } +}; + +const char *gsm_lchant_name(enum gsm_chan_t c) +{ + return get_value_string(gsm_lchant_names, c); +} + +static const struct value_string lchan_s_names[] = { + { LCHAN_S_NONE, "NONE" }, + { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" }, + { LCHAN_S_ACTIVE, "ACTIVE" }, + { LCHAN_S_INACTIVE, "INACTIVE" }, + { LCHAN_S_REL_REQ, "RELEASE REQUESTED" }, + { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" }, + { LCHAN_S_BROKEN, "BROKEN UNUSABLE" }, + { 0, NULL } +}; + +const char *gsm_lchans_name(enum gsm_lchan_state s) +{ + return get_value_string(lchan_s_names, s); +} + +static const struct value_string chreq_names[] = { + { GSM_CHREQ_REASON_EMERG, "EMERGENCY" }, + { GSM_CHREQ_REASON_PAG, "PAGING" }, + { GSM_CHREQ_REASON_CALL, "CALL" }, + { GSM_CHREQ_REASON_LOCATION_UPD,"LOCATION_UPDATE" }, + { GSM_CHREQ_REASON_OTHER, "OTHER" }, + { 0, NULL } +}; + +const char *gsm_chreq_name(enum gsm_chreq_reason_t c) +{ + return get_value_string(chreq_names, c); +} + +struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx); + int k; + + if (!trx) + return NULL; + + trx->bts = bts; + trx->nr = bts->num_trx++; + trx->mo.nm_state.administrative = NM_STATE_UNLOCKED; + + gsm_mo_init(&trx->mo, bts, NM_OC_RADIO_CARRIER, + bts->nr, trx->nr, 0xff); + gsm_mo_init(&trx->bb_transc.mo, bts, NM_OC_BASEB_TRANSC, + bts->nr, trx->nr, 0xff); + + for (k = 0; k < TRX_NR_TS; k++) { + struct gsm_bts_trx_ts *ts = &trx->ts[k]; + int l; + + ts->trx = trx; + ts->nr = k; + ts->pchan = GSM_PCHAN_NONE; + ts->tsc = -1; + + gsm_mo_init(&ts->mo, bts, NM_OC_CHANNEL, + bts->nr, trx->nr, ts->nr); + + ts->hopping.arfcns.data_len = sizeof(ts->hopping.arfcns_data); + ts->hopping.arfcns.data = ts->hopping.arfcns_data; + ts->hopping.ma.data_len = sizeof(ts->hopping.ma_data); + ts->hopping.ma.data = ts->hopping.ma_data; + + for (l = 0; l < TS_MAX_LCHAN; l++) { + struct gsm_lchan *lchan; + lchan = &ts->lchan[l]; + + lchan->ts = ts; + lchan->nr = l; + lchan->type = GSM_LCHAN_NONE; + +#ifndef ROLE_BSC + INIT_LLIST_HEAD(&lchan->sapi_cmds); +#endif + } + } + + if (trx->nr != 0) + trx->nominal_power = bts->c0->nominal_power; + + llist_add_tail(&trx->list, &bts->trx_list); + + return trx; +} + + +static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 }; +static const uint8_t bts_cell_timer_default[] = + { 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 }; +static const struct gprs_rlc_cfg rlc_cfg_default = { + .parameter = { + [RLC_T3142] = 20, + [RLC_T3169] = 5, + [RLC_T3191] = 5, + [RLC_T3193] = 160, /* 10ms */ + [RLC_T3195] = 5, + [RLC_N3101] = 10, + [RLC_N3103] = 4, + [RLC_N3105] = 8, + [CV_COUNTDOWN] = 15, + [T_DL_TBF_EXT] = 250 * 10, /* ms */ + [T_UL_TBF_EXT] = 250 * 10, /* ms */ + }, + .paging = { + .repeat_time = 5 * 50, /* ms */ + .repeat_count = 3, + }, + .cs_mask = 0x1fff, + .initial_cs = 2, + .initial_mcs = 6, +}; + +struct gsm_bts *gsm_bts_alloc(void *ctx) +{ + struct gsm_bts *bts = talloc_zero(ctx, struct gsm_bts); + int i; + + if (!bts) + return NULL; + + bts->num_trx = 0; + INIT_LLIST_HEAD(&bts->trx_list); + bts->ms_max_power = 15; /* dBm */ + + gsm_mo_init(&bts->mo, bts, NM_OC_BTS, + bts->nr, 0xff, 0xff); + gsm_mo_init(&bts->site_mgr.mo, bts, NM_OC_SITE_MANAGER, + 0xff, 0xff, 0xff); + + for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { + bts->gprs.nsvc[i].bts = bts; + bts->gprs.nsvc[i].id = i; + gsm_mo_init(&bts->gprs.nsvc[i].mo, bts, NM_OC_GPRS_NSVC, + bts->nr, i, 0xff); + } + memcpy(&bts->gprs.nse.timer, bts_nse_timer_default, + sizeof(bts->gprs.nse.timer)); + gsm_mo_init(&bts->gprs.nse.mo, bts, NM_OC_GPRS_NSE, + bts->nr, 0xff, 0xff); + memcpy(&bts->gprs.cell.timer, bts_cell_timer_default, + sizeof(bts->gprs.cell.timer)); + gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL, + bts->nr, 0xff, 0xff); + memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default, + sizeof(bts->gprs.cell.rlc_cfg)); + + /* create our primary TRX */ + bts->c0 = gsm_bts_trx_alloc(bts); + if (!bts->c0) { + talloc_free(bts); + return NULL; + } + bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4; + + bts->rach_b_thresh = -1; + bts->rach_ldavg_slots = -1; + bts->paging.free_chans_need = -1; + + return bts; +} + +/* reset the state of all MO in the BTS */ +void gsm_bts_mo_reset(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + unsigned int i; + + gsm_abis_mo_reset(&bts->mo); + gsm_abis_mo_reset(&bts->site_mgr.mo); + for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) + gsm_abis_mo_reset(&bts->gprs.nsvc[i].mo); + gsm_abis_mo_reset(&bts->gprs.nse.mo); + gsm_abis_mo_reset(&bts->gprs.cell.mo); + + llist_for_each_entry(trx, &bts->trx_list, list) { + gsm_abis_mo_reset(&trx->mo); + gsm_abis_mo_reset(&trx->bb_transc.mo); + + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + gsm_abis_mo_reset(&ts->mo); + } + } +} + +struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num) +{ + struct gsm_bts_trx *trx; + + if (num >= bts->num_trx) + return NULL; + + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->nr == num) + return trx; + } + + return NULL; +} + +static char ts2str[255]; + +char *gsm_trx_name(const struct gsm_bts_trx *trx) +{ + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)", + trx->bts->nr, trx->nr); + + return ts2str; +} + + +char *gsm_ts_name(const struct gsm_bts_trx_ts *ts) +{ + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)", + ts->trx->bts->nr, ts->trx->nr, ts->nr); + + return ts2str; +} + +char *gsm_lchan_name(const struct gsm_lchan *lchan) +{ + struct gsm_bts_trx_ts *ts = lchan->ts; + + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,ss=%d)", + ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr); + + return ts2str; +} + +/* obtain the MO structure for a given object instance */ +struct gsm_abis_mo * +gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class, + const struct abis_om_obj_inst *obj_inst) +{ + struct gsm_bts_trx *trx; + struct gsm_abis_mo *mo = NULL; + + switch (obj_class) { + case NM_OC_BTS: + mo = &bts->mo; + break; + case NM_OC_RADIO_CARRIER: + if (obj_inst->trx_nr >= bts->num_trx) { + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + mo = &trx->mo; + break; + case NM_OC_BASEB_TRANSC: + if (obj_inst->trx_nr >= bts->num_trx) { + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + mo = &trx->bb_transc.mo; + break; + case NM_OC_CHANNEL: + if (obj_inst->trx_nr >= bts->num_trx) { + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + if (obj_inst->ts_nr >= TRX_NR_TS) + return NULL; + mo = &trx->ts[obj_inst->ts_nr].mo; + break; + case NM_OC_SITE_MANAGER: + mo = &bts->site_mgr.mo; + break; + case NM_OC_BS11: + switch (obj_inst->bts_nr) { + case BS11_OBJ_CCLK: + mo = &bts->bs11.cclk.mo; + break; + case BS11_OBJ_BBSIG: + if (obj_inst->ts_nr > bts->num_trx) + return NULL; + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + mo = &trx->bs11.bbsig.mo; + break; + case BS11_OBJ_PA: + if (obj_inst->ts_nr > bts->num_trx) + return NULL; + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + mo = &trx->bs11.pa.mo; + break; + default: + return NULL; + } + break; + case NM_OC_BS11_RACK: + mo = &bts->bs11.rack.mo; + break; + case NM_OC_BS11_ENVABTSE: + if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse)) + return NULL; + mo = &bts->bs11.envabtse[obj_inst->trx_nr].mo; + break; + case NM_OC_GPRS_NSE: + mo = &bts->gprs.nse.mo; + break; + case NM_OC_GPRS_CELL: + mo = &bts->gprs.cell.mo; + break; + case NM_OC_GPRS_NSVC: + if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) + return NULL; + mo = &bts->gprs.nsvc[obj_inst->trx_nr].mo; + break; + } + return mo; +} + +/* obtain the gsm_nm_state data structure for a given object instance */ +struct gsm_nm_state * +gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class, + const struct abis_om_obj_inst *obj_inst) +{ + struct gsm_abis_mo *mo; + + mo = gsm_objclass2mo(bts, obj_class, obj_inst); + if (!mo) + return NULL; + + return &mo->nm_state; +} + +/* obtain the in-memory data structure of a given object instance */ +void * +gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class, + const struct abis_om_obj_inst *obj_inst) +{ + struct gsm_bts_trx *trx; + void *obj = NULL; + + switch (obj_class) { + case NM_OC_BTS: + obj = bts; + break; + case NM_OC_RADIO_CARRIER: + if (obj_inst->trx_nr >= bts->num_trx) { + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + obj = trx; + break; + case NM_OC_BASEB_TRANSC: + if (obj_inst->trx_nr >= bts->num_trx) { + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + obj = &trx->bb_transc; + break; + case NM_OC_CHANNEL: + if (obj_inst->trx_nr >= bts->num_trx) { + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + if (obj_inst->ts_nr >= TRX_NR_TS) + return NULL; + obj = &trx->ts[obj_inst->ts_nr]; + break; + case NM_OC_SITE_MANAGER: + obj = &bts->site_mgr; + break; + case NM_OC_GPRS_NSE: + obj = &bts->gprs.nse; + break; + case NM_OC_GPRS_CELL: + obj = &bts->gprs.cell; + break; + case NM_OC_GPRS_NSVC: + if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) + return NULL; + obj = &bts->gprs.nsvc[obj_inst->trx_nr]; + break; + } + return obj; +} + +/* See Table 10.5.25 of GSM04.08 */ +uint8_t gsm_ts2chan_nr(const struct gsm_bts_trx_ts *ts, uint8_t lchan_nr) +{ + uint8_t cbits, chan_nr; + + switch (ts->pchan) { + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_PDCH: + case GSM_PCHAN_TCH_F_PDCH: + cbits = 0x01; + break; + case GSM_PCHAN_TCH_H: + cbits = 0x02; + cbits += lchan_nr; + break; + case GSM_PCHAN_CCCH_SDCCH4: + cbits = 0x04; + cbits += lchan_nr; + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + cbits = 0x08; + cbits += lchan_nr; + break; + default: + case GSM_PCHAN_CCCH: + cbits = 0x10; + break; + } + + chan_nr = (cbits << 3) | (ts->nr & 0x7); + + return chan_nr; +} + +uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan) +{ + return gsm_ts2chan_nr(lchan->ts, lchan->nr); +} diff --git a/src/libcommon/socket.c b/src/libcommon/socket.c new file mode 100644 index 000000000..2a64767f8 --- /dev/null +++ b/src/libcommon/socket.c @@ -0,0 +1,111 @@ +/* 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/src/libcommon/talloc_ctx.c b/src/libcommon/talloc_ctx.c new file mode 100644 index 000000000..ae6a15636 --- /dev/null +++ b/src/libcommon/talloc_ctx.c @@ -0,0 +1,38 @@ +#include +#include + +extern void *tall_msgb_ctx; +extern void *tall_fle_ctx; +extern void *tall_locop_ctx; +extern void *tall_authciphop_ctx; +extern void *tall_gsms_ctx; +extern void *tall_subscr_ctx; +extern void *tall_sub_req_ctx; +extern void *tall_call_ctx; +extern void *tall_paging_ctx; +extern void *tall_sigh_ctx; +extern void *tall_tqe_ctx; +extern void *tall_trans_ctx; +extern void *tall_map_ctx; +extern void *tall_upq_ctx; +extern void *tall_ctr_ctx; + +void talloc_ctx_init(void) +{ + tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); + tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0, + "bs11_file_list_entry"); + tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 0, "loc_updating_oper"); + tall_authciphop_ctx = talloc_named_const(tall_bsc_ctx, 0, "auth_ciph_oper"); + tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 0, "sms"); + tall_subscr_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscriber"); + tall_sub_req_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscr_request"); + tall_call_ctx = talloc_named_const(tall_bsc_ctx, 0, "gsm_call"); + tall_paging_ctx = talloc_named_const(tall_bsc_ctx, 0, "paging_request"); + tall_sigh_ctx = talloc_named_const(tall_bsc_ctx, 0, "signal_handler"); + tall_tqe_ctx = talloc_named_const(tall_bsc_ctx, 0, "subch_txq_entry"); + tall_trans_ctx = talloc_named_const(tall_bsc_ctx, 0, "transaction"); + tall_map_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_map_entry"); + tall_upq_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_upq_entry"); + tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); +} diff --git a/src/libctrl/Makefile.am b/src/libctrl/Makefile.am new file mode 100644 index 000000000..4f039c826 --- /dev/null +++ b/src/libctrl/Makefile.am @@ -0,0 +1,7 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) $(COVERAGE_LDFLAGS) + +noinst_LIBRARIES = libctrl.a + +libctrl_a_SOURCES = control_if.c control_cmd.c diff --git a/src/libctrl/control_cmd.c b/src/libctrl/control_cmd.c new file mode 100644 index 000000000..3c4efc06d --- /dev/null +++ b/src/libctrl/control_cmd.c @@ -0,0 +1,493 @@ +/* SNMP-like status interface + * + * (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 +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +extern vector ctrl_node_vec; + +static struct ctrl_cmd_map ccm[] = { + {"GET", CTRL_TYPE_GET}, + {"SET", CTRL_TYPE_SET}, + {"GET_REPLY", CTRL_TYPE_GET_REPLY}, + {"SET_REPLY", CTRL_TYPE_SET_REPLY}, + {"TRAP", CTRL_TYPE_TRAP}, + {"ERROR", CTRL_TYPE_ERROR}, + {NULL} +}; + +int ctrl_cmd_str2type(char *s) +{ + int i; + for (i=0; ccm[i].cmd != NULL; i++) { + if (strcasecmp(s, ccm[i].cmd) == 0) + return ccm[i].type; + } + return CTRL_TYPE_UNKNOWN; +} + +char *ctrl_cmd_type2str(int type) +{ + int i; + for (i=0; ccm[i].cmd != NULL; i++) { + if (ccm[i].type == type) + return ccm[i].cmd; + } + return NULL; +} + +/* Functions from libosmocom */ +extern vector cmd_make_descvec(const char *string, const char *descstr); + +/* Get the ctrl_cmd_element that matches this command */ +static struct ctrl_cmd_element *ctrl_cmd_get_element_match(vector vline, vector node) +{ + int index, j; + const char *desc; + struct ctrl_cmd_element *cmd_el; + struct ctrl_cmd_struct *cmd_desc; + char *str; + + for (index = 0; index < vector_active(node); index++) { + if ((cmd_el = vector_slot(node, index))) { + cmd_desc = &cmd_el->strcmd; + if (cmd_desc->nr_commands > vector_active(vline)) + continue; + for (j =0; j < vector_active(vline); j++) { + str = vector_slot(vline, j); + desc = cmd_desc->command[j]; + if (desc[0] == '*') + return cmd_el; /* Partial match */ + if (strcmp(desc, str) != 0) + break; + } + /* We went through all the elements and all matched */ + if (j == cmd_desc->nr_commands) + return cmd_el; + } + } + + return NULL; +} + +int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data) +{ + int ret = CTRL_CMD_ERROR; + struct ctrl_cmd_element *cmd_el; + + if ((command->type != CTRL_TYPE_GET) && (command->type != CTRL_TYPE_SET)) { + command->reply = "Trying to execute something not GET or SET"; + goto out; + } + if ((command->type == CTRL_TYPE_SET) && (!command->value)) { + command->reply = "SET without a value"; + goto out; + } + + if (!vline) + goto out; + + cmd_el = ctrl_cmd_get_element_match(vline, node); + + if (!cmd_el) { + command->reply = "Command not found"; + goto out; + } + + if (command->type == CTRL_TYPE_SET) { + if (!cmd_el->set) { + command->reply = "SET not implemented"; + goto out; + } + if (cmd_el->verify) { + if ((ret = cmd_el->verify(command, command->value, data))) { + ret = CTRL_CMD_ERROR; + /* If verify() set an appropriate error message, don't change it. */ + if (!command->reply) + command->reply = "Value failed verification."; + goto out; + } + } else if (cmd_el->param) { + LOGP(DCTRL, LOGL_NOTICE, "Parameter verification unimplemented, continuing without\n"); + } + ret = cmd_el->set(command, data); + goto out; + } else if (command->type == CTRL_TYPE_GET) { + if (!cmd_el->get) { + command->reply = "GET not implemented"; + goto out; + } + ret = cmd_el->get(command, data); + goto out; + } +out: + if (ret == CTRL_CMD_REPLY) { + if (command->type == CTRL_TYPE_SET) { + command->type = CTRL_TYPE_SET_REPLY; + } else if (command->type == CTRL_TYPE_GET) { + command->type = CTRL_TYPE_GET_REPLY; + } + } else if (ret == CTRL_CMD_ERROR) { + command->type = CTRL_TYPE_ERROR; + } + return ret; +} + +static void add_word(struct ctrl_cmd_struct *cmd, + const char *start, const char *end) +{ + if (!cmd->command) { + cmd->command = talloc_zero_array(tall_vty_vec_ctx, + char*, 1); + cmd->nr_commands = 0; + } else { + cmd->command = talloc_realloc(tall_vty_vec_ctx, + cmd->command, char*, + cmd->nr_commands + 1); + } + + cmd->command[cmd->nr_commands++] = talloc_strndup(cmd->command, + start, end - start); +} + +static void create_cmd_struct(struct ctrl_cmd_struct *cmd, const char *name) +{ + const char *cur, *word; + + for (cur = name, word = NULL; cur[0] != '\0'; ++cur) { + /* warn about optionals */ + if (cur[0] == '(' || cur[0] == ')' || cur[0] == '|') { + LOGP(DCTRL, LOGL_ERROR, + "Optionals are not supported in '%s'\n", name); + goto failure; + } + + if (isspace(cur[0])) { + if (word) { + add_word(cmd, word, cur); + word = NULL; + } + continue; + } + + if (!word) + word = cur; + } + + if (word) + add_word(cmd, word, cur); + + return; +failure: + cmd->nr_commands = 0; + talloc_free(cmd->command); +} + +int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd) +{ + vector cmds_vec; + + cmds_vec = vector_lookup_ensure(ctrl_node_vec, node); + + if (!cmds_vec) { + cmds_vec = vector_init(5); + if (!cmds_vec) { + LOGP(DCTRL, LOGL_ERROR, "vector_init failed.\n"); + return -ENOMEM; + } + vector_set_index(ctrl_node_vec, node, cmds_vec); + } + + vector_set(cmds_vec, cmd); + + create_cmd_struct(&cmd->strcmd, cmd->name); + return 0; +} + +struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type type) +{ + struct ctrl_cmd *cmd; + + cmd = talloc_zero(ctx, struct ctrl_cmd); + if (!cmd) + return NULL; + + cmd->type = type; + return cmd; +} + +struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd) +{ + struct ctrl_cmd *cmd2; + + cmd2 = talloc_zero(ctx, struct ctrl_cmd); + if (!cmd2) + return NULL; + + cmd2->type = cmd->type; + if (cmd->id) { + cmd2->id = talloc_strdup(cmd2, cmd->id); + if (!cmd2->id) + goto err; + } + if (cmd->variable) { + cmd2->variable = talloc_strdup(cmd2, cmd->variable); + if (!cmd2->variable) + goto err; + } + if (cmd->value) { + cmd2->value = talloc_strdup(cmd2, cmd->value); + if (!cmd2->value) + goto err; + } + if (cmd->reply) { + cmd2->reply = talloc_strdup(cmd2, cmd->reply); + if (!cmd2->reply) + goto err; + } + + return cmd2; +err: + talloc_free(cmd2); + return NULL; +} + +struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg) +{ + char *str, *tmp, *saveptr = NULL; + char *var, *val; + struct ctrl_cmd *cmd; + + cmd = talloc_zero(ctx, struct ctrl_cmd); + if (!cmd) { + LOGP(DCTRL, LOGL_ERROR, "Failed to allocate.\n"); + return NULL; + } + + /* Make sure input is NULL terminated */ + msgb_put_u8(msg, 0); + str = (char *) msg->l2h; + + tmp = strtok_r(str, " ", &saveptr); + if (!tmp) { + cmd->type = CTRL_TYPE_ERROR; + cmd->id = "err"; + cmd->reply = "Request malformed"; + goto err; + } + + cmd->type = ctrl_cmd_str2type(tmp); + if (cmd->type == CTRL_TYPE_UNKNOWN) { + cmd->type = CTRL_TYPE_ERROR; + cmd->id = "err"; + cmd->reply = "Request type unknown"; + goto err; + } + + tmp = strtok_r(NULL, " ", &saveptr); + + if (!tmp) { + cmd->type = CTRL_TYPE_ERROR; + cmd->id = "err"; + cmd->reply = "Missing ID"; + goto err; + } + cmd->id = talloc_strdup(cmd, tmp); + if (!cmd->id) + goto oom; + + switch (cmd->type) { + case CTRL_TYPE_GET: + var = strtok_r(NULL, " ", &saveptr); + if (!var) { + cmd->type = CTRL_TYPE_ERROR; + cmd->reply = "GET incomplete"; + LOGP(DCTRL, LOGL_NOTICE, "GET Command incomplete\n"); + goto err; + } + cmd->variable = talloc_strdup(cmd, var); + LOGP(DCTRL, LOGL_DEBUG, "Command: GET %s\n", cmd->variable); + break; + case CTRL_TYPE_SET: + var = strtok_r(NULL, " ", &saveptr); + val = strtok_r(NULL, " ", &saveptr); + if (!var || !val) { + cmd->type = CTRL_TYPE_ERROR; + cmd->reply = "SET incomplete"; + LOGP(DCTRL, LOGL_NOTICE, "SET Command incomplete\n"); + goto err; + } + cmd->variable = talloc_strdup(cmd, var); + cmd->value = talloc_strdup(cmd, val); + if (!cmd->variable || !cmd->value) + goto oom; + LOGP(DCTRL, LOGL_DEBUG, "Command: SET %s = %s\n", cmd->variable, cmd->value); + break; + case CTRL_TYPE_GET_REPLY: + case CTRL_TYPE_SET_REPLY: + case CTRL_TYPE_TRAP: + var = strtok_r(NULL, " ", &saveptr); + val = strtok_r(NULL, " ", &saveptr); + if (!var || !val) { + cmd->type = CTRL_TYPE_ERROR; + cmd->reply = "Trap/Reply incomplete"; + LOGP(DCTRL, LOGL_NOTICE, "Trap/Reply incomplete\n"); + goto err; + } + cmd->variable = talloc_strdup(cmd, var); + cmd->reply = talloc_strdup(cmd, val); + if (!cmd->variable || !cmd->reply) + goto oom; + LOGP(DCTRL, LOGL_DEBUG, "Command: TRAP/REPLY %s: %s\n", cmd->variable, cmd->reply); + break; + case CTRL_TYPE_ERROR: + var = strtok_r(NULL, "\0", &saveptr); + if (!var) { + cmd->reply = ""; + goto err; + } + cmd->reply = talloc_strdup(cmd, var); + if (!cmd->reply) + goto oom; + LOGP(DCTRL, LOGL_DEBUG, "Command: ERROR %s\n", cmd->reply); + break; + case CTRL_TYPE_UNKNOWN: + default: + cmd->type = CTRL_TYPE_ERROR; + cmd->reply = "Unknown type"; + goto err; + } + + return cmd; +oom: + cmd->type = CTRL_TYPE_ERROR; + cmd->id = "err"; + cmd->reply = "OOM"; +err: + talloc_free(cmd); + return NULL; +} + +struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd) +{ + struct msgb *msg; + char *type, *tmp; + + if (!cmd->id) + return NULL; + + msg = msgb_alloc_headroom(4096, 128, "ctrl command make"); + if (!msg) + return NULL; + + type = ctrl_cmd_type2str(cmd->type); + + switch (cmd->type) { + case CTRL_TYPE_GET: + if (!cmd->variable) + goto err; + + tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable); + if (!tmp) { + LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); + goto err; + } + + msg->l2h = msgb_put(msg, strlen(tmp)); + memcpy(msg->l2h, tmp, strlen(tmp)); + talloc_free(tmp); + break; + case CTRL_TYPE_SET: + if (!cmd->variable || !cmd->value) + goto err; + + tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable, + cmd->value); + if (!tmp) { + LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); + goto err; + } + + msg->l2h = msgb_put(msg, strlen(tmp)); + memcpy(msg->l2h, tmp, strlen(tmp)); + talloc_free(tmp); + break; + case CTRL_TYPE_GET_REPLY: + case CTRL_TYPE_SET_REPLY: + case CTRL_TYPE_TRAP: + if (!cmd->variable || !cmd->reply) + goto err; + + tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable, + cmd->reply); + if (!tmp) { + LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); + goto err; + } + + msg->l2h = msgb_put(msg, strlen(tmp)); + memcpy(msg->l2h, tmp, strlen(tmp)); + talloc_free(tmp); + break; + case CTRL_TYPE_ERROR: + if (!cmd->reply) + goto err; + + tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, + cmd->reply); + if (!tmp) { + LOGP(DCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); + goto err; + } + + msg->l2h = msgb_put(msg, strlen(tmp)); + memcpy(msg->l2h, tmp, strlen(tmp)); + talloc_free(tmp); + break; + default: + LOGP(DCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type); + goto err; + break; + } + + return msg; + +err: + msgb_free(msg); + return NULL; +} diff --git a/src/libctrl/control_if.c b/src/libctrl/control_if.c new file mode 100644 index 000000000..b31f34fc6 --- /dev/null +++ b/src/libctrl/control_if.c @@ -0,0 +1,707 @@ +/* SNMP-like status interface + * + * (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 +#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 + +vector ctrl_node_vec; + +/* Send command to all */ +int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd) +{ + struct ctrl_connection *ccon; + int ret = 0; + + llist_for_each_entry(ccon, &ctrl->ccon_list, list_entry) { + if (ccon == cmd->ccon) + continue; + if (ctrl_cmd_send(&ccon->write_queue, cmd)) + ret++; + } + return ret; +} + +int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd) +{ + int ret; + struct msgb *msg; + + msg = ctrl_cmd_make(cmd); + if (!msg) { + LOGP(DCTRL, LOGL_ERROR, "Could not generate msg\n"); + return -1; + } + + ipaccess_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL); + ipaccess_prepend_header(msg, IPAC_PROTO_OSMO); + + ret = osmo_wqueue_enqueue(queue, msg); + if (ret != 0) { + LOGP(DCTRL, LOGL_ERROR, "Failed to enqueue the command.\n"); + msgb_free(msg); + } + return ret; +} + +struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd) +{ + struct ctrl_cmd *trap; + + trap = ctrl_cmd_cpy(tall_bsc_ctx, cmd); + if (!trap) + return NULL; + + trap->ccon = cmd->ccon; + trap->type = CTRL_TYPE_TRAP; + return trap; +} + +static int get_num(vector vline, int i, long *num) +{ + char *token, *tmp; + + if (i >= vector_active(vline)) + return 0; + token = vector_slot(vline, i); + + errno = 0; + if (token[0] == '\0') + return 0; + + *num = strtol(token, &tmp, 10); + if (tmp[0] != '\0' || errno != 0) + return 0; + + return 1; +} + +int ctrl_cmd_handle(struct ctrl_cmd *cmd, void *data) +{ + char *token, *request; + long num; + int i, j, ret, node; + + struct gsm_network *net = data; + struct gsm_bts *bts = NULL; + struct gsm_bts_trx *trx = NULL; + struct gsm_bts_trx_ts *ts = NULL; + vector vline, cmdvec, cmds_vec; + + ret = CTRL_CMD_ERROR; + cmd->reply = "Someone forgot to fill in the reply."; + node = CTRL_NODE_ROOT; + cmd->node = net; + + request = talloc_strdup(tall_bsc_ctx, cmd->variable); + if (!request) + goto err; + + for (i=0;ireply = "cmd_make_strvec failed."; + goto err; + } + + for (i=0;inode = bts; + node = CTRL_NODE_BTS; + } else if (!strcmp(token, "trx")) { + if (!bts) + goto err_missing; + i++; + if (!get_num(vline, i, &num)) + goto err_index; + + trx = gsm_bts_trx_num(bts, num); + if (!trx) + goto err_missing; + cmd->node = trx; + node = CTRL_NODE_TRX; + } else if (!strcmp(token, "ts")) { + if (!trx) + goto err_missing; + i++; + if (!get_num(vline, i, &num)) + goto err_index; + + if ((num >= 0) && (num < TRX_NR_TS)) + ts = &trx->ts[num]; + if (!ts) + goto err_missing; + cmd->node = ts; + node = CTRL_NODE_TS; + } else { + /* If we're here the rest must be the command */ + cmdvec = vector_init(vector_active(vline)-i); + for (j=i; jreply = "Command not found."; + vector_free(cmdvec); + break; + } + + ret = ctrl_cmd_exec(cmdvec, cmd, cmds_vec, data); + + vector_free(cmdvec); + break; + } + + if (i+1 == vector_active(vline)) + cmd->reply = "Command not present."; + } + + cmd_free_strvec(vline); + +err: + if (ret == CTRL_CMD_ERROR) + cmd->type = CTRL_TYPE_ERROR; + return ret; + +err_missing: + cmd_free_strvec(vline); + cmd->type = CTRL_TYPE_ERROR; + cmd->reply = "Error while resolving object"; + return ret; +err_index: + cmd_free_strvec(vline); + cmd->type = CTRL_TYPE_ERROR; + cmd->reply = "Error while parsing the index."; + return ret; +} + +static void control_close_conn(struct ctrl_connection *ccon) +{ + osmo_wqueue_clear(&ccon->write_queue); + close(ccon->write_queue.bfd.fd); + osmo_fd_unregister(&ccon->write_queue.bfd); + llist_del(&ccon->list_entry); + if (ccon->closed_cb) + ccon->closed_cb(ccon); + talloc_free(ccon); +} + +static int handle_control_read(struct osmo_fd * bfd) +{ + int ret = -1; + struct osmo_wqueue *queue; + struct ctrl_connection *ccon; + struct ipaccess_head *iph; + struct ipaccess_head_ext *iph_ext; + struct msgb *msg = NULL; + struct ctrl_cmd *cmd; + struct ctrl_handle *ctrl = bfd->data; + + queue = container_of(bfd, struct osmo_wqueue, bfd); + ccon = container_of(queue, struct ctrl_connection, write_queue); + + ret = ipa_msg_recv(bfd->fd, &msg); + if (ret <= 0) { + if (ret == 0) + LOGP(DCTRL, LOGL_INFO, "The control connection was closed\n"); + else + LOGP(DCTRL, LOGL_ERROR, "Failed to parse ip access message: %d\n", ret); + + goto err; + } + + if (msg->len < sizeof(*iph) + sizeof(*iph_ext)) { + LOGP(DCTRL, LOGL_ERROR, "The message is too short.\n"); + goto err; + } + + iph = (struct ipaccess_head *) msg->data; + if (iph->proto != IPAC_PROTO_OSMO) { + LOGP(DCTRL, LOGL_ERROR, "Protocol mismatch. We got 0x%x\n", iph->proto); + goto err; + } + + iph_ext = (struct ipaccess_head_ext *) iph->data; + if (iph_ext->proto != IPAC_PROTO_EXT_CTRL) { + LOGP(DCTRL, LOGL_ERROR, "Extended protocol mismatch. We got 0x%x\n", iph_ext->proto); + goto err; + } + + msg->l2h = iph_ext->data; + + cmd = ctrl_cmd_parse(ccon, msg); + + if (cmd) { + cmd->ccon = ccon; + if (ctrl_cmd_handle(cmd, ctrl->gsmnet) != CTRL_CMD_HANDLED) { + ctrl_cmd_send(queue, cmd); + talloc_free(cmd); + } + } else { + cmd = talloc_zero(ccon, struct ctrl_cmd); + if (!cmd) + goto err; + LOGP(DCTRL, LOGL_ERROR, "Command parser error.\n"); + cmd->type = CTRL_TYPE_ERROR; + cmd->id = "err"; + cmd->reply = "Command parser error."; + ctrl_cmd_send(queue, cmd); + talloc_free(cmd); + } + + msgb_free(msg); + return 0; + +err: + control_close_conn(ccon); + msgb_free(msg); + return ret; +} + +static int control_write_cb(struct osmo_fd *bfd, struct msgb *msg) +{ + int rc; + + rc = write(bfd->fd, msg->data, msg->len); + if (rc != msg->len) + LOGP(DCTRL, LOGL_ERROR, "Failed to write message to the control connection.\n"); + + return rc; +} + +static struct ctrl_connection *ctrl_connection_alloc(void *ctx) +{ + struct ctrl_connection *ccon = talloc_zero(ctx, struct ctrl_connection); + if (!ccon) + return NULL; + + osmo_wqueue_init(&ccon->write_queue, 100); + /* Error handling here? */ + + INIT_LLIST_HEAD(&ccon->cmds); + return ccon; +} + +static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what) +{ + int ret, fd, on; + struct ctrl_handle *ctrl; + struct ctrl_connection *ccon; + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + + + if (!(what & BSC_FD_READ)) + return 0; + + fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (fd < 0) { + perror("accept"); + return fd; + } + LOGP(DCTRL, LOGL_INFO, "accept()ed new control connection from %s\n", + inet_ntoa(sa.sin_addr)); + + on = 1; + ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); + if (ret != 0) { + LOGP(DNAT, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); + close(fd); + return ret; + } + ccon = ctrl_connection_alloc(listen_bfd->data); + if (!ccon) { + LOGP(DCTRL, LOGL_ERROR, "Failed to allocate.\n"); + close(fd); + return -1; + } + + ctrl = listen_bfd->data; + ccon->write_queue.bfd.data = ctrl; + ccon->write_queue.bfd.fd = fd; + ccon->write_queue.bfd.when = BSC_FD_READ; + ccon->write_queue.read_cb = handle_control_read; + ccon->write_queue.write_cb = control_write_cb; + + ret = osmo_fd_register(&ccon->write_queue.bfd); + if (ret < 0) { + LOGP(DCTRL, LOGL_ERROR, "Could not register FD.\n"); + close(ccon->write_queue.bfd.fd); + talloc_free(ccon); + } + + llist_add(&ccon->list_entry, &ctrl->ccon_list); + + return ret; +} + +static uint64_t get_rate_ctr_value(const struct rate_ctr *ctr, int intv) +{ + if (intv >= RATE_CTR_INTV_NUM) + return 0; + + /* Absolute value */ + if (intv == -1) { + return ctr->current; + } else { + return ctr->intv[intv].rate; + } +} + +static char *get_all_rate_ctr_in_group(const struct rate_ctr_group *ctrg, int intv) +{ + int i; + char *counters = talloc_strdup(tall_bsc_ctx, ""); + if (!counters) + return NULL; + + for (i=0;idesc->num_ctr;i++) { + counters = talloc_asprintf_append(counters, "\n%s.%u.%s %"PRIu64, + ctrg->desc->group_name_prefix, ctrg->idx, + ctrg->desc->ctr_desc[i].name, + get_rate_ctr_value(&ctrg->ctr[i], intv)); + if (!counters) + return NULL; + } + return counters; +} + +static int get_rate_ctr_group(const char *ctr_group, int intv, struct ctrl_cmd *cmd) +{ + int i; + char *counters; + struct rate_ctr_group *ctrg; + + cmd->reply = talloc_asprintf(cmd, "All counters in group %s", ctr_group); + if (!cmd->reply) + goto oom; + + for (i=0;;i++) { + ctrg = rate_ctr_get_group_by_name_idx(ctr_group, i); + if (!ctrg) + break; + + counters = get_all_rate_ctr_in_group(ctrg, intv); + if (!counters) + goto oom; + + cmd->reply = talloc_asprintf_append(cmd->reply, "%s", counters); + talloc_free(counters); + if (!cmd->reply) + goto oom; + } + + /* We found no counter group by that name */ + if (i == 0) { + cmd->reply = talloc_asprintf(cmd, "No counter group with name %s.", ctr_group); + return CTRL_CMD_ERROR; + } + + return CTRL_CMD_REPLY; +oom: + cmd->reply = "OOM."; + return CTRL_CMD_ERROR; +} + +static int get_rate_ctr_group_idx(const struct rate_ctr_group *ctrg, int intv, struct ctrl_cmd *cmd) +{ + char *counters; + + counters = get_all_rate_ctr_in_group(ctrg, intv); + if (!counters) + goto oom; + + cmd->reply = talloc_asprintf(cmd, "All counters in %s.%u%s", + ctrg->desc->group_name_prefix, ctrg->idx, counters); + talloc_free(counters); + if (!cmd->reply) + goto oom; + + return CTRL_CMD_REPLY; +oom: + cmd->reply = "OOM."; + return CTRL_CMD_ERROR; +} + +/* rate_ctr */ +CTRL_CMD_DEFINE(rate_ctr, "rate_ctr *"); +static int get_rate_ctr(struct ctrl_cmd *cmd, void *data) +{ + int intv; + unsigned int idx; + char *ctr_group, *ctr_idx, *ctr_name, *tmp, *dup, *saveptr, *interval; + struct rate_ctr_group *ctrg; + const struct rate_ctr *ctr; + + dup = talloc_strdup(cmd, cmd->variable); + if (!dup) + goto oom; + + /* Skip over possible prefixes (net.) */ + tmp = strstr(dup, "rate_ctr"); + if (!tmp) { + talloc_free(dup); + cmd->reply = "rate_ctr not a token in rate_ctr command!"; + goto err; + } + + strtok_r(tmp, ".", &saveptr); + interval = strtok_r(NULL, ".", &saveptr); + if (!interval) { + talloc_free(dup); + cmd->reply = "Missing interval."; + goto err; + } + + if (!strcmp(interval, "abs")) { + intv = -1; + } else if (!strcmp(interval, "per_sec")) { + intv = RATE_CTR_INTV_SEC; + } else if (!strcmp(interval, "per_min")) { + intv = RATE_CTR_INTV_MIN; + } else if (!strcmp(interval, "per_hour")) { + intv = RATE_CTR_INTV_HOUR; + } else if (!strcmp(interval, "per_day")) { + intv = RATE_CTR_INTV_DAY; + } else { + talloc_free(dup); + cmd->reply = "Wrong interval."; + goto err; + } + + ctr_group = strtok_r(NULL, ".", &saveptr); + tmp = strtok_r(NULL, ".", &saveptr); + if (!ctr_group || !tmp) { + talloc_free(dup); + cmd->reply = "Counter group must be of form a.b"; + goto err; + } + ctr_group[strlen(ctr_group)] = '.'; + + ctr_idx = strtok_r(NULL, ".", &saveptr); + if (!ctr_idx) { + talloc_free(dup); + return get_rate_ctr_group(ctr_group, intv, cmd); + } + idx = atoi(ctr_idx); + + ctrg = rate_ctr_get_group_by_name_idx(ctr_group, idx); + if (!ctrg) { + talloc_free(dup); + cmd->reply = "Counter group not found."; + goto err; + } + + ctr_name = strtok_r(NULL, "\0", &saveptr); + if (!ctr_name) { + talloc_free(dup); + return get_rate_ctr_group_idx(ctrg, intv, cmd); + } + + ctr = rate_ctr_get_by_name(ctrg, ctr_name); + if (!ctr) { + cmd->reply = "Counter name not found."; + talloc_free(dup); + goto err; + } + + talloc_free(dup); + + cmd->reply = talloc_asprintf(cmd, "%"PRIu64, get_rate_ctr_value(ctr, intv)); + if (!cmd->reply) + goto oom; + + return CTRL_CMD_REPLY; +oom: + cmd->reply = "OOM"; +err: + return CTRL_CMD_ERROR; +} + +static int set_rate_ctr(struct ctrl_cmd *cmd, void *data) +{ + cmd->reply = "Can't set rate counter."; + + return CTRL_CMD_ERROR; +} + +static int verify_rate_ctr(struct ctrl_cmd *cmd, const char *value, void *data) +{ + return 0; +} + +/* counter */ +CTRL_CMD_DEFINE(counter, "counter *"); +static int get_counter(struct ctrl_cmd *cmd, void *data) +{ + char *ctr_name, *tmp, *dup, *saveptr; + struct osmo_counter *counter; + + cmd->reply = "OOM"; + dup = talloc_strdup(cmd, cmd->variable); + if (!dup) + goto err; + + + tmp = strstr(dup, "counter"); + if (!tmp) { + talloc_free(dup); + goto err; + } + + strtok_r(tmp, ".", &saveptr); + ctr_name = strtok_r(NULL, "\0", &saveptr); + + if (!ctr_name) + goto err; + + counter = osmo_counter_get_by_name(ctr_name); + if (!counter) { + cmd->reply = "Counter name not found."; + talloc_free(dup); + goto err; + } + + talloc_free(dup); + + cmd->reply = talloc_asprintf(cmd, "%lu", counter->value); + if (!cmd->reply) { + cmd->reply = "OOM"; + goto err; + } + + return CTRL_CMD_REPLY; +err: + return CTRL_CMD_ERROR; +} + +static int set_counter(struct ctrl_cmd *cmd, void *data) +{ + + cmd->reply = "Can't set counter."; + + return CTRL_CMD_ERROR; +} + +static int verify_counter(struct ctrl_cmd *cmd, const char *value, void *data) +{ + return 0; +} + +struct ctrl_handle *controlif_setup(struct gsm_network *gsmnet, uint16_t port) +{ + int ret; + struct ctrl_handle *ctrl; + + ctrl = talloc_zero(tall_bsc_ctx, struct ctrl_handle); + if (!ctrl) + return NULL; + + INIT_LLIST_HEAD(&ctrl->ccon_list); + + ctrl->gsmnet = gsmnet; + + ctrl_node_vec = vector_init(5); + if (!ctrl_node_vec) + goto err; + + /* Listen for control connections */ + ret = make_sock(&ctrl->listen_fd, IPPROTO_TCP, INADDR_LOOPBACK, port, + 0, listen_fd_cb, ctrl); + if (ret < 0) + goto err_vec; + + ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rate_ctr); + if (ret) + goto err_vec; + ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_counter); + if (ret) + goto err_vec; + + return ctrl; +err_vec: + vector_free(ctrl_node_vec); + ctrl_node_vec = NULL; +err: + talloc_free(ctrl); + return NULL; +} diff --git a/src/libgb/Makefile.am b/src/libgb/Makefile.am new file mode 100644 index 000000000..159243111 --- /dev/null +++ b/src/libgb/Makefile.am @@ -0,0 +1,9 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) + +noinst_LIBRARIES = libgb.a + +libgb_a_SOURCES = gprs_ns.c gprs_ns_frgre.c gprs_ns_vty.c \ + gprs_bssgp.c gprs_bssgp_util.c gprs_bssgp_vty.c \ + gprs_bssgp_bss.c common_vty.c diff --git a/src/libmgcp/Makefile.am b/src/libmgcp/Makefile.am new file mode 100644 index 000000000..72f625dd4 --- /dev/null +++ b/src/libmgcp/Makefile.am @@ -0,0 +1,7 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) + +noinst_LIBRARIES = libmgcp.a + +libmgcp_a_SOURCES = mgcp_protocol.c mgcp_network.c mgcp_vty.c diff --git a/src/libmgcp/mgcp_network.c b/src/libmgcp/mgcp_network.c new file mode 100644 index 000000000..e9b58b24e --- /dev/null +++ b/src/libmgcp/mgcp_network.c @@ -0,0 +1,706 @@ +/* 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 + +#warning "Make use of the rtp proxy code" + +/* attempt to determine byte order */ +#include +#include +#include + +#ifndef __BYTE_ORDER +# ifdef __APPLE__ +# define __BYTE_ORDER __DARWIN_BYTE_ORDER +# define __LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +# define __BIG_ENDIAN __DARWIN_BIG_ENDIAN +# else +# error "__BYTE_ORDER should be defined by someone" +# endif +#endif + +/* according to rtp_proxy.c RFC 3550 */ +struct rtp_hdr { +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t csrc_count:4, + extension:1, + padding:1, + version:2; + uint8_t payload_type:7, + marker:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + uint8_t version:2, + padding:1, + extension:1, + csrc_count:4; + uint8_t marker:1, + payload_type:7; +#endif + uint16_t sequence; + uint32_t timestamp; + uint32_t ssrc; +} __attribute__((packed)); + +#define RTP_SEQ_MOD (1 << 16) +#define RTP_MAX_DROPOUT 3000 +#define RTP_MAX_MISORDER 100 + + +enum { + DEST_NETWORK = 0, + DEST_BTS = 1, +}; + +enum { + PROTO_RTP, + PROTO_RTCP, +}; + +#define DUMMY_LOAD 0x23 + + +/** + * This does not need to be a precision timestamp and + * is allowed to wrap quite fast. The returned value is + * milli seconds now. + */ +uint32_t get_current_ts(void) +{ + struct timespec tp; + uint64_t ret; + + memset(&tp, 0, sizeof(tp)); + if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) + LOGP(DMGCP, LOGL_NOTICE, + "Getting the clock failed.\n"); + + /* convert it to useconds */ + ret = tp.tv_sec; + ret *= 1000; + ret += tp.tv_nsec / 1000 / 1000; + + return ret; +} + +static int udp_send(int fd, struct in_addr *addr, int port, char *buf, int len) +{ + struct sockaddr_in out; + out.sin_family = AF_INET; + out.sin_port = port; + memcpy(&out.sin_addr, addr, sizeof(*addr)); + + return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out)); +} + +int mgcp_send_dummy(struct mgcp_endpoint *endp) +{ + static char buf[] = { DUMMY_LOAD }; + + return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr, + endp->net_end.rtp_port, buf, 1); +} + +/** + * 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 packages to appear as if there + * is only one source. + * There is also no probation period for new sources. Every package + * we receive will be seen as a switch in streams. + */ +static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, + int payload, struct sockaddr_in *addr, char *data, int len) +{ + uint32_t arrival_time; + int32_t transit, d; + uint16_t seq, udelta; + uint32_t timestamp; + struct rtp_hdr *rtp_hdr; + + if (len < sizeof(*rtp_hdr)) + return; + + rtp_hdr = (struct rtp_hdr *) data; + seq = ntohs(rtp_hdr->sequence); + timestamp = ntohl(rtp_hdr->timestamp); + arrival_time = get_current_ts(); + + if (!state->initialized) { + state->base_seq = seq; + state->max_seq = seq - 1; + state->ssrc = state->orig_ssrc = rtp_hdr->ssrc; + state->initialized = 1; + state->last_timestamp = timestamp; + state->jitter = 0; + state->transit = arrival_time - timestamp; + } else if (state->ssrc != rtp_hdr->ssrc) { + state->ssrc = rtp_hdr->ssrc; + state->seq_offset = (state->max_seq + 1) - seq; + state->timestamp_offset = state->last_timestamp - timestamp; + state->patch = endp->allow_patch; + LOGP(DMGCP, LOGL_NOTICE, + "The SSRC changed on 0x%x SSRC: %u offset: %d from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->ssrc, state->seq_offset, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); + } + + /* apply the offset and store it back to the packet */ + if (state->patch) { + seq += state->seq_offset; + rtp_hdr->sequence = htons(seq); + rtp_hdr->ssrc = state->orig_ssrc; + + timestamp += state->timestamp_offset; + rtp_hdr->timestamp = htonl(timestamp); + } + + /* + * The below takes the shape of the validation from Appendix A. Check + * if there is something weird with the sequence number, otherwise check + * for a wrap around in the sequence number. + */ + udelta = seq - state->max_seq; + if (udelta < RTP_MAX_DROPOUT) { + if (seq < state->max_seq) + state->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. The local timestamp has a usec resolution. + */ + transit = arrival_time - timestamp; + d = transit - state->transit; + state->transit = transit; + if (d < 0) + d = -d; + state->jitter += d - ((state->jitter + 8) >> 4); + + + state->max_seq = seq; + state->last_timestamp = timestamp; + + if (payload < 0) + return; + + rtp_hdr->payload_type = payload; +} + +/* + * The below code is for dispatching. We have a dedicated port for + * the data coming from the net and one to discover the BTS. + */ +static int forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf, int len) +{ + if (!tap->enabled) + return 0; + + return sendto(fd, buf, len, 0, + (struct sockaddr *)&tap->forward, sizeof(tap->forward)); +} + +static int send_transcoder(struct mgcp_rtp_end *end, struct mgcp_config *cfg, + int is_rtp, const char *buf, int len) +{ + int rc; + int port; + struct sockaddr_in addr; + + port = is_rtp ? end->rtp_port : end->rtcp_port; + + addr.sin_family = AF_INET; + addr.sin_addr = cfg->transcoder_in; + addr.sin_port = port; + + rc = sendto(is_rtp ? + end->rtp.fd : + end->rtcp.fd, buf, len, 0, + (struct sockaddr *) &addr, sizeof(addr)); + + if (rc != len) + LOGP(DMGCP, LOGL_ERROR, + "Failed to send data to the transcoder: %s\n", + strerror(errno)); + + return rc; +} + +static int send_to(struct mgcp_endpoint *endp, int dest, int is_rtp, + struct sockaddr_in *addr, char *buf, int rc) +{ + struct mgcp_trunk_config *tcfg = endp->tcfg; + /* For loop toggle the destination and then dispatch. */ + if (tcfg->audio_loop) + dest = !dest; + + /* Loop based on the conn_mode, maybe undoing the above */ + if (endp->conn_mode == MGCP_CONN_LOOPBACK) + dest = !dest; + + if (dest == DEST_NETWORK) { + if (is_rtp) { + patch_and_count(endp, &endp->bts_state, + endp->net_end.payload_type, + addr, buf, rc); + forward_data(endp->net_end.rtp.fd, + &endp->taps[MGCP_TAP_NET_OUT], buf, rc); + return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr, + endp->net_end.rtp_port, buf, rc); + } else if (!tcfg->omit_rtcp) { + return udp_send(endp->net_end.rtcp.fd, &endp->net_end.addr, + endp->net_end.rtcp_port, buf, rc); + } + } else { + if (is_rtp) { + patch_and_count(endp, &endp->net_state, + endp->bts_end.payload_type, + addr, buf, rc); + forward_data(endp->bts_end.rtp.fd, + &endp->taps[MGCP_TAP_BTS_OUT], buf, rc); + return udp_send(endp->bts_end.rtp.fd, &endp->bts_end.addr, + endp->bts_end.rtp_port, buf, rc); + } else if (!tcfg->omit_rtcp) { + return udp_send(endp->bts_end.rtcp.fd, &endp->bts_end.addr, + endp->bts_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[4096]; + 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; + } + + if (endp->net_end.rtp_port != addr.sin_port && + endp->net_end.rtcp_port != addr.sin_port) { + LOGP(DMGCP, LOGL_ERROR, + "Data from wrong source port %d on 0x%x\n", + ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); + return -1; + } + + /* throw away the dummy message */ + if (rc == 1 && buf[0] == DUMMY_LOAD) { + LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from network on 0x%x\n", + ENDPOINT_NUMBER(endp)); + return 0; + } + + proto = fd == &endp->net_end.rtp ? PROTO_RTP : PROTO_RTCP; + endp->net_end.packets += 1; + 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 send_to(endp, DEST_BTS, proto == PROTO_RTP, &addr, + buf, rc); + case MGCP_RTP_TRANSCODED: + return send_transcoder(&endp->trans_net, endp->cfg, + proto == PROTO_RTP, buf, rc); + } + + 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 == PROTO_RTP && endp->bts_end.rtp_port == 0) { + if (!cfg->bts_ip || + memcmp(&addr->sin_addr, + &cfg->bts_in, sizeof(cfg->bts_in)) == 0 || + memcmp(&addr->sin_addr, + &endp->bts_end.addr, sizeof(endp->bts_end.addr)) == 0) { + + endp->bts_end.rtp_port = addr->sin_port; + endp->bts_end.addr = addr->sin_addr; + + LOGP(DMGCP, LOGL_NOTICE, + "Found BTS for endpoint: 0x%x on port: %d/%d of %s\n", + ENDPOINT_NUMBER(endp), ntohs(endp->bts_end.rtp_port), + ntohs(endp->bts_end.rtcp_port), inet_ntoa(addr->sin_addr)); + } + } else if (proto == PROTO_RTCP && endp->bts_end.rtcp_port == 0) { + if (memcmp(&endp->bts_end.addr, &addr->sin_addr, + sizeof(endp->bts_end.addr)) == 0) { + endp->bts_end.rtcp_port = addr->sin_port; + } + } +} + +static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) +{ + char buf[4096]; + struct sockaddr_in addr; + struct mgcp_endpoint *endp; + int rc, proto; + + endp = (struct mgcp_endpoint *) fd->data; + + rc = receive_from(endp, fd->fd, &addr, buf, sizeof(buf)); + if (rc <= 0) + return -1; + + proto = fd == &endp->bts_end.rtp ? PROTO_RTP : PROTO_RTCP; + + /* We have no idea who called us, maybe it is the BTS. */ + /* it was the BTS... */ + discover_bts(endp, proto, &addr); + + if (memcmp(&endp->bts_end.addr, &addr.sin_addr, sizeof(addr.sin_addr)) != 0) { + LOGP(DMGCP, LOGL_ERROR, + "Data from wrong bts %s on 0x%x\n", + inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp)); + return -1; + } + + if (endp->bts_end.rtp_port != addr.sin_port && + endp->bts_end.rtcp_port != addr.sin_port) { + LOGP(DMGCP, LOGL_ERROR, + "Data from wrong bts source port %d on 0x%x\n", + ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); + return -1; + } + + /* throw away the dummy message */ + if (rc == 1 && buf[0] == DUMMY_LOAD) { + LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from bts on 0x%x\n", + ENDPOINT_NUMBER(endp)); + return 0; + } + + /* do this before the loop handling */ + endp->bts_end.packets += 1; + 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 send_to(endp, DEST_NETWORK, proto == PROTO_RTP, &addr, + buf, rc); + case MGCP_RTP_TRANSCODED: + return send_transcoder(&endp->trans_bts, endp->cfg, + proto == PROTO_RTP, buf, rc); + } + + 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[4096]; + 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 ? PROTO_RTP : PROTO_RTCP; + + if (memcmp(&addr.sin_addr, &cfg->transcoder_in, sizeof(addr.sin_addr)) != 0) { + LOGP(DMGCP, LOGL_ERROR, + "Data not coming from transcoder dest: %d %s on 0x%x\n", + dest, inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(_endp)); + return -1; + } + + if (end->rtp_port != addr.sin_port && + end->rtcp_port != addr.sin_port) { + LOGP(DMGCP, LOGL_ERROR, + "Data from wrong transcoder dest %d source port %d on 0x%x\n", + dest, ntohs(addr.sin_port), ENDPOINT_NUMBER(_endp)); + return -1; + } + + /* throw away the dummy message */ + if (rc == 1 && buf[0] == DUMMY_LOAD) { + LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from transcoder dest %d on 0x%x\n", + dest, ENDPOINT_NUMBER(_endp)); + return 0; + } + + end->packets += 1; + return send_to(_endp, dest, proto == PROTO_RTP, &addr, &buf[0], rc); +} + +static int rtp_data_trans_net(struct osmo_fd *fd, unsigned int what) +{ + struct mgcp_endpoint *endp; + endp = (struct mgcp_endpoint *) fd->data; + + return rtp_data_transcoder(&endp->trans_net, endp, DEST_NETWORK, fd); +} + +static int rtp_data_trans_bts(struct osmo_fd *fd, unsigned int what) +{ + struct mgcp_endpoint *endp; + endp = (struct mgcp_endpoint *) fd->data; + + return rtp_data_transcoder(&endp->trans_bts, endp, DEST_BTS, fd); +} + +static int create_bind(const char *source_addr, struct 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; +} + +static int set_ip_tos(int fd, int tos) +{ + int ret; + ret = setsockopt(fd, IPPROTO_IP, IP_TOS, + &tos, sizeof(tos)); + return ret != 0; +} + +static int bind_rtp(struct mgcp_config *cfg, struct mgcp_rtp_end *rtp_end, int endpno) +{ + if (create_bind(cfg->source_addr, &rtp_end->rtp, rtp_end->local_port) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to create RTP port: %s:%d on 0x%x\n", + cfg->source_addr, rtp_end->local_port, endpno); + goto cleanup0; + } + + if (create_bind(cfg->source_addr, &rtp_end->rtcp, rtp_end->local_port + 1) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to create RTCP port: %s:%d on 0x%x\n", + cfg->source_addr, rtp_end->local_port + 1, endpno); + goto cleanup1; + } + + set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp); + set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp); + + rtp_end->rtp.when = BSC_FD_READ; + if (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, int rtp_port) +{ + if (end->rtp.fd != -1 || end->rtcp.fd != -1) { + LOGP(DMGCP, LOGL_ERROR, "Previous %s was still bound on %d\n", + port, ENDPOINT_NUMBER(_endp)); + mgcp_free_rtp_port(end); + } + + end->local_port = rtp_port; + end->rtp.cb = cb; + end->rtp.data = _endp; + end->rtcp.data = _endp; + end->rtcp.cb = cb; + return bind_rtp(_endp->cfg, end, ENDPOINT_NUMBER(_endp)); +} + + +int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port) +{ + return int_bind("bts-port", &endp->bts_end, + rtp_data_bts, endp, rtp_port); +} + +int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port) +{ + return int_bind("net-port", &endp->net_end, + rtp_data_net, endp, rtp_port); +} + +int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port) +{ + return int_bind("trans-net", &endp->trans_net, + rtp_data_trans_net, endp, rtp_port); +} + +int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port) +{ + return int_bind("trans-bts", &endp->trans_bts, + rtp_data_trans_bts, endp, rtp_port); +} + +int mgcp_free_rtp_port(struct mgcp_rtp_end *end) +{ + if (end->rtp.fd != -1) { + close(end->rtp.fd); + end->rtp.fd = -1; + 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->cycles + state->max_seq; + *expected = *expected - state->base_seq + 1; + + if (!state->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->initialized) + return 0; + return state->jitter >> 4; +} diff --git a/src/libmgcp/mgcp_protocol.c b/src/libmgcp/mgcp_protocol.c new file mode 100644 index 000000000..616e0a94c --- /dev/null +++ b/src/libmgcp/mgcp_protocol.c @@ -0,0 +1,1203 @@ +/* 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_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_parse_data { + struct mgcp_config *cfg; + struct mgcp_endpoint *endp; + char *trans; + char *save; + int found; +}; + +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 mgcp_analyze_header(struct mgcp_parse_data *parse, char *data); + +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 struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, + const char *msg, const char *trans_id) +{ + const char *addr = endp->cfg->local_ip; + const char *fmtp_extra = endp->bts_end.fmtp_extra; + char sdp_record[4096]; + + if (!addr) + addr = endp->cfg->source_addr; + + snprintf(sdp_record, sizeof(sdp_record) - 1, + "I: %u\n\n" + "v=0\r\n" + "o=- %u 23 IN IP4 %s\r\n" + "c=IN IP4 %s\r\n" + "t=0 0\r\n" + "m=audio %d RTP/AVP %d\r\n" + "a=rtpmap:%d %s\r\n" + "%s%s", + endp->ci, endp->ci, addr, addr, + endp->net_end.local_port, endp->bts_end.payload_type, + endp->bts_end.payload_type, endp->tcfg->audio_name, + fmtp_extra ? fmtp_extra : "", fmtp_extra ? "\r\n" : ""); + return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record); +} + +/* + * handle incoming messages: + * - this can be a command (four letters, space, transaction id) + * - or a response (three numbers, space, transaction id) + */ +struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) +{ + struct mgcp_parse_data pdata; + int i, code, handled = 0; + struct msgb *resp = NULL; + char *data; + + if (msgb_l2len(msg) < 4) { + LOGP(DMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len); + return NULL; + } + + /* attempt to treat it as a response */ + if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) { + LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code); + return NULL; + } + + msg->l3h = &msg->l2h[4]; + + + /* + * Check for a duplicate message and respond. + * FIXME: Verify that the msg->l3h is NULL terminated. + */ + memset(&pdata, 0, sizeof(pdata)); + pdata.cfg = cfg; + data = strtok_r((char *) msg->l3h, "\r\n", &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); + } else { + gw = strtoul(mgcp, &endptr, 16); + if (gw > 0 && gw < cfg->trunk.number_endpoints && strcmp(endptr, "@mgw") == 0) + return &cfg->trunk.endpoints[gw]; + } + + LOGP(DMGCP, LOGL_ERROR, "Not able to find endpoint: '%s'\n", mgcp); + return NULL; +} + +/** + * @returns 0 when the status line was complete and transaction_id and + * endp out parameters are set. + */ +static int mgcp_analyze_header(struct mgcp_parse_data *pdata, char *data) +{ + int i = 0; + char *elem, *save = NULL; + + pdata->trans = "000000"; + + for (elem = strtok_r(data, " ", &save); elem; + elem = strtok_r(NULL, " ", &save)) { + switch (i) { + case 0: + pdata->trans = elem; + break; + case 1: + pdata->endp = find_endpoint(pdata->cfg, elem); + if (!pdata->endp) { + LOGP(DMGCP, LOGL_ERROR, + "Unable to find Endpoint `%s'\n", elem); + return -1; + } + break; + case 2: + if (strcmp("MGCP", elem)) { + LOGP(DMGCP, LOGL_ERROR, + "MGCP header parsing error\n"); + return -1; + } + break; + case 3: + if (strcmp("1.0", elem)) { + LOGP(DMGCP, LOGL_ERROR, "MGCP version `%s' " + "not supported\n", elem); + return -1; + } + break; + } + i++; + } + + if (i != 4) { + LOGP(DMGCP, LOGL_ERROR, "MGCP status line too short.\n"); + pdata->trans = "000000"; + pdata->endp = NULL; + return -1; + } + + return 0; +} + +static int verify_call_id(const struct mgcp_endpoint *endp, + const char *callid) +{ + if (strcmp(endp->callid, callid) != 0) { + LOGP(DMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n", + ENDPOINT_NUMBER(endp), endp->callid, callid); + return -1; + } + + return 0; +} + +static int verify_ci(const struct mgcp_endpoint *endp, + const char *_ci) +{ + uint32_t ci = strtoul(_ci, NULL, 10); + + if (ci != endp->ci) { + LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %u != %s\n", + ENDPOINT_NUMBER(endp), endp->ci, _ci); + return -1; + } + + return 0; +} + +static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p) +{ + if (p->found != 0) + return create_err_response(NULL, 500, "AUEP", p->trans); + else + return create_ok_response(p->endp, 200, "AUEP", p->trans); +} + +static int parse_conn_mode(const char *msg, int *conn_mode) +{ + int ret = 0; + if (strcmp(msg, "recvonly") == 0) + *conn_mode = MGCP_CONN_RECV_ONLY; + else if (strcmp(msg, "sendrecv") == 0) + *conn_mode = MGCP_CONN_RECV_SEND; + else if (strcmp(msg, "sendonly") == 0) + *conn_mode = MGCP_CONN_SEND_ONLY; + else if (strcmp(msg, "loopback") == 0) + *conn_mode = MGCP_CONN_LOOPBACK; + else { + LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg); + ret = -1; + } + + return ret; +} + +static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end, + struct mgcp_port_range *range, + int (*alloc)(struct mgcp_endpoint *endp, int port)) +{ + int i; + + if (range->mode == PORT_ALLOC_STATIC) { + end->local_alloc = PORT_ALLOC_STATIC; + return 0; + } + + /* attempt to find a port */ + for (i = 0; i < 200; ++i) { + int rc; + + if (range->last_port >= range->range_end) + range->last_port = range->range_start; + + rc = alloc(endp, range->last_port); + + range->last_port += 2; + if (rc == 0) { + end->local_alloc = PORT_ALLOC_DYNAMIC; + return 0; + } + + } + + LOGP(DMGCP, LOGL_ERROR, "Allocating a RTP/RTCP port failed 200 times 0x%x.\n", + ENDPOINT_NUMBER(endp)); + return -1; +} + +static int allocate_ports(struct mgcp_endpoint *endp) +{ + if (allocate_port(endp, &endp->net_end, &endp->cfg->net_ports, + mgcp_bind_net_rtp_port) != 0) + return -1; + + if (allocate_port(endp, &endp->bts_end, &endp->cfg->bts_ports, + mgcp_bind_bts_rtp_port) != 0) { + mgcp_rtp_end_reset(&endp->net_end); + return -1; + } + + if (endp->cfg->transcoder_ip && endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) { + if (allocate_port(endp, &endp->trans_net, + &endp->cfg->transcoder_ports, + mgcp_bind_trans_net_rtp_port) != 0) { + mgcp_rtp_end_reset(&endp->net_end); + mgcp_rtp_end_reset(&endp->bts_end); + return -1; + } + + if (allocate_port(endp, &endp->trans_bts, + &endp->cfg->transcoder_ports, + mgcp_bind_trans_bts_rtp_port) != 0) { + mgcp_rtp_end_reset(&endp->net_end); + mgcp_rtp_end_reset(&endp->bts_end); + mgcp_rtp_end_reset(&endp->trans_net); + return -1; + } + + /* remember that we have set up transcoding */ + endp->type = MGCP_RTP_TRANSCODED; + } + + return 0; +} + +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; + + 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) { + 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; + default: + LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", + *line, *line, ENDPOINT_NUMBER(endp)); + break; + } + } + + 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_free_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); + + if (local_options) + endp->local_options = talloc_strdup(tcfg->endpoints, local_options); + + if (parse_conn_mode(mode, &endp->conn_mode) != 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; + + /* 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; + + endp->allocated = 1; + endp->bts_end.payload_type = tcfg->audio_payload; + endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints, + tcfg->audio_fmtp_extra); + + /* 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_free_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); + + create_transcoder(endp); + return create_response_with_sdp(endp, "CRCX", p->trans); +error2: + mgcp_free_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; + char *line; + + 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) { + 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': + /* skip */ + break; + case 'M': + if (parse_conn_mode(line + 3, &endp->conn_mode) != 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 */ + break; + case 'a': + case 'o': + case 's': + case 't': + case 'v': + /* skip these SDP attributes */ + break; + case 'm': { + int port; + int payload; + + if (sscanf(line, "m=audio %d RTP/AVP %d", &port, &payload) == 2) { + endp->net_end.rtp_port = htons(port); + endp->net_end.rtcp_port = htons(port + 1); + endp->net_end.payload_type = payload; + } + break; + } + case 'c': { + char ipv4[16]; + + if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) { + inet_aton(ipv4, &endp->net_end.addr); + } + break; + } + default: + LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", + 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_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; + } + } + + /* 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 (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) { + 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_free_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); +} + +struct mgcp_config *mgcp_config_alloc(void) +{ + struct mgcp_config *cfg; + + cfg = talloc_zero(NULL, struct mgcp_config); + if (!cfg) { + LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n"); + return NULL; + } + + cfg->source_port = 2427; + cfg->source_addr = talloc_strdup(cfg, "0.0.0.0"); + + cfg->transcoder_remote_base = 4000; + + cfg->bts_ports.base_port = RTP_PORT_DEFAULT; + cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT; + + /* default trunk handling */ + cfg->trunk.cfg = cfg; + cfg->trunk.trunk_nr = 0; + cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL; + cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000"); + cfg->trunk.audio_payload = 126; + cfg->trunk.omit_rtcp = 0; + + INIT_LLIST_HEAD(&cfg->trunks); + + return cfg; +} + +struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr) +{ + struct mgcp_trunk_config *trunk; + + trunk = talloc_zero(cfg, struct mgcp_trunk_config); + if (!trunk) { + LOGP(DMGCP, LOGL_ERROR, "Failed to allocate.\n"); + return NULL; + } + + trunk->cfg = cfg; + trunk->trunk_type = MGCP_TRUNK_E1; + trunk->trunk_nr = nr; + trunk->audio_name = talloc_strdup(cfg, "AMR/8000"); + trunk->audio_payload = 126; + trunk->number_endpoints = 33; + trunk->omit_rtcp = 0; + llist_add_tail(&trunk->entry, &cfg->trunks); + return trunk; +} + +struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index) +{ + struct mgcp_trunk_config *trunk; + + llist_for_each_entry(trunk, &cfg->trunks, entry) + if (trunk->trunk_nr == index) + return trunk; + + return NULL; +} + +static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end) +{ + if (end->local_alloc == PORT_ALLOC_DYNAMIC) { + mgcp_free_rtp_port(end); + end->local_port = 0; + } + + end->packets = 0; + end->octets = 0; + memset(&end->addr, 0, sizeof(end->addr)); + end->rtp_port = end->rtcp_port = 0; + end->payload_type = -1; + end->local_alloc = -1; + talloc_free(end->fmtp_extra); + end->fmtp_extra = NULL; +} + +static void mgcp_rtp_end_init(struct mgcp_rtp_end *end) +{ + mgcp_rtp_end_reset(end); + end->rtp.fd = -1; + end->rtcp.fd = -1; +} + +int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg) +{ + int i; + + /* Initialize all endpoints */ + tcfg->endpoints = _talloc_zero_array(tcfg->cfg, + sizeof(struct mgcp_endpoint), + tcfg->number_endpoints, "endpoints"); + if (!tcfg->endpoints) + return -1; + + for (i = 0; i < tcfg->number_endpoints; ++i) { + tcfg->endpoints[i].ci = CI_UNUSED; + tcfg->endpoints[i].cfg = tcfg->cfg; + tcfg->endpoints[i].tcfg = tcfg; + mgcp_rtp_end_init(&tcfg->endpoints[i].net_end); + mgcp_rtp_end_init(&tcfg->endpoints[i].bts_end); + mgcp_rtp_end_init(&tcfg->endpoints[i].trans_net); + mgcp_rtp_end_init(&tcfg->endpoints[i].trans_bts); + } + + return 0; +} + +void mgcp_free_endp(struct mgcp_endpoint *endp) +{ + LOGP(DMGCP, LOGL_DEBUG, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp)); + endp->ci = CI_UNUSED; + endp->allocated = 0; + + talloc_free(endp->callid); + endp->callid = NULL; + + talloc_free(endp->local_options); + endp->local_options = NULL; + + mgcp_rtp_end_reset(&endp->bts_end); + mgcp_rtp_end_reset(&endp->net_end); + mgcp_rtp_end_reset(&endp->trans_net); + mgcp_rtp_end_reset(&endp->trans_bts); + endp->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; + endp->allow_patch = 0; + + memset(&endp->taps, 0, sizeof(endp->taps)); +} + +static int send_trans(struct mgcp_config *cfg, const char *buf, int len) +{ + struct sockaddr_in addr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr = cfg->transcoder_in; + addr.sin_port = htons(2427); + return sendto(cfg->gw_fd.bfd.fd, buf, len, 0, + (struct sockaddr *) &addr, sizeof(addr)); +} + +static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, + const char *msg, const char *mode) +{ + char buf[2096]; + int len; + + /* hardcoded to AMR right now, we do not know the real type at this point */ + len = snprintf(buf, sizeof(buf), + "%s 42 %x@mgw MGCP 1.0\r\n" + "C: 4256\r\n" + "M: %s\r\n" + "\r\n" + "c=IN IP4 %s\r\n" + "m=audio %d RTP/AVP %d\r\n" + "a=rtpmap:%d %s\r\n", + msg, endpoint, mode, endp->cfg->source_addr, + port, endp->tcfg->audio_payload, + endp->tcfg->audio_payload, endp->tcfg->audio_name); + + if (len < 0) + return; + + buf[sizeof(buf) - 1] = '\0'; + + send_trans(endp->cfg, buf, len); +} + +static void send_dlcx(struct mgcp_endpoint *endp, int endpoint) +{ + char buf[2096]; + int len; + + len = snprintf(buf, sizeof(buf), + "DLCX 43 %x@mgw MGCP 1.0\r\n" + "C: 4256\r\n" + , endpoint); + + if (len < 0) + return; + + buf[sizeof(buf) - 1] = '\0'; + + send_trans(endp->cfg, buf, len); +} + +static 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 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; + mgcp_state_calc_loss(&endp->net_state, &endp->net_end, + &expected, &ploss); + jitter = mgcp_state_calc_jitter(&endp->net_state); + + 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); + 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_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/src/libmgcp/mgcp_vty.c b/src/libmgcp/mgcp_vty.c new file mode 100644 index 000000000..69984ee92 --- /dev/null +++ b/src/libmgcp/mgcp_vty.c @@ -0,0 +1,941 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* The protocol implementation */ + +/* + * (C) 2009-2011 by Holger Hans Peter Freyther + * (C) 2009-2011 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include + +#include +#include +#include + +#include + +#define RTCP_OMIT_STR "Drop RTCP packets in both directions\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->net_ports.mode == PORT_ALLOC_STATIC) + vty_out(vty, " rtp net-base %u%s", g_cfg->net_ports.base_port, VTY_NEWLINE); + else + vty_out(vty, " rtp net-range %u %u%s", + g_cfg->net_ports.range_start, g_cfg->net_ports.range_end, VTY_NEWLINE); + + vty_out(vty, " rtp ip-dscp %d%s", g_cfg->endp_dscp, VTY_NEWLINE); + if (g_cfg->trunk.omit_rtcp) + vty_out(vty, " rtcp-omit%s", VTY_NEWLINE); + else + vty_out(vty, " no rtcp-omit%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, " loop %u%s", !!g_cfg->trunk.audio_loop, VTY_NEWLINE); + vty_out(vty, " number endpoints %u%s", g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE); + if (g_cfg->call_agent_addr) + vty_out(vty, " call-agent ip %s%s", g_cfg->call_agent_addr, VTY_NEWLINE); + if (g_cfg->transcoder_ip) + vty_out(vty, " transcoder-mgw %s%s", g_cfg->transcoder_ip, VTY_NEWLINE); + + if (g_cfg->transcoder_ports.mode == PORT_ALLOC_STATIC) + vty_out(vty, " rtp transcoder-base %u%s", g_cfg->transcoder_ports.base_port, VTY_NEWLINE); + else + vty_out(vty, " rtp transcoder-range %u %u%s", + g_cfg->transcoder_ports.range_start, g_cfg->transcoder_ports.range_end, VTY_NEWLINE); + vty_out(vty, " transcoder-remote-base %u%s", g_cfg->transcoder_remote_base, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg) +{ + int i; + + vty_out(vty, "%s trunk nr %d with %d endpoints:%s", + cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", + cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE); + + if (!cfg->endpoints) { + vty_out(vty, "No endpoints allocated yet.%s", VTY_NEWLINE); + return; + } + + for (i = 1; i < cfg->number_endpoints; ++i) { + struct mgcp_endpoint *endp = &cfg->endpoints[i]; + vty_out(vty, + " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u on %s " + "traffic received bts: %u 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); + } +} + +DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp", + SHOW_STR "Display information about the MGCP Media Gateway") +{ + struct mgcp_trunk_config *trunk; + + dump_trunk(vty, &g_cfg->trunk); + + llist_for_each_entry(trunk, &g_cfg->trunks, entry) + dump_trunk(vty, trunk); + + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp, + cfg_mgcp_cmd, + "mgcp", + "Configure the MGCP") +{ + vty->node = MGCP_NODE; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_local_ip, + cfg_mgcp_local_ip_cmd, + "local ip A.B.C.D", + "Local options for the SDP record\n" + IP_STR + "IPv4 Address to use in SDP record\n") +{ + bsc_replace_string(g_cfg, &g_cfg->local_ip, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bts_ip, + cfg_mgcp_bts_ip_cmd, + "bts ip A.B.C.D", + "BTS Audio source/destination options\n" + IP_STR + "IPv4 Address of the BTS\n") +{ + bsc_replace_string(g_cfg, &g_cfg->bts_ip, argv[0]); + inet_aton(g_cfg->bts_ip, &g_cfg->bts_in); + return CMD_SUCCESS; +} + +#define BIND_STR "Listen/Bind related socket option\n" +DEFUN(cfg_mgcp_bind_ip, + cfg_mgcp_bind_ip_cmd, + "bind ip A.B.C.D", + BIND_STR + IP_STR + "IPv4 Address to bind to\n") +{ + bsc_replace_string(g_cfg, &g_cfg->source_addr, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bind_port, + cfg_mgcp_bind_port_cmd, + "bind port <0-65534>", + BIND_STR + "Port information\n" + "UDP port to listen for MGCP messages\n") +{ + unsigned int port = atoi(argv[0]); + g_cfg->source_port = port; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bind_early, + cfg_mgcp_bind_early_cmd, + "bind early (0|1)", + BIND_STR + "Bind local ports on start up\n" + "Bind on demand\n" "Bind on startup\n") +{ + vty_out(vty, "bind early is deprecated, remove it from the config.\n"); + return CMD_WARNING; +} + +static void parse_base(struct mgcp_port_range *range, const char **argv) +{ + unsigned int port = atoi(argv[0]); + range->mode = PORT_ALLOC_STATIC; + range->base_port = port; +} + +static void parse_range(struct mgcp_port_range *range, const char **argv) +{ + range->mode = PORT_ALLOC_DYNAMIC; + range->range_start = atoi(argv[0]); + range->range_end = atoi(argv[1]); + range->last_port = g_cfg->bts_ports.range_start; +} + + +#define RTP_STR "RTP configuration\n" +#define BTS_START_STR "First UDP port allocated for the BTS side\n" +#define NET_START_STR "First UDP port allocated for the NET side\n" +#define UDP_PORT_STR "UDP Port number\n" +DEFUN(cfg_mgcp_rtp_bts_base_port, + cfg_mgcp_rtp_bts_base_port_cmd, + "rtp bts-base <0-65534>", + RTP_STR + BTS_START_STR + UDP_PORT_STR) +{ + parse_base(&g_cfg->bts_ports, argv); + return CMD_SUCCESS; +} + +#define RANGE_START_STR "Start of the range of ports\n" +#define RANGE_END_STR "End of the range of ports\n" +DEFUN(cfg_mgcp_rtp_bts_range, + cfg_mgcp_rtp_bts_range_cmd, + "rtp bts-range <0-65534> <0-65534>", + RTP_STR "Range of ports to use for the BTS side\n" + RANGE_START_STR RANGE_END_STR) +{ + parse_range(&g_cfg->bts_ports, argv); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_rtp_net_range, + cfg_mgcp_rtp_net_range_cmd, + "rtp net-range <0-65534> <0-65534>", + RTP_STR "Range of ports to use for the NET side\n" + RANGE_START_STR RANGE_END_STR) +{ + parse_range(&g_cfg->net_ports, argv); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_rtp_net_base_port, + cfg_mgcp_rtp_net_base_port_cmd, + "rtp net-base <0-65534>", + RTP_STR NET_START_STR UDP_PORT_STR) +{ + parse_base(&g_cfg->net_ports, argv); + return CMD_SUCCESS; +} + +ALIAS_DEPRECATED(cfg_mgcp_rtp_bts_base_port, cfg_mgcp_rtp_base_port_cmd, + "rtp base <0-65534>", + RTP_STR BTS_START_STR UDP_PORT_STR) + +DEFUN(cfg_mgcp_rtp_transcoder_range, + cfg_mgcp_rtp_transcoder_range_cmd, + "rtp transcoder-range <0-65534> <0-65534>", + RTP_STR "Range of ports to use for the Transcoder\n" + RANGE_START_STR RANGE_END_STR) +{ + parse_range(&g_cfg->transcoder_ports, argv); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_rtp_transcoder_base, + cfg_mgcp_rtp_transcoder_base_cmd, + "rtp transcoder-base <0-65534>", + RTP_STR "First UDP port allocated for the Transcoder side\n" + UDP_PORT_STR) +{ + parse_base(&g_cfg->transcoder_ports, argv); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_rtp_ip_dscp, + cfg_mgcp_rtp_ip_dscp_cmd, + "rtp ip-dscp <0-255>", + RTP_STR + "Apply IP_TOS to the audio stream\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") + +DEFUN(cfg_mgcp_sdp_fmtp_extra, + cfg_mgcp_sdp_fmtp_extra_cmd, + "sdp audio fmtp-extra .NAME", + "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n" + "Extra Information\n") +{ + char *txt = argv_concat(argv, argc, 0); + if (!txt) + return CMD_WARNING; + + bsc_replace_string(g_cfg, &g_cfg->trunk.audio_fmtp_extra, txt); + talloc_free(txt); + return CMD_SUCCESS; +} + +#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 <1-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 <1-255>", + SDP_STR AUDIO_STR AUDIO_STR "Number\n" "Payload number\n") + + +DEFUN(cfg_mgcp_sdp_payload_name, + cfg_mgcp_sdp_payload_name_cmd, + "sdp audio-payload name NAME", + SDP_STR AUDIO_STR "Name\n" "Payload name\n") +{ + bsc_replace_string(g_cfg, &g_cfg->trunk.audio_name, argv[0]); + return CMD_SUCCESS; +} + +ALIAS_DEPRECATED(cfg_mgcp_sdp_payload_name, cfg_mgcp_sdp_payload_name_cmd_old, + "sdp audio payload name NAME", + SDP_STR AUDIO_STR AUDIO_STR "Name\n" "Payload name\n") + +DEFUN(cfg_mgcp_loop, + cfg_mgcp_loop_cmd, + "loop (0|1)", + "Loop audio for all endpoints on main trunk\n" + "Don't Loop\n" "Loop\n") +{ + 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; +} + +#define CALL_AGENT_STR "Callagent information\n" +DEFUN(cfg_mgcp_agent_addr, + cfg_mgcp_agent_addr_cmd, + "call-agent ip A.B.C.D", + CALL_AGENT_STR IP_STR + "IPv4 Address of the callagent\n") +{ + bsc_replace_string(g_cfg, &g_cfg->call_agent_addr, argv[0]); + return CMD_SUCCESS; +} + +ALIAS_DEPRECATED(cfg_mgcp_agent_addr, cfg_mgcp_agent_addr_cmd_old, + "call agent ip A.B.C.D", + CALL_AGENT_STR CALL_AGENT_STR IP_STR + "IPv4 Address of the callagent\n") + + +DEFUN(cfg_mgcp_transcoder, + cfg_mgcp_transcoder_cmd, + "transcoder-mgw A.B.C.D", + "Use a MGW to detranscoder RTP\n" + "The IP address of the MGW") +{ + bsc_replace_string(g_cfg, &g_cfg->transcoder_ip, argv[0]); + inet_aton(g_cfg->transcoder_ip, &g_cfg->transcoder_in); + + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_no_transcoder, + cfg_mgcp_no_transcoder_cmd, + "no transcoder-mgw", + NO_STR "Disable the transcoding\n") +{ + if (g_cfg->transcoder_ip) { + LOGP(DMGCP, LOGL_NOTICE, "Disabling transcoding on future calls.\n"); + talloc_free(g_cfg->transcoder_ip); + g_cfg->transcoder_ip = NULL; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_transcoder_remote_base, + cfg_mgcp_transcoder_remote_base_cmd, + "transcoder-remote-base <0-65534>", + "Set the base port for the transcoder\n" "The RTP base port on the transcoder") +{ + g_cfg->transcoder_remote_base = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_trunk, cfg_mgcp_trunk_cmd, + "trunk <1-64>", + "Configure a SS7 trunk\n" "Trunk Nr\n") +{ + struct mgcp_trunk_config *trunk; + int index = atoi(argv[0]); + + trunk = mgcp_trunk_num(g_cfg, index); + if (!trunk) + trunk = mgcp_trunk_alloc(g_cfg, index); + + if (!trunk) { + vty_out(vty, "%%Unable to allocate trunk %u.%s", + index, VTY_NEWLINE); + return CMD_WARNING; + } + + vty->node = TRUNK_NODE; + vty->index = trunk; + return CMD_SUCCESS; +} + +static int config_write_trunk(struct vty *vty) +{ + struct mgcp_trunk_config *trunk; + + llist_for_each_entry(trunk, &g_cfg->trunks, entry) { + vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE); + vty_out(vty, " sdp audio-payload number %d%s", + trunk->audio_payload, VTY_NEWLINE); + vty_out(vty, " sdp audio-payload name %s%s", + trunk->audio_name, VTY_NEWLINE); + vty_out(vty, " 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->audio_fmtp_extra) + vty_out(vty, " sdp audio fmtp-extra %s%s", + trunk->audio_fmtp_extra, VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_trunk_sdp_fmtp_extra, + cfg_trunk_sdp_fmtp_extra_cmd, + "sdp audio fmtp-extra .NAME", + "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n" + "Extra Information\n") +{ + struct mgcp_trunk_config *trunk = vty->index; + char *txt = argv_concat(argv, argc, 0); + if (!txt) + return CMD_WARNING; + + bsc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt); + talloc_free(txt); + return CMD_SUCCESS; +} + +DEFUN(cfg_trunk_payload_number, + cfg_trunk_payload_number_cmd, + "sdp audio-payload number <1-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 <1-255>", + SDP_STR AUDIO_STR AUDIO_STR "Number\n" "Payload Number\n") + +DEFUN(cfg_trunk_payload_name, + cfg_trunk_payload_name_cmd, + "sdp audio-payload name NAME", + SDP_STR AUDIO_STR "Payload\n" "Payload Name\n") +{ + struct mgcp_trunk_config *trunk = vty->index; + + bsc_replace_string(g_cfg, &trunk->audio_name, argv[0]); + return CMD_SUCCESS; +} + +ALIAS_DEPRECATED(cfg_trunk_payload_name, cfg_trunk_payload_name_cmd_old, + "sdp audio payload name NAME", + SDP_STR AUDIO_STR AUDIO_STR "Payload\n" "Payload Name\n") + + +DEFUN(cfg_trunk_loop, + cfg_trunk_loop_cmd, + "loop (0|1)", + "Loop audio for all endpoints on this trunk\n" + "Don't Loop\n" "Loop\n") +{ + struct mgcp_trunk_config *trunk = vty->index; + + trunk->audio_loop = atoi(argv[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(loop_endp, + loop_endp_cmd, + "loop-endpoint <0-64> NAME (0|1)", + "Loop a given endpoint\n" "Trunk number\n" + "The name in hex of the endpoint\n" "Disable the loop\n" "Enable the loop\n") +{ + struct mgcp_trunk_config *trunk; + struct mgcp_endpoint *endp; + + trunk = find_trunk(g_cfg, atoi(argv[0])); + if (!trunk) { + vty_out(vty, "%%Trunk %d not found in the config.%s", + atoi(argv[0]), VTY_NEWLINE); + return CMD_WARNING; + } + + if (!trunk->endpoints) { + vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", + trunk->trunk_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + int endp_no = strtoul(argv[1], NULL, 16); + if (endp_no < 1 || endp_no >= trunk->number_endpoints) { + vty_out(vty, "Loopback number %s/%d is invalid.%s", + argv[1], endp_no, VTY_NEWLINE); + return CMD_WARNING; + } + + + endp = &trunk->endpoints[endp_no]; + int loop = atoi(argv[2]); + + if (loop) + endp->conn_mode = MGCP_CONN_LOOPBACK; + else + endp->conn_mode = endp->orig_mode; + endp->allow_patch = 1; + + return CMD_SUCCESS; +} + +DEFUN(tap_call, + tap_call_cmd, + "tap-call <0-64> ENDPOINT (bts-in|bts-out|net-in|net-out) A.B.C.D <0-65534>", + "Forward data on endpoint to a different system\n" "Trunk number\n" + "The endpoint in hex\n" + "Forward the data coming from the bts\n" + "Forward the data coming from the bts leaving to the network\n" + "Forward the data coming from the net\n" + "Forward the data coming from the net leaving to the bts\n" + "destination IP of the data\n" "destination port\n") +{ + struct mgcp_rtp_tap *tap; + struct mgcp_trunk_config *trunk; + struct mgcp_endpoint *endp; + int port = 0; + + trunk = find_trunk(g_cfg, atoi(argv[0])); + if (!trunk) { + vty_out(vty, "%%Trunk %d not found in the config.%s", + atoi(argv[0]), VTY_NEWLINE); + return CMD_WARNING; + } + + if (!trunk->endpoints) { + vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", + trunk->trunk_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + int endp_no = strtoul(argv[1], NULL, 16); + if (endp_no < 1 || endp_no >= trunk->number_endpoints) { + vty_out(vty, "Endpoint number %s/%d is invalid.%s", + argv[1], endp_no, VTY_NEWLINE); + return CMD_WARNING; + } + + endp = &trunk->endpoints[endp_no]; + + if (strcmp(argv[2], "bts-in") == 0) { + port = MGCP_TAP_BTS_IN; + } else if (strcmp(argv[2], "bts-out") == 0) { + port = MGCP_TAP_BTS_OUT; + } else if (strcmp(argv[2], "net-in") == 0) { + port = MGCP_TAP_NET_IN; + } else if (strcmp(argv[2], "net-out") == 0) { + port = MGCP_TAP_NET_OUT; + } else { + vty_out(vty, "Unknown mode... tricked vty?%s", VTY_NEWLINE); + return CMD_WARNING; + } + + tap = &endp->taps[port]; + memset(&tap->forward, 0, sizeof(tap->forward)); + inet_aton(argv[3], &tap->forward.sin_addr); + tap->forward.sin_port = htons(atoi(argv[4])); + tap->enabled = 1; + return CMD_SUCCESS; +} + +DEFUN(free_endp, free_endp_cmd, + "free-endpoint <0-64> NUMBER", + "Free the given endpoint\n" "Trunk number\n" + "Endpoint number in hex.\n") +{ + struct mgcp_trunk_config *trunk; + struct mgcp_endpoint *endp; + + trunk = find_trunk(g_cfg, atoi(argv[0])); + if (!trunk) { + vty_out(vty, "%%Trunk %d not found in the config.%s", + atoi(argv[0]), VTY_NEWLINE); + return CMD_WARNING; + } + + if (!trunk->endpoints) { + vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", + trunk->trunk_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + int endp_no = strtoul(argv[1], NULL, 16); + if (endp_no < 1 || endp_no >= trunk->number_endpoints) { + vty_out(vty, "Endpoint number %s/%d is invalid.%s", + argv[1], endp_no, VTY_NEWLINE); + return CMD_WARNING; + } + + endp = &trunk->endpoints[endp_no]; + mgcp_free_endp(endp); + return CMD_SUCCESS; +} + +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; +} + + +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); + + install_default(MGCP_NODE); + install_element(MGCP_NODE, &ournode_exit_cmd); + install_element(MGCP_NODE, &ournode_end_cmd); + install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd); + install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_base_port_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_net_base_port_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_range_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_net_range_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_range_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_base_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd); + install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd); + install_element(MGCP_NODE, &cfg_mgcp_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_sdp_fmtp_extra_cmd); + + install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd); + install_node(&trunk_node, config_write_trunk); + install_default(TRUNK_NODE); + install_element(TRUNK_NODE, &ournode_exit_cmd); + install_element(TRUNK_NODE, &ournode_end_cmd); + install_element(TRUNK_NODE, &cfg_trunk_payload_number_cmd); + install_element(TRUNK_NODE, &cfg_trunk_payload_name_cmd); + install_element(TRUNK_NODE, &cfg_trunk_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_sdp_fmtp_extra_cmd); + + return 0; +} + +static int allocate_trunk(struct mgcp_trunk_config *trunk) +{ + int i; + struct mgcp_config *cfg = trunk->cfg; + + if (mgcp_endpoints_allocate(trunk) != 0) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to allocate %d endpoints on trunk %d.\n", + trunk->number_endpoints, trunk->trunk_nr); + return -1; + } + + /* early bind */ + for (i = 1; i < trunk->number_endpoints; ++i) { + struct mgcp_endpoint *endp = &trunk->endpoints[i]; + + if (cfg->bts_ports.mode == PORT_ALLOC_STATIC) { + cfg->last_bts_port += 2; + if (mgcp_bind_bts_rtp_port(endp, cfg->last_bts_port) != 0) { + LOGP(DMGCP, LOGL_FATAL, + "Failed to bind: %d\n", cfg->last_bts_port); + return -1; + } + endp->bts_end.local_alloc = PORT_ALLOC_STATIC; + } + + if (cfg->net_ports.mode == PORT_ALLOC_STATIC) { + cfg->last_net_port += 2; + if (mgcp_bind_net_rtp_port(endp, cfg->last_net_port) != 0) { + LOGP(DMGCP, LOGL_FATAL, + "Failed to bind: %d\n", cfg->last_net_port); + return -1; + } + endp->net_end.local_alloc = PORT_ALLOC_STATIC; + } + + if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL && + cfg->transcoder_ip && cfg->transcoder_ports.mode == PORT_ALLOC_STATIC) { + int rtp_port; + + /* network side */ + rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), + cfg->transcoder_ports.base_port); + if (mgcp_bind_trans_net_rtp_port(endp, rtp_port) != 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port); + return -1; + } + endp->trans_net.local_alloc = PORT_ALLOC_STATIC; + + /* bts side */ + rtp_port = rtp_calculate_port(endp_back_channel(ENDPOINT_NUMBER(endp)), + cfg->transcoder_ports.base_port); + if (mgcp_bind_trans_bts_rtp_port(endp, rtp_port) != 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port); + return -1; + } + endp->trans_bts.local_alloc = PORT_ALLOC_STATIC; + } + } + + return 0; +} + +int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg) +{ + int rc; + struct mgcp_trunk_config *trunk; + + g_cfg = cfg; + rc = vty_read_config_file(config_file, NULL); + if (rc < 0) { + fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); + return rc; + } + + + if (!g_cfg->bts_ip) + fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n"); + + if (!g_cfg->source_addr) { + fprintf(stderr, "You need to specify a bind address.\n"); + return -1; + } + + /* initialize the last ports */ + g_cfg->last_bts_port = rtp_calculate_port(0, g_cfg->bts_ports.base_port); + g_cfg->last_net_port = rtp_calculate_port(0, g_cfg->net_ports.base_port); + + if (allocate_trunk(&g_cfg->trunk) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to initialize the virtual trunk.\n"); + return -1; + } + + llist_for_each_entry(trunk, &g_cfg->trunks, entry) { + if (allocate_trunk(trunk) != 0) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize E1 trunk %d.\n", trunk->trunk_nr); + return -1; + } + } + + return 0; +} + diff --git a/src/libmsc/Makefile.am b/src/libmsc/Makefile.am new file mode 100644 index 000000000..c36ba926b --- /dev/null +++ b/src/libmsc/Makefile.am @@ -0,0 +1,23 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) + +noinst_LIBRARIES = libmsc.a + +libmsc_a_SOURCES = auth.c \ + db.c \ + gsm_04_08.c gsm_04_11.c gsm_04_80.c \ + gsm_subscriber.c \ + mncc.c mncc_builtin.c mncc_sock.c \ + rrlp.c \ + silent_call.c \ + sms_queue.c \ + token_auth.c \ + ussd.c \ + vty_interface_layer3.c \ + transaction.c \ + osmo_msc.c + +if BUILD_SMPP +libmsc_a_SOURCES += smpp_smsc.c smpp_openbsc.c smpp_vty.c smpp_utils.c +endif diff --git a/src/libmsc/auth.c b/src/libmsc/auth.c new file mode 100644 index 000000000..10d8edf67 --- /dev/null +++ b/src/libmsc/auth.c @@ -0,0 +1,132 @@ +/* Authentication related functions */ + +/* + * (C) 2010 by Sylvain Munaut + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include + +#include + + +static int +_use_xor(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) +{ + int i, l = ainfo->a3a8_ki_len; + + if ((l > A38_XOR_MAX_KEY_LEN) || (l < A38_XOR_MIN_KEY_LEN)) { + LOGP(DMM, LOGL_ERROR, "Invalid XOR key (len=%d) %s\n", + ainfo->a3a8_ki_len, + osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); + return -1; + } + + for (i=0; i<4; i++) + atuple->sres[i] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; + for (i=4; i<12; i++) + atuple->kc[i-4] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; + + return 0; +} + +static int +_use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) +{ + if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) { + LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n", + ainfo->a3a8_ki_len, + osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); + return -1; + } + + comp128(ainfo->a3a8_ki, atuple->rand, atuple->sres, atuple->kc); + + return 0; +} + +/* Return values + * -1 -> Internal error + * 0 -> Not available + * 1 -> Tuple returned, need to do auth, then enable cipher + * 2 -> Tuple returned, need to enable cipher + */ +int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr, int key_seq) +{ + struct gsm_auth_info ainfo; + int i, rc; + + /* Get subscriber info (if any) */ + rc = db_get_authinfo_for_subscr(&ainfo, subscr); + if (rc < 0) { + LOGP(DMM, LOGL_NOTICE, + "No retrievable Ki for subscriber, skipping auth\n"); + return rc == -ENOENT ? AUTH_NOT_AVAIL : -1; + } + + /* If possible, re-use the last tuple and skip auth */ + rc = db_get_lastauthtuple_for_subscr(atuple, subscr); + if ((rc == 0) && + (key_seq != GSM_KEY_SEQ_INVAL) && + (atuple->use_count < 3)) + { + atuple->use_count++; + db_sync_lastauthtuple_for_subscr(atuple, subscr); + DEBUGP(DMM, "Auth tuple use < 3, just doing ciphering\n"); + return AUTH_DO_CIPH; + } + + /* Generate a new one */ + atuple->use_count = 1; + atuple->key_seq = (atuple->key_seq + 1) % 7; + for (i=0; irand); i++) + atuple->rand[i] = random() & 0xff; + + switch (ainfo.auth_algo) { + case AUTH_ALGO_NONE: + DEBUGP(DMM, "No authentication for subscriber\n"); + return 0; + + case AUTH_ALGO_XOR: + if (_use_xor(&ainfo, atuple)) + return 0; + break; + + case AUTH_ALGO_COMP128v1: + if (_use_comp128_v1(&ainfo, atuple)) + return 0; + break; + + default: + DEBUGP(DMM, "Unsupported auth type algo_id=%d\n", + ainfo.auth_algo); + return 0; + } + + db_sync_lastauthtuple_for_subscr(atuple, subscr); + + DEBUGP(DMM, "Need to do authentication and ciphering\n"); + return AUTH_DO_AUTH_THAN_CIPH; +} + diff --git a/src/libmsc/db.c b/src/libmsc/db.c new file mode 100644 index 000000000..21abce9de --- /dev/null +++ b/src/libmsc/db.c @@ -0,0 +1,1412 @@ +/* Simple HLR/VLR database backend using dbi */ +/* (C) 2008 by Jan Luebbe + * (C) 2009 by Holger Hans Peter Freyther + * (C) 2009 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +static char *db_basename = NULL; +static char *db_dirname = NULL; +static dbi_conn conn; + +#define SCHEMA_REVISION "3" + +static char *create_stmts[] = { + "CREATE TABLE IF NOT EXISTS Meta (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "key TEXT UNIQUE NOT NULL, " + "value TEXT NOT NULL" + ")", + "INSERT OR IGNORE INTO Meta " + "(key, value) " + "VALUES " + "('revision', " SCHEMA_REVISION ")", + "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" + ")", + "CREATE TABLE IF NOT EXISTS AuthToken (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "subscriber_id INTEGER UNIQUE NOT NULL, " + "created TIMESTAMP NOT NULL, " + "token TEXT UNIQUE NOT NULL" + ")", + "CREATE TABLE IF NOT EXISTS Equipment (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "created TIMESTAMP NOT NULL, " + "updated TIMESTAMP NOT NULL, " + "name TEXT, " + "classmark1 NUMERIC, " + "classmark2 BLOB, " + "classmark3 BLOB, " + "imei NUMERIC UNIQUE NOT NULL" + ")", + "CREATE TABLE IF NOT EXISTS EquipmentWatch (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "created TIMESTAMP NOT NULL, " + "updated TIMESTAMP NOT NULL, " + "subscriber_id NUMERIC NOT NULL, " + "equipment_id NUMERIC NOT NULL, " + "UNIQUE (subscriber_id, equipment_id) " + ")", + "CREATE TABLE IF NOT EXISTS SMS (" + /* metadata, not part of sms */ + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "created TIMESTAMP NOT NULL, " + "sent TIMESTAMP, " + "sender_id INTEGER NOT NULL, " + "receiver_id INTEGER NOT NULL, " + "deliver_attempts INTEGER NOT NULL DEFAULT 0, " + /* data directly copied/derived from SMS */ + "valid_until TIMESTAMP, " + "reply_path_req INTEGER NOT NULL, " + "status_rep_req INTEGER NOT NULL, " + "protocol_id INTEGER NOT NULL, " + "data_coding_scheme INTEGER NOT NULL, " + "ud_hdr_ind INTEGER NOT NULL, " + "dest_addr TEXT, " + "user_data BLOB, " /* TP-UD */ + /* additional data, interpreted from SMS */ + "header BLOB, " /* UD Header */ + "text TEXT " /* decoded UD after UDH */ + ")", + "CREATE TABLE IF NOT EXISTS VLR (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "created TIMESTAMP NOT NULL, " + "updated TIMESTAMP NOT NULL, " + "subscriber_id NUMERIC UNIQUE NOT NULL, " + "last_bts NUMERIC NOT NULL " + ")", + "CREATE TABLE IF NOT EXISTS ApduBlobs (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "created TIMESTAMP NOT NULL, " + "apdu_id_flags INTEGER NOT NULL, " + "subscriber_id INTEGER NOT NULL, " + "apdu BLOB " + ")", + "CREATE TABLE IF NOT EXISTS Counters (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "timestamp TIMESTAMP NOT NULL, " + "value INTEGER NOT NULL, " + "name TEXT NOT NULL " + ")", + "CREATE TABLE IF NOT EXISTS RateCounters (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "timestamp TIMESTAMP NOT NULL, " + "value INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "idx INTEGER NOT NULL " + ")", + "CREATE TABLE IF NOT EXISTS AuthKeys (" + "subscriber_id INTEGER PRIMARY KEY, " + "algorithm_id INTEGER NOT NULL, " + "a3a8_ki BLOB " + ")", + "CREATE TABLE IF NOT EXISTS AuthLastTuples (" + "subscriber_id INTEGER PRIMARY KEY, " + "issued TIMESTAMP NOT NULL, " + "use_count INTEGER NOT NULL DEFAULT 0, " + "key_seq INTEGER NOT NULL, " + "rand BLOB NOT NULL, " + "sres BLOB NOT NULL, " + "kc BLOB NOT NULL " + ")", +}; + +void db_error_func(dbi_conn conn, void *data) +{ + const char *msg; + dbi_conn_error(conn, &msg); + LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg); +} + +static int 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 vom 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 set new revision (upgrade vom rev 2).\n"); + return -EINVAL; + } + dbi_result_free(result); + + return 0; +} + +static int check_db_revision(void) +{ + dbi_result result; + const char *rev_s; + + result = dbi_conn_query(conn, + "SELECT value FROM Meta WHERE key='revision'"); + if (!result) + return -EINVAL; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -EINVAL; + } + rev_s = dbi_result_get_string(result, "value"); + if (!rev_s) { + dbi_result_free(result); + return -EINVAL; + } + if (!strcmp(rev_s, "2")) { + if (update_db_revision_2()) { + LOGP(DDB, LOGL_FATAL, "Failed to update database from schema revision '%s'.\n", rev_s); + dbi_result_free(result); + return -EINVAL; + } + } else if (!strcmp(rev_s, SCHEMA_REVISION)) { + /* everything is fine */ + } else { + LOGP(DDB, LOGL_FATAL, "Invalid database schema revision '%s'.\n", rev_s); + dbi_result_free(result); + return -EINVAL; + } + + dbi_result_free(result); + return 0; +} + +static int db_configure(void) +{ + dbi_result result; + + result = dbi_conn_query(conn, + "PRAGMA synchronous = FULL"); + if (!result) + return -EINVAL; + + dbi_result_free(result); + return 0; +} + +int db_init(const char *name) +{ + dbi_initialize(NULL); + + conn = dbi_conn_new("sqlite3"); + if (conn == NULL) { + LOGP(DDB, LOGL_FATAL, "Failed to create connection.\n"); + return 1; + } + + dbi_conn_error_handler( conn, db_error_func, NULL ); + + /* MySQL + dbi_conn_set_option(conn, "host", "localhost"); + dbi_conn_set_option(conn, "username", "your_name"); + dbi_conn_set_option(conn, "password", "your_password"); + dbi_conn_set_option(conn, "dbname", "your_dbname"); + dbi_conn_set_option(conn, "encoding", "UTF-8"); + */ + + /* SqLite 3 */ + db_basename = strdup(name); + db_dirname = strdup(name); + dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname)); + dbi_conn_set_option(conn, "dbname", basename(db_basename)); + + if (dbi_conn_connect(conn) < 0) + goto out_err; + + return 0; + +out_err: + free(db_dirname); + free(db_basename); + db_dirname = db_basename = NULL; + return -1; +} + + +int db_prepare(void) +{ + dbi_result result; + int i; + + for (i = 0; i < ARRAY_SIZE(create_stmts); i++) { + result = dbi_conn_query(conn, create_stmts[i]); + if (!result) { + LOGP(DDB, LOGL_ERROR, + "Failed to create some table.\n"); + return 1; + } + dbi_result_free(result); + } + + if (check_db_revision() < 0) { + LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, " + "please update your database schema\n"); + return -1; + } + + db_configure(); + + return 0; +} + +int db_fini(void) +{ + dbi_conn_close(conn); + dbi_shutdown(); + + free(db_dirname); + free(db_basename); + return 0; +} + +struct gsm_subscriber *db_create_subscriber(struct gsm_network *net, char *imsi) +{ + dbi_result result; + struct gsm_subscriber *subscr; + + /* Is this subscriber known in the db? */ + subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); + if (subscr) { + result = dbi_conn_queryf(conn, + "UPDATE Subscriber set updated = datetime('now') " + "WHERE imsi = %s " , imsi); + if (!result) + LOGP(DDB, LOGL_ERROR, "failed to update timestamp\n"); + else + dbi_result_free(result); + return subscr; + } + + subscr = subscr_alloc(); + 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->net = net; + subscr->id = dbi_conn_sequence_last(conn, NULL); + strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1); + dbi_result_free(result); + LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi); + db_subscriber_alloc_exten(subscr); + return subscr; +} + +osmo_static_assert(sizeof(unsigned char) == sizeof(struct gsm48_classmark1), classmark1_size); + +static int get_equipment_by_subscr(struct gsm_subscriber *subscr) +{ + dbi_result result; + const char *string; + unsigned char cm1; + const unsigned char *cm2, *cm3; + struct gsm_equipment *equip = &subscr->equipment; + + result = dbi_conn_queryf(conn, + "SELECT Equipment.* " + "FROM Equipment JOIN EquipmentWatch ON " + "EquipmentWatch.equipment_id=Equipment.id " + "WHERE EquipmentWatch.subscriber_id = %llu " + "ORDER BY EquipmentWatch.updated DESC", subscr->id); + if (!result) + return -EIO; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -ENOENT; + } + + equip->id = dbi_result_get_ulonglong(result, "id"); + + string = dbi_result_get_string(result, "imei"); + if (string) + strncpy(equip->imei, string, sizeof(equip->imei)); + + string = dbi_result_get_string(result, "classmark1"); + if (string) { + cm1 = atoi(string) & 0xff; + memcpy(&equip->classmark1, &cm1, sizeof(equip->classmark1)); + } + + equip->classmark2_len = dbi_result_get_field_length(result, "classmark2"); + cm2 = dbi_result_get_binary(result, "classmark2"); + if (equip->classmark2_len > sizeof(equip->classmark2)) + equip->classmark2_len = sizeof(equip->classmark2); + memcpy(equip->classmark2, cm2, equip->classmark2_len); + + equip->classmark3_len = dbi_result_get_field_length(result, "classmark3"); + cm3 = dbi_result_get_binary(result, "classmark3"); + if (equip->classmark3_len > sizeof(equip->classmark3)) + equip->classmark3_len = sizeof(equip->classmark3); + memcpy(equip->classmark3, cm3, equip->classmark3_len); + + dbi_result_free(result); + + return 0; +} + +int db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo, + struct gsm_subscriber *subscr) +{ + dbi_result result; + const unsigned char *a3a8_ki; + + result = dbi_conn_queryf(conn, + "SELECT * FROM AuthKeys WHERE subscriber_id=%llu", + subscr->id); + if (!result) + return -EIO; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -ENOENT; + } + + ainfo->auth_algo = dbi_result_get_ulonglong(result, "algorithm_id"); + ainfo->a3a8_ki_len = dbi_result_get_field_length(result, "a3a8_ki"); + a3a8_ki = dbi_result_get_binary(result, "a3a8_ki"); + if (ainfo->a3a8_ki_len > sizeof(ainfo->a3a8_ki)) + ainfo->a3a8_ki_len = sizeof(ainfo->a3a8_ki); + memcpy(ainfo->a3a8_ki, a3a8_ki, ainfo->a3a8_ki_len); + + dbi_result_free(result); + + return 0; +} + +int db_sync_authinfo_for_subscr(struct gsm_auth_info *ainfo, + struct gsm_subscriber *subscr) +{ + dbi_result result; + struct gsm_auth_info ainfo_old; + int rc, upd; + unsigned char *ki_str; + + /* Deletion ? */ + if (ainfo == NULL) { + result = dbi_conn_queryf(conn, + "DELETE FROM AuthKeys WHERE subscriber_id=%llu", + subscr->id); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; + } + + /* Check if already existing */ + rc = db_get_authinfo_for_subscr(&ainfo_old, subscr); + if (rc && rc != -ENOENT) + return rc; + upd = rc ? 0 : 1; + + /* Update / Insert */ + dbi_conn_quote_binary_copy(conn, + ainfo->a3a8_ki, ainfo->a3a8_ki_len, &ki_str); + + if (!upd) { + result = dbi_conn_queryf(conn, + "INSERT INTO AuthKeys " + "(subscriber_id, algorithm_id, a3a8_ki) " + "VALUES (%llu, %u, %s)", + subscr->id, ainfo->auth_algo, ki_str); + } else { + result = dbi_conn_queryf(conn, + "UPDATE AuthKeys " + "SET algorithm_id=%u, a3a8_ki=%s " + "WHERE subscriber_id=%llu", + ainfo->auth_algo, ki_str, subscr->id); + } + + free(ki_str); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; +} + +int db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr) +{ + dbi_result result; + int len; + const unsigned char *blob; + + result = dbi_conn_queryf(conn, + "SELECT * FROM AuthLastTuples WHERE subscriber_id=%llu", + subscr->id); + if (!result) + return -EIO; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -ENOENT; + } + + memset(atuple, 0, sizeof(*atuple)); + + atuple->use_count = dbi_result_get_ulonglong(result, "use_count"); + atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq"); + + len = dbi_result_get_field_length(result, "rand"); + if (len != sizeof(atuple->rand)) + goto err_size; + + blob = dbi_result_get_binary(result, "rand"); + memcpy(atuple->rand, blob, len); + + len = dbi_result_get_field_length(result, "sres"); + if (len != sizeof(atuple->sres)) + goto err_size; + + blob = dbi_result_get_binary(result, "sres"); + memcpy(atuple->sres, blob, len); + + len = dbi_result_get_field_length(result, "kc"); + if (len != sizeof(atuple->kc)) + goto err_size; + + blob = dbi_result_get_binary(result, "kc"); + memcpy(atuple->kc, blob, len); + + dbi_result_free(result); + + return 0; + +err_size: + dbi_result_free(result); + return -EIO; +} + +int db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr) +{ + dbi_result result; + int rc, upd; + struct gsm_auth_tuple atuple_old; + unsigned char *rand_str, *sres_str, *kc_str; + + /* Deletion ? */ + if (atuple == NULL) { + result = dbi_conn_queryf(conn, + "DELETE FROM AuthLastTuples WHERE subscriber_id=%llu", + subscr->id); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; + } + + /* Check if already existing */ + rc = db_get_lastauthtuple_for_subscr(&atuple_old, subscr); + if (rc && rc != -ENOENT) + return rc; + upd = rc ? 0 : 1; + + /* Update / Insert */ + dbi_conn_quote_binary_copy(conn, + atuple->rand, sizeof(atuple->rand), &rand_str); + dbi_conn_quote_binary_copy(conn, + atuple->sres, sizeof(atuple->sres), &sres_str); + dbi_conn_quote_binary_copy(conn, + atuple->kc, sizeof(atuple->kc), &kc_str); + + if (!upd) { + result = dbi_conn_queryf(conn, + "INSERT INTO AuthLastTuples " + "(subscriber_id, issued, use_count, " + "key_seq, rand, sres, kc) " + "VALUES (%llu, datetime('now'), %u, " + "%u, %s, %s, %s ) ", + subscr->id, atuple->use_count, atuple->key_seq, + rand_str, sres_str, kc_str); + } else { + char *issued = atuple->key_seq == atuple_old.key_seq ? + "issued" : "datetime('now')"; + result = dbi_conn_queryf(conn, + "UPDATE AuthLastTuples " + "SET issued=%s, use_count=%u, " + "key_seq=%u, rand=%s, sres=%s, kc=%s " + "WHERE subscriber_id = %llu", + issued, atuple->use_count, atuple->key_seq, + rand_str, sres_str, kc_str, subscr->id); + } + + free(rand_str); + free(sres_str); + free(kc_str); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; +} + +static void db_set_from_query(struct gsm_subscriber *subscr, dbi_conn result) +{ + const char *string; + string = dbi_result_get_string(result, "imsi"); + if (string) + strncpy(subscr->imsi, string, GSM_IMSI_LENGTH); + + string = dbi_result_get_string(result, "tmsi"); + if (string) + subscr->tmsi = tmsi_from_string(string); + + string = dbi_result_get_string(result, "name"); + if (string) + strncpy(subscr->name, string, GSM_NAME_LENGTH); + + string = dbi_result_get_string(result, "extension"); + if (string) + strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH); + + subscr->lac = dbi_result_get_uint(result, "lac"); + + 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_uint(result, "authorized"); +} + +#define BASE_QUERY "SELECT * FROM Subscriber " +struct gsm_subscriber *db_get_subscriber(struct gsm_network *net, + enum gsm_subscriber_field field, + const char *id) +{ + dbi_result result; + char *quoted; + struct gsm_subscriber *subscr; + + switch (field) { + case GSM_SUBSCRIBER_IMSI: + dbi_conn_quote_string_copy(conn, id, "ed); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE imsi = %s ", + quoted + ); + free(quoted); + break; + case GSM_SUBSCRIBER_TMSI: + dbi_conn_quote_string_copy(conn, id, "ed); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE tmsi = %s ", + quoted + ); + free(quoted); + break; + case GSM_SUBSCRIBER_EXTENSION: + dbi_conn_quote_string_copy(conn, id, "ed); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE extension = %s ", + quoted + ); + free(quoted); + break; + case GSM_SUBSCRIBER_ID: + dbi_conn_quote_string_copy(conn, id, "ed); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE id = %s ", quoted); + free(quoted); + break; + default: + LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n"); + return NULL; + } + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n"); + return NULL; + } + if (!dbi_result_next_row(result)) { + DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n", + field, id); + dbi_result_free(result); + return NULL; + } + + subscr = subscr_alloc(); + subscr->net = net; + subscr->id = dbi_result_get_ulonglong(result, "id"); + + db_set_from_query(subscr, result); + DEBUGP(DDB, "Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n", + subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension, + subscr->lac, subscr->authorized); + dbi_result_free(result); + + get_equipment_by_subscr(subscr); + + return subscr; +} + +int db_subscriber_update(struct gsm_subscriber *subscr) +{ + char buf[32]; + dbi_result result; + + /* Copy the id to a string as queryf with %llu is failing */ + sprintf(buf, "%llu", subscr->id); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE id = %s", buf); + + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber: %llu\n", subscr->id); + return -EIO; + } + if (!dbi_result_next_row(result)) { + DEBUGP(DDB, "Failed to find the Subscriber. %llu\n", + subscr->id); + dbi_result_free(result); + return -EIO; + } + + db_set_from_query(subscr, result); + dbi_result_free(result); + get_equipment_by_subscr(subscr); + + return 0; +} + +int db_sync_subscriber(struct gsm_subscriber *subscriber) +{ + dbi_result result; + char tmsi[14]; + char *q_tmsi, *q_name, *q_extension; + + dbi_conn_quote_string_copy(conn, + subscriber->name, &q_name); + dbi_conn_quote_string_copy(conn, + subscriber->extension, &q_extension); + + if (subscriber->tmsi != GSM_RESERVED_TMSI) { + sprintf(tmsi, "%u", subscriber->tmsi); + dbi_conn_quote_string_copy(conn, + tmsi, + &q_tmsi); + } else + q_tmsi = strdup("NULL"); + + 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_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 (;;) { + subscriber->tmsi = rand(); + if (subscriber->tmsi == GSM_RESERVED_TMSI) + continue; + + sprintf(tmsi, "%u", subscriber->tmsi); + dbi_conn_quote_string_copy(conn, tmsi, &tmsi_quoted); + result = dbi_conn_queryf(conn, + "SELECT * FROM Subscriber " + "WHERE tmsi = %s ", + tmsi_quoted); + + free(tmsi_quoted); + + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " + "while allocating new TMSI.\n"); + return 1; + } + if (dbi_result_get_numrows(result)) { + dbi_result_free(result); + continue; + } + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n", + subscriber->tmsi, subscriber->imsi); + return db_sync_subscriber(subscriber); + } + dbi_result_free(result); + } + return 0; +} + +int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber) +{ + dbi_result result = NULL; + uint32_t try; + + for (;;) { + try = (rand()%(GSM_MAX_EXTEN-GSM_MIN_EXTEN+1)+GSM_MIN_EXTEN); + result = dbi_conn_queryf(conn, + "SELECT * FROM Subscriber " + "WHERE extension = %i", + try + ); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " + "while allocating new extension.\n"); + return 1; + } + if (dbi_result_get_numrows(result)){ + dbi_result_free(result); + continue; + } + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + break; + } + dbi_result_free(result); + } + sprintf(subscriber->extension, "%i", try); + DEBUGP(DDB, "Allocated extension %i for IMSI %s.\n", try, subscriber->imsi); + return db_sync_subscriber(subscriber); +} +/* + * try to allocate a new unique token for this subscriber and return it + * via a parameter. if the subscriber already has a token, return + * an error. + */ + +int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t *token) +{ + dbi_result result; + uint32_t try; + + for (;;) { + try = rand(); + if (!try) /* 0 is an invalid token */ + continue; + result = dbi_conn_queryf(conn, + "SELECT * FROM AuthToken " + "WHERE subscriber_id = %llu OR token = \"%08X\" ", + subscriber->id, try); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken " + "while allocating new token.\n"); + return 1; + } + if (dbi_result_get_numrows(result)) { + dbi_result_free(result); + continue; + } + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + break; + } + dbi_result_free(result); + } + result = dbi_conn_queryf(conn, + "INSERT INTO AuthToken " + "(subscriber_id, created, token) " + "VALUES " + "(%llu, datetime('now'), \"%08X\") ", + subscriber->id, try); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for " + "IMSI %s.\n", try, subscriber->imsi); + return 1; + } + dbi_result_free(result); + *token = try; + DEBUGP(DDB, "Allocated token %08X for IMSI %s.\n", try, subscriber->imsi); + + return 0; +} + +int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM_IMEI_LENGTH]) +{ + unsigned long long equipment_id, watch_id; + dbi_result result; + + strncpy(subscriber->equipment.imei, imei, + sizeof(subscriber->equipment.imei)-1), + + result = dbi_conn_queryf(conn, + "INSERT OR IGNORE INTO Equipment " + "(imei, created, updated) " + "VALUES " + "(%s, datetime('now'), datetime('now')) ", + imei); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to create Equipment by IMEI.\n"); + return 1; + } + + equipment_id = 0; + if (dbi_result_get_numrows_affected(result)) { + equipment_id = dbi_conn_sequence_last(conn, NULL); + } + dbi_result_free(result); + + if (equipment_id) + DEBUGP(DDB, "New Equipment: ID %llu, IMEI %s\n", equipment_id, imei); + else { + result = dbi_conn_queryf(conn, + "SELECT id FROM Equipment " + "WHERE imei = %s ", + imei + ); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n"); + return 1; + } + if (!dbi_result_next_row(result)) { + LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n"); + dbi_result_free(result); + return 1; + } + equipment_id = dbi_result_get_ulonglong(result, "id"); + dbi_result_free(result); + } + + result = dbi_conn_queryf(conn, + "INSERT OR IGNORE INTO EquipmentWatch " + "(subscriber_id, equipment_id, created, updated) " + "VALUES " + "(%llu, %llu, datetime('now'), datetime('now')) ", + subscriber->id, equipment_id); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to create EquipmentWatch.\n"); + return 1; + } + + watch_id = 0; + if (dbi_result_get_numrows_affected(result)) + watch_id = dbi_conn_sequence_last(conn, NULL); + + dbi_result_free(result); + if (watch_id) + DEBUGP(DDB, "New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", + equipment_id, subscriber->imsi, imei); + else { + result = dbi_conn_queryf(conn, + "UPDATE EquipmentWatch " + "SET updated = datetime('now') " + "WHERE subscriber_id = %llu AND equipment_id = %llu ", + subscriber->id, equipment_id); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n"); + return 1; + } + dbi_result_free(result); + DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", + equipment_id, subscriber->imsi, imei); + } + + return 0; +} + +/* store an [unsent] SMS to the database */ +int db_sms_store(struct gsm_sms *sms) +{ + dbi_result result; + char *q_text, *q_daddr; + unsigned char *q_udata; + char *validity_timestamp = "2222-2-2"; + + /* FIXME: generate validity timestamp based on validity_minutes */ + + dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text); + dbi_conn_quote_string_copy(conn, (char *)sms->dst.addr, &q_daddr); + dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len, + &q_udata); + /* FIXME: correct validity period */ + result = dbi_conn_queryf(conn, + "INSERT INTO SMS " + "(created, sender_id, receiver_id, valid_until, " + "reply_path_req, status_rep_req, protocol_id, " + "data_coding_scheme, ud_hdr_ind, dest_addr, " + "user_data, text) VALUES " + "(datetime('now'), %llu, %llu, %u, " + "%u, %u, %u, %u, %u, %s, %s, %s)", + sms->sender->id, + sms->receiver ? sms->receiver->id : 0, validity_timestamp, + sms->reply_path_req, sms->status_rep_req, sms->protocol_id, + sms->data_coding_scheme, sms->ud_hdr_ind, + q_daddr, q_udata, q_text); + free(q_text); + free(q_daddr); + free(q_udata); + + if (!result) + return -EIO; + + dbi_result_free(result); + return 0; +} + +static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result) +{ + struct gsm_sms *sms = sms_alloc(); + long long unsigned int sender_id, receiver_id; + const char *text, *daddr; + const unsigned char *user_data; + + if (!sms) + return NULL; + + sms->id = dbi_result_get_ulonglong(result, "id"); + + sender_id = dbi_result_get_ulonglong(result, "sender_id"); + sms->sender = subscr_get_by_id(net, sender_id); + strncpy(sms->src.addr, sms->sender->extension, sizeof(sms->src.addr)-1); + + receiver_id = dbi_result_get_ulonglong(result, "receiver_id"); + sms->receiver = subscr_get_by_id(net, receiver_id); + + /* FIXME: validity */ + /* FIXME: those should all be get_uchar, but sqlite3 is braindead */ + sms->reply_path_req = dbi_result_get_uint(result, "reply_path_req"); + sms->status_rep_req = dbi_result_get_uint(result, "status_rep_req"); + sms->ud_hdr_ind = dbi_result_get_uint(result, "ud_hdr_ind"); + sms->protocol_id = dbi_result_get_uint(result, "protocol_id"); + sms->data_coding_scheme = dbi_result_get_uint(result, + "data_coding_scheme"); + /* sms->msg_ref is temporary and not stored in DB */ + + daddr = dbi_result_get_string(result, "dest_addr"); + if (daddr) { + strncpy(sms->dst.addr, daddr, sizeof(sms->dst.addr)); + sms->dst.addr[sizeof(sms->dst.addr)-1] = '\0'; + } + + sms->user_data_len = dbi_result_get_field_length(result, "user_data"); + user_data = dbi_result_get_binary(result, "user_data"); + if (sms->user_data_len > sizeof(sms->user_data)) + sms->user_data_len = (uint8_t) sizeof(sms->user_data); + memcpy(sms->user_data, user_data, sms->user_data_len); + + text = dbi_result_get_string(result, "text"); + if (text) { + strncpy(sms->text, text, sizeof(sms->text)); + sms->text[sizeof(sms->text)-1] = '\0'; + } + return sms; +} + +struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id) +{ + dbi_result result; + struct gsm_sms *sms; + + result = dbi_conn_queryf(conn, + "SELECT * FROM SMS WHERE SMS.id = %llu", id); + if (!result) + return NULL; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return NULL; + } + + sms = sms_from_result(net, result); + + dbi_result_free(result); + + return sms; +} + +/* retrieve the next unsent SMS with ID >= min_id */ +struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id) +{ + dbi_result result; + struct gsm_sms *sms; + + result = dbi_conn_queryf(conn, + "SELECT SMS.* " + "FROM SMS JOIN Subscriber ON " + "SMS.receiver_id = Subscriber.id " + "WHERE SMS.id >= %llu AND SMS.sent IS NULL " + "AND Subscriber.lac > 0 " + "ORDER BY SMS.id LIMIT 1", + min_id); + if (!result) + return NULL; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return NULL; + } + + sms = sms_from_result(net, result); + + dbi_result_free(result); + + return sms; +} + +struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, + unsigned long long min_subscr_id, + unsigned int failed) +{ + dbi_result result; + struct gsm_sms *sms; + + result = dbi_conn_queryf(conn, + "SELECT SMS.* " + "FROM SMS JOIN Subscriber ON " + "SMS.receiver_id = Subscriber.id " + "WHERE SMS.receiver_id >= %llu AND SMS.sent IS NULL " + "AND Subscriber.lac > 0 AND SMS.deliver_attempts < %u " + "ORDER BY SMS.receiver_id, SMS.id LIMIT 1", + min_subscr_id, failed); + if (!result) + return NULL; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return NULL; + } + + sms = sms_from_result(net, result); + + dbi_result_free(result); + + return sms; +} + +/* retrieve the next unsent SMS for a given subscriber */ +struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr) +{ + dbi_result result; + struct gsm_sms *sms; + + result = dbi_conn_queryf(conn, + "SELECT SMS.* " + "FROM SMS JOIN Subscriber ON " + "SMS.receiver_id = Subscriber.id " + "WHERE SMS.receiver_id = %llu AND SMS.sent IS NULL " + "AND Subscriber.lac > 0 " + "ORDER BY SMS.id LIMIT 1", + subscr->id); + if (!result) + return NULL; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return NULL; + } + + sms = sms_from_result(subscr->net, result); + + dbi_result_free(result); + + return sms; +} + +/* mark a given SMS as read */ +int db_sms_mark_sent(struct gsm_sms *sms) +{ + dbi_result result; + + result = dbi_conn_queryf(conn, + "UPDATE SMS " + "SET sent = datetime('now') " + "WHERE id = %llu", sms->id); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id); + return 1; + } + + dbi_result_free(result); + return 0; +} + +/* increase the number of attempted deliveries */ +int db_sms_inc_deliver_attempts(struct gsm_sms *sms) +{ + dbi_result result; + + result = dbi_conn_queryf(conn, + "UPDATE SMS " + "SET deliver_attempts = deliver_attempts + 1 " + "WHERE id = %llu", sms->id); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for " + "SMS %llu.\n", sms->id); + return 1; + } + + dbi_result_free(result); + return 0; +} + +int db_apdu_blob_store(struct gsm_subscriber *subscr, + 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/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c new file mode 100644 index 000000000..eea073614 --- /dev/null +++ b/src/libmsc/gsm_04_08.c @@ -0,0 +1,3283 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-2009 by Harald Welte + * (C) 2008-2012 by Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "bscconfig.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +void *tall_locop_ctx; +void *tall_authciphop_ctx; + +int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, uint32_t tmsi); +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 uint32_t new_callref = 0x80000001; + +void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg) +{ + net->mncc_recv(net, msg); +} + +static int gsm48_conn_sendmsg(struct msgb *msg, struct gsm_subscriber_connection *conn, + struct gsm_trans *trans) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data; + + /* if we get passed a transaction reference, do some common + * work that the caller no longer has to do */ + if (trans) { + gh->proto_discr = trans->protocol | (trans->transaction_id << 4); + msg->lchan = trans->conn->lchan; + } + + + if (msg->lchan) { + struct e1inp_sign_link *sign_link = + msg->lchan->ts->trx->rsl_link; + + msg->dst = sign_link; + if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC) + DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) " + "Sending '%s' to MS.\n", + sign_link->trx->bts->nr, + sign_link->trx->nr, msg->lchan->ts->nr, + gh->proto_discr & 0xf0, + gsm48_cc_msg_name(gh->msg_type)); + else + DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) " + "Sending 0x%02x to MS.\n", + sign_link->trx->bts->nr, + sign_link->trx->nr, msg->lchan->ts->nr, + gh->proto_discr, gh->msg_type); + } + + return gsm0808_submit_dtap(conn, msg, 0, 0); +} + +int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message) +{ + struct gsm48_hdr *gh; + struct msgb *ss_notify; + + ss_notify = gsm0480_create_notifySS(message); + if (!ss_notify) + return -1; + + gsm0480_wrap_invoke(ss_notify, GSM0480_OP_CODE_NOTIFY_SS, 0); + uint8_t *data = msgb_push(ss_notify, 1); + data[0] = ss_notify->len - 1; + gh = (struct gsm48_hdr *) msgb_push(ss_notify, sizeof(*gh)); + gh->msg_type = GSM48_MT_CC_FACILITY; + return gsm48_conn_sendmsg(ss_notify, trans->conn, trans); +} + +void release_security_operation(struct gsm_subscriber_connection *conn) +{ + if (!conn->sec_operation) + return; + + talloc_free(conn->sec_operation); + conn->sec_operation = NULL; + msc_release_connection(conn); +} + +void allocate_security_operation(struct gsm_subscriber_connection *conn) +{ + conn->sec_operation = talloc_zero(tall_authciphop_ctx, + struct gsm_security_operation); +} + +int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, + gsm_cbfn *cb, void *cb_data) +{ + struct gsm_network *net = conn->bts->network; + struct gsm_subscriber *subscr = conn->subscr; + struct gsm_security_operation *op; + struct gsm_auth_tuple atuple; + int status = -1, rc; + + /* Check if we _can_ enable encryption. Cases where we can't: + * - Encryption disabled in config + * - Channel already secured (nothing to do) + * - Subscriber equipment doesn't support configured encryption + */ + if (!net->a5_encryption) { + status = GSM_SECURITY_NOAVAIL; + } else if (conn->lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { + DEBUGP(DMM, "Requesting to secure an already secure channel"); + status = GSM_SECURITY_ALREADY; + } else if (!ms_cm2_a5n_support(subscr->equipment.classmark2, + net->a5_encryption)) { + DEBUGP(DMM, "Subscriber equipment doesn't support requested encryption"); + status = GSM_SECURITY_NOAVAIL; + } + + /* If not done yet, try to get info for this user */ + if (status < 0) { + rc = auth_get_tuple_for_subscr(&atuple, subscr, key_seq); + if (rc <= 0) + status = GSM_SECURITY_NOAVAIL; + } + + /* Are we done yet ? */ + if (status >= 0) + return cb ? + cb(GSM_HOOK_RR_SECURITY, status, NULL, conn, cb_data) : + 0; + + /* Start an operation (can't have more than one pending !!!) */ + if (conn->sec_operation) + return -EBUSY; + + allocate_security_operation(conn); + op = conn->sec_operation; + op->cb = cb; + op->cb_data = cb_data; + memcpy(&op->atuple, &atuple, sizeof(struct gsm_auth_tuple)); + + /* FIXME: Should start a timer for completion ... */ + + /* Then do whatever is needed ... */ + if (rc == AUTH_DO_AUTH_THAN_CIPH) { + /* Start authentication */ + return gsm48_tx_mm_auth_req(conn, op->atuple.rand, op->atuple.key_seq); + } else if (rc == AUTH_DO_CIPH) { + /* Start ciphering directly */ + return gsm0808_cipher_mode(conn, net->a5_encryption, + op->atuple.kc, 8, 0); + } + + return -EINVAL; /* not reached */ +} + +static int authorize_subscriber(struct gsm_loc_updating_operation *loc, + struct gsm_subscriber *subscriber) +{ + if (!subscriber) + return 0; + + /* + * Do not send accept yet as more information should arrive. Some + * phones will not send us the information and we will have to check + * what we want to do with that. + */ + if (loc && (loc->waiting_for_imsi || loc->waiting_for_imei)) + return 0; + + switch (subscriber->net->auth_policy) { + case GSM_AUTH_POLICY_CLOSED: + return subscriber->authorized; + case GSM_AUTH_POLICY_TOKEN: + if (subscriber->authorized) + return subscriber->authorized; + return (subscriber->flags & GSM_SUBSCRIBER_FIRST_CONTACT); + case GSM_AUTH_POLICY_ACCEPT_ALL: + return 1; + default: + return 0; + } +} + +static void release_loc_updating_req(struct gsm_subscriber_connection *conn) +{ + if (!conn->loc_operation) + return; + + /* No need to keep the connection up */ + release_anchor(conn); + + osmo_timer_del(&conn->loc_operation->updating_timer); + talloc_free(conn->loc_operation); + conn->loc_operation = NULL; + msc_release_connection(conn); +} + +static void allocate_loc_updating_req(struct gsm_subscriber_connection *conn) +{ + if (conn->loc_operation) + LOGP(DMM, LOGL_ERROR, "Connection already had operation.\n"); + release_loc_updating_req(conn); + + conn->loc_operation = talloc_zero(tall_locop_ctx, + struct gsm_loc_updating_operation); +} + +static int _gsm0408_authorize_sec_cb(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *data, void *param) +{ + struct gsm_subscriber_connection *conn = data; + int rc = 0; + + switch (event) { + case GSM_SECURITY_AUTH_FAILED: + release_loc_updating_req(conn); + break; + + case GSM_SECURITY_NOAVAIL: + case GSM_SECURITY_SUCCEEDED: + /* We're all good */ + db_subscriber_alloc_tmsi(conn->subscr); + rc = gsm0408_loc_upd_acc(conn, conn->subscr->tmsi); + if (conn->bts->network->send_mm_info) { + /* send MM INFO with network name */ + rc = gsm48_tx_mm_info(conn); + } + + /* call subscr_update after putting the loc_upd_acc + * in the transmit queue, since S_SUBSCR_ATTACHED might + * trigger further action like SMS delivery */ + subscr_update(conn->subscr, conn->bts, + GSM_SUBSCRIBER_UPDATE_ATTACHED); + + /* + * The gsm0408_loc_upd_acc sends a MI with the TMSI. The + * MS needs to respond with a TMSI REALLOCATION COMPLETE + * (even if the TMSI is the same). + */ + break; + + default: + rc = -EINVAL; + }; + + return rc; +} + +static int gsm0408_authorize(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + if (authorize_subscriber(conn->loc_operation, conn->subscr)) + return gsm48_secure_channel(conn, + conn->loc_operation->key_seq, + _gsm0408_authorize_sec_cb, NULL); + return 0; +} + +void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) +{ + struct gsm_trans *trans, *temp; + + /* avoid someone issuing a clear */ + conn->in_release = 1; + + /* + * Cancel any outstanding location updating request + * operation taking place on the subscriber connection. + */ + release_loc_updating_req(conn); + + /* We might need to cancel the paging response or such. */ + if (conn->sec_operation && conn->sec_operation->cb) { + conn->sec_operation->cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, + NULL, conn, conn->sec_operation->cb_data); + } + + release_security_operation(conn); + release_anchor(conn); + + /* + * Free all transactions that are associated with the released + * connection. The transaction code will inform the CC or SMS + * facilities that will send the release indications. As part of + * the CC REL_IND the remote leg might be released and this will + * trigger the call to trans_free. This is something the llist + * macro can not handle and we will need to re-iterate the list. + * + * TODO: Move the trans_list into the subscriber connection and + * create a pending list for MT transactions. These exist before + * we have a subscriber connection. + */ +restart: + llist_for_each_entry_safe(trans, temp, &conn->bts->network->trans_list, entry) { + if (trans->conn == conn) { + trans_free(trans); + goto restart; + } + } +} + +void gsm0408_clear_all_trans(struct gsm_network *net, int protocol) +{ + struct gsm_trans *trans, *temp; + + LOGP(DCC, LOGL_NOTICE, "Clearing all currently active transactions!!!\n"); + + llist_for_each_entry_safe(trans, temp, &net->trans_list, entry) { + if (trans->protocol == protocol) { + trans->callref = 0; + trans_free(trans); + } + } +} + +/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */ +int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause) +{ + struct gsm_bts *bts = conn->bts; + struct msgb *msg; + + osmo_counter_inc(bts->network->stats.loc_upd_resp.reject); + + msg = gsm48_create_loc_upd_rej(cause); + if (!msg) { + LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n"); + return -1; + } + + msg->lchan = conn->lchan; + + LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT " + "LAC=%u BTS=%u\n", conn->subscr ? + subscr_name(conn->subscr) : "unknown", + bts->location_area_code, bts->nr); + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +/* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */ +int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, uint32_t tmsi) +{ + struct gsm_bts *bts = conn->bts; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm48_loc_area_id *lai; + uint8_t *mid; + + msg->lchan = conn->lchan; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT; + + lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); + gsm48_generate_lai(lai, bts->network->country_code, + bts->network->network_code, bts->location_area_code); + + mid = msgb_put(msg, GSM48_MID_TMSI_LEN); + gsm48_generate_mid_from_tmsi(mid, tmsi); + + DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n"); + + osmo_counter_inc(bts->network->stats.loc_upd_resp.accept); + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +/* Transmit Chapter 9.2.10 Identity Request */ +static int mm_tx_identity_req(struct gsm_subscriber_connection *conn, uint8_t id_type) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + msg->lchan = conn->lchan; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_ID_REQ; + gh->data[0] = id_type; + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + + +/* Parse Chapter 9.2.11 Identity Response */ +static int mm_rx_id_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm_lchan *lchan = msg->lchan; + struct gsm_bts *bts = lchan->ts->trx->bts; + struct gsm_network *net = bts->network; + uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; + char mi_string[GSM48_MI_SIZE]; + + gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); + DEBUGP(DMM, "IDENTITY RESPONSE: mi_type=0x%02x MI(%s)\n", + 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, mi_string); + if (!conn->subscr) + conn->subscr = db_create_subscriber(net, mi_string); + } + if (conn->loc_operation) + conn->loc_operation->waiting_for_imsi = 0; + break; + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMEISV: + /* update subscribe <-> IMEI mapping */ + if (conn->subscr) { + db_subscriber_assoc_imei(conn->subscr, mi_string); + db_sync_equipment(&conn->subscr->equipment); + } + if (conn->loc_operation) + conn->loc_operation->waiting_for_imei = 0; + break; + } + + /* Check if we can let the mobile station enter */ + return gsm0408_authorize(conn, msg); +} + + +static void loc_upd_rej_cb(void *data) +{ + struct gsm_subscriber_connection *conn = data; + struct gsm_lchan *lchan = conn->lchan; + struct gsm_bts *bts = lchan->ts->trx->bts; + + LOGP(DMM, LOGL_DEBUG, "Location Updating Request procedure timedout.\n"); + gsm0408_loc_upd_rej(conn, bts->network->reject_cause); + release_loc_updating_req(conn); +} + +static void schedule_reject(struct gsm_subscriber_connection *conn) +{ + conn->loc_operation->updating_timer.cb = loc_upd_rej_cb; + conn->loc_operation->updating_timer.data = conn; + osmo_timer_schedule(&conn->loc_operation->updating_timer, 5, 0); +} + +static const char *lupd_name(uint8_t type) +{ + switch (type) { + case GSM48_LUPD_NORMAL: + return "NORMAL"; + case GSM48_LUPD_PERIODIC: + return "PEROIDOC"; + case GSM48_LUPD_IMSI_ATT: + return "IMSI ATTACH"; + default: + return "UNKNOWN"; + } +} + +/* Chapter 9.2.15: Receive Location Updating Request */ +static int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_loc_upd_req *lu; + struct gsm_subscriber *subscr = NULL; + struct gsm_bts *bts = conn->bts; + 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_type=0x%02x MI(%s) type=%s ", mi_type, mi_string, + lupd_name(lu->type)); + + osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len); + + switch (lu->type) { + case GSM48_LUPD_NORMAL: + osmo_counter_inc(bts->network->stats.loc_upd_type.normal); + break; + case GSM48_LUPD_IMSI_ATT: + osmo_counter_inc(bts->network->stats.loc_upd_type.attach); + break; + case GSM48_LUPD_PERIODIC: + osmo_counter_inc(bts->network->stats.loc_upd_type.periodic); + break; + } + + /* + * Pseudo Spoof detection: Just drop a second/concurrent + * location updating request. + */ + if (conn->loc_operation) { + DEBUGPC(DMM, "ignoring request due an existing one: %p.\n", + conn->loc_operation); + gsm0408_loc_upd_rej(conn, GSM48_REJECT_PROTOCOL_ERROR); + return 0; + } + + allocate_loc_updating_req(conn); + + conn->loc_operation->key_seq = lu->key_seq; + + switch (mi_type) { + case GSM_MI_TYPE_IMSI: + DEBUGPC(DMM, "\n"); + /* we always want the IMEI, too */ + mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI); + conn->loc_operation->waiting_for_imei = 1; + + /* look up subscriber based on IMSI, create if not found */ + subscr = subscr_get_by_imsi(bts->network, mi_string); + if (!subscr) { + subscr = db_create_subscriber(bts->network, mi_string); + } + break; + case GSM_MI_TYPE_TMSI: + DEBUGPC(DMM, "\n"); + /* look up the subscriber based on TMSI, request IMSI if it fails */ + subscr = subscr_get_by_tmsi(bts->network, + tmsi_from_string(mi_string)); + if (!subscr) { + /* send IDENTITY REQUEST message to get IMSI */ + mm_tx_identity_req(conn, GSM_MI_TYPE_IMSI); + conn->loc_operation->waiting_for_imsi = 1; + } + /* we always want the IMEI, too */ + mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI); + conn->loc_operation->waiting_for_imei = 1; + break; + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMEISV: + /* no sim card... FIXME: what to do ? */ + DEBUGPC(DMM, "unimplemented mobile identity type\n"); + break; + default: + DEBUGPC(DMM, "unknown mobile identity type\n"); + break; + } + + /* schedule the reject timer */ + schedule_reject(conn); + + if (!subscr) { + DEBUGPC(DRR, "<- Can't find any subscriber for this ID\n"); + /* FIXME: request id? close channel? */ + return -EINVAL; + } + + conn->subscr = subscr; + conn->subscr->equipment.classmark1 = lu->classmark1; + + /* check if we can let the subscriber into our network immediately + * or if we need to wait for identity responses. */ + return gsm0408_authorize(conn, msg); +} + +/* Turn int into semi-octet representation: 98 => 0x89 */ +static uint8_t bcdify(uint8_t value) +{ + uint8_t ret; + + ret = value / 10; + ret |= (value % 10) << 4; + + return ret; +} + + +/* Section 9.2.15a */ +int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm_network *net = conn->bts->network; + struct gsm_bts *bts = conn->bts; + uint8_t *ptr8; + int name_len, name_pad; + + time_t cur_t; + struct tm* gmt_time; + struct tm* local_time; + int tzunits; + + 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(ptr8, net->name_long); + + } + + 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(ptr8, net->name_short); + + } + + /* Section 10.5.3.9 */ + cur_t = time(NULL); + gmt_time = gmtime(&cur_t); + + ptr8 = msgb_put(msg, 8); + ptr8[0] = GSM48_IE_NET_TIME_TZ; + ptr8[1] = bcdify(gmt_time->tm_year % 100); + ptr8[2] = bcdify(gmt_time->tm_mon + 1); + ptr8[3] = bcdify(gmt_time->tm_mday); + ptr8[4] = bcdify(gmt_time->tm_hour); + ptr8[5] = bcdify(gmt_time->tm_min); + ptr8[6] = bcdify(gmt_time->tm_sec); + + if (bts->tz.override) { + /* Convert tz.hr and tz.mn to units */ + if (bts->tz.hr < 0) { + tzunits = ((bts->tz.hr/-1)*4); + tzunits = tzunits + (bts->tz.mn/15); + ptr8[7] = bcdify(tzunits); + /* Set negative time */ + ptr8[7] |= 0x08; + } + else { + tzunits = bts->tz.hr*4; + tzunits = tzunits + (bts->tz.mn/15); + ptr8[7] = bcdify(tzunits); + } + } + 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); + } + + DEBUGP(DMM, "-> MM INFO\n"); + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +/* Section 9.2.2 */ +int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, uint8_t *rand, int key_seq) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_auth_req *ar = (struct gsm48_auth_req *) msgb_put(msg, sizeof(*ar)); + + DEBUGP(DMM, "-> AUTH REQ (rand = %s)\n", osmo_hexdump(rand, 16)); + + msg->lchan = conn->lchan; + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_AUTH_REQ; + + ar->key_seq = key_seq; + + /* 16 bytes RAND parameters */ + if (rand) + memcpy(ar->rand, rand, 16); + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +/* Section 9.2.1 */ +int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn) +{ + DEBUGP(DMM, "-> AUTH REJECT\n"); + return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_AUTH_REJ); +} + +static int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn) +{ + DEBUGP(DMM, "-> CM SERVICE ACK\n"); + return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_ACC); +} + +/* 9.2.6 CM service reject */ +static int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, + enum gsm48_reject_value value) +{ + struct msgb *msg; + + msg = gsm48_create_mm_serv_rej(value); + if (!msg) { + LOGP(DMM, LOGL_ERROR, "Failed to allocate CM Service Reject.\n"); + return -1; + } + + DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value); + msg->lchan = conn->lchan; + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +static int _gsm48_rx_mm_serv_req_sec_cb( + unsigned int hooknum, unsigned int event, + struct msgb *msg, void *data, void *param) +{ + struct gsm_subscriber_connection *conn = data; + int rc = 0; + + /* 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); + break; + + case GSM_SECURITY_SUCCEEDED: + /* nothing to do. CIPHER MODE COMMAND is + * implicit CM SERV ACK */ + break; + + default: + rc = -EINVAL; + }; + + return rc; +} + +/* + * Handle CM Service Requests + * a) Verify that the packet is long enough to contain the information + * we require otherwsie reject with INCORRECT_MESSAGE + * b) Try to parse the TMSI. If we do not have one reject + * c) Check that we know the subscriber with the TMSI otherwise reject + * with a HLR cause + * d) Set the subscriber on the gsm_lchan and accept + */ +static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + uint8_t mi_type; + char mi_string[GSM48_MI_SIZE]; + + struct gsm_bts *bts = conn->bts; + struct gsm_subscriber *subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_service_request *req = + (struct gsm48_service_request *)gh->data; + /* unfortunately in Phase1 the classmark2 length is variable */ + uint8_t classmark2_len = gh->data[1]; + uint8_t *classmark2 = gh->data+2; + uint8_t mi_len = *(classmark2 + classmark2_len); + uint8_t *mi = (classmark2 + classmark2_len + 1); + + DEBUGP(DMM, "<- CM SERVICE REQUEST "); + if (msg->data_len < sizeof(struct gsm48_service_request*)) { + DEBUGPC(DMM, "wrong sized message\n"); + return gsm48_tx_mm_serv_rej(conn, + GSM48_REJECT_INCORRECT_MESSAGE); + } + + if (msg->data_len < req->mi_len + 6) { + DEBUGPC(DMM, "does not fit in packet\n"); + return gsm48_tx_mm_serv_rej(conn, + GSM48_REJECT_INCORRECT_MESSAGE); + } + + mi_type = mi[0] & GSM_MI_TYPE_MASK; + if (mi_type != GSM_MI_TYPE_TMSI) { + DEBUGPC(DMM, "mi_type is not TMSI: %d\n", mi_type); + return gsm48_tx_mm_serv_rej(conn, + GSM48_REJECT_INCORRECT_MESSAGE); + } + + gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); + DEBUGPC(DMM, "serv_type=0x%02x mi_type=0x%02x M(%s)\n", + req->cm_service_type, mi_type, mi_string); + + osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len)); + + if (is_siemens_bts(bts)) + send_siemens_mrpci(msg->lchan, classmark2-1); + + subscr = subscr_get_by_tmsi(bts->network, + tmsi_from_string(mi_string)); + + /* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */ + if (!subscr) + return gsm48_tx_mm_serv_rej(conn, + GSM48_REJECT_IMSI_UNKNOWN_IN_HLR); + if (subscr->lac == GSM_LAC_RESERVED_DETACHED) + /* If the subscriber is not attached, reject service */ + return gsm48_tx_mm_serv_rej(conn, + GSM48_REJECT_IMSI_UNKNOWN_IN_VLR); + + if (!conn->subscr) + conn->subscr = subscr; + else if (conn->subscr == subscr) + subscr_put(subscr); /* lchan already has a ref, don't need another one */ + else { + DEBUGP(DMM, "<- CM Channel already owned by someone else?\n"); + subscr_put(subscr); + } + + subscr->equipment.classmark2_len = classmark2_len; + memcpy(subscr->equipment.classmark2, classmark2, classmark2_len); + db_sync_equipment(&subscr->equipment); + + /* we will send a MM message soon */ + conn->expire_timer_stopped = 1; + + return gsm48_secure_channel(conn, req->cipher_key_seq, + _gsm48_rx_mm_serv_req_sec_cb, NULL); +} + +static int gsm48_rx_mm_imsi_detach_ind(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm_bts *bts = conn->bts; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_imsi_detach_ind *idi = + (struct gsm48_imsi_detach_ind *) gh->data; + uint8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK; + char mi_string[GSM48_MI_SIZE]; + struct gsm_subscriber *subscr = NULL; + + gsm48_mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len); + DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s)", + mi_type, mi_string); + + osmo_counter_inc(bts->network->stats.loc_upd_type.detach); + + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + DEBUGPC(DMM, "\n"); + subscr = subscr_get_by_tmsi(bts->network, + tmsi_from_string(mi_string)); + break; + case GSM_MI_TYPE_IMSI: + DEBUGPC(DMM, "\n"); + subscr = subscr_get_by_imsi(bts->network, mi_string); + break; + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMEISV: + /* no sim card... FIXME: what to do ? */ + DEBUGPC(DMM, ": unimplemented mobile identity type\n"); + break; + default: + DEBUGPC(DMM, ": unknown mobile identity type\n"); + break; + } + + if (subscr) { + subscr_update(subscr, bts, + GSM_SUBSCRIBER_UPDATE_DETACHED); + DEBUGP(DMM, "Subscriber: %s\n", subscr_name(subscr)); + + subscr->equipment.classmark1 = idi->classmark1; + db_sync_equipment(&subscr->equipment); + + subscr_put(subscr); + } else + DEBUGP(DMM, "Unknown Subscriber ?!?\n"); + + /* FIXME: iterate over all transactions and release them, + * imagine an IMSI DETACH happening during an active call! */ + + release_anchor(conn); + return 0; +} + +static int gsm48_rx_mm_status(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DMM, "MM STATUS (reject cause 0x%02x)\n", gh->data[0]); + + return 0; +} + +/* Chapter 9.2.3: Authentication Response */ +static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_auth_resp *ar = (struct gsm48_auth_resp*) gh->data; + struct gsm_network *net = conn->bts->network; + + DEBUGP(DMM, "MM AUTHENTICATION RESPONSE (sres = %s): ", + osmo_hexdump(ar->sres, 4)); + + /* Safety check */ + if (!conn->sec_operation) { + DEBUGP(DMM, "No authentication/cipher operation in progress !!!\n"); + return -EIO; + } + + /* Validate SRES */ + if (memcmp(conn->sec_operation->atuple.sres, ar->sres,4)) { + int rc; + gsm_cbfn *cb = conn->sec_operation->cb; + + DEBUGPC(DMM, "Invalid (expected %s)\n", + osmo_hexdump(conn->sec_operation->atuple.sres, 4)); + + if (cb) + cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, + NULL, conn, conn->sec_operation->cb_data); + + rc = gsm48_tx_mm_auth_rej(conn); + release_security_operation(conn); + return rc; + } + + DEBUGPC(DMM, "OK\n"); + + /* Start ciphering */ + return gsm0808_cipher_mode(conn, net->a5_encryption, + conn->sec_operation->atuple.kc, 8, 0); +} + +/* Receive a GSM 04.08 Mobility Management (MM) message */ +static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + int rc = 0; + + switch (gh->msg_type & 0xbf) { + case GSM48_MT_MM_LOC_UPD_REQUEST: + DEBUGP(DMM, "LOCATION UPDATING REQUEST: "); + rc = mm_rx_loc_upd_req(conn, msg); + break; + case GSM48_MT_MM_ID_RESP: + rc = mm_rx_id_resp(conn, msg); + break; + case GSM48_MT_MM_CM_SERV_REQ: + rc = gsm48_rx_mm_serv_req(conn, msg); + break; + case GSM48_MT_MM_STATUS: + rc = gsm48_rx_mm_status(msg); + break; + case GSM48_MT_MM_TMSI_REALL_COMPL: + DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n", + conn->subscr ? + subscr_name(conn->subscr) : + "unknown subscriber"); + release_loc_updating_req(conn); + break; + case GSM48_MT_MM_IMSI_DETACH_IND: + rc = gsm48_rx_mm_imsi_detach_ind(conn, msg); + break; + case GSM48_MT_MM_CM_REEST_REQ: + DEBUGP(DMM, "CM REESTABLISH REQUEST: Not implemented\n"); + break; + case GSM48_MT_MM_AUTH_RESP: + rc = gsm48_rx_mm_auth_resp(conn, msg); + break; + default: + LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n", + gh->msg_type); + break; + } + + return rc; +} + +/* Receive a PAGING RESPONSE message from the MS */ +static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm_bts *bts = conn->bts; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_pag_resp *resp; + uint8_t *classmark2_lv = gh->data + 1; + uint8_t mi_type; + char mi_string[GSM48_MI_SIZE]; + struct gsm_subscriber *subscr = NULL; + int rc = 0; + + resp = (struct gsm48_pag_resp *) &gh->data[0]; + gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), + mi_string, &mi_type); + DEBUGP(DRR, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n", + mi_type, mi_string); + + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + subscr = subscr_get_by_tmsi(bts->network, + tmsi_from_string(mi_string)); + break; + case GSM_MI_TYPE_IMSI: + subscr = subscr_get_by_imsi(bts->network, mi_string); + break; + } + + if (!subscr) { + DEBUGP(DRR, "<- Can't find any subscriber for this ID\n"); + /* FIXME: request id? close channel? */ + return -EINVAL; + } + DEBUGP(DRR, "<- Channel was requested by %s\n", + subscr->name && strlen(subscr->name) ? subscr->name : subscr->imsi); + + subscr->equipment.classmark2_len = *classmark2_lv; + memcpy(subscr->equipment.classmark2, classmark2_lv+1, *classmark2_lv); + db_sync_equipment(&subscr->equipment); + + /* We received a paging */ + conn->expire_timer_stopped = 1; + + rc = gsm48_handle_paging_resp(conn, msg, subscr); + return rc; +} + +static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t apdu_id_flags; + uint8_t apdu_len; + uint8_t *apdu_data; + + apdu_id_flags = gh->data[0]; + apdu_len = gh->data[1]; + apdu_data = gh->data+2; + + DEBUGP(DRR, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s", + apdu_id_flags, apdu_len, osmo_hexdump(apdu_data, apdu_len)); + + return db_apdu_blob_store(conn->subscr, apdu_id_flags, apdu_len, apdu_data); +} + +/* Receive a GSM 04.08 Radio Resource (RR) message */ +static int gsm0408_rcv_rr(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + int rc = 0; + + switch (gh->msg_type) { + case GSM48_MT_RR_PAG_RESP: + rc = gsm48_rx_rr_pag_resp(conn, msg); + break; + case GSM48_MT_RR_APP_INFO: + rc = gsm48_rx_rr_app_info(conn, msg); + break; + default: + LOGP(DRR, LOGL_NOTICE, "MSC: Unimplemented " + "GSM 04.08 RR msg type 0x%02x\n", gh->msg_type); + break; + } + + return rc; +} + +int gsm48_send_rr_app_info(struct gsm_subscriber_connection *conn, uint8_t apdu_id, + uint8_t apdu_len, const uint8_t *apdu) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + msg->lchan = conn->lchan; + + DEBUGP(DRR, "TX APPLICATION INFO id=0x%02x, len=%u\n", + apdu_id, apdu_len); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2 + apdu_len); + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_APP_INFO; + gh->data[0] = apdu_id; + gh->data[1] = apdu_len; + memcpy(gh->data+2, apdu, apdu_len); + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +/* Call Control */ + +/* The entire call control code is written in accordance with Figure 7.10c + * for 'very early assignment', i.e. we allocate a TCH/F during IMMEDIATE + * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY + * it for voice */ + +static void new_cc_state(struct gsm_trans *trans, int state) +{ + if (state > 31 || state < 0) + return; + + DEBUGP(DCC, "new state %s -> %s\n", + gsm48_cc_state_name(trans->cc.state), + gsm48_cc_state_name(state)); + + trans->cc.state = state; +} + +static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + uint8_t *cause, *call_state; + + gh->msg_type = GSM48_MT_CC_STATUS; + + cause = msgb_put(msg, 3); + cause[0] = 2; + cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER; + cause[2] = 0x80 | 30; /* response to status inquiry */ + + call_state = msgb_put(msg, 1); + call_state[0] = 0xc0 | 0x00; + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_tx_simple(struct gsm_subscriber_connection *conn, + uint8_t pdisc, uint8_t msg_type) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + msg->lchan = conn->lchan; + + gh->proto_discr = pdisc; + gh->msg_type = msg_type; + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +static void gsm48_stop_cc_timer(struct gsm_trans *trans) +{ + if (osmo_timer_pending(&trans->cc.timer)) { + DEBUGP(DCC, "stopping pending timer T%x\n", trans->cc.Tcurrent); + osmo_timer_del(&trans->cc.timer); + trans->cc.Tcurrent = 0; + } +} + +static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans, + int msg_type, struct gsm_mncc *mncc) +{ + struct msgb *msg; + unsigned char *data; + + if (trans) + if (trans->conn && trans->conn->lchan) + DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " + "Sending '%s' to MNCC.\n", + trans->conn->lchan->ts->trx->bts->nr, + trans->conn->lchan->ts->trx->nr, + trans->conn->lchan->ts->nr, trans->transaction_id, + (trans->subscr)?(trans->subscr->extension):"-", + get_mncc_name(msg_type)); + else + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Sending '%s' to MNCC.\n", + (trans->subscr)?(trans->subscr->extension):"-", + get_mncc_name(msg_type)); + else + DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) " + "Sending '%s' to MNCC.\n", get_mncc_name(msg_type)); + + mncc->msg_type = msg_type; + + msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC"); + if (!msg) + return -ENOMEM; + + data = msgb_put(msg, sizeof(struct gsm_mncc)); + memcpy(data, mncc, sizeof(struct gsm_mncc)); + + cc_tx_to_mncc(net, msg); + + return 0; +} + +int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans, + uint32_t callref, int location, int value) +{ + struct gsm_mncc rel; + + memset(&rel, 0, sizeof(rel)); + rel.callref = callref; + mncc_set_cause(&rel, location, value); + if (trans && trans->cc.state == GSM_CSTATE_RELEASE_REQ) + return mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); + return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); +} + +/* Call Control Specific transaction release. + * gets called by trans_free, DO NOT CALL YOURSELF! */ +void _gsm48_cc_trans_free(struct gsm_trans *trans) +{ + gsm48_stop_cc_timer(trans); + + /* send release to L4, if callref still exists */ + if (trans->callref) { + /* Ressource unavailable */ + mncc_release_ind(trans->subscr->net, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + } + if (trans->cc.state != GSM_CSTATE_NULL) + new_cc_state(trans, GSM_CSTATE_NULL); + if (trans->conn) + trau_mux_unmap(&trans->conn->lchan->ts->e1_link, trans->callref); +} + +static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg); + +/* call-back from paging the B-end of the connection */ +static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *_conn, void *param) +{ + int found = 0; + struct gsm_subscriber_connection *conn = _conn; + struct gsm_network **paging_request = param, *net; + struct gsm_trans *transt, *tmp; + + if (hooknum != GSM_HOOK_RR_PAGING) + return -EINVAL; + + net = *paging_request; + if (!net) { + DEBUGP(DCC, "Error Network not set!\n"); + return -EINVAL; + } + + /* check all tranactions (without lchan) for subscriber */ + llist_for_each_entry_safe(transt, tmp, &net->trans_list, entry) { + if (transt->paging_request != paging_request || transt->conn) + continue; + switch (event) { + case GSM_PAGING_SUCCEEDED: + if (!conn) // paranoid + break; + DEBUGP(DCC, "Paging subscr %s succeeded!\n", + transt->subscr->extension); + found = 1; + /* Assign lchan */ + if (!transt->conn) { + transt->paging_request = NULL; + transt->conn = conn; + conn->put_channel = 1; + } + /* send SETUP request to called party */ + gsm48_cc_tx_setup(transt, &transt->cc.msg); + break; + case GSM_PAGING_EXPIRED: + case GSM_PAGING_BUSY: + DEBUGP(DCC, "Paging subscr %s expired!\n", + transt->subscr->extension); + /* Temporarily out of order */ + found = 1; + mncc_release_ind(transt->subscr->net, transt, + transt->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_DEST_OOO); + transt->callref = 0; + transt->paging_request = NULL; + trans_free(transt); + break; + } + } + + talloc_free(paging_request); + + /* + * FIXME: The queue needs to be kicked. This is likely to go through a RF + * failure and then the subscr will be poke again. This needs a lot of fixing + * in the subscriber queue code. + */ + if (!found && conn) + conn->put_channel = 1; + return 0; +} + +static int tch_recv_mncc(struct gsm_network *net, 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; + + 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; +} + +/* 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, *old_lchan; + int rc; + struct gsm_network *net; + struct gsm_trans *trans; + + if (subsys != SS_ABISIP) + return 0; + + /* in case we use direct BTS-to-BTS RTP */ + if (ipacc_rtp_direct) + return 0; + + switch (signal) { + case S_ABISIP_CRCX_ACK: + /* in case we don't use direct BTS-to-BTS RTP */ + /* the BTS has successfully bound a TCH to a local ip/port, + * which means we can connect our UDP socket to it */ + if (lchan->abis_ip.rtp_socket) { + rtp_socket_free(lchan->abis_ip.rtp_socket); + lchan->abis_ip.rtp_socket = NULL; + } + + lchan->abis_ip.rtp_socket = rtp_socket_create(); + if (!lchan->abis_ip.rtp_socket) + return -EIO; + + rc = rtp_socket_connect(lchan->abis_ip.rtp_socket, + lchan->abis_ip.bound_ip, + lchan->abis_ip.bound_port); + if (rc < 0) + return -EIO; + + /* check if any transactions on this lchan still have + * a tch_recv_mncc request pending */ + net = lchan->ts->trx->bts->network; + llist_for_each_entry(trans, &net->trans_list, entry) { + if (trans->conn && trans->conn->lchan == lchan && trans->tch_recv) { + DEBUGP(DCC, "pending tch_recv_mncc request\n"); + tch_recv_mncc(net, trans->callref, 1); + } + } + + /* + * 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. + */ + old_lchan = bsc_handover_pending(lchan); + if (old_lchan) + switch_for_handover(old_lchan, lchan); + break; + case S_ABISIP_DLCX_IND: + /* the BTS tells us a RTP stream has been disconnected */ + if (lchan->abis_ip.rtp_socket) { + rtp_socket_free(lchan->abis_ip.rtp_socket); + lchan->abis_ip.rtp_socket = NULL; + } + + break; + } + + return 0; +} + +/* map two ipaccess RTP streams onto each other */ +static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan) +{ + struct gsm_bts *bts = lchan->ts->trx->bts; + struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts; + int rc; + + DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n", + bts->nr, lchan->ts->trx->nr, lchan->ts->nr, + remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr); + + if (bts->type != remote_bts->type) { + LOGP(DCC, LOGL_ERROR, "Cannot switch calls between different BTS types yet\n"); + return -EINVAL; + } + + // todo: map between different bts types + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_OSMO_SYSMO: + if (!ipacc_rtp_direct) { + /* connect the TCH's to our RTP proxy */ + rc = rsl_ipacc_mdcx_to_rtpsock(lchan); + if (rc < 0) + return rc; + rc = rsl_ipacc_mdcx_to_rtpsock(remote_lchan); + if (rc < 0) + return rc; + /* connect them with each other */ + rtp_socket_proxy(lchan->abis_ip.rtp_socket, + remote_lchan->abis_ip.rtp_socket); + } else { + /* directly connect TCH RTP streams to each other */ + rc = rsl_ipacc_mdcx(lchan, remote_lchan->abis_ip.bound_ip, + remote_lchan->abis_ip.bound_port, + remote_lchan->abis_ip.rtp_payload2); + if (rc < 0) + return rc; + rc = rsl_ipacc_mdcx(remote_lchan, lchan->abis_ip.bound_ip, + lchan->abis_ip.bound_port, + lchan->abis_ip.rtp_payload2); + } + break; + case GSM_BTS_TYPE_BS11: + case GSM_BTS_TYPE_RBS2000: + case GSM_BTS_TYPE_NOKIA_SITE: + trau_mux_map_lchan(lchan, remote_lchan); + break; + default: + LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type); + return -EINVAL; + } + + return 0; +} + +/* bridge channels of two transactions */ +static int tch_bridge(struct gsm_network *net, uint32_t *refs) +{ + struct gsm_trans *trans1 = trans_find_by_callref(net, refs[0]); + struct gsm_trans *trans2 = trans_find_by_callref(net, refs[1]); + + if (!trans1 || !trans2) + return -EIO; + + if (!trans1->conn || !trans2->conn) + return -EIO; + + /* 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; + lchan = trans->conn->lchan; + bts = lchan->ts->trx->bts; + + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_OSMO_SYSMO: + if (ipacc_rtp_direct) { + LOGP(DCC, LOGL_ERROR, "Error: RTP proxy is disabled\n"); + return -EINVAL; + } + /* in case, we don't have a RTP socket yet, we note this + * in the transaction and try later */ + if (!lchan->abis_ip.rtp_socket) { + trans->tch_recv = enable; + DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); + return 0; + } + if (enable) { + /* connect the TCH's to our RTP proxy */ + rc = rsl_ipacc_mdcx_to_rtpsock(lchan); + if (rc < 0) + return rc; + /* assign socket to application interface */ + rtp_socket_upstream(lchan->abis_ip.rtp_socket, + net, callref); + } else + rtp_socket_upstream(lchan->abis_ip.rtp_socket, + net, 0); + break; + case GSM_BTS_TYPE_BS11: + case GSM_BTS_TYPE_RBS2000: + case GSM_BTS_TYPE_NOKIA_SITE: + 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->subscr->net, trans, trans->callref, + l4_location, l4_cause); + trans->callref = 0; + } + + if (disconnect && trans->callref) { + /* process disconnect towards layer 4 */ + mncc_set_cause(&l4_rel, l4_location, l4_cause); + mncc_recvmsg(trans->subscr->net, trans, MNCC_DISC_IND, &l4_rel); + } + + /* process disconnect towards mobile station */ + if (disconnect || release) { + mncc_set_cause(&mo_rel, mo_location, mo_cause); + mo_rel.cause.diag[0] = ((trans->cc.Tcurrent & 0xf00) >> 8) + '0'; + mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0'; + mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0'; + mo_rel.cause.diag_len = 3; + + if (disconnect) + gsm48_cc_tx_disconnect(trans, &mo_rel); + if (release) + gsm48_cc_tx_release(trans, &mo_rel); + } + +} + +static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, + int sec, int micro) +{ + DEBUGP(DCC, "starting timer T%x with %d seconds\n", current, sec); + trans->cc.timer.cb = gsm48_cc_timeout; + trans->cc.timer.data = trans; + osmo_timer_schedule(&trans->cc.timer, sec, micro); + trans->cc.Tcurrent = current; +} + +static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t msg_type = gh->msg_type & 0xbf; + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc setup; + + memset(&setup, 0, sizeof(struct gsm_mncc)); + setup.callref = trans->callref; + if (trans->conn && trans->conn->lchan) + setup.lchan_type = trans->conn->lchan->type; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* emergency setup is identified by msg_type */ + if (msg_type == GSM48_MT_CC_EMERG_SETUP) + setup.emergency = 1; + + /* use subscriber as calling party number */ + setup.fields |= MNCC_F_CALLING; + strncpy(setup.calling.number, trans->subscr->extension, + sizeof(setup.calling.number)-1); + strncpy(setup.imsi, trans->subscr->imsi, + sizeof(setup.imsi)-1); + + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + setup.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&setup.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + setup.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&setup.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* called party bcd number */ + if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { + setup.fields |= MNCC_F_CALLED; + gsm48_decode_called(&setup.called, + TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + setup.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&setup.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + setup.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&setup.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + /* CLIR suppression */ + if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_SUPP)) + setup.clir.sup = 1; + /* CLIR invocation */ + if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_INVOC)) + setup.clir.inv = 1; + /* cc cap */ + if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { + setup.fields |= MNCC_F_CCCAP; + gsm48_decode_cccap(&setup.cccap, + TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); + } + + new_cc_state(trans, GSM_CSTATE_INITIATED); + + LOGP(DCC, LOGL_INFO, "Subscriber %s (%s) sends SETUP to %s\n", + subscr_name(trans->subscr), trans->subscr->extension, + setup.called.number); + + osmo_counter_inc(trans->subscr->net->stats.call.mo_setup); + + /* indicate setup to MNCC */ + mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_IND, &setup); + + /* MNCC code will modify the channel asynchronously, we should + * ipaccess-bind only after the modification has been made to the + * lchan->tch_mode */ + return 0; +} + +static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm_mncc *setup = arg; + int rc, trans_id; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + /* transaction id must not be assigned */ + if (trans->transaction_id != 0xff) { /* unasssigned */ + DEBUGP(DCC, "TX Setup with assigned transaction. " + "This is not allowed!\n"); + /* Temporarily out of order */ + rc = mncc_release_ind(trans->subscr->net, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + trans->callref = 0; + trans_free(trans); + return rc; + } + + /* Get free transaction_id */ + trans_id = trans_assign_trans_id(trans->subscr, GSM48_PDISC_CC, 0); + if (trans_id < 0) { + /* no free transaction ID */ + rc = mncc_release_ind(trans->subscr->net, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + trans->callref = 0; + trans_free(trans); + return rc; + } + trans->transaction_id = trans_id; + + gh->msg_type = GSM48_MT_CC_SETUP; + + gsm48_start_cc_timer(trans, 0x303, GSM48_T303); + + /* bearer capability */ + if (setup->fields & MNCC_F_BEARER_CAP) + gsm48_encode_bearer_cap(msg, 0, &setup->bearer_cap); + /* facility */ + if (setup->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &setup->facility); + /* progress */ + if (setup->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &setup->progress); + /* calling party BCD number */ + if (setup->fields & MNCC_F_CALLING) + gsm48_encode_calling(msg, &setup->calling); + /* called party BCD number */ + if (setup->fields & MNCC_F_CALLED) + gsm48_encode_called(msg, &setup->called); + /* user-user */ + if (setup->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &setup->useruser); + /* redirecting party BCD number */ + if (setup->fields & MNCC_F_REDIRECTING) + gsm48_encode_redirecting(msg, &setup->redirecting); + /* signal */ + if (setup->fields & MNCC_F_SIGNAL) + gsm48_encode_signal(msg, setup->signal); + + new_cc_state(trans, GSM_CSTATE_CALL_PRESENT); + + osmo_counter_inc(trans->subscr->net->stats.call.mt_setup); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc call_conf; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x310, GSM48_T310); + + memset(&call_conf, 0, sizeof(struct gsm_mncc)); + call_conf.callref = trans->callref; + if (trans->conn && trans->conn->lchan) + 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); + } + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + call_conf.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&call_conf.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* cc cap */ + if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { + call_conf.fields |= MNCC_F_CCCAP; + gsm48_decode_cccap(&call_conf.cccap, + TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); + } + + new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_CALL_CONF_IND, + &call_conf); +} + +static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *proceeding = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CALL_PROC; + + new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC); + + /* bearer capability */ + if (proceeding->fields & MNCC_F_BEARER_CAP) + gsm48_encode_bearer_cap(msg, 0, &proceeding->bearer_cap); + /* facility */ + if (proceeding->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &proceeding->facility); + /* progress */ + if (proceeding->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &proceeding->progress); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc alerting; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x301, GSM48_T301); + + memset(&alerting, 0, sizeof(struct gsm_mncc)); + alerting.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + alerting.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&alerting.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + alerting.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&alerting.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + alerting.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&alerting.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_ALERT_IND, + &alerting); +} + +static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *alerting = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_ALERTING; + + /* facility */ + if (alerting->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &alerting->facility); + /* progress */ + if (alerting->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &alerting->progress); + /* user-user */ + if (alerting->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &alerting->useruser); + + new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *progress = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_PROGRESS; + + /* progress */ + gsm48_encode_progress(msg, 1, &progress->progress); + /* user-user */ + if (progress->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &progress->useruser); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *connect = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CONNECT; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x313, GSM48_T313); + + /* facility */ + if (connect->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &connect->facility); + /* progress */ + if (connect->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &connect->progress); + /* connected number */ + if (connect->fields & MNCC_F_CONNECTED) + gsm48_encode_connected(msg, &connect->connected); + /* user-user */ + if (connect->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &connect->useruser); + + new_cc_state(trans, GSM_CSTATE_CONNECT_IND); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc connect; + + gsm48_stop_cc_timer(trans); + + memset(&connect, 0, sizeof(struct gsm_mncc)); + connect.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* use subscriber as connected party number */ + connect.fields |= MNCC_F_CONNECTED; + strncpy(connect.connected.number, trans->subscr->extension, + sizeof(connect.connected.number)-1); + strncpy(connect.imsi, trans->subscr->imsi, + sizeof(connect.imsi)-1); + + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + connect.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&connect.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + connect.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&connect.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + connect.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&connect.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST); + osmo_counter_inc(trans->subscr->net->stats.call.mt_connect); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_CNF, &connect); +} + + +static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc connect_ack; + + gsm48_stop_cc_timer(trans); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + osmo_counter_inc(trans->subscr->net->stats.call.mo_connect_ack); + + memset(&connect_ack, 0, sizeof(struct gsm_mncc)); + connect_ack.callref = trans->callref; + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_COMPL_IND, + &connect_ack); +} + +static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CONNECT_ACK; + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc disc; + + gsm48_stop_cc_timer(trans); + + new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ); + + memset(&disc, 0, sizeof(struct gsm_mncc)); + disc.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + disc.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&disc.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + disc.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&disc.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + disc.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&disc.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + disc.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&disc.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_DISC_IND, &disc); + +} + +static struct gsm_mncc_cause default_cause = { + .location = GSM48_CAUSE_LOC_PRN_S_LU, + .coding = 0, + .rec = 0, + .rec_val = 0, + .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, + .diag_len = 0, + .diag = { 0 }, +}; + +static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *disc = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_DISCONNECT; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x306, GSM48_T306); + + /* cause */ + if (disc->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 1, &disc->cause); + else + gsm48_encode_cause(msg, 1, &default_cause); + + /* facility */ + if (disc->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &disc->facility); + /* progress */ + if (disc->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &disc->progress); + /* user-user */ + if (disc->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &disc->useruser); + + /* store disconnect cause for T306 expiry */ + memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc)); + + new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc rel; + int rc; + + gsm48_stop_cc_timer(trans); + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + rel.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&rel.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + rel.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&rel.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + rel.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&rel.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + rel.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&rel.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) { + /* release collision 5.4.5 */ + rc = mncc_recvmsg(trans->subscr->net, trans, MNCC_REL_CNF, &rel); + } else { + rc = gsm48_tx_simple(trans->conn, + GSM48_PDISC_CC | (trans->transaction_id << 4), + GSM48_MT_CC_RELEASE_COMPL); + rc = mncc_recvmsg(trans->subscr->net, trans, MNCC_REL_IND, &rel); + } + + new_cc_state(trans, GSM_CSTATE_NULL); + + trans->callref = 0; + trans_free(trans); + + return rc; +} + +static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *rel = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RELEASE; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x308, GSM48_T308); + + /* cause */ + if (rel->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 0, &rel->cause); + /* facility */ + if (rel->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &rel->facility); + /* user-user */ + if (rel->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &rel->useruser); + + trans->cc.T308_second = 0; + memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc)); + + if (trans->cc.state != GSM_CSTATE_RELEASE_REQ) + new_cc_state(trans, GSM_CSTATE_RELEASE_REQ); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc rel; + int rc = 0; + + gsm48_stop_cc_timer(trans); + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + rel.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&rel.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + rel.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&rel.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + rel.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&rel.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + rel.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&rel.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + if (trans->callref) { + switch (trans->cc.state) { + case GSM_CSTATE_CALL_PRESENT: + rc = mncc_recvmsg(trans->subscr->net, trans, + MNCC_REJ_IND, &rel); + break; + case GSM_CSTATE_RELEASE_REQ: + rc = mncc_recvmsg(trans->subscr->net, trans, + MNCC_REL_CNF, &rel); + break; + default: + rc = mncc_recvmsg(trans->subscr->net, trans, + MNCC_REL_IND, &rel); + } + } + + trans->callref = 0; + trans_free(trans); + + return rc; +} + +static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *rel = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + int ret; + + gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; + + trans->callref = 0; + + gsm48_stop_cc_timer(trans); + + /* cause */ + if (rel->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 0, &rel->cause); + /* facility */ + if (rel->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &rel->facility); + /* user-user */ + if (rel->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &rel->useruser); + + ret = gsm48_conn_sendmsg(msg, trans->conn, trans); + + trans_free(trans); + + return ret; +} + +static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc fac; + + memset(&fac, 0, sizeof(struct gsm_mncc)); + fac.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0); + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + fac.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&fac.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + fac.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&fac.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_FACILITY_IND, &fac); +} + +static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *fac = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_FACILITY; + + /* facility */ + gsm48_encode_facility(msg, 1, &fac->facility); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc hold; + + memset(&hold, 0, sizeof(struct gsm_mncc)); + hold.callref = trans->callref; + return mncc_recvmsg(trans->subscr->net, trans, MNCC_HOLD_IND, &hold); +} + +static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_HOLD_ACK; + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *hold_rej = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_HOLD_REJ; + + /* cause */ + if (hold_rej->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 1, &hold_rej->cause); + else + gsm48_encode_cause(msg, 1, &default_cause); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc retrieve; + + memset(&retrieve, 0, sizeof(struct gsm_mncc)); + retrieve.callref = trans->callref; + return mncc_recvmsg(trans->subscr->net, trans, MNCC_RETRIEVE_IND, + &retrieve); +} + +static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RETR_ACK; + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *retrieve_rej = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RETR_REJ; + + /* cause */ + if (retrieve_rej->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 1, &retrieve_rej->cause); + else + gsm48_encode_cause(msg, 1, &default_cause); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc dtmf; + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* keypad facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { + dtmf.fields |= MNCC_F_KEYPAD; + gsm48_decode_keypad(&dtmf.keypad, + TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); + } + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_START_DTMF_IND, &dtmf); +} + +static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *dtmf = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_START_DTMF_ACK; + + /* keypad */ + if (dtmf->fields & MNCC_F_KEYPAD) + gsm48_encode_keypad(msg, dtmf->keypad); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *dtmf = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_START_DTMF_REJ; + + /* cause */ + if (dtmf->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 1, &dtmf->cause); + else + gsm48_encode_cause(msg, 1, &default_cause); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK; + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc dtmf; + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_STOP_DTMF_IND, &dtmf); +} + +static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + + new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_IND, &modify); +} + +static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY; + + gsm48_start_cc_timer(trans, 0x323, GSM48_T323); + + /* bearer capability */ + gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); + + new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + gsm48_stop_cc_timer(trans); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_CNF, &modify); +} + +static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; + + /* bearer capability */ + gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + gsm48_stop_cc_timer(trans); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= GSM48_IE_BEARER_CAP; + gsm48_decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + modify.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&modify.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_REJ, &modify); +} + +static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; + + /* bearer capability */ + gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); + /* cause */ + gsm48_encode_cause(msg, 1, &modify->cause); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *notify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_NOTIFY; + + /* notify */ + gsm48_encode_notify(msg, notify->notify); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); +// struct tlv_parsed tp; + struct gsm_mncc notify; + + memset(¬ify, 0, sizeof(struct gsm_mncc)); + notify.callref = trans->callref; +// tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len); + if (payload_len >= 1) + gsm48_decode_notify(¬ify.notify, gh->data); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_NOTIFY_IND, ¬ify); +} + +static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *user = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_USER_INFO; + + /* user-user */ + if (user->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 1, &user->useruser); + /* more data */ + if (user->more) + gsm48_encode_more(msg); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc user; + + memset(&user, 0, sizeof(struct gsm_mncc)); + user.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0); + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + user.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&user.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* more data */ + if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA)) + user.more = 1; + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_USERINFO_IND, &user); +} + +static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *mode = arg; + + return gsm0808_assign_req(trans->conn, mode->lchan_mode, 1); +} + +static struct downstate { + uint32_t states; + int type; + int (*rout) (struct gsm_trans *trans, void *arg); +} downstatelist[] = { + /* mobile originating call establishment */ + {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */ + MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc}, + {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */ + MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, + {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */ + MNCC_SETUP_RSP, gsm48_cc_tx_connect}, + {SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */ + MNCC_PROGRESS_REQ, gsm48_cc_tx_progress}, + /* mobile terminating call establishment */ + {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */ + MNCC_SETUP_REQ, gsm48_cc_tx_setup}, + {SBIT(GSM_CSTATE_CONNECT_REQUEST), + MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack}, + /* signalling during call */ + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_NOTIFY_REQ, gsm48_cc_tx_notify}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), + MNCC_FACILITY_REQ, gsm48_cc_tx_facility}, + {ALL_STATES, + MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack}, + {ALL_STATES, + MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej}, + {ALL_STATES, + MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_MODIFY_REQ, gsm48_cc_tx_modify}, + {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), + MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete}, + {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), + MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo}, + /* clearing */ + {SBIT(GSM_CSTATE_INITIATED), + MNCC_REJ_REQ, gsm48_cc_tx_release_compl}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */ + MNCC_DISC_REQ, gsm48_cc_tx_disconnect}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ + MNCC_REL_REQ, gsm48_cc_tx_release}, + /* special */ + {ALL_STATES, + MNCC_LCHAN_MODIFY, _gsm48_lchan_modify}, +}; + +#define DOWNSLLEN \ + (sizeof(downstatelist) / sizeof(struct downstate)) + + +int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) +{ + int i, rc = 0; + struct gsm_trans *trans = NULL, *transt; + struct gsm_subscriber_connection *conn = NULL; + struct gsm_bts *bts = NULL; + struct gsm_mncc *data = arg, rel; + + DEBUGP(DMNCC, "receive message %s\n", get_mncc_name(msg_type)); + + /* handle special messages */ + switch(msg_type) { + case MNCC_BRIDGE: + return tch_bridge(net, arg); + case MNCC_FRAME_DROP: + return tch_recv_mncc(net, data->callref, 0); + case MNCC_FRAME_RECV: + return tch_recv_mncc(net, data->callref, 1); + case GSM_TCHF_FRAME: + /* Find callref */ + trans = trans_find_by_callref(net, data->callref); + if (!trans) { + LOGP(DMNCC, LOGL_ERROR, "TCH frame for non-existing trans\n"); + return -EIO; + } + if (!trans->conn) { + LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without conn\n"); + return 0; + } + if (trans->conn->lchan->type != GSM_LCHAN_TCH_F) { + /* This should be LOGL_ERROR or NOTICE, but + * unfortuantely it happens for a couple of frames at + * the beginning of every RTP connection */ + LOGP(DMNCC, LOGL_DEBUG, "TCH frame for lchan != TCH_F\n"); + return 0; + } + bts = trans->conn->lchan->ts->trx->bts; + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_OSMO_SYSMO: + if (!trans->conn->lchan->abis_ip.rtp_socket) { + DEBUGP(DMNCC, "TCH frame to lchan without RTP connection\n"); + return 0; + } + return rtp_send_frame(trans->conn->lchan->abis_ip.rtp_socket, arg); + case GSM_BTS_TYPE_BS11: + case GSM_BTS_TYPE_RBS2000: + case GSM_BTS_TYPE_NOKIA_SITE: + return trau_send_frame(trans->conn->lchan, arg); + default: + LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type); + } + return -EINVAL; + } + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = data->callref; + + /* Find callref */ + trans = trans_find_by_callref(net, data->callref); + + /* Callref unknown */ + if (!trans) { + struct gsm_subscriber *subscr; + + if (msg_type != MNCC_SETUP_REQ) { + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "unknown callref %d\n", data->called.number, + get_mncc_name(msg_type), data->callref); + /* Invalid call reference */ + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_TRANS_ID); + } + if (!data->called.number[0] && !data->imsi[0]) { + DEBUGP(DCC, "(bts - trx - ts - ti) " + "Received '%s' from MNCC with " + "no number or IMSI\n", get_mncc_name(msg_type)); + /* Invalid number */ + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INV_NR_FORMAT); + } + /* New transaction due to setup, find subscriber */ + if (data->called.number[0]) + subscr = subscr_get_by_extension(net, + data->called.number); + else + subscr = subscr_get_by_imsi(net, data->imsi); + /* If subscriber is not found */ + if (!subscr) { + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "unknown subscriber %s\n", data->called.number, + get_mncc_name(msg_type), data->called.number); + /* Unknown subscriber */ + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_UNASSIGNED_NR); + } + /* If subscriber is not "attached" */ + if (!subscr->lac) { + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "detached subscriber %s\n", data->called.number, + get_mncc_name(msg_type), data->called.number); + subscr_put(subscr); + /* Temporarily out of order */ + return mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_DEST_OOO); + } + /* Create transaction */ + trans = trans_alloc(subscr, GSM48_PDISC_CC, 0xff, data->callref); + if (!trans) { + DEBUGP(DCC, "No memory for trans.\n"); + subscr_put(subscr); + /* Ressource unavailable */ + mncc_release_ind(net, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + return -ENOMEM; + } + /* Find lchan */ + conn = connection_for_subscr(subscr); + + /* If subscriber has no lchan */ + if (!conn) { + /* find transaction with this subscriber already paging */ + llist_for_each_entry(transt, &net->trans_list, entry) { + /* Transaction of our lchan? */ + if (transt == trans || + transt->subscr != subscr) + continue; + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC with " + "unallocated channel, paging already " + "started for lac %d.\n", + data->called.number, + get_mncc_name(msg_type), subscr->lac); + subscr_put(subscr); + trans_free(trans); + return 0; + } + /* store setup informations until paging was successfull */ + memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc)); + + /* Get a channel */ + trans->paging_request = talloc_zero(subscr->net, struct gsm_network*); + if (!trans->paging_request) { + LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n"); + subscr_put(subscr); + trans_free(trans); + return 0; + } + + *trans->paging_request = subscr->net; + subscr_get_channel(subscr, RSL_CHANNEED_TCH_F, setup_trig_pag_evt, trans->paging_request); + + subscr_put(subscr); + return 0; + } + /* Assign lchan */ + trans->conn = conn; + subscr_put(subscr); + } + + if (trans->conn) + conn = trans->conn; + + /* if paging did not respond yet */ + if (!conn) { + DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " + "Received '%s' from MNCC in paging state\n", + (trans->subscr)?(trans->subscr->extension):"-", + get_mncc_name(msg_type)); + mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_NORM_CALL_CLEAR); + if (msg_type == MNCC_REL_REQ) + rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); + else + rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); + trans->callref = 0; + trans_free(trans); + return rc; + } + + DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) " + "Received '%s' from MNCC in state %d (%s)\n", + conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr, + trans->transaction_id, + (trans->conn->subscr)?(trans->conn->subscr->extension):"-", + get_mncc_name(msg_type), trans->cc.state, + gsm48_cc_state_name(trans->cc.state)); + + /* Find function for current state and message */ + for (i = 0; i < DOWNSLLEN; i++) + if ((msg_type == downstatelist[i].type) + && ((1 << trans->cc.state) & downstatelist[i].states)) + break; + if (i == DOWNSLLEN) { + DEBUGP(DCC, "Message unhandled at this state.\n"); + return 0; + } + + rc = downstatelist[i].rout(trans, arg); + + return rc; +} + + +static struct datastate { + uint32_t states; + int type; + int (*rout) (struct gsm_trans *trans, struct msgb *msg); +} datastatelist[] = { + /* mobile originating call establishment */ + {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ + GSM48_MT_CC_SETUP, gsm48_cc_rx_setup}, + {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ + GSM48_MT_CC_EMERG_SETUP, gsm48_cc_rx_setup}, + {SBIT(GSM_CSTATE_CONNECT_IND), /* 5.2.1.2 */ + GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack}, + /* mobile terminating call establishment */ + {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.2 */ + GSM48_MT_CC_CALL_CONF, gsm48_cc_rx_call_conf}, + {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* ???? | 5.2.2.3.2 */ + GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting}, + {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */ + GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect}, + /* signalling during call */ + {ALL_STATES - SBIT(GSM_CSTATE_NULL), + GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify}, + {ALL_STATES, + GSM48_MT_CC_START_DTMF, gsm48_cc_rx_start_dtmf}, + {ALL_STATES, + GSM48_MT_CC_STOP_DTMF, gsm48_cc_rx_stop_dtmf}, + {ALL_STATES, + GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_HOLD, gsm48_cc_rx_hold}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_RETR, gsm48_cc_rx_retrieve}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify}, + {SBIT(GSM_CSTATE_MO_TERM_MODIFY), + GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete}, + {SBIT(GSM_CSTATE_MO_TERM_MODIFY), + GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject}, + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo}, + /* clearing */ + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ + GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.4.1.2.2 */ + GSM48_MT_CC_RELEASE, gsm48_cc_rx_release}, + {ALL_STATES, /* 5.4.3.4 */ + GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl}, +}; + +#define DATASLLEN \ + (sizeof(datastatelist) / sizeof(struct datastate)) + +static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t msg_type = gh->msg_type & 0xbf; + uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */ + struct gsm_trans *trans = NULL; + int i, rc = 0; + + if (msg_type & 0x80) { + DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type); + return -EINVAL; + } + + /* Find transaction */ + trans = trans_find_by_id(conn->subscr, GSM48_PDISC_CC, transaction_id); + + DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " + "Received '%s' from MS in state %d (%s)\n", + conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr, + transaction_id, (conn->subscr)?(conn->subscr->extension):"-", + gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0, + gsm48_cc_state_name(trans?(trans->cc.state):0)); + + /* Create transaction */ + if (!trans) { + DEBUGP(DCC, "Unknown transaction ID %x, " + "creating new trans.\n", transaction_id); + /* Create transaction */ + trans = trans_alloc(conn->subscr, GSM48_PDISC_CC, + transaction_id, new_callref++); + if (!trans) { + DEBUGP(DCC, "No memory for trans.\n"); + rc = gsm48_tx_simple(conn, + GSM48_PDISC_CC | (transaction_id << 4), + GSM48_MT_CC_RELEASE_COMPL); + return -ENOMEM; + } + /* Assign transaction */ + trans->conn = conn; + } + + /* find function for current state and message */ + for (i = 0; i < DATASLLEN; i++) + if ((msg_type == datastatelist[i].type) + && ((1 << trans->cc.state) & datastatelist[i].states)) + break; + if (i == DATASLLEN) { + DEBUGP(DCC, "Message unhandled at this state.\n"); + return 0; + } + + assert(trans->subscr); + + rc = datastatelist[i].rout(trans, msg); + + return rc; +} + +/* Create a dummy to wait five seconds */ +static void release_anchor(struct gsm_subscriber_connection *conn) +{ + if (!conn->anch_operation) + return; + + osmo_timer_del(&conn->anch_operation->timeout); + talloc_free(conn->anch_operation); + conn->anch_operation = NULL; +} + +static void anchor_timeout(void *_data) +{ + struct gsm_subscriber_connection *con = _data; + + release_anchor(con); + msc_release_connection(con); +} + +int gsm0408_new_conn(struct gsm_subscriber_connection *conn) +{ + conn->anch_operation = talloc_zero(conn, struct gsm_anchor_operation); + if (!conn->anch_operation) + return -1; + + conn->anch_operation->timeout.data = conn; + conn->anch_operation->timeout.cb = anchor_timeout; + osmo_timer_schedule(&conn->anch_operation->timeout, 5, 0); + return 0; +} + +/* here we get data from the BSC level... */ +int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t pdisc = gh->proto_discr & 0x0f; + int rc = 0; + + if (silent_call_reroute(conn, msg)) + return silent_call_rx(conn, msg); + + switch (pdisc) { + case GSM48_PDISC_CC: + release_anchor(conn); + rc = gsm0408_rcv_cc(conn, msg); + break; + case GSM48_PDISC_MM: + rc = gsm0408_rcv_mm(conn, msg); + break; + case GSM48_PDISC_RR: + rc = gsm0408_rcv_rr(conn, msg); + break; + case GSM48_PDISC_SMS: + release_anchor(conn); + rc = gsm0411_rcv_sms(conn, msg); + break; + case GSM48_PDISC_MM_GPRS: + case GSM48_PDISC_SM_GPRS: + LOGP(DRLL, LOGL_NOTICE, "Unimplemented " + "GSM 04.08 discriminator 0x%02x\n", pdisc); + break; + case GSM48_PDISC_NC_SS: + release_anchor(conn); + rc = handle_rcv_ussd(conn, msg); + break; + default: + LOGP(DRLL, LOGL_NOTICE, "Unknown " + "GSM 04.08 discriminator 0x%02x\n", pdisc); + break; + } + + return rc; +} + +/* + * This will be ran by the linker when loading the DSO. We use it to + * do system initialization, e.g. registration of signal handlers. + */ +static __attribute__((constructor)) void on_dso_load_0408(void) +{ + osmo_signal_register_handler(SS_ABISIP, handle_abisip_signal, NULL); +} diff --git a/src/libmsc/gsm_04_11.c b/src/libmsc/gsm_04_11.c new file mode 100644 index 000000000..2fc250bc8 --- /dev/null +++ b/src/libmsc/gsm_04_11.c @@ -0,0 +1,1004 @@ +/* Point-to-Point (PP) Short Message Service (SMS) + * Support on Mobile Radio Interface + * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ + +/* (C) 2008 by Daniel Willmann + * (C) 2009 by Harald Welte + * (C) 2010-2012 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * (C) 2011 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "bscconfig.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BUILD_SMPP +#include "smpp_smsc.h" +extern int smpp_try_deliver(struct gsm_sms *sms, + struct gsm_subscriber_connection *conn); +#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->sender) + subscr_put(sms->sender); + if (sms->receiver) + subscr_put(sms->receiver); +#ifdef BUILD_SMPP + if (sms->smpp.esme) + smpp_esme_put(sms->smpp.esme); +#endif + + talloc_free(sms); +} + +struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, + struct gsm_subscriber *sender, + int dcs, const char *text) +{ + struct gsm_sms *sms = sms_alloc(); + + if (!sms) + return NULL; + + sms->receiver = subscr_get(receiver); + strncpy(sms->text, text, sizeof(sms->text)-1); + + sms->sender = subscr_get(sender); + strncpy(sms->src.addr, sms->sender->extension, sizeof(sms->src.addr)-1); + sms->reply_path_req = 0; + sms->status_rep_req = 0; + sms->ud_hdr_ind = 0; + sms->protocol_id = 0; /* implicit */ + sms->data_coding_scheme = dcs; + strncpy(sms->dst.addr, receiver->extension, sizeof(sms->dst.addr)-1); + /* Generate user_data */ + sms->user_data_len = gsm_7bit_encode(sms->user_data, sms->text); + + return sms; +} + + +static void send_signal(int sig_no, + struct gsm_trans *trans, + struct gsm_sms *sms, + int paging_result) +{ + struct sms_signal_data sig; + sig.trans = trans; + sig.sms = sms; + sig.paging_result = paging_result; + 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 a TPDU derived from struct gsm_sms, + * returns total size of TPDU */ +static int gsm340_gen_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; +} + +/* process an incoming TPDU (called from RP-DATA) + * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */ +static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + uint8_t *smsp = msgb_sms(msg); + struct gsm_sms *gsms; + unsigned int sms_alphabet; + uint8_t sms_mti, sms_mms, sms_vpf, sms_rp; + uint8_t *sms_vp; + uint8_t da_len_bytes; + uint8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */ + int rc = 0; + + osmo_counter_inc(conn->bts->network->stats.sms.submitted); + + gsms = sms_alloc(); + if (!gsms) + return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; + + /* invert those fields where 0 means active/present */ + sms_mti = *smsp & 0x03; + sms_mms = !!(*smsp & 0x04); + sms_vpf = (*smsp & 0x18) >> 3; + gsms->status_rep_req = (*smsp & 0x20); + gsms->ud_hdr_ind = (*smsp & 0x40); + sms_rp = (*smsp & 0x80); + + smsp++; + gsms->msg_ref = *smsp++; + + /* length in bytes of the destination address */ + da_len_bytes = 2 + *smsp/2 + *smsp%2; + if (da_len_bytes > 12) { + LOGP(DLSMS, LOGL_ERROR, "Destination Address > 12 bytes ?!?\n"); + rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; + goto out; + } else if (da_len_bytes < 4) { + LOGP(DLSMS, LOGL_ERROR, "Destination Address < 4 bytes ?!?\n"); + rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; + goto out; + } + memset(address_lv, 0, sizeof(address_lv)); + memcpy(address_lv, smsp, da_len_bytes); + /* mangle first byte to reflect length in bytes, not digits */ + address_lv[0] = da_len_bytes - 1; + + gsms->dst.ton = (address_lv[1] >> 4) & 7; + gsms->dst.npi = address_lv[1] & 0xF; + /* convert to real number */ + gsm48_decode_bcd_number(gsms->dst.addr, + sizeof(gsms->dst.addr), address_lv, 1); + smsp += da_len_bytes; + + gsms->protocol_id = *smsp++; + gsms->data_coding_scheme = *smsp++; + + sms_alphabet = gsm338_get_sms_alphabet(gsms->data_coding_scheme); + if (sms_alphabet == 0xffffffff) { + sms_free(gsms); + return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; + } + + switch (sms_vpf) { + case GSM340_TP_VPF_RELATIVE: + sms_vp = smsp++; + break; + case GSM340_TP_VPF_ABSOLUTE: + case GSM340_TP_VPF_ENHANCED: + sms_vp = smsp; + /* the additional functionality indicator... */ + if (sms_vpf == GSM340_TP_VPF_ENHANCED && *smsp & (1<<7)) + smsp++; + smsp += 7; + break; + case GSM340_TP_VPF_NONE: + sms_vp = 0; + break; + default: + LOGP(DLSMS, LOGL_NOTICE, + "SMS Validity period not implemented: 0x%02x\n", sms_vpf); + return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; + } + gsms->user_data_len = *smsp++; + if (gsms->user_data_len) { + memcpy(gsms->user_data, smsp, gsms->user_data_len); + + switch (sms_alphabet) { + case DCS_7BIT_DEFAULT: + gsm_7bit_decode(gsms->text, smsp, gsms->user_data_len); + break; + case DCS_8BIT_DATA: + case DCS_UCS2: + case DCS_NONE: + break; + } + } + + gsms->sender = subscr_get(conn->subscr); + + LOGP(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(gsms->sender), 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); + + /* determine gsms->receiver based on dialled number */ + gsms->receiver = subscr_get_by_extension(conn->bts->network, gsms->dst.addr); + if (!gsms->receiver) { +#ifdef BUILD_SMPP + rc = smpp_try_deliver(gsms, conn); + if (rc == 1) { + rc = 1; /* cause 1: unknown subscriber */ + osmo_counter_inc(conn->bts->network->stats.sms.no_receiver); + } else if (rc < 0) { + rc = 21; /* cause 21: short message transfer rejected */ + /* FIXME: handle the error somehow? */ + } +#else + rc = 1; /* cause 1: unknown subscriber */ + osmo_counter_inc(conn->bts->network->stats.sms.no_receiver); +#endif + goto out; + } + + switch (sms_mti) { + case GSM340_SMS_SUBMIT_MS2SC: + /* MS is submitting a SMS */ + rc = gsm340_rx_sms_submit(msg, gsms); + break; + case GSM340_SMS_COMMAND_MS2SC: + case GSM340_SMS_DELIVER_REP_MS2SC: + LOGP(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; + +out: + sms_free(gsms); + + return rc; +} + +/* Prefix msg with a RP-DATA header and send as SMR DATA */ +static int gsm411_rp_sendmsg(struct gsm411_smr_inst *inst, struct msgb *msg, + uint8_t rp_msg_type, uint8_t rp_msg_ref, + int rl_msg_type) +{ + struct gsm411_rp_hdr *rp; + uint8_t len = msg->len; + + /* GSM 04.11 RP-DATA header */ + rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp)); + rp->len = len + 2; + rp->msg_type = rp_msg_type; + rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */ + + return gsm411_smr_send(inst, rl_msg_type, msg); +} + +static int gsm411_send_rp_ack(struct gsm_trans *trans, uint8_t msg_ref) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + DEBUGP(DLSMS, "TX: SMS RP ACK\n"); + + return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, GSM411_MT_RP_ACK_MT, + msg_ref, GSM411_SM_RL_REPORT_REQ); +} + +static int gsm411_send_rp_error(struct gsm_trans *trans, + uint8_t msg_ref, uint8_t cause) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + msgb_tv_put(msg, 1, cause); + + LOGP(DLSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause, + get_value_string(gsm411_rp_cause_strs, cause)); + + return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, + GSM411_MT_RP_ERROR_MT, msg_ref, GSM411_SM_RL_REPORT_REQ); +} + +/* Receive a 04.11 TPDU inside RP-DATA / user data */ +static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph, + uint8_t src_len, uint8_t *src, + uint8_t dst_len, uint8_t *dst, + uint8_t tpdu_len, uint8_t *tpdu) +{ + int rc = 0; + + if (src_len && src) + LOGP(DLSMS, LOGL_ERROR, "RP-DATA (MO) with SRC ?!?\n"); + + if (!dst_len || !dst || !tpdu_len || !tpdu) { + LOGP(DLSMS, LOGL_ERROR, + "RP-DATA (MO) without DST or TPDU ?!?\n"); + gsm411_send_rp_error(trans, rph->msg_ref, + GSM411_RP_CAUSE_INV_MAND_INF); + return -EIO; + } + msg->l4h = tpdu; + + DEBUGP(DLSMS, "DST(%u,%s)\n", dst_len, osmo_hexdump(dst, dst_len)); + + rc = gsm340_rx_tpdu(trans->conn, msg); + if (rc == 0) + return gsm411_send_rp_ack(trans, rph->msg_ref); + else if (rc > 0) + return gsm411_send_rp_error(trans, rph->msg_ref, rc); + else + return rc; +} + +/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */ +static int gsm411_rx_rp_data(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph) +{ + uint8_t src_len, dst_len, rpud_len; + uint8_t *src = NULL, *dst = NULL , *rp_ud = NULL; + + /* in the MO case, this should always be zero length */ + src_len = rph->data[0]; + if (src_len) + src = &rph->data[1]; + + dst_len = rph->data[1+src_len]; + if (dst_len) + dst = &rph->data[1+src_len+1]; + + rpud_len = rph->data[1+src_len+1+dst_len]; + if (rpud_len) + rp_ud = &rph->data[1+src_len+1+dst_len+1]; + + DEBUGP(DLSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n", + src_len, dst_len, rpud_len); + return gsm411_rx_rp_ud(msg, trans, rph, src_len, src, dst_len, dst, + rpud_len, rp_ud); +} + +/* Receive a 04.11 RP-ACK message (response to RP-DATA from us) */ +static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph) +{ + struct gsm_sms *sms = trans->sms.sms; + + /* Acnkowledgement to MT RP_DATA, i.e. the MS confirms it + * successfully received a SMS. We can now safely mark it as + * transmitted */ + + if (!sms) { + LOGP(DLSMS, LOGL_ERROR, "RX RP-ACK but no sms in transaction?!?\n"); + return gsm411_send_rp_error(trans, rph->msg_ref, + GSM411_RP_CAUSE_PROTOCOL_ERR); + } + + /* mark this SMS as sent in database */ + db_sms_mark_sent(sms); + + send_signal(S_SMS_DELIVERED, trans, sms, 0); + + sms_free(sms); + trans->sms.sms = NULL; + + /* check for more messages for this subscriber */ + sms = db_sms_get_unsent_for_subscr(trans->subscr); + if (sms) + gsm411_send_sms(trans->conn, sms); + + return 0; +} + +static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph) +{ + struct gsm_network *net = trans->conn->bts->network; + struct gsm_sms *sms = trans->sms.sms; + uint8_t cause_len = rph->data[0]; + uint8_t cause = rph->data[1]; + + /* Error in response to MT RP_DATA, i.e. the MS did not + * successfully receive the SMS. We need to investigate + * the cause and take action depending on it */ + + LOGP(DLSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n", + subscr_name(trans->conn->subscr), cause_len, cause, + get_value_string(gsm411_rp_cause_strs, cause)); + + if (!sms) { + LOGP(DLSMS, LOGL_ERROR, + "RX RP-ERR, but no sms in transaction?!?\n"); + return -EINVAL; +#if 0 + return gsm411_send_rp_error(trans, rph->msg_ref, + GSM411_RP_CAUSE_PROTOCOL_ERR); +#endif + } + + if (cause == GSM411_RP_CAUSE_MT_MEM_EXCEEDED) { + /* MS has not enough memory to store the message. We need + * to store this in our database and wait for a SMMA message */ + /* FIXME */ + send_signal(S_SMS_MEM_EXCEEDED, trans, sms, 0); + osmo_counter_inc(net->stats.sms.rp_err_mem); + } else { + send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); + osmo_counter_inc(net->stats.sms.rp_err_other); + } + + sms_free(sms); + trans->sms.sms = NULL; + + return 0; +} + +static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph) +{ + struct gsm_sms *sms; + 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); + + /* check for more messages for this subscriber */ + sms = db_sms_get_unsent_for_subscr(trans->subscr); + if (sms) + gsm411_send_sms(trans->conn, sms); + + return rc; +} + +/* receive RL DATA */ +static int gsm411_rx_rl_data(struct msgb *msg, struct gsm48_hdr *gh, + struct gsm_trans *trans) +{ + struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; + uint8_t msg_type = rp_data->msg_type & 0x07; + int rc = 0; + + switch (msg_type) { + case GSM411_MT_RP_DATA_MO: + DEBUGP(DLSMS, "RX SMS RP-DATA (MO)\n"); + rc = gsm411_rx_rp_data(msg, trans, rp_data); + break; + case GSM411_MT_RP_SMMA_MO: + DEBUGP(DLSMS, "RX SMS RP-SMMA\n"); + rc = gsm411_rx_rp_smma(msg, trans, rp_data); + break; + default: + LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); + rc = -EINVAL; + break; + } + + return rc; +} + +/* receive RL REPORT */ +static int gsm411_rx_rl_report(struct msgb *msg, struct gsm48_hdr *gh, + struct gsm_trans *trans) +{ + struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; + uint8_t msg_type = rp_data->msg_type & 0x07; + int rc = 0; + + switch (msg_type) { + case GSM411_MT_RP_ACK_MO: + DEBUGP(DLSMS, "RX SMS RP-ACK (MO)\n"); + rc = gsm411_rx_rp_ack(msg, trans, rp_data); + break; + case GSM411_MT_RP_ERROR_MO: + DEBUGP(DLSMS, "RX SMS RP-ERROR (MO)\n"); + rc = gsm411_rx_rp_error(msg, trans, rp_data); + break; + default: + LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); + rc = -EINVAL; + break; + } + + return rc; +} + +/* receive SM-RL sap message from SMR + * NOTE: Message is freed by sender + */ +int gsm411_rl_recv(struct gsm411_smr_inst *inst, int msg_type, + struct msgb *msg) +{ + struct gsm_trans *trans = + container_of(inst, struct gsm_trans, sms.smr_inst); + struct gsm48_hdr *gh = msgb_l3(msg); + int rc = 0; + + switch (msg_type) { + case GSM411_SM_RL_DATA_IND: + rc = gsm411_rx_rl_data(msg, gh, trans); + break; + case GSM411_SM_RL_REPORT_IND: + if (gh) + rc = gsm411_rx_rl_report(msg, gh, trans); + break; + default: + LOGP(DLSMS, LOGL_NOTICE, "Unhandled SM-RL message 0x%x\n", msg_type); + rc = -EINVAL; + } + + return rc; +} + +/* receive MNCCSMS sap message from SMC + * NOTE: Message is freed by sender + */ +static int gsm411_mn_recv(struct gsm411_smc_inst *inst, int msg_type, + struct msgb *msg) +{ + struct gsm_trans *trans = + container_of(inst, struct gsm_trans, sms.smc_inst); + struct gsm48_hdr *gh = msgb_l3(msg); + int rc = 0; + + switch (msg_type) { + case GSM411_MNSMS_EST_IND: + case GSM411_MNSMS_DATA_IND: + DEBUGP(DLSMS, "MNSMS-DATA/EST-IND\n"); + rc = gsm411_smr_recv(&trans->sms.smr_inst, msg_type, msg); + break; + case GSM411_MNSMS_ERROR_IND: + if (gh) + DEBUGP(DLSMS, "MNSMS-ERROR-IND, cause %d (%s)\n", + gh->data[0], + get_value_string(gsm411_cp_cause_strs, + gh->data[0])); + else + DEBUGP(DLSMS, "MNSMS-ERROR-IND, no cause\n"); + rc = gsm411_smr_recv(&trans->sms.smr_inst, msg_type, msg); + break; + default: + LOGP(DLSMS, LOGL_NOTICE, "Unhandled MNCCSMS msg 0x%x\n", msg_type); + rc = -EINVAL; + } + + return rc; +} + +/* Entry point for incoming GSM48_PDISC_SMS from abis_rsl.c */ +int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t msg_type = gh->msg_type; + uint8_t transaction_id = ((gh->proto_discr >> 4) ^ 0x8); /* flip */ + struct gsm_trans *trans; + int new_trans = 0; + int rc = 0; + + if (!conn->subscr) + return -EIO; + /* FIXME: send some error message */ + + DEBUGP(DLSMS, "receiving data (trans_id=%x)\n", transaction_id); + trans = trans_find_by_id(conn->subscr, 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->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->subscr, + 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 = 42; + int transaction_id; + int rc; + + transaction_id = + trans_assign_trans_id(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); + return -EBUSY; + } + + DEBUGP(DLSMS, "send_sms_lchan()\n"); + + /* FIXME: allocate transaction with message reference */ + trans = trans_alloc(conn->subscr, GSM48_PDISC_SMS, + transaction_id, new_callref++); + if (!trans) { + LOGP(DLSMS, LOGL_ERROR, "No memory for trans\n"); + send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0); + sms_free(sms); + /* FIXME: send some error message */ + return -ENOMEM; + } + 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 TPDU */ + rc = gsm340_gen_tpdu(msg, sms); + if (rc < 0) { + send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); + sms_free(sms); + trans->sms.sms = NULL; + trans_free(trans); + msgb_free(msg); + return rc; + } + + *rp_ud_len = rc; + + DEBUGP(DLSMS, "TX: SMS DELIVER\n"); + + osmo_counter_inc(conn->bts->network->stats.sms.delivered); + db_sms_inc_deliver_attempts(trans->sms.sms); + + return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, + GSM411_MT_RP_DATA_MT, msg_ref, GSM411_SM_RL_DATA_REQ); +} + +/* paging callback. Here we get called if paging a subscriber has + * succeeded or failed. */ +static int paging_cb_send_sms(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *_conn, void *_sms) +{ + struct gsm_subscriber_connection *conn = _conn; + struct gsm_sms *sms = _sms; + int rc = 0; + + DEBUGP(DLSMS, "paging_cb_send_sms(hooknum=%u, event=%u, msg=%p," + "conn=%p, sms=%p/id: %llu)\n", hooknum, event, msg, conn, sms, sms->id); + + if (hooknum != GSM_HOOK_RR_PAGING) + return -EINVAL; + + switch (event) { + case GSM_PAGING_SUCCEEDED: + gsm411_send_sms(conn, sms); + break; + case GSM_PAGING_EXPIRED: + case GSM_PAGING_OOM: + case GSM_PAGING_BUSY: + send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, event); + sms_free(sms); + rc = -ETIMEDOUT; + break; + default: + LOGP(DLSMS, LOGL_ERROR, "Unhandled paging event: %d\n", event); + } + + return rc; +} + +/* high-level function to send a SMS to a given subscriber. The function + * will take care of paging the subscriber, establishing the RLL SAPI3 + * connection, etc. */ +int gsm411_send_sms_subscr(struct gsm_subscriber *subscr, + struct gsm_sms *sms) +{ + struct gsm_subscriber_connection *conn; + + /* check if we already have an open lchan to the subscriber. + * if yes, send the SMS this way */ + conn = connection_for_subscr(subscr); + if (conn) { + return gsm411_send_sms(conn, sms); + } + + /* if not, we have to start paging */ + subscr_get_channel(subscr, RSL_CHANNEED_SDCCH, paging_cb_send_sms, sms); + return 0; +} + +void _gsm411_sms_trans_free(struct gsm_trans *trans) +{ + /* cleanup SMS instance */ + gsm411_smr_clear(&trans->sms.smr_inst); + trans->sms.smr_inst.rl_recv = NULL; + trans->sms.smr_inst.mn_send = NULL; + + gsm411_smc_clear(&trans->sms.smc_inst); + trans->sms.smc_inst.mn_recv = NULL; + trans->sms.smc_inst.mm_send = NULL; + + if (trans->sms.sms) { + LOGP(DLSMS, LOGL_ERROR, "Transaction contains SMS.\n"); + send_signal(S_SMS_UNKNOWN_ERROR, trans, trans->sms.sms, 0); + sms_free(trans->sms.sms); + trans->sms.sms = NULL; + } +} + +void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn) +{ + struct gsm_network *net; + struct gsm_trans *trans, *tmp; + + net = conn->bts->network; + + llist_for_each_entry_safe(trans, tmp, &net->trans_list, entry) + if (trans->conn == conn) { + struct gsm_sms *sms = trans->sms.sms; + if (!sms) { + LOGP(DLSMS, LOGL_ERROR, "SAPI Reject but no " + "SMS.\n"); + continue; + } + + send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); + sms_free(sms); + trans->sms.sms = NULL; + trans_free(trans); + } +} + diff --git a/src/libmsc/gsm_04_80.c b/src/libmsc/gsm_04_80.c new file mode 100644 index 000000000..39738a5ee --- /dev/null +++ b/src/libmsc/gsm_04_80.c @@ -0,0 +1,175 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-2009 by Harald Welte + * (C) 2008, 2009, 2010 by Holger Hans Peter Freyther + * (C) 2009 by Mike Haben + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, uint8_t tag) +{ + uint8_t *data = msgb_push(msgb, 2); + + data[0] = tag; + data[1] = msgb->len - 2; + return data; +} + +static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag, + uint8_t value) +{ + uint8_t *data = msgb_push(msgb, 3); + + data[0] = tag; + data[1] = 1; + data[2] = value; + return data; +} + + +/* Send response to a mobile-originated ProcessUnstructuredSS-Request */ +int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, + const struct msgb *in_msg, const char *response_text, + const struct ussd_request *req) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + uint8_t *ptr8; + int response_len; + + /* First put the payload text into the message */ + ptr8 = msgb_put(msg, 0); + response_len = gsm_7bit_encode(ptr8, response_text); + msgb_put(msg, response_len); + + /* Then wrap it as an Octet String */ + msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG); + + /* Pre-pend the DCS octet string */ + msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F); + + /* Then wrap these as a Sequence */ + msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); + + /* Pre-pend the operation code */ + msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, + GSM0480_OP_CODE_PROCESS_USS_REQ); + + /* Wrap the operation code and IA5 string as a sequence */ + msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); + + /* Pre-pend the invoke ID */ + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); + + /* Wrap this up as a Return Result component */ + msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); + + /* Wrap the component in a Facility message */ + msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); + + /* And finally pre-pend the L3 header */ + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id + | (1<<7); /* TI direction = 1 */ + gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; + + return gsm0808_submit_dtap(conn, msg, 0, 0); +} + +int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, + const struct msgb *in_msg, + const struct ussd_request *req) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + /* First insert the problem code */ + msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL, + GSM_0480_GEN_PROB_CODE_UNRECOGNISED); + + /* Before it insert the invoke ID */ + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); + + /* Wrap this up as a Reject component */ + msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT); + + /* Wrap the component in a Facility message */ + msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); + + /* And finally pre-pend the L3 header */ + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_NC_SS; + gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */ + gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; + + return gsm0808_submit_dtap(conn, msg, 0, 0); +} + +int gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn, int level, const char *text) +{ + struct gsm48_hdr *gh; + struct msgb *msg; + + msg = gsm0480_create_unstructuredSS_Notify(level, text); + if (!msg) + return -1; + + gsm0480_wrap_invoke(msg, GSM0480_OP_CODE_USS_NOTIFY, 0); + gsm0480_wrap_facility(msg); + + /* And finally pre-pend the L3 header */ + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_NC_SS; + gh->msg_type = GSM0480_MTYPE_REGISTER; + + return gsm0808_submit_dtap(conn, msg, 0, 0); +} + +int gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn) +{ + struct gsm48_hdr *gh; + struct msgb *msg; + + msg = gsm48_msgb_alloc(); + if (!msg) + return -1; + + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_NC_SS; + gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; + + return gsm0808_submit_dtap(conn, msg, 0, 0); +} diff --git a/src/libmsc/gsm_subscriber.c b/src/libmsc/gsm_subscriber.c new file mode 100644 index 000000000..5ace8f666 --- /dev/null +++ b/src/libmsc/gsm_subscriber.c @@ -0,0 +1,472 @@ +/* The concept of a subscriber for the MSC, roughly HLR/VLR functionality */ + +/* (C) 2008 by Harald Welte + * (C) 2009,2013 by Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +void *tall_sub_req_ctx; + +extern struct llist_head *subscr_bsc_active_subscribers(void); + +int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, + gsm_cbfn *cb, void *cb_data); + + +/* + * Struct for pending channel requests. This is managed in the + * llist_head requests of each subscriber. The reference counting + * should work in such a way that a subscriber with a pending request + * remains in memory. + */ +struct subscr_request { + struct llist_head entry; + + /* back reference */ + struct gsm_subscriber *subscr; + + /* the requested channel type */ + int channel_type; + + /* what did we do */ + int state; + + /* the callback data */ + gsm_cbfn *cbfn; + void *param; +}; + +enum { + REQ_STATE_INITIAL, + REQ_STATE_QUEUED, + REQ_STATE_PAGED, + REQ_STATE_FAILED_START, + REQ_STATE_DISPATCHED, +}; + +/* + * We got the channel assigned and can now hand this channel + * over to one of our callbacks. + */ +static int subscr_paging_dispatch(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *data, void *param) +{ + struct subscr_request *request; + struct gsm_subscriber_connection *conn = data; + struct gsm_subscriber *subscr = param; + struct paging_signal_data sig_data; + + /* There is no request anymore... */ + if (llist_empty(&subscr->requests)) + return -1; + + /* Dispatch signal */ + sig_data.subscr = subscr; + sig_data.bts = conn ? conn->bts : NULL; + sig_data.conn = conn; + sig_data.paging_result = event; + osmo_signal_dispatch( + SS_PAGING, + event == GSM_PAGING_SUCCEEDED ? + S_PAGING_SUCCEEDED : S_PAGING_EXPIRED, + &sig_data + ); + + /* + * FIXME: What to do with paging requests coming during + * this callback? We must be sure to not start paging when + * we have an active connection to a subscriber and to make + * the subscr_put_channel work as required... + */ + request = (struct subscr_request *)subscr->requests.next; + request->state = REQ_STATE_DISPATCHED; + llist_del(&request->entry); + subscr->in_callback = 1; + request->cbfn(hooknum, event, msg, data, request->param); + subscr->in_callback = 0; + + if (event != GSM_PAGING_SUCCEEDED) { + /* + * This is a workaround for a bigger issue. We have + * issued paging that might involve multiple BTSes + * and one of them have failed now. We will stop the + * other paging requests as well as the next timeout + * would work on the next paging request and the queue + * will do bad things. This should be fixed by counting + * the outstanding results. + */ + paging_request_stop(NULL, subscr, NULL, NULL); + subscr_put_channel(subscr); + } + + subscr_put(subscr); + talloc_free(request); + return 0; +} + +static int subscr_paging_sec_cb(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *data, void *param) +{ + int rc; + + switch (event) { + case GSM_SECURITY_AUTH_FAILED: + /* Dispatch as paging failure */ + rc = subscr_paging_dispatch( + GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, + msg, data, param); + break; + + case GSM_SECURITY_NOAVAIL: + case GSM_SECURITY_SUCCEEDED: + /* Dispatch as paging failure */ + rc = subscr_paging_dispatch( + GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, + msg, data, param); + break; + + default: + rc = -EINVAL; + } + + return rc; +} + +static int subscr_paging_cb(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *data, void *param) +{ + struct gsm_subscriber_connection *conn = data; + struct gsm48_hdr *gh; + struct gsm48_pag_resp *pr; + + /* Other cases mean problem, dispatch direclty */ + if (event != GSM_PAGING_SUCCEEDED) + return subscr_paging_dispatch(hooknum, event, msg, data, param); + + /* Get paging response */ + gh = msgb_l3(msg); + pr = (struct gsm48_pag_resp *)gh->data; + + /* We _really_ have a channel, secure it now ! */ + return gsm48_secure_channel(conn, pr->key_seq, subscr_paging_sec_cb, param); +} + + +static void subscr_send_paging_request(struct gsm_subscriber *subscr) +{ + struct subscr_request *request; + int rc; + + assert(!llist_empty(&subscr->requests)); + + request = (struct subscr_request *)subscr->requests.next; + request->state = REQ_STATE_PAGED; + rc = paging_request(subscr->net, subscr, request->channel_type, + subscr_paging_cb, subscr); + + /* paging failed, quit now */ + if (rc <= 0) { + request->state = REQ_STATE_FAILED_START; + subscr_paging_cb(GSM_HOOK_RR_PAGING, GSM_PAGING_BUSY, + NULL, NULL, subscr); + } +} + +void subscr_get_channel(struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *param) +{ + struct subscr_request *request; + + request = talloc(tall_sub_req_ctx, struct subscr_request); + if (!request) { + if (cbfn) + cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_OOM, + NULL, NULL, param); + return; + } + + memset(request, 0, sizeof(*request)); + request->subscr = subscr_get(subscr); + request->channel_type = type; + request->cbfn = cbfn; + request->param = param; + request->state = REQ_STATE_INITIAL; + + /* + * FIXME: We might be able to assign more than one + * channel, e.g. voice and SMS submit at the same + * time. + */ + if (!subscr->in_callback && llist_empty(&subscr->requests)) { + /* add to the list, send a request */ + llist_add_tail(&request->entry, &subscr->requests); + subscr_send_paging_request(subscr); + } else { + /* this will be picked up later, from subscr_put_channel */ + llist_add_tail(&request->entry, &subscr->requests); + request->state = REQ_STATE_QUEUED; + } +} + +void subscr_put_channel(struct gsm_subscriber *subscr) +{ + /* + * FIXME: Continue with other requests now... by checking + * the gsm_subscriber inside the gsm_lchan. Drop the ref count + * of the lchan after having asked the next requestee to handle + * the channel. + */ + /* + * FIXME: is the lchan is of a different type we could still + * issue an immediate assignment for another channel and then + * close this one. + */ + /* + * Currently we will drop the last ref of the lchan which + * will result in a channel release on RSL and we will start + * the paging. This should work most of the time as the MS + * will listen to the paging requests before we timeout + */ + + if (subscr && !llist_empty(&subscr->requests)) + subscr_send_paging_request(subscr); +} + + +struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net, + 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 db_get_subscriber(net, GSM_SUBSCRIBER_TMSI, tmsi_string); +} + +struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net, + const char *imsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { + if (strcmp(subscr->imsi, imsi) == 0) + return subscr_get(subscr); + } + + return db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); +} + +struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net, + const char *ext) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { + if (strcmp(subscr->extension, ext) == 0) + return subscr_get(subscr); + } + + return db_get_subscriber(net, GSM_SUBSCRIBER_EXTENSION, ext); +} + +struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net, + unsigned long long id) +{ + struct gsm_subscriber *subscr; + char buf[32]; + sprintf(buf, "%llu", id); + + llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { + if (subscr->id == id) + return subscr_get(subscr); + } + + return db_get_subscriber(net, GSM_SUBSCRIBER_ID, buf); +} + +int subscr_update_expire_lu(struct gsm_subscriber *s, struct gsm_bts *bts) +{ + int rc; + + /* Table 10.5.33: The T3212 timeout value field is coded as the + * binary representation of the timeout value for + * periodic updating in decihours. Mark the subscriber as + * inactive if it missed two consecutive location updates. + * Timeout is twice the t3212 value plus one minute */ + + /* Is expiration handling enabled? */ + if (bts->si_common.chan_desc.t3212 == 0) + s->expire_lu = GSM_SUBSCRIBER_NO_EXPIRATION; + else + s->expire_lu = time(NULL) + + (bts->si_common.chan_desc.t3212 * 60 * 6 * 2) + 60; + + rc = db_sync_subscriber(s); + db_subscriber_update(s); + return rc; +} + +int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason) +{ + int rc; + + /* FIXME: Migrate pending requests from one BSC to another */ + switch (reason) { + case GSM_SUBSCRIBER_UPDATE_ATTACHED: + s->net = bts->network; + /* Indicate "attached to LAC" */ + s->lac = bts->location_area_code; + + LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n", + subscr_name(s), s->lac); + + /* + * 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, 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); + 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_network *net) +{ + db_subscriber_expire(net, subscr_expire_callback); +} + +int subscr_pending_requests(struct gsm_subscriber *sub) +{ + struct subscr_request *req; + int pending = 0; + + llist_for_each_entry(req, &sub->requests, entry) + pending += 1; + + return pending; +} + +int subscr_pending_clear(struct gsm_subscriber *sub) +{ + int deleted = 0; + struct subscr_request *req, *tmp; + + llist_for_each_entry_safe(req, tmp, &sub->requests, entry) { + subscr_put(req->subscr); + llist_del(&req->entry); + talloc_free(req); + deleted += 1; + } + + return deleted; +} + +int subscr_pending_dump(struct gsm_subscriber *sub, struct vty *vty) +{ + struct subscr_request *req; + + vty_out(vty, "Pending Requests for Subscriber %llu.%s", sub->id, VTY_NEWLINE); + llist_for_each_entry(req, &sub->requests, entry) { + vty_out(vty, "Channel type: %d State: %d Sub: %llu.%s", + req->channel_type, req->state, req->subscr->id, VTY_NEWLINE); + } + + return 0; +} + +int subscr_pending_kick(struct gsm_subscriber *sub) +{ + subscr_put_channel(sub); + return 0; +} diff --git a/src/libmsc/mncc.c b/src/libmsc/mncc.c new file mode 100644 index 000000000..b48477225 --- /dev/null +++ b/src/libmsc/mncc.c @@ -0,0 +1,109 @@ +/* mncc.c - utility routines for the MNCC API between the 04.08 + * message parsing and the actual Call Control logic */ + +/* (C) 2008-2009 by Harald Welte + * (C) 2009 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static struct mncc_names { + char *name; + int value; +} mncc_names[] = { + {"MNCC_SETUP_REQ", 0x0101}, + {"MNCC_SETUP_IND", 0x0102}, + {"MNCC_SETUP_RSP", 0x0103}, + {"MNCC_SETUP_CNF", 0x0104}, + {"MNCC_SETUP_COMPL_REQ",0x0105}, + {"MNCC_SETUP_COMPL_IND",0x0106}, + {"MNCC_CALL_CONF_IND", 0x0107}, + {"MNCC_CALL_PROC_REQ", 0x0108}, + {"MNCC_PROGRESS_REQ", 0x0109}, + {"MNCC_ALERT_REQ", 0x010a}, + {"MNCC_ALERT_IND", 0x010b}, + {"MNCC_NOTIFY_REQ", 0x010c}, + {"MNCC_NOTIFY_IND", 0x010d}, + {"MNCC_DISC_REQ", 0x010e}, + {"MNCC_DISC_IND", 0x010f}, + {"MNCC_REL_REQ", 0x0110}, + {"MNCC_REL_IND", 0x0111}, + {"MNCC_REL_CNF", 0x0112}, + {"MNCC_FACILITY_REQ", 0x0113}, + {"MNCC_FACILITY_IND", 0x0114}, + {"MNCC_START_DTMF_IND", 0x0115}, + {"MNCC_START_DTMF_RSP", 0x0116}, + {"MNCC_START_DTMF_REJ", 0x0117}, + {"MNCC_STOP_DTMF_IND", 0x0118}, + {"MNCC_STOP_DTMF_RSP", 0x0119}, + {"MNCC_MODIFY_REQ", 0x011a}, + {"MNCC_MODIFY_IND", 0x011b}, + {"MNCC_MODIFY_RSP", 0x011c}, + {"MNCC_MODIFY_CNF", 0x011d}, + {"MNCC_MODIFY_REJ", 0x011e}, + {"MNCC_HOLD_IND", 0x011f}, + {"MNCC_HOLD_CNF", 0x0120}, + {"MNCC_HOLD_REJ", 0x0121}, + {"MNCC_RETRIEVE_IND", 0x0122}, + {"MNCC_RETRIEVE_CNF", 0x0123}, + {"MNCC_RETRIEVE_REJ", 0x0124}, + {"MNCC_USERINFO_REQ", 0x0125}, + {"MNCC_USERINFO_IND", 0x0126}, + {"MNCC_REJ_REQ", 0x0127}, + {"MNCC_REJ_IND", 0x0128}, + + {"MNCC_BRIDGE", 0x0200}, + {"MNCC_FRAME_RECV", 0x0201}, + {"MNCC_FRAME_DROP", 0x0202}, + {"MNCC_LCHAN_MODIFY", 0x0203}, + + {"GSM_TCH_FRAME", 0x0300}, + + {NULL, 0} }; + +char *get_mncc_name(int value) +{ + int i; + + for (i = 0; mncc_names[i].name; i++) { + if (mncc_names[i].value == value) + return mncc_names[i].name; + } + + return "MNCC_Unknown"; +} + +void mncc_set_cause(struct gsm_mncc *data, int loc, int val) +{ + data->fields |= MNCC_F_CAUSE; + data->cause.location = loc; + data->cause.value = val; +} + diff --git a/src/libmsc/mncc_builtin.c b/src/libmsc/mncc_builtin.c new file mode 100644 index 000000000..617cbf25f --- /dev/null +++ b/src/libmsc/mncc_builtin.c @@ -0,0 +1,424 @@ +/* mncc_builtin.c - default, minimal built-in MNCC Application for + * standalone bsc_hack (netowrk-in-the-box mode) */ + +/* (C) 2008-2010 by Harald Welte + * (C) 2009 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +void *tall_call_ctx; + +static LLIST_HEAD(call_list); + +static uint32_t new_callref = 0x00000001; + +struct mncc_int mncc_int = { + .def_codec = { GSM48_CMODE_SPEECH_EFR, 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; +} + +static uint8_t determine_lchan_mode(struct gsm_mncc *setup) +{ + /* FIXME: check codec capabilities of the phone */ + + if (setup->lchan_type == GSM_LCHAN_TCH_F) + return mncc_int.def_codec[0]; + else + return mncc_int.def_codec[1]; +} + +/* on incoming call, look up database and send setup to remote subscr. */ +static int mncc_setup_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *setup) +{ + struct gsm_mncc mncc; + struct gsm_call *remote; + + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + + /* already have remote call */ + if (call->remote_ref) + return 0; + + /* transfer mode 1 would be packet mode, which was never specified */ + if (setup->bearer_cap.mode != 0) { + LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support " + "packet mode\n", call->callref); + mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); + goto out_reject; + } + + /* we currently only do speech */ + if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) { + LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support " + "voice calls\n", call->callref); + mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); + goto out_reject; + } + + /* create remote call */ + if (!(remote = talloc_zero(tall_call_ctx, struct gsm_call))) { + mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + goto out_reject; + } + llist_add_tail(&remote->entry, &call_list); + remote->net = call->net; + remote->callref = new_callref++; + DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n", + call->callref, remote->callref); + + /* link remote call */ + call->remote_ref = remote->callref; + remote->remote_ref = call->callref; + + /* send call proceeding */ + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref); + mncc_tx_to_cc(call->net, MNCC_CALL_PROC_REQ, &mncc); + + /* modify mode */ + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + mncc.lchan_mode = determine_lchan_mode(setup); + DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref); + mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, &mncc); + + /* send setup to remote */ +// setup->fields |= MNCC_F_SIGNAL; +// setup->signal = GSM48_SIGNAL_DIALTONE; + setup->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref); + return mncc_tx_to_cc(remote->net, MNCC_SETUP_REQ, setup); + +out_reject: + mncc_tx_to_cc(call->net, MNCC_REJ_REQ, &mncc); + free_call(call); + return 0; +} + +static int mncc_alert_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *alert) +{ + struct gsm_call *remote; + + /* send alerting to remote */ + if (!(remote = get_call_ref(call->remote_ref))) + return 0; + alert->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref); + return mncc_tx_to_cc(remote->net, MNCC_ALERT_REQ, alert); +} + +static int mncc_notify_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *notify) +{ + struct gsm_call *remote; + + /* send notify to remote */ + if (!(remote = get_call_ref(call->remote_ref))) + return 0; + notify->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref); + return mncc_tx_to_cc(remote->net, MNCC_NOTIFY_REQ, notify); +} + +static int mncc_setup_cnf(struct gsm_call *call, int msg_type, + struct gsm_mncc *connect) +{ + struct gsm_mncc connect_ack, frame_recv; + struct gsm_network *net = call->net; + struct gsm_call *remote; + uint32_t refs[2]; + + /* acknowledge connect */ + memset(&connect_ack, 0, sizeof(struct gsm_mncc)); + connect_ack.callref = call->callref; + DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref); + mncc_tx_to_cc(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack); + + /* send connect message to remote */ + if (!(remote = get_call_ref(call->remote_ref))) + return 0; + connect->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref); + mncc_tx_to_cc(remote->net, MNCC_SETUP_RSP, connect); + + /* bridge tch */ + refs[0] = call->callref; + refs[1] = call->remote_ref; + DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref); + + /* in direct mode, we always have to bridge the channels */ + if (ipacc_rtp_direct) + return mncc_tx_to_cc(call->net, MNCC_BRIDGE, refs); + + /* proxy mode */ + if (!net->handover.active) { + /* in the no-handover case, we can bridge, i.e. use + * the old RTP proxy code */ + return mncc_tx_to_cc(call->net, MNCC_BRIDGE, refs); + } else { + /* in case of handover, we need to re-write the RTP + * SSRC, sequence and timestamp values and thus + * need to enable RTP receive for both directions */ + memset(&frame_recv, 0, sizeof(struct gsm_mncc)); + frame_recv.callref = call->callref; + mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); + frame_recv.callref = call->remote_ref; + return mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); + } +} + +static int mncc_disc_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *disc) +{ + struct gsm_call *remote; + + /* send release */ + DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n", + call->callref, disc->cause.value); + mncc_tx_to_cc(call->net, MNCC_REL_REQ, disc); + + /* send disc to remote */ + if (!(remote = get_call_ref(call->remote_ref))) { + return 0; + } + disc->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n", + remote->callref, disc->cause.value); + return mncc_tx_to_cc(remote->net, MNCC_DISC_REQ, disc); +} + +static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) +{ + struct gsm_call *remote; + + /* send release to remote */ + if (!(remote = get_call_ref(call->remote_ref))) { + free_call(call); + return 0; + } + + rel->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n", + call->callref, rel->cause.value); + + /* + * Release this side of the call right now. Otherwise we end up + * in this method for the other call and will also try to release + * it and then we will end up with a double free and a crash + */ + free_call(call); + mncc_tx_to_cc(remote->net, MNCC_REL_REQ, rel); + + return 0; +} + +static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) +{ + free_call(call); + return 0; +} + +/* receiving a TCH/F frame from the BSC code */ +static int mncc_rcv_tchf(struct gsm_call *call, int msg_type, + struct gsm_data_frame *dfr) +{ + struct gsm_trans *remote_trans; + + remote_trans = trans_find_by_callref(call->net, call->remote_ref); + + /* this shouldn't really happen */ + if (!remote_trans || !remote_trans->conn) { + LOGP(DMNCC, LOGL_ERROR, "No transaction or transaction without lchan?!?\n"); + return -EIO; + } + + /* RTP socket of remote end has meanwhile died */ + if (!remote_trans->conn->lchan->abis_ip.rtp_socket) + return -EIO; + + return rtp_send_frame(remote_trans->conn->lchan->abis_ip.rtp_socket, dfr); +} + + +/* Internal MNCC handler input function (from CC -> MNCC -> here) */ +int int_mncc_recv(struct gsm_network *net, struct msgb *msg) +{ + void *arg = msgb_data(msg); + struct gsm_mncc *data = arg; + int msg_type = data->msg_type; + int callref; + struct gsm_call *call = NULL, *callt; + int rc = 0; + + /* Special messages */ + switch(msg_type) { + } + + /* find callref */ + callref = data->callref; + llist_for_each_entry(callt, &call_list, entry) { + if (callt->callref == callref) { + call = callt; + break; + } + } + + /* create callref, if setup is received */ + if (!call) { + if (msg_type != MNCC_SETUP_IND) + goto out_free; /* drop */ + /* create call */ + if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) { + struct gsm_mncc rel; + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = callref; + mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + mncc_tx_to_cc(net, MNCC_REL_REQ, &rel); + goto out_free; + } + llist_add_tail(&call->entry, &call_list); + call->net = net; + call->callref = callref; + DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref); + } + + switch (msg_type) { + case GSM_TCHF_FRAME: + case GSM_TCHF_FRAME_EFR: + break; + default: + DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref, + get_mncc_name(msg_type)); + break; + } + + switch(msg_type) { + case MNCC_SETUP_IND: + rc = mncc_setup_ind(call, msg_type, arg); + break; + case MNCC_SETUP_CNF: + rc = mncc_setup_cnf(call, msg_type, arg); + break; + case MNCC_SETUP_COMPL_IND: + break; + case MNCC_CALL_CONF_IND: + /* we now need to MODIFY the channel */ + data->lchan_mode = determine_lchan_mode(data); + mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, data); + break; + case MNCC_ALERT_IND: + rc = mncc_alert_ind(call, msg_type, arg); + break; + case MNCC_NOTIFY_IND: + rc = mncc_notify_ind(call, msg_type, arg); + break; + case MNCC_DISC_IND: + rc = mncc_disc_ind(call, msg_type, arg); + break; + case MNCC_REL_IND: + case MNCC_REJ_IND: + rc = mncc_rel_ind(call, msg_type, arg); + break; + case MNCC_REL_CNF: + rc = mncc_rel_cnf(call, msg_type, arg); + break; + case MNCC_FACILITY_IND: + break; + case MNCC_START_DTMF_IND: + break; + case MNCC_STOP_DTMF_IND: + break; + case MNCC_MODIFY_IND: + mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n", + call->callref, data->cause.value); + rc = mncc_tx_to_cc(net, MNCC_MODIFY_REJ, data); + break; + case MNCC_MODIFY_CNF: + break; + case MNCC_HOLD_IND: + mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n", + call->callref, data->cause.value); + rc = mncc_tx_to_cc(net, MNCC_HOLD_REJ, data); + break; + case MNCC_RETRIEVE_IND: + mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n", + call->callref, data->cause.value); + rc = mncc_tx_to_cc(net, MNCC_RETRIEVE_REJ, data); + break; + case GSM_TCHF_FRAME: + case GSM_TCHF_FRAME_EFR: + rc = mncc_rcv_tchf(call, msg_type, arg); + break; + default: + LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref); + break; + } + +out_free: + msgb_free(msg); + + return rc; +} diff --git a/src/libmsc/mncc_sock.c b/src/libmsc/mncc_sock.c new file mode 100644 index 000000000..cf4bca87a --- /dev/null +++ b/src/libmsc/mncc_sock.c @@ -0,0 +1,365 @@ +/* mncc_sock.c: Tie the MNCC interface to a unix domain socket */ + +/* (C) 2008-2010 by Harald Welte + * (C) 2009 by Andreas Eversberg + * (C) 2012 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +struct mncc_sock_state { + struct gsm_network *net; + struct osmo_fd listen_bfd; /* fd for listen socket */ + struct osmo_fd conn_bfd; /* fd for connection to lcr */ +}; + +/* input from CC code into mncc_sock */ +int mncc_sock_from_cc(struct gsm_network *net, struct msgb *msg) +{ + struct gsm_mncc *mncc_in = (struct gsm_mncc *) msgb_data(msg); + int msg_type = mncc_in->msg_type; + + /* Check if we currently have a MNCC handler connected */ + if (net->mncc_state->conn_bfd.fd < 0) { + LOGP(DMNCC, LOGL_ERROR, "mncc_sock receives %s for external CC app " + "but socket is gone\n", get_mncc_name(msg_type)); + if (msg_type != GSM_TCHF_FRAME && + msg_type != GSM_TCHF_FRAME_EFR) { + /* release the request */ + struct gsm_mncc mncc_out; + memset(&mncc_out, 0, sizeof(mncc_out)); + mncc_out.callref = mncc_in->callref; + mncc_set_cause(&mncc_out, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_TEMP_FAILURE); + mncc_tx_to_cc(net, MNCC_REL_REQ, &mncc_out); + } + /* free the original message */ + msgb_free(msg); + return -1; + } + + /* FIXME: check for some maximum queue depth? */ + + /* Actually enqueue the message and mark socket write need */ + msgb_enqueue(&net->upqueue, msg); + net->mncc_state->conn_bfd.when |= BSC_FD_WRITE; + return 0; +} + +/* FIXME: move this to libosmocore */ +int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path); + +static void mncc_sock_close(struct mncc_sock_state *state) +{ + struct osmo_fd *bfd = &state->conn_bfd; + + LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has LOST connection\n"); + + close(bfd->fd); + bfd->fd = -1; + osmo_fd_unregister(bfd); + + /* re-enable the generation of ACCEPT for new connections */ + state->listen_bfd.when |= BSC_FD_READ; + + /* release all exisitng calls */ + gsm0408_clear_all_trans(state->net, GSM48_PDISC_CC); + + /* flush the queue */ + while (!llist_empty(&state->net->upqueue)) { + struct msgb *msg = msgb_dequeue(&state->net->upqueue); + msgb_free(msg); + } +} + +static int mncc_sock_read(struct osmo_fd *bfd) +{ + struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data; + struct gsm_mncc *mncc_prim; + struct msgb *msg; + int rc; + + msg = msgb_alloc(sizeof(*mncc_prim)+256, "mncc_sock_rx"); + if (!msg) + return -ENOMEM; + + mncc_prim = (struct gsm_mncc *) msg->tail; + + rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0); + if (rc == 0) + goto close; + + if (rc < 0) { + if (errno == EAGAIN) + return 0; + goto close; + } + + rc = mncc_tx_to_cc(state->net, mncc_prim->msg_type, mncc_prim); + + /* as we always synchronously process the message in mncc_send() and + * its callbacks, we can free the message here. */ + msgb_free(msg); + + return rc; + +close: + msgb_free(msg); + mncc_sock_close(state); + return -1; +} + +static int mncc_sock_write(struct osmo_fd *bfd) +{ + struct mncc_sock_state *state = bfd->data; + struct gsm_network *net = state->net; + int rc; + + while (!llist_empty(&net->upqueue)) { + struct msgb *msg, *msg2; + struct gsm_mncc *mncc_prim; + + /* peek at the beginning of the queue */ + msg = llist_entry(net->upqueue.next, struct msgb, list); + mncc_prim = (struct gsm_mncc *)msg->data; + + bfd->when &= ~BSC_FD_WRITE; + + /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */ + if (!msgb_length(msg)) { + LOGP(DMNCC, LOGL_ERROR, "message type (%d) with ZERO " + "bytes!\n", mncc_prim->msg_type); + goto dontsend; + } + + /* try to send it over the socket */ + rc = write(bfd->fd, msgb_data(msg), msgb_length(msg)); + if (rc == 0) + goto close; + if (rc < 0) { + if (errno == EAGAIN) { + bfd->when |= BSC_FD_WRITE; + break; + } + goto close; + } + +dontsend: + /* _after_ we send it, we can deueue */ + msg2 = msgb_dequeue(&net->upqueue); + assert(msg == msg2); + msgb_free(msg); + } + return 0; + +close: + mncc_sock_close(state); + + return -1; +} + +static int mncc_sock_cb(struct osmo_fd *bfd, unsigned int flags) +{ + int rc = 0; + + if (flags & BSC_FD_READ) + rc = mncc_sock_read(bfd); + if (rc < 0) + return rc; + + if (flags & BSC_FD_WRITE) + rc = mncc_sock_write(bfd); + + return rc; +} + +/** + * Send a version indication to the remote. + */ +static void queue_hello(struct mncc_sock_state *mncc) +{ + struct gsm_mncc_hello *hello; + struct msgb *msg; + + msg = msgb_alloc(512, "mncc hello"); + if (!msg) { + LOGP(DMNCC, LOGL_ERROR, "Failed to allocate hello.\n"); + mncc_sock_close(mncc); + return; + } + + hello = (struct gsm_mncc_hello *) msgb_put(msg, sizeof(*hello)); + hello->msg_type = MNCC_SOCKET_HELLO; + hello->version = MNCC_SOCK_VERSION; + hello->mncc_size = sizeof(struct gsm_mncc); + hello->data_frame_size = sizeof(struct gsm_data_frame); + hello->called_offset = offsetof(struct gsm_mncc, called); + hello->signal_offset = offsetof(struct gsm_mncc, signal); + hello->emergency_offset = offsetof(struct gsm_mncc, emergency); + hello->lchan_type_offset = offsetof(struct gsm_mncc, lchan_type); + + msgb_enqueue(&mncc->net->upqueue, msg); + mncc->conn_bfd.when |= BSC_FD_WRITE; +} + +/* accept a new connection */ +static int mncc_sock_accept(struct osmo_fd *bfd, unsigned int flags) +{ + struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data; + struct osmo_fd *conn_bfd = &state->conn_bfd; + struct sockaddr_un un_addr; + socklen_t len; + int rc; + + len = sizeof(un_addr); + rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len); + if (rc < 0) { + LOGP(DMNCC, LOGL_ERROR, "Failed to accept a new connection\n"); + return -1; + } + + if (conn_bfd->fd >= 0) { + LOGP(DMNCC, LOGL_NOTICE, "MNCC app connects but we already have " + "another active connection ?!?\n"); + /* We already have one MNCC app connected, this is all we support */ + state->listen_bfd.when &= ~BSC_FD_READ; + close(rc); + return 0; + } + + conn_bfd->fd = rc; + conn_bfd->when = BSC_FD_READ; + conn_bfd->cb = mncc_sock_cb; + conn_bfd->data = state; + + if (osmo_fd_register(conn_bfd) != 0) { + LOGP(DMNCC, LOGL_ERROR, "Failed to register new connection fd\n"); + close(conn_bfd->fd); + conn_bfd->fd = -1; + return -1; + } + + LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has connection with external " + "call control application\n"); + + queue_hello(state); + return 0; +} + + +int mncc_sock_init(struct gsm_network *net) +{ + struct mncc_sock_state *state; + struct osmo_fd *bfd; + int rc; + + state = talloc_zero(tall_bsc_ctx, struct mncc_sock_state); + if (!state) + return -ENOMEM; + + state->net = net; + state->conn_bfd.fd = -1; + + bfd = &state->listen_bfd; + + rc = osmo_unixsock_listen(bfd, SOCK_SEQPACKET, "/tmp/bsc_mncc"); + if (rc < 0) { + LOGP(DMNCC, LOGL_ERROR, "Could not create unix socket: %s\n", + strerror(errno)); + talloc_free(state); + return rc; + } + + bfd->when = BSC_FD_READ; + bfd->cb = mncc_sock_accept; + bfd->data = state; + + rc = osmo_fd_register(bfd); + if (rc < 0) { + LOGP(DMNCC, LOGL_ERROR, "Could not register listen fd: %d\n", rc); + close(bfd->fd); + talloc_free(state); + return rc; + } + + net->mncc_state = state; + + return 0; +} + +/* FIXME: move this to libosmocore */ +int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path) +{ + struct sockaddr_un local; + unsigned int namelen; + int rc; + + bfd->fd = socket(AF_UNIX, type, 0); + + if (bfd->fd < 0) { + fprintf(stderr, "Failed to create Unix Domain Socket.\n"); + return -1; + } + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + unlink(local.sun_path); + + /* we use the same magic that X11 uses in Xtranssock.c for + * calculating the proper length of the sockaddr */ +#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) + local.sun_len = strlen(local.sun_path); +#endif +#if defined(BSD44SOCKETS) || defined(SUN_LEN) + namelen = SUN_LEN(&local); +#else + namelen = strlen(local.sun_path) + + offsetof(struct sockaddr_un, sun_path); +#endif + + rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); + if (rc != 0) { + fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n", + local.sun_path); + return -1; + } + + if (listen(bfd->fd, 0) != 0) { + fprintf(stderr, "Failed to listen.\n"); + return -1; + } + + return 0; +} diff --git a/src/libmsc/osmo_msc.c b/src/libmsc/osmo_msc.c new file mode 100644 index 000000000..31b72b925 --- /dev/null +++ b/src/libmsc/osmo_msc.c @@ -0,0 +1,177 @@ +/* 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); + if (conn->put_channel) { + conn->put_channel = 0; + subscr_put_channel(conn->subscr); + } + return 1; +} + +static int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, + uint16_t chosen_channel) +{ + gsm0408_new_conn(conn); + gsm0408_dispatch(conn, msg); + + /* TODO: do better */ + 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; + 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) +{ +} + +static void msc_assign_fail(struct gsm_subscriber_connection *conn, + uint8_t cause, uint8_t *rr_cause) +{ +} + +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); + if (conn->put_channel) { + conn->put_channel = 0; + subscr_put_channel(conn->subscr); + } + subscr_con_free(conn); +} diff --git a/src/libmsc/rrlp.c b/src/libmsc/rrlp.c new file mode 100644 index 000000000..161456a06 --- /dev/null +++ b/src/libmsc/rrlp.c @@ -0,0 +1,104 @@ +/* Radio Resource LCS (Location) Protocol, GMS TS 04.31 */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + + +#include +#include +#include +#include + +/* RRLP msPositionReq, nsBased, + * Accuracy=60, Method=gps, ResponseTime=2, oneSet */ +static const uint8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 }; + +/* RRLP msPositionReq, msBasedPref, + Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ +static const uint8_t ms_pref_pos_req[] = { 0x40, 0x02, 0x79, 0x50 }; + +/* RRLP msPositionReq, msAssistedPref, + Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ +static const uint8_t ass_pref_pos_req[] = { 0x40, 0x03, 0x79, 0x50 }; + +static int send_rrlp_req(struct gsm_subscriber_connection *conn) +{ + struct gsm_network *net = conn->bts->network; + const uint8_t *req; + + switch (net->rrlp.mode) { + case RRLP_MODE_MS_BASED: + req = ms_based_pos_req; + break; + case RRLP_MODE_MS_PREF: + req = ms_pref_pos_req; + break; + case RRLP_MODE_ASS_PREF: + req = ass_pref_pos_req; + break; + case RRLP_MODE_NONE: + default: + return 0; + } + + return gsm48_send_rr_app_info(conn, 0x00, + sizeof(ms_based_pos_req), req); +} + +static int subscr_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_subscriber *subscr; + struct gsm_subscriber_connection *conn; + + switch (signal) { + case S_SUBSCR_ATTACHED: + /* A subscriber has attached. */ + subscr = signal_data; + conn = connection_for_subscr(subscr); + if (!conn) + break; + send_rrlp_req(conn); + break; + } + return 0; +} + +static int paging_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct paging_signal_data *psig_data = signal_data; + + switch (signal) { + case S_PAGING_SUCCEEDED: + /* A subscriber has attached. */ + send_rrlp_req(psig_data->conn); + break; + case S_PAGING_EXPIRED: + break; + } + return 0; +} + +void on_dso_load_rrlp(void) +{ + osmo_signal_register_handler(SS_SUBSCR, subscr_sig_cb, NULL); + osmo_signal_register_handler(SS_PAGING, paging_sig_cb, NULL); +} diff --git a/src/libmsc/silent_call.c b/src/libmsc/silent_call.c new file mode 100644 index 000000000..cdc82b534 --- /dev/null +++ b/src/libmsc/silent_call.c @@ -0,0 +1,143 @@ +/* GSM silent call feature */ + +/* + * (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* paging of the requested subscriber has completed */ +static int paging_cb_silent(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *_conn, void *_data) +{ + struct gsm_subscriber_connection *conn = _conn; + struct scall_signal_data sigdata; + int rc = 0; + + if (hooknum != GSM_HOOK_RR_PAGING) + return -EINVAL; + + DEBUGP(DLSMS, "paging_cb_silent: "); + + sigdata.conn = conn; + sigdata.data = _data; + + switch (event) { + case GSM_PAGING_SUCCEEDED: + DEBUGPC(DLSMS, "success, using Timeslot %u on ARFCN %u\n", + conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn); + conn->silent_call = 1; + /* increment lchan reference count */ + osmo_signal_dispatch(SS_SCALL, S_SCALL_SUCCESS, &sigdata); + break; + case GSM_PAGING_EXPIRED: + case GSM_PAGING_BUSY: + case GSM_PAGING_OOM: + DEBUGP(DLSMS, "expired\n"); + osmo_signal_dispatch(SS_SCALL, S_SCALL_EXPIRED, &sigdata); + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +/* receive a layer 3 message from a silent call */ +int silent_call_rx(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + /* FIXME: do something like sending it through a UDP port */ + return 0; +} + +struct msg_match { + uint8_t pdisc; + uint8_t msg_type; +}; + +/* list of messages that are handled inside OpenBSC, even in a silent call */ +static const struct msg_match silent_call_accept[] = { + { GSM48_PDISC_MM, GSM48_MT_MM_LOC_UPD_REQUEST }, + { GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_REQ }, +}; + +/* decide if we need to reroute a message as part of a silent call */ +int silent_call_reroute(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t pdisc = gh->proto_discr & 0x0f; + int i; + + /* if we're not part of a silent call, never reroute */ + if (!conn->silent_call) + return 0; + + /* check if we are a special message that is handled in openbsc */ + for (i = 0; i < ARRAY_SIZE(silent_call_accept); i++) { + if (silent_call_accept[i].pdisc == pdisc && + silent_call_accept[i].msg_type == gh->msg_type) + return 0; + } + + /* otherwise, reroute */ + return 1; +} + + +/* initiate a silent call with a given subscriber */ +int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type) +{ + int rc; + + rc = paging_request(subscr->net, subscr, type, + paging_cb_silent, data); + return rc; +} + +/* end a silent call with a given subscriber */ +int gsm_silent_call_stop(struct gsm_subscriber *subscr) +{ + struct gsm_subscriber_connection *conn; + + conn = connection_for_subscr(subscr); + if (!conn) + return -EINVAL; + + /* did we actually establish a silent call for this guy? */ + if (!conn->silent_call) + return -EINVAL; + + conn->silent_call = 0; + msc_release_connection(conn); + + return 0; +} diff --git a/src/libmsc/smpp_openbsc.c b/src/libmsc/smpp_openbsc.c new file mode 100644 index 000000000..8e0085d0c --- /dev/null +++ b/src/libmsc/smpp_openbsc.c @@ -0,0 +1,553 @@ +/* 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 "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, addr); + break; + case NPI_ISDN_E163_E164: + case NPI_Private: + subscr = subscr_get_by_extension(net, addr); + break; + default: + LOGP(DSMPP, LOGL_NOTICE, "Unsupported NPI: %u\n", npi); + break; + } + + 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"); + return ESME_ROPTPARNOTALLWD; + } + sms_msg = t->value.octet; + sms_msg_len = t->length; + } else if (submit->sm_length) { + sms_msg = submit->short_message; + sms_msg_len = submit->sm_length; + } else { + sms_msg = NULL; + sms_msg_len = 0; + } + + sms = sms_alloc(); + sms->source = SMS_SOURCE_SMPP; + sms->smpp.sequence_nr = submit->sequence_number; + + /* fill in the destination address */ + sms->receiver = dest; + sms->dst.ton = submit->dest_addr_ton; + sms->dst.npi = submit->dest_addr_npi; + strncpy(sms->dst.addr, dest->extension, sizeof(sms->dst.addr)-1); + + /* fill in the source address */ + sms->sender = subscr_get_by_id(net, 1); + sms->src.ton = submit->source_addr_ton; + sms->src.npi = submit->source_addr_npi; + strncpy(sms->src.addr, (char *)submit->source_addr, sizeof(sms->src.addr)-1); + + if (submit->esm_class & 0x40) + sms->ud_hdr_ind = 1; + + if (submit->esm_class & 0x80) { + sms->reply_path_req = 1; +#warning Implement reply path + } + + if (submit->data_coding == 0x00 || /* SMSC default */ + submit->data_coding == 0x01) { /* GSM default alphabet */ + sms->data_coding_scheme = GSM338_DCS_1111_7BIT; + mode = MODE_7BIT; + } else if ((submit->data_coding & 0xFC) == 0xF0) { /* 03.38 DCS default */ + /* pass DCS 1:1 through from SMPP to GSM */ + sms->data_coding_scheme = submit->data_coding; + mode = MODE_7BIT; + } else if (submit->data_coding == 0x02 || + submit->data_coding == 0x04) { + /* 8-bit binary */ + sms->data_coding_scheme = GSM338_DCS_1111_8BIT_DATA; + mode = MODE_8BIT; + } else if ((submit->data_coding & 0xFC) == 0xF4) { /* 03.38 DCS 8bit */ + /* pass DCS 1:1 through from SMPP to GSM */ + sms->data_coding_scheme = submit->data_coding; + mode = MODE_8BIT; + } else if (submit->data_coding == 0x80) { + /* 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; +} + +/*! \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; + int rc = 0; + + if (!sms) + return 0; + + if (sms->source != SMS_SOURCE_SMPP) + return 0; + + switch (signal) { + 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; + } + + 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; + struct osmo_esme *esme; + 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; + } + + 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 */ + smpp_tx_alert(esme, TON_Subscriber_Number, + NPI_Land_Mobile_E212, subscr->imsi, + smpp_avail_status); + } + + return 0; +} + +/* GSM 03.38 6.2.1 Character expanding (no decode!) */ +static int gsm_7bit_expand(char *text, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind) +{ + int i = 0; + int shift = 0; + uint8_t c; + + /* skip the user data header */ + if (ud_hdr_ind) { + /* get user data header length + 1 (for the 'user data header length'-field) */ + shift = ((user_data[0] + 1) * 8) / 7; + if ((((user_data[0] + 1) * 8) % 7) != 0) + shift++; + septet_l = septet_l - shift; + } + + for (i = 0; i < septet_l; i++) { + c = + ((user_data[((i + shift) * 7 + 7) >> 3] << + (7 - (((i + shift) * 7 + 7) & 7))) | + (user_data[((i + shift) * 7) >> 3] >> + (((i + shift) * 7) & 7))) & 0x7f; + + *(text++) = c; + } + + *text = '\0'; + + return i; +} + + +/* FIXME: libsmpp34 helpers, they should be part of libsmpp34! */ +void append_tlv(tlv_t **req_tlv, uint16_t tag, + const uint8_t *data, uint16_t len) +{ + tlv_t tlv; + + memset(&tlv, 0, sizeof(tlv)); + tlv.tag = tag; + tlv.length = len; + memcpy(tlv.value.octet, data, tlv.length); + build_tlv(req_tlv, &tlv); +} +void append_tlv_u8(tlv_t **req_tlv, uint16_t tag, uint8_t val) +{ + tlv_t tlv; + + memset(&tlv, 0, sizeof(tlv)); + tlv.tag = tag; + tlv.length = 1; + tlv.value.val08 = val; + build_tlv(req_tlv, &tlv); +} +void append_tlv_u16(tlv_t **req_tlv, uint16_t tag, uint16_t val) +{ + tlv_t tlv; + + memset(&tlv, 0, sizeof(tlv)); + tlv.tag = tag; + tlv.length = 2; + tlv.value.val16 = htons(val); + build_tlv(req_tlv, &tlv); +} + +/* Append the Osmocom vendor-specific additional TLVs to a SMPP msg */ +static void append_osmo_tlvs(tlv_t **req_tlv, const struct gsm_lchan *lchan) +{ + int idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), + lchan->meas_rep_idx, 1); + const struct gsm_meas_rep *mr = &lchan->meas_rep[idx]; + const struct gsm_meas_rep_unidir *ul_meas = &mr->ul; + const struct gsm_meas_rep_unidir *dl_meas = &mr->dl; + + /* Osmocom vendor-specific SMPP34 extensions */ + append_tlv_u16(req_tlv, TLVID_osmo_arfcn, lchan->ts->trx->arfcn); + if (mr->flags & MEAS_REP_F_MS_L1) { + uint8_t ms_dbm; + append_tlv_u8(req_tlv, TLVID_osmo_ta, mr->ms_l1.ta); + ms_dbm = ms_pwr_dbm(lchan->ts->trx->bts->band, mr->ms_l1.pwr); + append_tlv_u8(req_tlv, TLVID_osmo_ms_l1_txpwr, ms_dbm); + } else if (mr->flags & MEAS_REP_F_MS_TO) + append_tlv_u8(req_tlv, TLVID_osmo_ta, mr->ms_timing_offset); + + append_tlv_u16(req_tlv, TLVID_osmo_rxlev_ul, + rxlev2dbm(ul_meas->full.rx_lev)); + append_tlv_u8(req_tlv, TLVID_osmo_rxqual_ul, ul_meas->full.rx_qual); + + if (mr->flags & MEAS_REP_F_DL_VALID) { + append_tlv_u16(req_tlv, TLVID_osmo_rxlev_dl, + rxlev2dbm(dl_meas->full.rx_lev)); + append_tlv_u8(req_tlv, TLVID_osmo_rxqual_dl, + dl_meas->full.rx_qual); + } + + if (lchan->conn && lchan->conn->subscr) { + struct gsm_subscriber *subscr = lchan->conn->subscr; + size_t imei_len = strlen(subscr->equipment.imei); + if (imei_len) + append_tlv(req_tlv, TLVID_osmo_imei, + (uint8_t *)subscr->equipment.imei, imei_len+1); + } +} + +static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms, + struct gsm_subscriber_connection *conn) +{ + struct deliver_sm_t deliver; + uint8_t dcs; + int mode; + + memset(&deliver, 0, sizeof(deliver)); + deliver.command_length = 0; + deliver.command_id = DELIVER_SM; + deliver.command_status = ESME_ROK; + + strcpy((char *)deliver.service_type, "CMT"); + if (esme->acl && esme->acl->deliver_src_imsi) { + deliver.source_addr_ton = TON_Subscriber_Number; + deliver.source_addr_npi = NPI_Land_Mobile_E212; + snprintf((char *)deliver.source_addr, + sizeof(deliver.source_addr), "%s", + sms->sender->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", + sms->sender->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 && conn->lchan) + append_osmo_tlvs(&deliver.tlv, conn->lchan); + + return smpp_tx_deliver(esme, &deliver); +} + +static struct smsc *g_smsc; + +int smpp_try_deliver(struct gsm_sms *sms, struct gsm_subscriber_connection *conn) +{ + struct osmo_esme *esme; + struct osmo_smpp_addr dst; + + memset(&dst, 0, sizeof(dst)); + dst.ton = sms->dst.ton; + dst.npi = sms->dst.npi; + memcpy(dst.addr, sms->dst.addr, sizeof(dst.addr)); + + esme = smpp_route(g_smsc, &dst); + if (!esme) + return 1; /* unknown subscriber */ + + return deliver_to_esme(esme, sms, conn); +} + +struct smsc *smsc_from_vty(struct vty *v) +{ + /* FIXME: this is ugly */ + return g_smsc; +} + +/*! \brief Initialize the OpenBSC SMPP interface */ +int smpp_openbsc_init(void *ctx, uint16_t port) +{ + struct smsc *smsc = talloc_zero(ctx, struct smsc); + int rc; + + rc = smpp_smsc_init(smsc, port); + if (rc < 0) + talloc_free(smsc); + + osmo_signal_register_handler(SS_SMS, smpp_sms_cb, smsc); + osmo_signal_register_handler(SS_SUBSCR, smpp_subscr_cb, smsc); + + g_smsc = smsc; + + smpp_vty_init(); + + return rc; +} + +void smpp_openbsc_set_net(struct gsm_network *net) +{ + g_smsc->priv = net; +} diff --git a/src/libmsc/smpp_smsc.c b/src/libmsc/smpp_smsc.c new file mode 100644 index 000000000..64ed2005f --- /dev/null +++ b/src/libmsc/smpp_smsc.c @@ -0,0 +1,942 @@ +/* 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 "smpp_smsc.h" + +#include +#include + +/*! \brief Ugly wrapper. libsmpp34 should do this itself! */ +#define SMPP34_UNPACK(rc, type, str, data, len) \ + memset(str, 0, sizeof(*str)); \ + rc = smpp34_unpack(type, str, data, len) + +enum emse_bind { + ESME_BIND_RX = 0x01, + ESME_BIND_TX = 0x02, +}; + +const struct value_string smpp_status_strs[] = { + { ESME_ROK, "No Error" }, + { ESME_RINVMSGLEN, "Message Length is invalid" }, + { ESME_RINVCMDLEN, "Command Length is invalid" }, + { ESME_RINVCMDID, "Invalid Command ID" }, + { ESME_RINVBNDSTS, "Incorrect BIND Status for given command" }, + { ESME_RALYBND, "ESME Already in Bound State" }, + { ESME_RINVPRTFLG, "Invalid Priority Flag" }, + { ESME_RINVREGDLVFLG, "Invalid Registered Delivery Flag" }, + { ESME_RSYSERR, "System Error" }, + { ESME_RINVSRCADR, "Invalid Source Address" }, + { ESME_RINVDSTADR, "Invalid Destination Address" }, + { ESME_RINVMSGID, "Message ID is invalid" }, + { ESME_RBINDFAIL, "Bind failed" }, + { ESME_RINVPASWD, "Invalid Password" }, + { ESME_RINVSYSID, "Invalid System ID" }, + { ESME_RCANCELFAIL, "Cancel SM Failed" }, + { ESME_RREPLACEFAIL, "Replace SM Failed" }, + { ESME_RMSGQFUL, "Message Queue Full" }, + { ESME_RINVSERTYP, "Invalid Service Type" }, + { ESME_RINVNUMDESTS, "Invalid number of destinations" }, + { ESME_RINVDLNAME, "Invalid Distribution List name" }, + { ESME_RINVDESTFLAG, "Destination flag is invalid" }, + { ESME_RINVSUBREP, "Invalid submit with replace request" }, + { ESME_RINVESMCLASS, "Invalid esm_class field data" }, + { ESME_RCNTSUBDL, "Cannot Submit to Distribution List" }, + { ESME_RSUBMITFAIL, "submit_sm or submit_multi failed" }, + { ESME_RINVSRCTON, "Invalid Source address TON" }, + { ESME_RINVSRCNPI, "Invalid Sourec address NPI" }, + { ESME_RINVDSTTON, "Invalid Destination address TON" }, + { ESME_RINVDSTNPI, "Invalid Desetination address NPI" }, + { ESME_RINVSYSTYP, "Invalid system_type field" }, + { ESME_RINVREPFLAG, "Invalid replace_if_present field" }, + { ESME_RINVNUMMSGS, "Invalid number of messages" }, + { ESME_RTHROTTLED, "Throttling error (ESME has exceeded message limits)" }, + { ESME_RINVSCHED, "Invalid Scheduled Delivery Time" }, + { ESME_RINVEXPIRY, "Invalid message validity period (Expiry time)" }, + { ESME_RINVDFTMSGID, "Predefined Message Invalid or Not Found" }, + { ESME_RX_T_APPN, "ESME Receiver Temporary App Error Code" }, + { ESME_RX_P_APPN, "ESME Receiver Permanent App Error Code" }, + { ESME_RX_R_APPN, "ESME Receiver Reject Message Error Code" }, + { ESME_RQUERYFAIL, "query_sm request failed" }, + { ESME_RINVOPTPARSTREAM,"Error in the optional part of the PDU Body" }, + { ESME_ROPTPARNOTALLWD, "Optional Parameter not allowed" }, + { ESME_RINVPARLEN, "Invalid Parameter Length" }, + { ESME_RMISSINGOPTPARAM,"Expected Optional Parameter missing" }, + { ESME_RINVOPTPARAMVAL, "Invalid Optional Parameter Value" }, + { ESME_RDELIVERYFAILURE,"Delivery Failure (used for data_sm_resp)" }, + { ESME_RUNKNOWNERR, "Unknown Error" }, + { 0, NULL } +}; + +/*! \brief compare if two SMPP addresses are equal */ +int smpp_addr_eq(const struct osmo_smpp_addr *a, + const struct osmo_smpp_addr *b) +{ + if (a->ton == b->ton && + a->npi == b->npi && + !strcmp(a->addr, b->addr)) + return 1; + + return 0; +} + + +struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, + const char *sys_id) +{ + struct osmo_smpp_acl *acl; + + llist_for_each_entry(acl, &smsc->acl_list, list) { + if (!strcmp(acl->system_id, sys_id)) + return acl; + } + + return NULL; +} + +struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id) +{ + struct osmo_smpp_acl *acl; + + if (strlen(sys_id) > SMPP_SYS_ID_LEN) + return NULL; + + if (smpp_acl_by_system_id(smsc, sys_id)) + return NULL; + + acl = talloc_zero(smsc, struct osmo_smpp_acl); + if (!acl) + return NULL; + + acl->smsc = smsc; + strcpy(acl->system_id, sys_id); + INIT_LLIST_HEAD(&acl->route_list); + + llist_add_tail(&acl->list, &smsc->acl_list); + + return acl; +} + +void smpp_acl_delete(struct osmo_smpp_acl *acl) +{ + struct osmo_smpp_route *r, *r2; + + llist_del(&acl->list); + + /* kill any active ESMEs */ + if (acl->esme) { + struct osmo_esme *esme = acl->esme; + osmo_fd_unregister(&esme->wqueue.bfd); + close(esme->wqueue.bfd.fd); + esme->wqueue.bfd.fd = -1; + esme->acl = NULL; + smpp_esme_put(esme); + } + + /* delete all routes for this ACL */ + llist_for_each_entry_safe(r, r2, &acl->route_list, list) { + llist_del(&r->list); + llist_del(&r->global_list); + talloc_free(r); + } + + talloc_free(acl); +} + +static struct osmo_smpp_route *route_alloc(struct osmo_smpp_acl *acl) +{ + struct osmo_smpp_route *r; + + r = talloc_zero(acl, struct osmo_smpp_route); + if (!r) + return NULL; + + llist_add_tail(&r->list, &acl->route_list); + llist_add_tail(&r->global_list, &acl->smsc->route_list); + + return r; +} + +int smpp_route_pfx_add(struct osmo_smpp_acl *acl, + const struct osmo_smpp_addr *pfx) +{ + struct osmo_smpp_route *r; + + llist_for_each_entry(r, &acl->route_list, list) { + if (r->type == SMPP_ROUTE_PREFIX && + smpp_addr_eq(&r->u.prefix, pfx)) + return -EEXIST; + } + + r = route_alloc(acl); + if (!r) + return -ENOMEM; + r->type = SMPP_ROUTE_PREFIX; + r->acl = acl; + memcpy(&r->u.prefix, pfx, sizeof(r->u.prefix)); + + return 0; +} + +int smpp_route_pfx_del(struct osmo_smpp_acl *acl, + const struct osmo_smpp_addr *pfx) +{ + struct osmo_smpp_route *r, *r2; + + llist_for_each_entry_safe(r, r2, &acl->route_list, list) { + if (r->type == SMPP_ROUTE_PREFIX && + smpp_addr_eq(&r->u.prefix, pfx)) { + llist_del(&r->list); + talloc_free(r); + return 0; + } + } + + return -ENODEV; +} + + +/*! \brief increaes the use/reference count */ +void smpp_esme_get(struct osmo_esme *esme) +{ + esme->use++; +} + +static void esme_destroy(struct osmo_esme *esme) +{ + osmo_wqueue_clear(&esme->wqueue); + if (esme->wqueue.bfd.fd >= 0) { + osmo_fd_unregister(&esme->wqueue.bfd); + close(esme->wqueue.bfd.fd); + } + llist_del(&esme->list); + talloc_free(esme); +} + +static uint32_t esme_inc_seq_nr(struct osmo_esme *esme) +{ + esme->own_seq_nr++; + if (esme->own_seq_nr > 0x7fffffff) + esme->own_seq_nr = 1; + + return esme->own_seq_nr; +} + +/*! \brief decrease the use/reference count, free if it is 0 */ +void smpp_esme_put(struct osmo_esme *esme) +{ + esme->use--; + if (esme->use <= 0) + esme_destroy(esme); +} + +/*! \brief try to find a SMPP route (ESME) for given destination */ +struct osmo_esme * +smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest) +{ + struct osmo_smpp_route *r; + struct osmo_smpp_acl *acl = NULL; + + DEBUGP(DSMPP, "Looking up route for (%u/%u/%s)\n", + dest->ton, dest->npi, dest->addr); + + /* search for a specific route */ + llist_for_each_entry(r, &smsc->route_list, global_list) { + switch (r->type) { + case SMPP_ROUTE_PREFIX: + DEBUGP(DSMPP, "Checking prefix route (%u/%u/%s)->%s\n", + r->u.prefix.ton, r->u.prefix.npi, r->u.prefix.addr, + r->acl->system_id); + if (r->u.prefix.ton == dest->ton && + r->u.prefix.npi == dest->npi && + !strncmp(r->u.prefix.addr, dest->addr, + strlen(r->u.prefix.addr))) { + DEBUGP(DSMPP, "Found prefix route ACL\n"); + acl = r->acl; + } + break; + default: + break; + } + + if (acl) + break; + } + + if (!acl) { + /* check for default route */ + if (smsc->def_route) { + DEBUGP(DSMPP, "Using existing default route\n"); + acl = smsc->def_route; + } + } + + if (acl && acl->esme) { + struct osmo_esme *esme; + DEBUGP(DSMPP, "ACL even has ESME, we can route to it!\n"); + esme = acl->esme; + if (esme->bind_flags & ESME_BIND_RX) + return esme; + else + LOGP(DSMPP, LOGL_NOTICE, "[%s] is matching route, " + "but not bound for Rx, discarding MO SMS\n", + esme->system_id); + } + + return NULL; +} + + +/*! \brief initialize the libsmpp34 data structure for a response */ +#define INIT_RESP(type, resp, req) { \ + memset((resp), 0, sizeof(*(resp))); \ + (resp)->command_length = 0; \ + (resp)->command_id = type; \ + (resp)->command_status = ESME_ROK; \ + (resp)->sequence_number = (req)->sequence_number; \ +} + +/*! \brief pack a libsmpp34 data strcutrure and send it to the ESME */ +#define PACK_AND_SEND(esme, ptr) pack_and_send(esme, (ptr)->command_id, ptr) +static int pack_and_send(struct osmo_esme *esme, uint32_t type, void *ptr) +{ + struct msgb *msg = msgb_alloc(4096, "SMPP_Tx"); + int rc, rlen; + if (!msg) + return -ENOMEM; + + rc = smpp34_pack(type, msg->tail, msgb_tailroom(msg), &rlen, ptr); + if (rc != 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Error during smpp34_pack(): %s\n", + esme->system_id, smpp34_strerror); + msgb_free(msg); + return -EINVAL; + } + msgb_put(msg, rlen); + + return osmo_wqueue_enqueue(&esme->wqueue, msg); +} + +/*! \brief transmit a generic NACK to a remote ESME */ +static int smpp_tx_gen_nack(struct osmo_esme *esme, uint32_t seq, uint32_t status) +{ + struct generic_nack_t nack; + char buf[SMALL_BUFF]; + + nack.command_length = 0; + nack.command_id = GENERIC_NACK; + nack.sequence_number = seq; + nack.command_status = status; + + LOGP(DSMPP, LOGL_ERROR, "[%s] Tx GENERIC NACK: %s\n", + esme->system_id, str_command_status(status, buf)); + + return PACK_AND_SEND(esme, &nack); +} + +/*! \brief retrieve SMPP command ID from a msgb */ +static inline uint32_t smpp_msgb_cmdid(struct msgb *msg) +{ + uint8_t *tmp = msgb_data(msg) + 4; + return ntohl(*(uint32_t *)tmp); +} + +/*! \brief retrieve SMPP sequence number from a msgb */ +static inline uint32_t smpp_msgb_seq(struct msgb *msg) +{ + uint8_t *tmp = msgb_data(msg); + return ntohl(*(uint32_t *)tmp); +} + +/*! \brief handle an incoming SMPP generic NACK */ +static int smpp_handle_gen_nack(struct osmo_esme *esme, struct msgb *msg) +{ + struct generic_nack_t nack; + char buf[SMALL_BUFF]; + int rc; + + SMPP34_UNPACK(rc, GENERIC_NACK, &nack, msgb_data(msg), + msgb_length(msg)); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", + esme->system_id, smpp34_strerror); + return rc; + } + + LOGP(DSMPP, LOGL_ERROR, "[%s] Rx GENERIC NACK: %s\n", + esme->system_id, str_command_status(nack.command_status, buf)); + + return 0; +} + +static int _process_bind(struct osmo_esme *esme, uint8_t if_version, + uint32_t bind_flags, const char *sys_id, + const char *passwd) +{ + struct osmo_smpp_acl *acl; + + if (if_version != SMPP_VERSION) + return ESME_RSYSERR; + + if (esme->bind_flags) + return ESME_RALYBND; + + esme->smpp_version = if_version; + snprintf(esme->system_id, sizeof(esme->system_id), "%s", sys_id); + + acl = smpp_acl_by_system_id(esme->smsc, esme->system_id); + if (!esme->smsc->accept_all) { + if (!acl) { + /* This system is unknown */ + return ESME_RINVSYSID; + } else { + if (strlen(acl->passwd) && + strcmp(acl->passwd, passwd)) { + return ESME_RINVPASWD; + } + } + } + if (acl) { + esme->acl = acl; + acl->esme = esme; + } + + esme->bind_flags = bind_flags; + + return ESME_ROK; +} + + +/*! \brief handle an incoming SMPP BIND RECEIVER */ +static int smpp_handle_bind_rx(struct osmo_esme *esme, struct msgb *msg) +{ + struct bind_receiver_t bind; + struct bind_receiver_resp_t bind_r; + int rc; + + SMPP34_UNPACK(rc, BIND_RECEIVER, &bind, msgb_data(msg), + msgb_length(msg)); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", + esme->system_id, smpp34_strerror); + return rc; + } + + INIT_RESP(BIND_TRANSMITTER_RESP, &bind_r, &bind); + + LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Rx from (Version %02x)\n", + bind.system_id, bind.interface_version); + + rc = _process_bind(esme, bind.interface_version, ESME_BIND_RX, + (const char *)bind.system_id, (const char *)bind.password); + bind_r.command_status = rc; + + return PACK_AND_SEND(esme, &bind_r); +} + +/*! \brief handle an incoming SMPP BIND TRANSMITTER */ +static int smpp_handle_bind_tx(struct osmo_esme *esme, struct msgb *msg) +{ + struct bind_transmitter_t bind; + struct bind_transmitter_resp_t bind_r; + struct tlv_t tlv; + int rc; + + SMPP34_UNPACK(rc, BIND_TRANSMITTER, &bind, msgb_data(msg), + msgb_length(msg)); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", + esme->system_id, smpp34_strerror); + return rc; + } + + INIT_RESP(BIND_TRANSMITTER_RESP, &bind_r, &bind); + + LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Tx (Version %02x)\n", + bind.system_id, bind.interface_version); + + rc = _process_bind(esme, bind.interface_version, ESME_BIND_TX, + (const char *)bind.system_id, (const char *)bind.password); + bind_r.command_status = rc; + + /* build response */ + snprintf((char *)bind_r.system_id, sizeof(bind_r.system_id), "%s", + esme->smsc->system_id); + + /* add interface version TLV */ + tlv.tag = TLVID_sc_interface_version; + tlv.length = sizeof(uint8_t); + tlv.value.val16 = esme->smpp_version; + build_tlv(&bind_r.tlv, &tlv); + + return PACK_AND_SEND(esme, &bind_r); +} + +/*! \brief handle an incoming SMPP BIND TRANSCEIVER */ +static int smpp_handle_bind_trx(struct osmo_esme *esme, struct msgb *msg) +{ + struct bind_transceiver_t bind; + struct bind_transceiver_resp_t bind_r; + int rc; + + SMPP34_UNPACK(rc, BIND_TRANSCEIVER, &bind, msgb_data(msg), + msgb_length(msg)); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", + esme->system_id, smpp34_strerror); + return rc; + } + + INIT_RESP(BIND_TRANSCEIVER_RESP, &bind_r, &bind); + + LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Trx (Version %02x)\n", + bind.system_id, bind.interface_version); + + rc = _process_bind(esme, bind.interface_version, ESME_BIND_RX|ESME_BIND_TX, + (const char *)bind.system_id, (const char *)bind.password); + bind_r.command_status = rc; + + return PACK_AND_SEND(esme, &bind_r); +} + +/*! \brief handle an incoming SMPP UNBIND */ +static int smpp_handle_unbind(struct osmo_esme *esme, struct msgb *msg) +{ + struct unbind_t unbind; + struct unbind_resp_t unbind_r; + int rc; + + SMPP34_UNPACK(rc, UNBIND, &unbind, msgb_data(msg), + msgb_length(msg)); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", + esme->system_id, smpp34_strerror); + return rc; + } + + INIT_RESP(UNBIND_RESP, &unbind_r, &unbind); + + LOGP(DSMPP, LOGL_INFO, "[%s] Rx UNBIND\n", esme->system_id); + + if (esme->bind_flags == 0) { + unbind_r.command_status = ESME_RINVBNDSTS; + goto err; + } + + esme->bind_flags = 0; +err: + return PACK_AND_SEND(esme, &unbind_r); +} + +/*! \brief handle an incoming SMPP ENQUIRE LINK */ +static int smpp_handle_enq_link(struct osmo_esme *esme, struct msgb *msg) +{ + struct enquire_link_t enq; + struct enquire_link_resp_t enq_r; + int rc; + + SMPP34_UNPACK(rc, ENQUIRE_LINK, &enq, msgb_data(msg), + msgb_length(msg)); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", + esme->system_id, smpp34_strerror); + return rc; + } + + LOGP(DSMPP, LOGL_DEBUG, "[%s] Rx Enquire Link\n", esme->system_id); + + INIT_RESP(ENQUIRE_LINK_RESP, &enq_r, &enq); + + LOGP(DSMPP, LOGL_DEBUG, "[%s] Tx Enquire Link Response\n", esme->system_id); + + return PACK_AND_SEND(esme, &enq_r); +} + +/*! \brief send a SUBMIT-SM RESPONSE to a remote ESME */ +int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr, + uint32_t command_status, char *msg_id) +{ + struct submit_sm_resp_t submit_r; + + memset(&submit_r, 0, sizeof(submit_r)); + submit_r.command_length = 0; + submit_r.command_id = SUBMIT_SM_RESP; + submit_r.command_status = command_status; + submit_r.sequence_number= sequence_nr; + snprintf((char *) submit_r.message_id, sizeof(submit_r.message_id), "%s", msg_id); + + return PACK_AND_SEND(esme, &submit_r); +} + +static const struct value_string smpp_avail_strs[] = { + { 0, "Available" }, + { 1, "Denied" }, + { 2, "Unavailable" }, + { 0, NULL } +}; + +/*! \brief send an ALERT_NOTIFICATION to a remote ESME */ +int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, + const char *addr, uint8_t avail_status) +{ + struct alert_notification_t alert; + struct tlv_t tlv; + + memset(&alert, 0, sizeof(alert)); + alert.command_length = 0; + alert.command_id = ALERT_NOTIFICATION; + alert.command_status = ESME_ROK; + alert.sequence_number = esme_inc_seq_nr(esme); + alert.source_addr_ton = ton; + alert.source_addr_npi = npi; + snprintf((char *)alert.source_addr, sizeof(alert.source_addr), "%s", addr); + + tlv.tag = TLVID_ms_availability_status; + tlv.length = sizeof(uint8_t); + tlv.value.val08 = avail_status; + build_tlv(&alert.tlv, &tlv); + + LOGP(DSMPP, LOGL_DEBUG, "[%s] Tx ALERT_NOTIFICATION (%s/%u/%u): %s\n", + esme->system_id, alert.source_addr, alert.source_addr_ton, + alert.source_addr_npi, + get_value_string(smpp_avail_strs, avail_status)); + + return PACK_AND_SEND(esme, &alert); +} + +/* \brief send a DELIVER-SM message to given ESME */ +int smpp_tx_deliver(struct osmo_esme *esme, struct deliver_sm_t *deliver) +{ + deliver->sequence_number = esme_inc_seq_nr(esme); + + return PACK_AND_SEND(esme, deliver); +} + +/*! \brief handle an incoming SMPP DELIVER-SM RESPONSE */ +static int smpp_handle_deliver_resp(struct osmo_esme *esme, struct msgb *msg) +{ + struct deliver_sm_resp_t deliver_r; + int rc; + + memset(&deliver_r, 0, sizeof(deliver_r)); + SMPP34_UNPACK(rc, DELIVER_SM_RESP, &deliver_r, msgb_data(msg), + msgb_length(msg)); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", + esme->system_id, smpp34_strerror); + return rc; + } + + LOGP(DSMPP, LOGL_INFO, "[%s] Rx DELIVER-SM RESP (%s)\n", + esme->system_id, get_value_string(smpp_status_strs, + deliver_r.command_status)); + + return 0; +} + +/*! \brief handle an incoming SMPP SUBMIT-SM */ +static int smpp_handle_submit(struct osmo_esme *esme, struct msgb *msg) +{ + struct submit_sm_t submit; + struct submit_sm_resp_t submit_r; + int rc; + + memset(&submit, 0, sizeof(submit)); + SMPP34_UNPACK(rc, SUBMIT_SM, &submit, msgb_data(msg), + msgb_length(msg)); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", + esme->system_id, smpp34_strerror); + return rc; + } + + INIT_RESP(SUBMIT_SM_RESP, &submit_r, &submit); + + if (!(esme->bind_flags & ESME_BIND_TX)) { + submit_r.command_status = ESME_RINVBNDSTS; + return PACK_AND_SEND(esme, &submit_r); + } + + LOGP(DSMPP, LOGL_INFO, "[%s] Rx SUBMIT-SM (%s/%u/%u)\n", + esme->system_id, submit.destination_addr, + submit.dest_addr_ton, submit.dest_addr_npi); + + INIT_RESP(SUBMIT_SM_RESP, &submit_r, &submit); + + rc = handle_smpp_submit(esme, &submit, &submit_r); + if (rc == 0) + return PACK_AND_SEND(esme, &submit_r); + + return rc; +} + +/*! \brief one complete SMPP PDU from the ESME has been received */ +static int smpp_pdu_rx(struct osmo_esme *esme, struct msgb *msg __uses) +{ + uint32_t cmd_id = smpp_msgb_cmdid(msg); + int rc = 0; + + LOGP(DSMPP, LOGL_DEBUG, "[%s] smpp_pdu_rx(%s)\n", esme->system_id, + osmo_hexdump(msgb_data(msg), msgb_length(msg))); + + switch (cmd_id) { + case GENERIC_NACK: + rc = smpp_handle_gen_nack(esme, msg); + break; + case BIND_RECEIVER: + rc = smpp_handle_bind_rx(esme, msg); + break; + case BIND_TRANSMITTER: + rc = smpp_handle_bind_tx(esme, msg); + break; + case BIND_TRANSCEIVER: + rc = smpp_handle_bind_trx(esme, msg); + break; + case UNBIND: + rc = smpp_handle_unbind(esme, msg); + break; + case ENQUIRE_LINK: + rc = smpp_handle_enq_link(esme, msg); + break; + case SUBMIT_SM: + rc = smpp_handle_submit(esme, msg); + break; + case DELIVER_SM_RESP: + rc = smpp_handle_deliver_resp(esme, msg); + break; + case DELIVER_SM: + break; + case DATA_SM: + break; + case CANCEL_SM: + case QUERY_SM: + case REPLACE_SM: + case SUBMIT_MULTI: + LOGP(DSMPP, LOGL_NOTICE, "[%s] Unimplemented PDU Commmand " + "0x%08x\n", esme->system_id, cmd_id); + break; + default: + LOGP(DSMPP, LOGL_ERROR, "[%s] Unknown PDU Command 0x%08x\n", + esme->system_id, cmd_id); + rc = smpp_tx_gen_nack(esme, smpp_msgb_seq(msg), ESME_RINVCMDID); + break; + } + + return rc; +} + +/* !\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; + 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); + msgb_free(esme->read_msg); + esme->read_msg = NULL; + esme->read_idx = 0; + esme->read_len = 0; + esme->read_state = READ_ST_IN_LEN; + } + break; + } + + return 0; +dead_socket: + msgb_free(esme->read_msg); + osmo_fd_unregister(&esme->wqueue.bfd); + close(esme->wqueue.bfd.fd); + esme->wqueue.bfd.fd = -1; + smpp_esme_put(esme); + + return 0; +} + +/* call-back of write queue once it wishes to write a message to the socket */ +static int esme_link_write_cb(struct osmo_fd *ofd, struct msgb *msg) +{ + struct osmo_esme *esme = ofd->data; + int rc; + + rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); + if (rc == 0) { + osmo_fd_unregister(&esme->wqueue.bfd); + close(esme->wqueue.bfd.fd); + esme->wqueue.bfd.fd = -1; + smpp_esme_put(esme); + } else if (rc < msgb_length(msg)) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Short write\n", esme->system_id); + return -1; + } + + return 0; +} + +/* callback for already-accepted new TCP socket */ +static int link_accept_cb(struct smsc *smsc, int fd, + struct sockaddr_storage *s, socklen_t s_len) +{ + struct osmo_esme *esme = talloc_zero(smsc, struct osmo_esme); + if (!esme) { + close(fd); + return -ENOMEM; + } + + smpp_esme_get(esme); + esme->own_seq_nr = rand(); + esme_inc_seq_nr(esme); + esme->smsc = smsc; + osmo_wqueue_init(&esme->wqueue, 10); + esme->wqueue.bfd.fd = fd; + esme->wqueue.bfd.data = esme; + esme->wqueue.bfd.when = BSC_FD_READ; + + if (osmo_fd_register(&esme->wqueue.bfd) != 0) { + close(fd); + talloc_free(esme); + return -EIO; + } + + esme->wqueue.read_cb = esme_link_read_cb; + esme->wqueue.write_cb = esme_link_write_cb; + + esme->sa_len = OSMO_MIN(sizeof(esme->sa), s_len); + memcpy(&esme->sa, s, esme->sa_len); + + llist_add_tail(&esme->list, &smsc->esme_list); + + return 0; +} + +/* callback of listening TCP socket */ +static int smsc_fd_cb(struct osmo_fd *ofd, unsigned int what) +{ + int rc; + struct sockaddr_storage sa; + socklen_t sa_len = sizeof(sa); + + rc = accept(ofd->fd, (struct sockaddr *)&sa, &sa_len); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "Accept returns %d (%s)\n", + rc, strerror(errno)); + return rc; + } + return link_accept_cb(ofd->data, rc, &sa, sa_len); +} + +/*! \brief Initialize the SMSC-side SMPP implementation */ +int smpp_smsc_init(struct smsc *smsc, uint16_t port) +{ + int rc; + + /* default port for SMPP */ + if (port == 0) + port = 2775; + + /* This will not work if we were to actually ever use FD 0 + * (stdin) for this ... */ + if (smsc->listen_ofd.fd <= 0) { + INIT_LLIST_HEAD(&smsc->esme_list); + INIT_LLIST_HEAD(&smsc->acl_list); + INIT_LLIST_HEAD(&smsc->route_list); + smsc->listen_ofd.data = smsc; + smsc->listen_ofd.cb = smsc_fd_cb; + } else { + close(smsc->listen_ofd.fd); + osmo_fd_unregister(&smsc->listen_ofd); + } + + rc = osmo_sock_init_ofd(&smsc->listen_ofd, AF_UNSPEC, SOCK_STREAM, + IPPROTO_TCP, NULL, port, + OSMO_SOCK_F_BIND); + + /* if there is an error, try to re-bind to the old port */ + if (rc < 0) { + rc = osmo_sock_init_ofd(&smsc->listen_ofd, AF_UNSPEC, + SOCK_STREAM, IPPROTO_TCP, NULL, + smsc->listen_port, OSMO_SOCK_F_BIND); + } else + smsc->listen_port = port; + + return rc; +} diff --git a/src/libmsc/smpp_smsc.h b/src/libmsc/smpp_smsc.h new file mode 100644 index 000000000..21d28dda6 --- /dev/null +++ b/src/libmsc/smpp_smsc.h @@ -0,0 +1,134 @@ +#ifndef _SMPP_SMSC_H +#define _SMPP_SMSC_H + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define SMPP_SYS_ID_LEN 16 +#define SMPP_PASSWD_LEN 16 + +#define MODE_7BIT 7 +#define MODE_8BIT 8 + +enum esme_read_state { + READ_ST_IN_LEN = 0, + READ_ST_IN_MSG = 1, +}; + +struct osmo_smpp_acl; + +struct osmo_smpp_addr { + uint8_t ton; + uint8_t npi; + char addr[21+1]; +}; + +struct osmo_esme { + struct llist_head list; + struct smsc *smsc; + struct osmo_smpp_acl *acl; + int use; + + uint32_t own_seq_nr; + + struct osmo_wqueue wqueue; + struct sockaddr_storage sa; + socklen_t sa_len; + + enum esme_read_state read_state; + uint32_t read_len; + uint32_t read_idx; + struct msgb *read_msg; + + uint8_t smpp_version; + char system_id[SMPP_SYS_ID_LEN+1]; + + uint8_t bind_flags; +}; + +struct osmo_smpp_acl { + struct llist_head list; + struct smsc *smsc; + struct osmo_esme *esme; + char *description; + char system_id[SMPP_SYS_ID_LEN+1]; + char passwd[SMPP_PASSWD_LEN+1]; + int default_route; + int deliver_src_imsi; + int osmocom_ext; + int dcs_transparent; + struct llist_head route_list; +}; + +enum osmo_smpp_rtype { + SMPP_ROUTE_NONE, + SMPP_ROUTE_PREFIX, +}; + +struct osmo_smpp_route { + struct llist_head list; /*!< in acl.route_list */ + struct llist_head global_list; /*!< in smsc->route_list */ + struct osmo_smpp_acl *acl; + enum osmo_smpp_rtype type; + union { + struct osmo_smpp_addr prefix; + } u; +}; + + +struct smsc { + struct osmo_fd listen_ofd; + struct llist_head esme_list; + struct llist_head acl_list; + struct llist_head route_list; + uint16_t listen_port; + char system_id[SMPP_SYS_ID_LEN+1]; + int accept_all; + struct osmo_smpp_acl *def_route; + void *priv; +}; + +int smpp_addr_eq(const struct osmo_smpp_addr *a, + const struct osmo_smpp_addr *b); + +int smpp_smsc_init(struct smsc *smsc, uint16_t port); + +void smpp_esme_get(struct osmo_esme *esme); +void smpp_esme_put(struct osmo_esme *esme); + +struct osmo_esme * +smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest); + +struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id); +struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, + const char *sys_id); +void smpp_acl_delete(struct osmo_smpp_acl *acl); + +int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr, + uint32_t command_status, char *msg_id); + +int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, + const char *addr, uint8_t avail_status); + +int smpp_tx_deliver(struct osmo_esme *esme, struct deliver_sm_t *deliver); + +int handle_smpp_submit(struct osmo_esme *esme, struct submit_sm_t *submit, + struct submit_sm_resp_t *submit_r); + +int smpp_route_pfx_add(struct osmo_smpp_acl *acl, + const struct osmo_smpp_addr *pfx); +int smpp_route_pfx_del(struct osmo_smpp_acl *acl, + const struct osmo_smpp_addr *pfx); + +int smpp_vty_init(void); + +int smpp_determine_scheme(uint8_t dcs, uint8_t *data_coding, int *mode); +#endif diff --git a/src/libmsc/smpp_utils.c b/src/libmsc/smpp_utils.c new file mode 100644 index 000000000..d0850d8c1 --- /dev/null +++ b/src/libmsc/smpp_utils.c @@ -0,0 +1,62 @@ + +/* (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/src/libmsc/smpp_vty.c b/src/libmsc/smpp_vty.c new file mode 100644 index 000000000..4829eb900 --- /dev/null +++ b/src/libmsc/smpp_vty.c @@ -0,0 +1,516 @@ +/* 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_port, cfg_smpp_port_cmd, + "local-tcp-port <1-65535>", + "Set the local TCP port on which we listen for SMPP\n" + "TCP port number") +{ + struct smsc *smsc = smsc_from_vty(vty); + uint16_t port = atoi(argv[0]); + int rc; + + rc = smpp_smsc_init(smsc, port); + if (rc < 0) { + vty_out(vty, "%% Cannot bind to new port %u nor to " + "old port %u%s", port, smsc->listen_port, VTY_NEWLINE); + return CMD_WARNING; + } + + if (port != smsc->listen_port) { + vty_out(vty, "%% Cannot bind to new port %u, staying on old" + "port %u%s", port, smsc->listen_port, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_smpp_sys_id, cfg_smpp_sys_id_cmd, + "system-id ID", + "Set the System ID of this SMSC\n" + "Alphanumeric SMSC System ID\n") +{ + struct smsc *smsc = smsc_from_vty(vty); + + if (strlen(argv[0])+1 > sizeof(smsc->system_id)) + return CMD_WARNING; + + strcpy(smsc->system_id, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_smpp_policy, cfg_smpp_policy_cmd, + "policy (accept-all|closed)", + "Set the authentication policy of this SMSC\n" + "Accept all SMPP connections independeint of system ID / passwd\n" + "Accept only SMPP connections from ESMEs explicitly configured") +{ + struct smsc *smsc = smsc_from_vty(vty); + + if (!strcmp(argv[0], "accept-all")) + smsc->accept_all = 1; + else + smsc->accept_all = 0; + + return CMD_SUCCESS; +} + + +static int config_write_smpp(struct vty *vty) +{ + struct smsc *smsc = smsc_from_vty(vty); + + vty_out(vty, "smpp%s", VTY_NEWLINE); + vty_out(vty, " local-tcp-port %u%s", smsc->listen_port, VTY_NEWLINE); + if (strlen(smsc->system_id) > 0) + vty_out(vty, " system-id %s%s", smsc->system_id, VTY_NEWLINE); + vty_out(vty, " policy %s%s", + smsc->accept_all ? "accept-all" : "closed", VTY_NEWLINE); + + 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_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); + install_default(SMPP_NODE); + install_element(CONFIG_NODE, &cfg_smpp_cmd); + + install_element(SMPP_NODE, &cfg_smpp_port_cmd); + install_element(SMPP_NODE, &cfg_smpp_sys_id_cmd); + install_element(SMPP_NODE, &cfg_smpp_policy_cmd); + install_element(SMPP_NODE, &cfg_esme_cmd); + install_element(SMPP_NODE, &cfg_no_esme_cmd); + + install_node(&esme_node, config_write_esme); + 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_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(SMPP_ESME_NODE, &ournode_exit_cmd); + + install_element_ve(&show_esme_cmd); + + return 0; +} diff --git a/src/libmsc/sms_queue.c b/src/libmsc/sms_queue.c new file mode 100644 index 000000000..c8745c3da --- /dev/null +++ b/src/libmsc/sms_queue.c @@ -0,0 +1,500 @@ +/* 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->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->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_NOTICE, "Attempting to send %d SMS\n", attempts); + + do { + struct gsm_sms_pending *pending; + struct gsm_sms *sms; + + + sms = take_next_sms(smsq); + if (!sms) + break; + + rounds += 1; + + /* + * This code needs to detect a loop. It assumes that no SMS + * will vanish during the time this is executed. We will remember + * the id of the first GSM subscriber we see and then will + * compare this. The Database code should make sure that we will + * see all other subscribers first before seeing this one again. + * + * It is always scary to have an infinite loop like this. + */ + if (!initialized) { + first_sub = sms->receiver->id; + initialized = 1; + } else if (first_sub == sms->receiver->id) { + sms_free(sms); + break; + } + + /* no need to send a pending sms */ + if (sms_is_in_pending(smsq, sms)) { + LOGP(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); +} + +/* + * Kick off the queue again. + */ +int sms_queue_trigger(struct gsm_sms_queue *smsq) +{ + if (osmo_timer_pending(&smsq->push_queue)) + return 0; + + osmo_timer_schedule(&smsq->push_queue, 1, 0); + return 0; +} + +int sms_queue_start(struct gsm_network *network, int max_pending) +{ + struct gsm_sms_queue *sms = talloc_zero(network, struct gsm_sms_queue); + if (!sms) { + LOGP(DMSC, LOGL_ERROR, "Failed to create the SMS queue.\n"); + return -1; + } + + osmo_signal_register_handler(SS_SUBSCR, sms_subscr_cb, network); + osmo_signal_register_handler(SS_SMS, sms_sms_cb, network); + + network->sms_queue = sms; + INIT_LLIST_HEAD(&sms->pending_sms); + sms->max_fail = 1; + sms->network = network; + sms->max_pending = max_pending; + sms->push_queue.data = sms; + sms->push_queue.cb = sms_submit_pending; + sms->resend_pending.data = sms; + sms->resend_pending.cb = sms_resend_pending; + + sms_submit_pending(sms); + + return 0; +} + +static int sub_ready_for_sm(struct gsm_network *net, struct gsm_subscriber *subscr) +{ + struct gsm_sms *sms; + struct gsm_sms_pending *pending; + struct gsm_subscriber_connection *conn; + + /* + * The code used to be very clever and tried to submit + * a SMS during the Location Updating Request. This has + * two issues: + * 1.) The Phone might not be ready yet, e.g. the C155 + * will not respond to the Submit when it is booting. + * 2.) The queue is already trying to submit SMS to the + * user and by not responding to the paging request + * we will set the LAC back to 0. We would have to + * stop the paging and move things over. + * + * We need to be careful in what we try here. + */ + + /* check if we have pending requests */ + pending = sms_subscriber_find_pending(net->sms_queue, subscr); + if (pending) { + LOGP(DMSC, LOGL_NOTICE, + "Pending paging while subscriber %llu attached.\n", + subscr->id); + return 0; + } + + conn = connection_for_subscr(subscr); + if (!conn) + return -1; + + /* Now try to deliver any pending SMS to this sub */ + sms = db_sms_get_unsent_for_subscr(subscr); + if (!sms) + return -1; + gsm411_send_sms(conn, sms); + return 0; +} + +static int sms_subscr_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_subscriber *subscr = signal_data; + + if (signal != S_SUBSCR_ATTACHED) + return 0; + + /* this is readyForSM */ + return sub_ready_for_sm(handler_data, subscr); +} + +static int sms_sms_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_network *network = handler_data; + struct sms_signal_data *sig_sms = signal_data; + struct gsm_sms_pending *pending; + + /* We got a new SMS and maybe should launch the queue again. */ + if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) { + sms_queue_trigger(network->sms_queue); + return 0; + } + + if (!sig_sms->sms) + return -1; + + + /* + * Find the entry of our queue. The SMS subsystem will submit + * sms that are not in our control as we just have a channel + * open anyway. + */ + pending = sms_find_pending(network->sms_queue, sig_sms->sms); + if (!pending) + return 0; + + switch (signal) { + case S_SMS_DELIVERED: + /* + * Create place for a new SMS but keep the pending data + * so we will not attempt to send the SMS for this subscriber + * as we still have an open channel and will attempt to submit + * SMS to it anyway. + */ + network->sms_queue->pending -= 1; + sms_submit_pending(network->sms_queue); + sms_pending_free(pending); + break; + case S_SMS_MEM_EXCEEDED: + network->sms_queue->pending -= 1; + sms_pending_free(pending); + sms_queue_trigger(network->sms_queue); + break; + case S_SMS_UNKNOWN_ERROR: + /* + * There can be many reasons for this failure. E.g. the paging + * timed out, the subscriber was not paged at all, or there was + * a protocol error. The current strategy is to try sending the + * next SMS for busy/oom and to retransmit when we have paged. + * + * When the paging expires three times we will disable the + * subscriber. If we have some kind of other transmit error we + * should flag the SMS as bad. + */ + switch (sig_sms->paging_result) { + case 0: + /* BAD SMS? */ + db_sms_inc_deliver_attempts(sig_sms->sms); + sms_pending_failed(pending, 0); + break; + case GSM_PAGING_EXPIRED: + sms_pending_failed(pending, 1); + break; + + case GSM_PAGING_OOM: + case GSM_PAGING_BUSY: + network->sms_queue->pending -= 1; + sms_pending_free(pending); + sms_queue_trigger(network->sms_queue); + break; + default: + LOGP(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/src/libmsc/token_auth.c b/src/libmsc/token_auth.c new file mode 100644 index 000000000..45b5a8eab --- /dev/null +++ b/src/libmsc/token_auth.c @@ -0,0 +1,160 @@ +/* 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->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->net, 1); + + sms = sms_from_text(subscr, sender, 0, sms_str); + + subscr_put(sender); + talloc_free(sms_str); + if (!sms) { + rc = -ENOMEM; + goto unauth; + } + + rc = gsm411_send_sms_subscr(subscr, sms); + + /* FIXME: else, delete the subscirber from database */ +unauth: + + /* make sure we don't allow him in again unless he clicks the web UI */ + subscr->authorized = 0; + db_sync_subscriber(subscr); + if (rc) { + struct gsm_subscriber_connection *conn = connection_for_subscr(subscr); + if (conn) { + uint8_t auth_rand[16]; + /* kick the subscriber off the network */ + gsm48_tx_mm_auth_req(conn, auth_rand, 0); + gsm48_tx_mm_auth_rej(conn); + /* FIXME: close the channel early ?*/ + //gsm48_send_rr_Release(lchan); + } + } + } + + return rc; +} + +static int token_sms_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct sms_signal_data *sig = signal_data; + struct gsm_sms *sms = sig->sms;; + struct gsm_subscriber_connection *conn; + uint8_t auth_rand[16]; + + + if (signal != S_SMS_DELIVERED) + return 0; + + + /* these are not the droids we've been looking for */ + if (!sms->receiver || + !(sms->receiver->flags & GSM_SUBSCRIBER_FIRST_CONTACT)) + return 0; + + + if (sms->receiver->net->auth_policy != GSM_AUTH_POLICY_TOKEN) + return 0; + + + conn = connection_for_subscr(sms->receiver); + if (conn) { + /* kick the subscriber off the network */ + gsm48_tx_mm_auth_req(conn, auth_rand, 0); + gsm48_tx_mm_auth_rej(conn); + /* FIXME: close the channel early ?*/ + //gsm48_send_rr_Release(lchan); + } + + return 0; +} + +//static __attribute__((constructor)) void on_dso_load_token(void) +void on_dso_load_token(void) +{ + osmo_signal_register_handler(SS_SUBSCR, token_subscr_cb, NULL); + osmo_signal_register_handler(SS_SMS, token_sms_cb, NULL); +} diff --git a/src/libmsc/transaction.c b/src/libmsc/transaction.c new file mode 100644 index 000000000..c1441969d --- /dev/null +++ b/src/libmsc/transaction.c @@ -0,0 +1,164 @@ +/* GSM 04.07 Transaction handling */ + +/* (C) 2009 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void *tall_trans_ctx; + +void _gsm48_cc_trans_free(struct gsm_trans *trans); + +struct gsm_trans *trans_find_by_id(struct gsm_subscriber *subscr, + uint8_t proto, uint8_t trans_id) +{ + struct gsm_trans *trans; + struct gsm_network *net = subscr->net; + + llist_for_each_entry(trans, &net->trans_list, entry) { + if (trans->subscr == subscr && + trans->protocol == proto && + trans->transaction_id == trans_id) + return trans; + } + return NULL; +} + +struct gsm_trans *trans_find_by_callref(struct gsm_network *net, + 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_subscriber *subscr, + uint8_t protocol, uint8_t trans_id, + uint32_t callref) +{ + struct gsm_trans *trans; + + DEBUGP(DCC, "subscr=%p, subscr->net=%p\n", subscr, subscr->net); + + trans = talloc_zero(tall_trans_ctx, struct gsm_trans); + if (!trans) + return NULL; + + trans->subscr = subscr; + subscr_get(trans->subscr); + + trans->protocol = protocol; + trans->transaction_id = trans_id; + trans->callref = callref; + + llist_add_tail(&trans->entry, &subscr->net->trans_list); + + return trans; +} + +void trans_free(struct gsm_trans *trans) +{ + switch (trans->protocol) { + case GSM48_PDISC_CC: + _gsm48_cc_trans_free(trans); + break; + case GSM48_PDISC_SMS: + _gsm411_sms_trans_free(trans); + break; + } + + /* FIXME: implement a sane way to stop this. */ + if (!trans->conn && trans->paging_request) { + LOGP(DNM, LOGL_ERROR, + "Transaction freed while paging for sub: %llu\n", + trans->subscr->id); + trans->paging_request = NULL; + } + + if (trans->subscr) { + subscr_put(trans->subscr); + trans->subscr = NULL; + } + + llist_del(&trans->entry); + + if (trans->conn) + msc_release_connection(trans->conn); + + + talloc_free(trans); +} + +/* allocate an unused transaction ID for the given subscriber + * in the given protocol using the ti_flag specified */ +int trans_assign_trans_id(struct gsm_subscriber *subscr, + uint8_t protocol, uint8_t ti_flag) +{ + struct gsm_network *net = subscr->net; + struct gsm_trans *trans; + unsigned int used_tid_bitmask = 0; + int i, j, h; + + if (ti_flag) + ti_flag = 0x8; + + /* generate bitmask of already-used TIDs for this (subscr,proto) */ + llist_for_each_entry(trans, &net->trans_list, entry) { + if (trans->subscr != subscr || + trans->protocol != protocol || + trans->transaction_id == 0xff) + continue; + used_tid_bitmask |= (1 << trans->transaction_id); + } + + /* find a new one, trying to go in a 'circular' pattern */ + for (h = 6; h > 0; h--) + if (used_tid_bitmask & (1 << (h | ti_flag))) + break; + for (i = 0; i < 7; i++) { + j = ((h + i) % 7) | ti_flag; + if ((used_tid_bitmask & (1 << j)) == 0) + return j; + } + + return -1; +} + +int trans_has_conn(const struct gsm_subscriber_connection *conn) +{ + struct gsm_trans *trans; + + llist_for_each_entry(trans, &conn->bts->network->trans_list, entry) + if (trans->conn == conn) + return 1; + + return 0; +} diff --git a/src/libmsc/ussd.c b/src/libmsc/ussd.c new file mode 100644 index 000000000..76ee101a1 --- /dev/null +++ b/src/libmsc/ussd.c @@ -0,0 +1,79 @@ +/* Network-specific handling of mobile-originated USSDs. */ + +/* (C) 2008-2009 by Harald Welte + * (C) 2008, 2009 by Holger Hans Peter Freyther + * (C) 2009 by Mike Haben + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/* This module defines the network-specific handling of mobile-originated + USSD messages. */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Declarations of USSD strings to be recognised */ +const char USSD_TEXT_OWN_NUMBER[] = "*#100#"; + +/* Forward declarations of network-specific handler functions */ +static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req); + + +/* Entrypoint - handler function common to all mobile-originated USSDs */ +int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + int rc; + struct ussd_request req; + struct gsm48_hdr *gh; + + memset(&req, 0, sizeof(req)); + gh = msgb_l3(msg); + rc = gsm0480_decode_ussd_request(gh, msgb_l3len(msg), &req); + if (req.text[0] == 0xFF) /* Release-Complete */ + return 0; + + if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.text)) { + DEBUGP(DMM, "USSD: Own number requested\n"); + rc = send_own_number(conn, msg, &req); + } else { + DEBUGP(DMM, "Unhandled USSD %s\n", req.text); + rc = gsm0480_send_ussd_reject(conn, msg, &req); + } + + /* check if we can release it */ + msc_release_connection(conn); + return rc; +} + +/* A network-specific handler function */ +static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req) +{ + char *own_number = conn->subscr->extension; + char response_string[GSM_EXTENSION_LENGTH + 20]; + + /* Need trailing CR as EOT character */ + snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); + return gsm0480_send_ussd_response(conn, msg, response_string, req); +} diff --git a/src/libmsc/vty_interface_layer3.c b/src/libmsc/vty_interface_layer3.c new file mode 100644 index 000000000..79c345771 --- /dev/null +++ b/src/libmsc/vty_interface_layer3.c @@ -0,0 +1,987 @@ +/* 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 + +extern struct gsm_network *gsmnet_from_vty(struct vty *v); + +static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr, int pending) +{ + int rc; + struct gsm_auth_info ainfo; + struct gsm_auth_tuple atuple; + char expire_time[200]; + + vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, + subscr->authorized, VTY_NEWLINE); + if (subscr->name) + vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); + if (subscr->extension) + vty_out(vty, " Extension: %s%s", subscr->extension, + VTY_NEWLINE); + vty_out(vty, " LAC: %d/0x%x%s", + subscr->lac, subscr->lac, VTY_NEWLINE); + vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); + if (subscr->tmsi != GSM_RESERVED_TMSI) + vty_out(vty, " TMSI: %08X%s", subscr->tmsi, + VTY_NEWLINE); + + rc = db_get_authinfo_for_subscr(&ainfo, subscr); + if (!rc) { + vty_out(vty, " A3A8 algorithm id: %d%s", + ainfo.auth_algo, VTY_NEWLINE); + vty_out(vty, " A3A8 Ki: %s%s", + osmo_hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len), + VTY_NEWLINE); + } + + rc = db_get_lastauthtuple_for_subscr(&atuple, subscr); + if (!rc) { + vty_out(vty, " A3A8 last tuple (used %d times):%s", + atuple.use_count, VTY_NEWLINE); + vty_out(vty, " seq # : %d%s", + atuple.key_seq, VTY_NEWLINE); + vty_out(vty, " RAND : %s%s", + osmo_hexdump(atuple.rand, sizeof(atuple.rand)), + VTY_NEWLINE); + vty_out(vty, " SRES : %s%s", + osmo_hexdump(atuple.sres, sizeof(atuple.sres)), + VTY_NEWLINE); + vty_out(vty, " Kc : %s%s", + osmo_hexdump(atuple.kc, sizeof(atuple.kc)), + VTY_NEWLINE); + } + + /* print the expiration time of a subscriber */ + strftime(expire_time, sizeof(expire_time), + "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu)); + expire_time[sizeof(expire_time) - 1] = '\0'; + vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE); + + if (pending) + vty_out(vty, " Pending: %d%s", + subscr_pending_requests(subscr), VTY_NEWLINE); + + vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); +} + + +/* Subscriber */ +DEFUN(show_subscr_cache, + show_subscr_cache_cmd, + "show subscriber cache", + SHOW_STR "Show information about subscribers\n" + "Display contents of subscriber cache\n") +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, &active_subscribers, entry) { + vty_out(vty, " Subscriber:%s", VTY_NEWLINE); + subscr_dump_full_vty(vty, subscr, 0); + } + + return CMD_SUCCESS; +} + +DEFUN(sms_send_pend, + sms_send_pend_cmd, + "sms send pending", + "SMS related comamnds\n" "SMS Sending related commands\n" + "Send all pending SMS") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_sms *sms; + int id = 0; + + while (1) { + sms = db_sms_get_unsent_by_subscr(gsmnet, id, UINT_MAX); + if (!sms) + break; + + gsm411_send_sms_subscr(sms->receiver, sms); + + id = sms->receiver->id + 1; + } + + return CMD_SUCCESS; +} + +static int _send_sms_str(struct gsm_subscriber *receiver, + struct gsm_subscriber *sender, + char *str, uint8_t tp_pid) +{ + struct gsm_sms *sms; + + sms = sms_from_text(receiver, sender, 0, str); + sms->protocol_id = tp_pid; + + /* store in database for the queue */ + if (db_sms_store(sms) != 0) { + LOGP(DLSMS, LOGL_ERROR, "Failed to store SMS in Database\n"); + sms_free(sms); + return CMD_WARNING; + } + + sms_free(sms); + sms_queue_trigger(receiver->net->sms_queue); + return CMD_SUCCESS; +} + +static struct gsm_subscriber *get_subscr_by_argv(struct gsm_network *gsmnet, + const char *type, + const char *id) +{ + if (!strcmp(type, "extension")) + return subscr_get_by_extension(gsmnet, id); + else if (!strcmp(type, "imsi")) + return subscr_get_by_imsi(gsmnet, id); + else if (!strcmp(type, "tmsi")) + return subscr_get_by_tmsi(gsmnet, atoi(id)); + else if (!strcmp(type, "id")) + return subscr_get_by_id(gsmnet, atoi(id)); + + return NULL; +} +#define SUBSCR_TYPES "(extension|imsi|tmsi|id)" +#define SUBSCR_HELP "Operations on a Subscriber\n" \ + "Identify subscriber by his extension (phone number)\n" \ + "Identify subscriber by his IMSI\n" \ + "Identify subscriber by his TMSI\n" \ + "Identify subscriber by his database ID\n" \ + "Identifier for the subscriber\n" + +DEFUN(show_subscr, + show_subscr_cmd, + "show subscriber " SUBSCR_TYPES " ID", + SHOW_STR SUBSCR_HELP) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = + get_subscr_by_argv(gsmnet, argv[0], argv[1]); + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + subscr_dump_full_vty(vty, subscr, 1); + + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(subscriber_send_pending_sms, + subscriber_send_pending_sms_cmd, + "subscriber " SUBSCR_TYPES " ID sms pending-send", + SUBSCR_HELP "SMS Operations\n" "Send pending SMS\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr; + 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 send .LINE", + SUBSCR_HELP + "Silent SMS Operation\n" "Send Silent SMS\n" "Actual SMS text\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); + struct gsm_subscriber *sender = get_subscr_by_argv(gsmnet, argv[2], argv[3]); + char *str; + int rc; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + rc = CMD_WARNING; + goto err; + } + + if (!sender) { + vty_out(vty, "%% No sender found for %s %s%s", + argv[2], argv[3], VTY_NEWLINE); + rc = CMD_WARNING; + goto err; + } + + str = argv_concat(argv, argc, 4); + rc = _send_sms_str(subscr, sender, str, 64); + talloc_free(str); + +err: + if (sender) + subscr_put(sender); + + if (subscr) + subscr_put(subscr); + + return rc; +} + +#define CHAN_TYPES "(any|tch/f|tch/any|sdcch)" +#define CHAN_TYPE_HELP \ + "Any channel\n" \ + "TCH/F channel\n" \ + "Any TCH channel\n" \ + "SDCCH channel\n" + +DEFUN(subscriber_silent_call_start, + subscriber_silent_call_start_cmd, + "subscriber " SUBSCR_TYPES " ID silent-call start (any|tch/f|tch/any|sdcch)", + SUBSCR_HELP "Silent call operation\n" "Start silent call\n" + CHAN_TYPE_HELP) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); + int rc, type; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[2], "tch/f")) + type = RSL_CHANNEED_TCH_F; + else if (!strcmp(argv[2], "tch/any")) + type = RSL_CHANNEED_TCH_ForH; + else if (!strcmp(argv[2], "sdcch")) + type = RSL_CHANNEED_SDCCH; + else + type = RSL_CHANNEED_ANY; /* Defaults to ANY */ + + rc = gsm_silent_call_start(subscr, vty, type); + if (rc <= 0) { + vty_out(vty, "%% Subscriber not attached%s", + VTY_NEWLINE); + subscr_put(subscr); + return CMD_WARNING; + } + + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(subscriber_silent_call_stop, + subscriber_silent_call_stop_cmd, + "subscriber " SUBSCR_TYPES " ID silent-call stop", + SUBSCR_HELP "Silent call operation\n" "Stop silent call\n" + CHAN_TYPE_HELP) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); + int rc; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + rc = gsm_silent_call_stop(subscr); + if (rc < 0) { + subscr_put(subscr); + return CMD_WARNING; + } + + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(subscriber_ussd_notify, + subscriber_ussd_notify_cmd, + "subscriber " SUBSCR_TYPES " ID ussd-notify (0|1|2) .TEXT", + SUBSCR_HELP "Send a USSD notify to the subscriber\n" + "Alerting Level 0\n" + "Alerting Level 1\n" + "Alerting Level 2\n" + "Text of USSD message to send\n") +{ + char *text; + struct gsm_subscriber_connection *conn; + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); + int level; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + level = atoi(argv[2]); + text = argv_concat(argv, argc, 3); + if (!text) { + subscr_put(subscr); + return CMD_WARNING; + } + + conn = connection_for_subscr(subscr); + if (!conn) { + vty_out(vty, "%% An active connection is required for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + subscr_put(subscr); + talloc_free(text); + return CMD_WARNING; + } + + gsm0480_send_ussdNotify(conn, level, text); + gsm0480_send_releaseComplete(conn); + + subscr_put(subscr); + talloc_free(text); + return CMD_SUCCESS; +} + +DEFUN(ena_subscr_authorizde, + ena_subscr_authorized_cmd, + "subscriber " SUBSCR_TYPES " ID authorized (0|1)", + SUBSCR_HELP "(De-)Authorize subscriber in HLR\n" + "Subscriber should NOT be authorized\n" + "Subscriber should be authorized\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = + get_subscr_by_argv(gsmnet, argv[0], argv[1]); + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + subscr->authorized = atoi(argv[2]); + db_sync_subscriber(subscr); + + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(ena_subscr_name, + ena_subscr_name_cmd, + "subscriber " SUBSCR_TYPES " ID name .NAME", + SUBSCR_HELP "Set the name of the subscriber\n" + "Name of the Subscriber\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = + get_subscr_by_argv(gsmnet, argv[0], argv[1]); + char *name; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + name = argv_concat(argv, argc, 2); + if (!name) { + subscr_put(subscr); + return CMD_WARNING; + } + + strncpy(subscr->name, name, sizeof(subscr->name)); + talloc_free(name); + db_sync_subscriber(subscr); + + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(ena_subscr_extension, + ena_subscr_extension_cmd, + "subscriber " SUBSCR_TYPES " ID extension EXTENSION", + SUBSCR_HELP "Set the extension (phone number) of the subscriber\n" + "Extension (phone number)\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = + get_subscr_by_argv(gsmnet, argv[0], argv[1]); + const char *ext = argv[2]; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + strncpy(subscr->extension, ext, sizeof(subscr->extension)); + db_sync_subscriber(subscr); + + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(ena_subscr_clear, + ena_subscr_clear_cmd, + "subscriber " SUBSCR_TYPES " ID clear-requests", + SUBSCR_HELP "Clear the paging requests for this subscriber\n") +{ + int del; + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = + get_subscr_by_argv(gsmnet, argv[0], argv[1]); + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + del = subscr_pending_clear(subscr); + vty_out(vty, "Cleared %d pending requests.%s", del, VTY_NEWLINE); + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(ena_subscr_pend, + ena_subscr_pend_cmd, + "subscriber " SUBSCR_TYPES " ID show-pending", + SUBSCR_HELP "Clear the paging requests for this subscriber\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = + get_subscr_by_argv(gsmnet, argv[0], argv[1]); + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + subscr_pending_dump(subscr, vty); + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(ena_subscr_kick, + ena_subscr_kick_cmd, + "subscriber " SUBSCR_TYPES " ID kick-pending", + SUBSCR_HELP "Clear the paging requests for this subscriber\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = + get_subscr_by_argv(gsmnet, argv[0], argv[1]); + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + subscr_pending_kick(subscr); + subscr_put(subscr); + + return CMD_SUCCESS; +} + +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); + vty_out(vty, "%d subscriber(s) were purged.%s", purged, VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN(subscriber_update, + subscriber_update_cmd, + "subscriber " SUBSCR_TYPES " ID update", + SUBSCR_HELP "Update the subscriber data from the dabase.\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + subscr_update_from_db(subscr); + subscr_put(subscr); + return CMD_SUCCESS; +} + +static int scall_cbfn(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct scall_signal_data *sigdata = signal_data; + struct vty *vty = sigdata->data; + + switch (signal) { + case S_SCALL_SUCCESS: + vty_out(vty, "%% silent call on ARFCN %u timeslot %u%s", + sigdata->conn->lchan->ts->trx->arfcn, sigdata->conn->lchan->ts->nr, + VTY_NEWLINE); + break; + case S_SCALL_EXPIRED: + vty_out(vty, "%% silent call expired paging%s", VTY_NEWLINE); + break; + } + return 0; +} + +DEFUN(show_stats, + show_stats_cmd, + "show statistics", + SHOW_STR "Display network statistics\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + + openbsc_vty_print_statistics(vty, net); + vty_out(vty, "Channel Requests : %lu total, %lu no channel%s", + osmo_counter_get(net->stats.chreq.total), + osmo_counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); + vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s", + osmo_counter_get(net->stats.loc_upd_type.attach), + osmo_counter_get(net->stats.loc_upd_type.normal), + osmo_counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE); + vty_out(vty, "IMSI Detach Indications : %lu%s", + osmo_counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE); + vty_out(vty, "Location Update Response: %lu accept, %lu reject%s", + osmo_counter_get(net->stats.loc_upd_resp.accept), + osmo_counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE); + vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, " + "%lu completed, %lu failed%s", + osmo_counter_get(net->stats.handover.attempted), + osmo_counter_get(net->stats.handover.no_channel), + osmo_counter_get(net->stats.handover.timeout), + osmo_counter_get(net->stats.handover.completed), + osmo_counter_get(net->stats.handover.failed), VTY_NEWLINE); + vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s", + osmo_counter_get(net->stats.sms.submitted), + osmo_counter_get(net->stats.sms.no_receiver), VTY_NEWLINE); + vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s", + osmo_counter_get(net->stats.sms.delivered), + osmo_counter_get(net->stats.sms.rp_err_mem), + osmo_counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE); + vty_out(vty, "MO Calls : %lu setup, %lu connect ack%s", + osmo_counter_get(net->stats.call.mo_setup), + osmo_counter_get(net->stats.call.mo_connect_ack), VTY_NEWLINE); + vty_out(vty, "MT Calls : %lu setup, %lu connect%s", + osmo_counter_get(net->stats.call.mt_setup), + osmo_counter_get(net->stats.call.mt_connect), VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN(show_smsqueue, + show_smsqueue_cmd, + "show sms-queue", + SHOW_STR "Display SMSqueue statistics\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + + sms_queue_stats(net->sms_queue, vty); + return CMD_SUCCESS; +} + +DEFUN(smsqueue_trigger, + smsqueue_trigger_cmd, + "sms-queue trigger", + "SMS Queue\n" "Trigger sending messages\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + + sms_queue_trigger(net->sms_queue); + return CMD_SUCCESS; +} + +DEFUN(smsqueue_max, + smsqueue_max_cmd, + "sms-queue max-pending <1-500>", + "SMS Queue\n" "SMS to deliver in parallel\n" "Amount\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + + sms_queue_set_max_pending(net->sms_queue, atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(smsqueue_clear, + smsqueue_clear_cmd, + "sms-queue clear", + "SMS Queue\n" "Clear the queue of pending SMS\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + + sms_queue_clear(net->sms_queue); + return CMD_SUCCESS; +} + +DEFUN(smsqueue_fail, + smsqueue_fail_cmd, + "sms-queue max-failure <1-500>", + "SMS Queue\n" "Maximum amount of delivery failures\n" "Amount\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + + sms_queue_set_max_failure(net->sms_queue, atoi(argv[0])); + return CMD_SUCCESS; +} + + +DEFUN(cfg_mncc_int, cfg_mncc_int_cmd, + "mncc-int", "Configure internal MNCC handler") +{ + vty->node = MNCC_INT_NODE; + + return CMD_SUCCESS; +} + +static struct cmd_node mncc_int_node = { + MNCC_INT_NODE, + "%s(config-mncc-int)# ", + 1, +}; + +static const struct value_string tchf_codec_names[] = { + { GSM48_CMODE_SPEECH_V1, "fr" }, + { GSM48_CMODE_SPEECH_EFR, "efr" }, + { GSM48_CMODE_SPEECH_AMR, "amr" }, + { 0, NULL } +}; + +static const struct value_string tchh_codec_names[] = { + { GSM48_CMODE_SPEECH_V1, "hr" }, + { GSM48_CMODE_SPEECH_AMR, "amr" }, + { 0, NULL } +}; + +static int config_write_mncc_int(struct vty *vty) +{ + 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); + + 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; +} + +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_send_sms_cmd); + install_element_ve(&subscriber_silent_sms_cmd); + install_element_ve(&subscriber_silent_call_start_cmd); + install_element_ve(&subscriber_silent_call_stop_cmd); + install_element_ve(&subscriber_ussd_notify_cmd); + install_element_ve(&subscriber_update_cmd); + install_element_ve(&show_stats_cmd); + install_element_ve(&show_smsqueue_cmd); + + install_element(ENABLE_NODE, &ena_subscr_name_cmd); + install_element(ENABLE_NODE, &ena_subscr_extension_cmd); + install_element(ENABLE_NODE, &ena_subscr_authorized_cmd); + install_element(ENABLE_NODE, &ena_subscr_a3a8_cmd); + install_element(ENABLE_NODE, &ena_subscr_clear_cmd); + install_element(ENABLE_NODE, &ena_subscr_pend_cmd); + install_element(ENABLE_NODE, &ena_subscr_kick_cmd); + install_element(ENABLE_NODE, &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(CONFIG_NODE, &cfg_mncc_int_cmd); + install_node(&mncc_int_node, config_write_mncc_int); + install_default(MNCC_INT_NODE); + install_element(MNCC_INT_NODE, &ournode_exit_cmd); + install_element(MNCC_INT_NODE, &ournode_end_cmd); + install_element(MNCC_INT_NODE, &mnccint_def_codec_f_cmd); + install_element(MNCC_INT_NODE, &mnccint_def_codec_h_cmd); + + install_element(CFG_LOG_NODE, &log_level_sms_cmd); + + return 0; +} diff --git a/src/libtrau/Makefile.am b/src/libtrau/Makefile.am new file mode 100644 index 000000000..0c8cf17b5 --- /dev/null +++ b/src/libtrau/Makefile.am @@ -0,0 +1,7 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOABIS_LIBS) $(COVERAGE_LDFLAGS) + +noinst_LIBRARIES = libtrau.a + +libtrau_a_SOURCES = rtp_proxy.c trau_mux.c trau_upqueue.c diff --git a/src/libtrau/rtp_proxy.c b/src/libtrau/rtp_proxy.c new file mode 100644 index 000000000..0074b4a04 --- /dev/null +++ b/src/libtrau/rtp_proxy.c @@ -0,0 +1,757 @@ +/* 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 + +/* attempt to determine byte order */ +#include +#include + +#ifndef __BYTE_ORDER +# ifdef __APPLE__ +# define __BYTE_ORDER __DARWIN_BYTE_ORDER +# define __LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +# define __BIG_ENDIAN __DARWIN_BIG_ENDIAN +# else +# error "__BYTE_ORDER should be defined by someone" +# endif +#endif + +static LLIST_HEAD(rtp_sockets); + +/* should we mangle the CNAME inside SDES of RTCP packets? We disable + * this by default, as it seems to be not needed */ +static int mangle_rtcp_cname = 0; + +enum rtp_bfd_priv { + RTP_PRIV_NONE, + RTP_PRIV_RTP, + RTP_PRIV_RTCP +}; + +#define RTP_ALLOC_SIZE 1500 + +/* according to RFC 1889 */ +struct rtcp_hdr { + uint8_t byte0; + uint8_t type; + uint16_t length; +} __attribute__((packed)); + +#define RTCP_TYPE_SDES 202 + +#define RTCP_IE_CNAME 1 + +/* according to RFC 3550 */ +struct rtp_hdr { +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t csrc_count:4, + extension:1, + padding:1, + version:2; + uint8_t payload_type:7, + marker:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + uint8_t version:2, + padding:1, + extension:1, + csrc_count:4; + uint8_t marker:1, + payload_type:7; +#endif + uint16_t sequence; + uint32_t timestamp; + uint32_t ssrc; +} __attribute__((packed)); + +struct rtp_x_hdr { + uint16_t by_profile; + uint16_t length; +} __attribute__((packed)); + +#define RTP_VERSION 2 + +/* decode an rtp frame and create a new buffer with payload */ +static int rtp_decode(struct msgb *msg, 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; + 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 < 0) { + 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 != 33) { + DEBUGPC(DLMUX, "received RTP full rate frame with " + "payload length != 32 (len = %d)\n", + payload_len); + return -EINVAL; + } + break; + case RTP_PT_GSM_EFR: + msg_type = GSM_TCHF_FRAME_EFR; + break; + default: + DEBUGPC(DLMUX, "received RTP frame with unknown payload " + "type %d\n", rtph->payload_type); + return -EINVAL; + } + + new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len, + "GSM-DATA"); + if (!new_msg) + return -ENOMEM; + frame = (struct gsm_data_frame *)(new_msg->data); + frame->msg_type = msg_type; + frame->callref = callref; + memcpy(frame->data, payload, payload_len); + msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len); + + *data = new_msg; + return 0; +} + +/* "to - from" */ +static void tv_difference(struct timeval *diff, const struct timeval *from, + const struct timeval *__to) +{ + struct timeval _to = *__to, *to = &_to; + + if (to->tv_usec < from->tv_usec) { + to->tv_sec -= 1; + to->tv_usec += 1000000; + } + + diff->tv_usec = to->tv_usec - from->tv_usec; + diff->tv_sec = to->tv_sec - from->tv_sec; +} + +/*! \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; + int payload_type; + int payload_len; + int duration; /* in samples */ + + if (rs->tx_action != RTP_SEND_DOWNSTREAM) { + /* initialize sequences */ + rs->tx_action = RTP_SEND_DOWNSTREAM; + rs->transmit.ssrc = rand(); + rs->transmit.sequence = random(); + rs->transmit.timestamp = random(); + } + + switch (frame->msg_type) { + case GSM_TCHF_FRAME: + payload_type = RTP_PT_GSM_FULL; + payload_len = 33; + duration = 160; + break; + case GSM_TCHF_FRAME_EFR: + payload_type = RTP_PT_GSM_EFR; + payload_len = 31; + duration = 160; + break; + default: + DEBUGPC(DLMUX, "unsupported message type %d\n", + frame->msg_type); + return -EINVAL; + } + + { + struct timeval tv, tv_diff; + long int usec_diff, frame_diff; + + gettimeofday(&tv, NULL); + tv_difference(&tv_diff, &rs->transmit.last_tv, &tv); + rs->transmit.last_tv = tv; + + usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec; + frame_diff = (usec_diff / 20000); + + if (abs(frame_diff) > 1) { + long int frame_diff_excess = frame_diff - 1; + + LOGP(DLMUX, LOGL_NOTICE, + "Correcting frame difference of %ld frames\n", frame_diff_excess); + rs->transmit.sequence += frame_diff_excess; + rs->transmit.timestamp += frame_diff_excess * duration; + } + } + + msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL"); + if (!msg) + return -ENOMEM; + rtph = (struct rtp_hdr *)msg->data; + rtph->version = RTP_VERSION; + rtph->padding = 0; + rtph->extension = 0; + rtph->csrc_count = 0; + rtph->marker = 0; + rtph->payload_type = payload_type; + rtph->sequence = htons(rs->transmit.sequence++); + rtph->timestamp = htonl(rs->transmit.timestamp); + rs->transmit.timestamp += duration; + rtph->ssrc = htonl(rs->transmit.ssrc); + memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len); + msgb_put(msg, sizeof(struct rtp_hdr) + payload_len); + msgb_enqueue(&rss->tx_queue, msg); + rss->bfd.when |= BSC_FD_WRITE; + + return 0; +} + +/* iterate over all chunks in one RTCP message, look for CNAME IEs and + * replace all of those with 'new_cname' */ +static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh, + 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) { } + break; + } + len = *cur++; + + if (tag == RTCP_IE_CNAME) { + /* we've found the CNAME, lets mangle it */ + if (len < strlen(new_cname)) { + /* we need to make more space */ + int increase = strlen(new_cname) - len; + + msgb_push(msg, increase); + memmove(cur+len+increase, cur+len, + rtcp_end - (cur+len)); + /* FIXME: we have to respect RTCP + * padding/alignment rules! */ + len += increase; + *(cur-1) += increase; + rtcp_end += increase; + *rtcp_len += increase; + } + /* copy new CNAME into message */ + memcpy(cur, new_cname, strlen(new_cname)); + /* FIXME: zero the padding in case new CNAME + * is smaller than old one !!! */ + } + cur += len; + } + } + + return 0; +} + +static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs) +{ + struct rtp_sub_socket *rss = &rs->rtcp; + struct rtcp_hdr *rtph; + uint16_t old_len; + int rc; + + if (!mangle_rtcp_cname) + return 0; + + printf("RTCP\n"); + /* iterate over list of RTCP messages */ + rtph = (struct rtcp_hdr *)msg->data; + while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) { + old_len = (ntohs(rtph->length) + 1) * 4; + if ((void *)rtph + old_len > (void *)msg->data + msg->len) { + DEBUGPC(DLMUX, "received RTCP packet too short for " + "length element\n"); + return -EINVAL; + } + if (rtph->type == RTCP_TYPE_SDES) { + char new_cname[255]; + strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr), + sizeof(new_cname)); + new_cname[sizeof(new_cname)-1] = '\0'; + rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len, + new_cname); + if (rc < 0) + return rc; + } + rtph = (void *)rtph + old_len; + } + + return 0; +} + +/* read from incoming RTP/RTCP socket */ +static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss) +{ + int rc; + struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP"); + struct msgb *new_msg; + struct rtp_sub_socket *other_rss; + + if (!msg) + return -ENOMEM; + + rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE); + if (rc <= 0) { + rss->bfd.when &= ~BSC_FD_READ; + return rc; + } + + msgb_put(msg, rc); + + switch (rs->rx_action) { + case RTP_PROXY: + if (!rs->proxy.other_sock) { + rc = -EIO; + goto out_free; + } + if (rss->bfd.priv_nr == RTP_PRIV_RTP) + other_rss = &rs->proxy.other_sock->rtp; + else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) { + other_rss = &rs->proxy.other_sock->rtcp; + /* modify RTCP SDES CNAME */ + rc = rtcp_mangle(msg, rs); + if (rc < 0) + goto out_free; + } else { + rc = -EINVAL; + goto out_free; + } + msgb_enqueue(&other_rss->tx_queue, msg); + other_rss->bfd.when |= BSC_FD_WRITE; + break; + + case RTP_RECV_UPSTREAM: + if (!rs->receive.callref || !rs->receive.net) { + rc = -EIO; + goto out_free; + } + if (rss->bfd.priv_nr == RTP_PRIV_RTCP) { + if (!mangle_rtcp_cname) { + msgb_free(msg); + break; + } + /* modify RTCP SDES CNAME */ + rc = rtcp_mangle(msg, rs); + if (rc < 0) + goto out_free; + msgb_enqueue(&rss->tx_queue, msg); + rss->bfd.when |= BSC_FD_WRITE; + break; + } + if (rss->bfd.priv_nr != RTP_PRIV_RTP) { + rc = -EINVAL; + goto out_free; + } + rc = rtp_decode(msg, rs->receive.callref, &new_msg); + if (rc < 0) + goto out_free; + msgb_free(msg); + trau_tx_to_mncc(rs->receive.net, new_msg); + break; + + case RTP_NONE: /* if socket exists, but disabled by app */ + msgb_free(msg); + break; + } + + return 0; + +out_free: + msgb_free(msg); + return rc; +} + +/* \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/src/libtrau/trau_mux.c b/src/libtrau/trau_mux.c new file mode 100644 index 000000000..9272ac04f --- /dev/null +++ b/src/libtrau/trau_mux.c @@ -0,0 +1,329 @@ +/* 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 + +/* 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 +}; + +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[] = { 0, 0, 0, 1, 0 }; + +/* 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; + struct gsm_data_frame *frame; + unsigned char *data; + int i, j, k, l, o; + /* frame shall be sent to upqueue */ + if (!(ue = lookup_trau_upqueue(src_e1_ss))) + return -EINVAL; + if (!ue->callref) + return -EINVAL; + if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check))) + DEBUGPC(DLMUX, "illegal trau (C1-C5) %s\n", + osmo_hexdump(tf.c_bits, sizeof(c_bits_check))); + msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, + "GSM-DATA"); + if (!msg) + return -ENOMEM; + + frame = (struct gsm_data_frame *)msg->data; + memset(frame, 0, sizeof(struct gsm_data_frame)); + data = frame->data; + data[0] = 0xd << 4; + /* reassemble d-bits */ + i = 0; /* counts bits */ + j = 4; /* counts output bits */ + k = gsm_fr_map[0]-1; /* current number bit in element */ + l = 0; /* counts element bits */ + o = 0; /* offset input bits */ + while (i < 260) { + data[j/8] |= (tf.d_bits[k+o] << (7-(j%8))); + if (--k < 0) { + o += gsm_fr_map[l]; + k = gsm_fr_map[++l]-1; + } + i++; + j++; + } + frame->msg_type = GSM_TCHF_FRAME; + frame->callref = ue->callref; + msgb_put(msg, sizeof(struct gsm_data_frame) + 33); + 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; +} + +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; + int i, j, k, l, o; + unsigned char *data = frame->data; + struct decoded_trau_frame tf; + + mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts); + if (!mx) + return -EINVAL; + + switch (frame->msg_type) { + case GSM_TCHF_FRAME: + /* set c-bits and t-bits */ + tf.c_bits[0] = 1; + tf.c_bits[1] = 1; + tf.c_bits[2] = 1; + tf.c_bits[3] = 0; + tf.c_bits[4] = 0; + memset(&tf.c_bits[5], 0, 6); + memset(&tf.c_bits[11], 1, 10); + memset(&tf.t_bits[0], 1, 4); + /* reassemble d-bits */ + i = 0; /* counts bits */ + j = 4; /* counts input bits */ + k = gsm_fr_map[0]-1; /* current number bit in element */ + l = 0; /* counts element bits */ + o = 0; /* offset output bits */ + while (i < 260) { + tf.d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1; + if (--k < 0) { + o += gsm_fr_map[l]; + k = gsm_fr_map[++l]-1; + } + i++; + j++; + } + break; + default: + DEBUGPC(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); +} diff --git a/src/libtrau/trau_upqueue.c b/src/libtrau/trau_upqueue.c new file mode 100644 index 000000000..f8edaf0ff --- /dev/null +++ b/src/libtrau/trau_upqueue.c @@ -0,0 +1,27 @@ +/* trau_upqueue.c - Pass msgb's up the chain */ + +/* (C) 2010 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +void trau_tx_to_mncc(struct gsm_network *net, struct msgb *msg) +{ + net->mncc_recv(net, msg); +} diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am new file mode 100644 index 000000000..63972c838 --- /dev/null +++ b/src/osmo-bsc/Makefile.am @@ -0,0 +1,20 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) $(LIBOSMOABIS_CFLAGS) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +bin_PROGRAMS = osmo-bsc + + +osmo_bsc_SOURCES = osmo_bsc_main.c osmo_bsc_vty.c osmo_bsc_api.c \ + osmo_bsc_grace.c osmo_bsc_msc.c osmo_bsc_sccp.c \ + osmo_bsc_filter.c osmo_bsc_bssap.c osmo_bsc_audio.c osmo_bsc_ctrl.c +# once again since TRAU uses CC symbol :( +osmo_bsc_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(top_builddir)/src/libctrl/libctrl.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOSCCP_LIBS) $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS) $(LIBOSMOABIS_LIBS) diff --git a/src/osmo-bsc/osmo_bsc_api.c b/src/osmo-bsc/osmo_bsc_api.c new file mode 100644 index 000000000..df8c0444f --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_api.c @@ -0,0 +1,362 @@ +/* (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 + +#define return_when_not_connected(conn) \ + if (!conn->sccp_con) {\ + LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \ + return; \ + } + +#define return_when_not_connected_val(conn, ret) \ + if (!conn->sccp_con) {\ + LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \ + return ret; \ + } + +#define queue_msg_or_return(resp) \ + if (!resp) { \ + LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); \ + return; \ + } \ + bsc_queue_for_msc(conn->sccp_con, resp); + +static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause); +static int complete_layer3(struct gsm_subscriber_connection *conn, + struct msgb *msg, struct osmo_msc_data *msc); + +static uint16_t get_network_code_for_msc(struct osmo_msc_data *msc) +{ + if (msc->core_ncc != -1) + return msc->core_ncc; + return msc->network->network_code; +} + +static uint16_t get_country_code_for_msc(struct osmo_msc_data *msc) +{ + if (msc->core_mcc != -1) + return msc->core_mcc; + return msc->network->country_code; +} + +static 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); +} + +/* + * Instruct to reserve data for a new connectiom, create the complete + * layer three message, send it to open the connection. + */ +static int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, + uint16_t chosen_channel) +{ + struct osmo_msc_data *msc; + + LOGP(DMSC, LOGL_INFO, "Tx MSC COMPL L3\n"); + + /* find the MSC link we want to use */ + msc = bsc_find_msc(conn, msg); + if (!msc) { + LOGP(DMSC, LOGL_ERROR, "Failed to find a MSC for a connection.\n"); + return -1; + } + + return complete_layer3(conn, msg, msc); +} + +static int complete_layer3(struct gsm_subscriber_connection *conn, + struct msgb *msg, struct osmo_msc_data *msc) +{ + struct msgb *resp; + uint16_t network_code; + uint16_t country_code; + + /* allocate resource for a new connection */ + if (bsc_create_new_connection(conn, msc) != 0) + return BSC_API_CONN_POL_REJECT; + + network_code = get_network_code_for_msc(conn->sccp_con->msc); + country_code = get_country_code_for_msc(conn->sccp_con->msc); + + bsc_scan_bts_msg(conn, msg); + resp = gsm0808_create_layer3(msg, network_code, country_code, + conn->bts->location_area_code, + conn->bts->cell_identity); + if (!resp) { + LOGP(DMSC, LOGL_DEBUG, "Failed to create layer3 message.\n"); + sccp_connection_free(conn->sccp_con->sccp); + bsc_delete_connection(conn->sccp_con); + return BSC_API_CONN_POL_REJECT; + } + + if (bsc_open_connection(conn->sccp_con, resp) != 0) { + sccp_connection_free(conn->sccp_con->sccp); + bsc_delete_connection(conn->sccp_con); + msgb_free(resp); + return BSC_API_CONN_POL_REJECT; + } + + return BSC_API_CONN_POL_ACCEPT; +} + +/* + * Plastic surgery... we want to give up the current connection + */ +static int move_to_msc(struct gsm_subscriber_connection *_conn, + struct msgb *msg, struct osmo_msc_data *msc) +{ + struct osmo_bsc_sccp_con *old_con = _conn->sccp_con; + + /* + * 1. Give up the old connection. + * This happens by sending a clear request to the MSC, + * it should end with the MSC releasing the connection. + */ + old_con->conn = NULL; + bsc_clear_request(_conn, 0); + + /* + * 2. Attempt to create a new connection to the local + * MSC. If it fails the caller will need to handle this + * properly. + */ + _conn->sccp_con = NULL; + if (complete_layer3(_conn, msg, msc) != BSC_API_CONN_POL_ACCEPT) { + gsm0808_clear(_conn); + subscr_con_free(_conn); + return 1; + } + + return 2; +} + +static int handle_cc_setup(struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t pdisc = gh->proto_discr & 0x0f; + uint8_t mtype = gh->msg_type & 0xbf; + + struct osmo_msc_data *msc; + struct gsm_mncc_number called; + struct tlv_parsed tp; + unsigned payload_len; + + char _dest_nr[35]; + + /* + * Do we have a setup message here? if not return fast. + */ + if (pdisc != GSM48_PDISC_CC || mtype != GSM48_MT_CC_SETUP) + return 0; + + payload_len = msgb_l3len(msg) - sizeof(*gh); + + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { + LOGP(DMSC, LOGL_ERROR, "Called BCD not present in setup.\n"); + return -1; + } + + memset(&called, 0, sizeof(called)); + gsm48_decode_called(&called, + TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1); + + if (called.plan != 1 && called.plan != 0) + return 0; + + if (called.plan == 1 && called.type == 1) { + _dest_nr[0] = _dest_nr[1] = '0'; + memcpy(_dest_nr + 2, called.number, sizeof(called.number)); + } else + memcpy(_dest_nr, called.number, sizeof(called.number)); + + /* + * Check if the connection should be moved... + */ + llist_for_each_entry(msc, &conn->bts->network->bsc_data->mscs, entry) { + if (msc->type != MSC_CON_TYPE_LOCAL) + continue; + if (!msc->local_pref) + continue; + if (regexec(&msc->local_pref_reg, _dest_nr, 0, NULL, 0) != 0) + continue; + + return move_to_msc(conn, msg, msc); + } + + return 0; +} + + +static void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) +{ + 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; + + 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 gsm48_multi_rate_conf *conf) +{ + struct osmo_msc_data *msc; + + 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; + + conf->ver = 1; + conf->icmi = 1; + + /* maybe gcc see's it is copy of _one_ byte */ + conf->m4_75 = msc->amr_conf.m4_75; + conf->m5_15 = msc->amr_conf.m5_15; + conf->m5_90 = msc->amr_conf.m5_90; + conf->m6_70 = msc->amr_conf.m6_70; + conf->m7_40 = msc->amr_conf.m7_40; + conf->m7_95 = msc->amr_conf.m7_95; + conf->m10_2 = msc->amr_conf.m10_2; + conf->m12_2 = msc->amr_conf.m12_2; +} + +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/src/osmo-bsc/osmo_bsc_audio.c b/src/osmo-bsc/osmo_bsc_audio.c new file mode 100644 index 000000000..660d88497 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_audio.c @@ -0,0 +1,73 @@ +/* + * 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/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c new file mode 100644 index 000000000..c2c241732 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_bssap.c @@ -0,0 +1,556 @@ +/* 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 + +static uint16_t read_data16(const uint8_t *data) +{ + uint16_t res; + + memcpy(&res, data, sizeof(res)); + return res; +} + +/* + * helpers for the assignment command + */ +enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio) +{ + if (audio->hr) { + switch (audio->ver) { + case 1: + return GSM0808_PERM_HR1; + break; + case 2: + return GSM0808_PERM_HR2; + break; + case 3: + return GSM0808_PERM_HR3; + break; + default: + LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); + return GSM0808_PERM_FR1; + } + } else { + switch (audio->ver) { + case 1: + return GSM0808_PERM_FR1; + break; + case 2: + return GSM0808_PERM_FR2; + break; + case 3: + return GSM0808_PERM_FR3; + break; + default: + LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); + return GSM0808_PERM_HR1; + } + } +} + +enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech) +{ + switch (speech) { + case GSM0808_PERM_HR1: + case GSM0808_PERM_FR1: + return GSM48_CMODE_SPEECH_V1; + break; + case GSM0808_PERM_HR2: + case GSM0808_PERM_FR2: + return GSM48_CMODE_SPEECH_EFR; + break; + case GSM0808_PERM_HR3: + case GSM0808_PERM_FR3: + return GSM48_CMODE_SPEECH_AMR; + break; + } + + LOGP(DMSC, LOGL_FATAL, "Should not be reached.\n"); + return GSM48_CMODE_SPEECH_AMR; +} + +static int bssmap_handle_reset_ack(struct osmo_msc_data *msc, + struct msgb *msg, unsigned int length) +{ + LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n"); + return 0; +} + +/* GSM 08.08 § 3.2.1.19 */ +static int bssmap_handle_paging(struct osmo_msc_data *msc, + struct msgb *msg, unsigned int payload_length) +{ + struct gsm_subscriber *subscr; + struct tlv_parsed tp; + char mi_string[GSM48_MI_SIZE]; + uint32_t tmsi = GSM_RESERVED_TMSI; + unsigned int lac = GSM_LAC_RESERVED_ALL_BTS; + uint8_t data_length; + const uint8_t *data; + uint8_t chan_needed = RSL_CHANNEED_ANY; + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0); + + if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) { + LOGP(DMSC, LOGL_ERROR, "Mandatory IMSI not present.\n"); + return -1; + } else if ((TLVP_VAL(&tp, GSM0808_IE_IMSI)[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) { + LOGP(DMSC, LOGL_ERROR, "Wrong content in the IMSI\n"); + return -1; + } + + if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) { + LOGP(DMSC, LOGL_ERROR, "Mandatory CELL IDENTIFIER LIST not present.\n"); + return -1; + } + + if (TLVP_PRESENT(&tp, GSM0808_IE_TMSI) && + TLVP_LEN(&tp, GSM0808_IE_TMSI) == 4) { + tmsi = ntohl(*(uint32_t *) TLVP_VAL(&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 = ntohs(read_data16(&data[1])); + } else if (data_length > 1 || (data[0] & 0x0f) != CELL_IDENT_BSS) { + LOGP(DMSC, LOGL_ERROR, "Unsupported Cell Identifier List: %s\n", osmo_hexdump(data, data_length)); + return -1; + } + + if (TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_NEEDED) && TLVP_LEN(&tp, GSM0808_IE_CHANNEL_NEEDED) == 1) + chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03; + + if (TLVP_PRESENT(&tp, GSM0808_IE_EMLPP_PRIORITY)) { + LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n"); + } + + subscr = subscr_get_or_create(msc->network, mi_string); + if (!subscr) { + LOGP(DMSC, LOGL_ERROR, "Failed to allocate a subscriber for %s\n", mi_string); + return -1; + } + + subscr->lac = lac; + subscr->tmsi = tmsi; + + LOGP(DMSC, LOGL_INFO, "Paging request from MSC IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", mi_string, tmsi, tmsi, lac); + bsc_grace_paging_request(subscr, chan_needed, msc); + return 0; +} + +/* + * GSM 08.08 § 3.1.9.1 and 3.2.1.21... + * release our gsm_subscriber_connection and send message + */ +static int bssmap_handle_clear_command(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int payload_length) +{ + struct msgb *resp; + + /* TODO: handle the cause of this package */ + + if (conn->conn) { + LOGP(DMSC, LOGL_INFO, "Releasing all transactions on %p\n", conn); + gsm0808_clear(conn->conn); + subscr_con_free(conn->conn); + conn->conn = NULL; + } + + /* send the clear complete message */ + resp = gsm0808_create_clear_complete(); + if (!resp) { + LOGP(DMSC, LOGL_ERROR, "Sending clear complete failed.\n"); + return -1; + } + + bsc_queue_for_msc(conn, resp); + return 0; +} + +/* + * GSM 08.08 § 3.4.7 cipher mode handling. We will have to pick + * the cipher to be used for this. In case we are already using + * a cipher we will have to send cipher mode reject to the MSC, + * otherwise we will have to pick something that we and the MS + * is supporting. Currently we are doing it in a rather static + * way by picking one ecnryption or no encrytpion. + */ +static int bssmap_handle_cipher_mode(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int payload_length) +{ + uint16_t len; + struct gsm_network *network = NULL; + const uint8_t *data; + struct tlv_parsed tp; + struct msgb *resp; + int reject_cause = -1; + int include_imeisv = 1; + + if (!conn->conn) { + LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n"); + goto reject; + } + + if (conn->ciphering_handled) { + LOGP(DMSC, LOGL_ERROR, "Already seen ciphering command. Protocol Error.\n"); + goto reject; + } + + conn->ciphering_handled = 1; + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0); + if (!TLVP_PRESENT(&tp, GSM0808_IE_ENCRYPTION_INFORMATION)) { + LOGP(DMSC, LOGL_ERROR, "IE Encryption Information missing.\n"); + goto reject; + } + + /* + * check if our global setting is allowed + * - Currently we check for A5/0 and A5/1 + * - Copy the key if that is necessary + * - Otherwise reject + */ + len = TLVP_LEN(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); + if (len < 1) { + LOGP(DMSC, LOGL_ERROR, "IE Encryption Information is too short.\n"); + goto reject; + } + + network = conn->conn->bts->network; + data = TLVP_VAL(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); + + if (TLVP_PRESENT(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)) + include_imeisv = TLVP_VAL(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)[0] & 0x1; + + if (network->a5_encryption == 0 && (data[0] & 0x1) == 0x1) { + gsm0808_cipher_mode(conn->conn, 0, NULL, 0, include_imeisv); + } else if (network->a5_encryption != 0 && (data[0] & 0x2) == 0x2) { + gsm0808_cipher_mode(conn->conn, 1, &data[1], len - 1, include_imeisv); + } else { + LOGP(DMSC, LOGL_ERROR, "Can not select encryption...\n"); + goto reject; + } + + return 0; + +reject: + resp = gsm0808_create_cipher_reject(reject_cause); + if (!resp) { + LOGP(DMSC, LOGL_ERROR, "Sending the cipher reject failed.\n"); + return -1; + } + + bsc_queue_for_msc(conn, resp); + return -1; +} + +/* + * Handle the assignment request message. + * + * See §3.2.1.1 for the message type + */ +static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int length) +{ + struct msgb *resp; + struct osmo_msc_data *msc; + struct tlv_parsed tp; + uint8_t *data; + uint16_t cic; + 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; + } + + cic = ntohs(read_data16(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE))); + timeslot = cic & 0x1f; + multiplex = (cic & ~0x1f) >> 5; + + /* + * Currently we only support a limited subset of all + * possible channel types. The limitation ends by not using + * multi-slot, limiting the channel coding, speech... + */ + if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) { + LOGP(DMSC, LOGL_ERROR, "ChannelType len !=3 not supported: %d\n", + TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE)); + goto reject; + } + + /* + * Try to figure out if we support the proposed speech codecs. For + * now we will always pick the full rate codecs. + */ + + data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE); + if ((data[0] & 0xf) != 0x1) { + LOGP(DMSC, LOGL_ERROR, "ChannelType != speech: %d\n", data[0]); + goto reject; + } + + /* + * go through the list of preferred codecs of our gsm network + * and try to find it among the permitted codecs. If we found + * it we will send chan_mode to the right mode and break the + * inner loop. The outer loop will exit due chan_mode having + * the correct value. + */ + full_rate = 0; + msc = conn->msc; + for (supported = 0; + chan_mode == GSM48_CMODE_SIGN && supported < msc->audio_length; + ++supported) { + + int perm_val = audio_support_to_gsm88(msc->audio_support[supported]); + for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) { + if ((data[i] & 0x7f) == perm_val) { + chan_mode = gsm88_to_chan_mode(perm_val); + full_rate = (data[i] & 0x4) == 0; + break; + } else if ((data[i] & 0x80) == 0x00) { + break; + } + } + } + + if (chan_mode == GSM48_CMODE_SIGN) { + LOGP(DMSC, LOGL_ERROR, "No supported audio type found.\n"); + goto reject; + } + + /* map it to a MGCP Endpoint and a RTP port */ + port = mgcp_timeslot_to_endpoint(multiplex, timeslot); + conn->rtp_port = rtp_calculate_port(port, msc->rtp_base); + + return gsm0808_assign_req(conn->conn, chan_mode, full_rate); + +reject: + resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); + if (!resp) { + LOGP(DMSC, LOGL_ERROR, "Channel allocation failure.\n"); + return -1; + } + + bsc_queue_for_msc(conn, resp); + return -1; +} + +static int bssmap_rcvmsg_udt(struct osmo_msc_data *msc, + struct msgb *msg, unsigned int length) +{ + int ret = 0; + + if (length < 1) { + LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); + return -1; + } + + LOGP(DMSC, LOGL_INFO, "Rx MSC UDT BSSMAP %s\n", + gsm0808_bssmap_name(msg->l4h[0])); + + switch (msg->l4h[0]) { + case BSS_MAP_MSG_RESET_ACKNOWLEDGE: + ret = bssmap_handle_reset_ack(msc, msg, length); + break; + case BSS_MAP_MSG_PAGING: + ret = bssmap_handle_paging(msc, msg, length); + break; + } + + return ret; +} + +static int bssmap_rcvmsg_dt1(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int length) +{ + int ret = 0; + + if (length < 1) { + LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); + return -1; + } + + LOGP(DMSC, LOGL_INFO, "Rx MSC DT1 BSSMAP %s\n", + gsm0808_bssmap_name(msg->l4h[0])); + + switch (msg->l4h[0]) { + case BSS_MAP_MSG_CLEAR_CMD: + ret = bssmap_handle_clear_command(conn, msg, length); + break; + case BSS_MAP_MSG_CIPHER_MODE_CMD: + ret = bssmap_handle_cipher_mode(conn, msg, length); + break; + case BSS_MAP_MSG_ASSIGMENT_RQST: + ret = bssmap_handle_assignm_req(conn, msg, length); + break; + default: + LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", + gsm0808_bssmap_name(msg->l4h[0])); + break; + } + + return ret; +} + +static int dtap_rcvmsg(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int length) +{ + struct dtap_header *header; + struct msgb *gsm48; + uint8_t *data; + int rc, dtap_rc; + + LOGP(DMSC, LOGL_DEBUG, "Rx MSC DTAP: %s\n", + osmo_hexdump(msg->l3h, length)); + + if (!conn->conn) { + LOGP(DMSC, LOGL_ERROR, "No subscriber connection available\n"); + return -1; + } + + header = (struct dtap_header *) msg->l3h; + if (sizeof(*header) >= length) { + LOGP(DMSC, LOGL_ERROR, "The DTAP header does not fit. Wanted: %zu got: %u\n", sizeof(*header), length); + LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length)); + return -1; + } + + if (header->length > length - sizeof(*header)) { + LOGP(DMSC, LOGL_ERROR, "The DTAP l4 information does not fit: header: %u length: %u\n", header->length, length); + LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length)); + return -1; + } + + LOGP(DMSC, LOGL_INFO, "Rx MSC DTAP, SAPI: %u CHAN: %u\n", header->link_id & 0x07, header->link_id & 0xC0); + + /* forward the data */ + gsm48 = gsm48_msgb_alloc(); + if (!gsm48) { + LOGP(DMSC, LOGL_ERROR, "Allocation of the message failed.\n"); + return -1; + } + + gsm48->l3h = gsm48->data; + data = msgb_put(gsm48, length - sizeof(*header)); + memcpy(data, msg->l3h + sizeof(*header), length - sizeof(*header)); + + /* pass it to the filter for extra actions */ + rc = bsc_scan_msc_msg(conn->conn, gsm48); + dtap_rc = gsm0808_submit_dtap(conn->conn, gsm48, header->link_id, 1); + if (rc == BSS_SEND_USSD) + bsc_send_welcome_ussd(conn->conn); + return dtap_rc; +} + +int bsc_handle_udt(struct osmo_msc_data *msc, + struct msgb *msgb, unsigned int length) +{ + struct bssmap_header *bs; + + LOGP(DMSC, LOGL_DEBUG, "Rx MSC UDT: %s\n", + osmo_hexdump(msgb->l3h, length)); + + if (length < sizeof(*bs)) { + LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); + return -1; + } + + bs = (struct bssmap_header *) msgb->l3h; + if (bs->length < length - sizeof(*bs)) + return -1; + + switch (bs->type) { + case BSSAP_MSG_BSS_MANAGEMENT: + msgb->l4h = &msgb->l3h[sizeof(*bs)]; + bssmap_rcvmsg_udt(msc, msgb, length - sizeof(*bs)); + break; + default: + LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", + gsm0808_bssmap_name(bs->type)); + } + + return 0; +} + +int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int len) +{ + if (len < sizeof(struct bssmap_header)) { + LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); + } + + switch (msg->l3h[0]) { + case BSSAP_MSG_BSS_MANAGEMENT: + msg->l4h = &msg->l3h[sizeof(struct bssmap_header)]; + bssmap_rcvmsg_dt1(conn, msg, len - sizeof(struct bssmap_header)); + break; + case BSSAP_MSG_DTAP: + dtap_rcvmsg(conn, msg, len); + break; + default: + LOGP(DMSC, LOGL_NOTICE, "Unimplemented BSSAP msg type: %s\n", + gsm0808_bssap_name(msg->l3h[0])); + } + + return -1; +} diff --git a/src/osmo-bsc/osmo_bsc_ctrl.c b/src/osmo-bsc/osmo_bsc_ctrl.c new file mode 100644 index 000000000..72abcf427 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_ctrl.c @@ -0,0 +1,505 @@ +/* (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 + +void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_connection *msc_con) +{ + struct ctrl_cmd *trap; + struct ctrl_handle *ctrl; + struct osmo_msc_data *msc_data; + + msc_data = (struct osmo_msc_data *) msc_con->write_queue.bfd.data; + ctrl = msc_data->network->ctrl; + + trap = ctrl_cmd_trap(cmd); + if (!trap) { + LOGP(DCTRL, LOGL_ERROR, "Failed to create trap.\n"); + return; + } + + ctrl_cmd_send_to_all(ctrl, trap); + ctrl_cmd_send(&msc_con->write_queue, trap); + + talloc_free(trap); +} + +CTRL_CMD_DEFINE(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 set_msc_connection_status(struct ctrl_cmd *cmd, void *data) +{ + return CTRL_CMD_ERROR; +} + +static int verify_msc_connection_status(struct ctrl_cmd *cmd, const char *value, void *data) +{ + cmd->reply = "Read-only property"; + return 1; +} + +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(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 set_bts_connection_status(struct ctrl_cmd *cmd, void *data) +{ + return CTRL_CMD_ERROR; +} + +static int verify_bts_connection_status(struct ctrl_cmd *cmd, const char *value, void *data) +{ + cmd->reply = "Read-only property"; + return 1; +} + +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", oper, admin, policy); + + osmo_bsc_send_trap(cmd, msc_con); + talloc_free(cmd); +} + +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 osmo_msc_data *msc; + struct bts_location *curloc, *lastloc; + int ret; + struct gsm_network *gsmnet = (struct gsm_network *)data; + 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)) + llist_for_each_entry(msc, &gsmnet->bsc_data->mscs, entry) + generate_location_state_trap(bts, msc->msc_con); + + cleanup_locations(&bts->loc_list); + + return ret; + +oom: + cmd->reply = "OOM"; + return CTRL_CMD_ERROR; +} + +static int verify_bts_loc(struct ctrl_cmd *cmd, const char *value, void *data) +{ + char *saveptr, *latstr, *lonstr, *heightstr, *tstampstr, *validstr, *tmp; + time_t tstamp; + int valid; + double lat, lon, height __attribute__((unused)); + + tmp = talloc_strdup(cmd, value); + if (!tmp) + return 1; + + tstampstr = strtok_r(tmp, ",", &saveptr); + validstr = strtok_r(NULL, ",", &saveptr); + latstr = strtok_r(NULL, ",", &saveptr); + lonstr = strtok_r(NULL, ",", &saveptr); + heightstr = strtok_r(NULL, "\0", &saveptr); + + if ((tstampstr == NULL) || (validstr == NULL) || (latstr == NULL) || + (lonstr == NULL) || (heightstr == NULL)) + goto err; + + tstamp = atol(tstampstr); + valid = get_string_value(bts_loc_fix_names, validstr); + lat = atof(latstr); + lon = atof(lonstr); + height = atof(heightstr); + talloc_free(tmp); + tmp = NULL; + + if (((tstamp == 0) && (valid != BTS_LOC_FIX_INVALID)) || (lat < -90) || (lat > 90) || + (lon < -180) || (lon > 180) || (valid < 0)) { + goto err; + } + + return 0; + +err: + talloc_free(tmp); + cmd->reply = talloc_strdup(cmd, "The format is ,(invalid|fix2d|fix3d),,,"); + return 1; +} + +CTRL_CMD_DEFINE(bts_rf_state, "rf_state"); +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; +} + +static int set_bts_rf_state(struct ctrl_cmd *cmd, void *data) +{ + cmd->reply = "set is unimplemented"; + return CTRL_CMD_ERROR; +} + +static int verify_bts_rf_state(struct ctrl_cmd *cmd, const char *value, void *data) +{ + return 0; +} + +CTRL_CMD_DEFINE(net_rf_lock, "rf_locked"); +static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data) +{ + cmd->reply = "get only works for the individual trx properties."; + return CTRL_CMD_ERROR; +} + +static int set_net_rf_lock(struct ctrl_cmd *cmd, void *data) +{ + int locked = atoi(cmd->value); + struct gsm_network *net = cmd->node; + if (!net) { + cmd->reply = "net not found."; + return CTRL_CMD_ERROR; + } + + if (!net->bsc_data->rf_ctrl) { + cmd->reply = "RF Ctrl is not enabled in the BSC Configuration"; + return CTRL_CMD_ERROR; + } + + osmo_bsc_rf_schedule_lock(net->bsc_data->rf_ctrl, + 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; +} + +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 = ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_state); + 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_rf_lock); + 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 = osmo_signal_register_handler(SS_L_INPUT, &bts_connection_status_trap_cb, net); + +end: + return rc; +} diff --git a/src/osmo-bsc/osmo_bsc_filter.c b/src/osmo-bsc/osmo_bsc_filter.c new file mode 100644 index 000000000..957ceaf95 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_filter.c @@ -0,0 +1,291 @@ +/* (C) 2009-2011 by Holger Hans Peter Freyther + * (C) 2009-2011 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +static void handle_lu_request(struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct gsm48_hdr *gh; + struct gsm48_loc_upd_req *lu; + struct gsm48_loc_area_id lai; + struct gsm_network *net; + + if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu)) { + LOGP(DMSC, LOGL_ERROR, "LU too small to look at: %u\n", msgb_l3len(msg)); + return; + } + + net = conn->bts->network; + + gh = msgb_l3(msg); + lu = (struct gsm48_loc_upd_req *) gh->data; + + gsm48_generate_lai(&lai, net->country_code, net->network_code, + conn->bts->location_area_code); + + if (memcmp(&lai, &lu->lai, sizeof(lai)) != 0) { + LOGP(DMSC, LOGL_DEBUG, "Marking con for welcome USSD.\n"); + conn->sccp_con->new_subscriber = 1; + } +} + +/* extract a subscriber from the paging response */ +static struct gsm_subscriber *extract_sub(struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + uint8_t mi_type; + char mi_string[GSM48_MI_SIZE]; + struct gsm48_hdr *gh; + struct gsm48_pag_resp *resp; + struct gsm_subscriber *subscr; + + if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*resp)) { + LOGP(DMSC, LOGL_ERROR, "PagingResponse too small: %u\n", msgb_l3len(msg)); + return NULL; + } + + gh = msgb_l3(msg); + resp = (struct gsm48_pag_resp *) &gh->data[0]; + + gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), + mi_string, &mi_type); + DEBUGP(DRR, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n", + mi_type, mi_string); + + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + subscr = subscr_active_by_tmsi(conn->bts->network, + tmsi_from_string(mi_string)); + break; + case GSM_MI_TYPE_IMSI: + subscr = subscr_active_by_imsi(conn->bts->network, mi_string); + break; + default: + subscr = NULL; + break; + } + + return subscr; +} + +/* we will need to stop the paging request */ +static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm_subscriber *subscr = extract_sub(conn, msg); + + if (!subscr) { + LOGP(DMSC, LOGL_ERROR, "Non active subscriber got paged.\n"); + return -1; + } + + paging_request_stop(conn->bts, subscr, conn, msg); + subscr_put(subscr); + return 0; +} + +static int is_cm_service_for_emerg(struct msgb *msg) +{ + struct gsm48_service_request *cm; + struct gsm48_hdr *gh = msgb_l3(msg); + + if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*cm)) { + LOGP(DMSC, LOGL_ERROR, "CM ServiceRequest does not fit.\n"); + return 0; + } + + cm = (struct gsm48_service_request *) &gh->data[0]; + return cm->cm_service_type == GSM48_CMSERV_EMERGENCY; +} + +struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct gsm48_hdr *gh; + int8_t pdisc; + uint8_t mtype; + struct osmo_bsc_data *bsc; + struct osmo_msc_data *msc, *pag_msc; + struct gsm_subscriber *subscr; + int is_emerg = 0; + + bsc = conn->bts->network->bsc_data; + + if (msgb_l3len(msg) < sizeof(*gh)) { + LOGP(DMSC, LOGL_ERROR, "There is no GSM48 header here.\n"); + return NULL; + } + + gh = msgb_l3(msg); + pdisc = gh->proto_discr & 0x0f; + mtype = gh->msg_type & 0xbf; + + /* + * We are asked to select a MSC here but they are not equal. We + * want to respond to a paging request on the MSC where we got the + * request from. This is where we need to decide where this connection + * will go. + */ + if (pdisc == GSM48_PDISC_RR && mtype == GSM48_MT_RR_PAG_RESP) + goto paging; + else if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) { + is_emerg = is_cm_service_for_emerg(msg); + goto round_robin; + } else + goto round_robin; + +round_robin: + llist_for_each_entry(msc, &bsc->mscs, entry) { + if (!msc->msc_con->is_authenticated) + continue; + if (!is_emerg && msc->type != MSC_CON_TYPE_NORMAL) + continue; + if (is_emerg && !msc->allow_emerg) + continue; + + /* force round robin by moving it to the end */ + llist_move_tail(&msc->entry, &bsc->mscs); + return msc; + } + + return NULL; + +paging: + subscr = extract_sub(conn, msg); + + if (!subscr) { + LOGP(DMSC, LOGL_ERROR, "Got paged but no subscriber found.\n"); + return NULL; + } + + pag_msc = paging_get_data(conn->bts, subscr); + subscr_put(subscr); + + llist_for_each_entry(msc, &bsc->mscs, entry) { + if (msc != pag_msc) + continue; + + /* + * We don't check if the MSC is connected. In case it + * is not the connection will be dropped. + */ + + /* force round robin by moving it to the end */ + llist_move_tail(&msc->entry, &bsc->mscs); + return msc; + } + + LOGP(DMSC, LOGL_ERROR, "Got paged but no request found.\n"); + return NULL; +} + + +/** + * This is used to scan a message for extra functionality of the BSC. This + * includes scanning for location updating requests/acceptd and then send + * a welcome USSD message to the subscriber. + */ +int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t pdisc = gh->proto_discr & 0x0f; + uint8_t mtype = gh->msg_type & 0xbf; + + if (pdisc == GSM48_PDISC_MM) { + if (mtype == GSM48_MT_MM_LOC_UPD_REQUEST) + handle_lu_request(conn, msg); + } else if (pdisc == GSM48_PDISC_RR) { + if (mtype == GSM48_MT_RR_PAG_RESP) + handle_page_resp(conn, msg); + } + + return 0; +} + +static int send_welcome_ussd(struct gsm_subscriber_connection *conn) +{ + struct osmo_bsc_sccp_con *bsc_con; + + bsc_con = conn->sccp_con; + if (!bsc_con) { + LOGP(DMSC, LOGL_DEBUG, "No SCCP connection associated.\n"); + return 0; + } + + if (!bsc_con->msc->ussd_welcome_txt) { + LOGP(DMSC, LOGL_DEBUG, "No USSD Welcome text defined.\n"); + return 0; + } + + return BSS_SEND_USSD; +} + +int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn) +{ + gsm0480_send_ussdNotify(conn, 1, conn->sccp_con->msc->ussd_welcome_txt); + gsm0480_send_releaseComplete(conn); + + return 0; +} + +/** + * Messages coming back from the MSC. + */ +int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct osmo_msc_data *msc; + struct gsm_network *net; + struct gsm48_loc_area_id *lai; + struct gsm48_hdr *gh; + uint8_t mtype; + + if (msgb_l3len(msg) < sizeof(*gh)) { + LOGP(DMSC, LOGL_ERROR, "GSM48 header does not fit.\n"); + return -1; + } + + gh = (struct gsm48_hdr *) msgb_l3(msg); + mtype = gh->msg_type & 0xbf; + net = conn->bts->network; + msc = conn->sccp_con->msc; + + if (mtype == GSM48_MT_MM_LOC_UPD_ACCEPT) { + if (msc->core_ncc != -1 || msc->core_mcc != -1) { + if (msgb_l3len(msg) >= sizeof(*gh) + sizeof(*lai)) { + lai = (struct gsm48_loc_area_id *) &gh->data[0]; + gsm48_generate_lai(lai, net->country_code, + net->network_code, + conn->bts->location_area_code); + } + } + + if (conn->sccp_con->new_subscriber) + return send_welcome_ussd(conn); + return 0; + } + + return 0; +} diff --git a/src/osmo-bsc/osmo_bsc_grace.c b/src/osmo-bsc/osmo_bsc_grace.c new file mode 100644 index 000000000..341971ef2 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_grace.c @@ -0,0 +1,151 @@ +/* + * (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 (!network->bsc_data->rf_ctrl) + return 1; + if (bts->excl_from_rf_lock) + return 1; + return network->bsc_data->rf_ctrl->policy == S_RF_ON; +} + +/** + * Try to not page if everything the cell is not on. + */ +int bsc_grace_paging_request(struct gsm_subscriber *subscr, int chan_needed, + struct osmo_msc_data *msc) +{ + struct gsm_bts *bts = NULL; + + if (!subscr->net->bsc_data->rf_ctrl) + goto page; + if (subscr->net->bsc_data->rf_ctrl->policy == S_RF_ON) + goto page; + + /* + * Check if there is any BTS that is on for the given lac. Start + * with NULL and iterate through all bts. + */ + do { + bts = gsm_bts_by_lac(subscr->net, subscr->lac, bts); + if (!bts) + break; + + /* + * continue if the BTS is not excluded from the lock + */ + if (!bts->excl_from_rf_lock) + continue; + + /* + * now page on this bts + */ + paging_request_bts(bts, subscr, chan_needed, NULL, msc); + } while (1); + + /* All bts are either off or in the grace period */ + return 0; +page: + return paging_request(subscr->net, subscr, chan_needed, NULL, msc); +} + +static int handle_sub(struct gsm_lchan *lchan, const char *text) +{ + struct gsm_subscriber_connection *conn; + + /* only send it to TCH */ + if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F) + return -1; + + /* only send on the primary channel */ + conn = lchan->conn; + if (!conn) + return -1; + + if (conn->lchan != lchan) + return -1; + + /* only when active */ + if (lchan->state != LCHAN_S_ACTIVE) + return -1; + + gsm0480_send_ussdNotify(conn, 0, text); + gsm0480_send_releaseComplete(conn); + + return 0; +} + +/* + * The place to handle the grace mode. Right now we will send + * USSD messages to the subscriber, in the future we might start + * a timer to have different modes for the grace period. + */ +static int handle_grace(struct gsm_network *network) +{ + int ts_nr, lchan_nr; + struct gsm_bts *bts; + struct gsm_bts_trx *trx; + + if (!network->bsc_data->mid_call_txt) + return 0; + + llist_for_each_entry(bts, &network->bts_list, list) { + llist_for_each_entry(trx, &bts->trx_list, list) { + for (ts_nr = 0; ts_nr < TRX_NR_TS; ++ts_nr) { + struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; + for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; ++lchan_nr) { + handle_sub(&ts->lchan[lchan_nr], + network->bsc_data->mid_call_txt); + } + } + } + } + return 0; +} + +static int handle_rf_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct rf_signal_data *sig; + + if (subsys != SS_RF) + return -1; + + sig = signal_data; + + if (signal == S_RF_GRACE) + handle_grace(sig->net); + + return 0; +} + +static __attribute__((constructor)) void on_dso_load_grace(void) +{ + osmo_signal_register_handler(SS_RF, handle_rf_signal, NULL); +} diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c new file mode 100644 index 000000000..864d62970 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_main.c @@ -0,0 +1,277 @@ +/* (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 + +#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 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 = strdup(optarg); + break; + case 'T': + log_set_print_timestamp(osmo_stderr_target, 1); + break; + case 'e': + log_set_log_level(osmo_stderr_target, atoi(optarg)); + break; + case 'r': + rf_ctrl = optarg; + break; + default: + /* ignore */ + break; + } + } +} + +extern enum node_type bsc_vty_go_parent(struct vty *vty); + +static struct vty_app_info vty_info = { + .name = "OsmoBSC", + .version = PACKAGE_VERSION, + .go_parent_cb = bsc_vty_go_parent, + .is_config_node = bsc_vty_is_config_node, +}; + +extern int bsc_shutdown_net(struct gsm_network *net); +static void signal_handler(int signal) +{ + struct osmo_msc_data *msc; + + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + bsc_shutdown_net(bsc_gsmnet); + osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); + sleep(3); + exit(0); + break; + case SIGABRT: + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + talloc_report(tall_vty_ctx, stderr); + talloc_report_full(tall_bsc_ctx, stderr); + break; + case SIGUSR2: + if (!bsc_gsmnet->bsc_data) + return; + llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) + bsc_msc_lost(msc->msc_con); + break; + default: + break; + } +} + +int main(int argc, char **argv) +{ + struct osmo_msc_data *msc; + struct osmo_bsc_data *data; + int rc; + + tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); + + osmo_init_logging(&log_info); + + bts_init(); + libosmo_abis_init(tall_bsc_ctx); + + /* enable filters */ + + /* This needs to precede handle_options() */ + vty_info.copyright = openbsc_copyright; + vty_init(&vty_info); + bsc_vty_init(&log_info); + + /* parse options */ + handle_options(argc, argv); + + /* seed the PRNG */ + srand(time(NULL)); + + /* initialize SCCP */ + sccp_set_log_area(DSCCP); + + + rc = bsc_bootstrap_network(NULL, config_file); + if (rc < 0) { + fprintf(stderr, "Bootstrapping the network failed. exiting.\n"); + exit(1); + } + bsc_api_init(bsc_gsmnet, osmo_bsc_api()); + + bsc_gsmnet->ctrl = controlif_setup(bsc_gsmnet, 4249); + if (!bsc_gsmnet) { + fprintf(stderr, "Failed to init the control interface. Exiting.\n"); + exit(1); + } + + rc = bsc_ctrl_cmds_install(bsc_gsmnet); + if (rc < 0) { + fprintf(stderr, "Failed to install control commands. Exiting.\n"); + exit(1); + } + + data = bsc_gsmnet->bsc_data; + if (rf_ctrl) + bsc_replace_string(data, &data->rf_ctrl_name, rf_ctrl); + + if (data->rf_ctrl_name) { + 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/src/osmo-bsc/osmo_bsc_msc.c b/src/osmo-bsc/osmo_bsc_msc.c new file mode 100644 index 000000000..5517d3080 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_msc.c @@ -0,0 +1,505 @@ +/* + * Handle the connection to the MSC. This include ping/timeout/reconnect + * (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 + + +static void initialize_if_needed(struct bsc_msc_connection *conn); +static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn); +static void send_id_get_response(struct osmo_msc_data *data, int fd); +static void send_ping(struct osmo_msc_data *data); + +/* + * MGCP forwarding code + */ +static int mgcp_do_read(struct osmo_fd *fd) +{ + struct osmo_msc_data *data = (struct osmo_msc_data *) fd->data; + struct msgb *mgcp; + int ret; + + mgcp = msgb_alloc_headroom(4096, 128, "mgcp_from_gw"); + if (!mgcp) { + LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n"); + return -1; + } + + ret = read(fd->fd, mgcp->data, 4096 - 128); + if (ret <= 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno)); + msgb_free(mgcp); + return -1; + } else if (ret > 4096 - 128) { + LOGP(DMGCP, LOGL_ERROR, "Too much data: %d\n", ret); + msgb_free(mgcp); + return -1; + } + + mgcp->l2h = msgb_put(mgcp, ret); + msc_queue_write(data->msc_con, mgcp, IPAC_PROTO_MGCP_OLD); + return 0; +} + +static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg) +{ + int ret; + + LOGP(DMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len); + + ret = write(fd->fd, msg->data, msg->len); + if (ret != msg->len) + LOGP(DMGCP, LOGL_ERROR, "Failed to forward message to MGCP GW (%s).\n", strerror(errno)); + + return ret; +} + +static void mgcp_forward(struct osmo_msc_data *data, struct msgb *msg) +{ + struct msgb *mgcp; + + if (msgb_l2len(msg) > 4096) { + LOGP(DMGCP, LOGL_ERROR, "Can not forward too big message.\n"); + return; + } + + mgcp = msgb_alloc(4096, "mgcp_to_gw"); + if (!mgcp) { + LOGP(DMGCP, LOGL_ERROR, "Failed to send message.\n"); + return; + } + + msgb_put(mgcp, msgb_l2len(msg)); + memcpy(mgcp->data, msg->l2h, mgcp->len); + if (osmo_wqueue_enqueue(&data->mgcp_agent, mgcp) != 0) { + LOGP(DMGCP, LOGL_FATAL, "Could not queue message to MGCP GW.\n"); + msgb_free(mgcp); + } +} + +static int mgcp_create_port(struct osmo_msc_data *data) +{ + int on; + struct sockaddr_in addr; + + data->mgcp_agent.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); + if (data->mgcp_agent.bfd.fd < 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno); + return -1; + } + + on = 1; + setsockopt(data->mgcp_agent.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + /* try to bind the socket */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = 0; + + if (bind(data->mgcp_agent.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to bind to any port.\n"); + close(data->mgcp_agent.bfd.fd); + data->mgcp_agent.bfd.fd = -1; + return -1; + } + + /* connect to the remote */ + addr.sin_port = htons(2427); + if (connect(data->mgcp_agent.bfd.fd, (struct sockaddr *) & addr, sizeof(addr)) < 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to connect to local MGCP GW. %s\n", strerror(errno)); + close(data->mgcp_agent.bfd.fd); + data->mgcp_agent.bfd.fd = -1; + return -1; + } + + osmo_wqueue_init(&data->mgcp_agent, 10); + data->mgcp_agent.bfd.when = BSC_FD_READ; + data->mgcp_agent.bfd.data = data; + data->mgcp_agent.read_cb = mgcp_do_read; + data->mgcp_agent.write_cb = mgcp_do_write; + + if (osmo_fd_register(&data->mgcp_agent.bfd) != 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to register BFD\n"); + close(data->mgcp_agent.bfd.fd); + data->mgcp_agent.bfd.fd = -1; + return -1; + } + + return 0; +} + +/* + * Send data to the network + */ +int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto) +{ + ipaccess_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; +} + +static int msc_alink_do_write(struct osmo_fd *fd, struct msgb *msg) +{ + int ret; + + LOGP(DMSC, LOGL_DEBUG, "Sending SCCP to MSC: %u\n", msgb_l2len(msg)); + LOGP(DLMI, LOGL_DEBUG, "MSC TX %s\n", osmo_hexdump(msg->data, msg->len)); + + ret = write(fd->fd, msg->data, msg->len); + if (ret < msg->len) + perror("MSC: Failed to send SCCP"); + + return ret; +} + +static void handle_ctrl(struct osmo_msc_data *msc, struct msgb *msg) +{ + int ret; + struct ctrl_cmd *cmd; + + cmd = ctrl_cmd_parse(msc->msc_con, msg); + if (!cmd) { + LOGP(DMSC, LOGL_ERROR, "Failed to parse control message.\n"); + cmd = talloc_zero(msc->msc_con, struct ctrl_cmd); + if (!cmd) { + LOGP(DMSC, LOGL_ERROR, "OOM!\n"); + return; + } + cmd->type = CTRL_TYPE_ERROR; + cmd->id = "err"; + cmd->reply = "Failed to parse control message."; + + ctrl_cmd_send(&msc->msc_con->write_queue, cmd); + talloc_free(cmd); + + return; + } + + ret = ctrl_cmd_handle(cmd, msc->network); + if (ret != CTRL_CMD_HANDLED) + ctrl_cmd_send(&msc->msc_con->write_queue, cmd); + talloc_free(cmd); +} + +static void osmo_ext_handle(struct osmo_msc_data *msc, struct msgb *msg) +{ + struct ipaccess_head *hh; + struct ipaccess_head_ext *hh_ext; + + hh = (struct ipaccess_head *) msg->data; + hh_ext = (struct ipaccess_head_ext *) hh->data; + if (msg->len < sizeof(*hh) + sizeof(*hh_ext)) { + LOGP(DMSC, LOGL_ERROR, "Packet too short for extended header.\n"); + return; + } + + msg->l2h = hh_ext->data; + if (hh_ext->proto == IPAC_PROTO_EXT_MGCP) + mgcp_forward(msc, msg); + else if (hh_ext->proto == IPAC_PROTO_EXT_LAC) + send_lacs(msc->network, msc->msc_con); + else if (hh_ext->proto == IPAC_PROTO_EXT_CTRL) + handle_ctrl(msc, msg); +} + +static int ipaccess_a_fd_cb(struct osmo_fd *bfd) +{ + struct msgb *msg; + struct ipaccess_head *hh; + struct osmo_msc_data *data = (struct osmo_msc_data *) bfd->data; + int ret; + + ret = ipa_msg_recv(bfd->fd, &msg); + if (ret <= 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; + ipaccess_rcvmsg_base(msg, bfd); + + /* initialize the networking. This includes sending a GSM08.08 message */ + msg->cb[0] = (unsigned long) data; + if (hh->proto == IPAC_PROTO_IPACCESS) { + 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); + } else if (msg->l2h[0] == IPAC_MSGT_PONG) { + osmo_timer_del(&data->pong_timer); + } + } else if (hh->proto == IPAC_PROTO_SCCP) { + sccp_system_incoming(msg); + } else if (hh->proto == IPAC_PROTO_MGCP_OLD) { + mgcp_forward(data, msg); + } else if (hh->proto == IPAC_PROTO_OSMO) { + osmo_ext_handle(data, msg); + } + + msgb_free(msg); + return 0; +} + +static void send_ping(struct osmo_msc_data *data) +{ + struct msgb *msg; + + msg = msgb_alloc_headroom(4096, 128, "ping"); + if (!msg) { + LOGP(DMSC, LOGL_ERROR, "Failed to create PING.\n"); + return; + } + + msg->l2h = msgb_put(msg, 1); + msg->l2h[0] = IPAC_MSGT_PING; + + msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS); +} + +static void msc_ping_timeout_cb(void *_data) +{ + struct osmo_msc_data *data = (struct osmo_msc_data *) _data; + if (data->ping_timeout < 0) + return; + + send_ping(data); + + /* 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_pong_timeout_cb(void *_data) +{ + struct osmo_msc_data *data = (struct osmo_msc_data *) _data; + + LOGP(DMSC, LOGL_ERROR, "MSC didn't answer PING. Closing connection.\n"); + bsc_msc_lost(data->msc_con); +} + +static void msc_connection_connected(struct bsc_msc_connection *con) +{ + struct msc_signal_data sig; + struct osmo_msc_data *data; + int ret, on; + on = 1; + ret = setsockopt(con->write_queue.bfd.fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); + if (ret != 0) + LOGP(DMSC, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); + + data = (struct osmo_msc_data *) con->write_queue.bfd.data; + msc_ping_timeout_cb(data); + + sig.data = data; + osmo_signal_dispatch(SS_MSC, S_MSC_CONNECTED, &sig); +} + +/* + * The connection to the MSC was lost and we will need to free all + * resources and then attempt to reconnect. + */ +static void msc_connection_was_lost(struct bsc_msc_connection *msc) +{ + struct msc_signal_data sig; + struct osmo_msc_data *data; + + LOGP(DMSC, LOGL_ERROR, "Lost MSC connection. Freing stuff.\n"); + + data = (struct osmo_msc_data *) msc->write_queue.bfd.data; + osmo_timer_del(&data->ping_timer); + osmo_timer_del(&data->pong_timer); + + sig.data = data; + osmo_signal_dispatch(SS_MSC, S_MSC_LOST, &sig); + + msc->is_authenticated = 0; + bsc_msc_schedule_connect(msc); +} + +static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn) +{ + struct ipac_ext_lac_cmd *lac; + struct gsm_bts *bts; + struct msgb *msg; + int lacs = 0; + + if (llist_empty(&net->bts_list)) { + LOGP(DMSC, LOGL_ERROR, "No BTSs configured. Not sending LACs.\n"); + return; + } + + msg = msgb_alloc_headroom(4096, 128, "LAC Command"); + if (!msg) { + LOGP(DMSC, LOGL_ERROR, "Failed to create the LAC command.\n"); + return; + } + + lac = (struct ipac_ext_lac_cmd *) msgb_put(msg, sizeof(*lac)); + lac->add_remove = 1; + + llist_for_each_entry(bts, &net->bts_list, list) { + if (lacs++ == 0) + lac->lac = htons(bts->location_area_code); + else + msgb_put_u16(msg, htons(bts->location_area_code)); + } + + lac->nr_extra_lacs = lacs - 1; + ipaccess_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 void send_id_get_response(struct osmo_msc_data *data, int fd) +{ + struct msc_signal_data sig; + struct msgb *msg; + + msg = bsc_msc_id_get_resp(data->bsc_token); + if (!msg) + return; + msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS); + + sig.data = data; + osmo_signal_dispatch(SS_MSC, S_MSC_AUTHENTICATED, &sig); +} + +int osmo_bsc_msc_init(struct osmo_msc_data *data) +{ + if (mgcp_create_port(data) != 0) + return -1; + + data->msc_con = bsc_msc_create(data, &data->dests); + if (!data->msc_con) { + LOGP(DMSC, LOGL_ERROR, "Creating the MSC network connection failed.\n"); + return -1; + } + + data->ping_timer.cb = msc_ping_timeout_cb; + data->ping_timer.data = data; + data->pong_timer.cb = msc_pong_timeout_cb; + data->pong_timer.data = data; + + data->msc_con->write_queue.bfd.data = data; + data->msc_con->connection_loss = msc_connection_was_lost; + data->msc_con->connected = msc_connection_connected; + data->msc_con->write_queue.read_cb = ipaccess_a_fd_cb; + data->msc_con->write_queue.write_cb = msc_alink_do_write; + bsc_msc_connect(data->msc_con); + + return 0; +} + +struct osmo_msc_data *osmo_msc_data_find(struct gsm_network *net, int nr) +{ + struct osmo_msc_data *msc_data; + + llist_for_each_entry(msc_data, &net->bsc_data->mscs, entry) + if (msc_data->nr == nr) + return msc_data; + return NULL; +} + +struct osmo_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr) +{ + struct osmo_msc_data *msc_data; + + /* check if there is already one */ + msc_data = osmo_msc_data_find(net, nr); + if (msc_data) + return msc_data; + + msc_data = talloc_zero(net, struct osmo_msc_data); + if (!msc_data) + return NULL; + + llist_add_tail(&msc_data->entry, &net->bsc_data->mscs); + + /* Init back pointer */ + msc_data->network = net; + + INIT_LLIST_HEAD(&msc_data->dests); + msc_data->ping_timeout = 20; + msc_data->pong_timeout = 5; + msc_data->core_ncc = -1; + msc_data->core_mcc = -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/src/osmo-bsc/osmo_bsc_sccp.c b/src/osmo-bsc/osmo_bsc_sccp.c new file mode 100644 index 000000000..3533d6df0 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_sccp.c @@ -0,0 +1,300 @@ +/* Interaction with the SCCP subsystem */ +/* + * (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 + +/* SCCP helper */ +#define SCCP_IT_TIMER 60 + +static LLIST_HEAD(active_connections); + +static void free_queued(struct osmo_bsc_sccp_con *conn) +{ + struct msgb *msg; + + while (!llist_empty(&conn->sccp_queue)) { + /* this is not allowed to fail */ + msg = msgb_dequeue(&conn->sccp_queue); + msgb_free(msg); + } + + conn->sccp_queue_size = 0; +} + +static void send_queued(struct osmo_bsc_sccp_con *conn) +{ + struct msgb *msg; + + while (!llist_empty(&conn->sccp_queue)) { + /* this is not allowed to fail */ + msg = msgb_dequeue(&conn->sccp_queue); + sccp_connection_write(conn->sccp, msg); + msgb_free(msg); + conn->sccp_queue_size -= 1; + } +} + +static void msc_outgoing_sccp_data(struct sccp_connection *conn, + struct msgb *msg, unsigned int len) +{ + struct osmo_bsc_sccp_con *bsc_con = + (struct osmo_bsc_sccp_con *) conn->data_ctx; + + bsc_handle_dt1(bsc_con, msg, len); +} + +static void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state) +{ + struct osmo_bsc_sccp_con *con_data; + + if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) { + con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx; + if(con_data->conn) { + LOGP(DMSC, LOGL_ERROR, + "ERROR: The lchan is still associated.\n"); + gsm0808_clear(con_data->conn); + subscr_con_free(con_data->conn); + con_data->conn = NULL; + } + + con_data->sccp = NULL; + free_queued(con_data); + sccp_connection_free(conn); + bsc_delete_connection(con_data); + } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) { + LOGP(DMSC, LOGL_DEBUG, "Connection established: %p\n", conn); + con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx; + + osmo_timer_del(&con_data->sccp_cc_timeout); + osmo_timer_schedule(&con_data->sccp_it_timeout, SCCP_IT_TIMER, 0); + + send_queued(con_data); + } +} + +static void bsc_sccp_force_free(struct osmo_bsc_sccp_con *data) +{ + if (data->conn) { + gsm0808_clear(data->conn); + subscr_con_free(data->conn); + data->conn = NULL; + } + + free_queued(data); + sccp_connection_force_free(data->sccp); + data->sccp = NULL; + bsc_delete_connection(data); +} + +static void sccp_it_timeout(void *_data) +{ + struct osmo_bsc_sccp_con *data = + (struct osmo_bsc_sccp_con *) _data; + + sccp_connection_send_it(data->sccp); + osmo_timer_schedule(&data->sccp_it_timeout, SCCP_IT_TIMER, 0); +} + +static void sccp_cc_timeout(void *_data) +{ + struct osmo_bsc_sccp_con *data = + (struct osmo_bsc_sccp_con *) _data; + + if (data->sccp->connection_state >= SCCP_CONNECTION_STATE_ESTABLISHED) + return; + + LOGP(DMSC, LOGL_ERROR, "The connection was never established.\n"); + bsc_sccp_force_free(data); +} + +static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg, + void *global_ctx, void *ctx) +{ + struct bsc_msc_connection *msc_con; + + if (conn) { + struct osmo_bsc_sccp_con *bsc_con = conn->data_ctx; + msc_con = bsc_con->msc->msc_con; + } else { + msc_con = ctx; + } + + msc_queue_write(msc_con, msg, IPAC_PROTO_SCCP); +} + +static int msc_sccp_accept(struct sccp_connection *connection, void *data) +{ + LOGP(DMSC, LOGL_DEBUG, "Rejecting incoming SCCP connection.\n"); + return -1; +} + +static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data) +{ + struct osmo_msc_data *msc = (struct osmo_msc_data *) msgb->cb[0]; + return bsc_handle_udt(msc, msgb, length); +} + +int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg) +{ + struct sccp_connection *sccp = conn->sccp; + + if (sccp->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { + LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp); + msgb_free(msg); + } else if (sccp->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED + && conn->sccp_queue_size == 0) { + sccp_connection_write(sccp, msg); + msgb_free(msg); + } else if (conn->sccp_queue_size > 10) { + LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp); + msgb_free(msg); + } else { + LOGP(DMSC, LOGL_DEBUG, "Queueing packet on %p. Queue size: %d\n", sccp, conn->sccp_queue_size); + conn->sccp_queue_size += 1; + msgb_enqueue(&conn->sccp_queue, msg); + } + + return 0; +} + +int bsc_create_new_connection(struct gsm_subscriber_connection *conn, + struct osmo_msc_data *msc) +{ + struct osmo_bsc_sccp_con *bsc_con; + struct sccp_connection *sccp; + + /* This should not trigger */ + if (!msc->msc_con->is_authenticated) { + LOGP(DMSC, LOGL_ERROR, + "How did this happen? MSC is not connected. Dropping.\n"); + return -1; + } + + if (!bsc_grace_allow_new_connection(conn->bts->network, conn->bts)) { + LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n"); + return -1; + } + + sccp = sccp_connection_socket(); + if (!sccp) { + LOGP(DMSC, LOGL_ERROR, "Failed to allocate memory.\n"); + return -ENOMEM; + } + + 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 -1; + } + + /* callbacks */ + sccp->state_cb = msc_outgoing_sccp_state; + sccp->data_cb = msc_outgoing_sccp_data; + sccp->data_ctx = bsc_con; + + /* prepare the timers */ + bsc_con->sccp_it_timeout.cb = sccp_it_timeout; + bsc_con->sccp_it_timeout.data = bsc_con; + bsc_con->sccp_cc_timeout.cb = sccp_cc_timeout; + bsc_con->sccp_cc_timeout.data = bsc_con; + + INIT_LLIST_HEAD(&bsc_con->sccp_queue); + + bsc_con->sccp = sccp; + bsc_con->msc = msc; + bsc_con->conn = conn; + llist_add_tail(&bsc_con->entry, &active_connections); + conn->sccp_con = bsc_con; + return 0; +} + +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_close_connections(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) + 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_close_connections(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/src/osmo-bsc/osmo_bsc_vty.c b/src/osmo-bsc/osmo_bsc_vty.c new file mode 100644 index 000000000..49ae665a6 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_vty.c @@ -0,0 +1,599 @@ +/* Osmo BSC VTY Configuration */ +/* (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 + + +#define IPA_STR "IP.ACCESS specific\n" + +extern struct gsm_network *bsc_gsmnet; + +static struct osmo_bsc_data *osmo_bsc_data(struct vty *vty) +{ + return bsc_gsmnet->bsc_data; +} + +static struct osmo_msc_data *osmo_msc_data(struct vty *vty) +{ + return osmo_msc_data_find(bsc_gsmnet, (int) vty->index); +} + +static struct cmd_node bsc_node = { + BSC_NODE, + "%s(bsc)#", + 1, +}; + +static struct cmd_node msc_node = { + MSC_NODE, + "%s(config-msc)# ", + 1, +}; + +DEFUN(cfg_net_msc, cfg_net_msc_cmd, + "msc [<0-1000>]", "Configure MSC details\n" "MSC connection to configure\n") +{ + int index = argc == 1 ? atoi(argv[0]) : 0; + struct osmo_msc_data *msc; + + msc = osmo_msc_data_alloc(bsc_gsmnet, index); + if (!msc) { + vty_out(vty, "%%Failed to allocate MSC data.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vty->index = (void *) index; + vty->node = MSC_NODE; + return CMD_SUCCESS; +} + +DEFUN(cfg_net_bsc, cfg_net_bsc_cmd, + "bsc", "Configure BSC\n") +{ + vty->node = BSC_NODE; + return CMD_SUCCESS; +} + +static void write_msc_amr_options(struct vty *vty, struct osmo_msc_data *msc) +{ +#define WRITE_AMR(vty, msc, name, var) \ + vty_out(vty, " amr-config %s %s%s", \ + name, msc->amr_conf.var ? "allowed" : "forbidden", \ + VTY_NEWLINE); + + WRITE_AMR(vty, msc, "12_2k", m12_2); + WRITE_AMR(vty, msc, "10_2k", m10_2); + WRITE_AMR(vty, msc, "7_95k", m7_95); + WRITE_AMR(vty, msc, "7_40k", m7_40); + WRITE_AMR(vty, msc, "6_70k", m6_70); + WRITE_AMR(vty, msc, "5_90k", m5_90); + WRITE_AMR(vty, msc, "5_15k", m5_15); + WRITE_AMR(vty, msc, "4_75k", m4_75); +#undef WRITE_AMR +} + +static void write_msc(struct vty *vty, struct osmo_msc_data *msc) +{ + struct bsc_msc_dest *dest; + + vty_out(vty, "msc %d%s", msc->nr, VTY_NEWLINE); + if (msc->bsc_token) + vty_out(vty, " token %s%s", msc->bsc_token, VTY_NEWLINE); + if (msc->core_ncc != -1) + vty_out(vty, " core-mobile-network-code %d%s", + msc->core_ncc, VTY_NEWLINE); + if (msc->core_mcc != -1) + vty_out(vty, " core-mobile-country-code %d%s", + msc->core_mcc, VTY_NEWLINE); + vty_out(vty, " ip.access rtp-base %d%s", msc->rtp_base, VTY_NEWLINE); + 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->ussd_welcome_txt) + vty_out(vty, " bsc-welcome-text %s%s", msc->ussd_welcome_txt, 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); + + /* write amr options */ + write_msc_amr_options(vty, msc); +} + +static int config_write_msc(struct vty *vty) +{ + struct osmo_msc_data *msc; + struct osmo_bsc_data *bsc = osmo_bsc_data(vty); + + llist_for_each_entry(msc, &bsc->mscs, entry) + write_msc(vty, msc); + + return CMD_SUCCESS; +} + +static int config_write_bsc(struct vty *vty) +{ + struct osmo_bsc_data *bsc = osmo_bsc_data(vty); + + vty_out(vty, "bsc%s", VTY_NEWLINE); + if (bsc->mid_call_txt) + vty_out(vty, " mid-call-text %s%s", bsc->mid_call_txt, VTY_NEWLINE); + vty_out(vty, " mid-call-timeout %d%s", bsc->mid_call_timeout, VTY_NEWLINE); + if (bsc->rf_ctrl_name) + vty_out(vty, " bsc-rf-socket %s%s", + bsc->rf_ctrl_name, VTY_NEWLINE); + + if (bsc->auto_off_timeout != -1) + vty_out(vty, " bsc-auto-rf-off %d%s", + bsc->auto_off_timeout, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_bsc_token, + cfg_net_bsc_token_cmd, + "token TOKEN", + "A token for the BSC to be sent to the MSC\n" "A token\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + + bsc_replace_string(osmo_bsc_data(vty), &data->bsc_token, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_bsc_ncc, + cfg_net_bsc_ncc_cmd, + "core-mobile-network-code <1-999>", + "Use this network code for the backbone\n" "NCC value\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->core_ncc = 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 backbone\n" "MCC value\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->core_mcc = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_bsc_rtp_base, + cfg_net_bsc_rtp_base_cmd, + "ip.access rtp-base <1-65000>", + IPA_STR + "Set the rtp-base port for the RTP stream\n" + "Port number\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->rtp_base = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_bsc_codec_list, + cfg_net_bsc_codec_list_cmd, + "codec-list .LIST", + "Set the allowed audio codecs\n" + "List of audio codecs\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + int saw_fr, saw_hr; + int i; + + saw_fr = saw_hr = 0; + + /* free the old list... if it exists */ + if (data->audio_support) { + talloc_free(data->audio_support); + data->audio_support = NULL; + data->audio_length = 0; + } + + /* create a new array */ + data->audio_support = + talloc_zero_array(osmo_bsc_data(vty), struct gsm_audio_support *, argc); + data->audio_length = argc; + + for (i = 0; i < argc; ++i) { + /* check for hrX or frX */ + if (strlen(argv[i]) != 3 + || argv[i][1] != 'r' + || (argv[i][0] != 'h' && argv[i][0] != 'f') + || argv[i][2] < 0x30 + || argv[i][2] > 0x39) + goto error; + + data->audio_support[i] = talloc_zero(data->audio_support, + struct gsm_audio_support); + data->audio_support[i]->ver = atoi(argv[i] + 2); + + if (strncmp("hr", argv[i], 2) == 0) { + data->audio_support[i]->hr = 1; + saw_hr = 1; + } else if (strncmp("fr", argv[i], 2) == 0) { + data->audio_support[i]->hr = 0; + saw_fr = 1; + } + + if (saw_hr && saw_fr) { + vty_out(vty, "Can not have full-rate and half-rate codec.%s", + VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; + } + } + + return CMD_SUCCESS; + +error: + vty_out(vty, "Codec name must be hrX or frX. Was '%s'%s", + argv[i], VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; +} + +DEFUN(cfg_net_msc_dest, + cfg_net_msc_dest_cmd, + "dest A.B.C.D <1-65000> <0-255>", + "Add a destination to a MUX/MSC\n" + "IP Address\n" "Port\n" "DSCP\n") +{ + struct bsc_msc_dest *dest; + struct osmo_msc_data *data = osmo_msc_data(vty); + + dest = talloc_zero(osmo_bsc_data(vty), struct bsc_msc_dest); + if (!dest) { + vty_out(vty, "%%Failed to create structure.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + dest->ip = talloc_strdup(dest, argv[0]); + if (!dest->ip) { + vty_out(vty, "%%Failed to copy dest ip.%s", VTY_NEWLINE); + talloc_free(dest); + return CMD_WARNING; + } + + dest->port = atoi(argv[1]); + dest->dscp = atoi(argv[2]); + llist_add_tail(&dest->list, &data->dests); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_no_dest, + cfg_net_msc_no_dest_cmd, + "no dest A.B.C.D <1-65000> <0-255>", + NO_STR "Remove a destination to a MUX/MSC\n" + "IP Address\n" "Port\n" "DSCP\n") +{ + struct bsc_msc_dest *dest, *tmp; + struct osmo_msc_data *data = osmo_msc_data(vty); + + int port = atoi(argv[1]); + int dscp = atoi(argv[2]); + + llist_for_each_entry_safe(dest, tmp, &data->dests, list) { + if (port != dest->port || dscp != dest->dscp + || strcmp(dest->ip, argv[0]) != 0) + continue; + + llist_del(&dest->list); + talloc_free(dest); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_ping_time, + cfg_net_msc_ping_time_cmd, + "timeout-ping NR", + "Set the PING interval, negative for not sending PING\n" + "Timeout in seconds\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->ping_timeout = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_pong_time, + cfg_net_msc_pong_time_cmd, + "timeout-pong NR", + "Set the time to wait for a PONG\n" "Timeout in seconds\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->pong_timeout = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_welcome_ussd, + cfg_net_msc_welcome_ussd_cmd, + "bsc-welcome-text .TEXT", + "Set the USSD notification to be sent.\n" "Text to be sent\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + char *str = argv_concat(argv, argc, 0); + if (!str) + return CMD_WARNING; + + bsc_replace_string(osmo_bsc_data(vty), &data->ussd_welcome_txt, str); + talloc_free(str); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_type, + cfg_net_msc_type_cmd, + "type (normal|local)", + "Select the MSC type\n" + "Plain GSM MSC\n" "Special MSC for local call routing\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + + if (strcmp(argv[0], "normal") == 0) + data->type = MSC_CON_TYPE_NORMAL; + else if (strcmp(argv[0], "local") == 0) + data->type = MSC_CON_TYPE_LOCAL; + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_emerg, + cfg_net_msc_emerg_cmd, + "allow-emergency (allow|deny)", + "Allow CM ServiceRequests with type emergency\n" + "Allow\n" "Deny\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->allow_emerg = strcmp("allow", argv[0]) == 0; + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_local_prefix, + cfg_net_msc_local_prefix_cmd, + "local-prefix REGEXP", + "Prefix for local numbers\n" "REGEXP used\n") +{ + struct osmo_msc_data *msc = osmo_msc_data(vty); + + if (gsm_parse_reg(msc, &msc->local_pref_reg, &msc->local_pref, argc, argv) != 0) { + vty_out(vty, "%%Failed to parse the regexp: '%s'%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +#define AMR_CONF_STR "AMR Multirate Configuration\n" +#define AMR_COMMAND(name) \ + DEFUN(cfg_net_msc_amr_##name, \ + cfg_net_msc_amr_##name##_cmd, \ + "amr-config " #name "k (allowed|forbidden)", \ + AMR_CONF_STR "Bitrate\n" "Allowed\n" "Forbidden\n") \ +{ \ + struct osmo_msc_data *msc = osmo_msc_data(vty); \ + \ + msc->amr_conf.m##name = strcmp(argv[0], "allowed") == 0; \ + return CMD_SUCCESS; \ +} + +AMR_COMMAND(12_2) +AMR_COMMAND(10_2) +AMR_COMMAND(7_95) +AMR_COMMAND(7_40) +AMR_COMMAND(6_70) +AMR_COMMAND(5_90) +AMR_COMMAND(5_15) +AMR_COMMAND(4_75) + +DEFUN(cfg_net_bsc_mid_call_text, + cfg_net_bsc_mid_call_text_cmd, + "mid-call-text .TEXT", + "Set the USSD notifcation to be send.\n" "Text to be sent\n") +{ + struct osmo_bsc_data *data = osmo_bsc_data(vty); + char *txt = argv_concat(argv, argc, 0); + if (!txt) + return CMD_WARNING; + + bsc_replace_string(data, &data->mid_call_txt, txt); + talloc_free(txt); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_bsc_mid_call_timeout, + cfg_net_bsc_mid_call_timeout_cmd, + "mid-call-timeout NR", + "Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n") +{ + struct osmo_bsc_data *data = osmo_bsc_data(vty); + data->mid_call_timeout = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_rf_socket, + cfg_net_rf_socket_cmd, + "bsc-rf-socket PATH", + "Set the filename for the RF control interface.\n" "RF Control path\n") +{ + struct osmo_bsc_data *data = osmo_bsc_data(vty); + + bsc_replace_string(data, &data->rf_ctrl_name, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_rf_off_time, + cfg_net_rf_off_time_cmd, + "bsc-auto-rf-off <1-65000>", + "Disable RF on MSC Connection\n" "Timeout\n") +{ + struct osmo_bsc_data *data = osmo_bsc_data(vty); + data->auto_off_timeout = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_no_rf_off_time, + cfg_net_no_rf_off_time_cmd, + "no bsc-auto-rf-off", + NO_STR "Disable RF on MSC Connection\n") +{ + struct osmo_bsc_data *data = osmo_bsc_data(vty); + data->auto_off_timeout = -1; + return CMD_SUCCESS; +} + +DEFUN(show_statistics, + show_statistics_cmd, + "show statistics", + SHOW_STR "Statistics about the BSC\n") +{ + openbsc_vty_print_statistics(vty, bsc_gsmnet); + return CMD_SUCCESS; +} + +DEFUN(show_mscs, + show_mscs_cmd, + "show mscs", + SHOW_STR "MSC Connections and State\n") +{ + struct osmo_msc_data *msc; + llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) { + vty_out(vty, "MSC Nr: %d is connected: %d auth: %d.%s", + msc->nr, + msc->msc_con ? msc->msc_con->is_connected : -1, + msc->msc_con ? msc->msc_con->is_authenticated : -1, + VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN(show_pos, + show_pos_cmd, + "show position", + SHOW_STR "Position information of the BTS\n") +{ + struct gsm_bts *bts; + struct bts_location *curloc; + struct tm time; + char timestr[50]; + + llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { + if (llist_empty(&bts->loc_list)) { + vty_out(vty, "BTS Nr: %d position invalid%s", bts->nr, + VTY_NEWLINE); + continue; + } + curloc = llist_entry(bts->loc_list.next, struct bts_location, list); + if (gmtime_r(&curloc->tstamp, &time) == NULL) { + vty_out(vty, "Time conversion failed for BTS %d%s", bts->nr, + VTY_NEWLINE); + continue; + } + if (asctime_r(&time, timestr) == NULL) { + vty_out(vty, "Time conversion failed for BTS %d%s", bts->nr, + VTY_NEWLINE); + continue; + } + /* Last character in asctime is \n */ + timestr[strlen(timestr)-1] = 0; + + vty_out(vty, "BTS Nr: %d position: %s time: %s%s", bts->nr, + get_value_string(bts_loc_fix_names, curloc->valid), timestr, + VTY_NEWLINE); + vty_out(vty, " lat: %f lon: %f height: %f%s", curloc->lat, curloc->lon, + curloc->height, VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +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); + 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_node(&msc_node, config_write_msc); + install_default(MSC_NODE); + install_element(MSC_NODE, &cfg_net_bsc_token_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_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_ping_time_cmd); + install_element(MSC_NODE, &cfg_net_msc_pong_time_cmd); + install_element(MSC_NODE, &cfg_net_msc_welcome_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_ve(&show_statistics_cmd); + install_element_ve(&show_mscs_cmd); + install_element_ve(&show_pos_cmd); + + return 0; +} diff --git a/src/osmo-bsc_mgcp/Makefile.am b/src/osmo-bsc_mgcp/Makefile.am new file mode 100644 index 000000000..0456cf108 --- /dev/null +++ b/src/osmo-bsc_mgcp/Makefile.am @@ -0,0 +1,10 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) + +bin_PROGRAMS = osmo-bsc_mgcp + +osmo_bsc_mgcp_SOURCES = mgcp_main.c +osmo_bsc_mgcp_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ + $(top_builddir)/src/libmgcp/libmgcp.a -lrt \ + $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) diff --git a/src/osmo-bsc_mgcp/mgcp_main.c b/src/osmo-bsc_mgcp/mgcp_main.c new file mode 100644 index 000000000..540f7602b --- /dev/null +++ b/src/osmo-bsc_mgcp/mgcp_main.c @@ -0,0 +1,304 @@ +/* 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 "../../bscconfig.h" + +/* this is here for the vty... it will never be called */ +void subscr_put() { abort(); } + +#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 mgcp_change_cb(struct mgcp_trunk_config *cfg, int endpoint, int state) +{ + if (state != MGCP_ENDP_MDCX) + return 0; + + mgcp_send_dummy(&cfg->endpoints[endpoint]); + 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", + 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_free_endp(&reset_trunk->endpoints[i]); + } + + return 0; +} + +extern enum node_type bsc_vty_go_parent(struct vty *vty); + +static struct vty_app_info vty_info = { + .name = "OpenBSC MGCP", + .version = PACKAGE_VERSION, + .go_parent_cb = bsc_vty_go_parent, + .is_config_node = bsc_vty_is_config_node, +}; + +int main(int argc, char **argv) +{ + struct gsm_network dummy_network; + struct sockaddr_in addr; + int on = 1, rc; + + tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent"); + + osmo_init_ignore_signals(); + osmo_init_logging(&log_info); + + cfg = mgcp_config_alloc(); + if (!cfg) + return -1; + + vty_info.copyright = openbsc_copyright; + vty_init(&vty_info); + logging_vty_add_cmds(&log_info); + mgcp_vty_init(); + + handle_options(argc, argv); + + rc = mgcp_parse_config(config_file, cfg); + if (rc < 0) + return rc; + + rc = telnet_init(tall_bsc_ctx, &dummy_network, 4243); + if (rc < 0) + return rc; + + /* set some callbacks */ + cfg->reset_cb = mgcp_rsip_cb; + cfg->change_cb = mgcp_change_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/src/osmo-bsc_nat/Makefile.am b/src/osmo-bsc_nat/Makefile.am new file mode 100644 index 000000000..e2ba551a0 --- /dev/null +++ b/src/osmo-bsc_nat/Makefile.am @@ -0,0 +1,17 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +bin_PROGRAMS = osmo-bsc_nat + + +osmo_bsc_nat_SOURCES = bsc_filter.c bsc_mgcp_utils.c bsc_nat.c bsc_nat_utils.c \ + bsc_nat_vty.c bsc_sccp.c bsc_ussd.c bsc_nat_ctrl.c \ + bsc_nat_rewrite.c bsc_nat_filter.c +osmo_bsc_nat_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ + $(top_builddir)/src/libmgcp/libmgcp.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libctrl/libctrl.a \ + -lrt $(LIBOSMOSCCP_LIBS) $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOABIS_LIBS) diff --git a/src/osmo-bsc_nat/bsc_filter.c b/src/osmo-bsc_nat/bsc_filter.c new file mode 100644 index 000000000..bbbba6994 --- /dev/null +++ b/src/osmo-bsc_nat/bsc_filter.c @@ -0,0 +1,216 @@ +/* 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; + 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/src/osmo-bsc_nat/bsc_mgcp_utils.c b/src/osmo-bsc_nat/bsc_mgcp_utils.c new file mode 100644 index 000000000..8bb6075d1 --- /dev/null +++ b/src/osmo-bsc_nat/bsc_mgcp_utils.c @@ -0,0 +1,1049 @@ +/** + * 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 + +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(*(uint16_t *)TLVP_VAL(&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_free_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, + bsc->nat->mgcp_cfg->source_addr, + 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 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; + } + } + + /* we need to generate a new and patched message */ + bsc_msg = bsc_mgcp_rewrite((char *) nat->mgcp_msg, nat->mgcp_length, sccp->bsc_endp, + nat->mgcp_cfg->source_addr, mgcp_endp->bts_end.local_port); + 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; + 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); + 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_free_endp(endp); +} + +/* + * 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; + } + + /* 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, + bsc->nat->mgcp_cfg->source_addr, endp->net_end.local_port); + + 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 slen; + int ret; + char buf[40]; + + 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; + } + + slen = sprintf((char *) output->l3h, "%s %s %x@mgw MGCP 1.0%s", + op, buf, endp, 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) +{ + 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); + } else if (strncmp(dlcx_str, token, (sizeof dlcx_str) - 1) == 0) { + patch_mgcp(output, "DLCX", token, endpoint, len, cr); + } else if (strncmp(mdcx_str, token, (sizeof mdcx_str) - 1) == 0) { + patch_mgcp(output, "MDCX", token, endpoint, len, cr); + } 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) { + if (sscanf(token, "m=audio %*d RTP/AVP %d", &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 %d%s", + port, payload, cr ? "\r\n" : "\n"); + 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 (!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)); + } + + 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_free_endp(&bsc->nat->mgcp_cfg->trunk.endpoints[i]); + } +} diff --git a/src/osmo-bsc_nat/bsc_nat.c b/src/osmo-bsc_nat/bsc_nat.c new file mode 100644 index 000000000..12373397d --- /dev/null +++ b/src/osmo-bsc_nat/bsc_nat.c @@ -0,0 +1,1609 @@ +/* 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 + +#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 "../../bscconfig.h" + +#define SCCP_CLOSE_TIME 20 +#define SCCP_CLOSE_TIME_TIMEOUT 19 + +static const char *config_file = "bsc-nat.cfg"; +static struct in_addr local_addr; +static struct osmo_fd bsc_listen; +static const char *msc_ip = NULL; +static struct osmo_timer_list sccp_close; +static int daemonize = 0; + +const char *openbsc_copyright = + "Copyright (C) 2010 Holger Hans Peter Freyther and On-Waves\r\n" + "License AGPLv3+: GNU AGPL version 3 or later \r\n" + "This is free software: you are free to change and redistribute it.\r\n" + "There is NO WARRANTY, to the extent permitted by law.\r\n"; + +static struct bsc_nat *nat; +static void bsc_send_data(struct bsc_connection *bsc, const uint8_t *data, unsigned int length, int); +static void msc_send_reset(struct bsc_msc_connection *con); +static void bsc_stat_reject(int filter, struct bsc_connection *bsc, int normal); + +struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num) +{ + struct bsc_config *conf; + + llist_for_each_entry(conf, &nat->bsc_configs, entry) + if (conf->nr == num) + return conf; + + return NULL; +} + +static void queue_for_msc(struct bsc_msc_connection *con, struct msgb *msg) +{ + if (!con) { + LOGP(DLINP, LOGL_ERROR, "No MSC Connection assigned. Check your code.\n"); + msgb_free(msg); + return; + } + + + if (osmo_wqueue_enqueue(&con->write_queue, msg) != 0) { + LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the write.\n"); + msgb_free(msg); + } +} + +static void send_reset_ack(struct bsc_connection *bsc) +{ + static const uint8_t gsm_reset_ack[] = { + 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, + 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03, + 0x00, 0x01, 0x31, + }; + + bsc_send_data(bsc, gsm_reset_ack, sizeof(gsm_reset_ack), IPAC_PROTO_SCCP); +} + +static void send_ping(struct bsc_connection *bsc) +{ + static const uint8_t id_ping[] = { + IPAC_MSGT_PING, + }; + + bsc_send_data(bsc, id_ping, sizeof(id_ping), IPAC_PROTO_IPACCESS); +} + +static void send_pong(struct bsc_connection *bsc) +{ + static const uint8_t id_pong[] = { + IPAC_MSGT_PONG, + }; + + bsc_send_data(bsc, id_pong, sizeof(id_pong), IPAC_PROTO_IPACCESS); +} + +static void bsc_pong_timeout(void *_bsc) +{ + struct bsc_connection *bsc = _bsc; + + LOGP(DNAT, LOGL_ERROR, "BSC Nr: %d PONG timeout.\n", bsc->cfg->nr); + bsc_close_connection(bsc); +} + +static void bsc_ping_timeout(void *_bsc) +{ + struct bsc_connection *bsc = _bsc; + + if (bsc->nat->ping_timeout < 0) + return; + + send_ping(bsc); + + /* send another ping in 20 seconds */ + osmo_timer_schedule(&bsc->ping_timeout, bsc->nat->ping_timeout, 0); + + /* also start a pong timer */ + osmo_timer_schedule(&bsc->pong_timeout, bsc->nat->pong_timeout, 0); +} + +static void start_ping_pong(struct bsc_connection *bsc) +{ + bsc->pong_timeout.data = bsc; + bsc->pong_timeout.cb = bsc_pong_timeout; + bsc->ping_timeout.data = bsc; + bsc->ping_timeout.cb = bsc_ping_timeout; + + bsc_ping_timeout(bsc); +} + +static void send_id_ack(struct bsc_connection *bsc) +{ + static const uint8_t id_ack[] = { + IPAC_MSGT_ID_ACK + }; + + bsc_send_data(bsc, id_ack, sizeof(id_ack), IPAC_PROTO_IPACCESS); +} + +static void send_id_req(struct bsc_connection *bsc) +{ + static const uint8_t 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, + }; + + bsc_send_data(bsc, id_req, sizeof(id_req), IPAC_PROTO_IPACCESS); +} + +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; + + ipaccess_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 allocate clear command.\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; + + ipaccess_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) +{ + ipaccess_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(nat->token); + if (!msg) + return; + + ipaccess_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_nat_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 { + ipaccess_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->con_type == NAT_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->con_type = NAT_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_nat_reject_cause *cause) +{ + struct msgb *payload; + struct msgb *refuse; + + if (con_type == NAT_CON_TYPE_LU) + payload = gsm48_create_loc_upd_rej(cause->lu_reject_cause); + else if (con_type == NAT_CON_TYPE_CM_SERV_REQ || con_type == NAT_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->con_type = NAT_CON_TYPE_LOCAL_REJECT; + con->con_local = NAT_CON_END_LOCAL; + con->has_remote_ref = 1; + con->remote_ref = con->patched_ref; + + /* 1. create a confirmation */ + cc = sccp_create_cc(&con->remote_ref, &con->real_ref); + if (!cc) + goto send_refuse; + + /* 2. create the DT1 */ + gsm0808_prepend_dtap_header(payload, 0); + udt = sccp_create_dt1(&con->real_ref, payload->data, payload->len); + if (!udt) { + msgb_free(cc); + goto send_refuse; + } + + /* 3. send a Clear Command */ + clear = nat_creat_clrc(con, 0x20); + if (!clear) { + msgb_free(cc); + msgb_free(udt); + goto send_refuse; + } + + /* 4. send a RLSD */ + rlsd = sccp_create_rlsd(&con->remote_ref, &con->real_ref, + SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); + if (!rlsd) { + msgb_free(cc); + msgb_free(udt); + msgb_free(clear); + goto send_refuse; + } + + bsc_write(bsc, cc, IPAC_PROTO_SCCP); + bsc_write(bsc, udt, IPAC_PROTO_SCCP); + bsc_write(bsc, clear, IPAC_PROTO_SCCP); + bsc_write(bsc, rlsd, IPAC_PROTO_SCCP); + msgb_free(payload); + return; + } + + +send_refuse: + if (payload) + msgb_free(payload); + + refuse = sccp_create_refuse(parsed->src_local_ref, + SCCP_REFUSAL_SCCP_FAILURE, NULL, 0); + if (!refuse) { + LOGP(DNAT, LOGL_ERROR, + "Creating refuse msg failed for SCCP 0x%x on BSC Nr: %d.\n", + sccp_src_ref_to_int(parsed->src_local_ref), bsc->cfg->nr); + return; + } + + bsc_write(bsc, refuse, IPAC_PROTO_SCCP); +} + +static void bsc_nat_send_paging(struct bsc_connection *bsc, struct msgb *msg) +{ + if (bsc->cfg->forbid_paging) { + LOGP(DNAT, LOGL_DEBUG, "Paging forbidden for BTS: %d\n", bsc->cfg->nr); + return; + } + + bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), IPAC_PROTO_SCCP); +} + +static void bsc_nat_handle_paging(struct bsc_nat *nat, struct msgb *msg) +{ + struct bsc_connection *bsc; + const uint8_t *paging_start; + int paging_length, i, ret; + + ret = bsc_nat_find_paging(msg, &paging_start, &paging_length); + if (ret != 0) { + LOGP(DNAT, LOGL_ERROR, "Could not parse paging message: %d\n", ret); + return; + } + + /* This is quite expensive now */ + for (i = 0; i < paging_length; i += 2) { + unsigned int _lac = ntohs(*(unsigned int *) &paging_start[i]); + unsigned int paged = 0; + llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) { + if (!bsc->cfg) + continue; + if (!bsc->authenticated) + continue; + if (!bsc_config_handles_lac(bsc->cfg, _lac)) + continue; + bsc_nat_send_paging(bsc, msg); + paged += 1; + } + + /* highlight a possible config issue */ + if (paged == 0) + LOGP(DNAT, LOGL_ERROR, "No BSC for LAC %d/0x%d\n", _lac, _lac); + + } +} + + +/* + * Update the auth status. This can be either a CIPHER MODE COMAMND or + * a CM Serivce Accept. Maybe also LU Accept or such in the future. + */ +static void update_con_authorize(struct nat_sccp_connection *con, + struct bsc_nat_parsed *parsed, + struct msgb *msg) +{ + if (!con) + return; + if (con->authorized) + return; + + if (parsed->bssap == BSSAP_MSG_BSS_MANAGEMENT && + parsed->gsm_type == BSS_MAP_MSG_CIPHER_MODE_CMD) { + con->authorized = 1; + } else if (parsed->bssap == BSSAP_MSG_DTAP) { + uint8_t msg_type, proto; + uint32_t len; + struct gsm48_hdr *hdr48; + hdr48 = bsc_unpack_dtap(parsed, msg, &len); + if (!hdr48) + return; + + proto = hdr48->proto_discr & 0x0f; + msg_type = hdr48->msg_type & 0xbf; + if (proto == GSM48_PDISC_MM && + msg_type == GSM48_MT_MM_CM_SERV_ACC) + con->authorized = 1; + } +} + +static int forward_sccp_to_bts(struct bsc_msc_connection *msc_con, struct msgb *msg) +{ + struct nat_sccp_connection *con = NULL; + struct bsc_connection *bsc; + struct bsc_nat_parsed *parsed; + int proto; + + /* filter, drop, patch the message? */ + parsed = bsc_nat_parse(msg); + if (!parsed) { + LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n"); + return -1; + } + + if (bsc_nat_filter_ipa(DIR_BSC, msg, parsed)) + goto exit; + + proto = parsed->ipa_proto; + + /* Route and modify the SCCP packet */ + if (proto == IPAC_PROTO_SCCP) { + switch (parsed->sccp_type) { + case SCCP_MSG_TYPE_UDT: + /* forward UDT messages to every BSC */ + goto send_to_all; + break; + case SCCP_MSG_TYPE_RLSD: + case SCCP_MSG_TYPE_CREF: + case SCCP_MSG_TYPE_DT1: + case SCCP_MSG_TYPE_IT: + con = patch_sccp_src_ref_to_bsc(msg, parsed, nat); + if (parsed->gsm_type == BSS_MAP_MSG_ASSIGMENT_RQST) { + osmo_counter_inc(nat->stats.sccp.calls); + + if (con) { + struct rate_ctr_group *ctrg; + ctrg = con->bsc->cfg->stats.ctrg; + rate_ctr_inc(&ctrg->ctr[BCFG_CTR_SCCP_CALLS]); + if (bsc_mgcp_assign_patch(con, msg) != 0) + LOGP(DNAT, LOGL_ERROR, "Failed to assign...\n"); + } else + LOGP(DNAT, LOGL_ERROR, "Assignment command but no BSC.\n"); + } else if (con && con->con_local == NAT_CON_END_USSD && + parsed->gsm_type == BSS_MAP_MSG_CLEAR_CMD) { + LOGP(DNAT, LOGL_NOTICE, "Clear Command for USSD Connection. Ignoring.\n"); + con = NULL; + } + break; + case SCCP_MSG_TYPE_CC: + con = patch_sccp_src_ref_to_bsc(msg, parsed, nat); + if (!con || update_sccp_src_ref(con, parsed) != 0) + goto exit; + break; + case SCCP_MSG_TYPE_RLC: + LOGP(DNAT, LOGL_ERROR, "Unexpected release complete from MSC.\n"); + goto exit; + break; + case SCCP_MSG_TYPE_CR: + /* MSC never opens a SCCP connection, fall through */ + default: + goto exit; + } + + if (!con && parsed->sccp_type == SCCP_MSG_TYPE_RLSD) { + LOGP(DNAT, LOGL_NOTICE, "Sending fake RLC on RLSD message to network.\n"); + /* Exchange src/dest for the reply */ + nat_send_rlc(msc_con, parsed->dest_local_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; + struct ipaccess_head *hh; + int ret; + + msc_con = (struct bsc_msc_connection *) bfd->data; + + ret = ipa_msg_recv(bfd->fd, &msg); + if (ret <= 0) { + if (ret == 0) + LOGP(DNAT, LOGL_FATAL, "The connection the MSC was lost, exiting\n"); + else + LOGP(DNAT, LOGL_ERROR, "Failed to parse ip access message: %d\n", ret); + + bsc_msc_lost(msc_con); + return -1; + } + + LOGP(DNAT, LOGL_DEBUG, "MSG 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; + ipaccess_rcvmsg_base(msg, bfd); + + /* initialize the networking. This includes sending a GSM08.08 message */ + if (hh->proto == IPAC_PROTO_IPACCESS) { + 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); + + 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 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); + const int len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME); + + 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; + } + + llist_for_each_entry(conf, &bsc->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) { + 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); + return; + } + } + + LOGP(DNAT, LOGL_ERROR, "No bsc found for token '%s' on fd: %d.\n", token, + bsc->write_queue.bfd.fd); +} + +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_nat_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) { + bsc_stat_reject(filter, bsc, 0); + goto exit3; + } + + if (!create_sccp_src_ref(bsc, parsed)) + goto exit2; + con = patch_sccp_src_ref_to_msc(msg, parsed, bsc); + con->msc_con = bsc->nat->msc_con; + con_msc = con->msc_con; + con->con_type = con_type; + con->imsi_checked = filter; + bsc_nat_extract_lac(bsc, con, parsed, msg); + if (imsi) + con->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) { + 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_check_ussd(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->imsi); + talloc_free(parsed); + parsed = NULL; + } else if (con->con_local == NAT_CON_END_USSD) { + bsc_check_ussd(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) { + struct tlv_parsed tvp; + int ret; + ret = ipaccess_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"); + 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; + struct ipaccess_head *hh; + struct ipaccess_head_ext *hh_ext; + int ret; + + ret = ipa_msg_recv(bfd->fd, &msg); + if (ret <= 0) { + if (ret == 0) + LOGP(DNAT, LOGL_ERROR, + "The connection to the BSC Nr: %d was lost. Cleaning it\n", + bsc->cfg ? bsc->cfg->nr : -1); + else + LOGP(DNAT, LOGL_ERROR, + "Stream error on BSC Nr: %d. Failed to parse ip access message: %d\n", + bsc->cfg ? bsc->cfg->nr : -1, ret); + + bsc_close_connection(bsc); + return -1; + } + + + LOGP(DNAT, LOGL_DEBUG, "MSG from BSC: %s proto: %d\n", osmo_hexdump(msg->data, msg->len), msg->l2h[0]); + + /* Handle messages from the BSC */ + hh = (struct ipaccess_head *) msg->data; + + /* stop the pong timeout */ + if (hh->proto == IPAC_PROTO_IPACCESS) { + if (msg->l2h[0] == IPAC_MSGT_PONG) { + osmo_timer_del(&bsc->pong_timeout); + msgb_free(msg); + return 0; + } else if (msg->l2h[0] == IPAC_MSGT_PING) { + send_pong(bsc); + msgb_free(msg); + return 0; + } + /* Message contains the ipaccess_head_ext header, investigate further */ + } else if (hh->proto == IPAC_PROTO_OSMO && + msg->len > sizeof(*hh) + sizeof(*hh_ext)) { + + hh_ext = (struct ipaccess_head_ext *) hh->data; + /* l2h is where the actual command data is expected */ + msg->l2h = hh_ext->data; + + if (hh_ext->proto == IPAC_PROTO_EXT_CTRL) + return bsc_nat_handle_ctrlif_msg(bsc, msg); + } + + /* FIXME: Currently no PONG is sent to the BSC */ + /* FIXME: Currently no ID ACK is sent to the BSC */ + forward_sccp_to_msc(bsc, msg); + + return 0; +} + +static int ipaccess_listen_bsc_cb(struct osmo_fd *bfd, unsigned int what) +{ + struct bsc_connection *bsc; + int fd, rc, on; + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + + if (!(what & BSC_FD_READ)) + return 0; + + fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (fd < 0) { + perror("accept"); + return fd; + } + + /* count the reconnect */ + osmo_counter_inc(nat->stats.bsc.reconn); + + /* + * if we are not connected to a msc... just close the socket + */ + if (!bsc_nat_msc_is_connected(nat)) { + LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due lack of MSC connection.\n"); + close(fd); + return 0; + } + + if (nat->blocked) { + LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due NAT being blocked.\n"); + close(fd); + return 0; + } + + on = 1; + rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); + if (rc != 0) + LOGP(DNAT, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); + + rc = setsockopt(fd, IPPROTO_IP, IP_TOS, + &nat->bsc_ip_dscp, sizeof(nat->bsc_ip_dscp)); + if (rc != 0) + LOGP(DNAT, LOGL_ERROR, "Failed to set IP_TOS: %s\n", strerror(errno)); + + /* todo... do something with the connection */ + /* todo... use GNUtls to see if we want to trust this as a BTS */ + + /* + * + */ + bsc = bsc_connection_alloc(nat); + if (!bsc) { + LOGP(DNAT, LOGL_ERROR, "Failed to allocate BSC struct.\n"); + close(fd); + return -1; + } + + bsc->write_queue.bfd.data = bsc; + bsc->write_queue.bfd.fd = fd; + bsc->write_queue.read_cb = ipaccess_bsc_read_cb; + bsc->write_queue.write_cb = bsc_write_cb; + bsc->write_queue.bfd.when = BSC_FD_READ; + if (osmo_fd_register(&bsc->write_queue.bfd) < 0) { + LOGP(DNAT, LOGL_ERROR, "Failed to register BSC fd.\n"); + close(fd); + talloc_free(bsc); + return -2; + } + + LOGP(DNAT, LOGL_NOTICE, "BSC connection on %d with IP: %s\n", + fd, inet_ntoa(sa.sin_addr)); + + llist_add(&bsc->list_entry, &nat->bsc_connections); + bsc->last_id = 0; + + send_id_ack(bsc); + send_id_req(bsc); + send_mgcp_reset(bsc); + + /* + * start the hangup timer + */ + bsc->id_timeout.data = bsc; + bsc->id_timeout.cb = ipaccess_close_bsc; + osmo_timer_schedule(&bsc->id_timeout, nat->auth_timeout, 0); + return 0; +} + +static void print_usage() +{ + printf("Usage: bsc_nat\n"); +} + +static void print_help() +{ + printf(" Some useful help...\n"); + printf(" -h --help this text\n"); + printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n"); + printf(" -D --daemonize Fork the process into a background daemon\n"); + printf(" -s --disable-color\n"); + printf(" -c --config-file filename The config file to use.\n"); + printf(" -m --msc=IP. The address of the MSC.\n"); + printf(" -l --local=IP. The local address of this BSC.\n"); +} + +static void handle_options(int argc, char **argv) +{ + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"debug", 1, 0, 'd'}, + {"daemonize", 0, 0, 'D'}, + {"config-file", 1, 0, 'c'}, + {"disable-color", 0, 0, 's'}, + {"timestamp", 0, 0, 'T'}, + {"msc", 1, 0, 'm'}, + {"local", 1, 0, 'l'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "hd:sTPc:m:l:D", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + print_help(); + exit(0); + case 's': + log_set_use_color(osmo_stderr_target, 0); + break; + case 'd': + log_parse_category_mask(osmo_stderr_target, optarg); + break; + case 'D': + daemonize = 1; + break; + case 'c': + config_file = strdup(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.\n", + sccp_src_ref_to_int(&conn->real_ref), + sccp_src_ref_to_int(&conn->patched_ref)); + sccp_connection_destroy(conn); + destroyed = 1; + } + + if (!destroyed) + goto out; + + /* now close out any BSC */ + llist_for_each_entry_safe(bsc, bsc_tmp, &nat->bsc_connections, list_entry) + bsc_maybe_close(bsc); + +out: + osmo_timer_schedule(&sccp_close, SCCP_CLOSE_TIME, 0); +} + +extern void *tall_msgb_ctx; +extern void *tall_ctr_ctx; +static void talloc_init_ctx() +{ + tall_bsc_ctx = talloc_named_const(NULL, 0, "nat"); + tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); + tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); +} + +extern enum node_type 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; + } + + vty_info.copyright = openbsc_copyright; + vty_init(&vty_info); + logging_vty_add_cmds(&log_info); + bsc_nat_vty_init(nat); + + + /* parse options */ + local_addr.s_addr = INADDR_ANY; + handle_options(argc, argv); + + rate_ctr_init(tall_bsc_ctx); + + /* init vty and parse */ + telnet_init(tall_bsc_ctx, NULL, 4244); + if (mgcp_parse_config(config_file, nat->mgcp_cfg) < 0) { + fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); + return -3; + } + + /* over rule the VTY config */ + if (msc_ip) + bsc_nat_set_msc_ip(nat, msc_ip); + + /* seed the PRNG */ + srand(time(NULL)); + + /* + * Setup the MGCP code.. + */ + if (bsc_mgcp_nat_init(nat) != 0) + return -4; + + /* connect to the MSC */ + nat->msc_con = bsc_msc_create(nat, &nat->dests); + if (!nat->msc_con) { + fprintf(stderr, "Creating a bsc_msc_connection failed.\n"); + exit(1); + } + + nat->ctrl = bsc_nat_controlif_setup(nat, 4250); + if (!nat->ctrl) { + fprintf(stderr, "Creating the control interface failed.\n"); + exit(1); + } + + nat->msc_con->connection_loss = msc_connection_was_lost; + nat->msc_con->connected = msc_connection_connected; + nat->msc_con->write_queue.read_cb = ipaccess_msc_read_cb; + nat->msc_con->write_queue.write_cb = ipaccess_msc_write_cb;; + nat->msc_con->write_queue.bfd.data = nat->msc_con; + bsc_msc_connect(nat->msc_con); + + /* wait for the BSC */ + rc = make_sock(&bsc_listen, IPPROTO_TCP, ntohl(local_addr.s_addr), + 5000, 0, ipaccess_listen_bsc_cb, nat); + if (rc != 0) { + fprintf(stderr, "Failed to listen for BSC.\n"); + exit(1); + } + + rc = bsc_ussd_init(nat); + if (rc != 0) { + LOGP(DNAT, LOGL_ERROR, "Failed to bind the USSD socket.\n"); + exit(1); + } + + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + osmo_init_ignore_signals(); + + if (daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + perror("Error during daemonize"); + exit(1); + } + } + + /* recycle timer */ + sccp_set_log_area(DSCCP); + sccp_close.cb = sccp_close_unconfirmed; + sccp_close.data = NULL; + osmo_timer_schedule(&sccp_close, SCCP_CLOSE_TIME, 0); + + while (1) { + osmo_select_main(0); + } + + return 0; +} + +/* Close all connections handed out to the USSD module */ +int bsc_close_ussd_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/src/osmo-bsc_nat/bsc_nat_ctrl.c b/src/osmo-bsc_nat/bsc_nat_ctrl.c new file mode 100644 index 000000000..6ff4541a0 --- /dev/null +++ b/src/osmo-bsc_nat/bsc_nat_ctrl.c @@ -0,0 +1,324 @@ +/* + * (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 + + +#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 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 *nr_str, *tmp, *saveptr = NULL; + + /* Skip over the beginning (bsc.) */ + tmp = strtok_r(cmd->variable, ".", &saveptr); + tmp = strtok_r(NULL, ".", &saveptr); + tmp = strtok_r(NULL, ".", &saveptr); + nr_str = strtok_r(NULL, ".", &saveptr); + if (!nr_str) { + cmd->reply = "command incomplete"; + goto err; + } + nr = atoi(nr_str); + + tmp = strtok_r(NULL, "\0", &saveptr); + if (!tmp) { + 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, tmp); + if (!bsc_cmd->variable) { + cmd->reply = "OOM"; + goto err; + } + + if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) { + cmd->reply = "Sending failed"; + goto err; + } + pending->ccon = cmd->ccon; + pending->ccon->closed_cb = ctrl_conn_closed_cb; + pending->cmd = cmd; + + /* Setup the timeout */ + pending->timeout.data = pending; + pending->timeout.cb = pending_timeout_cb; + /* TODO: Make timeout configurable */ + osmo_timer_schedule(&pending->timeout, 10, 0); + llist_add_tail(&pending->list_entry, &bsc->cmd_pending); + + goto done; + } + } + /* We end up here if there's no bsc to handle our LAC */ + cmd->reply = "no BSC with this nr"; +err: + ret = CTRL_CMD_ERROR; +done: + if (bsc_cmd) + 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; +} + +struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, int port) +{ + struct ctrl_handle *ctrl; + int rc; + + + ctrl = controlif_setup(NULL, 4250); + 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"); + osmo_fd_unregister(&ctrl->listen_fd); + close(ctrl->listen_fd.fd); + talloc_free(ctrl); + return NULL; + } + + g_nat = nat; + return ctrl; +} + diff --git a/src/osmo-bsc_nat/bsc_nat_filter.c b/src/osmo-bsc_nat/bsc_nat_filter.c new file mode 100644 index 000000000..8ccc26259 --- /dev/null +++ b/src/osmo-bsc_nat/bsc_nat_filter.c @@ -0,0 +1,435 @@ +/* + * Access filtering + */ +/* + * (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 + +int bsc_nat_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu) +{ + struct bsc_nat_barr_entry *n; + n = rb_entry(root->rb_node, struct bsc_nat_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_nat_barr_entry, node); + }; + + return 0; +} + +static int insert_barr_node(struct bsc_nat_barr_entry *entry, struct rb_root *root) +{ + struct rb_node **new = &root->rb_node, *parent = NULL; + + while (*new) { + int rc; + struct bsc_nat_barr_entry *this; + this = rb_entry(*new, struct bsc_nat_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(DNAT, 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_nat_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_nat_barr_entry *entry; + entry = talloc_zero(ctx, struct bsc_nat_barr_entry); + if (!entry) { + LOGP(DNAT, 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_nat_acc_lst *lst, const char *mi_string) +{ + struct bsc_nat_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) + return 0; + } + + return 1; +} + +/* apply white/black list */ +static int auth_imsi(struct bsc_connection *bsc, const char *imsi, + struct bsc_nat_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_nat_acc_lst *nat_lst = NULL; + struct bsc_nat_acc_lst *bsc_lst = NULL; + + /* 1. global check for barred imsis */ + if (bsc_nat_barr_find(&bsc->nat->imsi_black_list, imsi, &cm, &lu)) { + cause->cm_reject_cause = cm; + cause->lu_reject_cause = lu; + LOGP(DNAT, LOGL_DEBUG, + "Blocking subscriber IMSI %s with CM: %d LU: %d\n", + imsi, cm, lu); + return -1; + } + + + bsc_lst = bsc_nat_acc_lst_find(bsc->nat, bsc->cfg->acc_lst_name); + nat_lst = bsc_nat_acc_lst_find(bsc->nat, bsc->nat->acc_lst_name); + + + if (bsc_lst) { + /* 2. BSC allow */ + if (bsc_nat_lst_check_allow(bsc_lst, imsi) == 0) + return 1; + + /* 3. BSC deny */ + if (lst_check_deny(bsc_lst, imsi) == 0) { + LOGP(DNAT, LOGL_ERROR, + "Filtering %s by imsi_deny on bsc nr: %d.\n", imsi, bsc->cfg->nr); + rate_ctr_inc(&bsc_lst->stats->ctr[ACC_LIST_BSC_FILTER]); + return -2; + } + + } + + /* 4. NAT deny */ + if (nat_lst) { + if (lst_check_deny(nat_lst, imsi) == 0) { + LOGP(DNAT, LOGL_ERROR, + "Filtering %s by nat imsi_deny on bsc nr: %d.\n", imsi, bsc->cfg->nr); + rate_ctr_inc(&nat_lst->stats->ctr[ACC_LIST_NAT_FILTER]); + return -3; + } + } + + return 1; +} + +static int _cr_check_loc_upd(struct bsc_connection *bsc, + 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(DNAT, 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(bsc, mi_string); + return 1; +} + +static int _cr_check_cm_serv_req(struct bsc_connection *bsc, + 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(DNAT, 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 = NAT_CON_TYPE_SSA; + rc = gsm48_extract_mi((uint8_t *) &req->classmark, + length - classmark_offset, mi_string, &mi_type); + if (rc < 0) { + LOGP(DNAT, 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(bsc, mi_string); + return 1; +} + +static int _cr_check_pag_resp(struct bsc_connection *bsc, + 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(DNAT, 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(DNAT, 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(bsc, mi_string); + return 1; +} + +static int _dt_check_id_resp(struct bsc_connection *bsc, + uint8_t *data, unsigned int length, + struct nat_sccp_connection *con, + struct bsc_nat_reject_cause *cause) +{ + char mi_string[GSM48_MI_SIZE]; + uint8_t mi_type; + + if (length < 2) { + LOGP(DNAT, LOGL_ERROR, "mi does not fit.\n"); + return -1; + } + + if (data[0] < length - 1) { + LOGP(DNAT, 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; + + con->imsi_checked = 1; + con->imsi = talloc_strdup(con, mi_string); + return auth_imsi(bsc, mi_string, cause); +} + + +/* 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_nat_reject_cause *cause) +{ + struct tlv_parsed tp; + struct gsm48_hdr *hdr48; + int hdr48_len; + int len, ret = 0; + uint8_t msg_type, proto; + + *con_type = NAT_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); + + proto = hdr48->proto_discr & 0x0f; + msg_type = hdr48->msg_type & 0xbf; + if (proto == GSM48_PDISC_MM && + msg_type == GSM48_MT_MM_LOC_UPD_REQUEST) { + *con_type = NAT_CON_TYPE_LU; + ret = _cr_check_loc_upd(bsc, &hdr48->data[0], + hdr48_len - sizeof(*hdr48), imsi); + } else if (proto == GSM48_PDISC_MM && + msg_type == GSM48_MT_MM_CM_SERV_REQ) { + *con_type = NAT_CON_TYPE_CM_SERV_REQ; + ret = _cr_check_cm_serv_req(bsc, &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 = NAT_CON_TYPE_PAG_RESP; + ret = _cr_check_pag_resp(bsc, &hdr48->data[0], + hdr48_len - sizeof(*hdr48), imsi); + } else { + /* We only want to filter the above, let other things pass */ + *con_type = NAT_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(bsc, *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_nat_reject_cause *cause) +{ + uint32_t len; + uint8_t msg_type, proto; + struct gsm48_hdr *hdr48; + + cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; + cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; + + if (con->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; + + proto = hdr48->proto_discr & 0x0f; + msg_type = hdr48->msg_type & 0xbf; + if (proto != GSM48_PDISC_MM || msg_type != GSM48_MT_MM_ID_RESP) + return 0; + + return _dt_check_id_resp(bsc, &hdr48->data[0], + len - sizeof(*hdr48), con, cause); +} diff --git a/src/osmo-bsc_nat/bsc_nat_rewrite.c b/src/osmo-bsc_nat/bsc_nat_rewrite.c new file mode 100644 index 000000000..06071c475 --- /dev/null +++ b/src/osmo-bsc_nat/bsc_nat_rewrite.c @@ -0,0 +1,640 @@ +/* + * 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 + +static char *match_and_rewrite_number(void *ctx, const char *number, + const char *imsi, + struct llist_head *list) +{ + 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) + 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, 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)) + return NULL; + + /* only ISDN plan */ + if (called->plan != 1) + 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, &nat->num_rewr); +} + + +/** + * 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 = NULL; + 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 */ + new_number = rewrite_isdn_number(nat, msg, imsi, &called); + + if (!new_number) { + LOGP(DNAT, LOGL_DEBUG, "No IMSI match found, returning message.\n"); + return NULL; + } + + if (strlen(new_number) > sizeof(called.number)) { + LOGP(DNAT, LOGL_ERROR, "Number is too long for structure.\n"); + talloc_free(new_number); + 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(DNAT, LOGL_ERROR, "Failed to allocate.\n"); + talloc_free(new_number); + 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 */ + if (strncmp(new_number, "00", 2) == 0) { + called.type = 1; + strncpy(called.number, new_number + 2, sizeof(called.number)); + } else { + /* rewrite international to unknown */ + if (called.type == 1) + called.type = 0; + strncpy(called.number, new_number, sizeof(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); + 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); +} + +/** + * This is a helper for GSM 04.11 8.2.5.2 Destination address element + */ +void sms_encode_addr_element(struct msgb *out, const char *new_number, + int format, int tp_data) +{ + uint8_t new_addr_len; + uint8_t new_addr[26]; + + /* + * Copy the new number. We let libosmocore encode it, then set + * the extension followed after the length. Depending on if + * we want to write RP we will let the TLV code add the + * length for us or we need to use strlen... This is not very clear + * as of 03.40 and 04.11. + */ + new_addr_len = gsm48_encode_bcd_number(new_addr, ARRAY_SIZE(new_addr), + 1, new_number); + new_addr[1] = format; + if (tp_data) { + uint8_t *data = msgb_put(out, new_addr_len); + memcpy(data, new_addr, new_addr_len); + data[0] = strlen(new_number); + } else { + msgb_lv_put(out, new_addr_len - 1, new_addr + 1); + } +} + +static struct msgb *sms_create_new(uint8_t type, uint8_t ref, + struct gsm48_hdr *old_hdr48, + const uint8_t *orig_addr_ptr, + int orig_addr_len, const char *new_number, + const uint8_t *data_ptr, int data_len, + uint8_t tpdu_first_byte, + const int old_dest_len, const char *new_dest_nr) +{ + struct gsm48_hdr *new_hdr48; + struct msgb *out; + + /* + * We need to re-create the patched structure. This is why we have + * saved the above pointers. + */ + out = msgb_alloc_headroom(4096, 128, "changed-smsc"); + if (!out) { + LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); + return NULL; + } + + out->l2h = out->data; + msgb_v_put(out, GSM411_MT_RP_DATA_MO); + msgb_v_put(out, ref); + msgb_lv_put(out, orig_addr_len, orig_addr_ptr); + + sms_encode_addr_element(out, new_number, 0x91, 0); + + + /* Patch the TPDU from here on */ + + /** + * Do we need to put a new TP-Destination-Address (TP-DA) here or + * can we copy the old thing? For the TP-DA we need to find out the + * new size. + */ + if (new_dest_nr) { + uint8_t *data, *new_size; + + /* reserve the size and write the header */ + new_size = msgb_put(out, 1); + out->l3h = new_size + 1; + msgb_v_put(out, tpdu_first_byte); + msgb_v_put(out, data_ptr[1]); + + /* encode the new number and put it */ + if (strncmp(new_dest_nr, "00", 2) == 0) + sms_encode_addr_element(out, new_dest_nr + 2, 0x91, 1); + else + sms_encode_addr_element(out, new_dest_nr, 0x81, 1); + + /* Copy the rest after the TP-DS */ + data = msgb_put(out, data_len - 2 - 1 - old_dest_len); + memcpy(data, &data_ptr[2 + 1 + old_dest_len], data_len - 2 - 1 - old_dest_len); + + /* fill in the new size */ + new_size[0] = msgb_l3len(out); + } else { + msgb_v_put(out, data_len); + msgb_tv_fixed_put(out, tpdu_first_byte, data_len - 1, &data_ptr[1]); + } + + /* prepend GSM 04.08 header */ + new_hdr48 = (struct gsm48_hdr *) msgb_push(out, sizeof(*new_hdr48) + 1); + memcpy(new_hdr48, old_hdr48, sizeof(*old_hdr48)); + new_hdr48->data[0] = msgb_l2len(out); + + return out; +} + +/** + * Parse the SMS and check if it needs to be rewritten + */ +static struct msgb *rewrite_sms(struct bsc_nat *nat, struct msgb *msg, + struct bsc_nat_parsed *parsed, const char *imsi, + struct gsm48_hdr *hdr48, const uint32_t len) +{ + unsigned int payload_len; + unsigned int cp_len; + + uint8_t ref; + uint8_t orig_addr_len, *orig_addr_ptr; + uint8_t dest_addr_len, *dest_addr_ptr; + uint8_t data_len, *data_ptr; + char smsc_addr[30]; + + + uint8_t dest_len, orig_dest_len; + char _dest_nr[30]; + char *dest_nr; + char *new_dest_nr; + + char *new_number = NULL; + uint8_t tpdu_hdr; + struct msgb *out; + + payload_len = len - sizeof(*hdr48); + if (payload_len < 1) { + LOGP(DNAT, LOGL_ERROR, "SMS too short for things. %d\n", payload_len); + return NULL; + } + + cp_len = hdr48->data[0]; + if (payload_len + 1 < cp_len) { + LOGP(DNAT, LOGL_ERROR, "SMS RPDU can not fit in: %d %d\n", cp_len, payload_len); + return NULL; + } + + if (hdr48->data[1] != GSM411_MT_RP_DATA_MO) + return NULL; + + if (cp_len < 5) { + LOGP(DNAT, LOGL_ERROR, "RD-DATA can not fit in the CP len: %d\n", cp_len); + return NULL; + } + + /* RP */ + ref = hdr48->data[2]; + orig_addr_len = hdr48->data[3]; + orig_addr_ptr = &hdr48->data[4]; + + /* the +1 is for checking if the following element has some space */ + if (cp_len < 3 + orig_addr_len + 1) { + LOGP(DNAT, LOGL_ERROR, "RP-Originator addr does not fit: %d\n", orig_addr_len); + return NULL; + } + + dest_addr_len = hdr48->data[3 + orig_addr_len + 1]; + dest_addr_ptr = &hdr48->data[3 + orig_addr_len + 2]; + + if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1) { + LOGP(DNAT, LOGL_ERROR, "RP-Destination addr does not fit: %d\n", dest_addr_len); + return NULL; + } + gsm48_decode_bcd_number(smsc_addr, ARRAY_SIZE(smsc_addr), dest_addr_ptr - 1, 1); + + data_len = hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 1]; + data_ptr = &hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 2]; + + if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1 + data_len) { + LOGP(DNAT, LOGL_ERROR, "RP-Data does not fit: %d\n", data_len); + return NULL; + } + + if (data_len < 3) { + LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT is too short.\n"); + return NULL; + } + + /* TP-PDU starts here */ + if ((data_ptr[0] & 0x03) != GSM340_SMS_SUBMIT_MS2SC) + return NULL; + + /* + * look into the phone number. The length is in semi-octets, we will + * need to add the byte for the number type as well. + */ + orig_dest_len = data_ptr[2]; + dest_len = ((orig_dest_len + 1) / 2) + 1; + if (data_len < dest_len + 3 || dest_len < 2) { + LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT can not have TP-DestAddr.\n"); + return NULL; + } + + if ((data_ptr[3] & 0x80) == 0) { + LOGP(DNAT, LOGL_ERROR, "TP-DestAddr has extension. Not handled.\n"); + return NULL; + } + + if ((data_ptr[3] & 0x0F) == 0) { + LOGP(DNAT, LOGL_ERROR, "TP-DestAddr is of unknown type.\n"); + return NULL; + } + + /** + * Besides of what I think I read in GSM 03.40 and 04.11 the TP-DA + * contains the semi-octets as length (strlen), change it to the + * the number of bytes, but then change it back. + */ + data_ptr[2] = dest_len; + gsm48_decode_bcd_number(_dest_nr + 2, ARRAY_SIZE(_dest_nr) - 2, + &data_ptr[2], 1); + data_ptr[2] = orig_dest_len; + if ((data_ptr[3] & 0x70) == 0x10) { + _dest_nr[0] = _dest_nr[1] = '0'; + dest_nr = &_dest_nr[0]; + } else { + dest_nr = &_dest_nr[2]; + } + + /** + * Call functions to rewrite the data + */ + tpdu_hdr = sms_new_tpdu_hdr(nat, imsi, dest_nr, data_ptr[0]); + new_number = find_new_smsc(nat, msg, imsi, smsc_addr, dest_nr); + new_dest_nr = sms_new_dest_nr(nat, msg, imsi, dest_nr); + + if (tpdu_hdr == data_ptr[0] && !new_number && !new_dest_nr) + return NULL; + + out = sms_create_new(GSM411_MT_RP_DATA_MO, ref, hdr48, + orig_addr_ptr, orig_addr_len, + new_number ? new_number : smsc_addr, + data_ptr, data_len, tpdu_hdr, + dest_len, new_dest_nr); + talloc_free(new_number); + talloc_free(new_dest_nr); + return out; +} + +struct msgb *bsc_nat_rewrite_msg(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *parsed, const char *imsi) +{ + struct gsm48_hdr *hdr48; + uint32_t len; + uint8_t msg_type, proto; + struct msgb *new_msg = NULL, *sccp; + uint8_t link_id; + + if (!imsi || strlen(imsi) < 5) + return msg; + + /* only care about DTAP messages */ + if (parsed->bssap != BSSAP_MSG_DTAP) + return msg; + if (!parsed->dest_local_ref) + return msg; + + hdr48 = bsc_unpack_dtap(parsed, msg, &len); + if (!hdr48) + return msg; + + link_id = msg->l3h[1]; + proto = hdr48->proto_discr & 0x0f; + msg_type = hdr48->msg_type & 0xbf; + + if (proto == GSM48_PDISC_CC && msg_type == GSM48_MT_CC_SETUP) + new_msg = rewrite_setup(nat, msg, parsed, imsi, hdr48, len); + else if (proto == GSM48_PDISC_SMS && msg_type == GSM411_MT_CP_DATA) + new_msg = rewrite_sms(nat, msg, parsed, imsi, hdr48, len); + + if (!new_msg) + return msg; + + /* wrap with DTAP, SCCP, then IPA. TODO: Stop copying */ + gsm0808_prepend_dtap_header(new_msg, link_id); + sccp = sccp_create_dt1(parsed->dest_local_ref, new_msg->data, new_msg->len); + talloc_free(new_msg); + + if (!sccp) { + LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); + return msg; + } + + ipaccess_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; + } + + /* 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/src/osmo-bsc_nat/bsc_nat_utils.c b/src/osmo-bsc_nat/bsc_nat_utils.c new file mode 100644 index 000000000..45c224ce8 --- /dev/null +++ b/src/osmo-bsc_nat/bsc_nat_utils.c @@ -0,0 +1,566 @@ + +/* 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 + +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, +}; + +static const struct rate_ctr_desc acc_list_ctr_description[] = { + [ACC_LIST_BSC_FILTER] = { "access-list.bsc-filter", "Rejected by rule for BSC"}, + [ACC_LIST_NAT_FILTER] = { "access-list.nat-filter", "Rejected by rule for NAT"}, +}; + +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, +}; + +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->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_set_msc_ip(struct bsc_nat *nat, const char *ip) +{ + bsc_replace_string(nat, &nat->main_dest->ip, ip); +} + +struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat) +{ + struct bsc_connection *con = talloc_zero(nat, struct bsc_connection); + if (!con) + return NULL; + + con->nat = nat; + osmo_wqueue_init(&con->write_queue, 100); + INIT_LLIST_HEAD(&con->cmd_pending); + INIT_LLIST_HEAD(&con->pending_dlcx); + return con; +} + +struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token) +{ + struct bsc_config *conf = talloc_zero(nat, struct bsc_config); + if (!conf) + return NULL; + + conf->token = talloc_strdup(conf, token); + conf->nr = nat->num_bsc; + conf->nat = nat; + conf->max_endpoints = 32; + conf->paging_group = PAGIN_GROUP_UNASSIGNED; + + INIT_LLIST_HEAD(&conf->lac_list); + + llist_add_tail(&conf->entry, &nat->bsc_configs); + ++nat->num_bsc; + + conf->stats.ctrg = rate_ctr_group_alloc(conf, &bsc_cfg_ctrg_desc, conf->nr); + if (!conf->stats.ctrg) { + talloc_free(conf); + return NULL; + } + + return conf; +} + +void bsc_config_free(struct bsc_config *cfg) +{ + rate_ctr_group_free(cfg->stats.ctrg); +} + +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 */ + ipaccess_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; +} + +int bsc_nat_lst_check_allow(struct bsc_nat_acc_lst *lst, const char *mi_string) +{ + struct bsc_nat_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 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 [] = { + [NAT_CON_TYPE_NONE] = "n/a", + [NAT_CON_TYPE_LU] = "Location Update", + [NAT_CON_TYPE_CM_SERV_REQ] = "CM Serv Req", + [NAT_CON_TYPE_PAG_RESP] = "Paging Response", + [NAT_CON_TYPE_SSA] = "Supplementar Service Activation", + [NAT_CON_TYPE_LOCAL_REJECT] = "Local Reject", + [NAT_CON_TYPE_OTHER] = "Other", +}; + +const char *bsc_con_type_to_string(int type) +{ + return con_types[type]; +} + +struct bsc_nat_acc_lst *bsc_nat_acc_lst_find(struct bsc_nat *nat, const char *name) +{ + struct bsc_nat_acc_lst *lst; + + if (!name) + return NULL; + + llist_for_each_entry(lst, &nat->access_lists, list) + if (strcmp(lst->name, name) == 0) + return lst; + + return NULL; +} + +struct bsc_nat_acc_lst *bsc_nat_acc_lst_get(struct bsc_nat *nat, const char *name) +{ + struct bsc_nat_acc_lst *lst; + + lst = bsc_nat_acc_lst_find(nat, name); + if (lst) + return lst; + + lst = talloc_zero(nat, struct bsc_nat_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, &nat->access_lists); + return lst; +} + +void bsc_nat_acc_lst_delete(struct bsc_nat_acc_lst *lst) +{ + llist_del(&lst->list); + rate_ctr_group_free(lst->stats); + talloc_free(lst); +} + +struct bsc_nat_acc_lst_entry *bsc_nat_acc_lst_entry_create(struct bsc_nat_acc_lst *lst) +{ + struct bsc_nat_acc_lst_entry *entry; + + entry = talloc_zero(lst, struct bsc_nat_acc_lst_entry); + if (!entry) + return NULL; + + llist_add_tail(&entry->list, &lst->fltr_list); + return entry; +} + +int bsc_nat_msc_is_connected(struct bsc_nat *nat) +{ + return nat->msc_con->is_connected; +} + +static const int con_to_ctr[] = { + [NAT_CON_TYPE_NONE] = -1, + [NAT_CON_TYPE_LU] = BCFG_CTR_CON_TYPE_LU, + [NAT_CON_TYPE_CM_SERV_REQ] = BCFG_CTR_CON_CMSERV_RQ, + [NAT_CON_TYPE_PAG_RESP] = BCFG_CTR_CON_PAG_RESP, + [NAT_CON_TYPE_SSA] = BCFG_CTR_CON_SSA, + [NAT_CON_TYPE_LOCAL_REJECT] = -1, + [NAT_CON_TYPE_OTHER] = BCFG_CTR_CON_OTHER, +}; + +int bsc_conn_type_to_ctr(struct nat_sccp_connection *conn) +{ + return con_to_ctr[conn->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/src/osmo-bsc_nat/bsc_nat_vty.c b/src/osmo-bsc_nat/bsc_nat_vty.c new file mode 100644 index 000000000..46dc38e14 --- /dev/null +++ b/src/osmo-bsc_nat/bsc_nat_vty.c @@ -0,0 +1,1160 @@ +/* OpenBSC NAT interface to quagga VTY */ +/* (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 + +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 write_acc_lst(struct vty *vty, struct bsc_nat_acc_lst *lst) +{ + struct bsc_nat_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%s", + lst->name, entry->imsi_deny, VTY_NEWLINE); + } +} + +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_nat_acc_lst *lst; + struct bsc_nat_paging_group *pgroup; + + vty_out(vty, "nat%s", VTY_NEWLINE); + vty_out(vty, " msc ip %s%s", _nat->main_dest->ip, VTY_NEWLINE); + vty_out(vty, " msc port %d%s", _nat->main_dest->port, VTY_NEWLINE); + vty_out(vty, " timeout auth %d%s", _nat->auth_timeout, VTY_NEWLINE); + vty_out(vty, " timeout ping %d%s", _nat->ping_timeout, VTY_NEWLINE); + vty_out(vty, " timeout pong %d%s", _nat->pong_timeout, VTY_NEWLINE); + if (_nat->token) + vty_out(vty, " token %s%s", _nat->token, VTY_NEWLINE); + vty_out(vty, " ip-dscp %d%s", _nat->bsc_ip_dscp, VTY_NEWLINE); + if (_nat->acc_lst_name) + vty_out(vty, " access-list-name %s%s", _nat->acc_lst_name, VTY_NEWLINE); + if (_nat->imsi_black_list_fn) + vty_out(vty, " imsi-black-list-file-name %s%s", + _nat->imsi_black_list_fn, VTY_NEWLINE); + if (_nat->ussd_lst_name) + vty_out(vty, " ussd-list-name %s%s", _nat->ussd_lst_name, VTY_NEWLINE); + if (_nat->ussd_query) + vty_out(vty, " ussd-query %s%s", _nat->ussd_query, VTY_NEWLINE); + if (_nat->ussd_token) + vty_out(vty, " ussd-token %s%s", _nat->ussd_token, VTY_NEWLINE); + if (_nat->ussd_local) + vty_out(vty, " ussd-local-ip %s%s", _nat->ussd_local, VTY_NEWLINE); + + if (_nat->num_rewr_name) + vty_out(vty, " number-rewrite %s%s", _nat->num_rewr_name, VTY_NEWLINE); + if (_nat->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); + + llist_for_each_entry(lst, &_nat->access_lists, list) + write_acc_lst(vty, lst); + llist_for_each_entry(pgroup, &_nat->paging_groups, entry) + write_pgroup_lst(vty, pgroup); + if (_nat->mgcp_ipa) + vty_out(vty, " mgcp-through-msc-ipa%s", 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); + 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); +} + +static int config_write_bsc(struct vty *vty) +{ + struct bsc_config *bsc; + + llist_for_each_entry(bsc, &_nat->bsc_configs, entry) + config_write_bsc_single(vty, bsc); + return CMD_SUCCESS; +} + + +DEFUN(show_sccp, show_sccp_cmd, "show sccp connections", + SHOW_STR "Display information about SCCP\n" + "All active connections\n") +{ + struct nat_sccp_connection *con; + vty_out(vty, "Listing all open SCCP connections%s", VTY_NEWLINE); + + llist_for_each_entry(con, &_nat->sccp_connections, list_entry) { + vty_out(vty, "For BSC Nr: %d BSC ref: 0x%x; MUX ref: 0x%x; Network has ref: %d ref: 0x%x MSC/BSC mux: 0x%x/0x%x type: %s%s", + con->bsc->cfg ? con->bsc->cfg->nr : -1, + sccp_src_ref_to_int(&con->real_ref), + sccp_src_ref_to_int(&con->patched_ref), + con->has_remote_ref, + sccp_src_ref_to_int(&con->remote_ref), + con->msc_endp, con->bsc_endp, + bsc_con_type_to_string(con->con_type), + VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN(show_bsc, show_bsc_cmd, "show bsc connections", + SHOW_STR BSC_STR + "All active connections\n") +{ + struct bsc_connection *con; + struct sockaddr_in sock; + socklen_t len = sizeof(sock); + + llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { + getpeername(con->write_queue.bfd.fd, (struct sockaddr *) &sock, &len); + vty_out(vty, "BSC nr: %d auth: %d fd: %d peername: %s pending-stats: %u%s", + con->cfg ? con->cfg->nr : -1, + con->authenticated, con->write_queue.bfd.fd, + inet_ntoa(sock.sin_addr), con->pending_dlcx_count, + VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN(show_bsc_mgcp, show_bsc_mgcp_cmd, "show bsc mgcp NR", + SHOW_STR BSC_STR MGCP_STR "Identifier of the BSC\n") +{ + struct bsc_connection *con; + int nr = atoi(argv[0]); + int i, j, endp; + + llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { + int max; + if (!con->cfg) + continue; + if (con->cfg->nr != nr) + continue; + + /* this bsc has no audio endpoints yet */ + if (!con->_endpoint_status) + continue; + + vty_out(vty, "MGCP Status for %d%s", con->cfg->nr, VTY_NEWLINE); + max = bsc_mgcp_nr_multiplexes(con->max_endpoints); + for (i = 0; i < max; ++i) { + for (j = 0; 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", + nat->msc_con->is_connected, 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.\n"); + return CMD_WARNING; + } + + vty_out(vty, "MSC is connected: %d%s\n", + _nat->msc_con->is_connected, 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", "Configute the NAT") +{ + vty->index = _nat; + vty->node = NAT_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_msc_ip, + cfg_nat_msc_ip_cmd, + "msc ip A.B.C.D", + "MSC related configuration\n" + "Configure the IP address\n" IP_STR) +{ + bsc_nat_set_msc_ip(_nat, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_msc_port, + cfg_nat_msc_port_cmd, + "msc port <1-65500>", + "MSC related configuration\n" + "Configure the port\n" + "Port number\n") +{ + _nat->main_dest->port = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_auth_time, + cfg_nat_auth_time_cmd, + "timeout auth <1-256>", + "Timeout configuration\n" + "Authentication timeout\n" + "Timeout in seconds\n") +{ + _nat->auth_timeout = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_ping_time, + cfg_nat_ping_time_cmd, + "timeout ping NR", + "Timeout configuration\n" + "Time between two pings\n" + "Timeout in seconds\n") +{ + _nat->ping_timeout = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_pong_time, + cfg_nat_pong_time_cmd, + "timeout pong NR", + "Timeout configuration\n" + "Waiting for pong timeout\n" + "Timeout in seconds\n") +{ + _nat->pong_timeout = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_token, cfg_nat_token_cmd, + "token TOKEN", + "Authentication token configuration\n" + "Token of the BSC, currently transferred in cleartext\n") +{ + bsc_replace_string(_nat, &_nat->token, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_bsc_ip_dscp, cfg_nat_bsc_ip_dscp_cmd, + "ip-dscp <0-255>", + "Set the IP DSCP for the BSCs to use\n" "Set the IP_TOS attribute") +{ + _nat->bsc_ip_dscp = atoi(argv[0]); + return CMD_SUCCESS; +} + +ALIAS_DEPRECATED(cfg_nat_bsc_ip_dscp, cfg_nat_bsc_ip_tos_cmd, + "ip-tos <0-255>", + "Use ip-dscp in the future.\n" "Set the DSCP\n") + + +DEFUN(cfg_nat_acc_lst_name, + cfg_nat_acc_lst_name_cmd, + "access-list-name NAME", + "Set the name of the access list to use.\n" + "The name of the to be used access list.") +{ + bsc_replace_string(_nat, &_nat->acc_lst_name, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_no_acc_lst_name, + cfg_nat_no_acc_lst_name_cmd, + "no access-list-name", + NO_STR "Remove the access list from the NAT.\n") +{ + if (_nat->acc_lst_name) { + talloc_free(_nat->acc_lst_name); + _nat->acc_lst_name = NULL; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_imsi_black_list_fn, + cfg_nat_imsi_black_list_fn_cmd, + "imsi-black-list-file-name NAME", + "IMSI black listing\n" "Filename IMSI and reject-cause\n") +{ + + bsc_replace_string(_nat, &_nat->imsi_black_list_fn, argv[0]); + if (_nat->imsi_black_list_fn) { + int rc; + struct osmo_config_list *rewr = NULL; + rewr = osmo_config_list_parse(_nat, _nat->imsi_black_list_fn); + rc = bsc_nat_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_nat_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_nat_barr_adapt(_nat, &_nat->imsi_black_list, NULL); + return CMD_SUCCESS; +} + +static int replace_rules(struct bsc_nat *nat, char **name, + struct llist_head *head, const char *file) +{ + struct osmo_config_list *rewr = NULL; + + bsc_replace_string(nat, name, file); + if (*name) { + rewr = osmo_config_list_parse(nat, *name); + bsc_nat_num_rewr_entry_adapt(nat, head, rewr); + talloc_free(rewr); + return CMD_SUCCESS; + } else { + bsc_nat_num_rewr_entry_adapt(nat, head, NULL); + return CMD_SUCCESS; + } +} + +DEFUN(cfg_nat_number_rewrite, + cfg_nat_number_rewrite_cmd, + "number-rewrite FILENAME", + "Set the file with rewriting rules.\n" "Filename") +{ + return replace_rules(_nat, &_nat->num_rewr_name, + &_nat->num_rewr, argv[0]); +} + +DEFUN(cfg_nat_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_ussd_lst_name, + cfg_nat_ussd_lst_name_cmd, + "ussd-list-name NAME", + "Set the name of the access list to check for IMSIs for USSD message\n" + "The name of the access list for HLR USSD handling") +{ + bsc_replace_string(_nat, &_nat->ussd_lst_name, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_ussd_query, + cfg_nat_ussd_query_cmd, + "ussd-query REGEXP", + "Set the USSD query to match with the ussd-list-name\n" + "The query to match") +{ + if (gsm_parse_reg(_nat, &_nat->ussd_query_re, &_nat->ussd_query, argc, argv) != 0) + return CMD_WARNING; + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_ussd_token, + cfg_nat_ussd_token_cmd, + "ussd-token TOKEN", + "Set the token used to identify the USSD module\n" "Secret key\n") +{ + bsc_replace_string(_nat, &_nat->ussd_token, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_ussd_local, + cfg_nat_ussd_local_cmd, + "ussd-local-ip A.B.C.D", + "Set the IP to listen for the USSD Provider\n" "IP Address\n") +{ + bsc_replace_string(_nat, &_nat->ussd_local, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_mgcp_ipa, + cfg_nat_mgcp_ipa_cmd, + "mgcp-through-msc-ipa", + "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; +} + +/* per BSC configuration */ +DEFUN(cfg_bsc, cfg_bsc_cmd, "bsc BSC_NR", + "BSC configuration\n" "Identifier of the BSC\n") +{ + int bsc_nr = atoi(argv[0]); + struct bsc_config *bsc; + + if (bsc_nr > _nat->num_bsc) { + vty_out(vty, "%% The next unused BSC number is %u%s", + _nat->num_bsc, VTY_NEWLINE); + return CMD_WARNING; + } else if (bsc_nr == _nat->num_bsc) { + /* allocate a new one */ + bsc = bsc_config_alloc(_nat, "unknown"); + } else + bsc = bsc_config_num(_nat, bsc_nr); + + if (!bsc) + return CMD_WARNING; + + vty->index = bsc; + vty->node = NAT_BSC_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bsc_token, cfg_bsc_token_cmd, "token TOKEN", + "Authentication token configuration\n" + "Token of the BSC, currently transferred in cleartext\n") +{ + struct bsc_config *conf = vty->index; + + bsc_replace_string(conf, &conf->token, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bsc_lac, cfg_bsc_lac_cmd, "location_area_code <0-65535>", + "Add the Location Area Code (LAC) of this BSC\n" "LAC\n") +{ + struct bsc_config *tmp; + struct bsc_config *conf = vty->index; + + int lac = atoi(argv[0]); + + if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { + vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", + lac, VTY_NEWLINE); + return CMD_WARNING; + } + + /* verify that the LACs are unique */ + llist_for_each_entry(tmp, &_nat->bsc_configs, entry) { + if (bsc_config_handles_lac(tmp, lac)) { + vty_out(vty, "%% LAC %d is already used.%s", lac, VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; + } + } + + bsc_config_add_lac(conf, lac); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bsc_no_lac, cfg_bsc_no_lac_cmd, + "no location_area_code <0-65535>", + NO_STR "Remove the Location Area Code (LAC) of this BSC\n" "LAC\n") +{ + int lac = atoi(argv[0]); + struct bsc_config *conf = vty->index; + + bsc_config_del_lac(conf, lac); + return CMD_SUCCESS; +} + + + +DEFUN(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_nat_acc_lst *acc; + struct bsc_nat_acc_lst_entry *entry; + + acc = bsc_nat_acc_lst_get(_nat, argv[0]); + if (!acc) + return CMD_WARNING; + + entry = bsc_nat_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]", + "Access list commands\n" + "Name of the access list\n" + "Add denied IMSI to the list\n" + "Regexp for IMSIs\n") +{ + struct bsc_nat_acc_lst *acc; + struct bsc_nat_acc_lst_entry *entry; + + acc = bsc_nat_acc_lst_get(_nat, argv[0]); + if (!acc) + return CMD_WARNING; + + entry = bsc_nat_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; + return CMD_SUCCESS; +} + +/* naming to follow Zebra... */ +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_nat_acc_lst *acc; + acc = bsc_nat_acc_lst_find(_nat, argv[0]); + if (!acc) + return CMD_WARNING; + + bsc_nat_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_nat_acc_lst *acc; + acc = bsc_nat_acc_lst_find(_nat, 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(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_nat_barr_entry *entry; + entry = rb_entry(node, struct bsc_nat_barr_entry, node); + + vty_out(vty, " IMSI(%s) CM-Reject-Cause(%d) LU-Reject-Cause(%d)%s", + entry->imsi, entry->cm_reject_cause, entry->lu_reject_cause, + VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + + +DEFUN(cfg_bsc_acc_lst_name, + cfg_bsc_acc_lst_name_cmd, + "access-list-name NAME", + "Set the name of the access list to use.\n" + "The name of the to be used access list.") +{ + struct bsc_config *conf = vty->index; + + bsc_replace_string(conf, &conf->acc_lst_name, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bsc_no_acc_lst_name, + cfg_bsc_no_acc_lst_name_cmd, + "no access-list-name", + NO_STR "Do not use an access-list for the BSC.\n") +{ + struct bsc_config *conf = vty->index; + + if (conf->acc_lst_name) { + talloc_free(conf->acc_lst_name); + conf->acc_lst_name = NULL; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bsc_max_endps, cfg_bsc_max_endps_cmd, + "max-endpoints <1-1024>", + "Highest endpoint to use (exclusively)\n" "Number of ports\n") +{ + struct bsc_config *conf = vty->index; + + conf->max_endpoints = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bsc_paging, + cfg_bsc_paging_cmd, + "paging forbidden (0|1)", + PAGING_STR "Forbid sending PAGING REQUESTS to the BSC.\n" + "Do not forbid\n" "Forbid\n") +{ + struct bsc_config *conf = vty->index; + + if (strcmp("1", argv[0]) == 0) + conf->forbid_paging = 1; + else + conf->forbid_paging = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bsc_desc, + cfg_bsc_desc_cmd, + "description DESC", + "Provide a description for the given BSC.\n" "Description\n") +{ + struct bsc_config *conf = vty->index; + + bsc_replace_string(conf, &conf->description, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bsc_paging_grp, + cfg_bsc_paging_grp_cmd, + "paging group <0-1000>", + PAGING_STR "Use a paging group\n" "Paging Group to use\n") +{ + struct bsc_config *conf = vty->index; + conf->paging_group = atoi(argv[0]); + return CMD_SUCCESS; +} + +ALIAS_DEPRECATED(cfg_bsc_paging_grp, cfg_bsc_old_grp_cmd, + "paging-group <0-1000>", + "Use a paging group\n" "Paging Group to use\n") + +DEFUN(cfg_bsc_no_paging_grp, + cfg_bsc_no_paging_grp_cmd, + "no paging group", + NO_STR PAGING_STR "Disable the usage of a paging group.\n") +{ + struct bsc_config *conf = vty->index; + conf->paging_group = PAGIN_GROUP_UNASSIGNED; + return CMD_SUCCESS; +} + +DEFUN(test_regex, test_regex_cmd, + "test regex PATTERN STRING", + "Test utilities\n" + "Regexp testing\n" "The regexp pattern\n" + "The string to match\n") +{ + regex_t reg; + char *str = NULL; + + memset(®, 0, sizeof(reg)); + if (gsm_parse_reg(_nat, ®, &str, 1, argv) != 0) + return CMD_WARNING; + + vty_out(vty, "String matches allow pattern: %d%s", + regexec(®, argv[1], 0, NULL, 0) == 0, VTY_NEWLINE); + + talloc_free(str); + regfree(®); + return CMD_SUCCESS; +} + +DEFUN(set_last_endp, set_last_endp_cmd, + "set bsc last-used-endpoint <0-9999999999> <0-1024>", + "Set a value\n" "Operate on a BSC\n" + "Last used endpoint for an assignment\n" "BSC configuration number\n" + "Endpoint number used\n") +{ + struct bsc_connection *con; + int nr = atoi(argv[0]); + int endp = atoi(argv[1]); + + + llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { + if (!con->cfg) + continue; + if (con->cfg->nr != nr) + continue; + + con->last_endpoint = endp; + vty_out(vty, "Updated the last endpoint for %d to %d.%s", + con->cfg->nr, con->last_endpoint, VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN(block_new_conn, block_new_conn_cmd, + "nat-block (block|unblock)", + "Block the NAT for new connections\n" + "Block\n" "Unblock\n") +{ + _nat->blocked = argv[0][0] == 'b'; + vty_out(vty, "%%Going to %s the NAT.%s", + _nat->blocked ? "block" : "unblock", VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* paging group */ +DEFUN(cfg_nat_pgroup, cfg_nat_pgroup_cmd, + "paging-group <0-1000>", + "Create a Paging Group\n" "Number of the Group\n") +{ + int group = atoi(argv[0]); + struct bsc_nat_paging_group *pgroup; + pgroup = bsc_nat_paging_group_num(_nat, group); + if (!pgroup) + pgroup = bsc_nat_paging_group_create(_nat, group); + if (!pgroup) { + vty_out(vty, "Failed to create the group.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vty->index = pgroup; + vty->node = PGROUP_NODE; + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_no_pgroup, cfg_nat_no_pgroup_cmd, + "no paging-group <0-1000>", + NO_STR "Delete paging-group\n" "Paging-group number\n") +{ + int group = atoi(argv[0]); + struct bsc_nat_paging_group *pgroup; + pgroup = bsc_nat_paging_group_num(_nat, group); + if (!pgroup) { + vty_out(vty, "No such paging group %d.%s", group, VTY_NEWLINE); + return CMD_WARNING; + } + + bsc_nat_paging_group_delete(pgroup); + return CMD_SUCCESS; +} + +DEFUN(cfg_pgroup_lac, cfg_pgroup_lac_cmd, + "location_area_code <0-65535>", + "Add the Location Area Code (LAC)\n" "LAC\n") +{ + struct bsc_nat_paging_group *pgroup = vty->index; + + int lac = atoi(argv[0]); + if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { + vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", + lac, VTY_NEWLINE); + return CMD_WARNING; + } + + bsc_nat_paging_group_add_lac(pgroup, lac); + return CMD_SUCCESS; +} + +DEFUN(cfg_pgroup_no_lac, cfg_pgroup_no_lac_cmd, + "no location_area_code <0-65535>", + NO_STR "Remove the Location Area Code (LAC)\n" "LAC\n") +{ + int lac = atoi(argv[0]); + struct bsc_nat_paging_group *pgroup = vty->index; + + bsc_nat_paging_group_del_lac(pgroup, lac); + return CMD_SUCCESS; +} + +int bsc_nat_vty_init(struct bsc_nat *nat) +{ + _nat = nat; + + /* show commands */ + install_element_ve(&show_sccp_cmd); + install_element_ve(&show_bsc_cmd); + install_element_ve(&show_bsc_cfg_cmd); + install_element_ve(&show_stats_cmd); + install_element_ve(&show_stats_lac_cmd); + install_element_ve(&close_bsc_cmd); + install_element_ve(&show_msc_cmd); + install_element_ve(&test_regex_cmd); + install_element_ve(&show_bsc_mgcp_cmd); + install_element_ve(&show_acc_lst_cmd); + install_element_ve(&show_bar_lst_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); + install_default(NAT_NODE); + install_element(NAT_NODE, &ournode_exit_cmd); + install_element(NAT_NODE, &ournode_end_cmd); + install_element(NAT_NODE, &cfg_nat_msc_ip_cmd); + install_element(NAT_NODE, &cfg_nat_msc_port_cmd); + install_element(NAT_NODE, &cfg_nat_auth_time_cmd); + install_element(NAT_NODE, &cfg_nat_ping_time_cmd); + install_element(NAT_NODE, &cfg_nat_pong_time_cmd); + install_element(NAT_NODE, &cfg_nat_token_cmd); + install_element(NAT_NODE, &cfg_nat_bsc_ip_dscp_cmd); + install_element(NAT_NODE, &cfg_nat_bsc_ip_tos_cmd); + install_element(NAT_NODE, &cfg_nat_acc_lst_name_cmd); + install_element(NAT_NODE, &cfg_nat_no_acc_lst_name_cmd); + install_element(NAT_NODE, &cfg_nat_imsi_black_list_fn_cmd); + install_element(NAT_NODE, &cfg_nat_no_imsi_black_list_fn_cmd); + install_element(NAT_NODE, &cfg_nat_ussd_lst_name_cmd); + install_element(NAT_NODE, &cfg_nat_ussd_query_cmd); + install_element(NAT_NODE, &cfg_nat_ussd_token_cmd); + install_element(NAT_NODE, &cfg_nat_ussd_local_cmd); + install_element(NAT_NODE, &cfg_nat_mgcp_ipa_cmd); + + /* access-list */ + install_element(NAT_NODE, &cfg_lst_imsi_allow_cmd); + install_element(NAT_NODE, &cfg_lst_imsi_deny_cmd); + install_element(NAT_NODE, &cfg_lst_no_cmd); + + /* number rewriting */ + install_element(NAT_NODE, &cfg_nat_number_rewrite_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_pgroup_cmd); + install_element(NAT_NODE, &cfg_nat_no_pgroup_cmd); + install_node(&pgroup_node, config_write_pgroup); + install_default(PGROUP_NODE); + install_element(PGROUP_NODE, &ournode_exit_cmd); + install_element(PGROUP_NODE, &ournode_end_cmd); + install_element(PGROUP_NODE, &cfg_pgroup_lac_cmd); + install_element(PGROUP_NODE, &cfg_pgroup_no_lac_cmd); + + /* BSC subgroups */ + install_element(NAT_NODE, &cfg_bsc_cmd); + install_node(&bsc_node, config_write_bsc); + install_default(NAT_BSC_NODE); + install_element(NAT_BSC_NODE, &ournode_exit_cmd); + install_element(NAT_BSC_NODE, &ournode_end_cmd); + install_element(NAT_BSC_NODE, &cfg_bsc_token_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); + + mgcp_vty_init(); + + return 0; +} + + +/* called by the telnet interface... we have our own init above */ +int bsc_vty_init(const struct log_info *cat) +{ + logging_vty_add_cmds(cat); + return 0; +} diff --git a/src/osmo-bsc_nat/bsc_sccp.c b/src/osmo-bsc_nat/bsc_sccp.c new file mode 100644 index 000000000..25c13d4f4 --- /dev/null +++ b/src/osmo-bsc_nat/bsc_sccp.c @@ -0,0 +1,249 @@ +/* SCCP patching and handling routines */ +/* + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include + +#include + +#include +#include + +static int equal(struct sccp_source_reference *ref1, struct sccp_source_reference *ref2) +{ + return memcmp(ref1, ref2, sizeof(*ref1)) == 0; +} + +/* + * SCCP patching below + */ + +/* check if we are using this ref for patched already */ +static int sccp_ref_is_free(struct sccp_source_reference *ref, struct bsc_nat *nat) +{ + struct nat_sccp_connection *conn; + + llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { + if (memcmp(ref, &conn->patched_ref, sizeof(*ref)) == 0) + return -1; + } + + return 0; +} + +/* copied from sccp.c */ +static int assign_src_local_reference(struct sccp_source_reference *ref, struct bsc_nat *nat) +{ + static uint32_t last_ref = 0x50000; + int wrapped = 0; + + do { + struct sccp_source_reference reference; + reference.octet1 = (last_ref >> 0) & 0xff; + reference.octet2 = (last_ref >> 8) & 0xff; + reference.octet3 = (last_ref >> 16) & 0xff; + + ++last_ref; + /* do not use the reversed word and wrap around */ + if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) { + LOGP(DNAT, LOGL_NOTICE, "Wrapped searching for a free code\n"); + last_ref = 0; + ++wrapped; + } + + if (sccp_ref_is_free(&reference, nat) == 0) { + *ref = reference; + return 0; + } + } while (wrapped != 2); + + LOGP(DNAT, LOGL_ERROR, "Finding a free reference failed\n"); + return -1; +} + +struct nat_sccp_connection *create_sccp_src_ref(struct bsc_connection *bsc, + struct bsc_nat_parsed *parsed) +{ + struct nat_sccp_connection *conn; + + /* Some commercial BSCs like to reassign there SRC ref */ + llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { + if (conn->bsc != bsc) + continue; + if (memcmp(&conn->real_ref, parsed->src_local_ref, sizeof(conn->real_ref)) != 0) + continue; + + /* the BSC has reassigned the SRC ref and we failed to keep track */ + memset(&conn->remote_ref, 0, sizeof(conn->remote_ref)); + if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) { + LOGP(DNAT, LOGL_ERROR, "BSC %d reused src ref: %d and we failed to generate a new id.\n", + bsc->cfg->nr, sccp_src_ref_to_int(parsed->src_local_ref)); + bsc_mgcp_dlcx(conn); + llist_del(&conn->list_entry); + talloc_free(conn); + return NULL; + } else { + clock_gettime(CLOCK_MONOTONIC, &conn->creation_time); + bsc_mgcp_dlcx(conn); + return conn; + } + } + + + conn = talloc_zero(bsc->nat, struct nat_sccp_connection); + if (!conn) { + LOGP(DNAT, LOGL_ERROR, "Memory allocation failure.\n"); + return NULL; + } + + conn->bsc = bsc; + clock_gettime(CLOCK_MONOTONIC, &conn->creation_time); + conn->real_ref = *parsed->src_local_ref; + if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) { + LOGP(DNAT, LOGL_ERROR, "Failed to assign a ref.\n"); + talloc_free(conn); + return NULL; + } + + bsc_mgcp_init(conn); + llist_add_tail(&conn->list_entry, &bsc->nat->sccp_connections); + rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_SCCP_CONN]); + osmo_counter_inc(bsc->cfg->nat->stats.sccp.conn); + + LOGP(DNAT, LOGL_DEBUG, "Created 0x%x <-> 0x%x mapping for con %p\n", + sccp_src_ref_to_int(&conn->real_ref), + sccp_src_ref_to_int(&conn->patched_ref), bsc); + + return conn; +} + +int update_sccp_src_ref(struct nat_sccp_connection *sccp, struct bsc_nat_parsed *parsed) +{ + if (!parsed->dest_local_ref || !parsed->src_local_ref) { + LOGP(DNAT, LOGL_ERROR, "CC MSG should contain both local and dest address.\n"); + return -1; + } + + sccp->remote_ref = *parsed->src_local_ref; + sccp->has_remote_ref = 1; + LOGP(DNAT, LOGL_DEBUG, "Updating 0x%x to remote 0x%x on %p\n", + sccp_src_ref_to_int(&sccp->patched_ref), + sccp_src_ref_to_int(&sccp->remote_ref), sccp->bsc); + + return 0; +} + +void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed) +{ + struct nat_sccp_connection *conn; + + llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { + if (memcmp(parsed->src_local_ref, + &conn->patched_ref, sizeof(conn->patched_ref)) == 0) { + + sccp_connection_destroy(conn); + return; + } + } + + LOGP(DNAT, LOGL_ERROR, "Can not remove connection: 0x%x\n", + sccp_src_ref_to_int(parsed->src_local_ref)); +} + +/* + * We have a message from the MSC to the BSC. The MSC is using + * an address that was assigned by the MUX, we need to update the + * dest reference to the real network. + */ +struct nat_sccp_connection *patch_sccp_src_ref_to_bsc(struct msgb *msg, + struct bsc_nat_parsed *parsed, + struct bsc_nat *nat) +{ + struct nat_sccp_connection *conn; + + if (!parsed->dest_local_ref) { + LOGP(DNAT, LOGL_ERROR, "MSG should contain dest_local_ref.\n"); + return NULL; + } + + + llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { + if (!equal(parsed->dest_local_ref, &conn->patched_ref)) + continue; + + /* Change the dest address to the real one */ + *parsed->dest_local_ref = conn->real_ref; + return conn; + } + + return NULL; +} + +/* + * These are message to the MSC. We will need to find the BSC + * Connection by either the SRC or the DST local reference. + * + * In case of a CR we need to work by the SRC local reference + * in all other cases we need to work by the destination local + * reference.. + */ +struct nat_sccp_connection *patch_sccp_src_ref_to_msc(struct msgb *msg, + struct bsc_nat_parsed *parsed, + struct bsc_connection *bsc) +{ + struct nat_sccp_connection *conn; + + llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { + if (conn->bsc != bsc) + continue; + + if (parsed->src_local_ref) { + if (equal(parsed->src_local_ref, &conn->real_ref)) { + *parsed->src_local_ref = conn->patched_ref; + return conn; + } + } else if (parsed->dest_local_ref) { + if (equal(parsed->dest_local_ref, &conn->remote_ref)) + return conn; + } else { + LOGP(DNAT, LOGL_ERROR, "Header has neither loc/dst ref.\n"); + return NULL; + } + } + + return NULL; +} + +struct nat_sccp_connection *bsc_nat_find_con_by_bsc(struct bsc_nat *nat, + struct sccp_source_reference *ref) +{ + struct nat_sccp_connection *conn; + + llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { + if (memcmp(ref, &conn->real_ref, sizeof(*ref)) == 0) + return conn; + } + + return NULL; +} diff --git a/src/osmo-bsc_nat/bsc_ussd.c b/src/osmo-bsc_nat/bsc_ussd.c new file mode 100644 index 000000000..e0c5c6dda --- /dev/null +++ b/src/osmo-bsc_nat/bsc_ussd.c @@ -0,0 +1,427 @@ +/* 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 + +#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_close_ussd_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); + talloc_free(con); +} + +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; + struct ipaccess_head *hh; + int ret; + + ret = ipa_msg_recv(bfd->fd, &msg); + if (ret <= 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 = ipaccess_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"); + return ret; + } + if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME)) + ussd_auth_con(&tvp, conn); + } + + msgb_free(msg); + } else if (hh->proto == IPAC_PROTO_SCCP) { + forward_sccp(conn->nat, msg); + } else { + msgb_free(msg); + } + + return 0; +} + +static void ussd_auth_cb(void *_data) +{ + LOGP(DNAT, LOGL_ERROR, "USSD module didn't authenticate\n"); + bsc_nat_ussd_destroy((struct bsc_nat_ussd_con *) _data); +} + +static void ussd_auth_con(struct tlv_parsed *tvp, struct bsc_nat_ussd_con *conn) +{ + const char *token; + int len; + if (!conn->nat->ussd_token) { + LOGP(DNAT, LOGL_ERROR, "No USSD token set. Closing\n"); + bsc_nat_ussd_destroy(conn); + return; + } + + token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME); + len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME); + + /* last byte should be a NULL */ + if (strlen(conn->nat->ussd_token) != len - 1) + goto disconnect; + /* compare everything including the null byte */ + if (memcmp(conn->nat->ussd_token, token, len) != 0) + goto disconnect; + + /* it is authenticated now */ + if (conn->nat->ussd_con && conn->nat->ussd_con != conn) + bsc_nat_ussd_destroy(conn->nat->ussd_con); + + LOGP(DNAT, LOGL_ERROR, "USSD token specified. USSD provider is connected.\n"); + osmo_timer_del(&conn->auth_timeout); + conn->authorized = 1; + conn->nat->ussd_con = conn; + return; + +disconnect: + LOGP(DNAT, LOGL_ERROR, "Wrong USSD token by client: %d\n", + conn->queue.bfd.fd); + bsc_nat_ussd_destroy(conn); +} + +static void ussd_start_auth(struct bsc_nat_ussd_con *conn) +{ + struct msgb *msg; + + conn->auth_timeout.data = conn; + conn->auth_timeout.cb = ussd_auth_cb; + osmo_timer_schedule(&conn->auth_timeout, conn->nat->auth_timeout, 0); + + msg = msgb_alloc_headroom(4096, 128, "auth message"); + if (!msg) { + LOGP(DNAT, LOGL_ERROR, "Failed to allocate auth msg\n"); + return; + } + + msgb_v_put(msg, IPAC_MSGT_ID_GET); + bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS); +} + +static int ussd_listen_cb(struct osmo_fd *bfd, unsigned int what) +{ + struct bsc_nat_ussd_con *conn; + struct bsc_nat *nat; + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + int fd; + + if (!(what & BSC_FD_READ)) + return 0; + + fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (fd < 0) { + perror("accept"); + return fd; + } + + nat = (struct bsc_nat *) bfd->data; + osmo_counter_inc(nat->stats.ussd.reconn); + + conn = bsc_nat_ussd_alloc(nat); + if (!conn) { + LOGP(DNAT, LOGL_ERROR, "Failed to allocate USSD con struct.\n"); + close(fd); + return -1; + } + + osmo_wqueue_init(&conn->queue, 10); + conn->queue.bfd.data = conn; + conn->queue.bfd.fd = fd; + conn->queue.bfd.when = BSC_FD_READ; + conn->queue.read_cb = ussd_read_cb; + conn->queue.write_cb = bsc_write_cb; + + if (osmo_fd_register(&conn->queue.bfd) < 0) { + LOGP(DNAT, LOGL_ERROR, "Failed to register USSD fd.\n"); + bsc_nat_ussd_destroy(conn); + return -1; + } + + LOGP(DNAT, LOGL_NOTICE, "USSD Connection on %d with IP: %s\n", + fd, inet_ntoa(sa.sin_addr)); + + /* do authentication */ + ussd_start_auth(conn); + return 0; +} + +int bsc_ussd_init(struct bsc_nat *nat) +{ + struct in_addr addr; + + addr.s_addr = INADDR_ANY; + if (nat->ussd_local) + inet_aton(nat->ussd_local, &addr); + + nat->ussd_listen.data = nat; + return make_sock(&nat->ussd_listen, IPPROTO_TCP, + ntohl(addr.s_addr), 5001, 0, ussd_listen_cb, nat); +} + +static int forward_ussd_simple(struct nat_sccp_connection *con, struct msgb *input) +{ + struct msgb *copy; + struct bsc_nat_ussd_con *ussd; + + if (!con->bsc->nat->ussd_con) + return -1; + + copy = msgb_alloc_headroom(4096, 128, "forward bts"); + if (!copy) { + LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); + return -1; + } + + /* copy the data into the copy */ + copy->l2h = msgb_put(copy, msgb_l2len(input)); + memcpy(copy->l2h, input->l2h, msgb_l2len(input)); + + /* send it out */ + ussd = con->bsc->nat->ussd_con; + bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP); + return 0; +} + +static int forward_ussd(struct nat_sccp_connection *con, const struct ussd_request *req, + struct msgb *input) +{ + struct msgb *msg, *copy; + struct ipac_msgt_sccp_state *state; + struct bsc_nat_ussd_con *ussd; + uint16_t lac, ci; + + if (!con->bsc->nat->ussd_con) + return -1; + + msg = msgb_alloc_headroom(4096, 128, "forward ussd"); + if (!msg) { + LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); + return -1; + } + + copy = msgb_alloc_headroom(4096, 128, "forward bts"); + if (!copy) { + LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); + msgb_free(msg); + return -1; + } + + copy->l2h = msgb_put(copy, msgb_l2len(input)); + memcpy(copy->l2h, input->l2h, msgb_l2len(input)); + + msg->l2h = msgb_put(msg, 1); + msg->l2h[0] = IPAC_MSGT_SCCP_OLD; + + /* fill out the data */ + state = (struct ipac_msgt_sccp_state *) msgb_put(msg, sizeof(*state)); + state->trans_id = req->transaction_id; + state->invoke_id = req->invoke_id; + memcpy(&state->src_ref, &con->remote_ref, sizeof(con->remote_ref)); + memcpy(&state->dst_ref, &con->real_ref, sizeof(con->real_ref)); + memcpy(state->imsi, con->imsi, strlen(con->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_check_ussd(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_nat_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->con_type != NAT_CON_TYPE_SSA) + return 0; + + if (!con->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->imsi) > GSM_IMSI_LENGTH) + return 0; + + hdr48 = bsc_unpack_dtap(parsed, msg, &len); + if (!hdr48) + return 0; + + proto = hdr48->proto_discr & 0x0f; + msg_type = hdr48->msg_type & 0xbf; + ti = (hdr48->proto_discr & 0x70) >> 4; + if (proto != GSM48_PDISC_NC_SS) + return 0; + + if (msg_type == GSM0480_MTYPE_REGISTER) { + + /* now check if it is a IMSI we care about */ + lst = bsc_nat_acc_lst_find(con->bsc->nat, + con->bsc->nat->ussd_lst_name); + if (!lst) + return 0; + + if (bsc_nat_lst_check_allow(lst, con->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->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->imsi); + if (forward_ussd_simple(con, msg) != 0) + return 0; + return 1; + } + + return 0; +} diff --git a/src/osmo-nitb/Makefile.am b/src/osmo-nitb/Makefile.am new file mode 100644 index 000000000..ce2f25d27 --- /dev/null +++ b/src/osmo-nitb/Makefile.am @@ -0,0 +1,20 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(COVERAGE_CFLAGS) \ + $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) $(LIBSMPP34_CFLAGS) + +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +bin_PROGRAMS = osmo-nitb + +osmo_nitb_SOURCES = bsc_hack.c +osmo_nitb_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libctrl/libctrl.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + -ldbi -ldl $(LIBCRYPT) \ + $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOABIS_LIBS) $(LIBSMPP34_LIBS) diff --git a/src/osmo-nitb/bsc_hack.c b/src/osmo-nitb/bsc_hack.c new file mode 100644 index 000000000..172fb8269 --- /dev/null +++ b/src/osmo-nitb/bsc_hack.c @@ -0,0 +1,348 @@ +/* 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 "../../bscconfig.h" + +/* MCC and MNC for the Location Area Identifier */ +struct gsm_network *bsc_gsmnet = 0; +static struct osmo_bsc_rf *rf_ctrl; +static const char *database_name = "hlr.sqlite3"; +static const char *config_file = "openbsc.cfg"; +static const char *rf_ctrl_name = NULL; +extern const char *openbsc_copyright; +static int daemonize = 0; +static int use_mncc_sock = 0; +static int use_db_counter = 1; + +/* timer to store statistics */ +#define DB_SYNC_INTERVAL 60, 0 +#define EXPIRE_INTERVAL 10, 0 + +static struct osmo_timer_list db_sync_timer; + +static void create_pcap_file(char *file) +{ + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + int fd = open(file, O_WRONLY|O_TRUNC|O_CREAT, mode); + + if (fd < 0) { + perror("Failed to open file for pcap"); + return; + } + + e1_set_pcap_fd(fd); +} + +static void print_usage() +{ + printf("Usage: bsc_hack\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(" -p --pcap file The filename of the pcap file\n"); + printf(" -T --timestamp Prefix every log line with a timestamp\n"); + printf(" -V --version. Print the version of OpenBSC.\n"); + printf(" -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC\n"); + printf(" -e --log-level number. Set a global loglevel.\n"); + printf(" -m --mncc-sock Disable built-in MNCC handler and offer socket\n"); + printf(" -C --no-dbcounter Disable regular syncing of counters to database\n"); + printf(" -r --rf-ctl NAME. A unix domain socket to listen for cmds.\n"); +} + +static void handle_options(int argc, char **argv) +{ + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"debug", 1, 0, 'd'}, + {"daemonize", 0, 0, 'D'}, + {"config-file", 1, 0, 'c'}, + {"disable-color", 0, 0, 's'}, + {"database", 1, 0, 'l'}, + {"authorize-everyone", 0, 0, 'a'}, + {"pcap", 1, 0, 'p'}, + {"timestamp", 0, 0, 'T'}, + {"version", 0, 0, 'V' }, + {"rtp-proxy", 0, 0, 'P'}, + {"log-level", 1, 0, 'e'}, + {"mncc-sock", 0, 0, 'm'}, + {"no-dbcounter", 0, 0, 'C'}, + {"rf-ctl", 1, 0, 'r'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "hd:Dsl:ar:p:TPVc:e:mCr:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + print_help(); + exit(0); + case 's': + log_set_use_color(osmo_stderr_target, 0); + break; + case 'd': + log_parse_category_mask(osmo_stderr_target, optarg); + break; + case 'D': + daemonize = 1; + break; + case 'l': + database_name = strdup(optarg); + break; + case 'c': + config_file = strdup(optarg); + break; + case 'p': + create_pcap_file(optarg); + break; + case 'T': + log_set_print_timestamp(osmo_stderr_target, 1); + break; + case 'P': + ipacc_rtp_direct = 0; + break; + case 'e': + log_set_log_level(osmo_stderr_target, atoi(optarg)); + break; + case 'm': + use_mncc_sock = 1; + break; + case 'C': + use_db_counter = 0; + break; + case 'V': + print_version(1); + exit(0); + break; + case 'r': + rf_ctrl_name = optarg; + break; + default: + /* ignore */ + break; + } + } +} + +extern void *tall_vty_ctx; +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + bsc_shutdown_net(bsc_gsmnet); + osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); + sleep(3); + exit(0); + break; + case SIGABRT: + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + talloc_report(tall_vty_ctx, stderr); + talloc_report_full(tall_bsc_ctx, stderr); + break; + case SIGUSR2: + talloc_report_full(tall_vty_ctx, stderr); + break; + default: + break; + } +} + +/* timer handling */ +static int _db_store_counter(struct osmo_counter *counter, void *data) +{ + return db_store_counter(counter); +} + +static void db_sync_timer_cb(void *data) +{ + /* store counters to database and re-schedule */ + osmo_counters_for_each(_db_store_counter, NULL); + osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); +} + +static void subscr_expire_cb(void *data) +{ + subscr_expire(bsc_gsmnet); + osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL); +} + +void talloc_ctx_init(void); + +extern enum node_type bsc_vty_go_parent(struct vty *vty); + +static struct vty_app_info vty_info = { + .name = "OpenBSC", + .version = PACKAGE_VERSION, + .go_parent_cb = bsc_vty_go_parent, + .is_config_node = bsc_vty_is_config_node, +}; + +int main(int argc, char **argv) +{ + int rc; + + vty_info.copyright = openbsc_copyright; + + tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); + talloc_ctx_init(); + on_dso_load_token(); + on_dso_load_rrlp(); + on_dso_load_ho_dec(); + + libosmo_abis_init(tall_bsc_ctx); + osmo_init_logging(&log_info); + bts_init(); + + /* This needs to precede handle_options() */ + vty_init(&vty_info); + bsc_vty_init(&log_info); + +#ifdef BUILD_SMPP + if (smpp_openbsc_init(tall_bsc_ctx, 0) < 0) + return -1; +#endif + + /* parse options */ + handle_options(argc, argv); + + /* internal MNCC handler or MNCC socket? */ + if (use_mncc_sock) { + rc = bsc_bootstrap_network(mncc_sock_from_cc, config_file); + if (rc >= 0) + mncc_sock_init(bsc_gsmnet); + } else + rc = bsc_bootstrap_network(int_mncc_recv, config_file); + if (rc < 0) + exit(1); +#ifdef BUILD_SMPP + smpp_openbsc_set_net(bsc_gsmnet); +#endif + bsc_api_init(bsc_gsmnet, msc_bsc_api()); + + bsc_gsmnet->ctrl = controlif_setup(bsc_gsmnet, 4249); + if (!bsc_gsmnet->ctrl) { + printf("Failed to initialize control interface. Exiting.\n"); + return -1; + } + + /* seed the PRNG */ + srand(time(NULL)); + + if (rf_ctrl_name) { + rf_ctrl = osmo_bsc_rf_create(rf_ctrl_name, bsc_gsmnet); + if (!rf_ctrl) { + fprintf(stderr, "Failed to create the RF service.\n"); + exit(1); + } + } + + if (db_init(database_name)) { + printf("DB: Failed to init database. Please check the option settings.\n"); + return -1; + } + printf("DB: Database initialized.\n"); + + if (db_prepare()) { + printf("DB: Failed to prepare database.\n"); + return -1; + } + printf("DB: Database prepared.\n"); + + /* setup the timer */ + db_sync_timer.cb = db_sync_timer_cb; + db_sync_timer.data = NULL; + if (use_db_counter) + osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); + + bsc_gsmnet->subscr_expire_timer.cb = subscr_expire_cb; + bsc_gsmnet->subscr_expire_timer.data = NULL; + osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL); + + signal(SIGINT, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); + osmo_init_ignore_signals(); + + /* start the SMS queue */ + if (sms_queue_start(bsc_gsmnet, 20) != 0) + return -1; + + if (daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + perror("Error during daemonize"); + exit(1); + } + } + + while (1) { + log_reset_context(); + osmo_select_main(0); + } +} diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am new file mode 100644 index 000000000..fd1f7aebf --- /dev/null +++ b/src/utils/Makefile.am @@ -0,0 +1,21 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +bin_PROGRAMS = bs11_config isdnsync + +if BUILD_SMPP +noinst_PROGRAMS = smpp_mirror +endif + +bs11_config_SOURCES = bs11_config.c +bs11_config_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOABIS_LIBS) + +isdnsync_SOURCES = isdnsync.c + +smpp_mirror_SOURCES = smpp_mirror.c +smpp_mirror_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) $(LIBSMPP34_LIBS) diff --git a/src/utils/bs11_config.c b/src/utils/bs11_config.c new file mode 100644 index 000000000..e8acb461a --- /dev/null +++ b/src/utils/bs11_config.c @@ -0,0 +1,949 @@ +/* Siemens BS-11 microBTS configuration tool */ + +/* (C) 2009-2010 by Harald Welte + * All Rights Reserved + * + * This software is based on ideas (but not code) of BS11Config + * (C) 2009 by Dieter Spaar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void *tall_bs11cfg_ctx; +static struct e1inp_sign_link *oml_link; + +/* state of our bs11_config application */ +enum bs11cfg_state { + STATE_NONE, + STATE_LOGON_WAIT, + STATE_LOGON_ACK, + STATE_SWLOAD, + STATE_QUERY, +}; +static enum bs11cfg_state bs11cfg_state = STATE_NONE; +static char *command, *value; +struct osmo_timer_list status_timer; + +static const uint8_t obj_li_attr[] = { + NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00, + NM_ATT_BS11_L1_PROT_TYPE, 0x00, + NM_ATT_BS11_LINE_CFG, 0x00, +}; +static const uint8_t obj_bbsig0_attr[] = { + NM_ATT_BS11_RSSI_OFFS, 0x02, 0x00, 0x00, + NM_ATT_BS11_DIVERSITY, 0x01, 0x00, +}; +static const uint8_t obj_pa0_attr[] = { + NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_GSM_30mW, +}; +static const char *trx1_password = "1111111111"; +#define TEI_OML 25 + +static const uint8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 }; + +/* dummy function to keep gsm_data.c happy */ +struct osmo_counter *osmo_counter_alloc(const char *name) +{ + return NULL; +} + +int handle_serial_msg(struct msgb *rx_msg); + +/* create all objects for an initial configuration */ +static int create_objects(struct gsm_bts *bts) +{ + fprintf(stdout, "Crating Objects for minimal config\n"); + abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr), + obj_li_attr); + abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL); + abis_nm_bs11_create_object(bts, BS11_OBJ_ALCO, 0, 0, NULL); + abis_nm_bs11_create_object(bts, BS11_OBJ_CCLK, 0, 0, NULL); + abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 0, + sizeof(obj_bbsig0_attr), obj_bbsig0_attr); + abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 0, + sizeof(obj_pa0_attr), obj_pa0_attr); + abis_nm_bs11_create_envaBTSE(bts, 0); + abis_nm_bs11_create_envaBTSE(bts, 1); + abis_nm_bs11_create_envaBTSE(bts, 2); + abis_nm_bs11_create_envaBTSE(bts, 3); + + abis_nm_bs11_conn_oml_tei(bts, 0, 1, 0xff, TEI_OML); + + abis_nm_bs11_set_trx_power(bts->c0, BS11_TRX_POWER_GSM_30mW); + + sleep(1); + + abis_nm_bs11_set_trx1_pw(bts, trx1_password); + + sleep(1); + + return 0; +} + +static int create_trx1(struct gsm_bts *bts) +{ + uint8_t bbsig1_attr[sizeof(obj_bbsig0_attr)+12]; + uint8_t *cur = bbsig1_attr; + struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, 1); + + if (!trx) + trx = gsm_bts_trx_alloc(bts); + + fprintf(stdout, "Crating Objects for TRX1\n"); + + abis_nm_bs11_set_trx1_pw(bts, trx1_password); + + sleep(1); + + cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10, + (uint8_t *)trx1_password); + memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr)); + abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 1, + sizeof(bbsig1_attr), bbsig1_attr); + abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 1, + sizeof(obj_pa0_attr), obj_pa0_attr); + abis_nm_bs11_set_trx_power(trx, BS11_TRX_POWER_GSM_30mW); + + return 0; +} + +static char *serial_port = "/dev/ttyUSB0"; +static char *fname_safety = "BTSBMC76.SWI"; +static char *fname_software = "HS011106.SWL"; +static int delay_ms = 0; +static int win_size = 8; +static int param_disconnect = 0; +static int param_restart = 0; +static int param_forced = 0; +static struct gsm_bts *g_bts; + +static int file_is_readable(const char *fname) +{ + int rc; + struct stat st; + + rc = stat(fname, &st); + if (rc < 0) + return 0; + + if (S_ISREG(st.st_mode) && (st.st_mode & S_IRUSR)) + return 1; + + return 0; +} + +static int percent; +static int percent_old; + +/* callback function passed to the ABIS OML code */ +static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg, + void *data, void *param) +{ + if (hook != GSM_HOOK_NM_SWLOAD) + return 0; + + switch (event) { + case NM_MT_LOAD_INIT_ACK: + fprintf(stdout, "Software Load Initiate ACK\n"); + break; + case NM_MT_LOAD_INIT_NACK: + fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); + exit(5); + break; + case NM_MT_LOAD_END_ACK: + if (data) { + /* we did a safety load and must activate it */ + abis_nm_software_activate(g_bts, fname_safety, + swload_cbfn, g_bts); + sleep(5); + } + break; + case NM_MT_LOAD_END_NACK: + fprintf(stderr, "ERROR: Software Load End NACK\n"); + exit(3); + break; + case NM_MT_ACTIVATE_SW_NACK: + fprintf(stderr, "ERROR: Activate Software NACK\n"); + exit(4); + break; + case NM_MT_ACTIVATE_SW_ACK: + bs11cfg_state = STATE_NONE; + + break; + case NM_MT_LOAD_SEG_ACK: + percent = abis_nm_software_load_status(g_bts); + if (percent > percent_old) + printf("Software Download Progress: %d%%\n", percent); + percent_old = percent; + break; + } + return 0; +} + +static const struct value_string bs11_linkst_names[] = { + { 0, "Down" }, + { 1, "Up" }, + { 2, "Restoring" }, + { 0, NULL } +}; + +static const char *linkstate_name(uint8_t linkstate) +{ + return get_value_string(bs11_linkst_names, linkstate); +} + +static const struct value_string mbccu_load_names[] = { + { 0, "No Load" }, + { 1, "Load BTSCAC" }, + { 2, "Load BTSDRX" }, + { 3, "Load BTSBBX" }, + { 4, "Load BTSARC" }, + { 5, "Load" }, + { 0, NULL } +}; + +static const char *mbccu_load_name(uint8_t linkstate) +{ + return get_value_string(mbccu_load_names, linkstate); +} + +static const char *bts_phase_name(uint8_t phase) +{ + switch (phase) { + case BS11_STATE_WARM_UP: + case BS11_STATE_WARM_UP_2: + return "Warm Up"; + break; + case BS11_STATE_LOAD_SMU_SAFETY: + return "Load SMU Safety"; + break; + case BS11_STATE_LOAD_SMU_INTENDED: + return "Load SMU Intended"; + break; + case BS11_STATE_LOAD_MBCCU: + return "Load MBCCU"; + break; + case BS11_STATE_SOFTWARE_RQD: + return "Software required"; + break; + case BS11_STATE_WAIT_MIN_CFG: + case BS11_STATE_WAIT_MIN_CFG_2: + return "Wait minimal config"; + break; + case BS11_STATE_MAINTENANCE: + return "Maintenance"; + break; + case BS11_STATE_NORMAL: + return "Normal"; + break; + case BS11_STATE_ABIS_LOAD: + return "Abis load"; + break; + default: + return "Unknown"; + break; + } +} + +static const char *trx_power_name(uint8_t pwr) +{ + switch (pwr) { + case BS11_TRX_POWER_GSM_2W: + return "2W (GSM)"; + case BS11_TRX_POWER_GSM_250mW: + return "250mW (GSM)"; + case BS11_TRX_POWER_GSM_80mW: + return "80mW (GSM)"; + case BS11_TRX_POWER_GSM_30mW: + return "30mW (GSM)"; + case BS11_TRX_POWER_DCS_3W: + return "3W (DCS)"; + case BS11_TRX_POWER_DCS_1W6: + return "1.6W (DCS)"; + case BS11_TRX_POWER_DCS_500mW: + return "500mW (DCS)"; + case BS11_TRX_POWER_DCS_160mW: + return "160mW (DCS)"; + default: + return "unknown value"; + } +} + +static const char *pll_mode_name(uint8_t mode) +{ + switch (mode) { + case BS11_LI_PLL_LOCKED: + return "E1 Locked"; + case BS11_LI_PLL_STANDALONE: + return "Standalone"; + default: + return "unknown"; + } +} + +static const char *cclk_acc_name(uint8_t acc) +{ + switch (acc) { + case 0: + /* Out of the demanded +/- 0.05ppm */ + return "Medium"; + case 1: + /* Synchronized with Abis, within demanded tolerance +/- 0.05ppm */ + return "High"; + default: + return "unknown"; + } +} + +static const char *bport_lcfg_name(uint8_t lcfg) +{ + switch (lcfg) { + case BS11_LINE_CFG_STAR: + return "Star"; + case BS11_LINE_CFG_MULTIDROP: + return "Multi-Drop"; + default: + return "unknown"; + } +} + +static const char *obj_name(struct abis_om_fom_hdr *foh) +{ + static char retbuf[256]; + + retbuf[0] = 0; + + switch (foh->obj_class) { + case NM_OC_BS11: + strcat(retbuf, "BS11 "); + switch (foh->obj_inst.bts_nr) { + case BS11_OBJ_PA: + sprintf(retbuf+strlen(retbuf), "Power Amplifier %d ", + foh->obj_inst.ts_nr); + break; + case BS11_OBJ_LI: + sprintf(retbuf+strlen(retbuf), "Line Interface "); + break; + case BS11_OBJ_CCLK: + sprintf(retbuf+strlen(retbuf), "CCLK "); + break; + } + break; + case NM_OC_SITE_MANAGER: + strcat(retbuf, "SITE MANAGER "); + break; + case NM_OC_BS11_BPORT: + sprintf(retbuf+strlen(retbuf), "BPORT%u ", + foh->obj_inst.bts_nr); + break; + } + return retbuf; +} + +static void print_state(struct tlv_parsed *tp) +{ + if (TLVP_PRESENT(tp, NM_ATT_BS11_BTS_STATE)) { + uint8_t phase, mbccu; + if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 1) { + phase = *TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE); + printf("PHASE: %u %-20s ", phase & 0xf, + bts_phase_name(phase)); + } + if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 2) { + mbccu = *(TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE)+1); + printf("MBCCU0: %-11s MBCCU1: %-11s ", + mbccu_load_name(mbccu & 0xf), mbccu_load_name(mbccu >> 4)); + } + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_E1_STATE) && + TLVP_LEN(tp, NM_ATT_BS11_E1_STATE) >= 1) { + uint8_t e1_state = *TLVP_VAL(tp, NM_ATT_BS11_E1_STATE); + printf("Abis-link: %-9s ", linkstate_name(e1_state & 0xf)); + } + printf("\n"); +} + +static int print_attr(struct tlv_parsed *tp) +{ + if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_PCB_SERIAL)) { + printf("\tBS-11 ESN PCB Serial Number: %s\n", + TLVP_VAL(tp, NM_ATT_BS11_ESN_PCB_SERIAL)); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_HW_CODE_NO)) { + printf("\tBS-11 ESN Hardware Code Number: %s\n", + TLVP_VAL(tp, NM_ATT_BS11_ESN_HW_CODE_NO)+6); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_FW_CODE_NO)) { + printf("\tBS-11 ESN Firmware Code Number: %s\n", + TLVP_VAL(tp, NM_ATT_BS11_ESN_FW_CODE_NO)+6); + } +#if 0 + if (TLVP_PRESENT(tp, NM_ATT_BS11_BOOT_SW_VERS)) { + printf("BS-11 Boot Software Version: %s\n", + TLVP_VAL(tp, NM_ATT_BS11_BOOT_SW_VERS)+6); + } +#endif + if (TLVP_PRESENT(tp, NM_ATT_ABIS_CHANNEL) && + TLVP_LEN(tp, NM_ATT_ABIS_CHANNEL) >= 3) { + const uint8_t *chan = TLVP_VAL(tp, NM_ATT_ABIS_CHANNEL); + printf("\tE1 Channel: Port=%u Timeslot=%u ", + chan[0], chan[1]); + if (chan[2] == 0xff) + printf("(Full Slot)\n"); + else + printf("Subslot=%u\n", chan[2]); + } + if (TLVP_PRESENT(tp, NM_ATT_TEI)) + printf("\tTEI: %d\n", *TLVP_VAL(tp, NM_ATT_TEI)); + if (TLVP_PRESENT(tp, NM_ATT_BS11_TXPWR) && + TLVP_LEN(tp, NM_ATT_BS11_TXPWR) >= 1) { + printf("\tTRX Power: %s\n", + trx_power_name(*TLVP_VAL(tp, NM_ATT_BS11_TXPWR))); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL_MODE) && + TLVP_LEN(tp, NM_ATT_BS11_PLL_MODE) >= 1) { + printf("\tPLL Mode: %s\n", + pll_mode_name(*TLVP_VAL(tp, NM_ATT_BS11_PLL_MODE))); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL) && + TLVP_LEN(tp, NM_ATT_BS11_PLL) >= 4) { + const uint8_t *vp = TLVP_VAL(tp, NM_ATT_BS11_PLL); + printf("\tPLL Set Value=%d, Work Value=%d\n", + vp[0] << 8 | vp[1], vp[2] << 8 | vp[3]); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_ACCURACY) && + TLVP_LEN(tp, NM_ATT_BS11_CCLK_ACCURACY) >= 1) { + const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_ACCURACY); + printf("\tCCLK Accuracy: %s (%d)\n", cclk_acc_name(*acc), *acc); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_TYPE) && + TLVP_LEN(tp, NM_ATT_BS11_CCLK_TYPE) >= 1) { + const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_TYPE); + printf("\tCCLK Type=%d\n", *acc); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_LINE_CFG) && + TLVP_LEN(tp, NM_ATT_BS11_LINE_CFG) >= 1) { + const uint8_t *lcfg = TLVP_VAL(tp, NM_ATT_BS11_LINE_CFG); + printf("\tLine Configuration: %s (%d)\n", + bport_lcfg_name(*lcfg), *lcfg); + } + + + + return 0; +} + +static void cmd_query(void) +{ + struct gsm_bts_trx *trx = g_bts->c0; + + bs11cfg_state = STATE_QUERY; + abis_nm_bs11_get_serno(g_bts); + abis_nm_bs11_get_oml_tei_ts(g_bts); + abis_nm_bs11_get_pll_mode(g_bts); + abis_nm_bs11_get_cclk(g_bts); + abis_nm_bs11_get_trx_power(trx); + trx = gsm_bts_trx_num(g_bts, 1); + if (trx) + abis_nm_bs11_get_trx_power(trx); + abis_nm_bs11_get_bport_line_cfg(g_bts, 0); + abis_nm_bs11_get_bport_line_cfg(g_bts, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; +} + +/* handle a response from the BTS to a GET STATE command */ +static int handle_state_resp(enum abis_bs11_phase state) +{ + int rc = 0; + + switch (state) { + case BS11_STATE_WARM_UP: + case BS11_STATE_LOAD_SMU_SAFETY: + case BS11_STATE_LOAD_SMU_INTENDED: + case BS11_STATE_LOAD_MBCCU: + break; + case BS11_STATE_SOFTWARE_RQD: + bs11cfg_state = STATE_SWLOAD; + /* send safety load. Use g_bts as private 'param' + * argument, so our swload_cbfn can distinguish + * a safety load from a regular software */ + if (file_is_readable(fname_safety)) + rc = abis_nm_software_load(g_bts, 0xff, fname_safety, + win_size, param_forced, + swload_cbfn, g_bts); + else + fprintf(stderr, "No valid Safety Load file \"%s\"\n", + fname_safety); + break; + case BS11_STATE_WAIT_MIN_CFG: + case BS11_STATE_WAIT_MIN_CFG_2: + bs11cfg_state = STATE_SWLOAD; + rc = create_objects(g_bts); + break; + case BS11_STATE_MAINTENANCE: + if (command) { + if (!strcmp(command, "disconnect")) + abis_nm_bs11_factory_logon(g_bts, 0); + else if (!strcmp(command, "reconnect")) + rc = abis_nm_bs11_bsc_disconnect(g_bts, 1); + else if (!strcmp(command, "software") + && bs11cfg_state != STATE_SWLOAD) { + bs11cfg_state = STATE_SWLOAD; + /* send software (FIXME: over A-bis?) */ + if (file_is_readable(fname_software)) + rc = abis_nm_bs11_load_swl(g_bts, fname_software, + win_size, param_forced, + swload_cbfn); + else + fprintf(stderr, "No valid Software file \"%s\"\n", + fname_software); + } else if (!strcmp(command, "delete-trx1")) { + printf("Locing BBSIG and PA objects of TRX1\n"); + abis_nm_chg_adm_state(g_bts, NM_OC_BS11, + BS11_OBJ_BBSIG, 0, 1, + NM_STATE_LOCKED); + abis_nm_chg_adm_state(g_bts, NM_OC_BS11, + BS11_OBJ_PA, 0, 1, + NM_STATE_LOCKED); + sleep(1); + printf("Deleting BBSIG and PA objects of TRX1\n"); + abis_nm_bs11_delete_object(g_bts, BS11_OBJ_BBSIG, 1); + abis_nm_bs11_delete_object(g_bts, BS11_OBJ_PA, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "create-trx1")) { + create_trx1(g_bts); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-e1-locked")) { + abis_nm_bs11_set_pll_locked(g_bts, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-standalone")) { + abis_nm_bs11_set_pll_locked(g_bts, 0); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-setvalue")) { + abis_nm_bs11_set_pll(g_bts, atoi(value)); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-workvalue")) { + /* To set the work value we need to login as FIELD */ + abis_nm_bs11_factory_logon(g_bts, 0); + sleep(1); + abis_nm_bs11_infield_logon(g_bts, 1); + sleep(1); + abis_nm_bs11_set_pll(g_bts, atoi(value)); + sleep(1); + abis_nm_bs11_infield_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "oml-tei")) { + abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML); + command = NULL; + } else if (!strcmp(command, "restart")) { + abis_nm_bs11_restart(g_bts); + command = NULL; + } else if (!strcmp(command, "query")) { + cmd_query(); + } else if (!strcmp(command, "create-bport1")) { + abis_nm_bs11_create_bport(g_bts, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "delete-bport1")) { + abis_nm_chg_adm_state(g_bts, NM_OC_BS11_BPORT, 1, 0xff, 0xff, NM_STATE_LOCKED); + sleep(1); + abis_nm_bs11_delete_bport(g_bts, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "bport0-star")) { + abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_STAR); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "bport0-multidrop")) { + abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_MULTIDROP); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "bport1-multidrop")) { + abis_nm_bs11_set_bport_line_cfg(g_bts, 1, BS11_LINE_CFG_MULTIDROP); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } + + } + break; + case BS11_STATE_NORMAL: + if (command) { + if (!strcmp(command, "reconnect")) + abis_nm_bs11_factory_logon(g_bts, 0); + else if (!strcmp(command, "disconnect")) + abis_nm_bs11_bsc_disconnect(g_bts, 0); + else if (!strcmp(command, "query")) { + cmd_query(); + } + } else if (param_disconnect) { + param_disconnect = 0; + abis_nm_bs11_bsc_disconnect(g_bts, 0); + if (param_restart) { + param_restart = 0; + abis_nm_bs11_restart(g_bts); + } + } + break; + default: + break; + } + return rc; +} + +/* handle a fully-received message/packet from the RS232 port */ +static int abis_nm_bs11cfg_rcvmsg(struct msgb *rx_msg) +{ + struct e1inp_sign_link *link = rx_msg->dst; + struct abis_om_hdr *oh; + struct abis_om_fom_hdr *foh; + struct tlv_parsed tp; + int rc = -1; + +#if 0 + if (rx_msg->len < LAPD_HDR_LEN + + sizeof(struct abis_om_fom_hdr) + + sizeof(struct abis_om_hdr)) { + if (!memcmp(rx_msg->data + 2, too_fast, + sizeof(too_fast))) { + fprintf(stderr, "BS11 tells us we're too " + "fast, try --delay bigger than %u\n", + delay_ms); + return -E2BIG; + } else + fprintf(stderr, "unknown BS11 message\n"); + } +#endif + + oh = (struct abis_om_hdr *) msgb_l2(rx_msg); + foh = (struct abis_om_fom_hdr *) oh->data; + switch (foh->msg_type) { + case NM_MT_BS11_LMT_LOGON_ACK: + printf("LMT LOGON: ACK\n\n"); + if (bs11cfg_state == STATE_NONE) + bs11cfg_state = STATE_LOGON_ACK; + rc = abis_nm_bs11_get_state(g_bts); + break; + case NM_MT_BS11_LMT_LOGOFF_ACK: + printf("LMT LOGOFF: ACK\n"); + exit(0); + break; + case NM_MT_BS11_GET_STATE_ACK: + rc = abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); + print_state(&tp); + if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) && + TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1) + rc = handle_state_resp(*TLVP_VAL(&tp, NM_ATT_BS11_BTS_STATE)); + break; + case NM_MT_GET_ATTR_RESP: + printf("\n%sATTRIBUTES:\n", obj_name(foh)); + abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); + rc = print_attr(&tp); + //osmo_hexdump(foh->data, oh->length-sizeof(*foh)); + break; + case NM_MT_BS11_SET_ATTR_ACK: + printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) ACK\n", + foh->obj_class, foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); + rc = 0; + break; + case NM_MT_BS11_SET_ATTR_NACK: + printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) NACK\n", + foh->obj_class, foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); + break; + case NM_MT_GET_ATTR_NACK: + printf("\n%sGET ATTR NACK\n", obj_name(foh)); + break; + case NM_MT_BS11_CREATE_OBJ_ACK: + printf("\n%sCREATE OBJECT ACK\n", obj_name(foh)); + break; + case NM_MT_BS11_CREATE_OBJ_NACK: + printf("\n%sCREATE OBJECT NACK\n", obj_name(foh)); + break; + case NM_MT_BS11_DELETE_OBJ_ACK: + printf("\n%sDELETE OBJECT ACK\n", obj_name(foh)); + break; + case NM_MT_BS11_DELETE_OBJ_NACK: + printf("\n%sDELETE OBJECT NACK\n", obj_name(foh)); + break; + default: + rc = abis_nm_rcvmsg(rx_msg); + } + if (rc < 0) { + perror("ERROR in main loop"); + //break; + } + /* flush the queue of pending messages to be sent. */ + abis_nm_queue_send_next(link->trx->bts); + if (rc == 1) + return rc; + + switch (bs11cfg_state) { + case STATE_NONE: + abis_nm_bs11_factory_logon(g_bts, 1); + break; + case STATE_LOGON_ACK: + osmo_timer_schedule(&status_timer, 5, 0); + break; + default: + break; + } + + return rc; +} + +void status_timer_cb(void *data) +{ + abis_nm_bs11_get_state(g_bts); +} + +static void print_banner(void) +{ + printf("bs11_config (C) 2009-2010 by Harald Welte and Dieter Spaar\n"); + printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); +} + +static void print_help(void) +{ + printf("bs11_config [options] [command]\n"); + printf("\nSupported options:\n"); + printf("\t-h --help\t\t\tPrint this help text\n"); + printf("\t-p --port \t\tSpecify serial port\n"); + printf("\t-s --software \t\tSpecify Software file\n"); + printf("\t-S --safety \t\tSpecify Safety Load file\n"); + printf("\t-d --delay \t\t\tSpecify delay in milliseconds\n"); + printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n"); + printf("\t-w --win-size \t\tSpecify Window Size\n"); + printf("\t-f --forced\t\t\tForce Software Load\n"); + printf("\nSupported commands:\n"); + printf("\tquery\t\t\tQuery the BS-11 about serial number and configuration\n"); + printf("\tdisconnect\t\tDisconnect A-bis link (go into administrative state)\n"); + printf("\tresconnect\t\tReconnect A-bis link (go into normal state)\n"); + printf("\trestart\t\t\tRestart the BTS\n"); + printf("\tsoftware\t\tDownload Software (only in administrative state)\n"); + printf("\tcreate-trx1\t\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n"); + printf("\tdelete-trx1\t\tDelete objects for TRX1\n"); + printf("\tpll-e1-locked\t\tSet the PLL to be locked to E1 clock\n"); + printf("\tpll-standalone\t\tSet the PLL to be in standalone mode\n"); + printf("\tpll-setvalue \tSet the PLL set value\n"); + printf("\tpll-workvalue \tSet the PLL work value\n"); + printf("\toml-tei\t\t\tSet OML E1 TS and TEI\n"); + printf("\tbport0-star\t\tSet BPORT0 line config to star\n"); + printf("\tbport0-multidrop\tSet BPORT0 line config to multidrop\n"); + printf("\tbport1-multidrop\tSet BPORT1 line config to multidrop\n"); + printf("\tcreate-bport1\t\tCreate BPORT1 object\n"); + printf("\tdelete-bport1\t\tDelete BPORT1 object\n"); +} + +static void handle_options(int argc, char **argv) +{ + int option_index = 0; + print_banner(); + + while (1) { + int c; + static struct option long_options[] = { + { "help", 0, 0, 'h' }, + { "port", 1, 0, 'p' }, + { "software", 1, 0, 's' }, + { "safety", 1, 0, 'S' }, + { "delay", 1, 0, 'd' }, + { "disconnect", 0, 0, 'D' }, + { "win-size", 1, 0, 'w' }, + { "forced", 0, 0, 'f' }, + { "restart", 0, 0, 'r' }, + { "debug", 1, 0, 'b'}, + }; + + c = getopt_long(argc, argv, "hp:s:S:td:Dw:fra:", + long_options, &option_index); + + if (c == -1) + break; + + switch (c) { + case 'h': + print_help(); + exit(0); + case 'p': + serial_port = optarg; + break; + case 'b': + log_parse_category_mask(osmo_stderr_target, optarg); + break; + case 's': + fname_software = optarg; + break; + case 'S': + fname_safety = optarg; + break; + case 'd': + delay_ms = atoi(optarg); + break; + case 'w': + win_size = atoi(optarg); + break; + case 'D': + param_disconnect = 1; + break; + case 'f': + param_forced = 1; + break; + case 'r': + param_disconnect = 1; + param_restart = 1; + break; + default: + break; + } + } + if (optind < argc) + command = argv[optind]; + if (optind+1 < argc) + value = argv[optind+1]; + +} + +static int num_sigint; + +static void signal_handler(int signal) +{ + fprintf(stdout, "\nsignal %u received\n", signal); + + switch (signal) { + case SIGINT: + num_sigint++; + abis_nm_bs11_factory_logon(g_bts, 0); + if (num_sigint >= 3) + exit(0); + break; + } +} + +static int bs11cfg_sign_link(struct msgb *msg) +{ + msg->dst = oml_link; + return abis_nm_bs11cfg_rcvmsg(msg); +} + +struct e1inp_line_ops bs11cfg_e1inp_line_ops = { + .sign_link = bs11cfg_sign_link, +}; + +extern int bts_model_bs11_init(void); +int main(int argc, char **argv) +{ + struct gsm_network *gsmnet; + struct e1inp_line *line; + + osmo_init_logging(&log_info); + handle_options(argc, argv); + bts_model_bs11_init(); + + gsmnet = gsm_network_init(1, 1, NULL); + if (!gsmnet) { + fprintf(stderr, "Unable to allocate gsm network\n"); + exit(1); + } + g_bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_BS11, HARDCODED_TSC, + HARDCODED_BSIC); + + /* Override existing OML callback handler to set our own. */ + g_bts->model->oml_rcvmsg = abis_nm_bs11cfg_rcvmsg; + + libosmo_abis_init(tall_bs11cfg_ctx); + + /* Initialize virtual E1 line over rs232. */ + line = talloc_zero(tall_bs11cfg_ctx, struct e1inp_line); + if (!line) { + fprintf(stderr, "Unable to allocate memory for virtual E1 line\n"); + exit(1); + } + /* set the serial port. */ + bs11cfg_e1inp_line_ops.cfg.rs232.port = serial_port; + bs11cfg_e1inp_line_ops.cfg.rs232.delay = delay_ms; + + line->driver = e1inp_driver_find("rs232"); + if (!line->driver) { + fprintf(stderr, "cannot find `rs232' driver, giving up.\n"); + exit(1); + } + e1inp_line_bind_ops(line, &bs11cfg_e1inp_line_ops); + + /* configure and create signalling link for OML. */ + e1inp_ts_config_sign(&line->ts[0], line); + g_bts->oml_link = oml_link = + e1inp_sign_link_create(&line->ts[0], E1INP_SIGN_OML, + g_bts->c0, TEI_OML, 0); + + e1inp_line_update(line); + + signal(SIGINT, &signal_handler); + + abis_nm_bs11_factory_logon(g_bts, 1); + //abis_nm_bs11_get_serno(g_bts); + + status_timer.cb = status_timer_cb; + + while (1) { + if (osmo_select_main(0) < 0) + break; + } + + abis_nm_bs11_factory_logon(g_bts, 0); + + exit(0); +} diff --git a/src/utils/isdnsync.c b/src/utils/isdnsync.c new file mode 100644 index 000000000..cc8ff6723 --- /dev/null +++ b/src/utils/isdnsync.c @@ -0,0 +1,189 @@ +/* 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/src/utils/smpp_mirror.c b/src/utils/smpp_mirror.c new file mode 100644 index 000000000..59e341d38 --- /dev/null +++ b/src/utils/smpp_mirror.c @@ -0,0 +1,317 @@ +#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); + + return osmo_wqueue_enqueue(&esme->wqueue, msg); +} +/* 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: + 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 void 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; + } +} + +static int smpp_esme_init(struct esme *esme, const char *host, uint16_t port) +{ + int rc; + + if (port == 0) + port = 2775; + + esme->own_seq_nr = rand(); + esme_inc_seq_nr(esme); + osmo_wqueue_init(&esme->wqueue, 10); + esme->wqueue.bfd.data = esme; + esme->wqueue.read_cb = esme_read_cb; + esme->wqueue.write_cb = esme_write_cb; + + rc = osmo_sock_init_ofd(&esme->wqueue.bfd, AF_UNSPEC, SOCK_STREAM, + IPPROTO_TCP, host, port, OSMO_SOCK_F_CONNECT); + if (rc < 0) + return rc; + + return bind_transceiver(esme); +} + + +int main(int argc, char **argv) +{ + struct esme esme; + char *host = "localhost"; + int port = 0; + int rc; + + memset(&esme, 0, sizeof(esme)); + + osmo_init_logging(&log_info); + + snprintf((char *) esme.system_id, sizeof(esme.system_id), "mirror"); + snprintf((char *) esme.password, sizeof(esme.password), "mirror"); + esme.smpp_version = 0x34; + + if (argc >= 2) + host = argv[1]; + if (argc >= 3) + port = atoi(argv[2]); + + rc = smpp_esme_init(&esme, host, port); + if (rc < 0) + exit(1); + + while (1) { + osmo_select_main(0); + } + + exit(0); +} diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 000000000..c9afeedd4 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,61 @@ +SUBDIRS = gsm0408 db channel mgcp gprs si abis + +if BUILD_NAT +SUBDIRS += bsc-nat +endif + +if BUILD_SMPP +SUBDIRS += smpp +endif + + +# The `:;' works around a Bash 3.2 bug when the output is not writeable. +$(srcdir)/package.m4: $(top_srcdir)/configure.ac + :;{ \ + echo '# Signature of the current package.' && \ + echo 'm4_define([AT_PACKAGE_NAME],' && \ + echo ' [$(PACKAGE_NAME)])' && \ + echo 'm4_define([AT_PACKAGE_TARNAME],' && \ + echo ' [$(PACKAGE_TARNAME)])' && \ + echo 'm4_define([AT_PACKAGE_VERSION],' && \ + echo ' [$(PACKAGE_VERSION)])' && \ + echo 'm4_define([AT_PACKAGE_STRING],' && \ + echo ' [$(PACKAGE_STRING)])' && \ + echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \ + echo ' [$(PACKAGE_BUGREPORT)])'; \ + echo 'm4_define([AT_PACKAGE_URL],' && \ + echo ' [$(PACKAGE_URL)])'; \ + } >'$(srcdir)/package.m4' + +EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) vty_test_runner.py +TESTSUITE = $(srcdir)/testsuite +DISTCLEANFILES = atconfig + +if ENABLE_VTY_TESTS +python-tests: $(BUILT_SOURCES) + osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v + osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v + $(PYTHON) $(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v + rm -f $(top_builddir)/hlr.sqlite3 +else +python-tests: $(BUILT_SOURCES) + echo "Not running python-based tests (determined at configure-time)" +endif + +check-local: atconfig $(TESTSUITE) + $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) + $(MAKE) $(AM_MAKEFLAGS) python-tests + +installcheck-local: atconfig $(TESTSUITE) + $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \ + $(TESTSUITEFLAGS) + +clean-local: + test ! -f '$(TESTSUITE)' || \ + $(SHELL) '$(TESTSUITE)' --clean + +AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te +AUTOTEST = $(AUTOM4TE) --language=autotest +$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4 + $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at + mv $@.tmp $@ diff --git a/tests/abis/Makefile.am b/tests/abis/Makefile.am new file mode 100644 index 000000000..c2e38deb8 --- /dev/null +++ b/tests/abis/Makefile.am @@ -0,0 +1,17 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOABIS_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) $(COVERAGE_CFLAGS) + +EXTRA_DIST = abis_test.ok + +noinst_PROGRAMS = abis_test + +abis_test_SOURCES = abis_test.c + +abis_test_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOGSM_LIBS) diff --git a/tests/abis/abis_test.c b/tests/abis/abis_test.c new file mode 100644 index 000000000..e7e78d202 --- /dev/null +++ b/tests/abis/abis_test.c @@ -0,0 +1,169 @@ +/* + * (C) 2012 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include +#include + +#include +#include +#include + +static const uint8_t simple_config[] = { + /*0, 13, */ + 66, 18, 0, 3, 1, 2, 3, 19, 0, 3, 3, 4, 5, +}; + +static const uint8_t dual_config[] = { + /*0, 26, */ + 66, 18, 0, 3, 1, 2, 3, 19, 0, 3, 3, 4, 5, + 66, 18, 0, 3, 9, 7, 5, 19, 0, 3, 6, 7, 8, +}; + +static const uint8_t load_config[] = { + 0x42, 0x12, 0x00, 0x08, 0x31, 0x36, 0x38, 0x64, + 0x34, 0x37, 0x32, 0x00, 0x13, 0x00, 0x0b, 0x76, + 0x32, 0x30, 0x30, 0x62, 0x31, 0x34, 0x33, 0x64, + 0x30, 0x00, 0x42, 0x12, 0x00, 0x08, 0x31, 0x36, + 0x38, 0x64, 0x34, 0x37, 0x32, 0x00, 0x13, 0x00, + 0x0b, 0x76, 0x32, 0x30, 0x30, 0x62, 0x31, 0x34, + 0x33, 0x64, 0x31, 0x00 +}; + +static void test_simple_sw_config(void) +{ + struct abis_nm_sw_descr descr[1]; + int rc; + + rc = abis_nm_parse_sw_config(simple_config, ARRAY_SIZE(simple_config), + &descr[0], ARRAY_SIZE(descr)); + if (rc != 1) { + printf("FAILED to parse the File Id/File version\n"); + abort(); + } + + if (descr[0].len != 13) { + printf("WRONG SIZE: %d\n", descr[0].len); + abort(); + } + + printf("Start: %u len: %zu\n", descr[0].start - simple_config, descr[0].len); + printf("file_id: %s\n", osmo_hexdump(descr[0].file_id, descr[0].file_id_len)); + printf("file_ver: %s\n", osmo_hexdump(descr[0].file_ver, descr[0].file_ver_len)); +} + +static void test_simple_sw_short(void) +{ + struct abis_nm_sw_descr descr[1]; + int i; + + for (i = 1; i < ARRAY_SIZE(simple_config); ++i) { + int rc = abis_nm_parse_sw_config(simple_config, + ARRAY_SIZE(simple_config) - i, &descr[0], + ARRAY_SIZE(descr)); + if (rc >= 1) { + printf("SHOULD not have parsed: %d\n", rc); + abort(); + } + } +} + +static void test_dual_sw_config(void) +{ + struct abis_nm_sw_descr descr[2]; + int rc; + + rc = abis_nm_parse_sw_config(dual_config, ARRAY_SIZE(dual_config), + &descr[0], ARRAY_SIZE(descr)); + if (rc != 2) { + printf("FAILED to parse the File Id/File version\n"); + abort(); + } + + if (descr[0].len != 13) { + printf("WRONG SIZE0: %d\n", descr[0].len); + abort(); + } + + if (descr[1].len != 13) { + printf("WRONG SIZE1: %d\n", descr[1].len); + abort(); + } + + printf("Start: %u len: %zu\n", descr[0].start - dual_config, descr[0].len); + printf("file_id: %s\n", osmo_hexdump(descr[0].file_id, descr[0].file_id_len)); + printf("file_ver: %s\n", osmo_hexdump(descr[0].file_ver, descr[0].file_ver_len)); + + printf("Start: %u len: %zu\n", descr[1].start - dual_config, descr[1].len); + printf("file_id: %s\n", osmo_hexdump(descr[1].file_id, descr[1].file_id_len)); + printf("file_ver: %s\n", osmo_hexdump(descr[1].file_ver, descr[1].file_ver_len)); +} + +static void test_sw_selection(void) +{ + struct abis_nm_sw_descr descr[8], tmp; + int rc, pos; + + rc = abis_nm_parse_sw_config(load_config, ARRAY_SIZE(load_config), + &descr[0], ARRAY_SIZE(descr)); + if (rc != 2) { + printf("FAILED to parse the File Id/File version\n"); + abort(); + } + + printf("Start: %u len: %zu\n", descr[0].start - load_config, descr[0].len); + printf("file_id: %s\n", osmo_hexdump(descr[0].file_id, descr[0].file_id_len)); + printf("file_ver: %s\n", osmo_hexdump(descr[0].file_ver, descr[0].file_ver_len)); + + printf("Start: %u len: %zu\n", descr[1].start - load_config, descr[1].len); + printf("file_id: %s\n", osmo_hexdump(descr[1].file_id, descr[1].file_id_len)); + printf("file_ver: %s\n", osmo_hexdump(descr[1].file_ver, descr[1].file_ver_len)); + + /* start */ + pos = abis_nm_select_newest_sw(descr, rc); + if (pos != 1) { + printf("Selected the wrong version: %d\n", pos); + abort(); + } + printf("SELECTED: %d\n", pos); + + /* shuffle */ + tmp = descr[0]; + descr[0] = descr[1]; + descr[1] = tmp; + pos = abis_nm_select_newest_sw(descr, rc); + if (pos != 0) { + printf("Selected the wrong version: %d\n", pos); + abort(); + } + printf("SELECTED: %d\n", pos); +} + +int main(int argc, char **argv) +{ + osmo_init_logging(&log_info); + test_simple_sw_config(); + test_simple_sw_short(); + test_dual_sw_config(); + test_sw_selection(); + + return EXIT_SUCCESS; +} diff --git a/tests/abis/abis_test.ok b/tests/abis/abis_test.ok new file mode 100644 index 000000000..2f99f9d49 --- /dev/null +++ b/tests/abis/abis_test.ok @@ -0,0 +1,17 @@ +Start: 0 len: 13 +file_id: 01 02 03 +file_ver: 03 04 05 +Start: 0 len: 13 +file_id: 01 02 03 +file_ver: 03 04 05 +Start: 13 len: 13 +file_id: 09 07 05 +file_ver: 06 07 08 +Start: 0 len: 26 +file_id: 31 36 38 64 34 37 32 00 +file_ver: 76 32 30 30 62 31 34 33 64 30 00 +Start: 26 len: 26 +file_id: 31 36 38 64 34 37 32 00 +file_ver: 76 32 30 30 62 31 34 33 64 31 00 +SELECTED: 1 +SELECTED: 0 diff --git a/tests/atlocal.in b/tests/atlocal.in new file mode 100644 index 000000000..bfbecd48f --- /dev/null +++ b/tests/atlocal.in @@ -0,0 +1,2 @@ +enable_nat_test='@osmo_ac_build_nat@' +enable_smpp_test='@osmo_ac_build_smpp@' diff --git a/tests/bsc-nat/Makefile.am b/tests/bsc-nat/Makefile.am new file mode 100644 index 000000000..084767829 --- /dev/null +++ b/tests/bsc-nat/Makefile.am @@ -0,0 +1,23 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +EXTRA_DIST = bsc_nat_test.ok bsc_data.c barr.cfg barr_dup.cfg + +noinst_PROGRAMS = bsc_nat_test + +bsc_nat_test_SOURCES = bsc_nat_test.c \ + $(top_srcdir)/src/osmo-bsc_nat/bsc_filter.c \ + $(top_srcdir)/src/osmo-bsc_nat/bsc_sccp.c \ + $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_utils.c \ + $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_filter.c \ + $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite.c \ + $(top_srcdir)/src/osmo-bsc_nat/bsc_mgcp_utils.c +bsc_nat_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_srcdir)/src/libctrl/libctrl.a \ + $(top_srcdir)/src/libmgcp/libmgcp.a \ + $(top_srcdir)/src/libtrau/libtrau.a \ + $(top_srcdir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lrt \ + $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOABIS_LIBS) diff --git a/tests/bsc-nat/barr.cfg b/tests/bsc-nat/barr.cfg new file mode 100644 index 000000000..a9a4a2b31 --- /dev/null +++ b/tests/bsc-nat/barr.cfg @@ -0,0 +1,12 @@ +12123124:3:2: +12123123:3:1: +12123128:3:6: +12123125:3:3: +12123127:3:5: +12123126:3:4: +12123120:3:4: +12123119:3:4: +12123118:3:4: +12123117:3:4: +12123116:3:4: +12123115:3:4: diff --git a/tests/bsc-nat/barr_dup.cfg b/tests/bsc-nat/barr_dup.cfg new file mode 100644 index 000000000..ea94631ce --- /dev/null +++ b/tests/bsc-nat/barr_dup.cfg @@ -0,0 +1,2 @@ +12123124:3:2: +12123124:3:2: diff --git a/tests/bsc-nat/bsc_data.c b/tests/bsc-nat/bsc_data.c new file mode 100644 index 000000000..5a76689e9 --- /dev/null +++ b/tests/bsc-nat/bsc_data.c @@ -0,0 +1,255 @@ +/* test data */ + +/* BSC -> MSC, CR */ +static const uint8_t bsc_cr[] = { +0x00, 0x2e, 0xfd, +0x01, 0x00, 0x00, 0x15, 0x02, 0x02, 0x04, 0x02, +0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05, +0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x1c, 0xc3, +0x51, 0x17, 0x12, 0x05, 0x08, 0x20, 0x72, 0xf4, +0x90, 0x20, 0x1d, 0x50, 0x08, 0x29, 0x47, 0x80, +0x00, 0x00, 0x00, 0x00, 0x80, 0x00 }; + +static const uint8_t bsc_cr_patched[] = { +0x00, 0x2e, 0xfd, +0x01, 0x00, 0x00, 0x05, 0x02, 0x02, 0x04, 0x02, +0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05, +0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x1c, 0xc3, +0x51, 0x17, 0x12, 0x05, 0x08, 0x20, 0x72, 0xf4, +0x90, 0x20, 0x1d, 0x50, 0x08, 0x29, 0x47, 0x80, +0x00, 0x00, 0x00, 0x00, 0x80, 0x00 }; + +/* CC, MSC -> BSC */ +static const uint8_t msc_cc[] = { +0x00, 0x0a, 0xfd, +0x02, 0x00, 0x00, 0x05, 0x01, 0x1f, 0xe4, 0x02, +0x01, 0x00 }; +static const uint8_t msc_cc_patched[] = { +0x00, 0x0a, 0xfd, +0x02, 0x00, 0x00, 0x15, 0x01, 0x1f, 0xe4, 0x02, +0x01, 0x00 }; + +/* Classmark, BSC -> MSC */ +static const uint8_t bsc_dtap[] = { +0x00, 0x17, 0xfd, +0x06, 0x01, 0x1f, 0xe4, 0x00, 0x01, 0x10, 0x00, +0x0e, 0x54, 0x12, 0x03, 0x50, 0x18, 0x93, 0x13, +0x06, 0x60, 0x14, 0x45, 0x00, 0x81, 0x00 }; + +static const uint8_t bsc_dtap_patched[] = { +0x00, 0x17, 0xfd, +0x06, 0x01, 0x1f, 0xe4, 0x00, 0x01, 0x10, 0x00, +0x0e, 0x54, 0x12, 0x03, 0x50, 0x18, 0x93, 0x13, +0x06, 0x60, 0x14, 0x45, 0x00, 0x81, 0x00 }; + +/* Clear command, MSC -> BSC */ +static const uint8_t msc_dtap[] = { +0x00, 0x0d, 0xfd, +0x06, 0x00, 0x00, 0x05, 0x00, 0x01, 0x06, 0x00, +0x04, 0x20, 0x04, 0x01, 0x09 }; +static const uint8_t msc_dtap_patched[] = { +0x00, 0x0d, 0xfd, +0x06, 0x00, 0x00, 0x15, 0x00, 0x01, 0x06, 0x00, +0x04, 0x20, 0x04, 0x01, 0x09 }; + +/*RLSD, MSC -> BSC */ +static const uint8_t msc_rlsd[] = { +0x00, 0x0a, 0xfd, +0x04, 0x00, 0x00, 0x05, 0x01, 0x1f, 0xe4, 0x00, +0x01, 0x00 }; +static const uint8_t msc_rlsd_patched[] = { +0x00, 0x0a, 0xfd, +0x04, 0x00, 0x00, 0x15, 0x01, 0x1f, 0xe4, 0x00, +0x01, 0x00 }; + +/* RLC, BSC -> MSC */ +static const uint8_t bsc_rlc[] = { +0x00, 0x07, 0xfd, +0x05, 0x01, 0x1f, 0xe4, 0x00, 0x00, 0x15 }; + +static const uint8_t bsc_rlc_patched[] = { +0x00, 0x07, 0xfd, +0x05, 0x01, 0x1f, 0xe4, 0x00, 0x00, 0x05 }; + + +/* a paging command */ +static const uint8_t paging_by_lac_cmd[] = { +0x00, 0x22, 0xfd, 0x09, +0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x02, 0x00, +0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x12, 0x00, +0x10, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10, 0x02, +0x01, 0x50, 0x02, 0x30, 0x1a, 0x03, 0x05, 0x20, +0x15 }; + +/* an assignment command */ +static const uint8_t ass_cmd[] = { +0x00, 0x12, 0xfd, 0x06, +0x00, 0x00, 0x49, 0x00, 0x01, 0x0b, 0x00, 0x09, +0x01, 0x0b, 0x03, 0x01, 0x0a, 0x11, 0x01, 0x00, +0x01 }; + +/* identity response */ +static const uint8_t id_resp[] = { +0x00, 0x15, 0xfd, 0x06, 0x01, 0x1c, 0xdc, +0x00, 0x01, 0x0e, 0x01, 0x00, 0x0b, 0x05, 0x59, +0x08, 0x29, 0x40, 0x21, 0x03, 0x07, 0x48, 0x66, +0x31 +}; + +/* sms code msg */ +static const uint8_t smsc_rewrite[] = { +0x00, 0x30, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, +0x01, 0x29, 0x01, 0x03, 0x26, 0x09, 0x01, 0x23, +0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08, +0x00, 0x10, 0x50, 0x17, 0x21, 0x0c, 0x0f, 0x81, +0x00, 0x94, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, +0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, +0xbf, 0xeb, 0x20 +}; + +static const uint8_t smsc_rewrite_patched[] = { +0x00, 0x31, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, +0x01, 0x2a, 0x01, 0x03, 0x27, 0x09, 0x01, 0x24, +0x00, 0x0c, 0x00, 0x08, 0x91, 0x66, 0x66, 0x66, +0x66, 0x66, 0x66, 0xf7, 0x17, 0x01, 0x0c, 0x0f, +0x81, 0x00, 0x94, 0x51, 0x87, 0x86, 0x78, 0x46, +0xf5, 0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, +0xca, 0xbf, 0xeb, 0x20 +}; + +static const uint8_t smsc_rewrite_patched_hdr[] = { +0x00, 0x30, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, +0x01, 0x29, 0x01, 0x03, 0x26, 0x09, 0x01, 0x23, +0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08, +0x00, 0x10, 0x50, 0x17, 0x01, 0x0c, 0x0f, 0x81, +0x00, 0x94, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, +0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, +0xbf, 0xeb, 0x20 +}; + +static const uint8_t smsc_rewrite_num_patched[] = { +0x00, 0x2f, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, +0x01, 0x28, 0x01, 0x03, 0x25, 0x09, 0x01, 0x22, +0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08, +0x00, 0x10, 0x50, 0x16, 0x21, 0x0c, 0x0d, 0x91, + 0x23, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, +0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, +0xbf, 0xeb, 0x20 +}; + +static const uint8_t smsc_rewrite_num_patched_tp_srr[] = { +0x00, 0x2f, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, +0x01, 0x28, 0x01, 0x03, 0x25, 0x09, 0x01, 0x22, +0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08, +0x00, 0x10, 0x50, 0x16, 0x01, 0x0c, 0x0d, 0x91, + 0x23, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, +0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, +0xbf, 0xeb, 0x20 +}; + +/* + * MGCP messages + */ + +/* nothing to patch */ +static const char crcx[] = "CRCX 23265295 8@mgw MGCP 1.0\r\nC: 394b0439fb\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n"; +static const char crcx_patched[] = "CRCX 23265295 1e@mgw MGCP 1.0\r\nC: 394b0439fb\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n"; + + +/* patch the ip and port */ +static const char crcx_resp[] = "200 23265295\r\nI: 1\r\n\r\nv=0\r\nc=IN IP4 172.16.18.2\r\nm=audio 4002 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\n"; +static const char crcx_resp_patched[] = "200 23265295\r\nI: 1\r\n\r\nv=0\r\nc=IN IP4 10.0.0.1\r\nm=audio 999 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\na=fmtp:98 mode-set=2\r\n"; + +/* patch the ip and port */ +static const char mdcx[] = "MDCX 23330829 8@mgw MGCP 1.0\r\nC: 394b0439fb\r\nI: 1\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 1049380491 0 IN IP4 172.16.18.2\r\ns=-\r\nc=IN IP4 172.16.18.2\r\nt=0 0\r\nm=audio 4410 RTP/AVP 126\r\na=rtpmap:126 AMR/8000/1\r\na=fmtp:126 mode-set=2;start-mode=0\r\na=ptime:20\r\na=recvonly\r\nm=image 4412 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"; +static const char mdcx_patched[] = "MDCX 23330829 1e@mgw MGCP 1.0\r\nC: 394b0439fb\r\nI: 1\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 1049380491 0 IN IP4 172.16.18.2\r\ns=-\r\nc=IN IP4 10.0.0.23\r\nt=0 0\r\nm=audio 6666 RTP/AVP 126\r\na=rtpmap:126 AMR/8000/1\r\na=fmtp:126 mode-set=2;start-mode=0\r\na=ptime:20\r\na=recvonly\r\nm=image 4412 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"; + + +static const char mdcx_resp[] = "200 23330829\r\n\r\nv=0\r\nc=IN IP4 172.16.18.2\r\nm=audio 4002 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\n"; +static const char mdcx_resp_patched[] = "200 23330829\r\n\r\nv=0\r\nc=IN IP4 10.0.0.23\r\nm=audio 5555 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\na=fmtp:98 mode-set=2\r\n"; + +/* different line ending */ +static const char mdcx_resp2[] = "200 33330829\n\nv=0\nc=IN IP4 172.16.18.2\nm=audio 4002 RTP/AVP 98\na=rtpmap:98 AMR/8000\n"; +static const char mdcx_resp_patched2[] = "200 33330829\n\nv=0\nc=IN IP4 10.0.0.23\nm=audio 5555 RTP/AVP 98\na=rtpmap:98 AMR/8000\na=fmtp:98 mode-set=2\n"; + +struct mgcp_patch_test { + const char *orig; + const char *patch; + const char *ip; + const int port; +}; + +static const struct mgcp_patch_test mgcp_messages[] = { + { + .orig = crcx, + .patch = crcx_patched, + .ip = "0.0.0.0", + .port = 2323, + }, + { + .orig = crcx_resp, + .patch = crcx_resp_patched, + .ip = "10.0.0.1", + .port = 999, + }, + { + .orig = mdcx, + .patch = mdcx_patched, + .ip = "10.0.0.23", + .port = 6666, + }, + { + .orig = mdcx_resp, + .patch = mdcx_resp_patched, + .ip = "10.0.0.23", + .port = 5555, + }, + { + .orig = mdcx_resp2, + .patch = mdcx_resp_patched2, + .ip = "10.0.0.23", + .port = 5555, + }, +}; + +/* CC Setup messages */ +static const uint8_t cc_setup_national[] = { + 0x00, 0x20, 0xfd, 0x06, 0x01, 0x12, + 0x6d, 0x00, 0x01, 0x19, 0x01, 0x00, 0x16, 0x03, + 0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, + 0x81, 0x5e, 0x06, 0x81, 0x10, 0x27, 0x33, 0x63, + 0x66, 0x15, 0x02, 0x11, 0x01 +}; + +static const uint8_t cc_setup_national_patched[] = { + 0x00, 0x21, 0xfd, 0x06, 0x01, 0x12, + 0x6d, 0x00, 0x01, 0x1a, 0x01, 0x00, 0x17, 0x03, + 0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, + 0x81, 0x5e, 0x07, 0x91, 0x94, 0x71, 0x32, 0x33, + 0x66, 0xf6, 0x15, 0x02, 0x11, 0x01 +}; + +/* patch the phone number of cc_setup_national_patched */ +static const uint8_t cc_setup_national_patched_patched[] = { + 0x00, 0x21, 0xfd, 0x06, 0x01, 0x12, + 0x6d, 0x00, 0x01, 0x1a, 0x01, 0x00, 0x17, 0x03, + 0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, + 0x81, 0x5e, 0x07, 0x91, 0x63, 0x71, 0x32, 0x33, + 0x66, 0xf6, 0x15, 0x02, 0x11, 0x01 +}; + +static const uint8_t cc_setup_international[] = { + 0x00, 0x22, 0xfd, 0x06, 0x01, 0x13, + 0xe7, 0x00, 0x01, 0x1b, 0x01, 0x00, 0x18, 0x03, + 0x45, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, + 0x81, 0x5e, 0x08, 0x81, 0x00, 0x94, 0x71, 0x33, + 0x63, 0x66, 0x03, 0x15, 0x02, 0x11, 0x01 +}; + +static const uint8_t cc_setup_national_again[] = { + 0x00, 0x22, 0xfd, 0x06, 0x01, 0x12, 0x6d, 0x00, + 0x01, 0x1b, 0x01, 0x00, 0x18, 0x03, 0x05, 0x04, + 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81, 0x5e, + 0x08, 0x81, 0x63, 0x94, 0x71, 0x32, 0x33, 0x66, + 0xf6, 0x15, 0x02, 0x11, 0x01 +}; diff --git a/tests/bsc-nat/bsc_nat_test.c b/tests/bsc-nat/bsc_nat_test.c new file mode 100644 index 000000000..4a244dba3 --- /dev/null +++ b/tests/bsc-nat/bsc_nat_test.c @@ -0,0 +1,1339 @@ +/* + * BSC NAT Message filtering + * + * (C) 2010-2013 by Holger Hans Peter Freyther + * (C) 2010-2013 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +/* test messages for ipa */ +static uint8_t ipa_id[] = { + 0x00, 0x01, 0xfe, 0x06, +}; + +/* SCCP messages are below */ +static uint8_t gsm_reset[] = { + 0x00, 0x12, 0xfd, + 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe, + 0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04, + 0x01, 0x20, +}; + +static const uint8_t gsm_reset_ack[] = { + 0x00, 0x13, 0xfd, + 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, + 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03, + 0x00, 0x01, 0x31, +}; + +static const uint8_t gsm_paging[] = { + 0x00, 0x20, 0xfd, + 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, + 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x10, + 0x00, 0x0e, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10, + 0x02, 0x01, 0x31, 0x97, 0x61, 0x1a, 0x01, 0x06, +}; + +/* BSC -> MSC connection open */ +static const uint8_t bssmap_cr[] = { + 0x00, 0x2c, 0xfd, + 0x01, 0x01, 0x02, 0x03, 0x02, 0x02, 0x04, 0x02, + 0x42, 0xfe, 0x0f, 0x1f, 0x00, 0x1d, 0x57, 0x05, + 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x12, 0xc3, + 0x50, 0x17, 0x10, 0x05, 0x24, 0x11, 0x03, 0x33, + 0x19, 0xa2, 0x08, 0x29, 0x47, 0x10, 0x02, 0x01, + 0x31, 0x97, 0x61, 0x00 +}; + +/* MSC -> BSC connection confirm */ +static const uint8_t bssmap_cc[] = { + 0x00, 0x0a, 0xfd, + 0x02, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, +}; + +/* MSC -> BSC released */ +static const uint8_t bssmap_released[] = { + 0x00, 0x0e, 0xfd, + 0x04, 0x00, 0x00, 0x03, 0x01, 0x02, 0x03, 0x00, 0x01, 0x0f, + 0x02, 0x23, 0x42, 0x00, +}; + +/* BSC -> MSC released */ +static const uint8_t bssmap_release_complete[] = { + 0x00, 0x07, 0xfd, + 0x05, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03 +}; + +/* both directions IT timer */ +static const uint8_t connnection_it[] = { + 0x00, 0x0b, 0xfd, + 0x10, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, + 0x00, 0x00, 0x00, 0x00, +}; + +/* error in both directions */ +static const uint8_t proto_error[] = { + 0x00, 0x05, 0xfd, + 0x0f, 0x22, 0x33, 0x44, 0x00, +}; + +/* MGCP wrap... */ +static const uint8_t mgcp_msg[] = { + 0x00, 0x03, 0xfc, + 0x20, 0x20, 0x20, +}; + +/* location updating request */ +static const uint8_t bss_lu[] = { + 0x00, 0x2e, 0xfd, + 0x01, 0x91, 0x45, 0x14, 0x02, 0x02, 0x04, 0x02, + 0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05, + 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x14, 0xc3, + 0x50, 0x17, 0x12, 0x05, 0x08, 0x70, 0x72, 0xf4, + 0x80, 0xff, 0xfe, 0x30, 0x08, 0x29, 0x44, 0x50, + 0x12, 0x03, 0x24, 0x01, 0x95, 0x00 +}; + +/* paging response */ +static const uint8_t pag_resp[] = { + 0x00, 0x2c, 0xfd, 0x01, 0xe5, 0x68, + 0x14, 0x02, 0x02, 0x04, 0x02, 0x42, 0xfe, 0x0f, + 0x1f, 0x00, 0x1d, 0x57, 0x05, 0x08, 0x00, 0x72, + 0xf4, 0x80, 0x20, 0x16, 0xc3, 0x50, 0x17, 0x10, + 0x06, 0x27, 0x01, 0x03, 0x30, 0x18, 0x96, 0x08, + 0x29, 0x26, 0x30, 0x32, 0x11, 0x42, 0x01, 0x19, + 0x00 +}; + +struct filter_result { + const uint8_t *data; + const uint16_t length; + const int dir; + const int result; +}; + +static const struct filter_result results[] = { + { + .data = ipa_id, + .length = ARRAY_SIZE(ipa_id), + .dir = DIR_MSC, + .result = 1, + }, + { + .data = gsm_reset, + .length = ARRAY_SIZE(gsm_reset), + .dir = DIR_MSC, + .result = 1, + }, + { + .data = gsm_reset_ack, + .length = ARRAY_SIZE(gsm_reset_ack), + .dir = DIR_BSC, + .result = 1, + }, + { + .data = gsm_paging, + .length = ARRAY_SIZE(gsm_paging), + .dir = DIR_BSC, + .result = 0, + }, + { + .data = bssmap_cr, + .length = ARRAY_SIZE(bssmap_cr), + .dir = DIR_MSC, + .result = 0, + }, + { + .data = bssmap_cc, + .length = ARRAY_SIZE(bssmap_cc), + .dir = DIR_BSC, + .result = 0, + }, + { + .data = bssmap_released, + .length = ARRAY_SIZE(bssmap_released), + .dir = DIR_MSC, + .result = 0, + }, + { + .data = bssmap_release_complete, + .length = ARRAY_SIZE(bssmap_release_complete), + .dir = DIR_BSC, + .result = 0, + }, + { + .data = mgcp_msg, + .length = ARRAY_SIZE(mgcp_msg), + .dir = DIR_MSC, + .result = 0, + }, + { + .data = connnection_it, + .length = ARRAY_SIZE(connnection_it), + .dir = DIR_BSC, + .result = 0, + }, + { + .data = connnection_it, + .length = ARRAY_SIZE(connnection_it), + .dir = DIR_MSC, + .result = 0, + }, + { + .data = proto_error, + .length = ARRAY_SIZE(proto_error), + .dir = DIR_BSC, + .result = 0, + }, + { + .data = proto_error, + .length = ARRAY_SIZE(proto_error), + .dir = DIR_MSC, + .result = 0, + }, + +}; + +static void test_filter(void) +{ + int i; + + + /* start testinh with proper messages */ + printf("Testing BSS Filtering.\n"); + for (i = 0; i < ARRAY_SIZE(results); ++i) { + int result; + struct bsc_nat_parsed *parsed; + struct msgb *msg = msgb_alloc(4096, "test-message"); + + printf("Going to test item: %d\n", i); + memcpy(msg->data, results[i].data, results[i].length); + msg->l2h = msgb_put(msg, results[i].length); + + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Failed to parse the message\n"); + continue; + } + + result = bsc_nat_filter_ipa(results[i].dir, msg, parsed); + if (result != results[i].result) { + printf("FAIL: Not the expected result got: %d wanted: %d\n", + result, results[i].result); + } + + msgb_free(msg); + } +} + +#include "bsc_data.c" + +static void copy_to_msg(struct msgb *msg, const uint8_t *data, unsigned int length) +{ + msgb_reset(msg); + msg->l2h = msgb_put(msg, length); + memcpy(msg->l2h, data, msgb_l2len(msg)); +} + +static void verify_msg(struct msgb *out, const uint8_t *ref, int ref_len) +{ + if (out->len != ref_len) { + printf("FAIL: The size should match: %d vs. %d\n", + out->len, ref_len); + printf("%s\n", osmo_hexdump(out->data, out->len)); + printf("Wanted\n"); + printf("%s\n", osmo_hexdump(ref, ref_len)); + abort(); + } + + if (memcmp(out->data, ref, out->len) != 0) { + printf("FAIL: the data should be changed.\n"); + printf("%s\n", osmo_hexdump(out->data, out->len)); + printf("Wanted\n"); + printf("%s\n", osmo_hexdump(ref, ref_len)); + abort(); + } +} + + +#define VERIFY(con_found, con, msg, ver, str) \ + if (!con_found) { \ + printf("Failed to find connection.\n"); \ + abort(); \ + } \ + if (con_found->bsc != con) { \ + printf("Got connection of the wrong BSC: %d\n", \ + con_found->bsc->cfg->nr); \ + abort(); \ + } \ + if (memcmp(msg->data, ver, sizeof(ver)) != 0) { \ + printf("Failed to patch the %s msg.\n", str); \ + abort(); \ + } + +/* test conn tracking once */ +static void test_contrack() +{ + struct bsc_nat *nat; + struct bsc_connection *con; + struct nat_sccp_connection *con_found; + struct nat_sccp_connection *rc_con; + struct bsc_nat_parsed *parsed; + struct msgb *msg; + + printf("Testing connection tracking.\n"); + nat = bsc_nat_alloc(); + con = bsc_connection_alloc(nat); + con->cfg = bsc_config_alloc(nat, "foo"); + bsc_config_add_lac(con->cfg, 23); + bsc_config_add_lac(con->cfg, 49); + bsc_config_add_lac(con->cfg, 42); + bsc_config_del_lac(con->cfg, 49); + bsc_config_add_lac(con->cfg, 1111); + msg = msgb_alloc(4096, "test"); + + /* 1.) create a connection */ + copy_to_msg(msg, bsc_cr, sizeof(bsc_cr)); + parsed = bsc_nat_parse(msg); + con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); + if (con_found != NULL) { + printf("Con should not exist realref(%u)\n", + sccp_src_ref_to_int(&con_found->real_ref)); + abort(); + } + rc_con = create_sccp_src_ref(con, parsed); + if (!rc_con) { + printf("Failed to create a ref\n"); + abort(); + } + con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); + if (!con_found) { + printf("Failed to find connection.\n"); + abort(); + } + if (con_found->bsc != con) { + printf("Got connection of the wrong BSC: %d\n", + con_found->bsc->cfg->nr); + abort(); + } + if (con_found != rc_con) { + printf("Failed to find the right connection.\n"); + abort(); + } + if (memcmp(msg->data, bsc_cr_patched, sizeof(bsc_cr_patched)) != 0) { + printf("Failed to patch the BSC CR msg.\n"); + abort(); + } + talloc_free(parsed); + + /* 2.) get the cc */ + copy_to_msg(msg, msc_cc, sizeof(msc_cc)); + parsed = bsc_nat_parse(msg); + con_found = patch_sccp_src_ref_to_bsc(msg, parsed, nat); + VERIFY(con_found, con, msg, msc_cc_patched, "MSC CC"); + if (update_sccp_src_ref(con_found, parsed) != 0) { + printf("Failed to update the SCCP con.\n"); + abort(); + } + + /* 3.) send some data */ + copy_to_msg(msg, bsc_dtap, sizeof(bsc_dtap)); + parsed = bsc_nat_parse(msg); + con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); + VERIFY(con_found, con, msg, bsc_dtap_patched, "BSC DTAP"); + + /* 4.) receive some data */ + copy_to_msg(msg, msc_dtap, sizeof(msc_dtap)); + parsed = bsc_nat_parse(msg); + con_found = patch_sccp_src_ref_to_bsc(msg, parsed, nat); + VERIFY(con_found, con, msg, msc_dtap_patched, "MSC DTAP"); + + /* 5.) close the connection */ + copy_to_msg(msg, msc_rlsd, sizeof(msc_rlsd)); + parsed = bsc_nat_parse(msg); + con_found = patch_sccp_src_ref_to_bsc(msg, parsed, nat); + VERIFY(con_found, con, msg, msc_rlsd_patched, "MSC RLSD"); + + /* 6.) confirm the connection close */ + copy_to_msg(msg, bsc_rlc, sizeof(bsc_rlc)); + parsed = bsc_nat_parse(msg); + con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); + if (!con_found) { + printf("Failed to find connection.\n"); + abort(); + } + if (con_found->bsc != con) { + printf("Got connection of the wrong BSC: %d\n", + con_found->bsc->cfg->nr); + abort(); + } + if (memcmp(msg->data, bsc_rlc_patched, sizeof(bsc_rlc_patched)) != 0) { + printf("Failed to patch the BSC CR msg.\n"); + abort(); + } + remove_sccp_src_ref(con, msg, parsed); + talloc_free(parsed); + + copy_to_msg(msg, bsc_rlc, sizeof(bsc_rlc)); + parsed = bsc_nat_parse(msg); + con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); + + /* verify that it is gone */ + if (con_found != NULL) { + printf("Con should not exist real_ref(%u)\n", + sccp_src_ref_to_int(&con_found->real_ref)); + abort(); + } + talloc_free(parsed); + + + bsc_config_free(con->cfg); + talloc_free(nat); + msgb_free(msg); +} + +static void test_paging(void) +{ + struct bsc_nat *nat; + struct bsc_connection *con; + struct bsc_config *cfg; + + printf("Testing paging by lac.\n"); + + nat = bsc_nat_alloc(); + con = bsc_connection_alloc(nat); + cfg = bsc_config_alloc(nat, "unknown"); + con->cfg = cfg; + bsc_config_add_lac(cfg, 23); + con->authenticated = 1; + llist_add(&con->list_entry, &nat->bsc_connections); + + /* Test it by not finding it */ + if (bsc_config_handles_lac(cfg, 8213) != 0) { + printf("Should not be handled.\n"); + abort(); + } + + /* Test by finding it */ + bsc_config_del_lac(cfg, 23); + bsc_config_add_lac(cfg, 8213); + if (bsc_config_handles_lac(cfg, 8213) == 0) { + printf("Should have found it.\n"); + abort(); + } +} + +static void test_mgcp_allocations(void) +{ +#if 0 + struct bsc_connection *bsc; + struct bsc_nat *nat; + struct nat_sccp_connection con; + int i, j, multiplex; + + printf("Testing MGCP.\n"); + memset(&con, 0, sizeof(con)); + + nat = bsc_nat_alloc(); + nat->bsc_endpoints = talloc_zero_array(nat, + struct bsc_endpoint, + 65); + nat->mgcp_cfg = mgcp_config_alloc(); + nat->mgcp_cfg->trunk.number_endpoints = 64; + + bsc = bsc_connection_alloc(nat); + bsc->cfg = bsc_config_alloc(nat, "foo"); + bsc->cfg->max_endpoints = 60; + bsc_config_add_lac(bsc->cfg, 2323); + bsc->last_endpoint = 0x22; + con.bsc = bsc; + + bsc_init_endps_if_needed(bsc); + + i = 1; + do { + if (bsc_assign_endpoint(bsc, &con) != 0) { + printf("failed to allocate... on iteration %d\n", i); + break; + } + ++i; + } while(1); + + multiplex = bsc_mgcp_nr_multiplexes(bsc->cfg->max_endpoints); + for (i = 0; i < multiplex; ++i) { + for (j = 0; j < 32; ++j) + printf("%d", bsc->_endpoint_status[i*32 + j]); + printf(": %d of %d\n", i*32 + 32, 32 * 8); + } +#endif +} + +static void test_mgcp_ass_tracking(void) +{ + struct bsc_connection *bsc; + struct bsc_nat *nat; + struct nat_sccp_connection con; + struct bsc_nat_parsed *parsed; + struct msgb *msg; + + printf("Testing MGCP.\n"); + memset(&con, 0, sizeof(con)); + + nat = bsc_nat_alloc(); + nat->bsc_endpoints = talloc_zero_array(nat, + struct bsc_endpoint, + 33); + nat->mgcp_cfg = mgcp_config_alloc(); + nat->mgcp_cfg->trunk.number_endpoints = 64; + mgcp_endpoints_allocate(&nat->mgcp_cfg->trunk); + + bsc = bsc_connection_alloc(nat); + bsc->cfg = bsc_config_alloc(nat, "foo"); + bsc_config_add_lac(bsc->cfg, 2323); + bsc->last_endpoint = 0x1e; + con.bsc = bsc; + + msg = msgb_alloc(4096, "foo"); + copy_to_msg(msg, ass_cmd, sizeof(ass_cmd)); + parsed = bsc_nat_parse(msg); + + if (msg->l2h[16] != 0 || + msg->l2h[17] != 0x1) { + printf("Input is not as expected.. %s 0x%x\n", + osmo_hexdump(msg->l2h, msgb_l2len(msg)), + msg->l2h[17]); + abort(); + } + + if (bsc_mgcp_assign_patch(&con, msg) != 0) { + printf("Failed to handle assignment.\n"); + abort(); + } + + if (con.msc_endp != 1) { + printf("Timeslot should be 1.\n"); + abort(); + } + + if (con.bsc_endp != 0x1) { + printf("Assigned timeslot should have been 1.\n"); + abort(); + } + if (con.bsc->_endpoint_status[0x1] != 1) { + printf("The status on the BSC is wrong.\n"); + abort(); + } + + int multiplex, timeslot; + mgcp_endpoint_to_timeslot(0x1, &multiplex, ×lot); + + uint16_t cic = htons(timeslot & 0x1f); + if (memcmp(&cic, &msg->l2h[16], sizeof(cic)) != 0) { + printf("Message was not patched properly\n"); + printf("data cic: 0x%x %s\n", cic, osmo_hexdump(msg->l2h, msgb_l2len(msg))); + abort(); + } + + talloc_free(parsed); + + bsc_mgcp_dlcx(&con); + if (con.bsc_endp != -1 || con.msc_endp != -1 || + con.bsc->_endpoint_status[1] != 0 || con.bsc->last_endpoint != 0x1) { + printf("Clearing should remove the mapping.\n"); + abort(); + } + + bsc_config_free(bsc->cfg); + talloc_free(nat); +} + +/* test the code to find a given connection */ +static void test_mgcp_find(void) +{ + struct bsc_nat *nat; + struct bsc_connection *con; + struct nat_sccp_connection *sccp_con; + + printf("Testing finding of a BSC Connection\n"); + + nat = bsc_nat_alloc(); + con = bsc_connection_alloc(nat); + llist_add(&con->list_entry, &nat->bsc_connections); + + sccp_con = talloc_zero(con, struct nat_sccp_connection); + sccp_con->msc_endp = 12; + sccp_con->bsc_endp = 12; + sccp_con->bsc = con; + llist_add(&sccp_con->list_entry, &nat->sccp_connections); + + if (bsc_mgcp_find_con(nat, 11) != NULL) { + printf("Found the wrong connection.\n"); + abort(); + } + + if (bsc_mgcp_find_con(nat, 12) != sccp_con) { + printf("Didn't find the connection\n"); + abort(); + } + + /* free everything */ + talloc_free(nat); +} + +static void test_mgcp_rewrite(void) +{ + int i; + struct msgb *output; + printf("Testing rewriting MGCP messages.\n"); + + for (i = 0; i < ARRAY_SIZE(mgcp_messages); ++i) { + const char *orig = mgcp_messages[i].orig; + const char *patc = mgcp_messages[i].patch; + const char *ip = mgcp_messages[i].ip; + const int port = mgcp_messages[i].port; + + char *input = strdup(orig); + + output = bsc_mgcp_rewrite(input, strlen(input), 0x1e, ip, port); + if (msgb_l2len(output) != strlen(patc)) { + printf("Wrong sizes for test: %d %d != %d != %d\n", i, msgb_l2len(output), strlen(patc), strlen(orig)); + printf("String '%s' vs '%s'\n", (const char *) output->l2h, patc); + abort(); + } + + if (memcmp(output->l2h, patc, msgb_l2len(output)) != 0) { + printf("Broken on %d msg: '%s'\n", i, (const char *) output->l2h); + abort(); + } + + msgb_free(output); + free(input); + } +} + +static void test_mgcp_parse(void) +{ + int code, ci; + char transaction[60]; + + printf("Testing MGCP response parsing.\n"); + + if (bsc_mgcp_parse_response(crcx_resp, &code, transaction) != 0) { + printf("Failed to parse CRCX resp.\n"); + abort(); + } + + if (code != 200) { + printf("Failed to parse the CODE properly. Got: %d\n", code); + abort(); + } + + if (strcmp(transaction, "23265295") != 0) { + printf("Failed to parse transaction id: '%s'\n", transaction); + abort(); + } + + ci = bsc_mgcp_extract_ci(crcx_resp); + if (ci != 1) { + printf("Failed to parse the CI. Got: %d\n", ci); + abort(); + } +} + +struct cr_filter { + const uint8_t *data; + int length; + int result; + int contype; + + const char *bsc_imsi_allow; + const char *bsc_imsi_deny; + const char *nat_imsi_deny; +}; + +static struct cr_filter cr_filter[] = { + { + .data = bssmap_cr, + .length = sizeof(bssmap_cr), + .result = 1, + .contype = NAT_CON_TYPE_CM_SERV_REQ, + }, + { + .data = bss_lu, + .length = sizeof(bss_lu), + .result = 1, + .contype = NAT_CON_TYPE_LU, + }, + { + .data = pag_resp, + .length = sizeof(pag_resp), + .result = 1, + .contype = NAT_CON_TYPE_PAG_RESP, + }, + { + /* nat deny is before blank/null BSC */ + .data = bss_lu, + .length = sizeof(bss_lu), + .result = -3, + .nat_imsi_deny = "[0-9]*", + .contype = NAT_CON_TYPE_LU, + }, + { + /* BSC allow is before NAT deny */ + .data = bss_lu, + .length = sizeof(bss_lu), + .result = 1, + .nat_imsi_deny = "[0-9]*", + .bsc_imsi_allow = "2440[0-9]*", + .contype = NAT_CON_TYPE_LU, + }, + { + /* BSC allow is before NAT deny */ + .data = bss_lu, + .length = sizeof(bss_lu), + .result = 1, + .bsc_imsi_allow = "[0-9]*", + .nat_imsi_deny = "[0-9]*", + .contype = NAT_CON_TYPE_LU, + }, + { + /* filter as deny is first */ + .data = bss_lu, + .length = sizeof(bss_lu), + .result = 1, + .bsc_imsi_deny = "[0-9]*", + .bsc_imsi_allow = "[0-9]*", + .nat_imsi_deny = "[0-9]*", + .contype = NAT_CON_TYPE_LU, + }, + { + /* deny by nat rule */ + .data = bss_lu, + .length = sizeof(bss_lu), + .result = -3, + .bsc_imsi_deny = "000[0-9]*", + .nat_imsi_deny = "[0-9]*", + .contype = NAT_CON_TYPE_LU, + }, + { + /* deny by bsc rule */ + .data = bss_lu, + .length = sizeof(bss_lu), + .result = -2, + .bsc_imsi_deny = "[0-9]*", + .contype = NAT_CON_TYPE_LU, + }, + +}; + +static void test_cr_filter() +{ + int i, res, contype; + struct msgb *msg = msgb_alloc(4096, "test_cr_filter"); + struct bsc_nat_parsed *parsed; + struct bsc_nat_acc_lst *nat_lst, *bsc_lst; + struct bsc_nat_acc_lst_entry *nat_entry, *bsc_entry; + struct bsc_nat_reject_cause cause; + + struct bsc_nat *nat = bsc_nat_alloc(); + struct bsc_connection *bsc = bsc_connection_alloc(nat); + bsc->cfg = bsc_config_alloc(nat, "foo"); + bsc_config_add_lac(bsc->cfg, 1234); + bsc->cfg->acc_lst_name = "bsc"; + nat->acc_lst_name = "nat"; + + nat_lst = bsc_nat_acc_lst_get(nat, "nat"); + bsc_lst = bsc_nat_acc_lst_get(nat, "bsc"); + + bsc_entry = bsc_nat_acc_lst_entry_create(bsc_lst); + nat_entry = bsc_nat_acc_lst_entry_create(nat_lst); + + for (i = 0; i < ARRAY_SIZE(cr_filter); ++i) { + char *imsi; + msgb_reset(msg); + copy_to_msg(msg, cr_filter[i].data, cr_filter[i].length); + + if (gsm_parse_reg(nat_entry, &nat_entry->imsi_deny_re, &nat_entry->imsi_deny, + cr_filter[i].nat_imsi_deny ? 1 : 0, + &cr_filter[i].nat_imsi_deny) != 0) + abort(); + if (gsm_parse_reg(bsc_entry, &bsc_entry->imsi_allow_re, &bsc_entry->imsi_allow, + cr_filter[i].bsc_imsi_allow ? 1 : 0, + &cr_filter[i].bsc_imsi_allow) != 0) + abort(); + if (gsm_parse_reg(bsc_entry, &bsc_entry->imsi_deny_re, &bsc_entry->imsi_deny, + cr_filter[i].bsc_imsi_deny ? 1 : 0, + &cr_filter[i].bsc_imsi_deny) != 0) + abort(); + + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Failed to parse the message\n"); + abort(); + } + + memset(&cause, 0, sizeof(cause)); + res = bsc_nat_filter_sccp_cr(bsc, msg, parsed, &contype, &imsi, &cause); + if (res != cr_filter[i].result) { + printf("FAIL: Wrong result %d for test %d.\n", res, i); + abort(); + } + + if (contype != cr_filter[i].contype) { + printf("FAIL: Wrong contype %d for test %d.\n", res, contype); + abort(); + } + + talloc_steal(parsed, imsi); + talloc_free(parsed); + } + + msgb_free(msg); +} + +static void test_dt_filter() +{ + int i; + struct msgb *msg = msgb_alloc(4096, "test_dt_filter"); + struct bsc_nat_parsed *parsed; + struct bsc_nat_reject_cause cause; + + struct bsc_nat *nat = bsc_nat_alloc(); + struct bsc_connection *bsc = bsc_connection_alloc(nat); + struct nat_sccp_connection *con = talloc_zero(0, struct nat_sccp_connection); + + bsc->cfg = bsc_config_alloc(nat, "foo"); + bsc_config_add_lac(bsc->cfg, 23); + con->bsc = bsc; + + msgb_reset(msg); + copy_to_msg(msg, id_resp, ARRAY_SIZE(id_resp)); + + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Could not parse ID resp\n"); + abort(); + } + + if (parsed->bssap != BSSAP_MSG_DTAP) { + printf("FAIL: It should be dtap\n"); + abort(); + } + + /* gsm_type is actually the size of the dtap */ + if (parsed->gsm_type < msgb_l3len(msg) - 3) { + printf("FAIL: Not enough space for the content\n"); + abort(); + } + + memset(&cause, 0, sizeof(cause)); + if (bsc_nat_filter_dt(bsc, msg, con, parsed, &cause) != 1) { + printf("FAIL: Should have passed..\n"); + abort(); + } + + /* just some basic length checking... */ + for (i = ARRAY_SIZE(id_resp); i >= 0; --i) { + msgb_reset(msg); + copy_to_msg(msg, id_resp, ARRAY_SIZE(id_resp)); + + parsed = bsc_nat_parse(msg); + if (!parsed) + continue; + + con->imsi_checked = 0; + memset(&cause, 0, sizeof(cause)); + bsc_nat_filter_dt(bsc, msg, con, parsed, &cause); + } +} + +static void test_setup_rewrite() +{ + struct msgb *msg = msgb_alloc(4096, "test_dt_filter"); + struct msgb *out; + struct bsc_nat_parsed *parsed; + const char *imsi = "27408000001234"; + + struct bsc_nat *nat = bsc_nat_alloc(); + + /* a fake list */ + struct osmo_config_list entries; + struct osmo_config_entry entry; + + INIT_LLIST_HEAD(&entries.entry); + entry.mcc = "274"; + entry.mnc = "08"; + entry.option = "^0([1-9])"; + entry.text = "0049"; + llist_add_tail(&entry.list, &entries.entry); + bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); + + /* verify that nothing changed */ + msgb_reset(msg); + copy_to_msg(msg, cc_setup_international, ARRAY_SIZE(cc_setup_international)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Could not parse ID resp\n"); + abort(); + } + + out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); + if (msg != out) { + printf("FAIL: The message should not have been changed\n"); + abort(); + } + + verify_msg(out, cc_setup_international, ARRAY_SIZE(cc_setup_international)); + talloc_free(parsed); + + /* verify that something in the message changes */ + msgb_reset(msg); + copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Could not parse ID resp\n"); + abort(); + } + + out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); + if (!out) { + printf("FAIL: A new message should be created.\n"); + abort(); + } + + if (msg == out) { + printf("FAIL: The message should have changed\n"); + abort(); + } + + verify_msg(out, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); + msgb_free(out); + + /* Make sure that a wildcard is matching */ + entry.mnc = "*"; + bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); + msg = msgb_alloc(4096, "test_dt_filter"); + copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Could not parse ID resp\n"); + abort(); + } + + out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); + if (!out) { + printf("FAIL: A new message should be created.\n"); + abort(); + } + + if (msg == out) { + printf("FAIL: The message should have changed\n"); + abort(); + } + + verify_msg(out, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); + msgb_free(out); + + /* Make sure that a wildcard is matching */ + entry.mnc = "09"; + bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); + msg = msgb_alloc(4096, "test_dt_filter"); + copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Could not parse ID resp\n"); + abort(); + } + + out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); + if (out != msg) { + printf("FAIL: The message should be unchanged.\n"); + abort(); + } + + verify_msg(out, cc_setup_national, ARRAY_SIZE(cc_setup_national)); + msgb_free(out); + + /* Now see what happens to an international number */ + entry.mnc = "*"; + entry.option = "^\\+[0-9][0-9]([1-9])"; + entry.text = "0036"; + bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); + msg = msgb_alloc(4096, "test_dt_filter"); + copy_to_msg(msg, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Could not parse ID resp %d\n", __LINE__); + abort(); + } + + out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); + if (!out) { + printf("FAIL: A new message should be created %d.\n", __LINE__); + abort(); + } + + if (msg == out) { + printf("FAIL: The message should have changed %d\n", __LINE__); + abort(); + } + + verify_msg(out, cc_setup_national_patched_patched, + ARRAY_SIZE(cc_setup_national_patched_patched)); + msgb_free(out); + + /* go from international back to national */ + entry.mnc = "*"; + entry.option = "^\\+([0-9])"; + entry.text = "36"; + bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); + msg = msgb_alloc(4096, "test_dt_filter"); + copy_to_msg(msg, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Could not parse ID resp %d\n", __LINE__); + abort(); + } + + out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); + if (!out) { + printf("FAIL: A new message should be created %d.\n", __LINE__); + abort(); + } + + if (msg == out) { + printf("FAIL: The message should have changed %d\n", __LINE__); + abort(); + } + + verify_msg(out, cc_setup_national_again, + ARRAY_SIZE(cc_setup_national_again)); + msgb_free(out); +} + +static void test_sms_smsc_rewrite() +{ + struct msgb *msg = msgb_alloc(4096, "SMSC rewrite"), *out; + struct bsc_nat_parsed *parsed; + const char *imsi = "515039900406700"; + + struct bsc_nat *nat = bsc_nat_alloc(); + + /* a fake list */ + struct osmo_config_list smsc_entries, dest_entries, clear_entries; + struct osmo_config_entry smsc_entry, dest_entry, clear_entry; + + INIT_LLIST_HEAD(&smsc_entries.entry); + INIT_LLIST_HEAD(&dest_entries.entry); + INIT_LLIST_HEAD(&clear_entries.entry); + smsc_entry.mcc = "^515039"; + smsc_entry.option = "639180000105()"; + smsc_entry.text = "6666666666667"; + llist_add_tail(&smsc_entry.list, &smsc_entries.entry); + dest_entry.mcc = "515"; + dest_entry.mnc = "03"; + dest_entry.option = "^0049"; + dest_entry.text = ""; + llist_add_tail(&dest_entry.list, &dest_entries.entry); + clear_entry.mcc = "^515039"; + clear_entry.option = "^0049"; + clear_entry.text = ""; + llist_add_tail(&clear_entry.list, &clear_entries.entry); + + bsc_nat_num_rewr_entry_adapt(nat, &nat->smsc_rewr, &smsc_entries); + bsc_nat_num_rewr_entry_adapt(nat, &nat->tpdest_match, &dest_entries); + bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, &clear_entries); + + printf("Testing SMSC rewriting.\n"); + + /* + * Check if the SMSC address is changed + */ + copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Could not parse SMS\n"); + abort(); + } + + out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); + if (out == msg) { + printf("FAIL: This should have changed.\n"); + abort(); + } + + verify_msg(out, smsc_rewrite_patched, ARRAY_SIZE(smsc_rewrite_patched)); + msgb_free(out); + + /* clear out the filter for SMSC */ + printf("Attempting to only rewrite the HDR\n"); + bsc_nat_num_rewr_entry_adapt(nat, &nat->smsc_rewr, NULL); + msg = msgb_alloc(4096, "SMSC rewrite"); + copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Could not parse SMS\n"); + abort(); + } + + out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); + if (out == msg) { + printf("FAIL: This should have changed.\n"); + abort(); + } + + verify_msg(out, smsc_rewrite_patched_hdr, ARRAY_SIZE(smsc_rewrite_patched_hdr)); + msgb_free(out); + + /* clear out the next filter */ + printf("Attempting to change nothing.\n"); + bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, NULL); + msg = msgb_alloc(4096, "SMSC rewrite"); + copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Could not parse SMS\n"); + abort(); + } + + out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); + if (out != msg) { + printf("FAIL: This should not have changed.\n"); + abort(); + } + + verify_msg(out, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); + msgb_free(out); +} + +static void test_sms_number_rewrite(void) +{ + struct msgb *msg, *out; + struct bsc_nat_parsed *parsed; + const char *imsi = "515039900406700"; + + struct bsc_nat *nat = bsc_nat_alloc(); + + /* a fake list */ + struct osmo_config_list num_entries, clear_entries; + struct osmo_config_entry num_entry, clear_entry; + + INIT_LLIST_HEAD(&num_entries.entry); + num_entry.mcc = "^515039"; + num_entry.option = "^0049()"; + num_entry.text = "0032"; + llist_add_tail(&num_entry.list, &num_entries.entry); + + bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_num_rewr, &num_entries); + + printf("Testing SMS TP-DA rewriting.\n"); + + /* + * Check if the SMSC address is changed + */ + msg = msgb_alloc(4096, "SMSC rewrite"); + copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Could not parse SMS\n"); + abort(); + } + + out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); + if (out == msg) { + printf("FAIL: This should have changed.\n"); + abort(); + } + + verify_msg(out, smsc_rewrite_num_patched, + ARRAY_SIZE(smsc_rewrite_num_patched)); + msgb_free(out); + + /* + * Now with TP-SRR rewriting enabled + */ + INIT_LLIST_HEAD(&clear_entries.entry); + clear_entry.mcc = "^515039"; + clear_entry.option = ""; + clear_entry.text = ""; + llist_add_tail(&clear_entry.list, &clear_entries.entry); + bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, &clear_entries); + + msg = msgb_alloc(4096, "SMSC rewrite"); + copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + printf("FAIL: Could not parse SMS\n"); + abort(); + } + + out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); + if (out == msg) { + printf("FAIL: This should have changed.\n"); + abort(); + } + + verify_msg(out, smsc_rewrite_num_patched_tp_srr, + ARRAY_SIZE(smsc_rewrite_num_patched_tp_srr)); + msgb_free(out); +} + +static void test_barr_list_parsing(void) +{ + int rc; + int cm, lu; + struct rb_node *node; + struct rb_root root = RB_ROOT; + struct osmo_config_list *lst = osmo_config_list_parse(NULL, "barr.cfg"); + if (lst == NULL) + abort(); + + rc = bsc_nat_barr_adapt(NULL, &root, lst); + if (rc != 0) + abort(); + talloc_free(lst); + + + for (node = rb_first(&root); node; node = rb_next(node)) { + struct bsc_nat_barr_entry *entry; + entry = rb_entry(node, struct bsc_nat_barr_entry, node); + printf("IMSI: %s CM: %d LU: %d\n", entry->imsi, + entry->cm_reject_cause, entry->lu_reject_cause); + } + + /* do the look up now.. */ + rc = bsc_nat_barr_find(&root, "12123119", &cm, &lu); + if (!rc) { + printf("Failed to find the IMSI.\n"); + abort(); + } + + if (cm != 3 || lu != 4) { + printf("Found CM(%d) and LU(%d)\n", cm, lu); + abort(); + } + + /* empty and check that it is empty */ + bsc_nat_barr_adapt(NULL, &root, NULL); + if (!RB_EMPTY_ROOT(&root)) { + printf("Failed to empty the list.\n"); + abort(); + } + + /* check that dup results in an error */ + lst = osmo_config_list_parse(NULL, "barr_dup.cfg"); + if (lst == NULL) { + printf("Failed to parse list with dups\n"); + abort(); + } + + rc = bsc_nat_barr_adapt(NULL, &root, lst); + if (rc != -1) { + printf("It should have failed due dup\n"); + abort(); + } + talloc_free(lst); + + /* dump for reference */ + for (node = rb_first(&root); node; node = rb_next(node)) { + struct bsc_nat_barr_entry *entry; + entry = rb_entry(node, struct bsc_nat_barr_entry, node); + printf("IMSI: %s CM: %d LU: %d\n", entry->imsi, + entry->cm_reject_cause, entry->lu_reject_cause); + + } +} + +static void test_nat_extract_lac() +{ + int res; + struct bsc_connection *bsc; + struct bsc_nat *nat; + struct nat_sccp_connection con; + struct bsc_nat_parsed *parsed; + struct msgb *msg = msgb_alloc(4096, "test-message"); + + printf("Testing LAC extraction from SCCP CR\n"); + + /* initialize the testcase */ + nat = bsc_nat_alloc(); + bsc = bsc_connection_alloc(nat); + bsc->cfg = bsc_config_alloc(nat, "foo"); + + memset(&con, 0, sizeof(con)); + con.bsc = bsc; + + /* create the SCCP CR */ + msg->l2h = msgb_put(msg, ARRAY_SIZE(bssmap_cr)); + memcpy(msg->l2h, bssmap_cr, ARRAY_SIZE(bssmap_cr)); + + /* parse it and pass it on */ + parsed = bsc_nat_parse(msg); + res = bsc_nat_extract_lac(bsc, &con, parsed, msg); + OSMO_ASSERT(res == 0); + + /* verify the LAC */ + OSMO_ASSERT(con.lac == 8210); + OSMO_ASSERT(con.ci == 50000); +} + +int main(int argc, char **argv) +{ + sccp_set_log_area(DSCCP); + osmo_init_logging(&log_info); + + test_filter(); + test_contrack(); + test_paging(); + test_mgcp_ass_tracking(); + test_mgcp_find(); + test_mgcp_rewrite(); + test_mgcp_parse(); + test_cr_filter(); + test_dt_filter(); + test_setup_rewrite(); + test_sms_smsc_rewrite(); + test_sms_number_rewrite(); + test_mgcp_allocations(); + test_barr_list_parsing(); + test_nat_extract_lac(); + + printf("Testing execution completed.\n"); + return 0; +} + +/* stub */ +void bsc_nat_send_mgcp_to_msc(struct bsc_nat *nat, struct msgb *msg) +{ + abort(); +} diff --git a/tests/bsc-nat/bsc_nat_test.ok b/tests/bsc-nat/bsc_nat_test.ok new file mode 100644 index 000000000..ab04f4273 --- /dev/null +++ b/tests/bsc-nat/bsc_nat_test.ok @@ -0,0 +1,39 @@ +Testing BSS Filtering. +Going to test item: 0 +Going to test item: 1 +Going to test item: 2 +Going to test item: 3 +Going to test item: 4 +Going to test item: 5 +Going to test item: 6 +Going to test item: 7 +Going to test item: 8 +Going to test item: 9 +Going to test item: 10 +Going to test item: 11 +Going to test item: 12 +Testing connection tracking. +Testing paging by lac. +Testing MGCP. +Testing finding of a BSC Connection +Testing rewriting MGCP messages. +Testing MGCP response parsing. +Testing SMSC rewriting. +Attempting to only rewrite the HDR +Attempting to change nothing. +Testing SMS TP-DA rewriting. +IMSI: 12123115 CM: 3 LU: 4 +IMSI: 12123116 CM: 3 LU: 4 +IMSI: 12123117 CM: 3 LU: 4 +IMSI: 12123118 CM: 3 LU: 4 +IMSI: 12123119 CM: 3 LU: 4 +IMSI: 12123120 CM: 3 LU: 4 +IMSI: 12123123 CM: 3 LU: 1 +IMSI: 12123124 CM: 3 LU: 2 +IMSI: 12123125 CM: 3 LU: 3 +IMSI: 12123126 CM: 3 LU: 4 +IMSI: 12123127 CM: 3 LU: 5 +IMSI: 12123128 CM: 3 LU: 6 +IMSI: 12123124 CM: 3 LU: 2 +Testing LAC extraction from SCCP CR +Testing execution completed. diff --git a/tests/channel/Makefile.am b/tests/channel/Makefile.am new file mode 100644 index 000000000..89f015a8a --- /dev/null +++ b/tests/channel/Makefile.am @@ -0,0 +1,12 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) + +EXTRA_DIST = channel_test.ok + +noinst_PROGRAMS = channel_test + +channel_test_SOURCES = channel_test.c +channel_test_LDADD = -ldl $(LIBOSMOCORE_LIBS) \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a -ldbi $(LIBOSMOGSM_LIBS) diff --git a/tests/channel/channel_test.c b/tests/channel/channel_test.c new file mode 100644 index 000000000..ab0d9eb0b --- /dev/null +++ b/tests/channel/channel_test.c @@ -0,0 +1,96 @@ +/* + * (C) 2009 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include + +#include +#include + +#include +#include +#include + +static int s_end = 0; +static struct gsm_subscriber_connection s_conn; + +/* our handler */ +static int subscr_cb(unsigned int hook, unsigned int event, struct msgb *msg, void *data, void *param) +{ + assert(hook == 101); + assert(event == 200); + assert(msg == (void*)0x1323L); + assert(data == &s_conn); + assert(param == (void*)0x2342L); + printf("Reached, didn't crash, test passed\n"); + s_end = true; + return 0; +} + +/* mock object for testing, directly invoke the cb... maybe later through the timer */ +void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscriber, int type, gsm_cbfn *cbfn, void *data) +{ + cbfn(101, 200, (void*)0x1323L, &s_conn, data); +} + + +int main(int argc, char **argv) +{ + struct gsm_network *network; + struct gsm_bts *bts; + + osmo_init_logging(&log_info); + + printf("Testing the gsm_subscriber chan logic\n"); + + /* Create a dummy network */ + network = gsm_network_init(1, 1, NULL); + if (!network) + exit(1); + bts = gsm_bts_alloc(network); + bts->location_area_code = 23; + + /* Create a dummy subscriber */ + struct gsm_subscriber *subscr = subscr_alloc(); + subscr->lac = 23; + subscr->net = network; + + /* Ask for a channel... */ + subscr_get_channel(subscr, RSL_CHANNEED_TCH_F, subscr_cb, (void*)0x2342L); + + while (!s_end) { + osmo_select_main(0); + } + + return EXIT_SUCCESS; +} + +void _abis_nm_sendmsg() {} +void sms_alloc() {} +void gsm_net_update_ctype(struct gsm_network *network) {} +void gsm48_secure_channel() {} +void paging_request_stop() {} +void vty_out() {} +void* connection_for_subscr(void) { abort(); return NULL; } + + +struct tlv_definition nm_att_tlvdef; + diff --git a/tests/channel/channel_test.ok b/tests/channel/channel_test.ok new file mode 100644 index 000000000..7976aee0f --- /dev/null +++ b/tests/channel/channel_test.ok @@ -0,0 +1,2 @@ +Testing the gsm_subscriber chan logic +Reached, didn't crash, test passed diff --git a/tests/db/Makefile.am b/tests/db/Makefile.am new file mode 100644 index 000000000..6f735d04b --- /dev/null +++ b/tests/db/Makefile.am @@ -0,0 +1,17 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBSMPP34_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +EXTRA_DIST = db_test.ok + +noinst_PROGRAMS = db_test + +db_test_SOURCES = db_test.c +db_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOGSM_LIBS) $(LIBSMPP34_LIBS) $(LIBOSMOVTY_LIBS) -ldl -ldbi + diff --git a/tests/db/db_test.c b/tests/db/db_test.c new file mode 100644 index 000000000..c0ee618e7 --- /dev/null +++ b/tests/db/db_test.c @@ -0,0 +1,118 @@ +/* (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 General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include + +#include +#include +#include + +static struct gsm_network dummy_net; + +#define SUBSCR_PUT(sub) \ + sub->net = &dummy_net; \ + subscr_put(sub); + +#define COMPARE(original, copy) \ + if (original->id != copy->id) \ + printf("Ids do not match in %s:%d %llu %llu\n", \ + __FUNCTION__, __LINE__, original->id, copy->id); \ + if (original->lac != copy->lac) \ + printf("LAC do not match in %s:%d %d %d\n", \ + __FUNCTION__, __LINE__, original->lac, copy->lac); \ + if (original->authorized != copy->authorized) \ + printf("Authorize do not match in %s:%d %d %d\n", \ + __FUNCTION__, __LINE__, original->authorized, \ + copy->authorized); \ + if (strcmp(original->imsi, copy->imsi) != 0) \ + printf("IMSIs do not match in %s:%d '%s' '%s'\n", \ + __FUNCTION__, __LINE__, original->imsi, copy->imsi); \ + if (original->tmsi != copy->tmsi) \ + printf("TMSIs do not match in %s:%d '%u' '%u'\n", \ + __FUNCTION__, __LINE__, original->tmsi, copy->tmsi); \ + if (strcmp(original->name, copy->name) != 0) \ + printf("names do not match in %s:%d '%s' '%s'\n", \ + __FUNCTION__, __LINE__, original->name, copy->name); \ + if (strcmp(original->extension, copy->extension) != 0) \ + printf("names do not match in %s:%d '%s' '%s'\n", \ + __FUNCTION__, __LINE__, original->extension, copy->extension); \ + +int main() +{ + printf("Testing subscriber database code.\n"); + osmo_init_logging(&log_info); + + if (db_init("hlr.sqlite3")) { + printf("DB: Failed to init database. Please check the option settings.\n"); + return 1; + } + printf("DB: Database initialized.\n"); + + if (db_prepare()) { + printf("DB: Failed to prepare database.\n"); + return 1; + } + printf("DB: Database prepared.\n"); + + struct gsm_subscriber *alice = NULL; + struct gsm_subscriber *alice_db; + + char *alice_imsi = "3243245432345"; + alice = db_create_subscriber(NULL, alice_imsi); + db_sync_subscriber(alice); + alice_db = db_get_subscriber(NULL, GSM_SUBSCRIBER_IMSI, alice->imsi); + COMPARE(alice, alice_db); + SUBSCR_PUT(alice_db); + SUBSCR_PUT(alice); + + alice_imsi = "3693245423445"; + alice = db_create_subscriber(NULL, alice_imsi); + db_subscriber_assoc_imei(alice, "1234567890"); + db_subscriber_alloc_tmsi(alice); + alice->lac=42; + db_sync_subscriber(alice); + alice_db = db_get_subscriber(NULL, GSM_SUBSCRIBER_IMSI, alice_imsi); + COMPARE(alice, alice_db); + SUBSCR_PUT(alice); + SUBSCR_PUT(alice_db); + + alice_imsi = "9993245423445"; + alice = db_create_subscriber(NULL, alice_imsi); + db_subscriber_alloc_tmsi(alice); + alice->lac=42; + db_sync_subscriber(alice); + db_subscriber_assoc_imei(alice, "1234567890"); + db_subscriber_assoc_imei(alice, "6543560920"); + alice_db = db_get_subscriber(NULL, GSM_SUBSCRIBER_IMSI, alice_imsi); + COMPARE(alice, alice_db); + SUBSCR_PUT(alice); + SUBSCR_PUT(alice_db); + + db_fini(); + + printf("Done\n"); + return 0; +} + +/* stubs */ +void vty_out() {} diff --git a/tests/db/db_test.ok b/tests/db/db_test.ok new file mode 100644 index 000000000..2632a8c8a --- /dev/null +++ b/tests/db/db_test.ok @@ -0,0 +1,4 @@ +Testing subscriber database code. +DB: Database initialized. +DB: Database prepared. +Done diff --git a/tests/gprs/Makefile.am b/tests/gprs/Makefile.am new file mode 100644 index 000000000..904d567c1 --- /dev/null +++ b/tests/gprs/Makefile.am @@ -0,0 +1,8 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) + +EXTRA_DIST = gprs_test.ok + +noinst_PROGRAMS = gprs_test + +gprs_test_SOURCES = gprs_test.c diff --git a/tests/gprs/gprs_test.c b/tests/gprs/gprs_test.c new file mode 100644 index 000000000..7b0e64113 --- /dev/null +++ b/tests/gprs/gprs_test.c @@ -0,0 +1,51 @@ +#include +#include +#include + +#include + +#define ASSERT_FALSE(x) if (x) { printf("Should have returned false.\n"); abort(); } +#define ASSERT_TRUE(x) if (!x) { printf("Should have returned true.\n"); abort(); } + +/** + * GSM 04.64 8.4.2 Receipt of unacknowledged information + */ +static int nu_is_retransmission(uint16_t nu, uint16_t vur) +{ + int ret = gprs_llc_is_retransmit(nu, vur); + printf("N(U) = %d, V(UR) = %d => %s\n", nu, vur, + ret == 1 ? "retransmit" : "new"); + return ret; +} + +static void test_8_4_2() +{ + printf("Testing gprs_llc_is_retransmit.\n"); + + ASSERT_FALSE(nu_is_retransmission(0, 0)); + ASSERT_TRUE (nu_is_retransmission(0, 1)); + + /* expect 1... check for retransmissions */ + ASSERT_TRUE (nu_is_retransmission(0, 1)); + ASSERT_TRUE (nu_is_retransmission(511, 1)); + ASSERT_TRUE (nu_is_retransmission(483, 1)); + ASSERT_TRUE (nu_is_retransmission(482, 1)); + ASSERT_FALSE(nu_is_retransmission(481, 1)); + + /* expect 511... check for retransmissions */ + ASSERT_FALSE(nu_is_retransmission(0, 240)); // ahead + ASSERT_FALSE(nu_is_retransmission(0, 511)); // ahead + ASSERT_FALSE(nu_is_retransmission(1, 511)); // ahead + ASSERT_FALSE(nu_is_retransmission(511, 511)); // same + ASSERT_TRUE (nu_is_retransmission(510, 511)); // behind + ASSERT_TRUE (nu_is_retransmission(481, 511)); // behind + ASSERT_FALSE(nu_is_retransmission(479, 511)); // wrapped +} + +int main(int argc, char **argv) +{ + test_8_4_2(); + + printf("Done.\n"); + return EXIT_SUCCESS; +} diff --git a/tests/gprs/gprs_test.ok b/tests/gprs/gprs_test.ok new file mode 100644 index 000000000..39d37c120 --- /dev/null +++ b/tests/gprs/gprs_test.ok @@ -0,0 +1,16 @@ +Testing gprs_llc_is_retransmit. +N(U) = 0, V(UR) = 0 => new +N(U) = 0, V(UR) = 1 => retransmit +N(U) = 0, V(UR) = 1 => retransmit +N(U) = 511, V(UR) = 1 => retransmit +N(U) = 483, V(UR) = 1 => retransmit +N(U) = 482, V(UR) = 1 => retransmit +N(U) = 481, V(UR) = 1 => new +N(U) = 0, V(UR) = 240 => new +N(U) = 0, V(UR) = 511 => new +N(U) = 1, V(UR) = 511 => new +N(U) = 511, V(UR) = 511 => new +N(U) = 510, V(UR) = 511 => retransmit +N(U) = 481, V(UR) = 511 => retransmit +N(U) = 479, V(UR) = 511 => new +Done. diff --git a/tests/gsm0408/Makefile.am b/tests/gsm0408/Makefile.am new file mode 100644 index 000000000..b000c0802 --- /dev/null +++ b/tests/gsm0408/Makefile.am @@ -0,0 +1,11 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) +noinst_PROGRAMS = gsm0408_test + +EXTRA_DIST = gsm0408_test.ok + +gsm0408_test_SOURCES = gsm0408_test.c +gsm0408_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -ldbi diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c new file mode 100644 index 000000000..60a151faf --- /dev/null +++ b/tests/gsm0408/gsm0408_test.c @@ -0,0 +1,104 @@ +/* simple test for the gsm0408 formatting functions */ +/* + * (C) 2008 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#define COMPARE(result, op, value) \ + if (!((result) op (value))) {\ + fprintf(stderr, "Compare failed. Was %x should be %x in %s:%d\n",result, value, __FILE__, __LINE__); \ + exit(-1); \ + } + +#define COMPARE_STR(result, value) \ + if (strcmp(result, value) != 0) { \ + fprintf(stderr, "Compare failed. Was %s should be %s in %s:%d\n",result, value, __FILE__, __LINE__); \ + exit(-1); \ + } + +/* + * Test Location Area Identifier formatting. Table 10.5.3 of 04.08 + */ +static void test_location_area_identifier(void) +{ + struct gsm48_loc_area_id lai48; + + printf("Testing test location area identifier\n"); + + /* + * Test the default/test setup. Coming from + * bsc_hack.c dumps + */ + gsm48_generate_lai(&lai48, 1, 1, 1); + COMPARE(lai48.digits[0], ==, 0x00); + COMPARE(lai48.digits[1], ==, 0xF1); + COMPARE(lai48.digits[2], ==, 0x10); + COMPARE(lai48.lac, ==, htons(0x0001)); + + gsm48_generate_lai(&lai48, 602, 1, 15); + COMPARE(lai48.digits[0], ==, 0x06); + COMPARE(lai48.digits[1], ==, 0xF2); + COMPARE(lai48.digits[2], ==, 0x10); + COMPARE(lai48.lac, ==, htons(0x000f)); +} + +static void test_mi_functionality(void) +{ + const char *imsi_odd = "987654321098763"; + const char *imsi_even = "9876543210987654"; + const uint32_t tmsi = 0xfabeacd0; + uint8_t mi[128]; + unsigned int mi_len; + char mi_parsed[GSM48_MI_SIZE]; + + printf("Testing parsing and generating TMSI/IMSI\n"); + + /* tmsi code */ + mi_len = gsm48_generate_mid_from_tmsi(mi, tmsi); + gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len - 2); + COMPARE((uint32_t)strtoul(mi_parsed, NULL, 10), ==, tmsi); + + /* imsi code */ + mi_len = gsm48_generate_mid_from_imsi(mi, imsi_odd); + gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len -2); + printf("hex: %s\n", osmo_hexdump(mi, mi_len)); + COMPARE_STR(mi_parsed, imsi_odd); + + mi_len = gsm48_generate_mid_from_imsi(mi, imsi_even); + gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len -2); + printf("hex: %s\n", osmo_hexdump(mi, mi_len)); + COMPARE_STR(mi_parsed, imsi_even); +} + +int main(int argc, char **argv) +{ + test_location_area_identifier(); + test_mi_functionality(); + + printf("Done.\n"); + return EXIT_SUCCESS; +} diff --git a/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok new file mode 100644 index 000000000..52c601e2d --- /dev/null +++ b/tests/gsm0408/gsm0408_test.ok @@ -0,0 +1,5 @@ +Testing test location area identifier +Testing parsing and generating TMSI/IMSI +hex: 17 08 99 78 56 34 12 90 78 36 +hex: 17 09 91 78 56 34 12 90 78 56 f4 +Done. diff --git a/tests/mgcp/Makefile.am b/tests/mgcp/Makefile.am new file mode 100644 index 000000000..8c365b416 --- /dev/null +++ b/tests/mgcp/Makefile.am @@ -0,0 +1,14 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +EXTRA_DIST = mgcp_test.ok + +noinst_PROGRAMS = mgcp_test + +mgcp_test_SOURCES = mgcp_test.c + +mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmgcp/libmgcp.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) -lrt $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) diff --git a/tests/mgcp/mgcp_test.c b/tests/mgcp/mgcp_test.c new file mode 100644 index 000000000..6307300f1 --- /dev/null +++ b/tests/mgcp/mgcp_test.c @@ -0,0 +1,362 @@ +/* + * (C) 2011-2012 by Holger Hans Peter Freyther + * (C) 2011-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 + +#define AUEP1 "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n" +#define AUEP1_RET "200 158663169 OK\r\n" +#define AUEP2 "AUEP 18983213 ds/e1-2/1@172.16.6.66 MGCP 1.0\r\n" +#define AUEP2_RET "500 18983213 FAIL\r\n" +#define EMPTY "\r\n" +#define EMPTY_RET NULL +#define SHORT "CRCX \r\n" +#define SHORT_RET "510 000000 FAIL\r\n" + +#define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@172.16.6.66 MGCP 1.0\r\n" +#define MDCX_ERR_RET "510 18983213 FAIL\r\n" +#define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n" +#define MDCX_RET "400 18983214 FAIL\r\n" +#define MDCX3 "MDCX 18983215 1@mgw MGCP 1.0\r\n" +#define MDCX3_RET "200 18983215 OK\r\n" \ + "I: 1\n" \ + "\n" \ + "v=0\r\n" \ + "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "c=IN IP4 0.0.0.0\r\n" \ + "t=0 0\r\n" \ + "m=audio 0 RTP/AVP 126\r\n" \ + "a=rtpmap:126 AMR/8000\r\n" + +#define SHORT2 "CRCX 1" +#define SHORT2_RET "510 000000 FAIL\r\n" +#define SHORT3 "CRCX 1 1@mgw" +#define SHORT4 "CRCX 1 1@mgw MGCP" +#define SHORT5 "CRCX 1 1@mgw MGCP 1.0" + +#define CRCX "CRCX 2 1@mgw MGCP 1.0\r\n" \ + "M: sendrecv\r\n" \ + "C: 2\r\n" \ + "\r\n" \ + "v=0\r\n" \ + "c=IN IP4 123.12.12.123\r\n" \ + "m=audio 5904 RTP/AVP 97\r\n" \ + "a=rtpmap:97 GSM-EFR/8000\r\n" + +#define CRCX_RET "200 2 OK\r\n" \ + "I: 1\n" \ + "\n" \ + "v=0\r\n" \ + "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "c=IN IP4 0.0.0.0\r\n" \ + "t=0 0\r\n" \ + "m=audio 0 RTP/AVP 126\r\n" \ + "a=rtpmap:126 AMR/8000\r\n" + + +#define CRCX_ZYN "CRCX 2 1@mgw MGCP 1.0\r" \ + "M: sendrecv\r" \ + "C: 2\r\r" \ + "v=0\r" \ + "c=IN IP4 123.12.12.123\r" \ + "m=audio 5904 RTP/AVP 97\r" \ + "a=rtpmap:97 GSM-EFR/8000\r" + +#define CRCX_ZYN_RET "200 2 OK\r\n" \ + "I: 2\n" \ + "\n" \ + "v=0\r\n" \ + "o=- 2 23 IN IP4 0.0.0.0\r\n" \ + "c=IN IP4 0.0.0.0\r\n" \ + "t=0 0\r\n" \ + "m=audio 0 RTP/AVP 126\r\n" \ + "a=rtpmap:126 AMR/8000\r\n" + +#define DLCX "DLCX 7 1@mgw MGCP 1.0\r\n" \ + "C: 2\r\n" + +#define DLCX_RET "250 7 OK\r\n" \ + "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" + +#define RQNT "RQNT 186908780 1@mgw MGCP 1.0\r\n" \ + "X: B244F267488\r\n" \ + "S: D/9\r\n" + +#define RQNT2 "RQNT 186908781 1@mgw MGCP 1.0\r\n" \ + "X: ADD4F26746F\r\n" \ + "R: D/[0-9#*](N), G/ft, fxr/t38\r\n" + +#define RQNT1_RET "200 186908780 OK\r\n" +#define RQNT2_RET "200 186908781 OK\r\n" + +struct mgcp_test { + const char *name; + const char *req; + const char *exp_resp; +}; + +static const struct mgcp_test tests[] = { + { "AUEP1", AUEP1, AUEP1_RET }, + { "AUEP2", AUEP2, AUEP2_RET }, + { "MDCX1", MDCX_WRONG_EP, MDCX_ERR_RET }, + { "MDCX2", MDCX_UNALLOCATED, MDCX_RET }, + { "CRCX", CRCX, CRCX_RET }, + { "MDCX3", MDCX3, MDCX3_RET }, + { "DLCX", DLCX, DLCX_RET }, + { "CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET }, + { "EMPTY", EMPTY, EMPTY_RET }, + { "SHORT1", SHORT, SHORT_RET }, + { "SHORT2", SHORT2, SHORT2_RET }, + { "SHORT3", SHORT3, SHORT2_RET }, + { "SHORT4", SHORT4, SHORT2_RET }, + { "RQNT1", RQNT, RQNT1_RET }, + { "RQNT2", RQNT2, RQNT2_RET }, + { "DLCX", DLCX, DLCX_RET }, +}; + +static const struct mgcp_test retransmit[] = { + { "CRCX", CRCX, CRCX_RET }, + { "RQNT1", RQNT, RQNT1_RET }, + { "RQNT2", RQNT2, RQNT2_RET }, + { "MDCX3", MDCX3, MDCX3_RET }, + { "DLCX", DLCX, DLCX_RET }, +}; + +static struct msgb *create_msg(const char *str) +{ + struct msgb *msg; + + msg = msgb_alloc_headroom(4096, 128, "MGCP msg"); + int len = sprintf((char *)msg->data, "%s", str); + msg->l2h = msgb_put(msg, len); + return msg; +} + +static void test_messages(void) +{ + struct mgcp_config *cfg; + int i; + + cfg = mgcp_config_alloc(); + + cfg->trunk.number_endpoints = 64; + mgcp_endpoints_allocate(&cfg->trunk); + + mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + const struct mgcp_test *t = &tests[i]; + struct msgb *inp; + struct msgb *msg; + + printf("Testing %s\n", t->name); + + inp = create_msg(t->req); + msg = mgcp_handle_message(cfg, inp); + msgb_free(inp); + if (!t->exp_resp) { + if (msg) + printf("%s failed '%s'\n", t->name, (char *) msg->data); + } else if (strcmp((char *) msg->data, t->exp_resp) != 0) + printf("%s failed '%s'\n", t->name, (char *) msg->data); + msgb_free(msg); + } + + talloc_free(cfg); +} + +static void test_retransmission(void) +{ + struct mgcp_config *cfg; + int i; + + cfg = mgcp_config_alloc(); + + cfg->trunk.number_endpoints = 64; + mgcp_endpoints_allocate(&cfg->trunk); + + mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); + + for (i = 0; i < ARRAY_SIZE(retransmit); i++) { + const struct mgcp_test *t = &retransmit[i]; + struct msgb *inp; + struct msgb *msg; + + printf("Testing %s\n", t->name); + + inp = create_msg(t->req); + msg = mgcp_handle_message(cfg, inp); + msgb_free(inp); + if (strcmp((char *) msg->data, t->exp_resp) != 0) + printf("%s failed '%s'\n", t->name, (char *) msg->data); + msgb_free(msg); + + /* Retransmit... */ + printf("Re-transmitting %s\n", t->name); + inp = create_msg(t->req); + msg = mgcp_handle_message(cfg, inp); + msgb_free(inp); + if (strcmp((char *) msg->data, t->exp_resp) != 0) + printf("%s failed '%s'\n", t->name, (char *) msg->data); + msgb_free(msg); + } + + talloc_free(cfg); +} + +static int rqnt_cb(struct mgcp_endpoint *endp, char _tone) +{ + ptrdiff_t tone = _tone; + endp->cfg->data = (void *) tone; + return 0; +} + +static void test_rqnt_cb(void) +{ + struct mgcp_config *cfg; + struct msgb *inp, *msg; + + cfg = mgcp_config_alloc(); + cfg->rqnt_cb = rqnt_cb; + + cfg->trunk.number_endpoints = 64; + mgcp_endpoints_allocate(&cfg->trunk); + + mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); + + inp = create_msg(CRCX); + msgb_free(mgcp_handle_message(cfg, inp)); + msgb_free(inp); + + /* send the RQNT and check for the CB */ + inp = create_msg(RQNT); + msg = mgcp_handle_message(cfg, inp); + if (strncmp((const char *) msg->l2h, "200", 3) != 0) { + printf("FAILED: message is not 200. '%s'\n", msg->l2h); + abort(); + } + + if (cfg->data != (void *) '9') { + printf("FAILED: callback not called: %p\n", cfg->data); + abort(); + } + + msgb_free(msg); + msgb_free(inp); + + inp = create_msg(DLCX); + msgb_free(mgcp_handle_message(cfg, inp)); + msgb_free(inp); + talloc_free(cfg); +} + +struct pl_test { + int cycles; + uint16_t base_seq; + uint16_t max_seq; + uint32_t packets; + + uint32_t expected; + int loss; +}; + +static const struct pl_test pl_test_dat[] = { + /* basic.. just one package */ + { .cycles = 0, .base_seq = 0, .max_seq = 0, .packets = 1, .expected = 1, .loss = 0}, + /* some packages and a bit of loss */ + { .cycles = 0, .base_seq = 0, .max_seq = 100, .packets = 100, .expected = 101, .loss = 1}, + /* wrap around */ + { .cycles = 1<<16, .base_seq = 0xffff, .max_seq = 2, .packets = 4, .expected = 4, .loss = 0}, + /* min loss */ + { .cycles = 0, .base_seq = 0, .max_seq = 0, .packets = UINT_MAX, .expected = 1, .loss = INT_MIN }, + /* max loss, with wrap around on expected max */ + { .cycles = INT_MAX, .base_seq = 0, .max_seq = UINT16_MAX, .packets = 0, .expected = ((uint32_t)(INT_MAX) + UINT16_MAX + 1), .loss = INT_MAX }, +}; + +static void test_packet_loss_calc(void) +{ + int i; + printf("Testing packet loss calculation.\n"); + + for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) { + uint32_t expected; + int loss; + struct mgcp_rtp_state state; + struct mgcp_rtp_end rtp; + memset(&state, 0, sizeof(state)); + memset(&rtp, 0, sizeof(rtp)); + + state.initialized = 1; + state.base_seq = pl_test_dat[i].base_seq; + state.max_seq = pl_test_dat[i].max_seq; + state.cycles = pl_test_dat[i].cycles; + + rtp.packets = pl_test_dat[i].packets; + mgcp_state_calc_loss(&state, &rtp, &expected, &loss); + + if (loss != pl_test_dat[i].loss || expected != pl_test_dat[i].expected) { + printf("FAIL: Wrong exp/loss at idx(%d) Loss(%d vs. %d) Exp(%u vs. %u)\n", + i, loss, pl_test_dat[i].loss, + expected, pl_test_dat[i].expected); + } + } +} + +static void test_mgcp_stats(void) +{ + printf("Testing stat parsing\n"); + + uint32_t bps, bos, pr, _or, jitter; + struct msgb *msg; + int loss; + int rc; + + msg = create_msg(DLCX_RET); + rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter); + printf("Parsing result: %d\n", rc); + if (bps != 0 || bos != 0 || pr != 0 || _or != 0 || loss != 0 || jitter != 0) + printf("FAIL: Parsing failed1.\n"); + msgb_free(msg); + + msg = create_msg("250 7 OK\r\nP: PS=10, OS=20, PR=30, OR=40, PL=-3, JI=40\r\n"); + rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter); + printf("Parsing result: %d\n", rc); + if (bps != 10 || bos != 20 || pr != 30 || _or != 40 || loss != -3 || jitter != 40) + printf("FAIL: Parsing failed2.\n"); + msgb_free(msg); +} + +int main(int argc, char **argv) +{ + osmo_init_logging(&log_info); + + test_messages(); + test_retransmission(); + test_packet_loss_calc(); + test_rqnt_cb(); + test_mgcp_stats(); + + printf("Done\n"); + return EXIT_SUCCESS; +} diff --git a/tests/mgcp/mgcp_test.ok b/tests/mgcp/mgcp_test.ok new file mode 100644 index 000000000..8711e38f2 --- /dev/null +++ b/tests/mgcp/mgcp_test.ok @@ -0,0 +1,31 @@ +Testing AUEP1 +Testing AUEP2 +Testing MDCX1 +Testing MDCX2 +Testing CRCX +Testing MDCX3 +Testing DLCX +Testing CRCX_ZYN +Testing EMPTY +Testing SHORT1 +Testing SHORT2 +Testing SHORT3 +Testing SHORT4 +Testing RQNT1 +Testing RQNT2 +Testing DLCX +Testing CRCX +Re-transmitting CRCX +Testing RQNT1 +Re-transmitting RQNT1 +Testing RQNT2 +Re-transmitting RQNT2 +Testing MDCX3 +Re-transmitting MDCX3 +Testing DLCX +Re-transmitting DLCX +Testing packet loss calculation. +Testing stat parsing +Parsing result: 0 +Parsing result: 0 +Done diff --git a/tests/si/Makefile.am b/tests/si/Makefile.am new file mode 100644 index 000000000..795bd3081 --- /dev/null +++ b/tests/si/Makefile.am @@ -0,0 +1,12 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) + +EXTRA_DIST = si_test.ok + +noinst_PROGRAMS = si_test + +si_test_SOURCES = si_test.c + +si_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) -lrt $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) diff --git a/tests/si/si_test.c b/tests/si/si_test.c new file mode 100644 index 000000000..16f6ea39d --- /dev/null +++ b/tests/si/si_test.c @@ -0,0 +1,156 @@ + + +#include +#include +#include + +#include + +#include + +#define DBG(...) + +#define VERIFY(res, cmp, wanted) \ + if (!(res cmp wanted)) { \ + printf("ASSERT failed: %s:%d Wanted: %d %s %d\n", \ + __FILE__, __LINE__, res, # cmp, wanted); \ + } + + +static int freqs1[] = { + 12, 70, 121, 190, 250, 320, 401, 475, 520, 574, 634, 700, 764, 830, 905, 980 +}; + +static int freqs2[] = { + 402, 460, 1, 67, 131, 197, 272, 347, +}; + +static int freqs3[] = { + 68, 128, 198, 279, 353, 398, 452, + +}; + +static int w_out[] = { + 122, 2, 69, 204, 75, 66, 60, 70, 83, 3, 24, 67, 54, 64, 70, 9, +}; + +static int range128[] = { + 1, 1 + 127, +}; + +static int range256[] = { + 1, 1 + 128, +}; + +static int range512[] = { + 1, 1+ 511, +}; + + +static void test_arfcn_filter() +{ + int arfcns[50], i, res, f0_included; + for (i = 0; i < ARRAY_SIZE(arfcns); ++i) + arfcns[i] = (i + 1) * 2; + + /* check that the arfcn is taken out. f0_included is only set for Range1024 */ + f0_included = 24; + res = range_enc_filter_arfcns(ARFCN_RANGE_512, arfcns, ARRAY_SIZE(arfcns), + arfcns[0], &f0_included); + VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1); + VERIFY(f0_included, ==, 0); + for (i = 0; i < res; ++i) + VERIFY(arfcns[i], ==, ((i+2) * 2) - (2+1)); + + /* check with range1024 */ + for (i = 0; i < ARRAY_SIZE(arfcns); ++i) + arfcns[i] = (i + 1) * 2; + res = range_enc_filter_arfcns(ARFCN_RANGE_1024, arfcns, ARRAY_SIZE(arfcns), + arfcns[0], &f0_included); + VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1); + VERIFY(f0_included, ==, 1); + for (i = 0; i < res; ++i) + VERIFY(arfcns[i], ==, ((i + 2) * 2) - 1); + + /* check with range1024, not included */ + for (i = 0; i < ARRAY_SIZE(arfcns); ++i) + arfcns[i] = (i + 1) * 2; + res = range_enc_filter_arfcns(ARFCN_RANGE_1024, arfcns, ARRAY_SIZE(arfcns), + 11, &f0_included); + VERIFY(res, ==, ARRAY_SIZE(arfcns)); + VERIFY(f0_included, ==, 0); + for (i = 0; i < res; ++i) + VERIFY(arfcns[i], ==, ((i + 1) * 2) - 1); +} + +static void test_print_encoding() +{ + int rc; + int w[17]; + uint8_t chan_list[16]; + memset(chan_list, 0x23, sizeof(chan_list)); + + for (rc = 0; rc < ARRAY_SIZE(w); ++rc) + switch (rc % 3) { + case 0: + w[rc] = 0xAAAA; + break; + case 1: + w[rc] = 0x5555; + break; + case 2: + w[rc] = 0x9696; + break; + } + + rc = range_enc_range512(chan_list, 0x96, w); + VERIFY(rc, ==, 0); + + printf("Range512: %s\n", osmo_hexdump(chan_list, ARRAY_SIZE(chan_list))); +} + +int main(int argc, char **argv) +{ + int ws[(sizeof(freqs1)/sizeof(freqs1[0]))]; + int i, f0 = 0xFFFFFF; + + memset(&ws[0], 0x23, sizeof(ws)); + + i = range_enc_find_index(1023, freqs1, ARRAY_SIZE(freqs1)); + printf("Element is: %d => freqs[i] = %d\n", i, freqs1[i]); + VERIFY(i, ==, 2); + + i = range_enc_find_index(511, freqs2, ARRAY_SIZE(freqs2)); + printf("Element is: %d => freqs[i] = %d\n", i, freqs2[i]); + VERIFY(i, ==, 2); + + i = range_enc_find_index(511, freqs3, ARRAY_SIZE(freqs3)); + printf("Element is: %d => freqs[i] = %d\n", i, freqs3[i]); + VERIFY(i, ==, 0); + + i = range_enc_arfcns(1023, freqs1, ARRAY_SIZE(freqs1), ws, 0); + VERIFY(i, ==, 0); + + for (i = 0; i < sizeof(freqs1)/sizeof(freqs1[0]); ++i) { + printf("w[%d]=%d\n", i, ws[i]); + VERIFY(ws[i], ==, w_out[i]); + } + + i = range_enc_determine_range(range128, ARRAY_SIZE(range128), &f0); + VERIFY(i, ==, ARFCN_RANGE_128); + VERIFY(f0, ==, 1); + + i = range_enc_determine_range(range256, ARRAY_SIZE(range256), &f0); + VERIFY(i, ==, ARFCN_RANGE_256); + VERIFY(f0, ==, 1); + + i = range_enc_determine_range(range512, ARRAY_SIZE(range512), &f0); + VERIFY(i, ==, ARFCN_RANGE_512); + VERIFY(f0, ==, 1); + + + test_arfcn_filter(); + test_print_encoding(); + + return 0; +} diff --git a/tests/si/si_test.ok b/tests/si/si_test.ok new file mode 100644 index 000000000..6c4b028b0 --- /dev/null +++ b/tests/si/si_test.ok @@ -0,0 +1,20 @@ +Element is: 2 => freqs[i] = 121 +Element is: 2 => freqs[i] = 1 +Element is: 0 => freqs[i] = 68 +w[0]=122 +w[1]=2 +w[2]=69 +w[3]=204 +w[4]=75 +w[5]=66 +w[6]=60 +w[7]=70 +w[8]=83 +w[9]=3 +w[10]=24 +w[11]=67 +w[12]=54 +w[13]=64 +w[14]=70 +w[15]=9 +Range512: 88 4b 2a 95 65 95 55 2c a9 55 aa 55 6a 95 59 55 diff --git a/tests/smpp/Makefile.am b/tests/smpp/Makefile.am new file mode 100644 index 000000000..eb259656b --- /dev/null +++ b/tests/smpp/Makefile.am @@ -0,0 +1,12 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_srcdir)/src/libmsc +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +EXTRA_DIST = smpp_test.ok smpp_test.err + +noinst_PROGRAMS = smpp_test + +smpp_test_SOURCES = smpp_test.c \ + $(top_srcdir)/src/libmsc/smpp_utils.c +smpp_test_LDADD = $(LIBOSMOCORE_LIBS) \ + $(top_srcdir)/src/libcommon/libcommon.a diff --git a/tests/smpp/smpp_test.c b/tests/smpp/smpp_test.c new file mode 100644 index 000000000..62fa9d2e9 --- /dev/null +++ b/tests/smpp/smpp_test.c @@ -0,0 +1,73 @@ +/* + * (C) 2013 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include + +#include +#include + +#include "smpp_smsc.h" + +struct coding_test { + uint8_t dcs; + uint8_t coding; + int mode; + int res; +}; + +static struct coding_test codecs[] = { + { .dcs = 0xf6 , .coding = 0x02, .mode = MODE_8BIT, .res = 0 }, + { .dcs = 0xf2 , .coding = 0x01, .mode = MODE_7BIT, .res = 0 }, + { .dcs = 0x02 , .coding = 0x01, .mode = MODE_7BIT, .res = 0 }, + { .dcs = 0x06 , .coding = 0x02, .mode = MODE_8BIT, .res = 0 }, + { .dcs = 0x0A , .coding = 0x08, .mode = MODE_8BIT, .res = 0 }, + { .dcs = 0x0E , .coding = 0xFF, .mode = 0xFF, .res = -1 }, + { .dcs = 0xE0 , .coding = 0xFF, .mode = 0xFF, .res = -1 }, +}; + +static void test_coding_scheme(void) +{ + int i; + printf("Testing coding scheme support\n"); + + for (i = 0; i < ARRAY_SIZE(codecs); ++i) { + uint8_t coding; + int mode, res; + + res = smpp_determine_scheme(codecs[i].dcs, &coding, &mode); + OSMO_ASSERT(res == codecs[i].res); + if (res != -1) { + OSMO_ASSERT(mode == codecs[i].mode); + OSMO_ASSERT(coding == codecs[i].coding); + } + } +} + +int main(int argc, char **argv) +{ + osmo_init_logging(&log_info); + log_set_use_color(osmo_stderr_target, 0); + log_set_print_filename(osmo_stderr_target, 0); + + test_coding_scheme(); + return EXIT_SUCCESS; +} diff --git a/tests/smpp/smpp_test.err b/tests/smpp/smpp_test.err new file mode 100644 index 000000000..ec966ba47 --- /dev/null +++ b/tests/smpp/smpp_test.err @@ -0,0 +1,2 @@ +SMPP MO Unknown Data Coding 0x0e +SMPP MO Unknown Data Coding 0xe0 diff --git a/tests/smpp/smpp_test.ok b/tests/smpp/smpp_test.ok new file mode 100644 index 000000000..fd44804d1 --- /dev/null +++ b/tests/smpp/smpp_test.ok @@ -0,0 +1 @@ +Testing coding scheme support diff --git a/tests/sms.txt b/tests/sms.txt new file mode 100644 index 000000000..06c885b8b --- /dev/null +++ b/tests/sms.txt @@ -0,0 +1,50 @@ +03 02 01 0a 02 43 0b 00 1d 39 01 1a 00 01 00 07 91 55 11 18 31 28 00 0e 31 20 04 81 21 43 00 00 ff 04 d4 f2 9c 0e + +03 02 01 0a 02 43 0b 00 1d - +39 - TransactionID 3, SMS messages :: gh->proto_discr +01 - CP-DATA :: gh->msg_type +1a - Length: 26 :: gh->data[0] +00 - MTI 0 RP-DATA (ms->n) +01 - MR 1 +00 - RP-OA +07 - RP-DA (SMSC Length) +91 - International No, Numbering Plan +55 11 18 31 28 00 - 551181138200 +0e - RP-UD len (14) + +TPDU (14 byte): +31 - MTI(01), VPF(2:relative), MMS(0), SRI(1), UDHI(0), RP(0) +20 - Message Reference +04 - DA length +81 - Numbering Plan, National number +21 43 - DA 1234 +00 - PID +00 - DCS +ff - Validity period +04 - User Data length (04) +d4 f2 9c 0e - gsm_default 7bit encoded "Test" (4 byte) + +03 02 01 0a 02 43 0b 00 9f 09 01 9c 00 da 00 07 91 88 96 13 00 00 99 90 11 7b 04 81 22 22 00 08 ff 86 6c 38 8c 50 92 80 88 4c 00 4d 00 4d 00 41 6a 19 67 03 74 06 8c a1 7d b2 00 20 00 20 51 68 74 03 99 96 52 75 7d b2 8d ef 6a 19 67 03 ff 0c 6a 19 67 03 96 f6 98 a8 96 aa ff 01 8b 93 60 a8 80 70 66 0e 51 32 84 c4 ff 0c 97 48 6d 3b 62 95 8c c7 ff 01 73 fe 57 28 52 a0 51 65 90 01 96 50 91 cf 59 27 80 6f 76 df 6d 0b 57 fa 96 8a 91 77 5e 63 53 61 ff 0c 8a cb 4e 0a 7d b2 64 1c 5c 0b 30 0c 6a 19 67 03 30 0d + +03 02 01 0a 02 43 0b 00 9f - lower levels +09 - TransactionID 0, SMS messages +01 - CP-DATA +9c - Length: (156) +00 - MTI 0 RP-DATA (ms->n) +da - MR (?) +00 - RP-OA +07 - RP-DA (SMSC Length) +91 - International No. +88 96 13 00 00 99 +90 - RP-UD len (144) +11 - +7b - Message Reference +04 - DA length +81 - Numbering Plan +22 22 - Address 2222 +00 - PID +08 - DCS (UCS2 charset) +ff - Validity period +86 - User Data length (134) +6c 38 8c 50 92 80 88 4c 00 4d 00 4d 00 41 6a 19 67 03 74 06 8c a1 7d b2 00 20 00 20 51 68 74 03 99 96 52 75 7d b2 8d ef 6a 19 67 03 ff 0c 6a 19 67 03 96 f6 98 a8 96 aa ff 01 8b 93 60 a8 80 70 66 0e 51 32 84 c4 ff 0c 97 48 6d 3b 62 95 8c c7 ff 01 73 fe 57 28 52 a0 51 65 90 01 96 50 91 cf 59 27 80 6f 76 df 6d 0b 57 fa 96 8a 91 77 5e 63 53 61 ff 0c 8a cb 4e 0a 7d b2 64 1c 5c 0b 30 0c 6a 19 67 03 30 0d + diff --git a/tests/testsuite.at b/tests/testsuite.at new file mode 100644 index 000000000..ea3fa1ce5 --- /dev/null +++ b/tests/testsuite.at @@ -0,0 +1,61 @@ +AT_INIT +AT_BANNER([Regression tests.]) + +AT_SETUP([gsm0408]) +AT_KEYWORDS([gsm0408]) +cat $abs_srcdir/gsm0408/gsm0408_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([db]) +AT_KEYWORDS([db]) +cat $abs_srcdir/db/db_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/db/db_test], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([channel]) +AT_KEYWORDS([channel]) +cat $abs_srcdir/channel/channel_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/channel/channel_test], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([mgcp]) +AT_KEYWORDS([mgcp]) +cat $abs_srcdir/mgcp/mgcp_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([gprs]) +AT_KEYWORDS([gprs]) +cat $abs_srcdir/gprs/gprs_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/gprs/gprs_test], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([bsc-nat]) +AT_KEYWORDS([bsc-nat]) +AT_CHECK([test "$enable_nat_test" != no || exit 77]) +cp $abs_srcdir/bsc-nat/barr.cfg . +cp $abs_srcdir/bsc-nat/barr_dup.cfg . +cat $abs_srcdir/bsc-nat/bsc_nat_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/bsc-nat/bsc_nat_test], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([smpp]) +AT_KEYWORDS([smpp]) +AT_CHECK([test "$enable_smpp_test" != no || exit 77]) +cat $abs_srcdir/smpp/smpp_test.ok > expout +cat $abs_srcdir/smpp/smpp_test.err > experr +AT_CHECK([$abs_top_builddir/tests/smpp/smpp_test], [], [expout], [experr]) +AT_CLEANUP + +AT_SETUP([si]) +AT_KEYWORDS([si]) +cat $abs_srcdir/si/si_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/si/si_test], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([abis]) +AT_KEYWORDS([abis]) +cat $abs_srcdir/abis/abis_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/abis/abis_test], [], [expout], [ignore]) +AT_CLEANUP diff --git a/tests/vty_test_runner.py b/tests/vty_test_runner.py new file mode 100644 index 000000000..778cde04c --- /dev/null +++ b/tests/vty_test_runner.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python + +# (C) 2013 by Katerina Barone-Adesi +# (C) 2013 by Holger Hans Peter Freyther +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os +import time +import unittest + +import osmopy.obscvty as obscvty +import osmopy.osmoutil as osmoutil + +confpath = '.' + +class TestVTYBase(unittest.TestCase): + + def vty_command(self): + raise Exception("Needs to be implemented by a subclass") + + def vty_app(self): + raise Exception("Needs to be implemented by a subclass") + + def setUp(self): + osmo_vty_cmd = self.vty_command()[:] + config_index = osmo_vty_cmd.index('-c') + if config_index: + cfi = config_index + 1 + osmo_vty_cmd[cfi] = os.path.join(confpath, osmo_vty_cmd[cfi]) + + try: + print "Launch: %s from %s" % (' '.join(osmo_vty_cmd), os.getcwd()) + self.proc = osmoutil.popen_devnull(osmo_vty_cmd) + except OSError: + print >> sys.stderr, "Current directory: %s" % os.getcwd() + print >> sys.stderr, "Consider setting -b" + time.sleep(1) + + appstring = self.vty_app()[2] + appport = self.vty_app()[0] + self.vty = obscvty.VTYInteract(appstring, "127.0.0.1", appport) + + def tearDown(self): + self.vty = None + osmoutil.end_proc(self.proc) + +class TestVTYNITB(TestVTYBase): + + def vty_command(self): + return ["./src/osmo-nitb/osmo-nitb", "-c", + "doc/examples/osmo-nitb/nanobts/openbsc.cfg"] + + def vty_app(self): + return (4242, "./src/osmo-nitb/osmo-nitb", "OpenBSC", "nitb") + + def testEnableDisablePeriodicLU(self): + self.vty.enable() + self.vty.command("configure terminal") + self.vty.command("network") + self.vty.command("bts 0") + + # Test invalid input + self.vty.verify("periodic location update 0", ['% Unknown command.']) + self.vty.verify("periodic location update 5", ['% Unknown command.']) + self.vty.verify("periodic location update 1531", ['% Unknown command.']) + + # Enable periodic lu.. + self.vty.verify("periodic location update 60", ['']) + res = self.vty.command("write terminal") + self.assert_(res.find('periodic location update 60') > 0) + self.assertEquals(res.find('no periodic location update'), -1) + + # Now disable it.. + self.vty.verify("no periodic location update", ['']) + res = self.vty.command("write terminal") + self.assertEquals(res.find('periodic location update 60'), -1) + self.assert_(res.find('no periodic location update') > 0) + +class TestVTYNAT(TestVTYBase): + + def vty_command(self): + return ["./src/osmo-bsc_nat/osmo-bsc_nat", "-c", + "doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"] + + def vty_app(self): + return (4244, "src/osmo-bsc_nat/osmo-bsc_nat", "OsmoBSCNAT", "nat") + + def testMoo(self): + pass + + +def add_nat_test(suite, workdir): + if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc_nat/osmo-bsc_nat")): + print("Skipping the NAT test") + return + test = unittest.TestLoader().loadTestsFromTestCase(TestVTYNAT) + suite.addTest(test) + +if __name__ == '__main__': + import argparse + import sys + + workdir = '.' + + parser = argparse.ArgumentParser() + parser.add_argument("-v", "--verbose", dest="verbose", + action="store_true", help="verbose mode") + parser.add_argument("-p", "--pythonconfpath", dest="p", + help="searchpath for config") + parser.add_argument("-w", "--workdir", dest="w", + help="Working directory") + args = parser.parse_args() + + verbose_level = 1 + if args.verbose: + verbose_level = 2 + + if args.w: + workdir = args.w + + if args.p: + confpath = args.p + + print "confpath %s, workdir %s" % (confpath, workdir) + os.chdir(workdir) + print "Running tests for specific VTY commands" + suite = unittest.TestSuite() + suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestVTYNITB)) + add_nat_test(suite, workdir) + res = unittest.TextTestRunner(verbosity=verbose_level).run(suite) + sys.exit(len(res.errors) + len(res.failures)) diff --git a/tools/hlrstat.pl b/tools/hlrstat.pl new file mode 100755 index 000000000..668fc9a4a --- /dev/null +++ b/tools/hlrstat.pl @@ -0,0 +1,73 @@ +#!/usr/bin/perl + +use strict; +use DBI; +my $dbh = DBI->connect("dbi:SQLite:dbname=hlr.sqlite3","",""); + + +my %mcc_names; +my %mcc_mnc_names; + +sub get_mcc_mnc_name($) +{ + my $mcc_mnc = shift; + my $ret = $mcc_mnc; + + if ($mcc_mnc_names{$mcc_mnc} ne '') { + $ret = $mcc_mnc_names{$mcc_mnc}; + } + + return $ret; +} + +sub read_networks($) +{ + my $filename = shift; + my $cur_name; + + open(INFILE, $filename); + while (my $l = ) { + chomp($l); + if ($l =~ /^#/) { + next; + } + if ($l =~ /^\t/) { + my ($mcc, $mnc, $brand, $r) = split(' ', $l, 4); + #printf("%s|%s|%s\n", $mcc, $mnc, $brand); + $mcc_mnc_names{"$mcc-$mnc"} = $brand; + $mcc_names{$mcc} = $cur_name; + } elsif ($l =~ /^(\w\w)\t(.*)/) { + #printf("%s|%s\n", $1, $2); + $cur_name = $2; + } + } + close(INFILE); +} + +read_networks("networks.tab"); + +my %oper_count; +my %country_count; + +#my $sth = $dbh->prepare("SELECT imsi FROM subscriber where authorized=1"); +my $sth = $dbh->prepare("SELECT imsi FROM subscriber"); + +$sth->execute(); + +while (my $href = $sth->fetchrow_hashref) { + my ($mcc, $mnc) = $$href{imsi} =~ /(\d{3})(\d{2}).*/; + #printf("%s %s-%s \n", $$href{imsi}, $mcc, $mnc); + $oper_count{"$mcc-$mnc"}++; + $country_count{$mcc}++; +} + + +foreach my $c (sort{$country_count{$b} <=> $country_count{$a}} keys %country_count) { + printf("%s: %d\n", $mcc_names{$c}, $country_count{$c}); + + foreach my $k (sort{$oper_count{$b} <=> $oper_count{$a}} keys %oper_count) { + if ($k =~ /^$c-/) { + printf("\t%s: %d\n", get_mcc_mnc_name($k), $oper_count{$k}); + } + } +} -- cgit v1.2.3