From patchwork Mon Apr 4 13:30:56 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max X-Patchwork-Id: 605868 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.osmocom.org (lists.osmocom.org [144.76.43.76]) by ozlabs.org (Postfix) with ESMTP id 3qdtD944dQz9sBM for ; Mon, 4 Apr 2016 23:31:05 +1000 (AEST) Received: from lists.osmocom.org (lists.osmocom.org [144.76.43.76]) by lists.osmocom.org (Postfix) with ESMTP id A8CB01C4B0; Mon, 4 Apr 2016 13:31:03 +0000 (UTC) X-Original-To: openbsc@lists.osmocom.org Delivered-To: openbsc@lists.osmocom.org Received: from mail.sysmocom.de (mail.sysmocom.de [IPv6:2a01:4f8:191:444c::2:4]) by lists.osmocom.org (Postfix) with ESMTP id B3E3B1C49D for ; Mon, 4 Apr 2016 13:31:01 +0000 (UTC) Received: from mail.sysmocom.de (mail.sysmocom.de [144.76.43.93]) by mail.sysmocom.de (Postfix) with ESMTP id CC05019C4CA; Mon, 4 Apr 2016 13:31:01 +0000 (UTC) Received: from pbell.local (ip5b418565.dynamic.kabel-deutschland.de [91.65.133.101]) by mail.sysmocom.de (Postfix) with ESMTPSA id 8231219C4C9; Mon, 4 Apr 2016 13:31:01 +0000 (UTC) From: msuraev@sysmocom.de To: openbsc@lists.osmocom.org Subject: [PATCH 4/4] NAT: reload BSCs config dynamically Date: Mon, 4 Apr 2016 15:30:56 +0200 Message-Id: <1459776656-31929-4-git-send-email-msuraev@sysmocom.de> X-Mailer: git-send-email 2.8.1 In-Reply-To: <1459776656-31929-1-git-send-email-msuraev@sysmocom.de> References: <1459776656-31929-1-git-send-email-msuraev@sysmocom.de> X-Virus-Scanned: ClamAV using ClamSMTP X-BeenThere: openbsc@lists.osmocom.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: "Development of OpenBSC, OsmoBSC, OsmoNITB, OsmoCSCN" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Max Errors-To: openbsc-bounces@lists.osmocom.org Sender: "OpenBSC" From: Max Add vty tests for BSC configuration reloading. Load BSCs configuration on bscs-config-file command: * remove all runtime configured BSC not in the config file * close connections to all BSC with updated token value Fixes: OS#1670 Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/bsc_nat.h | 7 +- openbsc/src/osmo-bsc_nat/bsc_nat.c | 4 +- openbsc/src/osmo-bsc_nat/bsc_nat_vty.c | 38 ++++++++- openbsc/tests/vty_test_runner.py | 140 ++++++++++++++++++++++++++++++++- 4 files changed, 183 insertions(+), 6 deletions(-) diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index ab1c590..419f111 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -35,10 +35,11 @@ #include #include +#include #define DIR_BSC 1 #define DIR_MSC 2 - +#define DEFAULT_TOKEN_VALUE "__UNKNOWN__" #define PAGIN_GROUP_UNASSIGNED -1 struct sccp_source_reference; @@ -164,6 +165,10 @@ struct bsc_config { /* audio handling */ int max_endpoints; + /* used internally for reload handling */ + bool remove; + bool token_updated; + /* backpointer */ struct bsc_nat *nat; diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c index 7dcacfd..9bbb253 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c @@ -1321,8 +1321,8 @@ static int ipaccess_bsc_read_cb(struct osmo_fd *bfd) bsc->cfg ? bsc->cfg->nr : -1); else LOGP(DNAT, LOGL_ERROR, - "Stream error on BSC Nr: %d. Failed to parse ip access message: %d\n", - bsc->cfg ? bsc->cfg->nr : -1, ret); + "Stream error on BSC Nr: %d. Failed to parse ip access message: %d (%s)\n", + bsc->cfg ? bsc->cfg->nr : -1, ret, strerror(-ret)); bsc_close_connection(bsc); return -1; diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c index 8def3ed..495e505 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c @@ -39,6 +39,7 @@ #include #include +#include static struct bsc_nat *_nat; @@ -512,15 +513,40 @@ DEFUN(cfg_nat_include, "Set the filename of the BSC configuration to include.\n" "The filename to be included.") { + int rc; + struct bsc_config *cf1, *cf2; + struct bsc_connection *con1, *con2; const char *path = bsc_nat_resolve_path(_nat, _nat->include_base, argv[0]); - int rc = vty_read_config_file(path, NULL); + + llist_for_each_entry_safe(cf1, cf2, &_nat->bsc_configs, entry) { + cf1->remove = true; + cf1->token_updated = false; + } + + rc = vty_read_config_file(path, NULL); if (rc < 0) { vty_out(vty, "Failed to parse the config file %s: %s%s", path, strerror(-rc), VTY_NEWLINE); return CMD_WARNING; } + bsc_replace_string(_nat, &_nat->include_file, argv[0]); + + llist_for_each_entry_safe(con1, con2, &_nat->bsc_connections, + list_entry) { + if (con1->cfg) + if (con1->cfg->token_updated || con1->cfg->remove) + bsc_close_connection(con1); + } + + llist_for_each_entry_safe(cf1, cf2, &_nat->bsc_configs, entry) { + if (cf1->remove) { + bsc_config_free(cf1); + _nat->num_bsc--; + } + } + return CMD_SUCCESS; } @@ -836,13 +862,14 @@ DEFUN(cfg_bsc, cfg_bsc_cmd, "bsc BSC_NR", return CMD_WARNING; } else if (bsc_nr == _nat->num_bsc) { /* allocate a new one */ - bsc = bsc_config_alloc(_nat, "unknown"); + bsc = bsc_config_alloc(_nat, DEFAULT_TOKEN_VALUE); } else bsc = bsc_config_num(_nat, bsc_nr); if (!bsc) return CMD_WARNING; + bsc->remove = false; vty->index = bsc; vty->node = NAT_BSC_NODE; @@ -855,6 +882,13 @@ DEFUN(cfg_bsc_token, cfg_bsc_token_cmd, "token TOKEN", { struct bsc_config *conf = vty->index; + if (strncmp(conf->token, DEFAULT_TOKEN_VALUE, 16) != 0 && + strncmp(conf->token, argv[0], 128) != 0) { + vty_out(vty, "updated token: %s -> %s%s", conf->token, argv[0], + VTY_NEWLINE); + conf->token_updated = true; + } + bsc_replace_string(conf, &conf->token, argv[0]); return CMD_SUCCESS; } diff --git a/openbsc/tests/vty_test_runner.py b/openbsc/tests/vty_test_runner.py index 8db0825..8e117b2 100644 --- a/openbsc/tests/vty_test_runner.py +++ b/openbsc/tests/vty_test_runner.py @@ -584,12 +584,56 @@ class TestVTYBSC(TestVTYGenericBSC): class TestVTYNAT(TestVTYGenericBSC): def vty_command(self): - return ["./src/osmo-bsc_nat/osmo-bsc_nat", "-c", + 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 separate ip to avoid interference with other tests + ip = "127.0.0.4" + self.vty.enable() + bscs1 = self.vty.command("show bsc-config") + nat_bsc_reload(self) + bscs2 = self.vty.command("show bsc-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 2") + 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) + msc = nat_msc_test(self, ip) + b0 = nat_bsc_sock_test(0, "lol") + b1 = nat_bsc_sock_test(1, "xyu") + b2 = nat_bsc_sock_test(2, "key") + + self.assertEquals("3 BSCs configured", self.vty.command("show nat bsc")) + 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 bsc-config") + # check that the reset to initial config succeeded + self.assertEquals(bscs1, bscs2) + + self.assertEquals("2 BSCs configured", self.vty.command("show nat bsc")) + self.assertTrue(1 == nat_bsc_num_con(self)) + self.assertEquals("MSC is connected: 1", self.vty.command("show msc connection")) + def testVtyTree(self): self.vty.enable() self.assertTrue(self.vty.verify('configure terminal', [''])) @@ -971,6 +1015,100 @@ def add_nat_test(suite, workdir): 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.config") + x.vty.command("end") + +def nat_msc_ip(x, ip): + x.vty.command("configure terminal") + x.vty.command("nat") + x.vty.command("msc ip " + ip) + x.vty.command("end") + +def data2str(d): + return "".join("{:02x}".format(ord(c)) for c in d) + +def nat_msc_test(x, ip, verbose = False): + msc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + msc.settimeout(32) + msc.bind((ip, 5000)) + msc.listen(5) + if (verbose): + print "MSC is ready at " + ip + while "MSC is connected: 0" == x.vty.command("show msc connection"): + conn, addr = msc.accept() + if (verbose): + print "MSC got connection from ", addr + return conn + +def ipa_send_pong(x, verbose = False): + if (verbose): + print "\tBSC -> NAT: PONG!" + x.send("\x00\x01\xfe\x01") + +def ipa_send_ping(x, verbose = False): + if (verbose): + print "\tBSC -> NAT: PING?" + x.send("\x00\x01\xfe\x00") + +def ipa_send_ack(x, verbose = False): + if (verbose): + print "\tBSC -> NAT: IPA ID ACK" + x.send("\x00\x01\xfe\x06") + +def ipa_handle_small(x, verbose = False): + s = data2str(x.recv(4)) + if "0001fe00" == s: + if (verbose): + print "\tBSC <- NAT: PING?" + ipa_send_pong(x, verbose) + elif "0001fe06" == s: + if (verbose): + print "\tBSC <- NAT: IPA ID ACK" + ipa_send_ack(x, verbose) + elif "0001fe00" == s: + if (verbose): + print "\tBSC <- NAT: PONG!" + else: + if (verbose): + print "\tBSC <- NAT: ", s + +def ipa_send_reset(x, verbose = False): + if (verbose): + print "\tBSC -> NAT: RESET" + x.send("\x00\x12\xfd\x09\x00\x03\x05\x07\x02\x42\xfe\x02\x42\xfe\x06\x00\x04\x30\x04\x01\x20") + +def ipa_send_resp(x, tk, verbose = False): + if (verbose): + print "\tBSC -> NAT: IPA ID RESP" + x.send("\x00\x07\xfe\x05\x00\x04\x01" + tk) + +def ipa_handle_resp(x, tk, verbose = False): + s = data2str(x.recv(38)) + if "0023fe040108010701020103010401050101010011" in s: + ipa_send_resp(x, tk, verbose) + 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): + bsc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + bsc.bind(('127.0.0.1' + str(nr), 0)) + bsc.connect(('127.0.0.1', 5000)) + if (verbose): + print "BSC%d " %nr + print "\tconnected to %s:%d" % bsc.getpeername() + ipa_handle_small(bsc, verbose) + ipa_handle_resp(bsc, tk, verbose) + bsc.recv(27) # MGCP msg + 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")