Port gbproxy to NS2

Since NS2 has a different abstraction we mock up the prim send/recv
functions and don't test NS like the old tests did.

Related: SYS#4998
Change-Id: Iecfd0408a35a11638d254c1db3c1d477b1a11524
diff --git a/src/gb_proxy.c b/src/gb_proxy.c
index 3f471ff..542d6d5 100644
--- a/src/gb_proxy.c
+++ b/src/gb_proxy.c
@@ -36,8 +36,9 @@
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/core/stats.h>
 
-#include <osmocom/gprs/gprs_ns.h>
+#include <osmocom/gprs/gprs_ns2.h>
 #include <osmocom/gprs/gprs_bssgp.h>
+#include <osmocom/gprs/gprs_bssgp_bss.h>
 
 #include <osmocom/gsm/gsm_utils.h>
 
@@ -385,6 +386,8 @@
 	link_info->vu_gen_tx_bss = (link_info->vu_gen_tx_bss + 1) % 512;
 
 	gprs_push_bssgp_dl_unitdata(msg, link_info->tlli.current);
+	msg->l3h = msg->data;
+
 	rc = gbprox_relay2peer(msg, peer, bvci);
 	msgb_free(msg);
 	return rc;
@@ -779,21 +782,23 @@
 {
 	/* create a copy of the message so the old one can
 	 * be free()d safely when we return from gbprox_rcvmsg() */
+	struct gprs_ns2_inst *nsi = cfg->nsi;
+	struct osmo_gprs_ns2_prim nsp = {};
 	struct msgb *msg = bssgp_msgb_copy(old_msg, "msgb_relay2sgsn");
 	int rc;
 
 	DEBUGP(DGPRS, "NSEI=%u proxying BTS->SGSN (NS_BVCI=%u, NSEI=%u)\n",
 		msgb_nsei(msg), ns_bvci, sgsn_nsei);
 
-	msgb_bvci(msg) = ns_bvci;
-	msgb_nsei(msg) = sgsn_nsei;
+	nsp.bvci = ns_bvci;
+	nsp.nsei = sgsn_nsei;
 
 	strip_ns_hdr(msg);
-
-	rc = gprs_ns_sendmsg(bssgp_nsi, msg);
+	osmo_prim_init(&nsp.oph, SAP_NS, PRIM_NS_UNIT_DATA,
+		       PRIM_OP_REQUEST, msg);
+	rc = gprs_ns2_recv_prim(nsi, &nsp.oph);
 	if (rc < 0)
 		rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_TX_ERR_SGSN]);
-
 	return rc;
 }
 
@@ -803,19 +808,23 @@
 {
 	/* create a copy of the message so the old one can
 	 * be free()d safely when we return from gbprox_rcvmsg() */
+	struct gprs_ns2_inst *nsi = peer->cfg->nsi;
+	struct osmo_gprs_ns2_prim nsp = {};
 	struct msgb *msg = bssgp_msgb_copy(old_msg, "msgb_relay2peer");
 	int rc;
 
 	DEBUGP(DGPRS, "NSEI=%u proxying SGSN->BSS (NS_BVCI=%u, NSEI=%u)\n",
 		msgb_nsei(msg), ns_bvci, peer->nsei);
 
-	msgb_bvci(msg) = ns_bvci;
-	msgb_nsei(msg) = peer->nsei;
+	nsp.bvci = ns_bvci;
+	nsp.nsei = peer->nsei;
 
 	/* Strip the old NS header, it will be replaced with a new one */
 	strip_ns_hdr(msg);
 
-	rc = gprs_ns_sendmsg(bssgp_nsi, msg);
+	osmo_prim_init(&nsp.oph, SAP_NS, PRIM_NS_UNIT_DATA,
+		       PRIM_OP_REQUEST, msg);
+	rc = gprs_ns2_recv_prim(nsi, &nsp.oph);
 	if (rc < 0)
 		rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_TX_ERR]);
 
@@ -875,7 +884,7 @@
 /* Receive an incoming PTP message from a BSS-side NS-VC */
 static int gbprox_rx_ptp_from_bss(struct gbproxy_config *cfg,
 				  struct msgb *msg, uint16_t nsei,
-				  uint16_t nsvci, uint16_t ns_bvci)
+				  uint16_t ns_bvci)
 {
 	struct gbproxy_peer *peer;
 	struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
@@ -885,9 +894,9 @@
 	peer = gbproxy_peer_by_bvci(cfg, ns_bvci);
 	if (!peer) {
 		LOGP(DGPRS, LOGL_NOTICE, "Didn't find peer for "
-		     "BVCI=%u for PTP message from NSVC=%u/NSEI=%u (BSS), "
+		     "BVCI=%u for PTP message from NSEI=%u (BSS), "
 		     "discarding message\n",
-		     ns_bvci, nsvci, nsei);
+		     ns_bvci, nsei);
 		return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI,
 				       &ns_bvci, msg);
 	}
@@ -917,7 +926,7 @@
 /* Receive an incoming PTP message from a SGSN-side NS-VC */
 static int gbprox_rx_ptp_from_sgsn(struct gbproxy_config *cfg,
 				   struct msgb *msg, uint16_t nsei,
-				   uint16_t nsvci, uint16_t ns_bvci)
+				   uint16_t ns_bvci)
 {
 	struct gbproxy_peer *peer;
 	struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
@@ -929,8 +938,8 @@
 
 	if (!peer) {
 		LOGP(DGPRS, LOGL_INFO, "Didn't find peer for "
-		     "BVCI=%u for message from NSVC=%u/NSEI=%u (SGSN)\n",
-		     ns_bvci, nsvci, nsei);
+		     "BVCI=%u for message from NSEI=%u (SGSN)\n",
+		     ns_bvci, nsei);
 		rate_ctr_inc(&cfg->ctrg->
 			     ctr[GBPROX_GLOB_CTR_INV_BVCI]);
 		return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI,
@@ -939,8 +948,8 @@
 
 	if (peer->blocked) {
 		LOGP(DGPRS, LOGL_NOTICE, "Dropping PDU for "
-		     "blocked BVCI=%u via NSVC=%u/NSEI=%u\n",
-		     ns_bvci, nsvci, nsei);
+		     "blocked BVCI=%u via NSEI=%u\n",
+		     ns_bvci, nsei);
 		rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DROPPED]);
 		return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &ns_bvci, msg);
 	}
@@ -1313,11 +1322,30 @@
 		(cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei);
 }
 
-/* Main input function for Gb proxy */
-int gbprox_rcvmsg(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei,
-		uint16_t ns_bvci, uint16_t nsvci)
+int gbprox_bssgp_send_cb(void *ctx, struct msgb *msg)
 {
 	int rc;
+	struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
+	struct gprs_ns2_inst *nsi = cfg->nsi;
+	struct osmo_gprs_ns2_prim nsp = {};
+
+	nsp.bvci = msgb_bvci(msg);
+	nsp.nsei = msgb_nsei(msg);
+
+	osmo_prim_init(&nsp.oph, SAP_NS, PRIM_NS_UNIT_DATA, PRIM_OP_REQUEST, msg);
+	rc = gprs_ns2_recv_prim(nsi, &nsp.oph);
+
+	return rc;
+}
+
+/* Main input function for Gb proxy */
+int gbprox_rcvmsg(void *ctx, struct msgb *msg)
+{
+	int rc;
+	uint16_t nsei = msgb_nsei(msg);
+	uint16_t ns_bvci = msgb_bvci(msg);
+	struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
+
 	int remote_end_is_sgsn = gbproxy_is_sgsn_nsei(cfg, nsei);
 
 	/* Only BVCI=0 messages need special treatment */
@@ -1329,102 +1357,122 @@
 	} else {
 		/* All other BVCI are PTP */
 		if (remote_end_is_sgsn)
-			rc = gbprox_rx_ptp_from_sgsn(cfg, msg, nsei, nsvci,
+			rc = gbprox_rx_ptp_from_sgsn(cfg, msg, nsei,
 						     ns_bvci);
 		else
-			rc = gbprox_rx_ptp_from_bss(cfg, msg, nsei, nsvci,
+			rc = gbprox_rx_ptp_from_bss(cfg, msg, nsei,
 						    ns_bvci);
 	}
 
 	return rc;
 }
 
-int gbprox_reset_persistent_nsvcs(struct gprs_ns_inst *nsi)
-{
-	struct gprs_nsvc *nsvc;
+/*  TODO: What about handling:
+ * 	NS_AFF_CAUSE_VC_FAILURE,
+	NS_AFF_CAUSE_VC_RECOVERY,
+	NS_AFF_CAUSE_FAILURE,
+	NS_AFF_CAUSE_RECOVERY,
+	osmocom own causes
+	NS_AFF_CAUSE_SNS_CONFIGURED,
+	NS_AFF_CAUSE_SNS_FAILURE,
+	*/
 
-	llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
-		if (!nsvc->persistent)
-			continue;
-		gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
-	}
-	return 0;
-}
-
-/* Signal handler for signals from NS layer */
-int gbprox_signal(unsigned int subsys, unsigned int signal,
-		  void *handler_data, void *signal_data)
+void gprs_ns_prim_status_cb(struct gbproxy_config *cfg, struct osmo_gprs_ns2_prim *nsp)
 {
-	struct gbproxy_config *cfg = handler_data;
-	struct ns_signal_data *nssd = signal_data;
-	struct gprs_nsvc *nsvc = nssd->nsvc;
+	/* TODO: bss nsei available/unavailable  bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK, nsvc->nsei, peer->bvci, 0);
+	 * TODO: sgsn nsei available/unavailable
+	 */
 	struct gbproxy_peer *peer;
-	int remote_end_is_sgsn = gbproxy_is_sgsn_nsei(cfg, nsvc->nsei);
 
-	if (subsys != SS_L_NS)
-		return 0;
+	switch (nsp->u.status.cause) {
+	case NS_AFF_CAUSE_SNS_FAILURE:
+	case NS_AFF_CAUSE_SNS_CONFIGURED:
+		break;
 
-	if (signal == S_NS_RESET && remote_end_is_sgsn) {
-		/* We have received a NS-RESET from the NSEI and NSVC
-		 * of the SGSN.  This might happen with SGSN that start
-		 * their own NS-RESET procedure without waiting for our
-		 * NS-RESET */
-		nsvc->remote_end_is_sgsn = 1;
-	}
+	case NS_AFF_CAUSE_RECOVERY:
+		LOGP(DPCU, LOGL_NOTICE, "NS-NSE %d became available\n", nsp->nsei);
+		if (nsp->nsei == cfg->nsip_sgsn_nsei) {
+			/* look-up or create the BTS context for this BVC */
+			struct bssgp_bvc_ctx *bctx = btsctx_by_bvci_nsei(nsp->bvci, nsp->nsei);
+			if (!bctx)
+				bctx = btsctx_alloc(nsp->bvci, nsp->nsei);
 
-	if (signal == S_NS_ALIVE_EXP && nsvc->remote_end_is_sgsn) {
-		LOGP(DGPRS, LOGL_NOTICE, "Tns alive expired too often, "
-			"re-starting RESET procedure\n");
-		rate_ctr_inc(&cfg->ctrg->
-			     ctr[GBPROX_GLOB_CTR_RESTART_RESET_SGSN]);
-		gprs_ns_nsip_connect(nsvc->nsi, &nsvc->ip.bts_addr,
-				  nsvc->nsei, nsvc->nsvci);
-	}
-
-	if (!nsvc->remote_end_is_sgsn) {
-		/* from BSS to SGSN */
-		peer = gbproxy_peer_by_nsei(cfg, nsvc->nsei);
-		if (!peer) {
-			LOGP(DGPRS, LOGL_NOTICE, "signal '%s' for unknown peer NSEI=%u/NSVCI=%u\n",
-			     get_value_string(gprs_ns_signal_ns_names, signal), nsvc->nsei, nsvc->nsvci);
-			return 0;
+			bssgp_tx_bvc_reset_nsei_bvci(cfg->nsip_sgsn_nsei, 0, BSSGP_CAUSE_OML_INTERV, NULL, 0);
 		}
-		switch (signal) {
-		case S_NS_RESET:
-		case S_NS_BLOCK:
-			if (!peer->blocked)
-				break;
-			LOGP(DGPRS, LOGL_NOTICE, "Converting '%s' from NSEI=%u/NSVCI=%u into BSSGP_BVC_BLOCK to SGSN\n",
-			     get_value_string(gprs_ns_signal_ns_names, signal), nsvc->nsei, nsvc->nsvci);
-			bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK, nsvc->nsei,
-					     peer->bvci, 0);
-			break;
-		}
-	} else {
-		/* Forward this message to all NS-VC to BSS */
-		struct gprs_ns_inst *nsi = cfg->nsi;
-		struct gprs_nsvc *next_nsvc;
-
-		llist_for_each_entry(next_nsvc, &nsi->gprs_nsvcs, list) {
-			if (next_nsvc->remote_end_is_sgsn)
-				continue;
-
-			/* Note that the following does not start the full
-			 * procedures including timer based retransmissions. */
-			switch (signal) {
-			case S_NS_RESET:
-				gprs_ns_tx_reset(next_nsvc, nssd->cause);
-				break;
-			case S_NS_BLOCK:
-				gprs_ns_tx_block(next_nsvc, nssd->cause);
-				break;
-			case S_NS_UNBLOCK:
-				gprs_ns_tx_unblock(next_nsvc);
+		break;
+	case NS_AFF_CAUSE_FAILURE:
+		if (nsp->nsei == cfg->nsip_sgsn_nsei) {
+			/* sgsn */
+			/* TODO: BSVC: block all PtP towards bss */
+			rate_ctr_inc(&cfg->ctrg->
+				     ctr[GBPROX_GLOB_CTR_RESTART_RESET_SGSN]);
+		} else {
+			/* bss became unavailable */
+			peer = gbproxy_peer_by_nsei(cfg, nsp->nsei);
+			if (!peer) {
+				/* TODO: use primitive name + status cause name */
+				LOGP(DGPRS, LOGL_NOTICE, "Received ns2 primitive %d for unknown peer NSEI=%u\n",
+				     nsp->u.status.cause, nsp->nsei);
 				break;
 			}
+
+			if (!peer->blocked)
+				break;
+			bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK, cfg->nsip_sgsn_nsei,
+					     peer->bvci, 0);
 		}
+		LOGP(DPCU, LOGL_NOTICE, "NS-NSE %d became unavailable\n", nsp->nsei);
+		break;
+	default:
+		LOGP(DPCU, LOGL_NOTICE,
+		     "NS: %s Unknown prim %d from NS\n",
+		     get_value_string(osmo_prim_op_names, nsp->oph.operation),
+		     nsp->oph.primitive);
+		break;
 	}
-	return 0;
+}
+
+
+/* called by the ns layer */
+int gprs_ns2_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+	struct osmo_gprs_ns2_prim *nsp;
+	struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
+	int rc = 0;
+
+	if (oph->sap != SAP_NS)
+		return 0;
+
+	nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
+
+	if (oph->operation != PRIM_OP_INDICATION) {
+		LOGP(DPCU, LOGL_NOTICE, "NS: %s Unknown prim %d from NS\n",
+		     get_value_string(osmo_prim_op_names, oph->operation),
+		     oph->operation);
+		return 0;
+	}
+
+	switch (oph->primitive) {
+	case PRIM_NS_UNIT_DATA:
+		/* hand the message into the BSSGP implementation */
+		msgb_bssgph(oph->msg) = oph->msg->l3h;
+		msgb_bvci(oph->msg) = nsp->bvci;
+		msgb_nsei(oph->msg) = nsp->nsei;
+
+		rc = gbprox_rcvmsg(cfg, oph->msg);
+		break;
+	case PRIM_NS_STATUS:
+		gprs_ns_prim_status_cb(cfg, nsp);
+		break;
+	default:
+		LOGP(DPCU, LOGL_NOTICE,
+		     "NS: %s Unknown prim %d from NS\n",
+		     get_value_string(osmo_prim_op_names, oph->operation),
+		     oph->primitive);
+		break;
+	}
+
+	return rc;
 }
 
 void gbprox_reset(struct gbproxy_config *cfg)
diff --git a/src/gb_proxy_ctrl.c b/src/gb_proxy_ctrl.c
index 4b7c2f4..3ebc7d1 100644
--- a/src/gb_proxy_ctrl.c
+++ b/src/gb_proxy_ctrl.c
@@ -33,22 +33,41 @@
 
 extern vector ctrl_node_vec;
 
+static int ctrl_nsvc_state_cb(struct gprs_ns2_vc *nsvc, void *ctx) {
+/* FIXME: Can't get NSVC state in ns2
+	struct ctrl_cmd *cmd = (struct ctrl_cmd *)ctx;
+
+	cmd->reply = gprs_ns2_vc_state_append(cmd->reply, nsvc);
+*/
+	return 0;
+}
+
 static int get_nsvc_state(struct ctrl_cmd *cmd, void *data)
 {
 	struct gbproxy_config *cfg = data;
-	struct gprs_ns_inst *nsi = cfg->nsi;
-	struct gprs_nsvc *nsvc;
+	struct gprs_ns2_inst *nsi = cfg->nsi;
+	struct gprs_ns2_nse *nse;
+	struct gbproxy_peer *peer;
 
 	cmd->reply = talloc_strdup(cmd, "");
 
-	llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
-		if (nsvc == nsi->unknown_nsvc)
-			continue;
+	/* NS-VCs for SGSN */
+	nse = gprs_ns2_nse_by_nsei(nsi, cfg->nsip_sgsn_nsei);
+	if (nse)
+		gprs_ns2_nse_foreach_nsvc(nse, &ctrl_nsvc_state_cb, cmd);
+	/* NS-VCs for SGSN2 */
+	nse = gprs_ns2_nse_by_nsei(nsi, cfg->nsip_sgsn2_nsei);
+	if (nse)
+		gprs_ns2_nse_foreach_nsvc(nse, &ctrl_nsvc_state_cb, cmd);
 
-		cmd->reply = gprs_nsvc_state_append(cmd->reply, nsvc);
+	/* NS-VCs for BSS peers */
+	llist_for_each_entry(peer, &cfg->bts_peers, list) {
+		nse = gprs_ns2_nse_by_nsei(nsi, peer->nsei);
+		if (nse)
+			gprs_ns2_nse_foreach_nsvc(nse, &ctrl_nsvc_state_cb, cmd);
 	}
-
-	return CTRL_CMD_REPLY;
+	cmd->reply = "Getting NSVC state not yet implemented for NS2";
+	return CTRL_CMD_ERROR;
 }
 
 CTRL_CMD_DEFINE_RO(nsvc_state, "nsvc-state");
diff --git a/src/gb_proxy_main.c b/src/gb_proxy_main.c
index 8c83980..f8c5ade 100644
--- a/src/gb_proxy_main.c
+++ b/src/gb_proxy_main.c
@@ -38,7 +38,7 @@
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/core/stats.h>
 
-#include <osmocom/gprs/gprs_ns.h>
+#include <osmocom/gprs/gprs_ns2.h>
 #include <osmocom/gprs/gprs_bssgp.h>
 
 #include <osmocom/sgsn/signal.h>
@@ -80,26 +80,6 @@
 /* Pointer to the SGSN peer */
 extern struct gbprox_peer *gbprox_peer_sgsn;
 
-/* call-back function for the NS protocol */
-static int proxy_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
-		      struct msgb *msg, uint16_t bvci)
-{
-	int rc = 0;
-
-	switch (event) {
-	case GPRS_NS_EVT_UNIT_DATA:
-		rc = gbprox_rcvmsg(gbcfg, msg, nsvc->nsei, bvci, nsvc->nsvci);
-		break;
-	default:
-		LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
-		if (msg)
-			msgb_free(msg);
-		rc = -EIO;
-		break;
-	}
-	return rc;
-}
-
 static void signal_handler(int signal)
 {
 	fprintf(stdout, "signal %u received\n", signal);
@@ -269,6 +249,8 @@
 	return stat(path, &sb) ? false : true;
 }
 
+int gbprox_bssgp_send_cb(void *ctx, struct msgb *msg);
+
 int main(int argc, char **argv)
 {
 	int rc;
@@ -314,23 +296,23 @@
 	rate_ctr_init(tall_sgsn_ctx);
 	osmo_stats_init(tall_sgsn_ctx);
 
-	bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_sgsn_ctx);
-	if (!bssgp_nsi) {
-		LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
-		exit(1);
-	}
-
 	gbcfg = talloc_zero(tall_sgsn_ctx, struct gbproxy_config);
 	if (!gbcfg) {
 		LOGP(DGPRS, LOGL_FATAL, "Unable to allocate config\n");
 		exit(1);
 	}
 	gbproxy_init_config(gbcfg);
-	gbcfg->nsi = bssgp_nsi;
-	gprs_ns_vty_init(bssgp_nsi);
-	gprs_ns_set_log_ss(DNS);
+	gbcfg->nsi = gprs_ns2_instantiate(tall_sgsn_ctx, gprs_ns2_prim_cb, gbcfg);
+	if (!gbcfg->nsi) {
+		LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
+		exit(1);
+	}
+
+	gprs_ns2_vty_init(gbcfg->nsi, NULL);
 	bssgp_set_log_ss(DBSSGP);
-	osmo_signal_register_handler(SS_L_NS, &gbprox_signal, gbcfg);
+	gprs_ns2_dynamic_create_nse(gbcfg->nsi, true);
+
+	bssgp_set_bssgp_callback(gbprox_bssgp_send_cb, gbcfg);
 
 	rc = gbproxy_parse_config(config_file, gbcfg);
 	if (rc < 0) {
@@ -338,6 +320,8 @@
 		exit(2);
 	}
 
+	gprs_ns2_vty_create();
+
 	/* start telnet after reading config for vty_get_bind_addr() */
 	rc = telnet_init_dynif(tall_sgsn_ctx, NULL,
 			       vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY);
@@ -357,26 +341,13 @@
 		exit(1);
 	}
 
-	if (!gprs_nsvc_by_nsei(gbcfg->nsi, gbcfg->nsip_sgsn_nsei)) {
+	if (!gprs_ns2_nse_by_nsei(gbcfg->nsi, gbcfg->nsip_sgsn_nsei)) {
 		LOGP(DGPRS, LOGL_FATAL, "You cannot proxy to NSEI %u "
 			"without creating that NSEI before\n",
 			gbcfg->nsip_sgsn_nsei);
 		exit(2);
 	}
 
-	rc = gprs_ns_nsip_listen(bssgp_nsi);
-	if (rc < 0) {
-		LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n");
-		exit(2);
-	}
-
-	rc = gprs_ns_frgre_listen(bssgp_nsi);
-	if (rc < 0) {
-		LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE "
-			"socket. Do you have CAP_NET_RAW?\n");
-		exit(2);
-	}
-
 	if (daemonize) {
 		rc = osmo_daemonize();
 		if (rc < 0) {
@@ -385,9 +356,6 @@
 		}
 	}
 
-	/* Reset all the persistent NS-VCs that we've read from the config */
-	gbprox_reset_persistent_nsvcs(bssgp_nsi);
-
 	while (1) {
 		rc = osmo_select_main(0);
 		if (rc < 0)
diff --git a/src/gb_proxy_patch.c b/src/gb_proxy_patch.c
index 2bc3b4b..9c70d3f 100644
--- a/src/gb_proxy_patch.c
+++ b/src/gb_proxy_patch.c
@@ -18,6 +18,7 @@
  *
  */
 
+#include <osmocom/gprs/gprs_msgb.h>
 #include <osmocom/sgsn/gb_proxy.h>
 
 #include <osmocom/sgsn/gprs_utils.h>
diff --git a/src/gb_proxy_vty.c b/src/gb_proxy_vty.c
index 355b23f..236d5d3 100644
--- a/src/gb_proxy_vty.c
+++ b/src/gb_proxy_vty.c
@@ -26,10 +26,11 @@
 #include <inttypes.h>
 
 #include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/gsm/gsm48.h>
 
-#include <osmocom/gprs/gprs_ns.h>
+#include <osmocom/gprs/gprs_ns2.h>
 #include <osmocom/gsm/apn.h>
 
 #include <osmocom/sgsn/debug.h>
@@ -666,27 +667,21 @@
 	}
 
 	if (delete_nsvc) {
-		struct gprs_ns_inst *nsi = g_cfg->nsi;
-		struct gprs_nsvc *nsvc, *nsvc2;
+		struct gprs_ns2_inst *nsi = g_cfg->nsi;
+		struct gprs_ns2_nse *nse;
 
-		counter = 0;
-		llist_for_each_entry_safe(nsvc, nsvc2, &nsi->gprs_nsvcs, list) {
-			if (nsvc->nsei != nsei)
-				continue;
-			if (nsvc->persistent)
-				continue;
-
-			if (!dry_run)
-				gprs_nsvc_delete(nsvc);
-			else
-				vty_out(vty, "NS-VC: NSEI %5u, NS-VCI %5u, "
-					"remote %s%s",
-					nsvc->nsei, nsvc->nsvci,
-					gprs_ns_ll_str(nsvc), VTY_NEWLINE);
-			counter += 1;
+		nse = gprs_ns2_nse_by_nsei(nsi, nsei);
+		if (!nse) {
+			vty_out(vty, "NSEI not found%s", VTY_NEWLINE);
+			return CMD_WARNING;
 		}
-		vty_out(vty, "%sDeleted %d NS-VC%s",
-			dry_run ? "Not " : "", counter, VTY_NEWLINE);
+
+		/* TODO: We should NOT delete a persistent NSEI/NSVC as soon as we can check for these */
+		if (!dry_run)
+			gprs_ns2_free_nse(nse);
+
+		vty_out(vty, "%sDeleted NS-VCs for NSEI %d%s",
+			dry_run ? "Not " : "", nsei, VTY_NEWLINE);
 	}
 
 	return CMD_SUCCESS;