gbproxy: Copy uplink messages to SGSN 2

Some messages that are related to the BVC itself must be forwarded to
the secondary SGSN, too.

This patch implements this for BVC-RESET (BVCI != 0) and FLOW-CONTROL-BVC
messages. The resulting acknowledgement messages from the secondary
SGSN are silently dropped. The idea behind this is that the primary
SGSN is responsible for setting up and maintaining the BVC whereas
the secondary SGSN is rather passive and just has to accept it.

Ticket: OW#1258
Sponsored-by: On-Waves ehf
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c
index 6e93e14..2026d1a 100644
--- a/openbsc/src/gprs/gb_proxy.c
+++ b/openbsc/src/gprs/gb_proxy.c
@@ -614,6 +614,8 @@
 				  uint16_t nsvci, uint16_t ns_bvci)
 {
 	struct gbproxy_peer *peer;
+	struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
+	uint8_t pdu_type = bgph->pdu_type;
 	int rc;
 
 	peer = gbproxy_peer_by_bvci(cfg, ns_bvci);
@@ -625,6 +627,19 @@
 	if (!rc)
 		return 0;
 
+	switch (pdu_type) {
+	case BSSGP_PDUT_FLOW_CONTROL_BVC:
+		if (!cfg->route_to_sgsn2)
+			break;
+
+		/* Send a copy to the secondary SGSN */
+		gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn2_nsei);
+		break;
+	default:
+		break;
+	}
+
+
 	return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei);
 }
 
@@ -634,6 +649,8 @@
 				   uint16_t nsvci, uint16_t ns_bvci)
 {
 	struct gbproxy_peer *peer;
+	struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
+	uint8_t pdu_type = bgph->pdu_type;
 
 	peer = gbproxy_peer_by_bvci(cfg, ns_bvci);
 
@@ -657,6 +674,19 @@
 		return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, NULL, msg);
 	}
 
+	switch (pdu_type) {
+	case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
+	case BSSGP_PDUT_BVC_BLOCK_ACK:
+	case BSSGP_PDUT_BVC_UNBLOCK_ACK:
+		if (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei)
+			/* Hide ACKs from the secondary SGSN, the primary SGSN
+			 * is responsible to send them. */
+			return 0;
+		break;
+	default:
+		break;
+	}
+
 	/* Optionally patch the message */
 	gbprox_process_bssgp_dl(cfg, msg, peer);
 
@@ -674,6 +704,7 @@
 	int data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
 	struct gbproxy_peer *from_peer = NULL;
 	struct gprs_ra_id raid;
+	int copy_to_sgsn2 = 0;
 	int rc;
 
 	if (ns_bvci != 0 && ns_bvci != 1) {
@@ -758,6 +789,8 @@
 				     bvci, raid.mcc, raid.mnc, raid.lac,
 				     raid.rac);
 			}
+			if (cfg->route_to_sgsn2)
+				copy_to_sgsn2 = 1;
 		}
 		break;
 	}
@@ -767,6 +800,10 @@
 	rc = gbprox_process_bssgp_ul(cfg, msg, from_peer);
 	if (!rc)
 		return 0;
+
+	if (copy_to_sgsn2)
+		gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn2_nsei);
+
 	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",
@@ -900,8 +937,11 @@
 	case BSSGP_PDUT_BVC_RESET:
 		rc = rx_reset_from_sgsn(cfg, msg, orig_msg, &tp, nsei, ns_bvci);
 		break;
-	case BSSGP_PDUT_FLUSH_LL:
 	case BSSGP_PDUT_BVC_RESET_ACK:
+		if (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei)
+			break;
+		/* fall through */
+	case BSSGP_PDUT_FLUSH_LL:
 		/* simple case: BVCI IE is mandatory */
 		if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI))
 			goto err_mand_ie;