diff options
author | Harald Welte <laforge@gnumonks.org> | 2010-05-12 15:55:23 +0000 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2010-05-12 18:28:00 +0200 |
commit | 2bffac599f4a93a3113a7260955ac3ec1cd4b39c (patch) | |
tree | 93fd3da9e1dd0066053ed302fa3d4298995e3615 | |
parent | f88dc001821bb98986668b9daee98d9cb5b1cdd2 (diff) |
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.
-rw-r--r-- | openbsc/include/openbsc/gprs_ns.h | 7 | ||||
-rw-r--r-- | openbsc/src/gprs/gprs_ns.c | 225 |
2 files changed, 222 insertions, 10 deletions
diff --git a/openbsc/include/openbsc/gprs_ns.h b/openbsc/include/openbsc/gprs_ns.h index c74546a8..60051d13 100644 --- a/openbsc/include/openbsc/gprs_ns.h +++ b/openbsc/include/openbsc/gprs_ns.h @@ -140,7 +140,8 @@ struct gprs_nsvc { enum nsvc_timer_mode timer_mode; int alive_retries; - int remote_end_is_sgsn; + unsigned int remote_end_is_sgsn:1; + unsigned int persistent:1; union { 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 sockaddr_in *dest, uint16_t nsei, uint16_t nsvci); + +/* Add NS-specific VTY stuff */ +int gprs_ns_vty_init(struct gprs_ns_inst *nsi); + #endif diff --git a/openbsc/src/gprs/gprs_ns.c b/openbsc/src/gprs/gprs_ns.c index 3d672fb8..ed642103 100644 --- a/openbsc/src/gprs/gprs_ns.c +++ b/openbsc/src/gprs/gprs_ns.c @@ -131,6 +131,14 @@ static struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci) 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, 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.ip.bts_addr = *saddr; fake_nsvc.state = NSE_S_ALIVE; -#if 0 return gprs_ns_tx_status(&fake_nsvc, NS_CAUSE_PDU_INCOMP_PSTATE, 0, 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, 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) { case NS_PDUT_ALIVE: - /* remote end inquires whether we're still alive, - * we need to respond with ALIVE_ACK */ - rc = gprs_ns_tx_alive_ack(nsvc); + /* If we're dead and blocked and suddenly receive a + * NS-ALIVE out of the blue, we might have been re-started + * 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; case NS_PDUT_ALIVE_ACK: /* stop Tns-alive */ @@ -844,3 +851,203 @@ struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi, 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; +} |