NAT: reload BSCs config dynamically

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
This commit is contained in:
Max 2016-04-13 11:36:39 +02:00 committed by Holger Hans Peter Freyther
parent 82f5ecde6a
commit 4936448761
3 changed files with 153 additions and 1 deletions

View File

@ -35,6 +35,7 @@
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <regex.h>
#include <stdbool.h>
#define DIR_BSC 1
#define DIR_MSC 2
@ -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;

View File

@ -39,6 +39,7 @@
#include <osmocom/sccp/sccp.h>
#include <stdlib.h>
#include <stdbool.h>
static struct bsc_nat *_nat;
@ -513,6 +514,8 @@ DEFUN(cfg_nat_include,
{
char *path;
int rc;
struct bsc_config *cf1, *cf2;
struct bsc_connection *con1, *con2;
if ('/' == argv[0][0])
bsc_replace_string(_nat, &_nat->resolved_path, argv[0]);
@ -523,13 +526,32 @@ DEFUN(cfg_nat_include,
talloc_free(path);
}
llist_for_each_entry_safe(cf1, cf2, &_nat->bsc_configs, entry) {
cf1->remove = true;
cf1->token_updated = false;
}
rc = vty_read_config_file(_nat->resolved_path, NULL);
if (rc < 0) {
vty_out(vty, "Failed to parse the config file %s: %s%s",
_nat->resolved_path, strerror(-rc), VTY_NEWLINE);
return CMD_WARNING;
}
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);
}
return CMD_SUCCESS;
}
@ -846,6 +868,7 @@ DEFUN(cfg_bsc, cfg_bsc_cmd, "bsc BSC_NR",
if (!bsc)
return CMD_WARNING;
bsc->remove = false;
vty->index = bsc;
vty->node = NAT_BSC_NODE;
@ -858,6 +881,12 @@ DEFUN(cfg_bsc_token, cfg_bsc_token_cmd, "token TOKEN",
{
struct bsc_config *conf = vty->index;
if (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;
}

View File

@ -620,12 +620,61 @@ 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 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)
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(5, "key")
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"))
def testVtyTree(self):
self.vty.enable()
self.assertTrue(self.vty.verify('configure terminal', ['']))
@ -1032,6 +1081,75 @@ def ipa_send_resp(x, tk, verbose = False):
print "\tBSC -> NAT: IPA ID RESP"
return x.send("\x00\x07\xfe\x05\x00\x04\x01" + tk)
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_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_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")