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:
parent
82f5ecde6a
commit
4936448761
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue