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:
Harald Welte 2010-05-12 15:55:23 +00:00
parent f6d67c04ee
commit 1194b584be
7 changed files with 236 additions and 44 deletions

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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: