NS: Add support for persistent NS-VC configuration
With persistent NS-VC configuration (configured through VTY), we can respond properly to BSS with a somewhat strange NS implementation Such as the BSplus. It enables us to respond with a proper NS-RESET (including NSVCI/NSEI) when receiving a NS-ALIVE or other PDU for a BLOCKED/DEAD NS-VC after our end of the connection is rebooted.
This commit is contained in:
parent
f6d67c04ee
commit
1194b584be
|
@ -140,7 +140,8 @@ struct gprs_nsvc {
|
||||||
enum nsvc_timer_mode timer_mode;
|
enum nsvc_timer_mode timer_mode;
|
||||||
int alive_retries;
|
int alive_retries;
|
||||||
|
|
||||||
int remote_end_is_sgsn;
|
unsigned int remote_end_is_sgsn:1;
|
||||||
|
unsigned int persistent:1;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
|
@ -178,4 +179,8 @@ int nsip_listen(struct gprs_ns_inst *nsi, uint16_t udp_port);
|
||||||
struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi,
|
struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi,
|
||||||
struct sockaddr_in *dest, uint16_t nsei,
|
struct sockaddr_in *dest, uint16_t nsei,
|
||||||
uint16_t nsvci);
|
uint16_t nsvci);
|
||||||
|
|
||||||
|
/* Add NS-specific VTY stuff */
|
||||||
|
int gprs_ns_vty_init(struct gprs_ns_inst *nsi);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -109,6 +109,7 @@ enum node_type {
|
||||||
MGCP_NODE,
|
MGCP_NODE,
|
||||||
GBPROXY_NODE,
|
GBPROXY_NODE,
|
||||||
SGSN_NODE,
|
SGSN_NODE,
|
||||||
|
NS_NODE,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Node which has some commands and prompt string and configuration
|
/* Node which has some commands and prompt string and configuration
|
||||||
|
|
|
@ -105,11 +105,6 @@ int main(int argc, char **argv)
|
||||||
log_set_all_filter(stderr_target, 1);
|
log_set_all_filter(stderr_target, 1);
|
||||||
|
|
||||||
telnet_init(&dummy_network, 4246);
|
telnet_init(&dummy_network, 4246);
|
||||||
rc = gbproxy_parse_config(config_file, &gbcfg);
|
|
||||||
if (rc < 0) {
|
|
||||||
LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n");
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb);
|
bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb);
|
||||||
if (!bssgp_nsi) {
|
if (!bssgp_nsi) {
|
||||||
|
@ -117,7 +112,15 @@ int main(int argc, char **argv)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
gbcfg.nsi = bssgp_nsi;
|
gbcfg.nsi = bssgp_nsi;
|
||||||
|
gprs_ns_vty_init(bssgp_nsi);
|
||||||
register_signal_handler(SS_NS, &gbprox_signal, NULL);
|
register_signal_handler(SS_NS, &gbprox_signal, NULL);
|
||||||
|
|
||||||
|
rc = gbproxy_parse_config(config_file, &gbcfg);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
nsip_listen(bssgp_nsi, gbcfg.nsip_listen_port);
|
nsip_listen(bssgp_nsi, gbcfg.nsip_listen_port);
|
||||||
|
|
||||||
/* 'establish' the outgoing connection to the SGSN */
|
/* 'establish' the outgoing connection to the SGSN */
|
||||||
|
|
|
@ -70,29 +70,6 @@ static int config_write_gbproxy(struct vty *vty)
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN(show_ns, show_ns_cmd, "show ns",
|
|
||||||
SHOW_STR "Display information about the NS protocol")
|
|
||||||
{
|
|
||||||
/* FIXME: iterate over list of NS-VC's and display their state */
|
|
||||||
struct gprs_ns_inst *nsi = g_cfg->nsi;
|
|
||||||
struct gprs_nsvc *nsvc;
|
|
||||||
|
|
||||||
llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
|
|
||||||
vty_out(vty, "NSEI %5u, NS-VC %5u, %s-mode, %s %s%s",
|
|
||||||
nsvc->nsei, nsvc->nsvci,
|
|
||||||
nsvc->remote_end_is_sgsn ? "BSS" : "SGSN",
|
|
||||||
nsvc->state & NSE_S_ALIVE ? "ALIVE" : "DEAD",
|
|
||||||
nsvc->state & NSE_S_BLOCKED ? "BLOCKED" : "UNBLOCKED",
|
|
||||||
VTY_NEWLINE);
|
|
||||||
if (nsvc->nsi->ll == GPRS_NS_LL_UDP)
|
|
||||||
vty_out(vty, " remote peer %s:%u%s",
|
|
||||||
inet_ntoa(nsvc->ip.bts_addr.sin_addr),
|
|
||||||
ntohs(nsvc->ip.bts_addr.sin_port), VTY_NEWLINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_gbproxy,
|
DEFUN(cfg_gbproxy,
|
||||||
cfg_gbproxy_cmd,
|
cfg_gbproxy_cmd,
|
||||||
"gbproxy",
|
"gbproxy",
|
||||||
|
@ -173,10 +150,8 @@ DEFUN(cfg_nsip_sgsn_nsvci,
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int gbproxy_vty_init(void)
|
int gbproxy_vty_init(void)
|
||||||
{
|
{
|
||||||
install_element(VIEW_NODE, &show_ns_cmd);
|
|
||||||
install_element(VIEW_NODE, &show_gbproxy_cmd);
|
install_element(VIEW_NODE, &show_gbproxy_cmd);
|
||||||
|
|
||||||
install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
|
install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
|
||||||
|
|
|
@ -131,6 +131,14 @@ static struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci)
|
||||||
return nsvc;
|
return nsvc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nsvc_delete(struct gprs_nsvc *nsvc)
|
||||||
|
{
|
||||||
|
if (bsc_timer_pending(&nsvc->timer))
|
||||||
|
bsc_del_timer(&nsvc->timer);
|
||||||
|
llist_del(&nsvc->list);
|
||||||
|
talloc_free(nsvc);
|
||||||
|
}
|
||||||
|
|
||||||
static void ns_dispatch_signal(struct gprs_nsvc *nsvc, unsigned int signal,
|
static void ns_dispatch_signal(struct gprs_nsvc *nsvc, unsigned int signal,
|
||||||
uint8_t cause)
|
uint8_t cause)
|
||||||
{
|
{
|
||||||
|
@ -592,15 +600,9 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
|
||||||
fake_nsvc.nsi = nsi;
|
fake_nsvc.nsi = nsi;
|
||||||
fake_nsvc.ip.bts_addr = *saddr;
|
fake_nsvc.ip.bts_addr = *saddr;
|
||||||
fake_nsvc.state = NSE_S_ALIVE;
|
fake_nsvc.state = NSE_S_ALIVE;
|
||||||
#if 0
|
|
||||||
return gprs_ns_tx_status(&fake_nsvc,
|
return gprs_ns_tx_status(&fake_nsvc,
|
||||||
NS_CAUSE_PDU_INCOMP_PSTATE, 0,
|
NS_CAUSE_PDU_INCOMP_PSTATE, 0,
|
||||||
msg);
|
msg);
|
||||||
#else
|
|
||||||
/* BS+ Gb implementation ignores STATUS, so we try
|
|
||||||
* our luck with a RESET incompatible with the spec */
|
|
||||||
return gprs_ns_tx_simple(&fake_nsvc, NS_PDUT_RESET);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data,
|
rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data,
|
||||||
msgb_l2len(msg), 0, 0);
|
msgb_l2len(msg), 0, 0);
|
||||||
|
@ -628,9 +630,14 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
|
||||||
|
|
||||||
switch (nsh->pdu_type) {
|
switch (nsh->pdu_type) {
|
||||||
case NS_PDUT_ALIVE:
|
case NS_PDUT_ALIVE:
|
||||||
/* remote end inquires whether we're still alive,
|
/* If we're dead and blocked and suddenly receive a
|
||||||
* we need to respond with ALIVE_ACK */
|
* NS-ALIVE out of the blue, we might have been re-started
|
||||||
rc = gprs_ns_tx_alive_ack(nsvc);
|
* and should send a NS-RESET to make sure everything recovers
|
||||||
|
* fine. */
|
||||||
|
if (nsvc->state == NSE_S_BLOCKED)
|
||||||
|
rc = gprs_ns_tx_reset(nsvc, NS_CAUSE_PDU_INCOMP_PSTATE);
|
||||||
|
else
|
||||||
|
rc = gprs_ns_tx_alive_ack(nsvc);
|
||||||
break;
|
break;
|
||||||
case NS_PDUT_ALIVE_ACK:
|
case NS_PDUT_ALIVE_ACK:
|
||||||
/* stop Tns-alive */
|
/* stop Tns-alive */
|
||||||
|
@ -844,3 +851,203 @@ struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi,
|
||||||
|
|
||||||
return nsvc;
|
return nsvc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <vty/vty.h>
|
||||||
|
#include <vty/command.h>
|
||||||
|
|
||||||
|
static struct gprs_ns_inst *vty_nsi = NULL;
|
||||||
|
|
||||||
|
static struct cmd_node ns_node = {
|
||||||
|
NS_NODE,
|
||||||
|
"%s(ns)#",
|
||||||
|
1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int config_write_ns(struct vty *vty)
|
||||||
|
{
|
||||||
|
struct gprs_nsvc *nsvc;
|
||||||
|
|
||||||
|
vty_out(vty, "ns%s", VTY_NEWLINE);
|
||||||
|
|
||||||
|
llist_for_each_entry(nsvc, &vty_nsi->gprs_nsvcs, list) {
|
||||||
|
if (!nsvc->persistent)
|
||||||
|
continue;
|
||||||
|
vty_out(vty, " nse %u nsvci %u%s",
|
||||||
|
nsvc->nsei, nsvc->nsvci, VTY_NEWLINE);
|
||||||
|
vty_out(vty, " nse %u remote-role %s%s",
|
||||||
|
nsvc->nsei, nsvc->remote_end_is_sgsn ? "sgsn" : "bss",
|
||||||
|
VTY_NEWLINE);
|
||||||
|
if (nsvc->nsi->ll == GPRS_NS_LL_UDP) {
|
||||||
|
vty_out(vty, " nse %u remote-ip %s%s",
|
||||||
|
nsvc->nsei,
|
||||||
|
inet_ntoa(nsvc->ip.bts_addr.sin_addr),
|
||||||
|
VTY_NEWLINE);
|
||||||
|
vty_out(vty, " nse %u remote-port %u%s",
|
||||||
|
nsvc->nsei, ntohs(nsvc->ip.bts_addr.sin_port),
|
||||||
|
VTY_NEWLINE);
|
||||||
|
}
|
||||||
|
vty_out(vty, "%s", VTY_NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_ns, cfg_ns_cmd,
|
||||||
|
"ns",
|
||||||
|
"Configure the GPRS Network Service")
|
||||||
|
{
|
||||||
|
vty->node = NS_NODE;
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(show_ns, show_ns_cmd, "show ns",
|
||||||
|
SHOW_STR "Display information about the NS protocol")
|
||||||
|
{
|
||||||
|
struct gprs_ns_inst *nsi = vty_nsi;
|
||||||
|
struct gprs_nsvc *nsvc;
|
||||||
|
|
||||||
|
llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
|
||||||
|
vty_out(vty, "NSEI %5u, NS-VC %5u, Remote: %-4s, %5s %9s",
|
||||||
|
nsvc->nsei, nsvc->nsvci,
|
||||||
|
nsvc->remote_end_is_sgsn ? "SGSN" : "BSS",
|
||||||
|
nsvc->state & NSE_S_ALIVE ? "ALIVE" : "DEAD",
|
||||||
|
nsvc->state & NSE_S_BLOCKED ? "BLOCKED" : "UNBLOCKED");
|
||||||
|
if (nsvc->nsi->ll == GPRS_NS_LL_UDP)
|
||||||
|
vty_out(vty, ", %15s:%u",
|
||||||
|
inet_ntoa(nsvc->ip.bts_addr.sin_addr),
|
||||||
|
ntohs(nsvc->ip.bts_addr.sin_port));
|
||||||
|
vty_out(vty, "%s", VTY_NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define NSE_CMD_STR "NS Entity\n" "NS Entity ID (NSEI)\n"
|
||||||
|
|
||||||
|
DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,
|
||||||
|
"nse <0-65535> nsvci <0-65534>",
|
||||||
|
NSE_CMD_STR
|
||||||
|
"NS Virtual Connection\n"
|
||||||
|
"NS Virtual Connection ID (NSVCI)\n"
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint16_t nsei = atoi(argv[0]);
|
||||||
|
uint16_t nsvci = atoi(argv[1]);
|
||||||
|
struct gprs_nsvc *nsvc;
|
||||||
|
|
||||||
|
nsvc = nsvc_by_nsei(vty_nsi, nsei);
|
||||||
|
if (!nsvc) {
|
||||||
|
nsvc = nsvc_create(vty_nsi, nsvci);
|
||||||
|
nsvc->nsei = nsei;
|
||||||
|
}
|
||||||
|
nsvc->nsvci = nsvci;
|
||||||
|
/* All NSVCs that are explicitly configured by VTY are
|
||||||
|
* marked as persistent so we can write them to the config
|
||||||
|
* file at some later point */
|
||||||
|
nsvc->persistent = 1;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd,
|
||||||
|
"nse <0-65535> remote-ip A.B.C.D",
|
||||||
|
NSE_CMD_STR
|
||||||
|
"Remote IP Address\n"
|
||||||
|
"Remote IP Address\n")
|
||||||
|
{
|
||||||
|
uint16_t nsei = atoi(argv[0]);
|
||||||
|
struct gprs_nsvc *nsvc;
|
||||||
|
|
||||||
|
nsvc = nsvc_by_nsei(vty_nsi, nsei);
|
||||||
|
if (!nsvc) {
|
||||||
|
vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
inet_aton(argv[1], &nsvc->ip.bts_addr.sin_addr);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd,
|
||||||
|
"nse <0-65535> remote-port <0-65535>",
|
||||||
|
NSE_CMD_STR
|
||||||
|
"Remote UDP Port\n"
|
||||||
|
"Remote UDP Port Number\n")
|
||||||
|
{
|
||||||
|
uint16_t nsei = atoi(argv[0]);
|
||||||
|
uint16_t port = atoi(argv[1]);
|
||||||
|
struct gprs_nsvc *nsvc;
|
||||||
|
|
||||||
|
nsvc = nsvc_by_nsei(vty_nsi, nsei);
|
||||||
|
if (!nsvc) {
|
||||||
|
vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsvc->ip.bts_addr.sin_port = htons(port);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd,
|
||||||
|
"nse <0-65535> remote-role (sgsn|bss)",
|
||||||
|
NSE_CMD_STR
|
||||||
|
"Remote NSE Role\n"
|
||||||
|
"Remote Peer is SGSN\n"
|
||||||
|
"Remote Peer is BSS\n")
|
||||||
|
{
|
||||||
|
uint16_t nsei = atoi(argv[0]);
|
||||||
|
struct gprs_nsvc *nsvc;
|
||||||
|
|
||||||
|
nsvc = nsvc_by_nsei(vty_nsi, nsei);
|
||||||
|
if (!nsvc) {
|
||||||
|
vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(argv[1], "sgsn"))
|
||||||
|
nsvc->remote_end_is_sgsn = 1;
|
||||||
|
else
|
||||||
|
nsvc->remote_end_is_sgsn = 0;
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_no_nse, cfg_no_nse_cmd,
|
||||||
|
"no nse <0-65535>",
|
||||||
|
"Delete NS Entity\n"
|
||||||
|
"Delete " NSE_CMD_STR)
|
||||||
|
{
|
||||||
|
uint16_t nsei = atoi(argv[0]);
|
||||||
|
struct gprs_nsvc *nsvc;
|
||||||
|
|
||||||
|
nsvc = nsvc_by_nsei(vty_nsi, nsei);
|
||||||
|
if (!nsvc) {
|
||||||
|
vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsvc_delete(nsvc);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gprs_ns_vty_init(struct gprs_ns_inst *nsi)
|
||||||
|
{
|
||||||
|
vty_nsi = nsi;
|
||||||
|
|
||||||
|
install_element(VIEW_NODE, &show_ns_cmd);
|
||||||
|
|
||||||
|
install_element(CONFIG_NODE, &cfg_ns_cmd);
|
||||||
|
install_node(&ns_node, config_write_ns);
|
||||||
|
install_default(NS_NODE);
|
||||||
|
install_element(NS_NODE, &cfg_nse_nsvci_cmd);
|
||||||
|
install_element(NS_NODE, &cfg_nse_remoteip_cmd);
|
||||||
|
install_element(NS_NODE, &cfg_nse_remoteport_cmd);
|
||||||
|
install_element(NS_NODE, &cfg_nse_remoterole_cmd);
|
||||||
|
install_element(NS_NODE, &cfg_no_nse_cmd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ line vty
|
||||||
!
|
!
|
||||||
gbproxy
|
gbproxy
|
||||||
nsip bss local port 23000
|
nsip bss local port 23000
|
||||||
nsip sgsn remote ip 192.168.100.239
|
nsip sgsn remote ip 127.0.0.1
|
||||||
nsip sgsn remote port 23000
|
nsip sgsn remote port 7777
|
||||||
nsip sgsn nsei 1
|
nsip sgsn nsei 101
|
||||||
nsip sgsn nsvci 11
|
nsip sgsn nsvci 101
|
||||||
|
|
|
@ -2365,6 +2365,7 @@ gDEFUN(config_exit,
|
||||||
case MGCP_NODE:
|
case MGCP_NODE:
|
||||||
case GBPROXY_NODE:
|
case GBPROXY_NODE:
|
||||||
case SGSN_NODE:
|
case SGSN_NODE:
|
||||||
|
case NS_NODE:
|
||||||
vty->node = CONFIG_NODE;
|
vty->node = CONFIG_NODE;
|
||||||
vty->index = NULL;
|
vty->index = NULL;
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Reference in New Issue