ns: Handle procedure changes for UDP in TS 48.016
diff mbox

Message ID 1451931067-7511-1-git-send-email-jerlbeck@sysmocom.de
State New
Headers show

Commit Message

Jacob Erlbeck Jan. 4, 2016, 6:11 p.m. UTC
In TS 48.016 the network service procedure change depending on
the network link type being used. This mainly affects the reset
and test procedures. Not obeying these changes makes it impossible
to set up NSVC with equipment relying on them.

This commits adds the possibility to be compliant to either
specification by adding a configuration option to select the
compliance mode explicitly for each NSE.

The TS 48.016 mode mainly gains conformance, but hinders dynamic NSCV
establishment since there aren't any RESET messages. Therefore that
mode is not enabled by default.

Note that dynamic NSVC establishment as defined by TS 48.016 is not
being implemented, since it does not work with routers using network
address translation (NAT).

New VTY commands (config-ns node):

 - nse <0-65535> compliance ts08.16   Just use UDP instead of FR
 - nse <0-65535> compliance ts48.016  Use the modified procedures

Sponsored-by: On-Waves ehf
---
 include/osmocom/gprs/gprs_ns.h |  8 ++++++
 src/gb/gprs_ns.c               | 62 ++++++++++++++++++++++++++++++++++++------
 src/gb/gprs_ns_vty.c           | 31 +++++++++++++++++++++
 3 files changed, 93 insertions(+), 8 deletions(-)

Patch
diff mbox

diff --git a/include/osmocom/gprs/gprs_ns.h b/include/osmocom/gprs/gprs_ns.h
index 7c3b23c..1219307 100644
--- a/include/osmocom/gprs/gprs_ns.h
+++ b/include/osmocom/gprs/gprs_ns.h
@@ -61,6 +61,11 @@  enum gprs_ns_cs {
 	GPRS_NS_CS_ERROR,       /*!< Failed to process message */
 };
 
+enum gprs_ns_spec {
+	GPRS_NS_TS_08_16 = 0,
+	GPRS_NS_TS_48_016,
+};
+
 struct gprs_nsvc;
 /*! \brief Osmocom GPRS callback function type */
 typedef int gprs_ns_cb_t(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
@@ -120,10 +125,13 @@  struct gprs_nsvc {
 	enum nsvc_timer_mode timer_mode;
 	struct timeval timer_started;
 	int alive_retries;
+	enum gprs_ns_spec compliance;
 
 	unsigned int remote_end_is_sgsn:1;
 	unsigned int persistent:1;
 	unsigned int nsvci_is_valid:1;
+	unsigned int static_config:1;
+	unsigned int config_completed:1;
 
 	struct rate_ctr_group *ctrg;
 	struct osmo_stat_item_group *statg;
diff --git a/src/gb/gprs_ns.c b/src/gb/gprs_ns.c
index a29c946..0e874cf 100644
--- a/src/gb/gprs_ns.c
+++ b/src/gb/gprs_ns.c
@@ -149,11 +149,28 @@  static const struct osmo_stat_item_group_desc nsvc_statg_desc = {
 	.class_id = OSMO_STATS_CLASS_PEER,
 };
 
+static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode);
+
 #define CHECK_TX_RC(rc, nsvc) \
 		if (rc < 0)							\
 			LOGP(DNS, LOGL_ERROR, "TX failed (%d) to peer %s\n",	\
 				rc, gprs_ns_ll_str(nsvc));
 
+static int enable_reset_block_proc(const struct gprs_nsvc *nsvc)
+{
+	return nsvc->compliance != GPRS_NS_TS_48_016 ||
+		!nsvc->static_config ||
+		nsvc->ll != GPRS_NS_LL_UDP;
+}
+
+static int continue_test_proc(const struct gprs_nsvc *nsvc)
+{
+	return nsvc->compliance == GPRS_NS_TS_48_016 &&
+		nsvc->static_config &&
+		nsvc->ll == GPRS_NS_LL_UDP &&
+		nsvc->remote_end_is_sgsn;
+}
+
 struct msgb *gprs_ns_msgb_alloc(void)
 {
 	struct msgb *msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM,
@@ -588,16 +605,24 @@  static void gprs_ns_timer_cb(void *data)
 		nsvc->alive_retries++;
 		if (nsvc->alive_retries >
 			nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) {
-			/* mark as dead and blocked */
-			nsvc->state = NSE_S_BLOCKED;
-			rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
-			rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_DEAD]);
 			LOGP(DNS, LOGL_NOTICE,
 				"NSEI=%u Tns-alive expired more then "
 				"%u times, blocking NS-VC\n", nsvc->nsei,
 				nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]);
+			/* mark as dead */
+			nsvc->state = nsvc->state & ~NSE_S_ALIVE;
+			rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_DEAD]);
 			ns_osmo_signal_dispatch(nsvc, S_NS_ALIVE_EXP, 0);
-			ns_osmo_signal_dispatch(nsvc, S_NS_BLOCK, NS_CAUSE_NSVC_BLOCKED);
+
+			if (continue_test_proc(nsvc)) {
+				nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST);
+			} else if (enable_reset_block_proc(nsvc)) {
+				/* mark as blocked */
+				nsvc->state = NSE_S_BLOCKED;
+				rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+				ns_osmo_signal_dispatch(nsvc, S_NS_BLOCK,
+						NS_CAUSE_NSVC_BLOCKED);
+			}
 			return;
 		}
 		/* Tns-test case: send NS-ALIVE PDU */
@@ -1109,6 +1134,14 @@  int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
 			return rc;
 
 		rc = 0;
+	} else if (nsvc->static_config && !nsvc->config_completed) {
+
+		if (!enable_reset_block_proc(nsvc)) {
+			nsvc->state = NSE_S_ALIVE;
+			nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST);
+		}
+
+		nsvc->config_completed = 1;
 	}
 
 	if (nsvc)
@@ -1311,10 +1344,18 @@  int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg,
 		 * 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)
+		if ((*nsvc)->state == NSE_S_BLOCKED) {
 			rc = gprs_nsvc_reset((*nsvc), NS_CAUSE_PDU_INCOMP_PSTATE);
-		else if (!((*nsvc)->state & NSE_S_RESET))
+		} else if (!((*nsvc)->state & NSE_S_RESET)) {
 			rc = gprs_ns_tx_alive_ack(*nsvc);
+			if (!enable_reset_block_proc(*nsvc) &&
+				!((*nsvc)->state & NSE_S_ALIVE))
+			{
+				/* start the test procedure */
+				gprs_ns_tx_simple((*nsvc), NS_PDUT_ALIVE);
+				nsvc_start_timer((*nsvc), NSVC_TIMER_TNS_TEST);
+			}
+		}
 		break;
 	case NS_PDUT_ALIVE_ACK:
 		if ((*nsvc)->timer_mode == NSVC_TIMER_TNS_ALIVE)
@@ -1605,7 +1646,12 @@  struct gprs_nsvc *gprs_ns_nsip_connect(struct gprs_ns_inst *nsi,
 	nsvc->nsei = nsei;
 	nsvc->remote_end_is_sgsn = 1;
 
-	gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
+	if (enable_reset_block_proc(nsvc)) {
+		gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
+	} else if (continue_test_proc(nsvc)) {
+		nsvc->state = NSE_S_ALIVE;
+		nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST);
+	}
 	return nsvc;
 }
 
diff --git a/src/gb/gprs_ns_vty.c b/src/gb/gprs_ns_vty.c
index 5a951dc..1353bf3 100644
--- a/src/gb/gprs_ns_vty.c
+++ b/src/gb/gprs_ns_vty.c
@@ -91,6 +91,11 @@  static int config_write_ns(struct vty *vty)
 		vty_out(vty, " nse %u remote-role %s%s",
 			nsvc->nsei, nsvc->remote_end_is_sgsn ? "sgsn" : "bss",
 			VTY_NEWLINE);
+		vty_out(vty, " nse %u compliance %s%s",
+			nsvc->nsei,
+			nsvc->compliance == GPRS_NS_TS_08_16 ?
+				"ts08.16" : "ts48.016",
+			VTY_NEWLINE);
 		switch (nsvc->ll) {
 		case GPRS_NS_LL_UDP:
 			vty_out(vty, " nse %u encapsulation udp%s", nsvc->nsei,
@@ -257,6 +262,7 @@  DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,
 	if (!nsvc) {
 		nsvc = gprs_nsvc_create(vty_nsi, nsvci);
 		nsvc->nsei = nsei;
+		nsvc->static_config = 1;
 	}
 	nsvc->nsvci = nsvci;
 	/* All NSVCs that are explicitly configured by VTY are
@@ -389,6 +395,30 @@  DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd,
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_nse_compliance, cfg_nse_compliance_cmd,
+	"nse <0-65535> compliance (ts08.16|ts48.016)",
+	NSE_CMD_STR
+	"Set protocol compliance\n"
+	"Use 3GPP TS 08.16\n"
+	"Use 3GPP TS 48.016\n")
+{
+	uint16_t nsei = atoi(argv[0]);
+	struct gprs_nsvc *nsvc;
+
+	nsvc = gprs_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], "ts08.16"))
+		nsvc->compliance = GPRS_NS_TS_08_16;
+	else
+		nsvc->compliance = GPRS_NS_TS_48_016;
+
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_no_nse, cfg_no_nse_cmd,
 	"no nse <0-65535>",
 	"Delete Persistent NS Entity\n"
@@ -593,6 +623,7 @@  int gprs_ns_vty_init(struct gprs_ns_inst *nsi)
 	install_element(L_NS_NODE, &cfg_nse_fr_dlci_cmd);
 	install_element(L_NS_NODE, &cfg_nse_encaps_cmd);
 	install_element(L_NS_NODE, &cfg_nse_remoterole_cmd);
+	install_element(L_NS_NODE, &cfg_nse_compliance_cmd);
 	install_element(L_NS_NODE, &cfg_no_nse_cmd);
 	install_element(L_NS_NODE, &cfg_ns_timer_cmd);
 	install_element(L_NS_NODE, &cfg_nsip_local_ip_cmd);