mirror of https://gerrit.osmocom.org/libosmocore
gprs_ns2: implement BLOCK/UNBLOCK of a NSVC by vty
The vty should be able to block or unblock a specific NSVC. Further more this case is special for the UNITDATA as those can be still received until the other side response to the BLOCK PDU. Related: OS#4939 Change-Id: Ic0ce3c5fabc8644cc1ee71a8f6dd783fadf7b84d
This commit is contained in:
parent
fd96dc5343
commit
47afc424c3
|
@ -318,6 +318,8 @@ int gprs_ns2_vc_force_unconfigured(struct gprs_ns2_vc *nsvc);
|
|||
int gprs_ns2_vc_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp);
|
||||
int gprs_ns2_vc_is_alive(struct gprs_ns2_vc *nsvc);
|
||||
int gprs_ns2_vc_is_unblocked(struct gprs_ns2_vc *nsvc);
|
||||
int ns2_vc_block(struct gprs_ns2_vc *nsvc);
|
||||
int ns2_vc_unblock(struct gprs_ns2_vc *nsvc);
|
||||
|
||||
/* nse */
|
||||
void ns2_nse_notify_unblocked(struct gprs_ns2_vc *nsvc, bool unblocked);
|
||||
|
|
|
@ -59,6 +59,10 @@ struct gprs_ns2_vc_priv {
|
|||
* It can change during runtime. The side which blocks an unblocked side.*/
|
||||
bool initiate_block;
|
||||
bool initiate_reset;
|
||||
/* if blocked by O&M/vty */
|
||||
bool om_blocked;
|
||||
/* if unitdata is forwarded to the user */
|
||||
bool accept_unitdata;
|
||||
|
||||
/* the alive counter is present in all states */
|
||||
struct {
|
||||
|
@ -111,7 +115,9 @@ enum gprs_ns2_vc_event {
|
|||
|
||||
GPRS_NS2_EV_UNITDATA,
|
||||
|
||||
GPRS_NS2_EV_FORCE_UNCONFIGURED, /* called via vty for tests */
|
||||
GPRS_NS2_EV_FORCE_UNCONFIGURED, /* called via vty for tests */
|
||||
GPRS_NS2_EV_REQ_OM_BLOCK, /* vty cmd: block */
|
||||
GPRS_NS2_EV_REQ_OM_UNBLOCK, /* vty cmd: unblock*/
|
||||
};
|
||||
|
||||
static const struct value_string gprs_ns2_vc_event_names[] = {
|
||||
|
@ -127,6 +133,8 @@ static const struct value_string gprs_ns2_vc_event_names[] = {
|
|||
{ GPRS_NS2_EV_STATUS, "STATUS" },
|
||||
{ GPRS_NS2_EV_UNITDATA, "UNITDATA" },
|
||||
{ GPRS_NS2_EV_FORCE_UNCONFIGURED, "FORCE_UNCONFIGURED" },
|
||||
{ GPRS_NS2_EV_REQ_OM_BLOCK, "REQ-O&M-BLOCK"},
|
||||
{ GPRS_NS2_EV_REQ_OM_UNBLOCK, "REQ-O&M-UNBLOCK"},
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
@ -245,6 +253,7 @@ static void gprs_ns2_st_reset_onenter(struct osmo_fsm_inst *fi, uint32_t old_sta
|
|||
if (old_state != GPRS_NS2_ST_RESET)
|
||||
priv->N = 0;
|
||||
|
||||
priv->accept_unitdata = false;
|
||||
if (priv->initiate_reset)
|
||||
ns2_tx_reset(priv->nsvc, NS_CAUSE_OM_INTERVENTION);
|
||||
|
||||
|
@ -283,8 +292,16 @@ static void gprs_ns2_st_blocked_onenter(struct osmo_fsm_inst *fi, uint32_t old_s
|
|||
if (old_state != GPRS_NS2_ST_BLOCKED)
|
||||
priv->N = 0;
|
||||
|
||||
if (priv->initiate_block)
|
||||
if (priv->om_blocked) {
|
||||
/* we are already blocked after a RESET */
|
||||
if (old_state == GPRS_NS2_ST_RESET) {
|
||||
osmo_timer_del(&fi->timer);
|
||||
} else {
|
||||
ns2_tx_block(priv->nsvc, NS_CAUSE_OM_INTERVENTION);
|
||||
}
|
||||
} else if (priv->initiate_block) {
|
||||
ns2_tx_unblock(priv->nsvc);
|
||||
}
|
||||
|
||||
start_test_procedure(priv);
|
||||
}
|
||||
|
@ -293,7 +310,24 @@ static void gprs_ns2_st_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *
|
|||
{
|
||||
struct gprs_ns2_vc_priv *priv = fi->priv;
|
||||
|
||||
if (priv->initiate_block) {
|
||||
if (priv->om_blocked) {
|
||||
switch (event) {
|
||||
case GPRS_NS2_EV_BLOCK_ACK:
|
||||
priv->accept_unitdata = false;
|
||||
osmo_timer_del(&fi->timer);
|
||||
break;
|
||||
case GPRS_NS2_EV_BLOCK:
|
||||
priv->accept_unitdata = false;
|
||||
ns2_tx_block_ack(priv->nsvc);
|
||||
osmo_timer_del(&fi->timer);
|
||||
break;
|
||||
case GPRS_NS2_EV_UNBLOCK:
|
||||
priv->accept_unitdata = false;
|
||||
ns2_tx_block(priv->nsvc, NS_CAUSE_OM_INTERVENTION);
|
||||
osmo_timer_add(&fi->timer);
|
||||
break;
|
||||
}
|
||||
} else if (priv->initiate_block) {
|
||||
switch (event) {
|
||||
case GPRS_NS2_EV_BLOCK:
|
||||
/* TODO: BLOCK is a UNBLOCK_NACK */
|
||||
|
@ -303,6 +337,7 @@ static void gprs_ns2_st_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *
|
|||
ns2_tx_unblock_ack(priv->nsvc);
|
||||
/* fall through */
|
||||
case GPRS_NS2_EV_UNBLOCK_ACK:
|
||||
priv->accept_unitdata = true;
|
||||
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_UNBLOCKED,
|
||||
0, NS_TOUT_TNS_TEST);
|
||||
break;
|
||||
|
@ -325,6 +360,7 @@ static void gprs_ns2_st_unblocked_on_enter(struct osmo_fsm_inst *fi, uint32_t ol
|
|||
struct gprs_ns2_vc *nsvc = priv->nsvc;
|
||||
struct gprs_ns2_nse *nse = nsvc->nse;
|
||||
|
||||
priv->accept_unitdata = true;
|
||||
ns2_nse_notify_unblocked(nsvc, true);
|
||||
ns2_prim_status_ind(nse, nsvc, 0, NS_AFF_CAUSE_VC_RECOVERY);
|
||||
}
|
||||
|
@ -446,10 +482,19 @@ static int gprs_ns2_vc_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
|||
case GPRS_NS2_ST_BLOCKED:
|
||||
if (priv->initiate_block) {
|
||||
priv->N++;
|
||||
if (priv->N <= nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES]) {
|
||||
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
|
||||
if (priv->om_blocked) {
|
||||
if (priv->N <= nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES]) {
|
||||
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
|
||||
} else {
|
||||
/* 7.2 stop accepting data when BLOCK PDU not responded */
|
||||
priv->accept_unitdata = false;
|
||||
}
|
||||
} else {
|
||||
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], 0);
|
||||
if (priv->N <= nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES]) {
|
||||
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
|
||||
} else {
|
||||
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -550,7 +595,7 @@ static void gprs_ns2_vc_fsm_allstate_action(struct osmo_fsm_inst *fi,
|
|||
switch (fi->state) {
|
||||
case GPRS_NS2_ST_BLOCKED:
|
||||
/* 7.2.1: the BLOCKED_ACK might be lost */
|
||||
if (priv->initiate_block) {
|
||||
if (priv->accept_unitdata) {
|
||||
gprs_ns2_recv_unitdata(fi, msg);
|
||||
return;
|
||||
}
|
||||
|
@ -576,6 +621,20 @@ static void gprs_ns2_vc_fsm_allstate_action(struct osmo_fsm_inst *fi,
|
|||
return;
|
||||
}
|
||||
break;
|
||||
case GPRS_NS2_EV_REQ_OM_BLOCK:
|
||||
/* vty cmd: block */
|
||||
priv->initiate_block = true;
|
||||
priv->om_blocked = true;
|
||||
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
|
||||
break;
|
||||
case GPRS_NS2_EV_REQ_OM_UNBLOCK:
|
||||
/* vty cmd: unblock*/
|
||||
if (!priv->om_blocked)
|
||||
return;
|
||||
priv->om_blocked = false;
|
||||
if (fi->state == GPRS_NS2_ST_BLOCKED)
|
||||
osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -595,7 +654,9 @@ static struct osmo_fsm gprs_ns2_vc_fsm = {
|
|||
S(GPRS_NS2_EV_RESET) |
|
||||
S(GPRS_NS2_EV_ALIVE) |
|
||||
S(GPRS_NS2_EV_ALIVE_ACK) |
|
||||
S(GPRS_NS2_EV_FORCE_UNCONFIGURED),
|
||||
S(GPRS_NS2_EV_FORCE_UNCONFIGURED) |
|
||||
S(GPRS_NS2_EV_REQ_OM_BLOCK) |
|
||||
S(GPRS_NS2_EV_REQ_OM_UNBLOCK),
|
||||
.allstate_action = gprs_ns2_vc_fsm_allstate_action,
|
||||
.cleanup = gprs_ns2_vc_fsm_clean,
|
||||
.timer_cb = gprs_ns2_vc_fsm_timer_cb,
|
||||
|
@ -653,6 +714,22 @@ int gprs_ns2_vc_force_unconfigured(struct gprs_ns2_vc *nsvc)
|
|||
return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_FORCE_UNCONFIGURED, NULL);
|
||||
}
|
||||
|
||||
/*! Block a NS-VC.
|
||||
* \param nsvc the virtual circuit
|
||||
* \return 0 on success; negative on error */
|
||||
int ns2_vc_block(struct gprs_ns2_vc *nsvc)
|
||||
{
|
||||
return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_REQ_OM_BLOCK, NULL);
|
||||
}
|
||||
|
||||
/*! Unblock a NS-VC.
|
||||
* \param nsvc the virtual circuit
|
||||
* \return 0 on success; negative on error */
|
||||
int ns2_vc_unblock(struct gprs_ns2_vc *nsvc)
|
||||
{
|
||||
return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_REQ_OM_UNBLOCK, NULL);
|
||||
}
|
||||
|
||||
/*! entry point for messages from the driver/VL
|
||||
* \param nsvc virtual circuit on which the message was received
|
||||
* \param msg message that was received
|
||||
|
|
|
@ -1503,6 +1503,33 @@ DEFUN_HIDDEN(nsvc_force_unconf, nsvc_force_unconf_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(nsvc_block, nsvc_block_cmd,
|
||||
"nsvc <0-65535> (block|unblock)",
|
||||
"NS Virtual Connection\n"
|
||||
NSVCI_STR
|
||||
"Block a NSVC. As cause code O&M intervention will be used.\n"
|
||||
"Unblock a NSVC. As cause code O&M intervention will be used.\n")
|
||||
{
|
||||
struct gprs_ns2_inst *nsi = vty_nsi;
|
||||
struct gprs_ns2_vc *nsvc;
|
||||
|
||||
uint16_t id = atoi(argv[0]);
|
||||
|
||||
nsvc = gprs_ns2_nsvc_by_nsvci(nsi, id);
|
||||
if (!nsvc) {
|
||||
vty_out(vty, "Could not find NSVCI %05u%s", id, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "block")) {
|
||||
ns2_vc_block(nsvc);
|
||||
} else {
|
||||
ns2_vc_unblock(nsvc);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void log_set_nse_filter(struct log_target *target,
|
||||
struct gprs_ns2_nse *nse)
|
||||
{
|
||||
|
@ -1608,6 +1635,7 @@ int gprs_ns2_vty2_init(struct gprs_ns2_inst *nsi)
|
|||
install_lib_element_ve(&logging_fltr_nsvc_cmd);
|
||||
|
||||
install_lib_element(ENABLE_NODE, &nsvc_force_unconf_cmd);
|
||||
install_lib_element(ENABLE_NODE, &nsvc_block_cmd);
|
||||
|
||||
install_lib_element(CFG_LOG_NODE, &logging_fltr_nse_cmd);
|
||||
install_lib_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
@ -44,6 +45,34 @@ static int ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct msgb *get_pdu(struct gprs_ns2_vc_bind *bind, enum ns_pdu_type pdu_type)
|
||||
{
|
||||
struct gprs_ns_hdr *nsh;
|
||||
struct osmo_wqueue *queue = bind->priv;
|
||||
|
||||
while (!llist_empty(&queue->msg_queue)) {
|
||||
struct msgb *msg = msgb_dequeue(&queue->msg_queue);
|
||||
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
||||
if (nsh->pdu_type == pdu_type)
|
||||
return msg;
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool find_pdu(struct gprs_ns2_vc_bind *bind, enum ns_pdu_type pdu_type)
|
||||
{
|
||||
struct msgb *msg;
|
||||
msg = get_pdu(bind, pdu_type);
|
||||
if (msg) {
|
||||
msgb_free(msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void clear_pdus(struct gprs_ns2_vc_bind *bind)
|
||||
{
|
||||
struct osmo_wqueue *queue = bind->priv;
|
||||
|
@ -147,6 +176,62 @@ void test_nse_transfer_cap(void *ctx)
|
|||
|
||||
}
|
||||
|
||||
/* setup NSE with 2x NSVCs.
|
||||
* block 1x NSVC
|
||||
* unblock 1x NSVC*/
|
||||
void test_block_unblock_nsvc(void *ctx)
|
||||
{
|
||||
struct gprs_ns2_inst *nsi;
|
||||
struct gprs_ns2_vc_bind *bind[2];
|
||||
struct gprs_ns2_nse *nse;
|
||||
struct gprs_ns2_vc *nsvc[2];
|
||||
struct gprs_ns_hdr *nsh;
|
||||
struct msgb *msg;
|
||||
char idbuf[32];
|
||||
|
||||
printf("--- Testing NSE block unblock nsvc\n");
|
||||
printf("---- Create NSE + Binds\n");
|
||||
nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
|
||||
bind[0] = dummy_bind(nsi, "bblock1");
|
||||
bind[1] = dummy_bind(nsi, "bblock2");
|
||||
nse = gprs_ns2_create_nse(nsi, 1001, GPRS_NS2_LL_UDP, NS2_DIALECT_STATIC_RESETBLOCK);
|
||||
OSMO_ASSERT(nse);
|
||||
|
||||
for (int i=0; i<2; i++) {
|
||||
printf("---- Create NSVC[i]\n");
|
||||
snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i);
|
||||
nsvc[i] = ns2_vc_alloc(bind[i], nse, false, NS2_VC_MODE_BLOCKRESET, idbuf);
|
||||
OSMO_ASSERT(nsvc[i]);
|
||||
nsvc[i]->fi->state = 3; /* HACK: 3 = GPRS_NS2_ST_UNBLOCKED */
|
||||
/* ensure the fi->state works correct */
|
||||
OSMO_ASSERT(gprs_ns2_vc_is_unblocked(nsvc[i]));
|
||||
ns2_nse_notify_unblocked(nsvc[i], true);
|
||||
}
|
||||
|
||||
/* both nsvcs are unblocked and alive. Let's block it. */
|
||||
OSMO_ASSERT(!find_pdu(bind[0], NS_PDUT_BLOCK));
|
||||
clear_pdus(bind[0]);
|
||||
ns2_vc_block(nsvc[0]);
|
||||
OSMO_ASSERT(find_pdu(bind[0], NS_PDUT_BLOCK));
|
||||
/* state == BLOCKED */
|
||||
clear_pdus(bind[0]);
|
||||
|
||||
/* now unblocking it */
|
||||
ns2_vc_unblock(nsvc[0]);
|
||||
OSMO_ASSERT(find_pdu(bind[0], NS_PDUT_UNBLOCK));
|
||||
clear_pdus(bind[0]);
|
||||
|
||||
msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM, "test_unblock");
|
||||
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
||||
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
||||
nsh->pdu_type = NS_PDUT_UNBLOCK_ACK;
|
||||
ns2_recv_vc(nsvc[0], msg);
|
||||
|
||||
OSMO_ASSERT(gprs_ns2_vc_is_unblocked(nsvc[0]));
|
||||
gprs_ns2_free(nsi);
|
||||
printf("--- Finish NSE block unblock nsvc\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *ctx = talloc_named_const(NULL, 0, "gprs_ns2_test");
|
||||
|
@ -159,6 +244,7 @@ int main(int argc, char **argv)
|
|||
|
||||
printf("===== NS2 protocol test START\n");
|
||||
test_nse_transfer_cap(ctx);
|
||||
test_block_unblock_nsvc(ctx);
|
||||
printf("===== NS2 protocol test END\n\n");
|
||||
|
||||
talloc_free(ctx);
|
||||
|
|
|
@ -6,5 +6,10 @@
|
|||
---- Test with NSVC[2]
|
||||
---- Test with NSVC[1] removed
|
||||
--- Finish NSE transfer cap
|
||||
--- Testing NSE block unblock nsvc
|
||||
---- Create NSE + Binds
|
||||
---- Create NSVC[i]
|
||||
---- Create NSVC[i]
|
||||
--- Finish NSE block unblock nsvc
|
||||
===== NS2 protocol test END
|
||||
|
||||
|
|
Loading…
Reference in New Issue