diff options
author | Ruben Undheim <ruben.undheim@gmail.com> | 2018-07-20 22:28:06 +0200 |
---|---|---|
committer | Ruben Undheim <ruben.undheim@gmail.com> | 2018-07-20 22:30:32 +0200 |
commit | 616f07068ce1d53ec1dda14b3ac25c9799f4cecc (patch) | |
tree | 94d84ee18abfd3a76940a4acc219b053f0832d5a /tests |
Imported 1.2.1
Diffstat (limited to 'tests')
44 files changed, 8699 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..652dfe1 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,85 @@ +SUBDIRS = \ + bsc \ + gsm0408 \ + channel \ + abis \ + subscr \ + nanobts_omlattr \ + bsc-nat \ + bsc-nat-trie \ + bssap \ + handover \ + $(NULL) + +# 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 \ + ctrl_test_runner.py \ + handover_cfg.vty \ + $(NULL) + +TESTSUITE = $(srcdir)/testsuite + +DISTCLEANFILES = \ + atconfig \ + $(NULL) + +if ENABLE_EXT_TESTS +python-tests: $(BUILT_SOURCES) + $(MAKE) vty-test + osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v + osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v + $(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v + $(srcdir)/ctrl_test_runner.py -w $(abs_top_builddir) -v + rm -f $(top_builddir)/sms.db $(top_builddir)/gsn_restart $(top_builddir)/gtphub_restart_count + +# To update the VTY script from current application behavior, +# pass -u to vty_script_runner.py by doing: +# make vty-test U=-u +vty-test: + osmo_verify_transcript_vty.py -v \ + -n OsmoBSC -p 4242 \ + -r "$(top_builddir)/src/osmo-bsc/osmo-bsc -c $(top_srcdir)/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg" \ + $(U) $(srcdir)/*.vty +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 0000000..8dc829f --- /dev/null +++ b/tests/abis/Makefile.am @@ -0,0 +1,32 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + -ggdb3 \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(COVERAGE_CFLAGS) \ + $(NULL) + +EXTRA_DIST = \ + abis_test.ok \ + $(NULL) + +noinst_PROGRAMS = \ + abis_test \ + $(NULL) + +abis_test_SOURCES = \ + abis_test.c \ + $(NULL) + +abis_test_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(NULL) diff --git a/tests/abis/abis_test.c b/tests/abis/abis_test.c new file mode 100644 index 0000000..faf9ea5 --- /dev/null +++ b/tests/abis/abis_test.c @@ -0,0 +1,188 @@ +/* + * (C) 2012 by Holger Hans Peter Freyther <zecke@selfish.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/protocol/gsm_12_21.h> +#include <osmocom/gsm/gsm23003.h> + +#include <osmocom/bsc/gsm_data.h> +#include <osmocom/bsc/abis_nm.h> +#include <osmocom/bsc/debug.h> + +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_sw_selection(void) +{ + struct abis_nm_sw_desc descr[8], tmp; + uint16_t len0, len1; + int rc, pos; + + rc = abis_nm_get_sw_conf(load_config, ARRAY_SIZE(load_config), + &descr[0], ARRAY_SIZE(descr)); + if (rc != 2) { + printf("%s(): FAILED to parse the File Id/File version: %d\n", + __func__, rc); + abort(); + } + + len0 = abis_nm_sw_desc_len(&descr[0], true); + printf("len: %u\n", len0); + 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_version, descr[0].file_version_len)); + + len1 = abis_nm_sw_desc_len(&descr[1], true); + printf("len: %u\n", len1); + 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_version, descr[1].file_version_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); + printf("%s(): OK\n", __func__); +} + +struct test_abis_nm_ipaccess_cgi { + struct osmo_plmn_id plmn; + uint16_t lac; + uint16_t cell_identity; + const char *expect; +}; +static const struct test_abis_nm_ipaccess_cgi test_abis_nm_ipaccess_cgi_data[] = { + { + .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = false }, + .lac = 3, + .cell_identity = 4, + .expect = "00f120" "0003" "0004", + }, + { + .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = true }, + .lac = 3, + .cell_identity = 4, + .expect = "002100" "0003" "0004", + }, + { + .plmn = { .mcc = 0, .mnc = 0, .mnc_3_digits = false }, + .lac = 0, + .cell_identity = 0, + .expect = "00f000" "0000" "0000", + }, + { + .plmn = { .mcc = 0, .mnc = 0, .mnc_3_digits = true }, + .lac = 0, + .cell_identity = 0, + .expect = "000000" "0000" "0000", + }, + { + .plmn = { .mcc = 999, .mnc = 999, .mnc_3_digits = false }, + .lac = 65535, + .cell_identity = 65535, + .expect = "999999" "ffff" "ffff", + }, + { + .plmn = { .mcc = 909, .mnc = 90, .mnc_3_digits = false }, + .lac = 0xabcd, + .cell_identity = 0x2345, + .expect = "09f909" "abcd" "2345", + }, + { + .plmn = { .mcc = 909, .mnc = 90, .mnc_3_digits = true }, + .lac = 0xabcd, + .cell_identity = 0x2345, + .expect = "090990" "abcd" "2345", + }, +}; + +static void test_abis_nm_ipaccess_cgi() +{ + int i; + bool pass = true; + + for (i = 0; i < ARRAY_SIZE(test_abis_nm_ipaccess_cgi_data); i++) { + struct gsm_network net; + struct gsm_bts bts; + const struct test_abis_nm_ipaccess_cgi *t = &test_abis_nm_ipaccess_cgi_data[i]; + uint8_t result_buf[7] = {}; + char *result; + bool ok; + + net.plmn = t->plmn; + bts.network = &net; + bts.location_area_code = t->lac; + bts.cell_identity = t->cell_identity; + + abis_nm_ipaccess_cgi(result_buf, &bts); + result = osmo_hexdump_nospc(result_buf, sizeof(result_buf)); + + ok = (strcmp(result, t->expect) == 0); + printf("%s[%d]: result=%s %s\n", __func__, i, result, ok ? "pass" : "FAIL"); + pass = pass && ok; + } + + OSMO_ASSERT(pass); +} + + +static const struct log_info_cat log_categories[] = { +}; + +static const struct log_info log_info = { + .cat = log_categories, + .num_cat = ARRAY_SIZE(log_categories), +}; + +int main(int argc, char **argv) +{ + osmo_init_logging2(NULL, &log_info); + + test_sw_selection(); + test_abis_nm_ipaccess_cgi(); + + return EXIT_SUCCESS; +} + +struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) { + OSMO_ASSERT(0); +} diff --git a/tests/abis/abis_test.ok b/tests/abis/abis_test.ok new file mode 100644 index 0000000..e7e309c --- /dev/null +++ b/tests/abis/abis_test.ok @@ -0,0 +1,16 @@ +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 +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 +test_sw_selection(): OK +test_abis_nm_ipaccess_cgi[0]: result=00f12000030004 pass +test_abis_nm_ipaccess_cgi[1]: result=00210000030004 pass +test_abis_nm_ipaccess_cgi[2]: result=00f00000000000 pass +test_abis_nm_ipaccess_cgi[3]: result=00000000000000 pass +test_abis_nm_ipaccess_cgi[4]: result=999999ffffffff pass +test_abis_nm_ipaccess_cgi[5]: result=09f909abcd2345 pass +test_abis_nm_ipaccess_cgi[6]: result=090990abcd2345 pass diff --git a/tests/atlocal.in b/tests/atlocal.in new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/atlocal.in diff --git a/tests/bsc-nat-trie/Makefile.am b/tests/bsc-nat-trie/Makefile.am new file mode 100644 index 0000000..4afa1a4 --- /dev/null +++ b/tests/bsc-nat-trie/Makefile.am @@ -0,0 +1,19 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) \ + $(LIBOSMOLEGACYMGCP_CFLAGS) \ + $(NULL) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +EXTRA_DIST = bsc_nat_trie_test.ok prefixes.csv + +noinst_PROGRAMS = bsc_nat_trie_test + +bsc_nat_trie_test_SOURCES = bsc_nat_trie_test.c \ + $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c +bsc_nat_trie_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lrt \ + $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOLEGACYMGCP_LIBS) \ + $(LIBRARY_GSM) \ + $(NULL) diff --git a/tests/bsc-nat-trie/bsc_nat_trie_test.c b/tests/bsc-nat-trie/bsc_nat_trie_test.c new file mode 100644 index 0000000..32323d9 --- /dev/null +++ b/tests/bsc-nat-trie/bsc_nat_trie_test.c @@ -0,0 +1,95 @@ +/* + * (C) 2013 by On-Waves + * (C) 2013 by Holger Hans Peter Freyther <zecke@selfish.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/bsc/nat_rewrite_trie.h> +#include <osmocom/bsc/debug.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/backtrace.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> + +#include <string.h> + +static const struct log_info_cat log_categories[] = { +}; + +static const struct log_info log_info = { + .cat = log_categories, + .num_cat = ARRAY_SIZE(log_categories), +}; + +int main(int argc, char **argv) +{ + struct nat_rewrite *trie; + void *tall_ctx = talloc_named_const(NULL, 1, "bsc_nat_trie_test"); + osmo_init_logging2(tall_ctx, &log_info); + + printf("Testing the trie\n"); + + trie = nat_rewrite_parse(NULL, "prefixes.csv"); + OSMO_ASSERT(trie); + + /* verify that it has been parsed */ + OSMO_ASSERT(trie->prefixes == 17); + printf("Dumping the internal trie\n"); + nat_rewrite_dump(trie); + + /* now do the matching... */ + OSMO_ASSERT(!nat_rewrite_lookup(trie, "")); + OSMO_ASSERT(!nat_rewrite_lookup(trie, "2")); + + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1")->rewrite, "1") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12")->rewrite, "2") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "123")->rewrite, "3") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1234")->rewrite, "4") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12345")->rewrite, "5") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "123456")->rewrite, "6") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1234567")->rewrite, "7") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12345678")->rewrite, "8") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "123456789")->rewrite, "9") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1234567890")->rewrite, "10") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "13")->rewrite, "11") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "14")->rewrite, "12") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "15")->rewrite, "13") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "16")->rewrite, "14") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "823455")->rewrite, "15") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "82")->rewrite, "16") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "+49123445")->rewrite, "17") == 0); + + /* match a prefix */ + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "121")->rewrite, "2") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1292323")->rewrite, "2") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12345678901")->rewrite, "10") == 0); + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "160")->rewrite, "14") == 0); + + OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12345678901123452123123")->rewrite, "10") == 0); + + /* invalid input */ + OSMO_ASSERT(!nat_rewrite_lookup(trie, "12abc")); + + talloc_free(trie); + + trie = nat_rewrite_parse(NULL, "does_not_exist.csv"); + OSMO_ASSERT(!trie); + + printf("Done with the tests.\n"); + return 0; +} diff --git a/tests/bsc-nat-trie/bsc_nat_trie_test.ok b/tests/bsc-nat-trie/bsc_nat_trie_test.ok new file mode 100644 index 0000000..4d4cc99 --- /dev/null +++ b/tests/bsc-nat-trie/bsc_nat_trie_test.ok @@ -0,0 +1,20 @@ +Testing the trie +Dumping the internal trie +1,1 +12,2 +123,3 +1234,4 +12345,5 +123456,6 +1234567,7 +12345678,8 +123456789,9 +1234567890,10 +13,11 +14,12 +15,13 +16,14 +82,16 +823455,15 ++49123,17 +Done with the tests. diff --git a/tests/bsc-nat-trie/prefixes.csv b/tests/bsc-nat-trie/prefixes.csv new file mode 100644 index 0000000..35485b1 --- /dev/null +++ b/tests/bsc-nat-trie/prefixes.csv @@ -0,0 +1,25 @@ +1,1 +12,2 +123,3 +1234,4 +12345,5 +123456,6 +1234567,7 +12345678,8 +123456789,9 +1234567890,10 +13,11 +14,12 +15,13 +16,14 +823455,15 +82,16 ++49123,17 +1ABC,18 +12345678901234567890,19 +,20 +14A,21 +124,324324324234 +1234567890,10 +no line +99, diff --git a/tests/bsc-nat/Makefile.am b/tests/bsc-nat/Makefile.am new file mode 100644 index 0000000..c2ed6e4 --- /dev/null +++ b/tests/bsc-nat/Makefile.am @@ -0,0 +1,58 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + -ggdb3 \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOCTRL_LIBS) \ + $(LIBOSMOSCCP_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(LIBOSMONETIF_CFLAGS) \ + $(LIBOSMOLEGACYMGCP_CFLAGS) \ + $(COVERAGE_CFLAGS) \ + $(NULL) + +AM_LDFLAGS = \ + $(COVERAGE_LDFLAGS) \ + $(NULL) + +EXTRA_DIST = \ + bsc_nat_test.ok \ + bsc_data.c \ + barr.cfg \ + barr_dup.cfg \ + prefixes.csv \ + $(NULL) + +noinst_PROGRAMS = \ + bsc_nat_test \ + $(NULL) + +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_rewrite.c \ + $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c \ + $(top_srcdir)/src/osmo-bsc_nat/bsc_mgcp_utils.c \ + $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_filter.c + +bsc_nat_test_LDADD = \ + $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOSCCP_LIBS) \ + $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(LIBOSMONETIF_LIBS) \ + $(LIBOSMOCTRL_LIBS) \ + $(LIBOSMOLEGACYMGCP_LIBS) \ + $(LIBRARY_GSM) \ + -lrt \ + $(NULL) diff --git a/tests/bsc-nat/barr.cfg b/tests/bsc-nat/barr.cfg new file mode 100644 index 0000000..a9a4a2b --- /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 0000000..ea94631 --- /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 0000000..3a9f1da --- /dev/null +++ b/tests/bsc-nat/bsc_data.c @@ -0,0 +1,275 @@ +/* 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 3\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 3\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"; +static const char mdcx_resp_patched2_noamr[] = "200 33330829\n\nv=0\nc=IN IP4 10.0.0.23\nm=audio 5555 RTP/AVP 98\na=rtpmap:98 AMR/8000\n"; + +struct mgcp_patch_test { + const char *orig; + const char *patch; + const char *ip; + const int port; + const int payload_type; + const int ensure_mode_set; +}; + +static const struct mgcp_patch_test mgcp_messages[] = { + { + .orig = crcx, + .patch = crcx_patched, + .ip = "0.0.0.0", + .port = 2323, + .ensure_mode_set = 1, + }, + { + .orig = crcx_resp, + .patch = crcx_resp_patched, + .ip = "10.0.0.1", + .port = 999, + .payload_type = 98, + .ensure_mode_set = 1, + }, + { + .orig = mdcx, + .patch = mdcx_patched, + .ip = "10.0.0.23", + .port = 6666, + .payload_type = 126, + .ensure_mode_set = 1, + }, + { + .orig = mdcx_resp, + .patch = mdcx_resp_patched, + .ip = "10.0.0.23", + .port = 5555, + .payload_type = 98, + .ensure_mode_set = 1, + }, + { + .orig = mdcx_resp2, + .patch = mdcx_resp_patched2, + .ip = "10.0.0.23", + .port = 5555, + .payload_type = 98, + .ensure_mode_set = 1, + }, + { + .orig = mdcx_resp2, + .patch = mdcx_resp_patched2_noamr, + .ip = "10.0.0.23", + .port = 5555, + .payload_type = 98, + .ensure_mode_set = 0, + }, +}; + +/* 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 0000000..7aa39ec --- /dev/null +++ b/tests/bsc-nat/bsc_nat_test.c @@ -0,0 +1,1595 @@ +/* + * BSC NAT Message filtering + * + * (C) 2010-2013 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010-2013 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#include <osmocom/bsc/debug.h> +#include <osmocom/bsc/gsm_data.h> +#include <osmocom/bsc/bsc_nat.h> +#include <osmocom/bsc/bsc_nat_sccp.h> +#include <osmocom/bsc/bsc_msg_filter.h> +#include <osmocom/bsc/nat_rewrite_trie.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/backtrace.h> +#include <osmocom/core/talloc.h> + +#include <osmocom/sccp/sccp.h> +#include <osmocom/gsm/protocol/gsm_08_08.h> + +#include <stdio.h> + +/* 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", 0); + 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); + bsc_nat_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", 0); + 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(); + } + + bsc_nat_free(nat); +} + +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", 0); + 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", 0); + 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); + bsc_nat_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 */ + bsc_nat_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; + const int expected_payload_type = mgcp_messages[i].payload_type; + const int ensure_mode_set = mgcp_messages[i].ensure_mode_set; + int payload_type = -1; + + char *input = strdup(orig); + + output = bsc_mgcp_rewrite(input, strlen(input), 0x1e, + ip, port, -1, &payload_type, ensure_mode_set); + + if (payload_type != -1) { + fprintf(stderr, "Found media payload type %d in SDP data\n", + payload_type); + if (payload_type != expected_payload_type) { + printf("Wrong payload type %d (expected %d)\n", + payload_type, expected_payload_type); + abort(); + } + } + + if (msgb_l2len(output) != strlen(patc)) { + printf("Wrong sizes for test: %d %u != %zu != %zu\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; + int nat_cm_reject_cause; + int nat_lu_reject_cause; + int bsc_cm_reject_cause; + int bsc_lu_reject_cause; + int want_cm_reject_cause; + int want_lu_reject_cause; +}; + +static struct cr_filter cr_filter[] = { + { + .data = bssmap_cr, + .length = sizeof(bssmap_cr), + .result = 1, + .contype = FLT_CON_TYPE_CM_SERV_REQ, + .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + }, + { + .data = bss_lu, + .length = sizeof(bss_lu), + .result = 1, + .contype = FLT_CON_TYPE_LU, + .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + }, + { + .data = pag_resp, + .length = sizeof(pag_resp), + .result = 1, + .contype = FLT_CON_TYPE_PAG_RESP, + .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + }, + { + /* nat deny is before blank/null BSC */ + .data = bss_lu, + .length = sizeof(bss_lu), + .result = -3, + .nat_imsi_deny = "[0-9]*", + .contype = FLT_CON_TYPE_LU, + .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + }, + { + /* 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 = FLT_CON_TYPE_LU, + .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + }, + { + /* 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 = FLT_CON_TYPE_LU, + .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + }, + { + /* 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 = FLT_CON_TYPE_LU, + .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + }, + { + /* 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 = FLT_CON_TYPE_LU, + .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + }, + { + /* 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 = FLT_CON_TYPE_LU, + .nat_cm_reject_cause = 0x23, + .nat_lu_reject_cause = 0x42, + .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_lu_reject_cause = 0x42, + .want_cm_reject_cause = 0x23, + }, + { + /* deny by bsc rule */ + .data = bss_lu, + .length = sizeof(bss_lu), + .result = -2, + .bsc_imsi_deny = "[0-9]*", + .contype = FLT_CON_TYPE_LU, + .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + }, + { + /* deny by bsc rule */ + .data = bss_lu, + .length = sizeof(bss_lu), + .result = -2, + .bsc_imsi_deny = "[0-9]*", + .contype = FLT_CON_TYPE_LU, + .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, + .bsc_cm_reject_cause = 0x42, + .bsc_lu_reject_cause = 0x23, + .want_lu_reject_cause = 0x23, + .want_cm_reject_cause = 0x42, + }, +}; + +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_msg_acc_lst *nat_lst, *bsc_lst; + struct bsc_msg_acc_lst_entry *nat_entry, *bsc_entry; + struct bsc_filter_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", 0); + bsc_config_add_lac(bsc->cfg, 1234); + bsc->cfg->acc_lst_name = "bsc"; + nat->acc_lst_name = "nat"; + + nat_lst = bsc_msg_acc_lst_get(nat, &nat->access_lists, "nat"); + bsc_lst = bsc_msg_acc_lst_get(nat, &nat->access_lists, "bsc"); + + bsc_entry = bsc_msg_acc_lst_entry_create(bsc_lst); + nat_entry = bsc_msg_acc_lst_entry_create(nat_lst); + + /* test the default value as we are going to overwrite it */ + OSMO_ASSERT(bsc_entry->cm_reject_cause == GSM48_REJECT_PLMN_NOT_ALLOWED); + OSMO_ASSERT(bsc_entry->lu_reject_cause == GSM48_REJECT_PLMN_NOT_ALLOWED); + + 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); + + bsc_entry->cm_reject_cause = cr_filter[i].bsc_cm_reject_cause; + bsc_entry->lu_reject_cause = cr_filter[i].bsc_lu_reject_cause; + nat_entry->cm_reject_cause = cr_filter[i].nat_cm_reject_cause; + nat_entry->lu_reject_cause = cr_filter[i].nat_lu_reject_cause; + + 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(); + } + + + OSMO_ASSERT(cause.cm_reject_cause == cr_filter[i].want_cm_reject_cause); + OSMO_ASSERT(cause.lu_reject_cause == cr_filter[i].want_lu_reject_cause); + + 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); + bsc_nat_free(nat); +} + +static void test_dt_filter() +{ + int i; + struct msgb *msg = msgb_alloc(4096, "test_dt_filter"); + struct bsc_nat_parsed *parsed; + struct bsc_filter_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", 0); + 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)); + OSMO_ASSERT(!con->filter_state.imsi); + if (bsc_nat_filter_dt(bsc, msg, con, parsed, &cause) != 1) { + printf("FAIL: Should have passed..\n"); + abort(); + } + OSMO_ASSERT(con->filter_state.imsi); + OSMO_ASSERT(talloc_parent(con->filter_state.imsi) == con); + + /* 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->filter_state.imsi_checked = 0; + memset(&cause, 0, sizeof(cause)); + bsc_nat_filter_dt(bsc, msg, con, parsed, &cause); + } + + msgb_free(msg); + bsc_nat_free(nat); +} + +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); + bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, NULL); + bsc_nat_free(nat); +} + +static void test_setup_rewrite_prefix(void) +{ + 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 = "prefix_lookup"; + llist_add_tail(&entry.list, &entries.entry); + bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); + + nat->num_rewr_trie = nat_rewrite_parse(nat, "prefixes.csv"); + + 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); + + bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, NULL); + bsc_nat_free(nat); +} + +static void test_setup_rewrite_post(void) +{ + 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; + struct osmo_config_list entries_post; + struct osmo_config_entry entry_post; + + 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); + + /* attempt to undo the previous one */ + INIT_LLIST_HEAD(&entries_post.entry); + entry_post.mcc = "274"; + entry_post.mnc = "08"; + entry_post.option = "^\\+49([1-9])"; + entry_post.text = "prefix_lookup"; + llist_add_tail(&entry_post.list, &entries_post.entry); + bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr_post, &entries_post); + + nat->num_rewr_trie = nat_rewrite_parse(nat, "prefixes.csv"); + + 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, ARRAY_SIZE(cc_setup_national)); + msgb_free(out); + + bsc_nat_free(nat); +} + +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); + bsc_nat_free(nat); +} + +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); + bsc_nat_free(nat); +} + +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_filter_barr_adapt(NULL, &root, lst); + if (rc != 0) + abort(); + talloc_free(lst); + + + for (node = rb_first(&root); node; node = rb_next(node)) { + struct bsc_filter_barr_entry *entry; + entry = rb_entry(node, struct bsc_filter_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_filter_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_filter_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_filter_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_filter_barr_entry *entry; + entry = rb_entry(node, struct bsc_filter_barr_entry, node); + printf("IMSI: %s CM: %d LU: %d\n", entry->imsi, + entry->cm_reject_cause, entry->lu_reject_cause); + + } + rc = bsc_filter_barr_adapt(NULL, &root, NULL); +} + +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", 0); + + 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); + + bsc_nat_free(nat); +} + +static const struct log_info_cat log_categories[] = { +}; + +static const struct log_info log_info = { + .cat = log_categories, + .num_cat = ARRAY_SIZE(log_categories), +}; + +int main(int argc, char **argv) +{ + void *tall_ctx = talloc_named_const(NULL, 1, "bsc_nat_test"); + msgb_talloc_ctx_init(tall_ctx, 0); + sccp_set_log_area(DLSCCP); + osmo_init_logging2(tall_ctx, &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_setup_rewrite_prefix(); + test_setup_rewrite_post(); + 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(); +} + +struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *network) { return NULL; } diff --git a/tests/bsc-nat/bsc_nat_test.ok b/tests/bsc-nat/bsc_nat_test.ok new file mode 100644 index 0000000..ab04f42 --- /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/bsc-nat/prefixes.csv b/tests/bsc-nat/prefixes.csv new file mode 100644 index 0000000..0c7660f --- /dev/null +++ b/tests/bsc-nat/prefixes.csv @@ -0,0 +1,2 @@ +0172,0049 ++49,0 diff --git a/tests/bsc/Makefile.am b/tests/bsc/Makefile.am new file mode 100644 index 0000000..a436c27 --- /dev/null +++ b/tests/bsc/Makefile.am @@ -0,0 +1,43 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + -ggdb3 \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(LIBOSMOLEGACYMGCP_CFLAGS) \ + $(LIBOSMOSIGTRAN_CFLAGS) \ + $(COVERAGE_CFLAGS) \ + $(NULL) + +AM_LDFLAGS = \ + $(COVERAGE_LDFLAGS) \ + $(NULL) + +EXTRA_DIST = \ + bsc_test.ok \ + $(NULL) + +noinst_PROGRAMS = \ + bsc_test \ + $(NULL) + +bsc_test_SOURCES = \ + bsc_test.c \ + $(top_srcdir)/src/osmo-bsc/osmo_bsc_filter.c \ + $(NULL) + +bsc_test_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOLEGACYMGCP_LIBS) \ + $(LIBRARY_GSM) \ + -lrt \ + $(NULL) diff --git a/tests/bsc/bsc_test.c b/tests/bsc/bsc_test.c new file mode 100644 index 0000000..106b08b --- /dev/null +++ b/tests/bsc/bsc_test.c @@ -0,0 +1,245 @@ +/* + * BSC Message filtering + * + * (C) 2013 by sysmocom s.f.m.c. GmbH + * Written by Jacob Erlbeck <jerlbeck@sysmocom.de> + * (C) 2010-2013 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010-2013 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#include <osmocom/bsc/debug.h> +#include <osmocom/bsc/gsm_data.h> + +#include <osmocom/bsc/osmo_bsc.h> +#include <osmocom/bsc/bsc_msc_data.h> +#include <osmocom/bsc/gsm_04_80.h> +#include <osmocom/bsc/common_bsc.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/backtrace.h> +#include <osmocom/core/talloc.h> + +#include <stdio.h> +#include <search.h> + +void *ctx = NULL; + +enum test { + TEST_SCAN_TO_BTS, + TEST_SCAN_TO_MSC, +}; + +/* GSM 04.08 MM INFORMATION test message */ +static uint8_t gsm48_mm_info_nn_tzt[] = { + 0x05, 0x32, 0x45, 0x08, 0x80, 0x4f, 0x77, 0xeb, + 0x1a, 0xb6, 0x97, 0xe7, 0x47, 0x31, 0x90, 0x61, + 0x11, 0x02, 0x73, 0x00, +}; + +static uint8_t gsm48_mm_info_nn_tzt_out[] = { + 0x05, 0x32, 0x45, 0x08, 0x80, 0x4f, 0x77, 0xeb, + 0x1a, 0xb6, 0x97, 0xe7, 0x47, 0x31, 0x90, 0x61, + 0x11, 0x02, 0x73, 0x1a, +}; + +static uint8_t gsm48_mm_info_nn_tzt_dst[] = { + 0x05, 0x32, 0x45, 0x08, 0x80, 0x4f, 0x77, 0xeb, + 0x1a, 0xb6, 0x97, 0xe7, 0x47, 0x31, 0x90, 0x61, + 0x11, 0x02, 0x73, 0x00, 0x49, 0x01, 0x00, +}; + +static uint8_t gsm48_mm_info_nn_tzt_dst_out[] = { + 0x05, 0x32, 0x45, 0x08, 0x80, 0x4f, 0x77, 0xeb, + 0x1a, 0xb6, 0x97, 0xe7, 0x47, 0x31, 0x90, 0x61, + 0x11, 0x02, 0x73, 0x1a, 0x49, 0x01, 0x02, +}; + +struct test_definition { + const uint8_t *data; + const uint16_t length; + const int dir; + const int result; + const uint8_t *out_data; + const uint16_t out_length; + const char* params; + const int n_params; +}; + +static int get_int(const char *params, size_t nmemb, const char *key, int def, int *is_set) +{ + const char *kv = NULL; + + kv = strstr(params, key); + if (kv) { + kv += strlen(key) + 1; + fprintf(stderr, "get_int(%s) -> %d\n", key, atoi(kv)); + if (is_set) + *is_set = 1; + } + + return kv ? atoi(kv) : def; +} + +static const struct test_definition test_scan_defs[] = { + { + .data = gsm48_mm_info_nn_tzt_dst, + .length = ARRAY_SIZE(gsm48_mm_info_nn_tzt), + .dir = TEST_SCAN_TO_BTS, + .result = 0, + .out_data = gsm48_mm_info_nn_tzt_dst_out, + .out_length = ARRAY_SIZE(gsm48_mm_info_nn_tzt_out), + .params = "tz_hr=-5 tz_mn=15 tz_dst=2", + .n_params = 3, + }, + { + .data = gsm48_mm_info_nn_tzt_dst, + .length = ARRAY_SIZE(gsm48_mm_info_nn_tzt_dst), + .dir = TEST_SCAN_TO_BTS, + .result = 0, + .out_data = gsm48_mm_info_nn_tzt_dst_out, + .out_length = ARRAY_SIZE(gsm48_mm_info_nn_tzt_dst_out), + .params = "tz_hr=-5 tz_mn=15 tz_dst=2", + .n_params = 3, + }, +}; + +static void test_scan(void) +{ + int i; + + struct gsm_network *net = bsc_network_init(ctx); + struct gsm_bts *bts = gsm_bts_alloc(net, 0); + struct bsc_msc_data *msc; + struct gsm_subscriber_connection *conn; + + msc = talloc_zero(net, struct bsc_msc_data); + conn = talloc_zero(net, struct gsm_subscriber_connection); + + bts->network = net; + conn->sccp.msc = msc; + conn->lchan = &bts->c0->ts[1].lchan[0]; + + /* start testing with proper messages */ + printf("Testing BTS<->MSC message scan.\n"); + for (i = 0; i < ARRAY_SIZE(test_scan_defs); ++i) { + const struct test_definition *test_def = &test_scan_defs[i]; + int result; + struct msgb *msg = msgb_alloc(4096, "test-message"); + int is_set = 0; + + net->tz.hr = get_int(test_def->params, test_def->n_params, "tz_hr", 0, &is_set); + net->tz.mn = get_int(test_def->params, test_def->n_params, "tz_mn", 0, &is_set); + net->tz.dst = get_int(test_def->params, test_def->n_params, "tz_dst", 0, &is_set); + net->tz.override = 1; + + printf("Going to test item: %d\n", i); + msg->l3h = msgb_put(msg, test_def->length); + memcpy(msg->l3h, test_def->data, test_def->length); + + switch (test_def->dir) { + case TEST_SCAN_TO_BTS: + /* override timezone of msg coming from the MSC */ + result = bsc_scan_msc_msg(conn, msg); + break; + case TEST_SCAN_TO_MSC: + /* override timezone of msg coming from the BSC */ + /* FIXME: no test for this case is defined in + * test_scan_defs[], so this is never used. */ + result = bsc_scan_bts_msg(conn, msg); + break; + default: + abort(); + break; + } + + if (result != test_def->result) { + printf("FAIL: Not the expected result, got: %d wanted: %d\n", + result, test_def->result); + goto out; + } + + if (msgb_l3len(msg) != test_def->out_length) { + printf("FAIL: Not the expected message size, got: %d wanted: %d\n", + msgb_l3len(msg), test_def->out_length); + goto out; + } + + if (memcmp(msgb_l3(msg), test_def->out_data, test_def->out_length) != 0) { + printf("FAIL: Not the expected message\n"); + goto out; + } + +out: + msgb_free(msg); + } + + talloc_free(net); +} + +static const struct log_info_cat log_categories[] = { + [DNM] = { + .name = "DNM", + .description = "A-bis Network Management / O&M (NM/OML)", + .color = "\033[1;36m", + .enabled = 1, .loglevel = LOGL_INFO, + }, + [DNAT] = { + .name = "DNAT", + .description = "GSM 08.08 NAT/Multiplexer", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DMSC] = { + .name = "DMSC", + .description = "Mobile Switching Center", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DCTRL] = { + .name = "DCTRL", + .description = "Control interface", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DFILTER] = { + .name = "DFILTER", + .description = "BSC/NAT IMSI based filtering", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, +}; + +static const struct log_info log_info = { + .cat = log_categories, + .num_cat = ARRAY_SIZE(log_categories), +}; + +int main(int argc, char **argv) +{ + ctx = talloc_named_const(NULL, 0, "bsc-test"); + msgb_talloc_ctx_init(ctx, 0); + osmo_init_logging2(ctx, &log_info); + + test_scan(); + + printf("Testing execution completed.\n"); + talloc_free(ctx); + return 0; +} + +struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) { + OSMO_ASSERT(0); +} diff --git a/tests/bsc/bsc_test.ok b/tests/bsc/bsc_test.ok new file mode 100644 index 0000000..0564bf0 --- /dev/null +++ b/tests/bsc/bsc_test.ok @@ -0,0 +1,4 @@ +Testing BTS<->MSC message scan. +Going to test item: 0 +Going to test item: 1 +Testing execution completed. diff --git a/tests/bssap/Makefile.am b/tests/bssap/Makefile.am new file mode 100644 index 0000000..30a9246 --- /dev/null +++ b/tests/bssap/Makefile.am @@ -0,0 +1,49 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + -ggdb3 \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(LIBOSMOSIGTRAN_CFLAGS) \ + $(COVERAGE_CFLAGS) \ + $(LIBOSMOMGCPCLIENT_CFLAGS) \ + $(NULL) + +EXTRA_DIST = \ + bssap_test.ok \ + bssap_test.err \ + $(NULL) + +noinst_PROGRAMS = \ + bssap_test \ + $(NULL) + +bssap_test_SOURCES = \ + bssap_test.c \ + $(top_srcdir)/src/osmo-bsc/osmo_bsc_bssap.c \ + $(top_srcdir)/src/osmo-bsc/osmo_bsc_sigtran.c \ + $(top_srcdir)/src/osmo-bsc/osmo_bsc_filter.c \ + $(top_srcdir)/src/osmo-bsc/osmo_bsc_grace.c \ + $(NULL) + +bssap_test_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOSIGTRAN_LIBS) \ + $(LIBOSMOMGCPCLIENT_LIBS) \ + $(NULL) + +bssap_test_LDFLAGS = \ + -Wl,--wrap=bsc_grace_paging_request \ + $(NULL) + +.PHONY: update_exp +update_exp: + $(builddir)/bssap_test >$(srcdir)/bssap_test.ok 2>$(srcdir)/bssap_test.err diff --git a/tests/bssap/bssap_test.c b/tests/bssap/bssap_test.c new file mode 100644 index 0000000..00bc64c --- /dev/null +++ b/tests/bssap/bssap_test.c @@ -0,0 +1,158 @@ +/* + * (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/core/application.h> + +#include <osmocom/bsc/debug.h> +#include <osmocom/bsc/osmo_bsc.h> +#include <osmocom/bsc/signal.h> +#include <osmocom/bsc/bsc_subscriber.h> +#include <osmocom/bsc/bsc_msc_data.h> +#include <osmocom/bsc/common_bsc.h> +#include <osmocom/bsc/osmo_bsc_rf.h> + +struct msgb *msgb_from_hex(const char *label, uint16_t size, const char *hex) +{ + struct msgb *msg = msgb_alloc(size, label); + unsigned char *rc; + msg->l2h = msg->l3h = msg->head; + rc = msgb_put(msg, osmo_hexparse(hex, msg->head, msgb_tailroom(msg))); + OSMO_ASSERT(rc == msg->l2h); + return msg; +} + +uint16_t gl_expect_lac = 0; + +/* override, requires '-Wl,--wrap=bsc_grace_paging_request' */ +int __real_bsc_grace_paging_request(enum signal_rf rf_policy, struct bsc_subscr *subscr, int chan_needed, + struct bsc_msc_data *msc, struct gsm_bts *bts); +int __wrap_bsc_grace_paging_request(enum signal_rf rf_policy, struct bsc_subscr *subscr, int chan_needed, + struct bsc_msc_data *msc, struct gsm_bts *bts) +{ + if (subscr->lac == GSM_LAC_RESERVED_ALL_BTS) + fprintf(stderr, "BSC paging started on entire BSS (%u)\n", subscr->lac); + else + fprintf(stderr, "BSC paging started with LAC %u\n", subscr->lac); + OSMO_ASSERT(gl_expect_lac == subscr->lac); + return 1; /* pretend one BTS was paged */ +} + +struct { + const char *msg; + uint16_t expect_lac; + int expect_rc; +} cell_identifier_tests[] = { + { + "001652080859512069000743940904010844601a03050065", + /* ^^^^^^ Cell Identifier List: LAC */ + 0x65, 0 + }, + { + "001452080859512069000743940904010844601a0106", + /* ^^ Cell Identifier List: BSS */ + GSM_LAC_RESERVED_ALL_BTS, 0 + }, + { + "001952080859512069000743940904010844601a060415f5490065", + /* ^^^^^^^^^^^^ Cell Identifier List: LAI */ + GSM_LAC_RESERVED_ALL_BTS, 0 + }, + { + "001952080859512069000743940904010844601a060400f1100065", + /* ^^^^^^^^^^^^ Cell Identifier List: LAI */ + 0x65, 0 + }, +}; + +struct gsm_network *bsc_gsmnet; + +void test_cell_identifier() +{ + int i; + int rc; + struct bsc_msc_data *msc; + struct gsm_bts *bts; + + bsc_gsmnet = bsc_network_init(NULL); + bsc_gsmnet->bsc_data->rf_ctrl = talloc_zero(NULL, struct osmo_bsc_rf); + bsc_gsmnet->bsc_data->rf_ctrl->policy = S_RF_ON; + + msc = talloc_zero(bsc_gsmnet, struct bsc_msc_data); + msc->network = bsc_gsmnet; + + bts = gsm_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_UNKNOWN, 0); + if (bts == NULL) { + fprintf(stderr, "gsm_bts_alloc_register() returned NULL\n"); + return; + } + + log_set_log_level(osmo_stderr_target, LOGL_DEBUG); + + for (i = 0; i < ARRAY_SIZE(cell_identifier_tests); i++) { + struct msgb *msg; + fprintf(stderr, "\n%d:\n", i); + msg = msgb_from_hex("test_cell_identifier", 1024, cell_identifier_tests[i].msg); + + gl_expect_lac = cell_identifier_tests[i].expect_lac; + bts->location_area_code = (gl_expect_lac == GSM_LAC_RESERVED_ALL_BTS ? 0 : gl_expect_lac); + rc = bsc_handle_udt(msc, msg, msgb_l2len(msg)); + + fprintf(stderr, "bsc_handle_udt() returned %d\n", rc); + OSMO_ASSERT(rc == cell_identifier_tests[i].expect_rc); + + msgb_free(msg); + } +} + +static const struct log_info_cat log_categories[] = { + [DMSC] = { + .name = "DMSC", + .description = "Mobile Switching Center", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DREF] = { + .name = "DREF", + .description = "Reference Counting", + .enabled = 0, .loglevel = LOGL_DEBUG, + }, +}; + +static const struct log_info log_info = { + .cat = log_categories, + .num_cat = ARRAY_SIZE(log_categories), +}; + +int main(int argc, char **argv) +{ + void *tall_ctx = talloc_named_const(NULL, 1, "bssap_test"); + msgb_talloc_ctx_init(tall_ctx, 0); + osmo_init_logging2(tall_ctx, &log_info); + log_set_use_color(osmo_stderr_target, 0); + log_set_print_timestamp(osmo_stderr_target, 0); + log_set_print_filename(osmo_stderr_target, 0); + log_set_print_category(osmo_stderr_target, 1); + + test_cell_identifier(); + + return 0; +} + +struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) { + OSMO_ASSERT(0); +} diff --git a/tests/bssap/bssap_test.err b/tests/bssap/bssap_test.err new file mode 100644 index 0000000..8ae3b22 --- /dev/null +++ b/tests/bssap/bssap_test.err @@ -0,0 +1,27 @@ + +0: +DMSC Rx MSC UDT: 00 16 52 08 08 59 51 20 69 00 07 43 94 09 04 01 08 44 60 1a 03 05 00 65 +DMSC Rx MSC UDT BSSMAP PAGING +DMSC Paging request from MSC BTS: 0 IMSI: '515029600703449' TMSI: '0x1084460/17319008' LAC: 0x65 +BSC paging started with LAC 101 +bsc_handle_udt() returned 0 + +1: +DMSC Rx MSC UDT: 00 14 52 08 08 59 51 20 69 00 07 43 94 09 04 01 08 44 60 1a 01 06 +DMSC Rx MSC UDT BSSMAP PAGING +DMSC Paging request from MSC BTS: 0 IMSI: '515029600703449' TMSI: '0x1084460/17319008' LAC: 0xfffe +BSC paging started on entire BSS (65534) +bsc_handle_udt() returned 0 + +2: +DMSC Rx MSC UDT: 00 19 52 08 08 59 51 20 69 00 07 43 94 09 04 01 08 44 60 1a 06 04 15 f5 49 00 65 +DMSC Rx MSC UDT BSSMAP PAGING +DMSC Paging IMSI 515029600703449: MCC-MNC in Cell Identifier List (515-94) do not match our network (001-01) +bsc_handle_udt() returned 0 + +3: +DMSC Rx MSC UDT: 00 19 52 08 08 59 51 20 69 00 07 43 94 09 04 01 08 44 60 1a 06 04 00 f1 10 00 65 +DMSC Rx MSC UDT BSSMAP PAGING +DMSC Paging request from MSC BTS: 0 IMSI: '515029600703449' TMSI: '0x1084460/17319008' LAC: 0x65 +BSC paging started with LAC 101 +bsc_handle_udt() returned 0 diff --git a/tests/bssap/bssap_test.ok b/tests/bssap/bssap_test.ok new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/bssap/bssap_test.ok diff --git a/tests/channel/Makefile.am b/tests/channel/Makefile.am new file mode 100644 index 0000000..f641f60 --- /dev/null +++ b/tests/channel/Makefile.am @@ -0,0 +1,31 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + -ggdb3 \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(NULL) + +EXTRA_DIST = \ + channel_test.ok \ + $(NULL) + +noinst_PROGRAMS = \ + channel_test \ + $(NULL) + +channel_test_SOURCES = \ + channel_test.c \ + $(NULL) + +channel_test_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(NULL) diff --git a/tests/channel/channel_test.c b/tests/channel/channel_test.c new file mode 100644 index 0000000..e8f6cd9 --- /dev/null +++ b/tests/channel/channel_test.c @@ -0,0 +1,124 @@ +/* + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <assert.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/select.h> + +#include <osmocom/bsc/common_bsc.h> +#include <osmocom/bsc/abis_rsl.h> +#include <osmocom/bsc/debug.h> + +void test_bts_debug_print(void) +{ + struct gsm_network *network; + struct gsm_bts *bts; + struct gsm_bts_trx *trx; + + printf("Testing the lchan printing:"); + + /* Create a dummy network */ + network = bsc_network_init(tall_bsc_ctx); + if (!network) + exit(1); + /* Add a BTS with some reasonanbly non-zero id */ + bts = gsm_bts_alloc(network, 45); + /* Add a second TRX to test on multiple TRXs */ + gsm_bts_trx_alloc(bts); + + llist_for_each_entry(trx, &bts->trx_list, list) { + char *name = gsm_lchan_name(&trx->ts[3].lchan[4]); + + if (name) + printf(" %s", name); + else + printf("NULL name"); + } + printf("\n"); +} + + +void test_dyn_ts_subslots(void) +{ + struct gsm_bts_trx_ts ts; + + printf("Testing subslot numbers for pchan types\n"); + + ts.pchan = GSM_PCHAN_TCH_F; + OSMO_ASSERT(ts_subslots(&ts) == 1); + + ts.pchan = GSM_PCHAN_TCH_H; + OSMO_ASSERT(ts_subslots(&ts) == 2); + + ts.pchan = GSM_PCHAN_PDCH; + OSMO_ASSERT(ts_subslots(&ts) == 0); + + ts.pchan = GSM_PCHAN_TCH_F_PDCH; + ts.flags = 0; /* TCH_F mode */ + OSMO_ASSERT(ts_subslots(&ts) == 1); + ts.flags = TS_F_PDCH_ACTIVE; + OSMO_ASSERT(ts_subslots(&ts) == 0); + + ts.pchan = GSM_PCHAN_TCH_F_TCH_H_PDCH; + ts.dyn.pchan_is = GSM_PCHAN_TCH_F; + OSMO_ASSERT(ts_subslots(&ts) == 1); + ts.dyn.pchan_is = GSM_PCHAN_TCH_H; + OSMO_ASSERT(ts_subslots(&ts) == 2); + ts.dyn.pchan_is = GSM_PCHAN_PDCH; + OSMO_ASSERT(ts_subslots(&ts) == 0); +} + +static const struct log_info_cat log_categories[] = { +}; + +static const struct log_info log_info = { + .cat = log_categories, + .num_cat = ARRAY_SIZE(log_categories), +}; + +int main(int argc, char **argv) +{ + osmo_init_logging2(NULL, &log_info); + + test_dyn_ts_subslots(); + test_bts_debug_print(); + + return EXIT_SUCCESS; +} + +void sms_alloc() {} +void sms_free() {} +void gsm48_secure_channel() {} +void vty_out() {} + +void ipa_client_conn_clear_queue() {} +void ipa_client_conn_close() {} +void ipa_client_conn_create() {} +void ipa_client_conn_destroy() {} +void ipa_client_conn_open() {} +void ipa_client_conn_send() {} +void ipa_msg_push_header() {} +void ipaccess_bts_handle_ccm() {} +struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *network) { 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 0000000..81d6569 --- /dev/null +++ b/tests/channel/channel_test.ok @@ -0,0 +1,2 @@ +Testing subslot numbers for pchan types +Testing the lchan printing: (bts=45,trx=0,ts=3,ss=4) (bts=45,trx=1,ts=3,ss=4) diff --git a/tests/ctrl_test_runner.py b/tests/ctrl_test_runner.py new file mode 100755 index 0000000..f43c09a --- /dev/null +++ b/tests/ctrl_test_runner.py @@ -0,0 +1,609 @@ +#!/usr/bin/env python2 + +# (C) 2013 by Jacob Erlbeck <jerlbeck@sysmocom.de> +# (C) 2014 by Holger Hans Peter Freyther +# based on vty_test_runner.py: +# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com> +# (C) 2013 by Holger Hans Peter Freyther +# based on bsc_control.py. + +# 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 <http://www.gnu.org/licenses/>. + +import os +import time +import unittest +import socket +import sys +import struct + +import osmopy.obscvty as obscvty +import osmopy.osmoutil as osmoutil +from osmopy.osmo_ipa import Ctrl, IPA + +# to be able to find $top_srcdir/doc/... +confpath = os.path.join(sys.path[0], '..') +verbose = False + +class TestCtrlBase(unittest.TestCase): + + def ctrl_command(self): + raise Exception("Needs to be implemented by a subclass") + + def ctrl_app(self): + raise Exception("Needs to be implemented by a subclass") + + def setUp(self): + osmo_ctrl_cmd = self.ctrl_command()[:] + config_index = osmo_ctrl_cmd.index('-c') + if config_index: + cfi = config_index + 1 + osmo_ctrl_cmd[cfi] = os.path.join(confpath, osmo_ctrl_cmd[cfi]) + + try: + self.proc = osmoutil.popen_devnull(osmo_ctrl_cmd) + except OSError: + print >> sys.stderr, "Current directory: %s" % os.getcwd() + print >> sys.stderr, "Consider setting -b" + time.sleep(2) + + appstring = self.ctrl_app()[2] + appport = self.ctrl_app()[0] + self.connect("127.0.0.1", appport) + self.next_id = 1000 + + def tearDown(self): + self.disconnect() + osmoutil.end_proc(self.proc) + + def disconnect(self): + if not (self.sock is None): + self.sock.close() + + def connect(self, host, port): + if verbose: + print "Connecting to host %s:%i" % (host, port) + + retries = 30 + while True: + try: + sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sck.setblocking(1) + sck.connect((host, port)) + except IOError: + retries -= 1 + if retries <= 0: + raise + time.sleep(.1) + continue + break + self.sock = sck + return sck + + def send(self, data): + if verbose: + print "Sending \"%s\"" %(data) + data = Ctrl().add_header(data) + return self.sock.send(data) == len(data) + + def send_set(self, var, value, id): + setmsg = "SET %s %s %s" %(id, var, value) + return self.send(setmsg) + + def send_get(self, var, id): + getmsg = "GET %s %s" %(id, var) + return self.send(getmsg) + + def do_set(self, var, value): + id = self.next_id + self.next_id += 1 + self.send_set(var, value, id) + return self.recv_msgs()[id] + + def do_get(self, var): + id = self.next_id + self.next_id += 1 + self.send_get(var, id) + return self.recv_msgs()[id] + + def recv_msgs(self): + responses = {} + data = self.sock.recv(4096) + while (len(data)>0): + (head, data) = IPA().split_combined(data) + answer = Ctrl().rem_header(head) + if verbose: + print "Got message:", answer + (mtype, id, msg) = answer.split(None, 2) + id = int(id) + rsp = {'mtype': mtype, 'id': id} + if mtype == "ERROR": + rsp['error'] = msg + else: + split = msg.split(None, 1) + rsp['var'] = split[0] + if len(split) > 1: + rsp['value'] = split[1] + else: + rsp['value'] = None + responses[id] = rsp + + if verbose: + print "Decoded replies: ", responses + + return responses + + +class TestCtrlBSC(TestCtrlBase): + + def tearDown(self): + TestCtrlBase.tearDown(self) + os.unlink("tmp_dummy_sock") + + def ctrl_command(self): + return ["./src/osmo-bsc/osmo-bsc", "-r", "tmp_dummy_sock", "-c", + "doc/examples/osmo-bsc/osmo-bsc.cfg"] + + def ctrl_app(self): + return (4249, "./src/osmo-bsc/osmo-bsc", "OsmoBSC", "bsc") + + def testCtrlErrs(self): + r = self.do_get('invalid') + self.assertEquals(r['mtype'], 'ERROR') + self.assertEquals(r['error'], 'Command not found') + + r = self.do_set('rf_locked', '999') + self.assertEquals(r['mtype'], 'ERROR') + self.assertEquals(r['error'], 'Value failed verification.') + + r = self.do_get('bts') + self.assertEquals(r['mtype'], 'ERROR') + self.assertEquals(r['error'], 'Error while parsing the index.') + + r = self.do_get('bts.999') + self.assertEquals(r['mtype'], 'ERROR') + self.assertEquals(r['error'], 'Error while resolving object') + + def testBtsLac(self): + r = self.do_get('bts.0.location-area-code') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'bts.0.location-area-code') + self.assertEquals(r['value'], '1') + + r = self.do_set('bts.0.location-area-code', '23') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'bts.0.location-area-code') + self.assertEquals(r['value'], '23') + + r = self.do_get('bts.0.location-area-code') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'bts.0.location-area-code') + self.assertEquals(r['value'], '23') + + r = self.do_set('bts.0.location-area-code', '-1') + self.assertEquals(r['mtype'], 'ERROR') + self.assertEquals(r['error'], 'Input not within the range') + + def testBtsCi(self): + r = self.do_get('bts.0.cell-identity') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'bts.0.cell-identity') + self.assertEquals(r['value'], '0') + + r = self.do_set('bts.0.cell-identity', '23') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'bts.0.cell-identity') + self.assertEquals(r['value'], '23') + + r = self.do_get('bts.0.cell-identity') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'bts.0.cell-identity') + self.assertEquals(r['value'], '23') + + r = self.do_set('bts.0.cell-identity', '-1') + self.assertEquals(r['mtype'], 'ERROR') + self.assertEquals(r['error'], 'Input not within the range') + + def testBtsGenerateSystemInformation(self): + r = self.do_get('bts.0.send-new-system-informations') + self.assertEquals(r['mtype'], 'ERROR') + self.assertEquals(r['error'], 'Write Only attribute') + + # No RSL links so it will fail + r = self.do_set('bts.0.send-new-system-informations', '1') + self.assertEquals(r['mtype'], 'ERROR') + self.assertEquals(r['error'], 'Failed to generate SI') + + def testBtsChannelLoad(self): + r = self.do_set('bts.0.channel-load', '1') + self.assertEquals(r['mtype'], 'ERROR') + self.assertEquals(r['error'], 'Read Only attribute') + + # No RSL link so everything is 0 + r = self.do_get('bts.0.channel-load') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['value'], + 'CCCH+SDCCH4,0,0 TCH/F,0,0 TCH/H,0,0 SDCCH8,0,0' + + ' TCH/F_PDCH,0,0 CCCH+SDCCH4+CBCH,0,0' + + ' SDCCH8+CBCH,0,0 TCH/F_TCH/H_PDCH,0,0') + + def testBtsOmlConnectionState(self): + """Check OML state. It will not be connected""" + r = self.do_set('bts.0.oml-connection-state', '1') + self.assertEquals(r['mtype'], 'ERROR') + self.assertEquals(r['error'], 'Read Only attribute') + + # No RSL link so everything is 0 + r = self.do_get('bts.0.oml-connection-state') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['value'], 'disconnected') + + def testTrxPowerRed(self): + r = self.do_get('bts.0.trx.0.max-power-reduction') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'bts.0.trx.0.max-power-reduction') + self.assertEquals(r['value'], '20') + + r = self.do_set('bts.0.trx.0.max-power-reduction', '22') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'bts.0.trx.0.max-power-reduction') + self.assertEquals(r['value'], '22') + + r = self.do_get('bts.0.trx.0.max-power-reduction') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'bts.0.trx.0.max-power-reduction') + self.assertEquals(r['value'], '22') + + r = self.do_set('bts.0.trx.0.max-power-reduction', '1') + self.assertEquals(r['mtype'], 'ERROR') + self.assertEquals(r['error'], 'Value must be even') + + def testTrxArfcn(self): + r = self.do_get('bts.0.trx.0.arfcn') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'bts.0.trx.0.arfcn') + self.assertEquals(r['value'], '871') + + r = self.do_set('bts.0.trx.0.arfcn', '873') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'bts.0.trx.0.arfcn') + self.assertEquals(r['value'], '873') + + r = self.do_get('bts.0.trx.0.arfcn') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'bts.0.trx.0.arfcn') + self.assertEquals(r['value'], '873') + + r = self.do_set('bts.0.trx.0.arfcn', '2000') + self.assertEquals(r['mtype'], 'ERROR') + self.assertEquals(r['error'], 'Input not within the range') + + def testRfLock(self): + r = self.do_get('bts.0.rf_state') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'bts.0.rf_state') + self.assertEquals(r['value'], 'inoperational,unlocked,on') + + r = self.do_set('rf_locked', '1') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'rf_locked') + self.assertEquals(r['value'], '1') + + time.sleep(1.5) + + r = self.do_get('bts.0.rf_state') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'bts.0.rf_state') + self.assertEquals(r['value'], 'inoperational,locked,off') + + r = self.do_get('rf_locked') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'rf_locked') + self.assertEquals(r['value'], 'state=off,policy=off') + + r = self.do_set('rf_locked', '0') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'rf_locked') + self.assertEquals(r['value'], '0') + + time.sleep(1.5) + + r = self.do_get('bts.0.rf_state') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'bts.0.rf_state') + self.assertEquals(r['value'], 'inoperational,unlocked,on') + + r = self.do_get('rf_locked') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'rf_locked') + self.assertEquals(r['value'], 'state=off,policy=on') + + def testTimezone(self): + r = self.do_get('timezone') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'timezone') + self.assertEquals(r['value'], 'off') + + r = self.do_set('timezone', '-2,15,2') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'timezone') + self.assertEquals(r['value'], '-2,15,2') + + r = self.do_get('timezone') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'timezone') + self.assertEquals(r['value'], '-2,15,2') + + # Test invalid input + r = self.do_set('timezone', '-2,15,2,5,6,7') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'timezone') + self.assertEquals(r['value'], '-2,15,2') + + r = self.do_set('timezone', '-2,15') + self.assertEquals(r['mtype'], 'ERROR') + r = self.do_set('timezone', '-2') + self.assertEquals(r['mtype'], 'ERROR') + r = self.do_set('timezone', '1') + + r = self.do_set('timezone', 'off') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'timezone') + self.assertEquals(r['value'], 'off') + + r = self.do_get('timezone') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'timezone') + self.assertEquals(r['value'], 'off') + + def testMcc(self): + r = self.do_set('mcc', '23') + r = self.do_get('mcc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mcc') + self.assertEquals(r['value'], '023') + + r = self.do_set('mcc', '023') + r = self.do_get('mcc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mcc') + self.assertEquals(r['value'], '023') + + def testMnc(self): + r = self.do_set('mnc', '9') + r = self.do_get('mnc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mnc') + self.assertEquals(r['value'], '09') + + r = self.do_set('mnc', '09') + r = self.do_get('mnc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mnc') + self.assertEquals(r['value'], '09') + + r = self.do_set('mnc', '009') + r = self.do_get('mnc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mnc') + self.assertEquals(r['value'], '009') + + + def testMccMncApply(self): + # Test some invalid input + r = self.do_set('mcc-mnc-apply', 'WRONG') + self.assertEquals(r['mtype'], 'ERROR') + + r = self.do_set('mcc-mnc-apply', '1,') + self.assertEquals(r['mtype'], 'ERROR') + + r = self.do_set('mcc-mnc-apply', '200,3') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'mcc-mnc-apply') + self.assertEquals(r['value'], 'Tried to drop the BTS') + + # Set it again + r = self.do_set('mcc-mnc-apply', '200,3') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'mcc-mnc-apply') + self.assertEquals(r['value'], 'Nothing changed') + + # Change it + r = self.do_set('mcc-mnc-apply', '200,4') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'mcc-mnc-apply') + self.assertEquals(r['value'], 'Tried to drop the BTS') + + # Change it + r = self.do_set('mcc-mnc-apply', '201,4') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'mcc-mnc-apply') + self.assertEquals(r['value'], 'Tried to drop the BTS') + + # Verify + r = self.do_get('mnc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mnc') + self.assertEquals(r['value'], '04') + + r = self.do_get('mcc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mcc') + self.assertEquals(r['value'], '201') + + # Change it + r = self.do_set('mcc-mnc-apply', '202,03') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'mcc-mnc-apply') + self.assertEquals(r['value'], 'Tried to drop the BTS') + + r = self.do_get('mnc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mnc') + self.assertEquals(r['value'], '03') + + r = self.do_get('mcc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mcc') + self.assertEquals(r['value'], '202') + + # Test MNC with 3 digits + r = self.do_set('mcc-mnc-apply', '2,003') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'mcc-mnc-apply') + self.assertEquals(r['value'], 'Tried to drop the BTS') + + r = self.do_get('mnc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mnc') + self.assertEquals(r['value'], '003') + + r = self.do_get('mcc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mcc') + self.assertEquals(r['value'], '002') + + # Set same MNC with 3 digits + r = self.do_set('mcc-mnc-apply', '2,003') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'mcc-mnc-apply') + self.assertEquals(r['value'], 'Nothing changed') + + r = self.do_get('mnc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mnc') + self.assertEquals(r['value'], '003') + + r = self.do_get('mcc') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'mcc') + self.assertEquals(r['value'], '002') + +class TestCtrlNAT(TestCtrlBase): + + def ctrl_command(self): + return ["./src/osmo-bsc_nat/osmo-bsc_nat", "-c", + "doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"] + + def ctrl_app(self): + return (4250, "./src/osmo-bsc_nat/osmo-bsc_nat", "OsmoNAT", "nat") + + def testAccessList(self): + r = self.do_get('net.0.bsc_cfg.0.access-list-name') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'net') + self.assertEquals(r['value'], None) + + r = self.do_set('net.0.bsc_cfg.0.access-list-name', 'bla') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'net') + self.assertEquals(r['value'], 'bla') + + r = self.do_get('net.0.bsc_cfg.0.access-list-name') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'net') + self.assertEquals(r['value'], 'bla') + + r = self.do_set('net.0.bsc_cfg.0.no-access-list-name', '1') + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'net') + self.assertEquals(r['value'], None) + + r = self.do_get('net.0.bsc_cfg.0.access-list-name') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'net') + self.assertEquals(r['value'], None) + + def testAccessListManagement(self): + r = self.do_set("net.0.add.allow.access-list.404", "abc") + self.assertEquals(r['mtype'], 'ERROR') + + r = self.do_set("net.0.add.allow.access-list.bla", "^234$") + self.assertEquals(r['mtype'], 'SET_REPLY') + self.assertEquals(r['var'], 'net.0.add.allow.access-list.bla') + self.assertEquals(r['value'], 'IMSI allow added to access list') + + # TODO.. find a way to actually see if this rule has been + # added. e.g. by implementing a get for the list. + +class TestCtrlSGSN(TestCtrlBase): + def ctrl_command(self): + return ["./src/gprs/osmo-sgsn", "-c", + "doc/examples/osmo-sgsn/osmo-sgsn.cfg"] + + def ctrl_app(self): + return (4251, "./src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn") + + def testListSubscribers(self): + # TODO. Add command to mark a subscriber as active + r = self.do_get('subscriber-list-active-v1') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'subscriber-list-active-v1') + self.assertEquals(r['value'], None) + +def add_bsc_test(suite, workdir): + if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")): + print("Skipping the BSC test") + return + test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlBSC) + suite.addTest(test) + +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(TestCtrlNAT) + suite.addTest(test) + +def add_sgsn_test(suite, workdir): + if not os.path.isfile(os.path.join(workdir, "src/gprs/osmo-sgsn")): + print("Skipping the SGSN test") + return + test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlSGSN) + 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 + verbose = True + + 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 control commands" + suite = unittest.TestSuite() + add_bsc_test(suite, workdir) + add_nat_test(suite, workdir) + add_sgsn_test(suite, workdir) + res = unittest.TextTestRunner(verbosity=verbose_level).run(suite) + sys.exit(len(res.errors) + len(res.failures)) diff --git a/tests/gsm0408/Makefile.am b/tests/gsm0408/Makefile.am new file mode 100644 index 0000000..9a74d44 --- /dev/null +++ b/tests/gsm0408/Makefile.am @@ -0,0 +1,30 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(NULL) + +noinst_PROGRAMS = \ + gsm0408_test \ + $(NULL) + +EXTRA_DIST = \ + gsm0408_test.ok \ + $(NULL) + +gsm0408_test_SOURCES = \ + gsm0408_test.c \ + $(NULL) + +gsm0408_test_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(NULL) diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c new file mode 100644 index 0000000..a934806 --- /dev/null +++ b/tests/gsm0408/gsm0408_test.c @@ -0,0 +1,850 @@ +/* simple test for the gsm0408 formatting functions */ +/* + * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <arpa/inet.h> + +#include <osmocom/bsc/common_bsc.h> +#include <osmocom/bsc/gsm_data.h> +#include <osmocom/bsc/debug.h> +#include <osmocom/bsc/arfcn_range_encode.h> +#include <osmocom/bsc/system_information.h> +#include <osmocom/bsc/abis_rsl.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/byteswap.h> +#include <osmocom/gsm/sysinfo.h> +#include <osmocom/gsm/gsm48.h> + +#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); \ + } + +#define DBG(...) + +#define VERIFY(res, cmp, wanted) \ + if (!(res cmp wanted)) { \ + printf("ASSERT failed: %s:%d Wanted: %d %s %d\n", \ + __FILE__, __LINE__, (int) res, # cmp, (int) wanted); \ + } + + + +static inline void gen(struct gsm_bts *bts, const char *s) +{ + int r; + + bts->si_valid = 0; + bts->si_valid |= (1 << SYSINFO_TYPE_2quater); + + printf("generating SI2quater for %zu EARFCNs and %zu UARFCNs...\n", + si2q_earfcn_count(&bts->si_common.si2quater_neigh_list), bts->si_common.uarfcn_length); + + r = gsm_generate_si(bts, SYSINFO_TYPE_2quater); + if (r > 0) + for (bts->si2q_index = 0; bts->si2q_index < bts->si2q_count + 1; bts->si2q_index++) + printf("generated %s SI2quater [%02u/%02u]: [%d] %s\n", + GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_2quater) ? "valid" : "invalid", + bts->si2q_index, bts->si2q_count, r, + osmo_hexdump((void *)GSM_BTS_SI2Q(bts, bts->si2q_index), GSM_MACBLOCK_LEN)); + else + printf("%s() failed to generate SI2quater: %s\n", s, strerror(-r)); +} + +static inline void del_earfcn_b(struct gsm_bts *bts, uint16_t earfcn) +{ + struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; + int r = osmo_earfcn_del(e, earfcn); + if (r) + printf("failed to remove EARFCN %u: %s\n", earfcn, strerror(-r)); + else + printf("removed EARFCN %u - ", earfcn); + + gen(bts, __func__); +} + +static inline void add_earfcn_b(struct gsm_bts *bts, uint16_t earfcn, uint8_t bw) +{ + struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; + int r = osmo_earfcn_add(e, earfcn, bw); + if (r) + printf("failed to add EARFCN %u: %s\n", earfcn, strerror(-r)); + else + printf("added EARFCN %u - ", earfcn); + + gen(bts, __func__); +} + +static inline void _bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble, bool diversity) +{ + int r; + + bts->u_offset = 0; + + r = bts_uarfcn_add(bts, arfcn, scramble, diversity); + if (r < 0) + printf("failed to add UARFCN to SI2quater: %s\n", strerror(-r)); + else { + bts->si2q_count = si2q_num(bts) - 1; + gen(bts, __func__); + } +} + +#define bts_init(net) _bts_init(net, __func__) +static inline struct gsm_bts *_bts_init(struct gsm_network *net, const char *msg) +{ + struct gsm_bts *bts = gsm_bts_alloc(net, 0); + if (!bts) { + printf("BTS allocation failure in %s()\n", msg); + exit(1); + } + printf("BTS allocation OK in %s()\n", msg); + + bts->network = net; + + return bts; +} + +#define bts_del(bts) _bts_del(bts, __func__) +static inline void _bts_del(struct gsm_bts *bts, const char *msg) +{ + osmo_stat_item_group_free(bts->bts_statg); + rate_ctr_group_free(bts->bts_ctrs); + /* no need to llist_del(&bts->list), we never registered the bts there. */ + talloc_free(bts); + printf("BTS deallocated OK in %s()\n", msg); +} + +static inline void test_si2q_segfault(struct gsm_network *net) +{ + struct gsm_bts *bts = bts_init(net); + printf("Test SI2quater UARFCN (same scrambling code and diversity):\n"); + + _bts_uarfcn_add(bts, 10564, 319, 0); + _bts_uarfcn_add(bts, 10612, 319, 0); + gen(bts, __func__); + + bts_del(bts); +} + +static inline void test_si2q_mu(struct gsm_network *net) +{ + struct gsm_bts *bts = bts_init(net); + printf("Test SI2quater multiple UARFCNs:\n"); + + _bts_uarfcn_add(bts, 10564, 318, 0); + _bts_uarfcn_add(bts, 10612, 319, 0); + _bts_uarfcn_add(bts, 10612, 31, 0); + _bts_uarfcn_add(bts, 10612, 19, 0); + _bts_uarfcn_add(bts, 10613, 64, 0); + _bts_uarfcn_add(bts, 10613, 164, 0); + _bts_uarfcn_add(bts, 10613, 14, 0); + + bts_del(bts); +} + +static inline void test_si2q_u(struct gsm_network *net) +{ + struct gsm_bts *bts = bts_init(net); + printf("Testing SYSINFO_TYPE_2quater UARFCN generation:\n"); + + /* first generate invalid SI as no UARFCN added */ + gen(bts, __func__); + + /* subsequent calls should produce valid SI if there's enough memory */ + _bts_uarfcn_add(bts, 1982, 13, 1); + _bts_uarfcn_add(bts, 1982, 44, 0); + _bts_uarfcn_add(bts, 1982, 61, 1); + _bts_uarfcn_add(bts, 1982, 89, 1); + _bts_uarfcn_add(bts, 1982, 113, 0); + _bts_uarfcn_add(bts, 1982, 123, 0); + _bts_uarfcn_add(bts, 1982, 56, 1); + _bts_uarfcn_add(bts, 1982, 72, 1); + _bts_uarfcn_add(bts, 1982, 223, 1); + _bts_uarfcn_add(bts, 1982, 14, 0); + _bts_uarfcn_add(bts, 1982, 88, 0); + + bts_del(bts); +} + +static inline void test_si2q_e(struct gsm_network *net) +{ + struct gsm_bts *bts = bts_init(net); + printf("Testing SYSINFO_TYPE_2quater EARFCN generation:\n"); + + bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list; + bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list; + bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST; + bts->si_common.si2quater_neigh_list.thresh_hi = 5; + + osmo_earfcn_init(&bts->si_common.si2quater_neigh_list); + + /* first generate invalid SI as no EARFCN added */ + gen(bts, __func__); + + /* subsequent calls should produce valid SI if there's enough memory and EARFCNs */ + add_earfcn_b(bts, 1917, 5); + del_earfcn_b(bts, 1917); + add_earfcn_b(bts, 1917, 1); + add_earfcn_b(bts, 1932, OSMO_EARFCN_MEAS_INVALID); + add_earfcn_b(bts, 1937, 2); + add_earfcn_b(bts, 1945, OSMO_EARFCN_MEAS_INVALID); + add_earfcn_b(bts, 1965, OSMO_EARFCN_MEAS_INVALID); + add_earfcn_b(bts, 1967, 4); + add_earfcn_b(bts, 1982, 3); + + bts_del(bts); +} + +static inline void test_si2q_long(struct gsm_network *net) +{ + struct gsm_bts *bts = bts_init(net); + printf("Testing SYSINFO_TYPE_2quater combined EARFCN & UARFCN generation:\n"); + + bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list; + bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list; + bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST; + bts->si_common.si2quater_neigh_list.thresh_hi = 5; + + osmo_earfcn_init(&bts->si_common.si2quater_neigh_list); + + bts_earfcn_add(bts, 1922, 11, 22, 8,32, 8); + bts_earfcn_add(bts, 1922, 11, 22, 8, 32, 8); + bts_earfcn_add(bts, 1924, 11, 12, 6, 11, 5); + bts_earfcn_add(bts, 1923, 11, 12, 6, 11, 5); + bts_earfcn_add(bts, 1925, 11, 12, 6, 11, 5); + bts_earfcn_add(bts, 2111, 11, 12, 6, 11, 5); + bts_earfcn_add(bts, 2112, 11, 12, 6, 11, 4); + bts_earfcn_add(bts, 2113, 11, 12, 6, 11, 3); + bts_earfcn_add(bts, 2114, 11, 12, 6, 11, 2); + bts_earfcn_add(bts, 2131, 11, 12, 6, 11, 5); + bts_earfcn_add(bts, 2132, 11, 12, 6, 11, 4); + bts_earfcn_add(bts, 2133, 11, 12, 6, 11, 3); + bts_earfcn_add(bts, 2134, 11, 12, 6, 11, 2); + bts_earfcn_add(bts, 2121, 11, 12, 6, 11, 5); + bts_earfcn_add(bts, 2122, 11, 12, 6, 11, 4); + bts_earfcn_add(bts, 2123, 11, 12, 6, 11, 3); + bts_earfcn_add(bts, 2124, 11, 12, 6, 11, 2); + _bts_uarfcn_add(bts, 1976, 13, 1); + _bts_uarfcn_add(bts, 1976, 38, 1); + _bts_uarfcn_add(bts, 1976, 44, 1); + _bts_uarfcn_add(bts, 1976, 120, 1); + _bts_uarfcn_add(bts, 1976, 140, 1); + _bts_uarfcn_add(bts, 1976, 163, 1); + _bts_uarfcn_add(bts, 1976, 166, 1); + _bts_uarfcn_add(bts, 1976, 217, 1); + _bts_uarfcn_add(bts, 1976, 224, 1); + _bts_uarfcn_add(bts, 1976, 225, 1); + _bts_uarfcn_add(bts, 1976, 226, 1); + + bts_del(bts); +} + +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); +} + +struct { + int range; + int arfcns_num; + int arfcns[RANGE_ENC_MAX_ARFCNS]; +} arfcn_test_ranges[] = { + {ARFCN_RANGE_512, 12, + { 1, 12, 31, 51, 57, 91, 97, 98, 113, 117, 120, 125 }}, + {ARFCN_RANGE_512, 17, + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }}, + {ARFCN_RANGE_512, 18, + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }}, + {ARFCN_RANGE_512, 18, + { 1, 17, 31, 45, 58, 79, 81, 97, + 113, 127, 213, 277, 287, 311, 331, 391, + 417, 511 }}, + {ARFCN_RANGE_512, 6, + { 1, 17, 31, 45, 58, 79 }}, + {ARFCN_RANGE_512, 6, + { 10, 17, 31, 45, 58, 79 }}, + {ARFCN_RANGE_1024, 17, + { 0, 17, 31, 45, 58, 79, 81, 97, + 113, 127, 213, 277, 287, 311, 331, 391, + 1023 }}, + {ARFCN_RANGE_1024, 16, + { 17, 31, 45, 58, 79, 81, 97, 113, + 127, 213, 277, 287, 311, 331, 391, 1023 }}, + {-1} +}; + +static int test_single_range_encoding(int range, const int *orig_arfcns, + int arfcns_num, int silent) +{ + int arfcns[RANGE_ENC_MAX_ARFCNS]; + int w[RANGE_ENC_MAX_ARFCNS]; + int f0_included = 0; + int rc, f0; + uint8_t chan_list[16] = {0}; + struct gsm_sysinfo_freq dec_freq[1024] = {{0}}; + int dec_arfcns[RANGE_ENC_MAX_ARFCNS] = {0}; + int dec_arfcns_count = 0; + int arfcns_used = 0; + int i; + + arfcns_used = arfcns_num; + memmove(arfcns, orig_arfcns, sizeof(arfcns)); + + f0 = range == ARFCN_RANGE_1024 ? 0 : arfcns[0]; + /* + * Manipulate the ARFCN list according to the rules in J4 depending + * on the selected range. + */ + arfcns_used = range_enc_filter_arfcns(arfcns, arfcns_used, + f0, &f0_included); + + memset(w, 0, sizeof(w)); + range_enc_arfcns(range, arfcns, arfcns_used, w, 0); + + if (!silent) + fprintf(stderr, "range=%d, arfcns_used=%d, f0=%d, f0_included=%d\n", + range, arfcns_used, f0, f0_included); + + /* Select the range and the amount of bits needed */ + switch (range) { + case ARFCN_RANGE_128: + range_enc_range128(chan_list, f0, w); + break; + case ARFCN_RANGE_256: + range_enc_range256(chan_list, f0, w); + break; + case ARFCN_RANGE_512: + range_enc_range512(chan_list, f0, w); + break; + case ARFCN_RANGE_1024: + range_enc_range1024(chan_list, f0, f0_included, w); + break; + default: + return 1; + }; + + if (!silent) + printf("chan_list = %s\n", + osmo_hexdump(chan_list, sizeof(chan_list))); + + rc = gsm48_decode_freq_list(dec_freq, chan_list, sizeof(chan_list), + 0xfe, 1); + if (rc != 0) { + printf("Cannot decode freq list, rc = %d\n", rc); + return 1; + } + + for (i = 0; i < ARRAY_SIZE(dec_freq); i++) { + if (dec_freq[i].mask && + dec_arfcns_count < ARRAY_SIZE(dec_arfcns)) + dec_arfcns[dec_arfcns_count++] = i; + } + + if (!silent) { + printf("Decoded freqs %d (expected %d)\n", + dec_arfcns_count, arfcns_num); + printf("Decoded: "); + for (i = 0; i < dec_arfcns_count; i++) { + printf("%d ", dec_arfcns[i]); + if (dec_arfcns[i] != orig_arfcns[i]) + printf("(!= %d) ", orig_arfcns[i]); + } + printf("\n"); + } + + if (dec_arfcns_count != arfcns_num) { + printf("Wrong number of arfcns\n"); + return 1; + } + + if (memcmp(dec_arfcns, orig_arfcns, sizeof(dec_arfcns)) != 0) { + printf("Decoding error, got wrong freqs\n"); + fprintf(stderr, " w = "); + for (i = 0; i < ARRAY_SIZE(w); i++) + fprintf(stderr, "%d ", w[i]); + fprintf(stderr, "\n"); + return 1; + } + + return 0; +} + +static void test_random_range_encoding(int range, int max_arfcn_num) +{ + int arfcns_num = 0; + int test_idx; + int rc, max_count; + int num_tests = 1024; + + printf("Random range test: range %d, max num ARFCNs %d\n", + range, max_arfcn_num); + + srandom(1); + + for (max_count = 1; max_count < max_arfcn_num; max_count++) { + for (test_idx = 0; test_idx < num_tests; test_idx++) { + int count; + int i; + int min_freq = 0; + + int rnd_arfcns[RANGE_ENC_MAX_ARFCNS] = {0}; + char rnd_arfcns_set[1024] = {0}; + + if (range < ARFCN_RANGE_1024) + min_freq = random() % (1023 - range); + + for (count = max_count; count; ) { + int arfcn = min_freq + random() % (range + 1); + OSMO_ASSERT(arfcn < ARRAY_SIZE(rnd_arfcns_set)); + + if (!rnd_arfcns_set[arfcn]) { + rnd_arfcns_set[arfcn] = 1; + count -= 1; + } + } + + arfcns_num = 0; + for (i = 0; i < ARRAY_SIZE(rnd_arfcns_set); i++) + if (rnd_arfcns_set[i]) + rnd_arfcns[arfcns_num++] = i; + + rc = test_single_range_encoding(range, rnd_arfcns, + arfcns_num, 1); + if (rc != 0) { + printf("Failed on test %d, range %d, num ARFCNs %d\n", + test_idx, range, max_count); + test_single_range_encoding(range, rnd_arfcns, + arfcns_num, 0); + return; + } + } + } +} + +static void test_range_encoding() +{ + int *arfcns; + int arfcns_num = 0; + int test_idx; + int range; + + for (test_idx = 0; arfcn_test_ranges[test_idx].arfcns_num > 0; test_idx++) + { + arfcns_num = arfcn_test_ranges[test_idx].arfcns_num; + arfcns = &arfcn_test_ranges[test_idx].arfcns[0]; + range = arfcn_test_ranges[test_idx].range; + + printf("Range test %d: range %d, num ARFCNs %d\n", + test_idx, range, arfcns_num); + + test_single_range_encoding(range, arfcns, arfcns_num, 0); + } + + test_random_range_encoding(ARFCN_RANGE_128, 29); + test_random_range_encoding(ARFCN_RANGE_256, 22); + test_random_range_encoding(ARFCN_RANGE_512, 18); + test_random_range_encoding(ARFCN_RANGE_1024, 16); +} + +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(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) - (2+1)); + + /* check with range1024, ARFCN 0 is included */ + for (i = 0; i < ARRAY_SIZE(arfcns); ++i) + arfcns[i] = i * 2; + res = range_enc_filter_arfcns(arfcns, ARRAY_SIZE(arfcns), + 0, &f0_included); + VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1); + VERIFY(f0_included, ==, 1); + for (i = 0; i < res; ++i) + VERIFY(arfcns[i], ==, (i + 1) * 2 - 1); + + /* check with range1024, ARFCN 0 not included */ + for (i = 0; i < ARRAY_SIZE(arfcns); ++i) + arfcns[i] = (i + 1) * 2; + res = range_enc_filter_arfcns(arfcns, ARRAY_SIZE(arfcns), + 0, &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; + } + + range_enc_range512(chan_list, (1 << 9) | 0x96, w); + + printf("Range512: %s\n", osmo_hexdump(chan_list, ARRAY_SIZE(chan_list))); +} + +static void test_si_range_helpers() +{ + 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, i >= 0 ? freqs1[i] : -1); + VERIFY(i, ==, 2); + + i = range_enc_find_index(511, freqs2, ARRAY_SIZE(freqs2)); + printf("Element is: %d => freqs[i] = %d\n", i, i >= 0 ? freqs2[i] : -1); + VERIFY(i, ==, 2); + + i = range_enc_find_index(511, freqs3, ARRAY_SIZE(freqs3)); + printf("Element is: %d => freqs[i] = %d\n", i, i >= 0 ? freqs3[i] : -1); + VERIFY(i, ==, 0); + + range_enc_arfcns(1023, freqs1, ARRAY_SIZE(freqs1), ws, 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); +} + +static void test_si_ba_ind(struct gsm_network *net) +{ + struct gsm_bts *bts = bts_init(net); + + const struct gsm48_system_information_type_2 *si2 = + (struct gsm48_system_information_type_2 *) GSM_BTS_SI(bts, SYSINFO_TYPE_2); + const struct gsm48_system_information_type_2bis *si2bis = + (struct gsm48_system_information_type_2bis *) GSM_BTS_SI(bts, SYSINFO_TYPE_2bis); + const struct gsm48_system_information_type_2ter *si2ter = + (struct gsm48_system_information_type_2ter *) GSM_BTS_SI(bts, SYSINFO_TYPE_2ter); + const struct gsm48_system_information_type_5 *si5 = + (struct gsm48_system_information_type_5 *) GSM_BTS_SI(bts, SYSINFO_TYPE_5); + const struct gsm48_system_information_type_5bis *si5bis = + (struct gsm48_system_information_type_5bis *) GSM_BTS_SI(bts, SYSINFO_TYPE_5bis); + const struct gsm48_system_information_type_5ter *si5ter = + (struct gsm48_system_information_type_5ter *) GSM_BTS_SI(bts, SYSINFO_TYPE_5ter); + + int rc; + + bts->c0->arfcn = 23; + + printf("Testing if BA-IND is set as expected in SI2xxx and SI5xxx\n"); + + rc = gsm_generate_si(bts, SYSINFO_TYPE_2); + OSMO_ASSERT(rc > 0); + printf("SI2: %s\n", osmo_hexdump((uint8_t *)si2, rc)); + /* Validate BA-IND == 0 */ + OSMO_ASSERT(!(si2->bcch_frequency_list[0] & 0x10)); + + rc = gsm_generate_si(bts, SYSINFO_TYPE_2bis); + OSMO_ASSERT(rc > 0); + printf("SI2bis: %s\n", osmo_hexdump((uint8_t *)si2bis, rc)); + /* Validate BA-IND == 0 */ + OSMO_ASSERT(!(si2bis->bcch_frequency_list[0] & 0x10)); + + rc = gsm_generate_si(bts, SYSINFO_TYPE_2ter); + OSMO_ASSERT(rc > 0); + printf("SI2ter: %s\n", osmo_hexdump((uint8_t *)si2ter, rc)); + /* Validate BA-IND == 0 */ + OSMO_ASSERT(!(si2ter->ext_bcch_frequency_list[0] & 0x10)); + + rc = gsm_generate_si(bts, SYSINFO_TYPE_5); + OSMO_ASSERT(rc > 0); + printf("SI5: %s\n", osmo_hexdump((uint8_t *)si5, rc)); + /* Validate BA-IND == 1 */ + OSMO_ASSERT(si5->bcch_frequency_list[0] & 0x10); + + rc = gsm_generate_si(bts, SYSINFO_TYPE_5bis); + OSMO_ASSERT(rc > 0); + printf("SI5bis: %s\n", osmo_hexdump((uint8_t *)si5bis, rc)); + /* Validate BA-IND == 1 */ + OSMO_ASSERT(si5bis->bcch_frequency_list[0] & 0x10); + + rc = gsm_generate_si(bts, SYSINFO_TYPE_5ter); + OSMO_ASSERT(rc > 0); + printf("SI5ter: %s\n", osmo_hexdump((uint8_t *)si5ter, rc)); + /* Validate BA-IND == 1 */ + OSMO_ASSERT(si5ter->bcch_frequency_list[0] & 0x10); + + bts_del(bts); +} + +struct test_gsm48_ra_id_by_bts { + struct osmo_plmn_id plmn; + uint16_t lac; + uint8_t rac; + struct gsm48_ra_id expect; +}; +static const struct test_gsm48_ra_id_by_bts test_gsm48_ra_id_by_bts_data[] = { + { + .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = false }, + .lac = 3, + .rac = 4, + .expect = { + .digits = { 0x00, 0xf1, 0x20 }, + .lac = 0x0300, /* network byte order of 3 */ + .rac = 4, + }, + }, + { + .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = true }, + .lac = 3, + .rac = 4, + .expect = { + .digits = { 0x00, 0x21, 0x00 }, + .lac = 0x0300, /* network byte order of 3 */ + .rac = 4, + }, + }, + { + .plmn = { .mcc = 0, .mnc = 0, .mnc_3_digits = false }, + .lac = 0, + .rac = 0, + .expect = { + .digits = { 0x00, 0xf0, 0x00 }, + }, + }, + { + .plmn = { .mcc = 0, .mnc = 0, .mnc_3_digits = true }, + .lac = 0, + .rac = 0, + .expect = { + .digits = {}, + }, + }, + { + .plmn = { .mcc = 999, .mnc = 999, .mnc_3_digits = false }, + .lac = 65535, + .rac = 255, + .expect = { + .digits = { 0x99, 0x99, 0x99 }, + .lac = 0xffff, + .rac = 0xff, + }, + }, + { + .plmn = { .mcc = 909, .mnc = 90, .mnc_3_digits = false }, + .lac = 0xabcd, + .rac = 0xab, + .expect = { + .digits = { 0x09, 0xf9, 0x09 }, + .lac = 0xcdab, + .rac = 0xab, + }, + }, + { + .plmn = { .mcc = 909, .mnc = 90, .mnc_3_digits = true }, + .lac = 0xabcd, + .rac = 0xab, + .expect = { + .digits = { 0x09, 0x09, 0x90 }, + .lac = 0xcdab, + .rac = 0xab, + }, + }, +}; + +static void test_gsm48_ra_id_by_bts() +{ + int i; + bool pass = true; + + for (i = 0; i < ARRAY_SIZE(test_gsm48_ra_id_by_bts_data); i++) { + struct gsm_network net; + struct gsm_bts bts; + const struct test_gsm48_ra_id_by_bts *t = &test_gsm48_ra_id_by_bts_data[i]; + struct gsm48_ra_id result = {}; + bool ok; + + net.plmn = t->plmn; + bts.network = &net; + bts.location_area_code = t->lac; + bts.gprs.rac = t->rac; + + gsm48_ra_id_by_bts(&result, &bts); + + ok = (t->expect.digits[0] == result.digits[0]) + && (t->expect.digits[1] == result.digits[1]) + && (t->expect.digits[2] == result.digits[2]) + && (t->expect.lac == result.lac) + && (t->expect.rac == result.rac); + printf("%s[%d]: digits='%02x%02x%02x' lac=0x%04x=htons(%u) rac=0x%02x=%u %s\n", + __func__, i, + result.digits[0], result.digits[1], result.digits[2], + result.lac, osmo_ntohs(result.lac), result.rac, result.rac, + ok ? "pass" : "FAIL"); + pass = pass && ok; + } + + OSMO_ASSERT(pass); +} + +static const struct log_info_cat log_categories[] = { +}; + +static const struct log_info log_info = { + .cat = log_categories, + .num_cat = ARRAY_SIZE(log_categories), +}; + +int main(int argc, char **argv) +{ + struct gsm_network *net; + + tall_bsc_ctx = talloc_named_const(NULL, 0, "gsm0408_test"); + + osmo_init_logging2(tall_bsc_ctx, &log_info); + log_set_log_level(osmo_stderr_target, LOGL_INFO); + + net = bsc_network_init(tall_bsc_ctx); + if (!net) { + printf("Network init failure.\n"); + return EXIT_FAILURE; + } + + test_mi_functionality(); + + test_si_range_helpers(); + test_arfcn_filter(); + test_print_encoding(); + test_range_encoding(); + + test_si2q_segfault(net); + test_si2q_e(net); + test_si2q_u(net); + test_si2q_mu(net); + test_si2q_long(net); + + test_si_ba_ind(net); + + test_gsm48_ra_id_by_bts(); + + printf("Done.\n"); + + return EXIT_SUCCESS; +} + +struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) { + OSMO_ASSERT(0); +} diff --git a/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok new file mode 100644 index 0000000..7270721 --- /dev/null +++ b/tests/gsm0408/gsm0408_test.ok @@ -0,0 +1,229 @@ +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 +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: 89 4b 2a 95 65 95 55 2c a9 55 aa 55 6a 95 59 55 +Range test 0: range 511, num ARFCNs 12 +chan_list = 88 00 98 34 85 36 7c 50 22 dc 5e ec 00 00 00 00 +Decoded freqs 12 (expected 12) +Decoded: 1 12 31 51 57 91 97 98 113 117 120 125 +Range test 1: range 511, num ARFCNs 17 +chan_list = 88 00 82 7f 01 3f 7e 04 0b ff ff fc 10 41 07 e0 +Decoded freqs 17 (expected 17) +Decoded: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +Range test 2: range 511, num ARFCNs 18 +chan_list = 88 00 82 7f 01 7f 7e 04 0b ff ff fc 10 41 07 ff +Decoded freqs 18 (expected 18) +Decoded: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 +Range test 3: range 511, num ARFCNs 18 +chan_list = 88 00 94 3a 44 32 d7 2a 43 2a 13 94 e5 38 39 f6 +Decoded freqs 18 (expected 18) +Decoded: 1 17 31 45 58 79 81 97 113 127 213 277 287 311 331 391 417 511 +Range test 4: range 511, num ARFCNs 6 +chan_list = 88 00 8b 3c 88 b9 6b 00 00 00 00 00 00 00 00 00 +Decoded freqs 6 (expected 6) +Decoded: 1 17 31 45 58 79 +Range test 5: range 511, num ARFCNs 6 +chan_list = 88 05 08 fc 88 b9 6b 00 00 00 00 00 00 00 00 00 +Decoded freqs 6 (expected 6) +Decoded: 10 17 31 45 58 79 +Range test 6: range 1023, num ARFCNs 17 +chan_list = 84 71 e4 ab b9 58 05 cb 39 17 fd b0 75 62 0f 2f +Decoded freqs 17 (expected 17) +Decoded: 0 17 31 45 58 79 81 97 113 127 213 277 287 311 331 391 1023 +Range test 7: range 1023, num ARFCNs 16 +chan_list = 80 71 e4 ab b9 58 05 cb 39 17 fd b0 75 62 0f 2f +Decoded freqs 16 (expected 16) +Decoded: 17 31 45 58 79 81 97 113 127 213 277 287 311 331 391 1023 +Random range test: range 127, max num ARFCNs 29 +Random range test: range 255, max num ARFCNs 22 +Random range test: range 511, max num ARFCNs 18 +Random range test: range 1023, max num ARFCNs 16 +BTS allocation OK in test_si2q_segfault() +Test SI2quater UARFCN (same scrambling code and diversity): +generating SI2quater for 0 EARFCNs and 1 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7e 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 2 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7f 52 e8 0a 7e 0b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 2 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7f 52 e8 0a 7e 0b 2b 2b 2b 2b 2b 2b 2b 2b +BTS deallocated OK in test_si2q_segfault() +BTS allocation OK in test_si2q_e() +Testing SYSINFO_TYPE_2quater EARFCN generation: +generating SI2quater for 0 EARFCNs and 0 UARFCNs... +generated invalid SI2quater [00/00]: [23] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +added EARFCN 1917 - generating SI2quater for 1 EARFCNs and 0 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be e8 50 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b +removed EARFCN 1917 - generating SI2quater for 0 EARFCNs and 0 UARFCNs... +generated invalid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be e8 50 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b +added EARFCN 1917 - generating SI2quater for 1 EARFCNs and 0 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be c8 50 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b +added EARFCN 1932 - generating SI2quater for 2 EARFCNs and 0 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be cc 1e 30 14 03 2b 2b 2b 2b 2b 2b 2b 2b +added EARFCN 1937 - generating SI2quater for 3 EARFCNs and 0 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be cc 1e 31 07 91 a0 a0 2b 2b 2b 2b 2b 2b +added EARFCN 1945 - generating SI2quater for 4 EARFCNs and 0 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be cc 1e 31 07 91 a8 3c c8 28 0b 2b 2b 2b +added EARFCN 1965 - generating SI2quater for 5 EARFCNs and 0 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be cc 1e 31 07 91 a8 3c ca 0f 5a 0a 03 2b +added EARFCN 1967 - generating SI2quater for 6 EARFCNs and 0 UARFCNs... +generated valid SI2quater [00/01]: [23] 59 06 07 40 20 04 86 59 83 be cc 1e 31 07 91 a8 3c ca 0f 5a 0a 03 2b +generated valid SI2quater [01/01]: [23] 59 06 07 42 20 04 86 59 83 d7 e0 50 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b +added EARFCN 1982 - generating SI2quater for 7 EARFCNs and 0 UARFCNs... +generated valid SI2quater [00/01]: [23] 59 06 07 40 20 04 86 59 83 be cc 1e 31 07 91 a8 3c ca 0f 5a 0a 03 2b +generated valid SI2quater [01/01]: [23] 59 06 07 42 20 04 86 59 83 d7 e4 1e fa c2 80 2b 2b 2b 2b 2b 2b 2b 2b +BTS deallocated OK in test_si2q_e() +BTS allocation OK in test_si2q_u() +Testing SYSINFO_TYPE_2quater UARFCN generation: +generating SI2quater for 0 EARFCNs and 0 UARFCNs... +generated invalid SI2quater [00/00]: [23] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +generating SI2quater for 0 EARFCNs and 1 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 0c 1a 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 2 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 14 1a 1f 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 3 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 1c 7b d0 f7 03 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 4 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 24 b3 e4 e9 68 03 2b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 5 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 2c 7a 34 0e 4e e9 83 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 6 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 34 7a 34 0e 4e e9 85 03 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 7 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 3c 70 39 02 ce f7 85 0e 03 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 8 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 44 7a 34 05 e4 72 05 08 d5 0b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 9 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 4c 7a 34 0e 64 77 85 43 55 c8 0b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 10 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 50 1c 3b 31 fa dd 88 85 7b c4 1c 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 11 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 58 1c 3b 25 7a ea 08 91 fb c4 1f b0 2b 2b 2b +BTS deallocated OK in test_si2q_u() +BTS allocation OK in test_si2q_mu() +Test SI2quater multiple UARFCNs: +generating SI2quater for 0 EARFCNs and 1 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7c 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 2 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 0a 7e 0b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 3 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 12 7e e0 0b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 4 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 18 3f f4 90 03 2b 2b 2b 2b 2b 2b +generating SI2quater for 0 EARFCNs and 5 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 18 3f f4 90 54 ba 82 20 03 2b 2b +generating SI2quater for 0 EARFCNs and 6 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 18 3f f4 90 54 ba 84 52 67 03 2b +generating SI2quater for 0 EARFCNs and 7 UARFCNs... +generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 18 3f f4 90 54 ba 86 20 73 8c 81 +BTS deallocated OK in test_si2q_mu() +BTS allocation OK in test_si2q_long() +Testing SYSINFO_TYPE_2quater combined EARFCN & UARFCN generation: +generating SI2quater for 17 EARFCNs and 1 UARFCNs... +generated valid SI2quater [00/04]: [23] 59 06 07 40 80 25 0f 70 0c 1a 10 99 66 0f 04 83 c1 1c bb 2b 03 2b 2b +generated valid SI2quater [01/04]: [23] 59 06 07 42 80 04 86 59 83 c2 6c 1e 0f 60 f0 bb 08 3f d7 2e ca c1 2b +generated valid SI2quater [02/04]: [23] 59 06 07 44 80 04 86 59 84 20 64 21 06 e1 08 55 08 53 d7 2e ca c1 2b +generated valid SI2quater [03/04]: [23] 59 06 07 46 80 04 86 59 84 2a 64 21 56 e1 0a d5 08 49 d7 2e ca c1 2b +generated valid SI2quater [04/04]: [23] 59 06 07 48 80 04 86 59 84 25 64 21 2e e1 09 94 e5 d9 58 2b 2b 2b 2b +generating SI2quater for 17 EARFCNs and 2 UARFCNs... +generated valid SI2quater [00/04]: [23] 59 06 07 40 80 25 0f 70 14 4d e7 00 44 b3 07 82 41 e0 8e 5d 95 83 2b +generated valid SI2quater [01/04]: [23] 59 06 07 42 80 04 86 59 83 c2 6c 1e 0f 60 f0 bb 08 3f d7 2e ca c1 2b +generated valid SI2quater [02/04]: [23] 59 06 07 44 80 04 86 59 84 20 64 21 06 e1 08 55 08 53 d7 2e ca c1 2b +generated valid SI2quater [03/04]: [23] 59 06 07 46 80 04 86 59 84 2a 64 21 56 e1 0a d5 08 49 d7 2e ca c1 2b +generated valid SI2quater [04/04]: [23] 59 06 07 48 80 04 86 59 84 25 64 21 2e e1 09 94 e5 d9 58 2b 2b 2b 2b +generating SI2quater for 17 EARFCNs and 3 UARFCNs... +generated valid SI2quater [00/04]: [23] 59 06 07 40 80 25 0f 70 1c 4d e7 03 04 86 59 83 c1 20 f0 47 2e ca c1 +generated valid SI2quater [01/04]: [23] 59 06 07 42 80 04 86 59 83 c2 6c 1e 0f 60 f0 bb 08 3f d7 2e ca c1 2b +generated valid SI2quater [02/04]: [23] 59 06 07 44 80 04 86 59 84 20 64 21 06 e1 08 55 08 53 d7 2e ca c1 2b +generated valid SI2quater [03/04]: [23] 59 06 07 46 80 04 86 59 84 2a 64 21 56 e1 0a d5 08 49 d7 2e ca c1 2b +generated valid SI2quater [04/04]: [23] 59 06 07 48 80 04 86 59 84 25 64 21 2e e1 09 94 e5 d9 58 2b 2b 2b 2b +generating SI2quater for 17 EARFCNs and 4 UARFCNs... +generated valid SI2quater [00/04]: [23] 59 06 07 40 80 25 0f 70 24 59 fa 26 73 84 86 59 83 c1 1c bb 2b 03 2b +generated valid SI2quater [01/04]: [23] 59 06 07 42 80 04 86 59 83 c1 20 f0 9b 07 83 d8 3c 2e b9 76 56 0b 2b +generated valid SI2quater [02/04]: [23] 59 06 07 44 80 04 86 59 84 1f ec 21 03 21 08 37 08 42 a7 2e ca c1 2b +generated valid SI2quater [03/04]: [23] 59 06 07 46 80 04 86 59 84 29 ec 21 53 21 0a b7 08 56 a7 2e ca c1 2b +generated valid SI2quater [04/04]: [23] 59 06 07 48 80 04 86 59 84 24 ec 21 2b 21 09 77 08 4c a7 2e ca c1 2b +generating SI2quater for 17 EARFCNs and 5 UARFCNs... +generated valid SI2quater [00/04]: [23] 59 06 07 40 80 25 0f 70 2c 59 fa 30 73 f6 04 86 59 83 c1 1c bb 2b 03 +generated valid SI2quater [01/04]: [23] 59 06 07 42 80 04 86 59 83 c1 20 f0 9b 07 83 d8 3c 2e b9 76 56 0b 2b +generated valid SI2quater [02/04]: [23] 59 06 07 44 80 04 86 59 84 1f ec 21 03 21 08 37 08 42 a7 2e ca c1 2b +generated valid SI2quater [03/04]: [23] 59 06 07 46 80 04 86 59 84 29 ec 21 53 21 0a b7 08 56 a7 2e ca c1 2b +generated valid SI2quater [04/04]: [23] 59 06 07 48 80 04 86 59 84 24 ec 21 2b 21 09 77 08 4c a7 2e ca c1 2b +generating SI2quater for 17 EARFCNs and 6 UARFCNs... +generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 34 f1 ae 15 f3 f4 83 04 86 59 72 ec ac 0b 2b +generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 86 59 83 c1 20 f0 48 3c 26 c1 e0 f5 cb b2 b0 2b 2b +generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 86 59 83 c2 ec 20 ff 61 08 19 08 41 b7 2e ca c1 2b +generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 86 59 84 21 54 21 4f 61 0a 99 08 55 b7 2e ca c1 2b +generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 86 59 84 2b 54 21 27 61 09 59 08 4b b7 2e ca c1 2b +generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 86 59 84 26 53 97 65 60 2b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 17 EARFCNs and 7 UARFCNs... +generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 3c f1 ae 15 f3 f4 83 01 84 86 59 72 ec ac 0b +generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 86 59 83 c1 20 f0 48 3c 26 c1 e0 f5 cb b2 b0 2b 2b +generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 86 59 83 c2 ec 20 ff 61 08 19 08 41 b7 2e ca c1 2b +generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 86 59 84 21 54 21 4f 61 0a 99 08 55 b7 2e ca c1 2b +generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 86 59 84 2b 54 21 27 61 09 59 08 4b b7 2e ca c1 2b +generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 86 59 84 26 53 97 65 60 2b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 17 EARFCNs and 8 UARFCNs... +generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 45 19 a0 0d 7d 7e a6 19 e7 0b 2b 2b 2b 2b 2b +generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 86 59 83 c1 20 f0 48 3c 26 c1 e0 f5 cb b2 b0 2b 2b +generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 86 59 83 c2 ec 20 ff 61 08 19 08 41 b7 2e ca c1 2b +generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 86 59 84 21 54 21 4f 61 0a 99 08 55 b7 2e ca c1 2b +generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 86 59 84 2b 54 21 27 61 09 59 08 4b b7 2e ca c1 2b +generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 86 59 84 26 53 97 65 60 2b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 17 EARFCNs and 9 UARFCNs... +generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 4d 19 a0 26 fd 66 a6 03 e7 fa 0b 2b 2b 2b 2b +generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 86 59 83 c1 20 f0 48 3c 26 c1 e0 f5 cb b2 b0 2b 2b +generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 86 59 83 c2 ec 20 ff 61 08 19 08 41 b7 2e ca c1 2b +generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 86 59 84 21 54 21 4f 61 0a 99 08 55 b7 2e ca c1 2b +generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 86 59 84 2b 54 21 27 61 09 59 08 4b b7 2e ca c1 2b +generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 86 59 84 26 53 97 65 60 2b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 17 EARFCNs and 10 UARFCNs... +generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 55 47 89 1e fd 7c b0 00 e7 9b b0 2b 2b 2b 2b +generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 86 59 83 c1 20 f0 48 3c 26 c1 e0 f5 cb b2 b0 2b 2b +generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 86 59 83 c2 ec 20 ff 61 08 19 08 41 b7 2e ca c1 2b +generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 86 59 84 21 54 21 4f 61 0a 99 08 55 b7 2e ca c1 2b +generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 86 59 84 2b 54 21 27 61 09 59 08 4b b7 2e ca c1 2b +generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 86 59 84 26 53 97 65 60 2b 2b 2b 2b 2b 2b 2b 2b 2b +generating SI2quater for 17 EARFCNs and 11 UARFCNs... +generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 5d 47 89 1e fd 7c b0 01 67 9b b3 f8 2b 2b 2b +generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 86 59 83 c1 20 f0 48 3c 26 c1 e0 f5 cb b2 b0 2b 2b +generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 86 59 83 c2 ec 20 ff 61 08 19 08 41 b7 2e ca c1 2b +generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 86 59 84 21 54 21 4f 61 0a 99 08 55 b7 2e ca c1 2b +generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 86 59 84 2b 54 21 27 61 09 59 08 4b b7 2e ca c1 2b +generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 86 59 84 26 53 97 65 60 2b 2b 2b 2b 2b 2b 2b 2b 2b +BTS deallocated OK in test_si2q_long() +BTS allocation OK in test_si_ba_ind() +Testing if BA-IND is set as expected in SI2xxx and SI5xxx +SI2: 59 06 1a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e5 04 00 +SI2bis: 59 06 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e5 04 00 2b +SI2ter: 59 06 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2b 2b 2b 2b +SI5: 06 1d 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +SI5bis: 06 05 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +SI5ter: 06 06 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +BTS deallocated OK in test_si_ba_ind() +test_gsm48_ra_id_by_bts[0]: digits='00f120' lac=0x0300=htons(3) rac=0x04=4 pass +test_gsm48_ra_id_by_bts[1]: digits='002100' lac=0x0300=htons(3) rac=0x04=4 pass +test_gsm48_ra_id_by_bts[2]: digits='00f000' lac=0x0000=htons(0) rac=0x00=0 pass +test_gsm48_ra_id_by_bts[3]: digits='000000' lac=0x0000=htons(0) rac=0x00=0 pass +test_gsm48_ra_id_by_bts[4]: digits='999999' lac=0xffff=htons(65535) rac=0xff=255 pass +test_gsm48_ra_id_by_bts[5]: digits='09f909' lac=0xcdab=htons(43981) rac=0xab=171 pass +test_gsm48_ra_id_by_bts[6]: digits='090990' lac=0xcdab=htons(43981) rac=0xab=171 pass +Done. diff --git a/tests/handover/Makefile.am b/tests/handover/Makefile.am new file mode 100644 index 0000000..957bbee --- /dev/null +++ b/tests/handover/Makefile.am @@ -0,0 +1,43 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + -ggdb3 \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(LIBOSMOSIGTRAN_CFLAGS) \ + $(LIBOSMOMGCPCLIENT_CFLAGS) \ + $(NULL) + +AM_LDFLAGS = \ + $(COVERAGE_LDFLAGS) \ + $(NULL) + +EXTRA_DIST = \ + handover_test.ok \ + $(NULL) + +noinst_PROGRAMS = \ + handover_test \ + $(NULL) + +handover_test_SOURCES = \ + handover_test.c \ + $(NULL) + +handover_test_LDFLAGS =\ + -Wl,--wrap=abis_rsl_sendmsg,--wrap=mgcp_conn_modify,--wrap=mgcp_conn_delete\ + $(NULL) + +handover_test_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOSIGTRAN_LIBS) \ + $(LIBOSMOMGCPCLIENT_LIBS) \ + $(NULL) diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c new file mode 100644 index 0000000..82afbe5 --- /dev/null +++ b/tests/handover/handover_test.c @@ -0,0 +1,1699 @@ +/* + * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <assert.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/select.h> +#include <osmocom/core/talloc.h> + +#include <osmocom/mgcp_client/mgcp_client_fsm.h> + +#include <osmocom/bsc/abis_rsl.h> +#include <osmocom/bsc/debug.h> +#include <osmocom/bsc/bsc_subscriber.h> +#include <osmocom/bsc/chan_alloc.h> +#include <osmocom/bsc/handover_decision.h> +#include <osmocom/bsc/system_information.h> +#include <osmocom/bsc/handover_cfg.h> +#include <osmocom/bsc/handover_decision_2.h> +#include <osmocom/bsc/common_bsc.h> +#include <osmocom/bsc/bss.h> +#include <osmocom/bsc/bsc_api.h> +#include <osmocom/bsc/osmo_bsc.h> +#include <osmocom/bsc/bsc_subscr_conn_fsm.h> + +void *ctx; + +struct gsm_network *bsc_gsmnet; + +/* override, requires '-Wl,--wrap=mgcp_conn_modify'. + * Catch modification of an MGCP connection. */ +int __real_mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer); +int __wrap_mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer) +{ + /* CAUTION HACK: + * + * The pointer fi is misused to pass a reference to GSCON FSM ! + * + * This function is called from gscon_fsm_wait_ho_compl() from + * bsc_subscr_conn_fsm.c when GSCON_EV_HO_COMPL is dispatched to the + * GSCON FSM. By then, the GSCON FSM has already changed to the state + * ST_WAIT_MDCX_BTS_HO (see gscon_fsm_wait_mdcx_bts_ho()) and waits for + * GSCON_EV_MGW_MDCX_RESP_BTS. The signal GSCON_EV_MGW_MDCX_RESP_BTS + * is sent to this function using the parameter parent_evt. So we + * implicitly know the event that is needed to simulate a successful + * MGW negotiation to the GSCON FSM. All we need to do is to dispatch + * parent_evt back to the GSCON FSM in order to make it think that the + * MGW negotiation is done. + * + * Unfortunately, there is a problem with this test implementation. + * in order to simplfy the test we do not allocate any MGCP Client + * FSM but the GSCON FSM will call this function with the fi pointer + * pointing to the MGCP Client FSM. This means we get a nullpointer + * here and there is no way to distinguish which GSCON FSM called + * the function at all (normally we would know through the parent + * pointer). + * + * To get around this problem we populate the fi pointer with the + * reference to the GSCON FSM itsself, so we can know who called the + * function. This is a misuse of the pointer since it normally would + * hold an MGCP Client FSM instead of a GSCON FSM. + * + * See also note in function create_conn() */ + + osmo_fsm_inst_dispatch(fi, parent_evt, NULL); + return 0; +} + +/* override, requires '-Wl,--wrap=mgcp_conn_delete'. + * Catch deletion of an MGCP connection. */ +int __real_mgcp_conn_delete(struct osmo_fsm_inst *fi); +int __wrap_mgcp_conn_delete(struct osmo_fsm_inst *fi) +{ + /* Just do nothing and pretend that everything went well. + * We never have allocatec any MGCP connections. */ + return 0; +} + +/* measurement report */ + +uint8_t meas_rep_ba = 0, meas_rep_valid = 1, meas_valid = 1, meas_multi_rep = 0; +uint8_t meas_dl_rxlev = 0, meas_dl_rxqual = 0; +uint8_t meas_ul_rxlev = 0, meas_ul_rxqual = 0; +uint8_t meas_tx_power_ms = 0, meas_tx_power_bs = 0, meas_ta_ms = 0; +uint8_t meas_dtx_ms = 0, meas_dtx_bs = 0, meas_nr = 0; +uint8_t meas_num_nc = 0, meas_rxlev_nc[6], meas_bsic_nc[6], meas_bcch_f_nc[6]; + +static void gen_meas_rep(struct gsm_lchan *lchan) +{ + struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL"); + struct abis_rsl_dchan_hdr *dh; + uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t ulm[3], l1i[2], *buf; + struct gsm48_hdr *gh; + struct gsm48_meas_res *mr; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; + dh->c.msg_type = RSL_MT_MEAS_RES; + dh->ie_chan = RSL_IE_CHAN_NR; + dh->chan_nr = chan_nr; + + msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, meas_nr++); + + ulm[0] = meas_ul_rxlev | (meas_dtx_bs << 7); + ulm[1] = meas_ul_rxlev; + ulm[2] = (meas_ul_rxqual << 3) | meas_ul_rxqual; + msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, sizeof(ulm), ulm); + + msgb_tv_put(msg, RSL_IE_BS_POWER, meas_tx_power_bs); + + l1i[0] = 0; + l1i[1] = meas_ta_ms; + msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, sizeof(l1i), l1i); + + buf = msgb_put(msg, 3); + buf[0] = RSL_IE_L3_INFO; + buf[1] = (sizeof(*gh) + sizeof(*mr)) >> 8; + buf[2] = (sizeof(*gh) + sizeof(*mr)) & 0xff; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + mr = (struct gsm48_meas_res *) msgb_put(msg, sizeof(*mr)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_MEAS_REP; + + /* measurement results */ + mr->rxlev_full = meas_dl_rxlev; + mr->rxlev_sub = meas_dl_rxlev; + mr->rxqual_full = meas_dl_rxqual; + mr->rxqual_sub = meas_dl_rxqual; + mr->dtx_used = meas_dtx_ms; + mr->ba_used = meas_rep_ba; + mr->meas_valid = !meas_valid; /* 0 = valid */ + if (meas_rep_valid) { + mr->no_nc_n_hi = meas_num_nc >> 2; + mr->no_nc_n_lo = meas_num_nc & 3; + } else { + /* no results for serving cells */ + mr->no_nc_n_hi = 1; + mr->no_nc_n_lo = 3; + } + mr->rxlev_nc1 = meas_rxlev_nc[0]; + mr->rxlev_nc2_hi = meas_rxlev_nc[1] >> 1; + mr->rxlev_nc2_lo = meas_rxlev_nc[1] & 1; + mr->rxlev_nc3_hi = meas_rxlev_nc[2] >> 2; + mr->rxlev_nc3_lo = meas_rxlev_nc[2] & 3; + mr->rxlev_nc4_hi = meas_rxlev_nc[3] >> 3; + mr->rxlev_nc4_lo = meas_rxlev_nc[3] & 7; + mr->rxlev_nc5_hi = meas_rxlev_nc[4] >> 4; + mr->rxlev_nc5_lo = meas_rxlev_nc[4] & 15; + mr->rxlev_nc6_hi = meas_rxlev_nc[5] >> 5; + mr->rxlev_nc6_lo = meas_rxlev_nc[5] & 31; + mr->bsic_nc1_hi = meas_bsic_nc[0] >> 3; + mr->bsic_nc1_lo = meas_bsic_nc[0] & 7; + mr->bsic_nc2_hi = meas_bsic_nc[1] >> 4; + mr->bsic_nc2_lo = meas_bsic_nc[1] & 15; + mr->bsic_nc3_hi = meas_bsic_nc[2] >> 5; + mr->bsic_nc3_lo = meas_bsic_nc[2] & 31; + mr->bsic_nc4 = meas_bsic_nc[3]; + mr->bsic_nc5 = meas_bsic_nc[4]; + mr->bsic_nc6 = meas_bsic_nc[5]; + mr->bcch_f_nc1 = meas_bcch_f_nc[0]; + mr->bcch_f_nc2 = meas_bcch_f_nc[1]; + mr->bcch_f_nc3 = meas_bcch_f_nc[2]; + mr->bcch_f_nc4 = meas_bcch_f_nc[3]; + mr->bcch_f_nc5_hi = meas_bcch_f_nc[4] >> 1; + mr->bcch_f_nc5_lo = meas_bcch_f_nc[4] & 1; + mr->bcch_f_nc6_hi = meas_bcch_f_nc[5] >> 2; + mr->bcch_f_nc6_lo = meas_bcch_f_nc[5] & 3; + + msg->dst = lchan->ts->trx->bts->c0->rsl_link; + msg->l2h = (unsigned char *)dh; + msg->l3h = (unsigned char *)gh; + + abis_rsl_rcvmsg(msg); +} + +static struct gsm_bts *create_bts(int arfcn) +{ + struct gsm_bts *bts; + struct e1inp_sign_link *rsl_link; + int i; + + bts = gsm_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_OSMOBTS, 0x3f); + if (!bts) { + printf("No resource for bts1\n"); + return NULL; + } + + bts->location_area_code = 23; + bts->c0->arfcn = arfcn; + + bts->codec.efr = 1; + bts->codec.hr = 1; + bts->codec.amr = 1; + + rsl_link = talloc_zero(ctx, struct e1inp_sign_link); + rsl_link->trx = bts->c0; + bts->c0->rsl_link = rsl_link; + + bts->c0->mo.nm_state.operational = NM_OPSTATE_ENABLED; + bts->c0->mo.nm_state.availability = NM_AVSTATE_OK; + bts->c0->bb_transc.mo.nm_state.operational = NM_OPSTATE_ENABLED; + bts->c0->bb_transc.mo.nm_state.availability = NM_AVSTATE_OK; + + /* 4 full rate and 4 half rate channels */ + for (i = 1; i <= 6; i++) { + bts->c0->ts[i].pchan = + (i < 5) ? GSM_PCHAN_TCH_F : GSM_PCHAN_TCH_H; + bts->c0->ts[i].mo.nm_state.operational = NM_OPSTATE_ENABLED; + bts->c0->ts[i].mo.nm_state.availability = NM_AVSTATE_OK; + bts->c0->ts[i].lchan[0].type = GSM_LCHAN_NONE; + bts->c0->ts[i].lchan[0].state = LCHAN_S_NONE; + bts->c0->ts[i].lchan[1].type = GSM_LCHAN_NONE; + bts->c0->ts[i].lchan[1].state = LCHAN_S_NONE; + } + return bts; +} + +void create_conn(struct gsm_lchan *lchan) +{ + struct gsm_subscriber_connection *conn; + conn = bsc_subscr_con_allocate(lchan->ts->trx->bts->network); + + /* CAUTION HACK: When __real_mgcp_conn_modify() is called by the GSCON + * FSM, then we need to know the reference to caller FSM (GSCON FSM). + * Unfortunately the function __real_mgcp_conn_modify() is called with + * fi_bts, which is unpopulated in this setup. The real function would + * perform the communication with the MGW and then dispatch a signal + * back to the parent FSM. Since we do not have all that in this setup + * we populate the fi_bts pointer with a reference to the GSCON FSM in + * order to have it available later in __real_mgcp_conn_modify(). */ + conn->user_plane.fi_bts = conn->fi; + + lchan->conn = conn; + conn->lchan = lchan; + /* kick the FSM from INIT through to the ACTIVE state */ + osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_REQ, NULL); + osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_CFM, NULL); +} + +/* create lchan */ +struct gsm_lchan *create_lchan(struct gsm_bts *bts, int full_rate, char *codec) +{ + struct gsm_lchan *lchan; + + lchan = lchan_alloc(bts, + (full_rate) ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H, 0); + if (!lchan) { + printf("No resource for lchan\n"); + exit(EXIT_FAILURE); + } + lchan->state = LCHAN_S_ACTIVE; + create_conn(lchan); + if (!strcasecmp(codec, "FR") && full_rate) + lchan->tch_mode = GSM48_CMODE_SPEECH_V1; + else if (!strcasecmp(codec, "HR") && !full_rate) + lchan->tch_mode = GSM48_CMODE_SPEECH_V1; + else if (!strcasecmp(codec, "EFR") && full_rate) + lchan->tch_mode = GSM48_CMODE_SPEECH_EFR; + else if (!strcasecmp(codec, "AMR")) + lchan->tch_mode = GSM48_CMODE_SPEECH_AMR; + else { + printf("Given codec unknown\n"); + exit(EXIT_FAILURE); + } + + lchan->conn->codec_list = (struct gsm0808_speech_codec_list){ + .codec = { + { .fi=true, .type=GSM0808_SCT_FR1, }, + { .fi=true, .type=GSM0808_SCT_FR2, }, + { .fi=true, .type=GSM0808_SCT_FR3, }, + { .fi=true, .type=GSM0808_SCT_HR1, }, + { .fi=true, .type=GSM0808_SCT_HR3, }, + }, + .len = 5, + }; + lchan->conn->codec_list_present = true; + + return lchan; +} + +/* parse channel request */ + +static int got_chan_req = 0; +static struct gsm_lchan *chan_req_lchan = NULL; + +static int parse_chan_act(struct gsm_lchan *lchan, uint8_t *data) +{ + chan_req_lchan = lchan; + return 0; +} + +static int parse_chan_rel(struct gsm_lchan *lchan, uint8_t *data) +{ + chan_req_lchan = lchan; + return 0; +} + +/* parse handover request */ + +static int got_ho_req = 0; +static struct gsm_lchan *ho_req_lchan = NULL; + +static int parse_ho_command(struct gsm_lchan *lchan, uint8_t *data, int len) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) data; + struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *) gh->data; + int arfcn; + struct gsm_bts *neigh; + + switch (gh->msg_type) { + case GSM48_MT_RR_HANDO_CMD: + arfcn = (ho->cell_desc.arfcn_hi << 8) | ho->cell_desc.arfcn_lo; + + /* look up trx. since every dummy bts uses different arfcn and + * only one trx, it is simple */ + llist_for_each_entry(neigh, &bsc_gsmnet->bts_list, list) { + if (neigh->c0->arfcn != arfcn) + continue; + ho_req_lchan = lchan; + return 0; + } + break; + case GSM48_MT_RR_ASS_CMD: + ho_req_lchan = lchan; + return 0; + break; + default: + fprintf(stderr, "Error, expecting HO or AS command\n"); + return -EINVAL; + } + + return -1; +} + +/* send channel activation ack */ +static void send_chan_act_ack(struct gsm_lchan *lchan, int act) +{ + struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL"); + struct abis_rsl_dchan_hdr *dh; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; + dh->c.msg_type = (act) ? RSL_MT_CHAN_ACTIV_ACK : RSL_MT_RF_CHAN_REL_ACK; + dh->ie_chan = RSL_IE_CHAN_NR; + dh->chan_nr = gsm_lchan2chan_nr(lchan); + + msg->dst = lchan->ts->trx->bts->c0->rsl_link; + msg->l2h = (unsigned char *)dh; + + abis_rsl_rcvmsg(msg); +} + +/* send handover complete */ +static void send_ho_complete(struct gsm_lchan *lchan, bool success) +{ + struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL"); + struct abis_rsl_rll_hdr *rh; + uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t *buf; + struct gsm48_hdr *gh; + struct gsm48_ho_cpl *hc; + + rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh)); + rh->c.msg_discr = ABIS_RSL_MDISC_RLL; + rh->c.msg_type = RSL_MT_DATA_IND; + rh->ie_chan = RSL_IE_CHAN_NR; + rh->chan_nr = chan_nr; + rh->ie_link_id = RSL_IE_LINK_IDENT; + rh->link_id = 0x00; + + buf = msgb_put(msg, 3); + buf[0] = RSL_IE_L3_INFO; + buf[1] = (sizeof(*gh) + sizeof(*hc)) >> 8; + buf[2] = (sizeof(*gh) + sizeof(*hc)) & 0xff; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + hc = (struct gsm48_ho_cpl *) msgb_put(msg, sizeof(*hc)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = + success ? GSM48_MT_RR_HANDO_COMPL : GSM48_MT_RR_HANDO_FAIL; + + msg->dst = lchan->ts->trx->bts->c0->rsl_link; + msg->l2h = (unsigned char *)rh; + msg->l3h = (unsigned char *)gh; + + abis_rsl_rcvmsg(msg); +} + +/* override, requires '-Wl,--wrap=abis_rsl_sendmsg'. + * Catch RSL messages sent towards the BTS. */ +int __real_abis_rsl_sendmsg(struct msgb *msg); +int __wrap_abis_rsl_sendmsg(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = (struct abis_rsl_dchan_hdr *) msg->data; + struct e1inp_sign_link *sign_link = msg->dst; + int rc; + struct gsm_lchan *lchan = rsl_lchan_lookup(sign_link->trx, dh->chan_nr, &rc); + + if (rc) { + printf("rsl_lchan_lookup() failed\n"); + exit(1); + } + + switch (dh->c.msg_type) { + case RSL_MT_CHAN_ACTIV: + rc = parse_chan_act(lchan, dh->data); + if (rc == 0) + got_chan_req = 1; + break; + case RSL_MT_RF_CHAN_REL: + rc = parse_chan_rel(lchan, dh->data); + if (rc == 0) + send_chan_act_ack(chan_req_lchan, 0); + break; + case RSL_MT_DATA_REQ: + rc = parse_ho_command(lchan, msg->l3h, msgb_l3len(msg)); + if (rc == 0) + got_ho_req = 1; + break; + case RSL_MT_IPAC_CRCX: + break; + default: + printf("unknown rsl message=0x%x\n", dh->c.msg_type); + } + return 0; +} + +/* test cases */ + +static char *test_case_0[] = { + "2", + + "Stay in better cell\n\n" + "There are many neighbor cells, but only the current cell is the best\n" + "cell, so no handover is performed\n", + + "create-bts", "7", + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "0", "30","0", + "6","0","20","1","21","2","18","3","20","4","23","5","19", + "expect-no-chan", + NULL +}; + +static char *test_case_1[] = { + "2", + + "Handover to best better cell\n\n" + "The best neighbor cell is selected\n", + + "create-bts", "7", + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "0", "10","0", + "6","0","20","1","21","2","18","3","20","4","23","5","19", + "expect-chan", "5", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_2[] = { + "2", + + "Handover and Assignment must be enabled\n\n" + "This test will start with disabled assignment and handover. A\n" + "better neighbor cell (assignment enabled) will not be selected and \n" + "also no assignment from TCH/H to TCH/F to improve quality. There\n" + "will be no handover nor assignment. After enabling assignment on the\n" + "current cell, the MS will assign to TCH/F. After enabling handover\n" + "in the current cell, but disabling in the neighbor cell, handover\n" + "will not be performed, until it is enabled in the neighbor cell too.\n", + + "create-bts", "2", + "afs-rxlev-improve", "0", "5", + "create-ms", "0", "TCH/H", "AMR", + "as-enable", "0", "0", + "ho-enable", "0", "0", + "meas-rep", "0", "0","0", "1","0","30", + "expect-no-chan", + "as-enable", "0", "1", + "meas-rep", "0", "0","0", "1","0","30", + "expect-chan", "0", "1", + "ack-chan", + "expect-ho", "0", "5", + "ho-complete", + "ho-enable", "0", "1", + "ho-enable", "1", "0", + "meas-rep", "0", "0","0", "1","0","30", + "expect-no-chan", + "ho-enable", "1", "1", + "meas-rep", "0", "0","0", "1","0","30", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_3[] = { + "2", + + "Penalty timer must not run\n\n" + "The MS will try to handover to a better cell, but this will fail.\n" + "Even though the cell is still better, handover will not be performed\n" + "due to penalty timer after handover failure\n", + + "create-bts", "2", + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "0", "20","0", "1","0","30", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-failed", + "meas-rep", "0", "20","0", "1","0","30", + "expect-no-chan", + NULL +}; + +static char *test_case_4[] = { + "2", + + "TCH/H keeping with HR codec\n\n" + "The MS is using half rate V1 codec, but the better cell is congested\n" + "at TCH/H slots. As the congestion is removed, the handover takes\n" + "place.\n", + + "create-bts", "2", + "set-min-free", "1", "TCH/H", "4", + "create-ms", "0", "TCH/H", "HR", + "meas-rep", "0", "20","0", "1","0","30", + "expect-no-chan", + "set-min-free", "1", "TCH/H", "3", + "meas-rep", "0", "20","0", "1","0","30", + "expect-chan", "1", "5", + "ack-chan", + "expect-ho", "0", "5", + "ho-complete", + NULL +}; + +static char *test_case_5[] = { + "2", + + "TCH/F keeping with FR codec\n\n" + "The MS is using full rate V1 codec, but the better cell is congested\n" + "at TCH/F slots. As the congestion is removed, the handover takes\n" + "place.\n", + + "create-bts", "2", + "set-min-free", "1", "TCH/F", "4", + "create-ms", "0", "TCH/F", "FR", + "meas-rep", "0", "20","0", "1","0","30", + "expect-no-chan", + "set-min-free", "1", "TCH/F", "3", + "meas-rep", "0", "20","0", "1","0","30", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_6[] = { + "2", + + "TCH/F keeping with EFR codec\n\n" + "The MS is using full rate V2 codec, but the better cell is congested\n" + "at TCH/F slots. As the congestion is removed, the handover takes\n" + "place.\n", + + "create-bts", "2", + "set-min-free", "1", "TCH/F", "4", + "create-ms", "0", "TCH/F", "EFR", + "meas-rep", "0", "20","0", "1","0","30", + "expect-no-chan", + "set-min-free", "1", "TCH/F", "3", + "meas-rep", "0", "20","0", "1","0","30", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_7[] = { + "2", + + "TCH/F to TCH/H changing with AMR codec\n\n" + "The MS is using AMR V3 codec, the better cell is congested at TCH/F\n" + "slots. The handover is performed to non-congested TCH/H slots.\n", + + "create-bts", "2", + "set-min-free", "1", "TCH/F", "4", + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "0", "20","0", "1","0","30", + "expect-chan", "1", "5", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_8[] = { + "2", + + "No handover to a cell with no slots available\n\n" + "If no slot is available, no handover is performed\n", + + "create-bts", "2", + "create-ms", "0", "TCH/F", "AMR", + "create-ms", "1", "TCH/F", "AMR", + "create-ms", "1", "TCH/F", "AMR", + "create-ms", "1", "TCH/F", "AMR", + "create-ms", "1", "TCH/F", "AMR", + "create-ms", "1", "TCH/H", "AMR", + "create-ms", "1", "TCH/H", "AMR", + "create-ms", "1", "TCH/H", "AMR", + "create-ms", "1", "TCH/H", "AMR", + "meas-rep", "0", "0","0", "1","0","30", + "expect-no-chan", + NULL +}; + +static char *test_case_9[] = { + "2", + + "No more parallel handovers, if max_unsync_ho is defined\n\n" + "There are tree mobiles that want to handover, but only two can do\n" + "it at a time, because the maximum number is limited to two.\n", + + "create-bts", "2", + "set-max-ho", "1", "2", + "create-ms", "0", "TCH/F", "AMR", + "create-ms", "0", "TCH/F", "AMR", + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "0", "0","0", "1","0","30", + "expect-chan", "1", "1", + "meas-rep", "1", "0","0", "1","0","30", + "expect-chan", "1", "2", + "meas-rep", "2", "0","0", "1","0","30", + "expect-no-chan", + NULL +}; + +static char *test_case_10[] = { + "2", + + "Hysteresis\n\n" + "If neighbor cell is better, handover is only performed if the\n" + "ammount of improvement is greater or equal hyteresis\n", + + "create-bts", "2", + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "0", "27","0", "1","0","30", + "expect-no-chan", + "meas-rep", "0", "26","0", "1","0","30", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_11[] = { + "2", + + "No Hysteresis and minimum RX level\n\n" + "If current cell's RX level is below mimium level, handover must be\n" + "performed, no matter of the hysteresis. First do not perform\n" + "handover to better neighbor cell, because the hysteresis is not\n" + "met. Second do not perform handover because better neighbor cell is\n" + "below minimum RX level. Third perform handover because current cell\n" + "is below minimum RX level, even if the better neighbor cell (minimum\n" + "RX level reached) does not meet the hysteresis.\n", + + "create-bts", "2", + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "0", "10","0", "1","0","11", + "expect-no-chan", + "meas-rep", "0", "8","0", "1","0","9", + "expect-no-chan", + "meas-rep", "0", "9","0", "1","0","10", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_12[] = { + "2", + + "No handover to congested cell\n\n" + "The better neighbor cell is congested, so no handover is performed.\n" + "After the congestion is over, handover will be performed.\n", + + "create-bts", "2", + "create-ms", "0", "TCH/F", "AMR", + "set-min-free", "1", "TCH/F", "4", + "set-min-free", "1", "TCH/H", "4", + "meas-rep", "0", "20","0", "1","0","30", + "expect-no-chan", + "set-min-free", "1", "TCH/F", "3", + "set-min-free", "1", "TCH/H", "3", + "meas-rep", "0", "20","0", "1","0","30", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_13[] = { + "2", + + "Handover to balance congestion\n\n" + "The current and the better cell are congested, so no handover is\n" + "performed. This is because handover would congest the neighbor cell\n" + "more. After congestion raises in the current cell, the handover is\n" + "performed to balance congestion\n", + + "create-bts", "2", + "create-ms", "0", "TCH/F", "AMR", + "set-min-free", "0", "TCH/F", "4", + "set-min-free", "0", "TCH/H", "4", + "set-min-free", "1", "TCH/F", "4", + "set-min-free", "1", "TCH/H", "4", + "meas-rep", "0", "20","0", "1","0","30", + "expect-no-chan", + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "0", "20","0", "1","0","30", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_14[] = { + "2", + + "Handover to congested cell, if RX level is below minimum\n\n" + "The better neighbor cell is congested, so no handover is performed.\n" + "If the RX level of the current cell drops below minimum acceptable\n" + "level, the handover is performed.\n", + + "create-bts", "2", + "create-ms", "0", "TCH/F", "AMR", + "set-min-free", "1", "TCH/F", "4", + "set-min-free", "1", "TCH/H", "4", + "meas-rep", "0", "10","0", "1","0","30", + "expect-no-chan", + "meas-rep", "0", "9","0", "1","0","30", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_15[] = { + "2", + + "Handover to cell with worse RXLEV, if RXQUAL is below minimum\n\n" + "The neighbor cell has worse RXLEV, so no handover is performed.\n" + "If the RXQUAL of the current cell drops below minimum acceptable\n" + "level, the handover is performed. It is also required that 10\n" + "reports are received, before RXQUAL is checked.\n", + /* (See also test 28, which tests for RXQUAL triggering HO to congested cell.) */ + /* TODO: bad RXQUAL may want to prefer assignment within the same cell to avoid interference. + * See Performence Enhancements in a Frequency Hopping GSM Network (Nielsen Wigard 2002), Chapter + * 2.1.1, "Interference" in the list of triggers on p.157. */ + + "create-bts", "2", + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "0", "40","6", "1","0","30", + "expect-no-chan", + "meas-rep", "0", "40","6", "1","0","30", + "expect-no-chan", + "meas-rep", "0", "40","6", "1","0","30", + "expect-no-chan", + "meas-rep", "0", "40","6", "1","0","30", + "expect-no-chan", + "meas-rep", "0", "40","6", "1","0","30", + "expect-no-chan", + "meas-rep", "0", "40","6", "1","0","30", + "expect-no-chan", + "meas-rep", "0", "40","6", "1","0","30", + "expect-no-chan", + "meas-rep", "0", "40","6", "1","0","30", + "expect-no-chan", + "meas-rep", "0", "40","6", "1","0","30", + "expect-no-chan", + "meas-rep", "0", "40","6", "1","0","30", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_16[] = { + "2", + + "Handover due to maximum TA exceeded\n\n" + "The MS in the current (best) cell has reached maximum allowed timing\n" + "advance. No handover is performed until the timing advance exceeds\n" + "it. The originating cell is still the best, but no handover is\n" + "performed back to that cell, because the penalty timer (due to\n" + "maximum allowed timing advance) is running.\n", + + "create-bts", "2", + "create-ms", "0", "TCH/F", "AMR", + "set-max-ta", "0", "5", /* of cell */ + "set-ta", "0", "5", /* of ms */ + "meas-rep", "0", "30","0", "1","0","20", + "expect-no-chan", + "set-ta", "0", "6", /* of ms */ + "meas-rep", "0", "30","0", "1","0","20", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + "meas-rep", "0", "20","0", "1","0","30", + "expect-no-chan", + NULL +}; + +static char *test_case_17[] = { + "2", + + "Congestion check: No congestion\n\n" + "Three cells have different number of used slots, but there is no\n" + "congestion in any of these cells. No handover is performed.\n", + + "create-bts", "3", + "set-min-free", "0", "TCH/F", "2", + "set-min-free", "0", "TCH/H", "2", + "set-min-free", "1", "TCH/F", "2", + "set-min-free", "1", "TCH/H", "2", + "set-min-free", "2", "TCH/F", "2", + "set-min-free", "2", "TCH/H", "2", + "create-ms", "0", "TCH/F", "AMR", + "create-ms", "0", "TCH/F", "AMR", + "create-ms", "0", "TCH/H", "AMR", + "create-ms", "0", "TCH/H", "AMR", + "create-ms", "1", "TCH/F", "AMR", + "create-ms", "1", "TCH/H", "AMR", + "meas-rep", "0", "30","0", "2","0","20","1","20", + "expect-no-chan", + "meas-rep", "1", "30","0", "2","0","20","1","20", + "expect-no-chan", + "meas-rep", "2", "30","0", "2","0","20","1","20", + "expect-no-chan", + "meas-rep", "3", "30","0", "2","0","20","1","20", + "expect-no-chan", + "meas-rep", "4", "30","0", "2","0","20","1","20", + "expect-no-chan", + "meas-rep", "5", "30","0", "2","0","20","1","20", + "expect-no-chan", + "congestion-check", + "expect-no-chan", + NULL +}; + +static char *test_case_18[] = { + "2", + + "Congestion check: One out of three cells is congested\n\n" + "Three cells have different number of used slots, but there is\n" + "congestion at TCH/F in the first cell. Handover is performed with\n" + "the best candidate.\n", + + "create-bts", "3", + "set-min-free", "0", "TCH/F", "2", + "set-min-free", "0", "TCH/H", "2", + "set-min-free", "1", "TCH/F", "2", + "set-min-free", "1", "TCH/H", "2", + "set-min-free", "2", "TCH/F", "2", + "set-min-free", "2", "TCH/H", "2", + "create-ms", "0", "TCH/F", "AMR", + "create-ms", "0", "TCH/F", "AMR", + "create-ms", "0", "TCH/F", "AMR", + "create-ms", "0", "TCH/H", "AMR", + "create-ms", "0", "TCH/H", "AMR", + "create-ms", "1", "TCH/F", "AMR", + "create-ms", "1", "TCH/H", "AMR", + "meas-rep", "0", "30","0", "2","0","20","1","20", + "expect-no-chan", + "meas-rep", "1", "30","0", "2","0","20","1","20", + "expect-no-chan", + "meas-rep", "2", "30","0", "2","0","21","1","20", + "expect-no-chan", + "meas-rep", "3", "30","0", "2","0","20","1","20", + "expect-no-chan", + "meas-rep", "4", "30","0", "2","0","20","1","20", + "expect-no-chan", + "meas-rep", "5", "30","0", "2","0","20","1","20", + "expect-no-chan", + "meas-rep", "6", "30","0", "2","0","20","1","20", + "expect-no-chan", + "congestion-check", + "expect-chan", "1", "2", + "ack-chan", + "expect-ho", "0", "3", /* best candidate is MS 2 at BTS 1, TS 3 */ + "ho-complete", + NULL +}; + +static char *test_case_19[] = { + "2", + + "Congestion check: Balancing over congested cells\n\n" + "Two cells are congested, but the second cell is more congested.\n" + "Handover is performed to solve the congestion.\n", + + "create-bts", "2", + "set-min-free", "0", "TCH/F", "4", + "set-min-free", "1", "TCH/F", "4", + "create-ms", "0", "TCH/F", "FR", + "create-ms", "0", "TCH/F", "FR", + "create-ms", "0", "TCH/F", "FR", + "create-ms", "1", "TCH/F", "FR", + "meas-rep", "0", "30","0", "1","0","20", + "expect-no-chan", + "meas-rep", "1", "30","0", "1","0","21", + "expect-no-chan", + "meas-rep", "2", "30","0", "1","0","20", + "expect-no-chan", + "meas-rep", "3", "30","0", "1","0","20", + "expect-no-chan", + "congestion-check", + "expect-chan", "1", "2", + "ack-chan", + "expect-ho", "0", "2", /* best candidate is MS 1 at BTS 0, TS 2 */ + "ho-complete", + NULL +}; + +static char *test_case_20[] = { + "2", + + "Congestion check: Solving congestion by handover TCH/F -> TCH/H\n\n" + "Two BTS, one MS in the first congested BTS must handover to\n" + "non-congested TCH/H of second BTS, in order to solve congestion\n", + "create-bts", "2", + "set-min-free", "0", "TCH/F", "4", + "set-min-free", "0", "TCH/H", "4", + "set-min-free", "1", "TCH/F", "4", + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "0", "30","0", "1","0","30", + "expect-no-chan", + "congestion-check", + "expect-chan", "1", "5", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_21[] = { + "2", + + "Congestion check: Balancing congestion by handover TCH/F -> TCH/H\n\n" + "Two BTS, one MS in the first congested BTS must handover to\n" + "less-congested TCH/H of second BTS, in order to balance congestion\n", + "create-bts", "2", + "set-min-free", "0", "TCH/F", "4", + "set-min-free", "0", "TCH/H", "4", + "set-min-free", "1", "TCH/F", "4", + "set-min-free", "1", "TCH/H", "4", + "create-ms", "0", "TCH/F", "AMR", + "create-ms", "0", "TCH/F", "AMR", + "create-ms", "0", "TCH/H", "AMR", + "meas-rep", "0", "30","0", "1","0","30", + "expect-no-chan", + "congestion-check", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_22[] = { + "2", + + "Congestion check: Upgrading worst candidate from TCH/H -> TCH/F\n\n" + "There is only one BTS. The TCH/H slots are congested. Since\n" + "assignment is performed to less-congested TCH/F, the candidate with\n" + "the worst RX level is chosen.\n", + + "create-bts", "1", + "set-min-free", "0", "TCH/F", "4", + "set-min-free", "0", "TCH/H", "4", + "create-ms", "0", "TCH/H", "AMR", + "create-ms", "0", "TCH/H", "AMR", + "create-ms", "0", "TCH/H", "AMR", + "meas-rep", "0", "30","0", "0", + "meas-rep", "1", "34","0", "0", + "meas-rep", "2", "20","0", "0", + "expect-no-chan", + "congestion-check", + "expect-chan", "0", "1", + "ack-chan", + "expect-ho", "0", "6", + "ho-complete", + NULL +}; + +static char *test_case_23[] = { + "2", + + "Story: 'A neighbor is your friend'\n", + + "create-bts", "3", + + "print", + "Andreas is driving along the coast, on a sunny june afternoon.\n" + "Suddenly he is getting a call from his friend and neighbor Axel.\n" + "\n" + "What happens: Two MS are created, #0 for Axel, #1 for Andreas.", + /* Axel */ + "create-ms", "2", "TCH/F", "AMR", + /* andreas */ + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "1", "40","0", "1","0","30", + "expect-no-chan", + + "print", + "Axel asks Andreas if he would like to join them for a barbecue.\n" + "Axel's house is right in the neighborhood and the weather is fine.\n" + "Andreas agrees, so he drives to a close store to buy some barbecue\n" + "skewers.\n" + "\n" + "What happens: While driving, a different cell (mounted atop the\n" + "store) becomes better.", + /* drive to bts 1 */ + "meas-rep", "1", "20","0", "1","0","35", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + + "print", + "While Andreas is walking into the store, Axel asks, if he could also\n" + "bring some beer. Andreas has problems understanding him: \"I have a\n" + "bad reception here. The cell tower is right atop the store, but poor\n" + "coverage inside. Can you repeat please?\"\n" + "\n" + "What happens: Inside the store the close cell is so bad, that\n" + "handover back to the previous cell is required.", + /* bts 1 becomes bad, so bts 0 helps out */ + "meas-rep", "1", "5","0", "1","0","20", + "expect-chan", "0", "1", + "ack-chan", + "expect-ho", "1", "1", + "ho-complete", + + "print", + "After Andreas bought skewers and beer, he leaves the store.\n" + "\n" + "What happens: Outside the store the close cell is better again, so\n" + "handover back to the that cell is performed.", + /* bts 1 becomes better again */ + "meas-rep", "1", "20","0", "1","0","35", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + + "print", + /* bts 2 becomes better */ + "Andreas drives down to the lake where Axel's house is.\n" + "\n" + "What happens: There is a small cell at Axel's house, which becomes\n" + "better, because the current cell has no good comverage at the lake.", + "meas-rep", "1", "14","0", "2","0","2","1","63", + "expect-chan", "2", "2", + "ack-chan", + "expect-ho", "1", "1", + "ho-complete", + + "print", + "Andreas wonders why he still has good radio coverage: \"Last time it\n" + "was so bad\". Axel says: \"I installed a pico cell in my house,\n" + "now we can use our mobile phones down here at the lake.\"", + + NULL +}; + +static char *test_case_24[] = { + "2", + "No (or not enough) measurements for handover\n\n" + "Do not solve congestion in cell, because there is no measurement.\n" + "As soon as enough measurments available (1 in our case), perform\n" + "handover. Afterwards the old cell becomes congested and the new\n" + "cell is not. Do not perform handover until new measurements are\n" + "received.\n", + + /* two cells, first in congested, but no handover */ + "create-bts", "2", + "set-min-free", "0", "TCH/F", "4", + "set-min-free", "0", "TCH/H", "4", + "create-ms", "0", "TCH/F", "AMR", + "congestion-check", + "expect-no-chan", + + /* send measurement and trigger congestion check */ + "meas-rep", "0", "20","0", "1","0","20", + "expect-no-chan", + "congestion-check", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + + /* congest the first cell and remove congestion from second cell */ + "set-min-free", "0", "TCH/F", "0", + "set-min-free", "0", "TCH/H", "0", + "set-min-free", "1", "TCH/F", "4", + "set-min-free", "1", "TCH/H", "4", + + /* no handover until measurements applied */ + "congestion-check", + "expect-no-chan", + "meas-rep", "0", "20","0", "1","0","20", + "expect-no-chan", + "congestion-check", + "expect-chan", "0", "1", + "ack-chan", + "expect-ho", "1", "1", + "ho-complete", + NULL +}; + +static char *test_case_25[] = { + "1", + + "Stay in better cell\n\n" + "There are many neighbor cells, but only the current cell is the best\n" + "cell, so no handover is performed\n", + + "create-bts", "7", + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "0", "30","0", + "6","0","20","1","21","2","18","3","20","4","23","5","19", + "expect-no-chan", + NULL +}; + +static char *test_case_26[] = { + "1", + + "Handover to best better cell\n\n" + "The best neighbor cell is selected\n", + + "create-bts", "7", + "create-ms", "0", "TCH/F", "AMR", + "meas-rep", "0", "10","0", + "6","0","20","1","21","2","18","3","20","4","23","5","19", + "expect-chan", "5", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char *test_case_27[] = { + "2", + + "Congestion check: Upgrading worst candidate from TCH/H -> TCH/F\n\n" + "There is only one BTS. The TCH/H slots are congested. Since\n" + "assignment is performed to less-congested TCH/F, the candidate with\n" + "the worst RX level is chosen. (So far like test 22.)\n" + "After that, trigger more congestion checks to ensure stability.\n", + + "create-bts", "1", + "set-min-free", "0", "TCH/F", "2", + "set-min-free", "0", "TCH/H", "4", + "create-ms", "0", "TCH/H", "AMR", + "create-ms", "0", "TCH/H", "AMR", + "create-ms", "0", "TCH/H", "AMR", + "meas-rep", "0", "30","0", "0", + "meas-rep", "1", "34","0", "0", + "meas-rep", "2", "20","0", "0", + "expect-no-chan", + "congestion-check", + "expect-chan", "0", "1", + "ack-chan", + "expect-ho", "0", "6", + "ho-complete", + "congestion-check", + "expect-chan", "0", "2", + "ack-chan", + "expect-ho", "0", "5", + "ho-complete", + "congestion-check", + "expect-no-chan", + "congestion-check", + "expect-no-chan", + NULL +}; + +static char *test_case_28[] = { + "2", + + "Handover to congested cell, if RX quality is below minimum\n\n" + "The better neighbor cell is congested, so no handover is performed.\n" + "If the RX quality of the current cell drops below minimum acceptable\n" + "level, the handover is performed. It is also required that 10\n" + "resports are received, before RX quality is checked.\n", + + "create-bts", "2", + "create-ms", "0", "TCH/F", "AMR", + "set-min-free", "1", "TCH/F", "4", + "set-min-free", "1", "TCH/H", "4", + "meas-rep", "0", "30","6", "1","0","40", + "expect-no-chan", + "meas-rep", "0", "30","6", "1","0","40", + "expect-no-chan", + "meas-rep", "0", "30","6", "1","0","40", + "expect-no-chan", + "meas-rep", "0", "30","6", "1","0","40", + "expect-no-chan", + "meas-rep", "0", "30","6", "1","0","40", + "expect-no-chan", + "meas-rep", "0", "30","6", "1","0","40", + "expect-no-chan", + "meas-rep", "0", "30","6", "1","0","40", + "expect-no-chan", + "meas-rep", "0", "30","6", "1","0","40", + "expect-no-chan", + "meas-rep", "0", "30","6", "1","0","40", + "expect-no-chan", + "meas-rep", "0", "30","6", "1","0","40", + "expect-chan", "1", "1", + "ack-chan", + "expect-ho", "0", "1", + "ho-complete", + NULL +}; + +static char **test_cases[] = { + test_case_0, + test_case_1, + test_case_2, + test_case_3, + test_case_4, + test_case_5, + test_case_6, + test_case_7, + test_case_8, + test_case_9, + test_case_10, + test_case_11, + test_case_12, + test_case_13, + test_case_14, + test_case_15, + test_case_16, + test_case_17, + test_case_18, + test_case_19, + test_case_20, + test_case_21, + test_case_22, + test_case_23, + test_case_24, + test_case_25, + test_case_26, + test_case_27, + test_case_28, +}; + +static const struct log_info_cat log_categories[] = { + [DHO] = { + .name = "DHO", + .description = "Hand-Over Process", + .color = "\033[1;38m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DHODEC] = { + .name = "DHODEC", + .description = "Hand-Over Decision", + .color = "\033[1;38m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DMEAS] = { + .name = "DMEAS", + .description = "Radio Measurement Processing", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DREF] = { + .name = "DREF", + .description = "Reference Counting", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DRSL] = { + .name = "DRSL", + .description = "A-bis Radio Signalling Link (RSL)", + .color = "\033[1;35m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DMSC] = { + .name = "DMSC", + .description = "Mobile Switching Center", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, +}; + +const struct log_info log_info = { + .cat = log_categories, + .num_cat = ARRAY_SIZE(log_categories), +}; + +int main(int argc, char **argv) +{ + char **test_case; + struct gsm_bts *bts[256]; + int bts_num = 0; + struct gsm_lchan *lchan[256]; + int lchan_num = 0; + int i; + int algorithm; + struct bsc_api bsc_api = {}; + int test_case_i; + int last_test_i; + + ctx = talloc_named_const(NULL, 0, "handover_test"); + msgb_talloc_ctx_init(ctx, 0); + + test_case_i = argc > 1? atoi(argv[1]) : -1; + last_test_i = ARRAY_SIZE(test_cases) - 1; + + if (test_case_i < 0 || test_case_i > last_test_i) { + for (i = 0; i <= last_test_i; i++) { + printf("Test #%d (algorithm %s):\n%s\n", i, + test_cases[i][0], test_cases[i][1]); + } + printf("\nPlease specify test case number 0..%d\n", last_test_i); + return EXIT_FAILURE; + } + + osmo_init_logging2(ctx, &log_info); + + log_set_print_category(osmo_stderr_target, 1); + log_set_print_category_hex(osmo_stderr_target, 0); + log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME); + + /* Create a dummy network */ + bsc_gsmnet = bsc_network_init(ctx); + if (!bsc_gsmnet) + exit(1); + + bsc_api_init(bsc_gsmnet, &bsc_api); + + ho_set_algorithm(bsc_gsmnet->ho, 2); + ho_set_ho_active(bsc_gsmnet->ho, true); + ho_set_hodec2_as_active(bsc_gsmnet->ho, true); + ho_set_hodec2_min_rxlev(bsc_gsmnet->ho, -100); + ho_set_hodec2_rxlev_avg_win(bsc_gsmnet->ho, 1); + ho_set_hodec2_rxlev_neigh_avg_win(bsc_gsmnet->ho, 1); + ho_set_hodec2_rxqual_avg_win(bsc_gsmnet->ho, 10); + ho_set_hodec2_pwr_hysteresis(bsc_gsmnet->ho, 3); + ho_set_hodec2_pwr_interval(bsc_gsmnet->ho, 1); + ho_set_hodec2_afs_bias_rxlev(bsc_gsmnet->ho, 0); + ho_set_hodec2_min_rxqual(bsc_gsmnet->ho, 5); + ho_set_hodec2_afs_bias_rxqual(bsc_gsmnet->ho, 0); + ho_set_hodec2_max_distance(bsc_gsmnet->ho, 9999); + ho_set_hodec2_ho_max(bsc_gsmnet->ho, 9999); + ho_set_hodec2_penalty_max_dist(bsc_gsmnet->ho, 300); + ho_set_hodec2_penalty_failed_ho(bsc_gsmnet->ho, 60); + ho_set_hodec2_penalty_failed_as(bsc_gsmnet->ho, 60); + + bts_model_sysmobts_init(); + + test_case = test_cases[test_case_i]; + + fprintf(stderr, "--------------------\n"); + fprintf(stderr, "Performing the following test %d (algorithm %s):\n%s", + test_case_i, test_case[0], test_case[1]); + algorithm = atoi(test_case[0]); + test_case += 2; + fprintf(stderr, "--------------------\n"); + + /* Disable the congestion check timer, we will trigger manually. */ + bsc_gsmnet->hodec2.congestion_check_interval_s = 0; + + handover_decision_1_init(); + hodec2_init(bsc_gsmnet); + + while (*test_case) { + if (!strcmp(*test_case, "create-bts")) { + static int arfcn = 870; + int n = atoi(test_case[1]); + fprintf(stderr, "- Creating %d BTS (one TRX each, " + "TS(1-4) are TCH/F, TS(5-6) are TCH/H)\n", n); + for (i = 0; i < n; i++) + bts[bts_num + i] = create_bts(arfcn++); + for (i = 0; i < n; i++) { + if (gsm_generate_si(bts[bts_num + i], SYSINFO_TYPE_2)) + fprintf(stderr, "Error generating SI2\n"); + } + bts_num += n; + test_case += 2; + } else + if (!strcmp(*test_case, "as-enable")) { + fprintf(stderr, "- Set assignment enable state at " + "BTS %s to %s\n", test_case[1], test_case[2]); + ho_set_hodec2_as_active(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); + test_case += 3; + } else + if (!strcmp(*test_case, "ho-enable")) { + fprintf(stderr, "- Set handover enable state at " + "BTS %s to %s\n", test_case[1], test_case[2]); + ho_set_ho_active(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); + test_case += 3; + } else + if (!strcmp(*test_case, "afs-rxlev-improve")) { + fprintf(stderr, "- Set afs RX level improvement at " + "BTS %s to %s\n", test_case[1], test_case[2]); + ho_set_hodec2_afs_bias_rxlev(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); + test_case += 3; + } else + if (!strcmp(*test_case, "afs-rxqual-improve")) { + fprintf(stderr, "- Set afs RX quality improvement at " + "BTS %s to %s\n", test_case[1], test_case[2]); + ho_set_hodec2_afs_bias_rxqual(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); + test_case += 3; + } else + if (!strcmp(*test_case, "set-min-free")) { + fprintf(stderr, "- Setting minimum required free %s " + "slots at BTS %s to %s\n", test_case[2], + test_case[1], test_case[3]); + if (!strcmp(test_case[2], "TCH/F")) + ho_set_hodec2_tchf_min_slots(bts[atoi(test_case[1])]->ho, atoi(test_case[3])); + else + ho_set_hodec2_tchh_min_slots(bts[atoi(test_case[1])]->ho, atoi(test_case[3])); + test_case += 4; + } else + if (!strcmp(*test_case, "set-max-ho")) { + fprintf(stderr, "- Setting maximum parallel handovers " + "at BTS %s to %s\n", test_case[1], + test_case[2]); + ho_set_hodec2_ho_max( bts[atoi(test_case[1])]->ho, atoi(test_case[2])); + test_case += 3; + } else + if (!strcmp(*test_case, "set-max-ta")) { + fprintf(stderr, "- Setting maximum timing advance " + "at BTS %s to %s\n", test_case[1], + test_case[2]); + ho_set_hodec2_max_distance(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); + test_case += 3; + } else + if (!strcmp(*test_case, "create-ms")) { + fprintf(stderr, "- Creating mobile #%d at BTS %s on " + "%s with %s codec\n", lchan_num, test_case[1], + test_case[2], test_case[3]); + lchan[lchan_num] = create_lchan(bts[atoi(test_case[1])], + !strcmp(test_case[2], "TCH/F"), test_case[3]); + if (!lchan[lchan_num]) { + printf("Failed to create lchan!\n"); + return EXIT_FAILURE; + } + fprintf(stderr, " * New MS is at BTS %d TS %d\n", + lchan[lchan_num]->ts->trx->bts->nr, + lchan[lchan_num]->ts->nr); + lchan_num++; + test_case += 4; + } else + if (!strcmp(*test_case, "set-ta")) { + fprintf(stderr, "- Setting maximum timing advance " + "at MS %s to %s\n", test_case[1], + test_case[2]); + meas_ta_ms = atoi(test_case[2]); + test_case += 3; + } else + if (!strcmp(*test_case, "meas-rep")) { + /* meas-rep <lchan-nr> <rxlev> <rxqual> <nr-of-neighbors> [<cell-idx> <rxlev> [...]] */ + int n = atoi(test_case[4]); + struct gsm_lchan *lc = lchan[atoi(test_case[1])]; + fprintf(stderr, "- Sending measurement report from " + "mobile #%s (rxlev=%s, rxqual=%s)\n", + test_case[1], test_case[2], test_case[3]); + meas_dl_rxlev = atoi(test_case[2]); + meas_dl_rxqual = atoi(test_case[3]); + meas_num_nc = n; + test_case += 5; + for (i = 0; i < n; i++) { + int nr = atoi(test_case[0]); + /* since our bts is not in the list of neighbor + * cells, we need to shift */ + if (nr >= lc->ts->trx->bts->nr) + nr++; + fprintf(stderr, " * Neighbor cell #%s, actual " + "BTS %d (rxlev=%s)\n", test_case[0], nr, + test_case[1]); + meas_bcch_f_nc[i] = atoi(test_case[0]); + /* bts number, not counting our own */ + meas_rxlev_nc[i] = atoi(test_case[1]); + meas_bsic_nc[i] = 0x3f; + test_case += 2; + } + got_chan_req = 0; + gen_meas_rep(lc); + } else + if (!strcmp(*test_case, "congestion-check")) { + fprintf(stderr, "- Triggering congestion check\n"); + got_chan_req = 0; + if (algorithm == 2) + hodec2_congestion_check(bsc_gsmnet); + test_case += 1; + } else + if (!strcmp(*test_case, "expect-chan")) { + fprintf(stderr, "- Expecting channel request at BTS %s " + "TS %s\n", test_case[1], test_case[2]); + if (!got_chan_req) { + printf("Test failed, because no channel was " + "requested\n"); + return EXIT_FAILURE; + } + fprintf(stderr, " * Got channel request at BTS %d " + "TS %d\n", chan_req_lchan->ts->trx->bts->nr, + chan_req_lchan->ts->nr); + if (chan_req_lchan->ts->trx->bts->nr + != atoi(test_case[1])) { + printf("Test failed, because channel was not " + "requested on expected BTS\n"); + return EXIT_FAILURE; + } + if (chan_req_lchan->ts->nr != atoi(test_case[2])) { + printf("Test failed, because channel was not " + "requested on expected TS\n"); + return EXIT_FAILURE; + } + test_case += 3; + } else + if (!strcmp(*test_case, "expect-no-chan")) { + fprintf(stderr, "- Expecting no channel request\n"); + if (got_chan_req) { + fprintf(stderr, " * Got channel request at " + "BTS %d TS %d\n", + chan_req_lchan->ts->trx->bts->nr, + chan_req_lchan->ts->nr); + printf("Test failed, because channel was " + "requested\n"); + return EXIT_FAILURE; + } + fprintf(stderr, " * Got no channel request\n"); + test_case += 1; + } else + if (!strcmp(*test_case, "expect-ho")) { + fprintf(stderr, "- Expecting handover/assignment " + "request at BTS %s TS %s\n", test_case[1], + test_case[2]); + if (!got_ho_req) { + printf("Test failed, because no handover was " + "requested\n"); + return EXIT_FAILURE; + } + fprintf(stderr, " * Got handover/assignment request at " + "BTS %d TS %d\n", + ho_req_lchan->ts->trx->bts->nr, + ho_req_lchan->ts->nr); + if (ho_req_lchan->ts->trx->bts->nr + != atoi(test_case[1])) { + printf("Test failed, because " + "handover/assignment was not commanded " + "at the expected BTS\n"); + return EXIT_FAILURE; + } + if (ho_req_lchan->ts->nr != atoi(test_case[2])) { + printf("Test failed, because " + "handover/assignment was not commanded " + "at the expected TS\n"); + return EXIT_FAILURE; + } + test_case += 3; + } else + if (!strcmp(*test_case, "ack-chan")) { + fprintf(stderr, "- Acknowledging channel request\n"); + if (!got_chan_req) { + printf("Cannot ack channel, because no " + "request\n"); + return EXIT_FAILURE; + } + test_case += 1; + got_ho_req = 0; + send_chan_act_ack(chan_req_lchan, 1); + } else + if (!strcmp(*test_case, "ho-complete")) { + fprintf(stderr, "- Acknowledging handover/assignment " + "request\n"); + if (!got_chan_req) { + printf("Cannot ack handover/assignment, " + "because no chan request\n"); + return EXIT_FAILURE; + } + if (!got_ho_req) { + printf("Cannot ack handover/assignment, " + "because no ho request\n"); + return EXIT_FAILURE; + } + test_case += 1; + got_chan_req = 0; + got_ho_req = 0; + /* switch lchan */ + for (i = 0; i < lchan_num; i++) { + if (lchan[i] == ho_req_lchan) { + fprintf(stderr, " * MS %d changes from " + "BTS=%d TS=%d to BTS=%d " + "TS=%d\n", i, + lchan[i]->ts->trx->bts->nr, + lchan[i]->ts->nr, + chan_req_lchan->ts->trx->bts->nr, + chan_req_lchan->ts->nr); + lchan[i] = chan_req_lchan; + } + } + send_ho_complete(chan_req_lchan, true); + } else + if (!strcmp(*test_case, "ho-failed")) { + fprintf(stderr, "- Making handover fail\n"); + if (!got_chan_req) { + printf("Cannot fail handover, because no chan " + "request\n"); + return EXIT_FAILURE; + } + test_case += 1; + got_chan_req = 0; + got_ho_req = 0; + send_ho_complete(ho_req_lchan, false); + } else + if (!strcmp(*test_case, "print")) { + fprintf(stderr, "\n%s\n\n", test_case[1]); + test_case += 2; + } else { + printf("Unknown test command '%s', please fix!\n", + *test_case); + return EXIT_FAILURE; + } + } + + for (i = 0; i < lchan_num; i++) { + struct gsm_subscriber_connection *conn = lchan[i]->conn; + lchan[i]->conn = NULL; + conn->lchan = NULL; + osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_REGULAR, NULL); + lchan_free(lchan[i]); + } + + fprintf(stderr, "--------------------\n"); + + printf("Test OK\n"); + + fprintf(stderr, "--------------------\n"); + + talloc_free(ctx); + return EXIT_SUCCESS; +} + +void rtp_socket_free() {} +void rtp_send_frame() {} +void rtp_socket_upstream() {} +void rtp_socket_create() {} +void rtp_socket_connect() {} +void rtp_socket_proxy() {} +void trau_mux_unmap() {} +void trau_mux_map_lchan() {} +void trau_recv_lchan() {} +void trau_send_frame() {} +int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; } +int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; } diff --git a/tests/handover/handover_test.ok b/tests/handover/handover_test.ok new file mode 100644 index 0000000..678f9a3 --- /dev/null +++ b/tests/handover/handover_test.ok @@ -0,0 +1 @@ +Test OK diff --git a/tests/handover_cfg.vty b/tests/handover_cfg.vty new file mode 100644 index 0000000..3ad44d8 --- /dev/null +++ b/tests/handover_cfg.vty @@ -0,0 +1,622 @@ +OsmoBSC> show network +... + Handover: Off +... +OsmoBSC> enable + +OsmoBSC# ### No handover config present +OsmoBSC# show running-config +... !handover + +OsmoBSC# ### Toggling handover on network level affects 'show network': +OsmoBSC# configure terminal +OsmoBSC(config)# network +OsmoBSC(config-net)# do show network +... + Handover: Off +... +OsmoBSC(config-net)# handover 1 +OsmoBSC(config-net)# do show network +... + Handover: On +... + +OsmoBSC(config-net)# ### If network level default is 'on', bts level can still override to 'off': +OsmoBSC(config-net)# bts 0 +OsmoBSC(config-net-bts)# handover 0 +OsmoBSC(config-net-bts)# do show network +... + Handover: Off +... +OsmoBSC(config-net-bts)# exit + +OsmoBSC(config-net)# ### Create a *second* BTS that is not explicitly 'off': +OsmoBSC(config-net)# bts 1 +OsmoBSC(config-net-bts)# do show network +... + Handover: On at 1 BTS, Off at 1 BTS +... + +OsmoBSC(config-net-bts)# ### Add arbitrary handover config item for bts 1: +OsmoBSC(config-net-bts)# handover1 power budget interval 23 +OsmoBSC(config-net-bts)# exit +OsmoBSC(config-net)# ### HO is 'on' globally, bts 0 disables it, bts 1 tweaks a param: +OsmoBSC(config-net)# show running-config +... +network +... !handover + handover 1 +... !handover + bts 0 +... !handover + handover 0 +... !handover + bts 1 +... !handover + handover1 power budget interval 23 +... !handover + +OsmoBSC(config-net)# ### Set global default to 'off', now bts 1 also uses the global default of 'off': +OsmoBSC(config-net)# handover 0 +OsmoBSC(config-net)# do show network +... + Handover: Off +... +OsmoBSC(config-net)# show running-config +... +network +... !handover + handover 0 +... !handover + bts 0 +... !handover + handover 0 +... !handover + bts 1 +... !handover + handover1 power budget interval 23 +... !handover + +OsmoBSC(config-net)# ### Remove the global setting, i.e. use the factory default net level, with same effect: +OsmoBSC(config-net)# handover default +% 'handover' setting removed, now is 0 +OsmoBSC(config-net)# handover default +% 'handover' already was unset, still is 0 +OsmoBSC(config-net)# do show network +... + Handover: Off +... +OsmoBSC(config-net)# show running-config +... +network +... !handover + bts 0 +... !handover + handover 0 +... !handover + bts 1 +... !handover + handover1 power budget interval 23 +... !handover + +OsmoBSC(config-net)# ### Re-enable net-level handover, but bts 0 remains disabled explicitly +OsmoBSC(config-net)# handover 1 +OsmoBSC(config-net)# do show network +... + Handover: On at 1 BTS, Off at 1 BTS +... +OsmoBSC(config-net)# show running-config +... +network +... !handover + handover 1 +... !handover + bts 0 +... !handover + handover 0 +... !handover + bts 1 +... !handover + handover1 power budget interval 23 +... !handover + +OsmoBSC(config-net)# ### Remove explicit setting of bts 0 to also use the global setting: +OsmoBSC(config-net)# bts 0 +OsmoBSC(config-net-bts)# handover default +% 'handover' setting removed, now is 1 (set on higher level node) +OsmoBSC(config-net-bts)# handover default +% 'handover' already was unset, still is 1 (set on higher level node) +OsmoBSC(config-net-bts)# do show network +... + Handover: On +... +OsmoBSC(config-net-bts)# show running-config +... +network +... !handover + handover 1 +... !handover + bts 0 +... !handover + bts 1 +... !handover + handover1 power budget interval 23 +... !handover + +OsmoBSC(config-net-bts)# ### Verify that 'min rxlev' value range stops at -50 +OsmoBSC(config-net-bts)# handover2 min rxlev ? + <-110--50> minimum RxLev (dBm) + default Use default (-100), remove explicit setting on this node +OsmoBSC(config-net-bts)# handover2 min rxlev -111 +% Unknown command. +OsmoBSC(config-net-bts)# handover2 min rxlev -110 +OsmoBSC(config-net-bts)# handover2 min rxlev -50 +OsmoBSC(config-net-bts)# handover2 min rxlev -49 +% Unknown command. +OsmoBSC(config-net-bts)# handover2 min rxlev 50 +% Unknown command. +OsmoBSC(config-net-bts)# handover2 min rxlev default +% 'handover2 min rxlev' setting removed, now is -100 + + +OsmoBSC(config-net-bts)# ### Checking online help +OsmoBSC(config-net-bts)# exit +OsmoBSC(config-net)# list +... + handover (0|1|default) + handover algorithm (1|2|default) + handover1 window rxlev averaging (<1-10>|default) + handover1 window rxqual averaging (<1-10>|default) + handover1 window rxlev neighbor averaging (<1-10>|default) + handover1 power budget interval (<1-99>|default) + handover1 power budget hysteresis (<0-999>|default) + handover1 maximum distance (<0-9999>|default) + handover2 window rxlev averaging (<1-10>|default) + handover2 window rxqual averaging (<1-10>|default) + handover2 window rxlev neighbor averaging (<1-10>|default) + handover2 power budget interval (<1-99>|default) + handover2 power budget hysteresis (<0-999>|default) + handover2 maximum distance (<0-9999>|default) + handover2 assignment (0|1|default) + handover2 tdma-measurement (full|subset|default) + handover2 min rxlev (<-110--50>|default) + handover2 min rxqual (<0-7>|default) + handover2 afs-bias rxlev (<0-20>|default) + handover2 afs-bias rxqual (<0-7>|default) + handover2 min-free-slots tch/f (<0-9999>|default) + handover2 min-free-slots tch/h (<0-9999>|default) + handover2 max-handovers (<1-9999>|default) + handover2 penalty-time max-distance (<0-99999>|default) + handover2 penalty-time failed-ho (<0-99999>|default) + handover2 penalty-time failed-assignment (<0-99999>|default) + handover2 retries (<0-9>|default) + handover2 congestion-check (disabled|<1-999>|now) +... + +OsmoBSC(config-net)# handover? + handover Handover general config + +OsmoBSC(config-net)# handover1? + handover1 Handover options for handover decision algorithm 1 + +OsmoBSC(config-net)# handover2? + handover2 Handover options for handover decision algorithm 2 + +OsmoBSC(config-net)# handover ? + 0 Disable in-call handover + 1 Enable in-call handover + default Enable/disable handover: Use default (0), remove explicit setting on this node + algorithm Choose algorithm for handover decision +... + +OsmoBSC(config-net)# handover1 ? + window Measurement averaging settings + power Neighbor cell power triggering + maximum Maximum Timing-Advance value (i.e. MS distance) before triggering HO + +OsmoBSC(config-net)# handover2 ? + window Measurement averaging settings + power Neighbor cell power triggering + maximum Maximum Timing-Advance value (i.e. MS distance) before triggering HO + assignment Enable or disable in-call channel re-assignment (HO algo 2 only) + tdma-measurement Define measurement set of TDMA frames (HO algo 2 only) + min Minimum Level/Quality thresholds before triggering HO (HO algo 2 only) + afs-bias Configure bias to prefer AFS (AMR on TCH/F) over other codecs (HO algo 2 only) + min-free-slots Minimum free TCH timeslots before cell is considered congested (HO algo 2 only) + max-handovers Maximum number of concurrent handovers allowed per cell (HO algo 2 only) + penalty-time Set penalty times to wait between repeated handovers (HO algo 2 only) + retries Immediately retry on handover/assignment failure (HO algo 2 only) + congestion-check Configure congestion check interval (HO algo 2 only) + +OsmoBSC(config-net)# handover algorithm ? + 1 Algorithm 1: trigger handover based on comparing current cell and neighbor RxLev and RxQual, only. + 2 Algorithm 2: trigger handover on RxLev/RxQual, and also to balance the load across several cells. Consider available codecs. Prevent repeated handover by penalty timers. + default Use default (1), remove explicit setting on this node + +OsmoBSC(config-net)# handover1 window ? + rxlev Received-Level averaging + rxqual Received-Quality averaging + +OsmoBSC(config-net)# handover1 window rxlev ? + averaging How many RxLev measurements are used for averaging + neighbor How many Neighbor RxLev measurements are used for averaging + +OsmoBSC(config-net)# handover1 window rxlev averaging ? + <1-10> RxLev averaging: Number of values to average over + default Use default (10), remove explicit setting on this node + +OsmoBSC(config-net)# handover1 window rxlev neighbor ? + averaging How many Neighbor RxLev measurements are used for averaging + +OsmoBSC(config-net)# handover1 window rxlev neighbor averaging ? + <1-10> Neighbor RxLev averaging: Number of values to average over + default Use default (10), remove explicit setting on this node + +OsmoBSC(config-net)# handover1 window rxqual ? + averaging How many RxQual measurements are used for averaging + +OsmoBSC(config-net)# handover1 window rxqual averaging ? + <1-10> RxQual averaging: Number of values to average over + default Use default (1), remove explicit setting on this node + +OsmoBSC(config-net)# handover1 power ? + budget Neighbor cell power triggering + +OsmoBSC(config-net)# handover1 power budget ? + interval How often to check for a better cell (SACCH frames) + hysteresis How many dBm stronger must a neighbor be to become a HO candidate + +OsmoBSC(config-net)# handover1 power budget interval ? + <1-99> Check for stronger neighbor every N number of SACCH frames + default Use default (6), remove explicit setting on this node + +OsmoBSC(config-net)# handover1 power budget hysteresis ? + <0-999> Neighbor's strength difference in dBm + default Use default (3), remove explicit setting on this node + +OsmoBSC(config-net)# handover1 maximum ? + distance Maximum Timing-Advance value (i.e. MS distance) before triggering HO + +OsmoBSC(config-net)# handover1 maximum distance ? + <0-9999> Maximum Timing-Advance value (i.e. MS distance) before triggering HO + default Use default (9999), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 window ? + rxlev Received-Level averaging + rxqual Received-Quality averaging + +OsmoBSC(config-net)# handover2 window rxlev ? + averaging How many RxLev measurements are used for averaging + neighbor How many Neighbor RxLev measurements are used for averaging + +OsmoBSC(config-net)# handover2 window rxlev averaging ? + <1-10> RxLev averaging: Number of values to average over + default Use default (10), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 window rxlev neighbor ? + averaging How many Neighbor RxLev measurements are used for averaging + +OsmoBSC(config-net)# handover2 window rxlev neighbor averaging ? + <1-10> Neighbor RxLev averaging: Number of values to average over + default Use default (10), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 window rxqual ? + averaging How many RxQual measurements are used for averaging + +OsmoBSC(config-net)# handover2 window rxqual averaging ? + <1-10> RxQual averaging: Number of values to average over + default Use default (1), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 power ? + budget Neighbor cell power triggering + +OsmoBSC(config-net)# handover2 power budget ? + interval How often to check for a better cell (SACCH frames) + hysteresis How many dBm stronger must a neighbor be to become a HO candidate + +OsmoBSC(config-net)# handover2 power budget interval ? + <1-99> Check for stronger neighbor every N number of SACCH frames + default Use default (6), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 power budget hysteresis ? + <0-999> Neighbor's strength difference in dBm + default Use default (3), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 maximum ? + distance Maximum Timing-Advance value (i.e. MS distance) before triggering HO + +OsmoBSC(config-net)# handover2 maximum distance ? + <0-9999> Maximum Timing-Advance value (i.e. MS distance) before triggering HO + default Use default (9999), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 assignment ? + 0 Disable in-call assignment + 1 Enable in-call assignment + default Use default (0), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 tdma-measurement ? + full Full set of 102/104 TDMA frames + subset Sub set of 4 TDMA frames (SACCH) + default Use default (subset), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 min ? + rxlev How weak may RxLev of an MS become before triggering HO + rxqual How bad may RxQual of an MS become before triggering HO + +OsmoBSC(config-net)# handover2 min rxlev ? + <-110--50> minimum RxLev (dBm) + default Use default (-100), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 min rxqual ? + <0-7> minimum RxQual (dBm) + default Use default (5), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 afs-bias ? + rxlev RxLev improvement bias for AFS over other codecs + rxqual RxQual improvement bias for AFS over other codecs + +OsmoBSC(config-net)# handover2 afs-bias rxlev ? + <0-20> Virtual RxLev improvement (dBm) + default Use default (0), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 afs-bias rxqual ? + <0-7> Virtual RxQual improvement (dBm) + default Use default (0), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 min-free-slots ? + tch/f Minimum free TCH/F timeslots before cell is considered congested + tch/h Minimum free TCH/H timeslots before cell is considered congested + +OsmoBSC(config-net)# handover2 min-free-slots tch/f ? + <0-9999> Number of TCH/F slots + default Use default (0), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 min-free-slots TCH/F ? +% There is no matched command. + +OsmoBSC(config-net)# handover2 min-free-slots tch/h ? + <0-9999> Number of TCH/H slots + default Use default (0), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 max-handovers ? + <1-9999> Number + default Use default (9999), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 penalty-time ? + max-distance Time to suspend handovers after leaving this cell due to exceeding max distance + failed-ho Time to suspend handovers after handover failure to this cell + failed-assignment Time to suspend handovers after assignment failure in this cell + +OsmoBSC(config-net)# handover2 penalty-time max-distance ? + <0-99999> Seconds + default Use default (300), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 penalty-time failed-ho ? + <0-99999> Seconds + default Use default (60), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 penalty-time failed-assignment ? + <0-99999> Seconds + default Use default (60), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 retries ? + <0-9> Number of retries + default Use default (0), remove explicit setting on this node + +OsmoBSC(config-net)# handover2 congestion-check ? + disabled Disable congestion checking, do not handover based on cell overload + <1-999> Congestion check interval in seconds (default 10) + now Manually trigger a congestion check to run right now + + +OsmoBSC(config-net)# ### Same on BTS level, except for the congestion-check +OsmoBSC(config-net)# bts 0 + +OsmoBSC(config-net-bts)# handover? + handover Handover general config + +OsmoBSC(config-net-bts)# handover1? + handover1 Handover options for handover decision algorithm 1 + +OsmoBSC(config-net-bts)# handover2? + handover2 Handover options for handover decision algorithm 2 + +OsmoBSC(config-net-bts)# handover ? + 0 Disable in-call handover + 1 Enable in-call handover + default Enable/disable handover: Use default (0), remove explicit setting on this node + algorithm Choose algorithm for handover decision +... + +OsmoBSC(config-net-bts)# handover1 ? + window Measurement averaging settings + power Neighbor cell power triggering + maximum Maximum Timing-Advance value (i.e. MS distance) before triggering HO + +OsmoBSC(config-net-bts)# handover2 ? + window Measurement averaging settings + power Neighbor cell power triggering + maximum Maximum Timing-Advance value (i.e. MS distance) before triggering HO + assignment Enable or disable in-call channel re-assignment (HO algo 2 only) + tdma-measurement Define measurement set of TDMA frames (HO algo 2 only) + min Minimum Level/Quality thresholds before triggering HO (HO algo 2 only) + afs-bias Configure bias to prefer AFS (AMR on TCH/F) over other codecs (HO algo 2 only) + min-free-slots Minimum free TCH timeslots before cell is considered congested (HO algo 2 only) + max-handovers Maximum number of concurrent handovers allowed per cell (HO algo 2 only) + penalty-time Set penalty times to wait between repeated handovers (HO algo 2 only) + retries Immediately retry on handover/assignment failure (HO algo 2 only) + +OsmoBSC(config-net-bts)# handover algorithm ? + 1 Algorithm 1: trigger handover based on comparing current cell and neighbor RxLev and RxQual, only. + 2 Algorithm 2: trigger handover on RxLev/RxQual, and also to balance the load across several cells. Consider available codecs. Prevent repeated handover by penalty timers. + default Use default (1), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover1 window ? + rxlev Received-Level averaging + rxqual Received-Quality averaging + +OsmoBSC(config-net-bts)# handover1 window rxlev ? + averaging How many RxLev measurements are used for averaging + neighbor How many Neighbor RxLev measurements are used for averaging + +OsmoBSC(config-net-bts)# handover1 window rxlev averaging ? + <1-10> RxLev averaging: Number of values to average over + default Use default (10), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover1 window rxlev neighbor ? + averaging How many Neighbor RxLev measurements are used for averaging + +OsmoBSC(config-net-bts)# handover1 window rxlev neighbor averaging ? + <1-10> Neighbor RxLev averaging: Number of values to average over + default Use default (10), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover1 window rxqual ? + averaging How many RxQual measurements are used for averaging + +OsmoBSC(config-net-bts)# handover1 window rxqual averaging ? + <1-10> RxQual averaging: Number of values to average over + default Use default (1), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover1 power ? + budget Neighbor cell power triggering + +OsmoBSC(config-net-bts)# handover1 power budget ? + interval How often to check for a better cell (SACCH frames) + hysteresis How many dBm stronger must a neighbor be to become a HO candidate + +OsmoBSC(config-net-bts)# handover1 power budget interval ? + <1-99> Check for stronger neighbor every N number of SACCH frames + default Use default (6), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover1 power budget hysteresis ? + <0-999> Neighbor's strength difference in dBm + default Use default (3), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover1 maximum ? + distance Maximum Timing-Advance value (i.e. MS distance) before triggering HO + +OsmoBSC(config-net-bts)# handover1 maximum distance ? + <0-9999> Maximum Timing-Advance value (i.e. MS distance) before triggering HO + default Use default (9999), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 window ? + rxlev Received-Level averaging + rxqual Received-Quality averaging + +OsmoBSC(config-net-bts)# handover2 window rxlev ? + averaging How many RxLev measurements are used for averaging + neighbor How many Neighbor RxLev measurements are used for averaging + +OsmoBSC(config-net-bts)# handover2 window rxlev averaging ? + <1-10> RxLev averaging: Number of values to average over + default Use default (10), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 window rxlev neighbor ? + averaging How many Neighbor RxLev measurements are used for averaging + +OsmoBSC(config-net-bts)# handover2 window rxlev neighbor averaging ? + <1-10> Neighbor RxLev averaging: Number of values to average over + default Use default (10), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 window rxqual ? + averaging How many RxQual measurements are used for averaging + +OsmoBSC(config-net-bts)# handover2 window rxqual averaging ? + <1-10> RxQual averaging: Number of values to average over + default Use default (1), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 power ? + budget Neighbor cell power triggering + +OsmoBSC(config-net-bts)# handover2 power budget ? + interval How often to check for a better cell (SACCH frames) + hysteresis How many dBm stronger must a neighbor be to become a HO candidate + +OsmoBSC(config-net-bts)# handover2 power budget interval ? + <1-99> Check for stronger neighbor every N number of SACCH frames + default Use default (6), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 power budget hysteresis ? + <0-999> Neighbor's strength difference in dBm + default Use default (3), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 maximum ? + distance Maximum Timing-Advance value (i.e. MS distance) before triggering HO + +OsmoBSC(config-net-bts)# handover2 maximum distance ? + <0-9999> Maximum Timing-Advance value (i.e. MS distance) before triggering HO + default Use default (9999), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 assignment ? + 0 Disable in-call assignment + 1 Enable in-call assignment + default Use default (0), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 tdma-measurement ? + full Full set of 102/104 TDMA frames + subset Sub set of 4 TDMA frames (SACCH) + default Use default (subset), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 min ? + rxlev How weak may RxLev of an MS become before triggering HO + rxqual How bad may RxQual of an MS become before triggering HO + +OsmoBSC(config-net-bts)# handover2 min rxlev ? + <-110--50> minimum RxLev (dBm) + default Use default (-100), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 min rxqual ? + <0-7> minimum RxQual (dBm) + default Use default (5), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 afs-bias ? + rxlev RxLev improvement bias for AFS over other codecs + rxqual RxQual improvement bias for AFS over other codecs + +OsmoBSC(config-net-bts)# handover2 afs-bias rxlev ? + <0-20> Virtual RxLev improvement (dBm) + default Use default (0), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 afs-bias rxqual ? + <0-7> Virtual RxQual improvement (dBm) + default Use default (0), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 min-free-slots ? + tch/f Minimum free TCH/F timeslots before cell is considered congested + tch/h Minimum free TCH/H timeslots before cell is considered congested + +OsmoBSC(config-net-bts)# handover2 min-free-slots tch/f ? + <0-9999> Number of TCH/F slots + default Use default (0), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 min-free-slots TCH/F ? +% There is no matched command. + +OsmoBSC(config-net-bts)# handover2 min-free-slots tch/h ? + <0-9999> Number of TCH/H slots + default Use default (0), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 max-handovers ? + <1-9999> Number + default Use default (9999), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 penalty-time ? + max-distance Time to suspend handovers after leaving this cell due to exceeding max distance + failed-ho Time to suspend handovers after handover failure to this cell + failed-assignment Time to suspend handovers after assignment failure in this cell + +OsmoBSC(config-net-bts)# handover2 penalty-time max-distance ? + <0-99999> Seconds + default Use default (300), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 penalty-time failed-ho ? + <0-99999> Seconds + default Use default (60), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 penalty-time failed-assignment ? + <0-99999> Seconds + default Use default (60), remove explicit setting on this node + +OsmoBSC(config-net-bts)# handover2 retries ? + <0-9> Number of retries + default Use default (0), remove explicit setting on this node diff --git a/tests/nanobts_omlattr/Makefile.am b/tests/nanobts_omlattr/Makefile.am new file mode 100644 index 0000000..c2b2c3b --- /dev/null +++ b/tests/nanobts_omlattr/Makefile.am @@ -0,0 +1,30 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(NULL) + +noinst_PROGRAMS = \ + nanobts_omlattr_test \ + $(NULL) + +EXTRA_DIST = \ + nanobts_omlattr_test.ok \ + $(NULL) + +nanobts_omlattr_test_SOURCES = \ + nanobts_omlattr_test.c \ + $(NULL) + +nanobts_omlattr_test_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(NULL) diff --git a/tests/nanobts_omlattr/nanobts_omlattr_test.c b/tests/nanobts_omlattr/nanobts_omlattr_test.c new file mode 100644 index 0000000..8e8626d --- /dev/null +++ b/tests/nanobts_omlattr/nanobts_omlattr_test.c @@ -0,0 +1,307 @@ +/* Test OML attribute generator */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * Author: Philipp Maier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <osmocom/bsc/debug.h> +#include <osmocom/bsc/gsm_data.h> +#include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/application.h> + +#include <stdio.h> +#include <string.h> + +struct gsm_bts_model bts_model_nanobts = { + .type = GSM_BTS_TYPE_NANOBTS, + .name = "nanobts", + .start = NULL, + .oml_rcvmsg = NULL, + .e1line_bind_ops = NULL, + .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 void test_nanobts_attr_bts_get(struct gsm_bts *bts, uint8_t *expected) +{ + struct msgb *msgb; + + printf("Testing nanobts_attr_bts_get()...\n"); + + msgb = nanobts_attr_bts_get(bts); + printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len)); + printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len)); + OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0); + msgb_free(msgb); + + printf("ok.\n"); + printf("\n"); +} + +static void test_nanobts_attr_nse_get(struct gsm_bts *bts, uint8_t *expected) +{ + struct msgb *msgb; + + printf("Testing nanobts_attr_nse_get()...\n"); + + msgb = nanobts_attr_nse_get(bts); + printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len)); + printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len)); + OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0); + msgb_free(msgb); + + printf("ok.\n"); + printf("\n"); +} + +static void test_nanobts_attr_cell_get(struct gsm_bts *bts, uint8_t *expected) +{ + struct msgb *msgb; + + printf("Testing nanobts_attr_cell_get()...\n"); + + msgb = nanobts_attr_cell_get(bts); + printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len)); + printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len)); + OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0); + msgb_free(msgb); + + printf("ok.\n"); + printf("\n"); +} + +static void test_nanobts_attr_nscv_get(struct gsm_bts *bts, uint8_t *expected) +{ + struct msgb *msgb; + + printf("Testing nanobts_attr_nscv_get()...\n"); + + msgb = nanobts_attr_nscv_get(bts); + printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len)); + printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len)); + OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0); + msgb_free(msgb); + + printf("ok.\n"); + printf("\n"); +} + +static void test_nanobts_attr_radio_get(struct gsm_bts *bts, + struct gsm_bts_trx *trx, + uint8_t *expected) +{ + struct msgb *msgb; + + printf("Testing nanobts_attr_nscv_get()...\n"); + + msgb = nanobts_attr_radio_get(bts, trx); + printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len)); + printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len)); + OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0); + msgb_free(msgb); + + printf("ok.\n"); + printf("\n"); +} + +static const struct log_info_cat log_categories[] = { +}; + +static const struct log_info log_info = { + .cat = log_categories, + .num_cat = ARRAY_SIZE(log_categories), +}; + +int main(int argc, char **argv) +{ + void *ctx; + + struct gsm_bts *bts; + struct gsm_network *net; + struct gsm_bts_trx *trx; + + ctx = talloc_named_const(NULL, 0, "ctx"); + + osmo_init_logging2(ctx, &log_info); + log_set_log_level(osmo_stderr_target, LOGL_INFO); + + /* Allocate environmental structs (bts, net, trx) */ + net = talloc_zero(ctx, struct gsm_network); + INIT_LLIST_HEAD(&net->bts_list); + gsm_bts_model_register(&bts_model_nanobts); + bts = gsm_bts_alloc_register(net, GSM_BTS_TYPE_NANOBTS, 63); + OSMO_ASSERT(bts); + bts->network = net; + trx = talloc_zero(ctx, struct gsm_bts_trx); + + /* Parameters needed by nanobts_attr_bts_get() */ + bts->rach_b_thresh = -1; + bts->rach_ldavg_slots = -1; + bts->c0->arfcn = 866; + bts->cell_identity = 1337; + bts->network->plmn = (struct osmo_plmn_id){ .mcc=1, .mnc=1 }; + bts->location_area_code = 1; + bts->gprs.rac = 0; + uint8_t attr_bts_expected[] = + { 0x19, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73, 0x18, 0x06, 0x0e, 0x00, + 0x02, 0x01, 0x20, 0x33, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, + 0xa8, 0x1f, 0x3f, 0x25, + 0x00, 0x01, 0x0a, 0x0c, 0x0a, 0x0b, 0x01, 0x2a, 0x5a, 0x2b, + 0x03, 0xe8, 0x0a, 0x0d, + 0x23, 0x0a, 0x08, 0x03, 0x62, 0x09, 0x3f, 0x99, 0x00, 0x07, + 0x00, 0xf1, 0x10, 0x00, + 0x01, 0x05, 0x39 + }; + + /* Parameters needed to test nanobts_attr_nse_get() */ + bts->gprs.nse.nsei = 101; + uint8_t attr_nse_expected[] = + { 0x9d, 0x00, 0x02, 0x00, 0x65, 0xa0, 0x00, 0x07, 0x03, 0x03, 0x03, + 0x03, 0x1e, 0x03, 0x0a, 0xa1, 0x00, 0x0b, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x0a, 0x03, + 0x0a, 0x03, 0x0a, 0x03 + }; + + /* Parameters needed to test nanobts_attr_cell_get() */ + bts->gprs.rac = 0x00; + bts->gprs.cell.bvci = 2; + bts->gprs.mode = BTS_GPRS_GPRS; + uint8_t attr_cell_expected[] = + { 0x9a, 0x00, 0x01, 0x00, 0x9c, 0x00, 0x02, 0x05, 0x03, 0x9e, 0x00, + 0x02, 0x00, 0x02, 0xa3, 0x00, 0x09, 0x14, 0x05, 0x05, 0xa0, + 0x05, 0x0a, 0x04, 0x08, + 0x0f, 0xa8, 0x00, 0x02, 0x0f, 0x00, 0xa9, 0x00, 0x05, 0x00, + 0xfa, 0x00, 0xfa, 0x02 + }; + + /* Parameters needed to test nanobts_attr_nscv_get() */ + bts->gprs.nsvc[0].nsvci = 0x65; + bts->gprs.nsvc[0].remote_port = 0x59d8; + bts->gprs.nsvc[0].remote_ip = 0x0a090165; + bts->gprs.nsvc[0].local_port = 0x5a3c; + uint8_t attr_nscv_expected[] = + { 0x9f, 0x00, 0x02, 0x00, 0x65, 0xa2, 0x00, 0x08, 0x59, 0xd8, 0x0a, + 0x09, 0x01, 0x65, 0x5a, 0x3c + }; + + /* Parameters needed to test nanobts_attr_radio_get() */ + trx->arfcn = 866; + trx->max_power_red = 22; + bts->c0->max_power_red = 22; + uint8_t attr_radio_expected[] = + { 0x2d, 0x0b, 0x05, 0x00, 0x02, 0x03, 0x62 }; + + /* Run tests */ + test_nanobts_attr_bts_get(bts, attr_bts_expected); + test_nanobts_attr_nse_get(bts, attr_nse_expected); + test_nanobts_attr_cell_get(bts, attr_cell_expected); + test_nanobts_attr_nscv_get(bts, attr_nscv_expected); + test_nanobts_attr_radio_get(bts, trx, attr_radio_expected); + + printf("Done\n"); + talloc_free(bts); + talloc_free(net); + talloc_free(trx); + talloc_report_full(ctx, stderr); + /* Expecting something like: + * full talloc report on 'ctx' (total 813 bytes in 6 blocks) + * logging contains 813 bytes in 5 blocks (ref 0) 0x60b0000000a0 + * struct log_target contains 196 bytes in 2 blocks (ref 0) 0x6110000000a0 + * struct log_category contains 36 bytes in 1 blocks (ref 0) 0x60d0000003e0 + * struct log_info contains 616 bytes in 2 blocks (ref 0) 0x60d000000310 + * struct log_info_cat contains 576 bytes in 1 blocks (ref 0) 0x6170000000e0 + * That's the root ctx + 5x logging: */ + OSMO_ASSERT(talloc_total_blocks(ctx) == 6); + talloc_free(ctx); + return 0; +} + +/* stubs */ +struct osmo_prim_hdr; +int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) +{ + abort(); +} + +struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) { + OSMO_ASSERT(0); +} diff --git a/tests/nanobts_omlattr/nanobts_omlattr_test.ok b/tests/nanobts_omlattr/nanobts_omlattr_test.ok new file mode 100644 index 0000000..ef46cf9 --- /dev/null +++ b/tests/nanobts_omlattr/nanobts_omlattr_test.ok @@ -0,0 +1,26 @@ +Testing nanobts_attr_bts_get()... +result= 19555b61676d7318060e00020120331e2424a83421a81f3f2500010a0c0a0b012a5a2b03e80a0d230a080362093f99000700f11000010539 +expected=19555b61676d7318060e00020120331e2424a83421a81f3f2500010a0c0a0b012a5a2b03e80a0d230a080362093f99000700f11000010539 +ok. + +Testing nanobts_attr_nse_get()... +result= 9d00020065a00007030303031e030aa1000b03030303030a030a030a03 +expected=9d00020065a00007030303031e030aa1000b03030303030a030a030a03 +ok. + +Testing nanobts_attr_cell_get()... +result= 9a0001009c000205039e00020002a30009140505a0050a04080fa800020f00a9000500fa00fa02 +expected=9a0001009c000205039e00020002a30009140505a0050a04080fa800020f00a9000500fa00fa02 +ok. + +Testing nanobts_attr_nscv_get()... +result= 9f00020065a2000859d80a0901655a3c +expected=9f00020065a2000859d80a0901655a3c +ok. + +Testing nanobts_attr_nscv_get()... +result= 2d0b0500020362 +expected=2d0b0500020362 +ok. + +Done diff --git a/tests/osmo-bsc.vty b/tests/osmo-bsc.vty new file mode 100644 index 0000000..560fb36 --- /dev/null +++ b/tests/osmo-bsc.vty @@ -0,0 +1,19 @@ +OsmoBSC> enable + +OsmoBSC# configure terminal +OsmoBSC(config)# network +OsmoBSC(config-net)# list +... + meas-feed destination ADDR <0-65535> + meas-feed scenario NAME +... + +OsmoBSC(config-net)# meas-feed destination 127.0.0.23 4223 +OsmoBSC(config-net)# meas-feed scenario foo23 +OsmoBSC(config-net)# show running-config +... +network +... + meas-feed destination 127.0.0.23 4223 + meas-feed scenario foo23 +... diff --git a/tests/subscr/Makefile.am b/tests/subscr/Makefile.am new file mode 100644 index 0000000..8d14ebf --- /dev/null +++ b/tests/subscr/Makefile.am @@ -0,0 +1,40 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + -ggdb3 \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(LIBSMPP34_CFLAGS) \ + $(COVERAGE_CFLAGS) \ + $(NULL) + +AM_LDFLAGS = \ + $(COVERAGE_LDFLAGS) \ + $(NULL) + +EXTRA_DIST = \ + bsc_subscr_test.ok \ + bsc_subscr_test.err \ + $(NULL) + +noinst_PROGRAMS = \ + bsc_subscr_test \ + $(NULL) + +bsc_subscr_test_SOURCES = \ + bsc_subscr_test.c \ + $(NULL) + +bsc_subscr_test_LDADD = \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBSMPP34_LIBS) \ + $(LIBOSMOVTY_LIBS) \ + $(NULL) diff --git a/tests/subscr/bsc_subscr_test.c b/tests/subscr/bsc_subscr_test.c new file mode 100644 index 0000000..3c94b86 --- /dev/null +++ b/tests/subscr/bsc_subscr_test.c @@ -0,0 +1,143 @@ +/* (C) 2008 by Jan Luebbe <jluebbe@debian.org> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2014 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/bsc/debug.h> +#include <osmocom/bsc/bsc_subscriber.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/utils.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> + +struct llist_head *bsc_subscribers; + +#define VERBOSE_ASSERT(val, expect_op, fmt) \ + do { \ + printf(#val " == " fmt "\n", (val)); \ + OSMO_ASSERT((val) expect_op); \ + } while (0); + +static void assert_bsc_subscr(const struct bsc_subscr *bsub, const char *imsi) +{ + struct bsc_subscr *sfound; + OSMO_ASSERT(bsub); + OSMO_ASSERT(strcmp(bsub->imsi, imsi) == 0); + + sfound = bsc_subscr_find_by_imsi(bsc_subscribers, imsi); + OSMO_ASSERT(sfound == bsub); + + bsc_subscr_put(sfound); +} + +static void test_bsc_subscr(void) +{ + struct bsc_subscr *s1, *s2, *s3; + const char *imsi1 = "1234567890"; + const char *imsi2 = "9876543210"; + const char *imsi3 = "5656565656"; + + printf("Test BSC subscriber allocation and deletion\n"); + + /* Check for emptiness */ + VERBOSE_ASSERT(llist_count(bsc_subscribers), == 0, "%d"); + OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi1) == NULL); + OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi2) == NULL); + OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi3) == NULL); + + /* Allocate entry 1 */ + s1 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi1); + VERBOSE_ASSERT(llist_count(bsc_subscribers), == 1, "%d"); + assert_bsc_subscr(s1, imsi1); + VERBOSE_ASSERT(llist_count(bsc_subscribers), == 1, "%d"); + OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi2) == NULL); + + /* Allocate entry 2 */ + s2 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi2); + VERBOSE_ASSERT(llist_count(bsc_subscribers), == 2, "%d"); + + /* Allocate entry 3 */ + s3 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi3); + VERBOSE_ASSERT(llist_count(bsc_subscribers), == 3, "%d"); + + /* Check entries */ + assert_bsc_subscr(s1, imsi1); + assert_bsc_subscr(s2, imsi2); + assert_bsc_subscr(s3, imsi3); + + /* Free entry 1 */ + bsc_subscr_put(s1); + s1 = NULL; + VERBOSE_ASSERT(llist_count(bsc_subscribers), == 2, "%d"); + OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi1) == NULL); + + assert_bsc_subscr(s2, imsi2); + assert_bsc_subscr(s3, imsi3); + + /* Free entry 2 */ + bsc_subscr_put(s2); + s2 = NULL; + VERBOSE_ASSERT(llist_count(bsc_subscribers), == 1, "%d"); + OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi1) == NULL); + OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi2) == NULL); + assert_bsc_subscr(s3, imsi3); + + /* Free entry 3 */ + bsc_subscr_put(s3); + s3 = NULL; + VERBOSE_ASSERT(llist_count(bsc_subscribers), == 0, "%d"); + OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi3) == NULL); + + OSMO_ASSERT(llist_empty(bsc_subscribers)); +} + +static const struct log_info_cat log_categories[] = { + [DREF] = { + .name = "DREF", + .description = "Reference Counting", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, +}; + +static const struct log_info log_info = { + .cat = log_categories, + .num_cat = ARRAY_SIZE(log_categories), +}; + +int main() +{ + void *ctx = talloc_named_const(NULL, 0, "bsc_subscr_test"); + printf("Testing BSC subscriber core code.\n"); + osmo_init_logging2(ctx, &log_info); + log_set_print_filename(osmo_stderr_target, 0); + log_set_print_timestamp(osmo_stderr_target, 0); + log_set_use_color(osmo_stderr_target, 0); + log_set_print_category(osmo_stderr_target, 1); + + bsc_subscribers = talloc_zero(ctx, struct llist_head); + INIT_LLIST_HEAD(bsc_subscribers); + + test_bsc_subscr(); + + printf("Done\n"); + return 0; +} diff --git a/tests/subscr/bsc_subscr_test.err b/tests/subscr/bsc_subscr_test.err new file mode 100644 index 0000000..a66317a --- /dev/null +++ b/tests/subscr/bsc_subscr_test.err @@ -0,0 +1,17 @@ +DREF BSC subscr IMSI:1234567890 usage increases to: 2 +DREF BSC subscr IMSI:1234567890 usage decreases to: 1 +DREF BSC subscr IMSI:1234567890 usage increases to: 2 +DREF BSC subscr IMSI:1234567890 usage decreases to: 1 +DREF BSC subscr IMSI:9876543210 usage increases to: 2 +DREF BSC subscr IMSI:9876543210 usage decreases to: 1 +DREF BSC subscr IMSI:5656565656 usage increases to: 2 +DREF BSC subscr IMSI:5656565656 usage decreases to: 1 +DREF BSC subscr IMSI:1234567890 usage decreases to: 0 +DREF BSC subscr IMSI:9876543210 usage increases to: 2 +DREF BSC subscr IMSI:9876543210 usage decreases to: 1 +DREF BSC subscr IMSI:5656565656 usage increases to: 2 +DREF BSC subscr IMSI:5656565656 usage decreases to: 1 +DREF BSC subscr IMSI:9876543210 usage decreases to: 0 +DREF BSC subscr IMSI:5656565656 usage increases to: 2 +DREF BSC subscr IMSI:5656565656 usage decreases to: 1 +DREF BSC subscr IMSI:5656565656 usage decreases to: 0 diff --git a/tests/subscr/bsc_subscr_test.ok b/tests/subscr/bsc_subscr_test.ok new file mode 100644 index 0000000..0f6a8be --- /dev/null +++ b/tests/subscr/bsc_subscr_test.ok @@ -0,0 +1,11 @@ +Testing BSC subscriber core code. +Test BSC subscriber allocation and deletion +llist_count(bsc_subscribers) == 0 +llist_count(bsc_subscribers) == 1 +llist_count(bsc_subscribers) == 1 +llist_count(bsc_subscribers) == 2 +llist_count(bsc_subscribers) == 3 +llist_count(bsc_subscribers) == 2 +llist_count(bsc_subscribers) == 1 +llist_count(bsc_subscribers) == 0 +Done diff --git a/tests/testsuite.at b/tests/testsuite.at new file mode 100644 index 0000000..f0f6fd1 --- /dev/null +++ b/tests/testsuite.at @@ -0,0 +1,227 @@ +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([bsc_subscr]) +AT_KEYWORDS([bsc_subscr]) +cat $abs_srcdir/subscr/bsc_subscr_test.ok > expout +cat $abs_srcdir/subscr/bsc_subscr_test.err > experr +AT_CHECK([$abs_top_builddir/tests/subscr/bsc_subscr_test], [], [expout], [experr]) +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([bsc-nat-trie]) +AT_KEYWORDS([bsc-nat-trie]) +cp $abs_srcdir/bsc-nat-trie/prefixes.csv . +cat $abs_srcdir/bsc-nat-trie/bsc_nat_trie_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/bsc-nat-trie/bsc_nat_trie_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 + +AT_SETUP([bsc]) +AT_KEYWORDS([bsc]) +cat $abs_srcdir/bsc/bsc_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/bsc/bsc_test], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([nanobts_omlattr]) +AT_KEYWORDS([nanobts_omlattr]) +cat $abs_srcdir/nanobts_omlattr/nanobts_omlattr_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/nanobts_omlattr/nanobts_omlattr_test], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([bssap]) +AT_KEYWORDS([bssap]) +cat $abs_srcdir/bssap/bssap_test.ok > expout +cat $abs_srcdir/bssap/bssap_test.err > experr +AT_CHECK([$abs_top_builddir/tests/bssap/bssap_test], [], [expout], [experr]) +AT_CLEANUP + +AT_SETUP([handover test 0]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 0], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 1]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 1], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 2]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 2], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 3]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 3], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 4]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 4], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 5]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 5], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 6]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 6], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 7]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 7], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 8]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 8], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 9]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 9], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 10]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 10], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 11]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 11], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 12]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 12], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 13]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 13], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 14]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 14], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 15]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 15], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 16]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 16], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 17]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 17], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 18]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 18], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 19]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 19], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 20]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 20], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 21]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 21], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 22]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 22], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 23]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 23], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 24]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 24], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 25]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 25], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 26]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 26], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 27]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 27], [], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([handover test 28]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test 28], [], [expout], [ignore]) +AT_CLEANUP diff --git a/tests/vty_test_runner.py b/tests/vty_test_runner.py new file mode 100755 index 0000000..3b73ce7 --- /dev/null +++ b/tests/vty_test_runner.py @@ -0,0 +1,650 @@ +#!/usr/bin/env python2 + +# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com> +# (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 <http://www.gnu.org/licenses/>. + +import os, sys +import time +import unittest +import socket +import subprocess + +import osmopy.obscvty as obscvty +import osmopy.osmoutil as osmoutil +from osmopy.osmo_ipa import IPA + +# to be able to find $top_srcdir/doc/... +confpath = os.path.join(sys.path[0], '..') + +class TestVTYBase(unittest.TestCase): + + def checkForEndAndExit(self): + res = self.vty.command("list") + #print ('looking for "exit"\n') + self.assert_(res.find(' exit\r') > 0) + #print 'found "exit"\nlooking for "end"\n' + self.assert_(res.find(' end\r') > 0) + #print 'found "end"\n' + + 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: + self.proc = osmoutil.popen_devnull(osmo_vty_cmd) + except OSError: + print >> sys.stderr, "Current directory: %s" % os.getcwd() + print >> sys.stderr, "Consider setting -b" + + appstring = self.vty_app()[2] + appport = self.vty_app()[0] + self.vty = obscvty.VTYInteract(appstring, "127.0.0.1", appport) + + def tearDown(self): + if self.vty: + self.vty._close_socket() + self.vty = None + osmoutil.end_proc(self.proc) + + +class TestVTYGenericBSC(TestVTYBase): + + def _testConfigNetworkTree(self, include_bsc_items=True): + self.vty.enable() + self.assertTrue(self.vty.verify("configure terminal",[''])) + self.assertEquals(self.vty.node(), 'config') + self.checkForEndAndExit() + self.assertTrue(self.vty.verify("network",[''])) + self.assertEquals(self.vty.node(), 'config-net') + self.checkForEndAndExit() + self.assertTrue(self.vty.verify("bts 0",[''])) + self.assertEquals(self.vty.node(), 'config-net-bts') + self.checkForEndAndExit() + self.assertTrue(self.vty.verify("trx 0",[''])) + self.assertEquals(self.vty.node(), 'config-net-bts-trx') + self.checkForEndAndExit() + self.vty.command("write terminal") + self.assertTrue(self.vty.verify("exit",[''])) + self.assertEquals(self.vty.node(), 'config-net-bts') + self.assertTrue(self.vty.verify("exit",[''])) + self.assertTrue(self.vty.verify("bts 1",[''])) + self.assertEquals(self.vty.node(), 'config-net-bts') + self.checkForEndAndExit() + self.assertTrue(self.vty.verify("trx 1",[''])) + self.assertEquals(self.vty.node(), 'config-net-bts-trx') + self.checkForEndAndExit() + self.vty.command("write terminal") + self.assertTrue(self.vty.verify("exit",[''])) + self.assertEquals(self.vty.node(), 'config-net-bts') + self.assertTrue(self.vty.verify("exit",[''])) + self.assertEquals(self.vty.node(), 'config-net') + self.assertTrue(self.vty.verify("exit",[''])) + self.assertEquals(self.vty.node(), 'config') + self.assertTrue(self.vty.verify("exit",[''])) + self.assertTrue(self.vty.node() is None) + + +class TestVTYBSC(TestVTYGenericBSC): + + def vty_command(self): + return ["./src/osmo-bsc/osmo-bsc", "-c", + "doc/examples/osmo-bsc/osmo-bsc.cfg"] + + def vty_app(self): + return (4242, "./src/osmo-bsc/osmo-bsc", "OsmoBSC", "bsc") + + def testConfigNetworkTree(self): + self._testConfigNetworkTree() + + def testVtyTree(self): + self.vty.enable() + self.assertTrue(self.vty.verify("configure terminal", [''])) + self.assertEquals(self.vty.node(), 'config') + self.checkForEndAndExit() + self.assertTrue(self.vty.verify("msc 0", [''])) + self.assertEquals(self.vty.node(), 'config-msc') + self.checkForEndAndExit() + self.assertTrue(self.vty.verify("exit", [''])) + self.assertEquals(self.vty.node(), 'config') + self.assertTrue(self.vty.verify("bsc", [''])) + self.assertEquals(self.vty.node(), 'config-bsc') + self.checkForEndAndExit() + self.assertTrue(self.vty.verify("exit", [''])) + self.assertEquals(self.vty.node(), 'config') + self.assertTrue(self.vty.verify("exit", [''])) + self.assertTrue(self.vty.node() is None) + + def testUssdNotificationsMsc(self): + self.vty.enable() + self.vty.command("configure terminal") + self.vty.command("msc") + + # Test invalid input + self.vty.verify("bsc-msc-lost-text", ['% Command incomplete.']) + self.vty.verify("bsc-welcome-text", ['% Command incomplete.']) + self.vty.verify("bsc-grace-text", ['% Command incomplete.']) + + # Enable USSD notifications + self.vty.verify("bsc-msc-lost-text MSC disconnected", ['']) + self.vty.verify("bsc-welcome-text Hello MS", ['']) + self.vty.verify("bsc-grace-text In grace period", ['']) + + # Verify settings + res = self.vty.command("write terminal") + self.assert_(res.find('bsc-msc-lost-text MSC disconnected') > 0) + self.assertEquals(res.find('no bsc-msc-lost-text'), -1) + self.assert_(res.find('bsc-welcome-text Hello MS') > 0) + self.assertEquals(res.find('no bsc-welcome-text'), -1) + self.assert_(res.find('bsc-grace-text In grace period') > 0) + self.assertEquals(res.find('no bsc-grace-text'), -1) + + # Now disable it.. + self.vty.verify("no bsc-msc-lost-text", ['']) + self.vty.verify("no bsc-welcome-text", ['']) + self.vty.verify("no bsc-grace-text", ['']) + + # Verify settings + res = self.vty.command("write terminal") + self.assertEquals(res.find('bsc-msc-lost-text MSC disconnected'), -1) + self.assert_(res.find('no bsc-msc-lost-text') > 0) + self.assertEquals(res.find('bsc-welcome-text Hello MS'), -1) + self.assert_(res.find('no bsc-welcome-text') > 0) + self.assertEquals(res.find('bsc-grace-text In grace period'), -1) + self.assert_(res.find('no bsc-grace-text') > 0) + + def testUssdNotificationsBsc(self): + self.vty.enable() + self.vty.command("configure terminal") + self.vty.command("bsc") + + # Test invalid input + self.vty.verify("missing-msc-text", ['% Command incomplete.']) + + # Enable USSD notifications + self.vty.verify("missing-msc-text No MSC found", ['']) + + # Verify settings + res = self.vty.command("write terminal") + self.assert_(res.find('missing-msc-text No MSC found') > 0) + self.assertEquals(res.find('no missing-msc-text'), -1) + + # Now disable it.. + self.vty.verify("no missing-msc-text", ['']) + + # Verify settings + res = self.vty.command("write terminal") + self.assertEquals(res.find('missing-msc-text No MSC found'), -1) + self.assert_(res.find('no missing-msc-text') > 0) + + def testNetworkTimezone(self): + self.vty.enable() + self.vty.verify("configure terminal", ['']) + self.vty.verify("network", ['']) + + # Test invalid input + self.vty.verify("timezone", ['% Command incomplete.']) + self.vty.verify("timezone 20 0", ['% Unknown command.']) + self.vty.verify("timezone 0 11", ['% Unknown command.']) + self.vty.verify("timezone 0 0 99", ['% Unknown command.']) + + # Set time zone without DST + self.vty.verify("timezone 2 30", ['']) + + # Verify settings + res = self.vty.command("write terminal") + self.assert_(res.find('timezone 2 30') > 0) + self.assertEquals(res.find('timezone 2 30 '), -1) + + # Set time zone with DST + self.vty.verify("timezone 2 30 1", ['']) + + # Verify settings + res = self.vty.command("write terminal") + self.assert_(res.find('timezone 2 30 1') > 0) + + # Now disable it.. + self.vty.verify("no timezone", ['']) + + # Verify settings + res = self.vty.command("write terminal") + self.assertEquals(res.find(' timezone'), -1) + + def testShowNetwork(self): + res = self.vty.command("show network") + self.assert_(res.startswith('BSC is on Country Code') >= 0) + + def testMscDataCoreLACCI(self): + self.vty.enable() + res = self.vty.command("show running-config") + self.assertEquals(res.find("core-location-area-code"), -1) + self.assertEquals(res.find("core-cell-identity"), -1) + + self.vty.command("configure terminal") + self.vty.command("msc 0") + self.vty.command("core-location-area-code 666") + self.vty.command("core-cell-identity 333") + + res = self.vty.command("show running-config") + self.assert_(res.find("core-location-area-code 666") > 0) + self.assert_(res.find("core-cell-identity 333") > 0) + +class TestVTYNAT(TestVTYGenericBSC): + + def vty_command(self): + return ["./src/osmo-bsc_nat/osmo-bsc_nat", "-l", "127.0.0.1", "-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 testBSCreload(self): + # Use different port for the mock msc to avoid clashing with + # the osmo-bsc_nat itself + ip = "127.0.0.1" + port = 5522 + self.vty.enable() + bscs1 = self.vty.command("show bscs-config") + nat_bsc_reload(self) + bscs2 = self.vty.command("show bscs-config") + # check that multiple calls to bscs-config-file give the same result + self.assertEquals(bscs1, bscs2) + + # add new bsc + self.vty.command("configure terminal") + self.vty.command("nat") + self.vty.command("bsc 5") + self.vty.command("token key") + self.vty.command("location_area_code 666") + self.vty.command("end") + + # update bsc token + self.vty.command("configure terminal") + self.vty.command("nat") + self.vty.command("bsc 1") + self.vty.command("token xyu") + self.vty.command("end") + + nat_msc_ip(self, ip, port) + msc_socket, msc = nat_msc_test(self, ip, port, verbose=True) + try: + b0 = nat_bsc_sock_test(0, "lol", verbose=True, proc=self.proc) + b1 = nat_bsc_sock_test(1, "xyu", verbose=True, proc=self.proc) + b2 = nat_bsc_sock_test(5, "key", verbose=True, proc=self.proc) + + self.assertEquals("3 BSCs configured", self.vty.command("show nat num-bscs-configured")) + self.assertTrue(3 == nat_bsc_num_con(self)) + self.assertEquals("MSC is connected: 1", self.vty.command("show msc connection")) + + nat_bsc_reload(self) + bscs2 = self.vty.command("show bscs-config") + # check that the reset to initial config succeeded + self.assertEquals(bscs1, bscs2) + + self.assertEquals("2 BSCs configured", self.vty.command("show nat num-bscs-configured")) + self.assertTrue(1 == nat_bsc_num_con(self)) + rem = self.vty.command("show bsc connections").split(' ') + # remaining connection is for BSC0 + self.assertEquals('0', rem[2]) + # remaining connection is authorized + self.assertEquals('1', rem[4]) + self.assertEquals("MSC is connected: 1", self.vty.command("show msc connection")) + finally: + msc.close() + msc_socket.close() + + def testVtyTree(self): + self.vty.enable() + self.assertTrue(self.vty.verify('configure terminal', [''])) + self.assertEquals(self.vty.node(), 'config') + self.checkForEndAndExit() + self.assertTrue(self.vty.verify('mgcp', [''])) + self.assertEquals(self.vty.node(), 'config-mgcp') + self.checkForEndAndExit() + self.assertTrue(self.vty.verify('exit', [''])) + self.assertEquals(self.vty.node(), 'config') + self.assertTrue(self.vty.verify('nat', [''])) + self.assertEquals(self.vty.node(), 'config-nat') + self.checkForEndAndExit() + self.assertTrue(self.vty.verify('bsc 0', [''])) + self.assertEquals(self.vty.node(), 'config-nat-bsc') + self.checkForEndAndExit() + self.assertTrue(self.vty.verify('exit', [''])) + self.assertEquals(self.vty.node(), 'config-nat') + self.assertTrue(self.vty.verify('exit', [''])) + self.assertEquals(self.vty.node(), 'config') + self.assertTrue(self.vty.verify('exit', [''])) + self.assertTrue(self.vty.node() is None) + + def testRewriteNoRewrite(self): + self.vty.enable() + res = self.vty.command("configure terminal") + res = self.vty.command("nat") + res = self.vty.command("number-rewrite rewrite.cfg") + res = self.vty.command("no number-rewrite") + + def testEnsureNoEnsureModeSet(self): + self.vty.enable() + res = self.vty.command("configure terminal") + res = self.vty.command("nat") + + # Ensure the default + res = self.vty.command("show running-config") + self.assert_(res.find('\n sdp-ensure-amr-mode-set') > 0) + + self.vty.command("sdp-ensure-amr-mode-set") + res = self.vty.command("show running-config") + self.assert_(res.find('\n sdp-ensure-amr-mode-set') > 0) + + self.vty.command("no sdp-ensure-amr-mode-set") + res = self.vty.command("show running-config") + self.assert_(res.find('\n no sdp-ensure-amr-mode-set') > 0) + + def testRewritePostNoRewrite(self): + self.vty.enable() + self.vty.command("configure terminal") + self.vty.command("nat") + self.vty.verify("number-rewrite-post rewrite.cfg", ['']) + self.vty.verify("no number-rewrite-post", ['']) + + + def testPrefixTreeLoading(self): + cfg = os.path.join(confpath, "tests/bsc-nat-trie/prefixes.csv") + + self.vty.enable() + self.vty.command("configure terminal") + self.vty.command("nat") + res = self.vty.command("prefix-tree %s" % cfg) + self.assertEqual(res, "% prefix-tree loaded 17 rules.") + self.vty.command("end") + + res = self.vty.command("show prefix-tree") + self.assertEqual(res, '1,1\r\n12,2\r\n123,3\r\n1234,4\r\n12345,5\r\n123456,6\r\n1234567,7\r\n12345678,8\r\n123456789,9\r\n1234567890,10\r\n13,11\r\n14,12\r\n15,13\r\n16,14\r\n82,16\r\n823455,15\r\n+49123,17') + + self.vty.command("configure terminal") + self.vty.command("nat") + self.vty.command("no prefix-tree") + self.vty.command("end") + + res = self.vty.command("show prefix-tree") + self.assertEqual(res, "% there is now prefix tree loaded.") + + def testUssdSideChannelProvider(self): + self.vty.command("end") + self.vty.enable() + self.vty.command("configure terminal") + self.vty.command("nat") + self.vty.command("ussd-token key") + self.vty.command("end") + + res = self.vty.verify("show ussd-connection", ['The USSD side channel provider is not connected and not authorized.']) + self.assertTrue(res) + + ussdSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ussdSocket.connect(('127.0.0.1', 5001)) + ussdSocket.settimeout(2.0) + print "Connected to %s:%d" % ussdSocket.getpeername() + + print "Expecting ID_GET request" + data = ussdSocket.recv(4) + self.assertEqual(data, "\x00\x01\xfe\x04") + + print "Going to send ID_RESP response" + res = ussdSocket.send(IPA().id_resp(IPA().tag_name('key'))) + self.assertEqual(res, 10) + + # initiating PING/PONG cycle to know, that the ID_RESP message has been processed + + print "Going to send PING request" + res = ussdSocket.send(IPA().ping()) + self.assertEqual(res, 4) + + print "Expecting PONG response" + data = ussdSocket.recv(4) + self.assertEqual(data, "\x00\x01\xfe\x01") + + res = self.vty.verify("show ussd-connection", ['The USSD side channel provider is connected and authorized.']) + self.assertTrue(res) + + print "Going to shut down connection" + ussdSocket.shutdown(socket.SHUT_WR) + + print "Expecting EOF" + data = ussdSocket.recv(4) + self.assertEqual(data, "") + + ussdSocket.close() + + res = self.vty.verify("show ussd-connection", ['The USSD side channel provider is not connected and not authorized.']) + self.assertTrue(res) + + def testAccessList(self): + """ + Verify that the imsi-deny can have a reject cause or no reject cause + """ + self.vty.enable() + self.vty.command("configure terminal") + self.vty.command("nat") + + # Old default + self.vty.command("access-list test-default imsi-deny ^123[0-9]*$") + res = self.vty.command("show running-config").split("\r\n") + asserted = False + for line in res: + if line.startswith(" access-list test-default"): + self.assertEqual(line, " access-list test-default imsi-deny ^123[0-9]*$ 11 11") + asserted = True + self.assert_(asserted) + + # Check the optional CM Service Reject Cause + self.vty.command("access-list test-cm-deny imsi-deny ^123[0-9]*$ 42").split("\r\n") + res = self.vty.command("show running-config").split("\r\n") + asserted = False + for line in res: + if line.startswith(" access-list test-cm"): + self.assertEqual(line, " access-list test-cm-deny imsi-deny ^123[0-9]*$ 42 11") + asserted = True + self.assert_(asserted) + + # Check the optional LU Reject Cause + self.vty.command("access-list test-lu-deny imsi-deny ^123[0-9]*$ 23 42").split("\r\n") + res = self.vty.command("show running-config").split("\r\n") + asserted = False + for line in res: + if line.startswith(" access-list test-lu"): + self.assertEqual(line, " access-list test-lu-deny imsi-deny ^123[0-9]*$ 23 42") + asserted = True + self.assert_(asserted) + + +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) + +def nat_bsc_reload(x): + x.vty.command("configure terminal") + x.vty.command("nat") + x.vty.command("bscs-config-file bscs.cfg") + x.vty.command("end") + +def nat_msc_ip(x, ip, port): + x.vty.command("configure terminal") + x.vty.command("nat") + x.vty.command("msc ip " + ip) + x.vty.command("msc port " + str(port)) + x.vty.command("end") + +def data2str(d): + return d.encode('hex').lower() + +def nat_msc_test(x, ip, port, verbose = False): + msc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + msc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + msc.settimeout(5) + msc.bind((ip, port)) + msc.listen(5) + if (verbose): + print "MSC is ready at " + ip + conn = None + while True: + vty_response = x.vty.command("show msc connection") + print "'show msc connection' says: %r" % vty_response + if vty_response == "MSC is connected: 1": + # success + break; + if vty_response != "MSC is connected: 0": + raise Exception("Unexpected response to 'show msc connection'" + " vty command: %r" % vty_response) + + timeout_retries = 6 + while timeout_retries > 0: + try: + conn, addr = msc.accept() + print "MSC got connection from ", addr + break + except socket.timeout: + print "socket timed out." + timeout_retries -= 1 + continue + + if not conn: + raise Exception("VTY reports MSC is connected, but I haven't" + " connected yet: %r %r" % (ip, port)) + return msc, conn + +def ipa_handle_small(x, verbose = False): + s = data2str(x.recv(4)) + if len(s) != 4*2: + raise Exception("expected to receive 4 bytes, but got %d (%r)" % (len(s)/2, s)) + if "0001fe00" == s: + if (verbose): + print "\tBSC <- NAT: PING?" + x.send(IPA().pong()) + elif "0001fe06" == s: + if (verbose): + print "\tBSC <- NAT: IPA ID ACK" + x.send(IPA().id_ack()) + elif "0001fe00" == s: + if (verbose): + print "\tBSC <- NAT: PONG!" + else: + if (verbose): + print "\tBSC <- NAT: ", s + +def ipa_handle_resp(x, tk, verbose = False, proc=None): + s = data2str(x.recv(38)) + if "0023fe040108010701020103010401050101010011" in s: + retries = 3 + while True: + print "\tsending IPA identity(%s) at %s" % (tk, time.strftime("%T")) + try: + x.send(IPA().id_resp(IPA().identity(name = tk.encode('utf-8')))) + print "\tdone sending IPA identity(%s) at %s" % (tk, + time.strftime("%T")) + break + except: + print "\tfailed sending IPA identity at", time.strftime("%T") + if proc: + print "\tproc.poll() = %r" % proc.poll() + if retries < 1: + print "\tgiving up" + raise + print "\tretrying (%d attempts left)" % retries + retries -= 1 + else: + if (verbose): + print "\tBSC <- NAT: ", s + +def nat_bsc_num_con(x): + return len(x.vty.command("show bsc connections").split('\n')) + +def nat_bsc_sock_test(nr, tk, verbose = False, proc=None): + bsc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + bsc.bind(('127.0.0.1', 0)) + bsc.connect(('127.0.0.1', 5000)) + if (verbose): + print "BSC%d " %nr + print "\tconnected to %s:%d" % bsc.getpeername() + if proc: + print "\tproc.poll() = %r" % proc.poll() + print "\tproc.pid = %r" % proc.pid + ipa_handle_small(bsc, verbose) + ipa_handle_resp(bsc, tk, verbose, proc=proc) + if proc: + print "\tproc.poll() = %r" % proc.poll() + bsc.recv(27) # MGCP msg + if proc: + print "\tproc.poll() = %r" % proc.poll() + ipa_handle_small(bsc, verbose) + return bsc + +def add_bsc_test(suite, workdir): + if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")): + print("Skipping the BSC test") + return + test = unittest.TestLoader().loadTestsFromTestCase(TestVTYBSC) + 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") + parser.add_argument("test_name", nargs="*", help="(parts of) test names to run, case-insensitive") + 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() + add_bsc_test(suite, workdir) + add_nat_test(suite, workdir) + + if args.test_name: + osmoutil.pick_tests(suite, *args.test_name) + + res = unittest.TextTestRunner(verbosity=verbose_level, stream=sys.stdout).run(suite) + sys.exit(len(res.errors) + len(res.failures)) + +# vim: shiftwidth=4 expandtab nocin ai |