gbproxy: Support a secondary SGSN

This patch refactors SGSN NSEI handling to support a secondary SGSN.

It adds the following VTY commands:
  - secondary-sgsn nsei <0-65534>
  - no secondary-sgsn

Sending messages to the secondary SGSN is not yet implemented, but
received messages from such a SGSN would be forwarded to the BSS
peers.

Sponsored-by: On-Waves ehf
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c
index f0375ee..539e3ae 100644
--- a/openbsc/src/gprs/gb_proxy.c
+++ b/openbsc/src/gprs/gb_proxy.c
@@ -75,7 +75,7 @@
 static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_peer *peer,
 			  uint16_t ns_bvci);
 static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg,
-			     uint16_t ns_bvci);
+			     uint16_t ns_bvci, uint16_t sgsn_nsei);
 
 static int check_peer_nsei(struct gbproxy_peer *peer, uint16_t nsei)
 {
@@ -358,7 +358,8 @@
 			gbproxy_update_tlli_state_after(peer, tlli_info, now,
 							&tmp_parse_ctx);
 
-			rc = gbprox_relay2sgsn(cfg, stored_msg, msgb_bvci(msg));
+			rc = gbprox_relay2sgsn(cfg, stored_msg, msgb_bvci(msg),
+					       cfg->nsip_sgsn_nsei);
 
 			if (rc < 0)
 				LOGP(DLLC, LOGL_ERROR,
@@ -496,7 +497,7 @@
 
 /* feed a message down the NS-VC associated with the specified peer */
 static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg,
-			     uint16_t ns_bvci)
+			     uint16_t ns_bvci, uint16_t sgsn_nsei)
 {
 	/* create a copy of the message so the old one can
 	 * be free()d safely when we return from gbprox_rcvmsg() */
@@ -504,10 +505,10 @@
 	int rc;
 
 	DEBUGP(DGPRS, "NSEI=%u proxying BTS->SGSN (NS_BVCI=%u, NSEI=%u)\n",
-		msgb_nsei(msg), ns_bvci, cfg->nsip_sgsn_nsei);
+		msgb_nsei(msg), ns_bvci, sgsn_nsei);
 
 	msgb_bvci(msg) = ns_bvci;
-	msgb_nsei(msg) = cfg->nsip_sgsn_nsei;
+	msgb_nsei(msg) = sgsn_nsei;
 
 	strip_ns_hdr(msg);
 
@@ -610,7 +611,7 @@
 	if (!rc)
 		return 0;
 
-	return gbprox_relay2sgsn(cfg, msg, ns_bvci);
+	return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei);
 }
 
 /* Receive an incoming PTP message from a SGSN-side NS-VC */
@@ -752,7 +753,7 @@
 	rc = gbprox_process_bssgp_ul(cfg, msg, from_peer);
 	if (!rc)
 		return 0;
-	return gbprox_relay2sgsn(cfg, msg, ns_bvci);
+	return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei);
 err_no_peer:
 	LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) cannot find peer based on NSEI\n",
 		nsei);
@@ -981,12 +982,18 @@
 	return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, orig_msg);
 }
 
+static int gbproxy_is_sgsn_nsei(struct gbproxy_config *cfg, uint16_t nsei)
+{
+	return nsei == cfg->nsip_sgsn_nsei ||
+		(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 rc;
-	int remote_end_is_sgsn = nsei == cfg->nsip_sgsn_nsei;
+	int remote_end_is_sgsn = gbproxy_is_sgsn_nsei(cfg, nsei);
 
 	/* Only BVCI=0 messages need special treatment */
 	if (ns_bvci == 0 || ns_bvci == 1) {
@@ -1027,11 +1034,12 @@
 	struct ns_signal_data *nssd = signal_data;
 	struct gprs_nsvc *nsvc = nssd->nsvc;
 	struct gbproxy_peer *peer;
+	int remote_end_is_sgsn = gbproxy_is_sgsn_nsei(cfg, nsvc->nsei);
 
 	if (subsys != SS_L_NS)
 		return 0;
 
-	if (signal == S_NS_RESET && nsvc->nsei == cfg->nsip_sgsn_nsei) {
+	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
diff --git a/openbsc/src/gprs/gb_proxy_vty.c b/openbsc/src/gprs/gb_proxy_vty.c
index 49c0020..cd16f38 100644
--- a/openbsc/src/gprs/gb_proxy_vty.c
+++ b/openbsc/src/gprs/gb_proxy_vty.c
@@ -317,6 +317,32 @@
 	return CMD_SUCCESS;
 }
 
+#define GBPROXY_SECOND_SGSN_STR "Route matching LLC connections to a second SGSN (Experimental)\n"
+
+DEFUN(cfg_gbproxy_secondary_sgsn,
+      cfg_gbproxy_secondary_sgsn_cmd,
+      "secondary-sgsn nsei <0-65534>",
+      GBPROXY_SECOND_SGSN_STR
+      "NSEI to be used in the connection with the SGSN\n"
+      "The NSEI\n")
+{
+	g_cfg->route_to_sgsn2 = 1;
+	g_cfg->nsip_sgsn2_nsei = atoi(argv[0]);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gbproxy_no_secondary_sgsn,
+      cfg_gbproxy_no_secondary_sgsn_cmd,
+      "no secondary-sgsn",
+      NO_STR GBPROXY_SECOND_SGSN_STR)
+{
+	g_cfg->route_to_sgsn2 = 0;
+	g_cfg->nsip_sgsn2_nsei = 0xFFFF;
+
+	return CMD_SUCCESS;
+}
+
 #define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n"
 #define GBPROXY_MAX_AGE_STR "Limit maximum age\n"
 
@@ -627,6 +653,7 @@
 	install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_match_cmd);
 	install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd);
 	install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd);
+	install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd);
 	install_element(GBPROXY_NODE, &cfg_gbproxy_tlli_list_max_age_cmd);
 	install_element(GBPROXY_NODE, &cfg_gbproxy_tlli_list_max_len_cmd);
 	install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd);
@@ -634,6 +661,7 @@
 	install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd);
 	install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd);
 	install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd);
+	install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd);
 	install_element(GBPROXY_NODE, &cfg_gbproxy_tlli_list_no_max_age_cmd);
 	install_element(GBPROXY_NODE, &cfg_gbproxy_tlli_list_no_max_len_cmd);
 	install_element(GBPROXY_NODE, &cfg_gbproxy_patch_mode_cmd);