diff --git a/openbsc/.gitignore b/openbsc/.gitignore index 79222f751..595548258 100644 --- a/openbsc/.gitignore +++ b/openbsc/.gitignore @@ -63,6 +63,7 @@ tests/gprs/gprs_test tests/abis/abis_test tests/si/si_test tests/smpp/smpp_test +tests/bsc/bsc_test tests/atconfig tests/atlocal diff --git a/openbsc/configure.ac b/openbsc/configure.ac index ce2a32853..ddd15e3df 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -36,11 +36,12 @@ AC_SUBST(osmo_ac_build_nat) # Enable/disable the BSC? AC_ARG_ENABLE([osmo-bsc], [AS_HELP_STRING([--enable-osmo-bsc], [Build the Osmo BSC])], - [osmo_ac_build_bsc="$enableval"]) + [osmo_ac_build_bsc="$enableval"],[osmo_ac_build_bsc="no"]) if test "$osmo_ac_build_bsc" = "yes" ; then PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.0.6) fi AM_CONDITIONAL(BUILD_BSC, test "x$osmo_ac_build_bsc" = "xyes") +AC_SUBST(osmo_ac_build_bsc) # Enable/disable smpp support in the nitb? AC_ARG_ENABLE([smpp], [AS_HELP_STRING([--enable-smpp], [Build the SMPP interface])], @@ -156,6 +157,7 @@ AC_OUTPUT( tests/gsm0408/Makefile tests/db/Makefile tests/channel/Makefile + tests/bsc/Makefile tests/bsc-nat/Makefile tests/bsc-nat-trie/Makefile tests/mgcp/Makefile diff --git a/openbsc/include/openbsc/gsm_data_shared.h b/openbsc/include/openbsc/gsm_data_shared.h index 3ef145765..437378acf 100644 --- a/openbsc/include/openbsc/gsm_data_shared.h +++ b/openbsc/include/openbsc/gsm_data_shared.h @@ -594,6 +594,7 @@ struct gsm_bts { int hr; int mn; int override; + int dst; } tz; /* ip.accesss Unit ID's have Site/BTS/TRX layout */ diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c index d1567169a..5d03b2ad4 100644 --- a/openbsc/src/libbsc/bsc_vty.c +++ b/openbsc/src/libbsc/bsc_vty.c @@ -489,8 +489,14 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) VTY_NEWLINE); vty_out(vty, " training_sequence_code %u%s", bts->tsc, VTY_NEWLINE); vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE); - if (bts->tz.override != 0) - vty_out(vty, " timezone %d %d%s", bts->tz.hr, bts->tz.mn, VTY_NEWLINE); + if (bts->tz.override != 0) { + if (bts->tz.dst) + vty_out(vty, " timezone %d %d %d%s", + bts->tz.hr, bts->tz.mn, bts->tz.dst, VTY_NEWLINE); + else + vty_out(vty, " timezone %d %d%s", + bts->tz.hr, bts->tz.mn, VTY_NEWLINE); + } vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE); vty_out(vty, " cell reselection hysteresis %u%s", bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); @@ -1621,6 +1627,32 @@ DEFUN(cfg_bts_timezone, bts->tz.hr = tzhr; bts->tz.mn = tzmn; + bts->tz.dst = 0; + bts->tz.override = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_timezone_dst, + cfg_bts_timezone_dst_cmd, + "timezone <-19-19> (0|15|30|45) <0-2>", + "Set the Timezone Offset of this BTS\n" + "Timezone offset (hours)\n" + "Timezone offset (00 minutes)\n" + "Timezone offset (15 minutes)\n" + "Timezone offset (30 minutes)\n" + "Timezone offset (45 minutes)\n" + "DST offset (hours)\n" + ) +{ + struct gsm_bts *bts = vty->index; + int tzhr = atoi(argv[0]); + int tzmn = atoi(argv[1]); + int tzdst = atoi(argv[2]); + + bts->tz.hr = tzhr; + bts->tz.mn = tzmn; + bts->tz.dst = tzdst; bts->tz.override = 1; return CMD_SUCCESS; @@ -3117,6 +3149,7 @@ int bsc_vty_init(const struct log_info *cat) install_element(BTS_NODE, &cfg_bts_unit_id_cmd); install_element(BTS_NODE, &cfg_bts_rsl_ip_cmd); install_element(BTS_NODE, &cfg_bts_timezone_cmd); + install_element(BTS_NODE, &cfg_bts_timezone_dst_cmd); install_element(BTS_NODE, &cfg_bts_no_timezone_cmd); install_element(BTS_NODE, &cfg_bts_nokia_site_skip_reset_cmd); install_element(BTS_NODE, &cfg_bts_stream_id_cmd); diff --git a/openbsc/src/osmo-bsc/osmo_bsc_filter.c b/openbsc/src/osmo-bsc/osmo_bsc_filter.c index 957ceaf95..608f52552 100644 --- a/openbsc/src/osmo-bsc/osmo_bsc_filter.c +++ b/openbsc/src/osmo-bsc/osmo_bsc_filter.c @@ -251,6 +251,65 @@ int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn) return 0; } +static int bsc_patch_mm_info(struct gsm_subscriber_connection *conn, + uint8_t *data, unsigned int length) +{ + struct tlv_parsed tp; + int parse_res; + struct gsm_bts *bts = conn->bts; + int tzunits; + uint8_t tzbsd = 0; + uint8_t dst = 0; + + parse_res = tlv_parse(&tp, &gsm48_mm_att_tlvdef, data, length, 0, 0); + if (parse_res <= 0 && parse_res != -3) + return 0; + + /* Is TZ patching enabled? */ + if (!bts->tz.override) + return 0; + + /* Convert tz.hr and tz.mn to units */ + if (bts->tz.hr < 0) { + tzunits = -bts->tz.hr*4; + tzbsd |= 0x08; + } else + tzunits = bts->tz.hr*4; + + tzunits = tzunits + (bts->tz.mn/15); + + tzbsd |= (tzunits % 10)*0x10 + (tzunits / 10); + + /* Convert DST value */ + if (bts->tz.dst >= 0 && bts->tz.dst <= 2) + dst = bts->tz.dst; + + if (TLVP_PRESENT(&tp, GSM48_IE_UTC)) { + LOGP(DMSC, LOGL_DEBUG, + "Changing 'Local time zone' from 0x%02x to 0x%02x.\n", + TLVP_VAL(&tp, GSM48_IE_UTC)[6], tzbsd); + ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_UTC)))[0] = tzbsd; + } + if (TLVP_PRESENT(&tp, GSM48_IE_NET_TIME_TZ)) { + LOGP(DMSC, LOGL_DEBUG, + "Changing 'Universal time and local time zone' TZ from " + "0x%02x to 0x%02x.\n", + TLVP_VAL(&tp, GSM48_IE_NET_TIME_TZ)[6], tzbsd); + ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_NET_TIME_TZ)))[6] = tzbsd; + } +#ifdef GSM48_IE_NET_DST + if (TLVP_PRESENT(&tp, GSM48_IE_NET_DST)) { + LOGP(DMSC, LOGL_DEBUG, + "Changing 'Network daylight saving time' from " + "0x%02x to 0x%02x.\n", + TLVP_VAL(&tp, GSM48_IE_NET_DST)[0], dst); + ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_NET_DST)))[0] = dst; + } +#endif + + return 0; +} + /** * Messages coming back from the MSC. */ @@ -261,13 +320,16 @@ int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) struct gsm48_loc_area_id *lai; struct gsm48_hdr *gh; uint8_t mtype; + int length = msgb_l3len(msg); - if (msgb_l3len(msg) < sizeof(*gh)) { + if (length < sizeof(*gh)) { LOGP(DMSC, LOGL_ERROR, "GSM48 header does not fit.\n"); return -1; } gh = (struct gsm48_hdr *) msgb_l3(msg); + length -= (const char *)&gh->data[0] - (const char *)gh; + mtype = gh->msg_type & 0xbf; net = conn->bts->network; msc = conn->sccp_con->msc; @@ -285,6 +347,8 @@ int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) if (conn->sccp_con->new_subscriber) return send_welcome_ussd(conn); return 0; + } else if (mtype == GSM48_MT_MM_INFO) { + bsc_patch_mm_info(conn, &gh->data[0], length); } return 0; diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am index f2dc057fa..ed21fc332 100644 --- a/openbsc/tests/Makefile.am +++ b/openbsc/tests/Makefile.am @@ -4,6 +4,10 @@ if BUILD_NAT SUBDIRS += bsc-nat bsc-nat-trie endif +if BUILD_BSC +SUBDIRS += bsc +endif + if BUILD_SMPP SUBDIRS += smpp endif diff --git a/openbsc/tests/atlocal.in b/openbsc/tests/atlocal.in index bfbecd48f..46351137b 100644 --- a/openbsc/tests/atlocal.in +++ b/openbsc/tests/atlocal.in @@ -1,2 +1,3 @@ enable_nat_test='@osmo_ac_build_nat@' enable_smpp_test='@osmo_ac_build_smpp@' +enable_bsc_test='@osmo_ac_build_bsc@' diff --git a/openbsc/tests/bsc/Makefile.am b/openbsc/tests/bsc/Makefile.am new file mode 100644 index 000000000..0c79b8374 --- /dev/null +++ b/openbsc/tests/bsc/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) +AM_LDFLAGS = $(COVERAGE_LDFLAGS) + +EXTRA_DIST = bsc_test.ok + +noinst_PROGRAMS = bsc_test + +bsc_test_SOURCES = bsc_test.c \ + $(top_srcdir)/src/osmo-bsc/osmo_bsc_filter.c +bsc_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libctrl/libctrl.a \ + $(top_builddir)/src/libmgcp/libmgcp.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lrt \ + $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOABIS_LIBS) diff --git a/openbsc/tests/bsc/bsc_test.c b/openbsc/tests/bsc/bsc_test.c new file mode 100644 index 000000000..881c785f3 --- /dev/null +++ b/openbsc/tests/bsc/bsc_test.c @@ -0,0 +1,204 @@ +/* + * BSC Message filtering + * + * (C) 2013 by sysmocom s.f.m.c. GmbH + * Written by Jacob Erlbeck + * (C) 2010-2013 by Holger Hans Peter Freyther + * (C) 2010-2013 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +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; + struct gsm_bts *bts; + struct osmo_bsc_sccp_con *sccp_con; + struct osmo_msc_data *msc; + struct gsm_subscriber_connection *conn; + + net = talloc_zero(NULL, struct gsm_network); + bts = talloc_zero(net, struct gsm_bts); + sccp_con = talloc_zero(net, struct osmo_bsc_sccp_con); + msc = talloc_zero(net, struct osmo_msc_data); + conn = talloc_zero(net, struct gsm_subscriber_connection); + + bts->network = net; + sccp_con->msc = msc; + conn->bts = bts; + conn->sccp_con = sccp_con; + + /* start testinh 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; + + bts->tz.hr = get_int(test_def->params, test_def->n_params, "tz_hr", 0, &is_set); + bts->tz.mn = get_int(test_def->params, test_def->n_params, "tz_mn", 0, &is_set); + bts->tz.dst = get_int(test_def->params, test_def->n_params, "tz_dst", 0, &is_set); + bts->tz.override = get_int(test_def->params, test_def->n_params, "tz_dst", is_set ? 1 : 0, NULL); + + 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: + result = bsc_scan_msc_msg(conn, msg); + break; + case TEST_SCAN_TO_MSC: + result = bsc_scan_msc_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); +} + + +int main(int argc, char **argv) +{ + osmo_init_logging(&log_info); + + test_scan(); + + printf("Testing execution completed.\n"); + return 0; +} diff --git a/openbsc/tests/bsc/bsc_test.ok b/openbsc/tests/bsc/bsc_test.ok new file mode 100644 index 000000000..0564bf0cd --- /dev/null +++ b/openbsc/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/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index e54d4b57a..27225a864 100644 --- a/openbsc/tests/testsuite.at +++ b/openbsc/tests/testsuite.at @@ -68,3 +68,10 @@ 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]) +AT_CHECK([test "$enable_bsc_test" != no || exit 77]) +cat $abs_srcdir/bsc/bsc_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/bsc/bsc_test], [], [expout], [ignore]) +AT_CLEANUP diff --git a/openbsc/tests/vty_test_runner.py b/openbsc/tests/vty_test_runner.py index 46dbce7c4..3bb7bd86b 100644 --- a/openbsc/tests/vty_test_runner.py +++ b/openbsc/tests/vty_test_runner.py @@ -315,6 +315,40 @@ class TestVTYBSC(TestVTYGenericBSC): 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", ['']) + self.vty.verify("bts 0", ['']) + + # 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) + class TestVTYNAT(TestVTYGenericBSC): def vty_command(self):