Support forwarding RIM messages over GTPCv1 EUTRAN<->GERAN

MMEs connect over Gn interface using GTPCv1 towards the SGSN in order to
exchange RIM PDUs by using "RAN Information Relay" GTPCv1 message type.
For more info, see 3GPP TS 29.060 sec 7.5.14.1 "RAN Information Relay"

In order to support it, this commit does the following:

* Uses new libgtp APIs to rx and tx RAN Information Relay messages. The
  same "gsn" object is reused, ie. the local GTPCv1 socket address used
  for exchanging messages against GGSN is reused.
* Adds a new "sgsn_mme_ctx" struct holding information about MMEs
  allowed by the SGSN, each one containing information about the GTP
  address it uses, the in/out routing based on TAI requests, etc. The
  set of MMEs and their config can be set up using new VTY node introduced
  in this commit.
* The RIM related code in SGSN is refactored to allow forwarding from
  and to several types of addresses/interfaces.

Depends: osmo-ggsn.git Change-Id Iea3eb032ccd4aed5187baca7f7719349d76039d4
Depends: libosmocore.git Change-Id I534db7d8bc5ceb19a2a6866f07d5f5c70e456c5c
Related: SYS#5314
Change-Id: I396450b8d8b66595dab8ff7bf41cbf964bb40d93
diff --git a/src/sgsn/sgsn_rim.c b/src/sgsn/sgsn_rim.c
index 71e6d98..f28ba60 100644
--- a/src/sgsn/sgsn_rim.c
+++ b/src/sgsn/sgsn_rim.c
@@ -11,57 +11,113 @@
 #include <osmocom/gprs/gprs_bssgp.h>
 #include <osmocom/gprs/gprs_bssgp_rim.h>
 #include <osmocom/sgsn/sgsn_rim.h>
+#include <osmocom/sgsn/gtp_mme.h>
 #include <osmocom/sgsn/debug.h>
+#include <osmocom/sgsn/sgsn.h>
 
-/* Find an NSEI for the destination cell, this function works only for GERAN! */
-static int find_dest_nsei_geran(struct bssgp_rim_routing_info *dest_rim_ri, uint16_t nsei)
+static int sgsn_bssgp_fwd_rim_to_geran(const struct bssgp_ran_information_pdu *pdu)
 {
 	struct bssgp_bvc_ctx *bvc_ctx;
+	OSMO_ASSERT(pdu->routing_info_dest.discr == BSSGP_RIM_ROUTING_INFO_GERAN);
 
-	OSMO_ASSERT(dest_rim_ri->discr == BSSGP_RIM_ROUTING_INFO_GERAN);
-
-	bvc_ctx = btsctx_by_raid_cid(&dest_rim_ri->geran.raid, dest_rim_ri->geran.cid);
+	bvc_ctx = btsctx_by_raid_cid(&pdu->routing_info_dest.geran.raid, pdu->routing_info_dest.geran.cid);
 	if (!bvc_ctx) {
-		LOGP(DRIM, LOGL_ERROR, "BSSGP RIM (NSEI=%u) cannot find NSEI for destination cell\n", nsei);
+		LOGP(DRIM, LOGL_ERROR, "Unable to find NSEI for destination cell %s\n",
+		       bssgp_rim_ri_name(&pdu->routing_info_dest));
 		return -EINVAL;
 	}
 
-	return bvc_ctx->nsei;
+	/* Forward PDU as it is to the correct interface */
+	return bssgp_tx_rim(pdu, bvc_ctx->nsei);
 }
 
-int sgsn_rim_rx(struct osmo_bssgp_prim *bp, struct msgb *msg)
+static int sgsn_bssgp_fwd_rim_to_eutran(const struct bssgp_ran_information_pdu *pdu)
 {
-	struct bssgp_ran_information_pdu *pdu = &bp->u.rim_pdu;
-	int d_nsei;
-	uint16_t nsei = msgb_nsei(msg);
+	struct sgsn_mme_ctx *mme;
+	OSMO_ASSERT(pdu->routing_info_dest.discr == BSSGP_RIM_ROUTING_INFO_EUTRAN);
 
-	/* At the moment we only support GERAN, so we block all other network
-	 * types here. */
-	if (pdu->routing_info_dest.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
-		LOGP(DRIM, LOGL_ERROR,
-		     "BSSGP RIM (NSEI=%u) only GERAN supported, destination cell is not a GERAN cell -- rejected.\n",
-		     nsei);
-		/* At the moment we can only handle GERAN addresses, any other
-		 * type of address will be consideres as an invalid address.
-		 * see also: 3GPP TS 48.018, section 8c.3.1.3 */
-		return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
+	mme = sgsn_mme_ctx_by_route(sgsn, &pdu->routing_info_dest.eutran.tai);
+	if (!mme) { /* See if we have a default route configured */
+		mme = sgsn_mme_ctx_by_default_route(sgsn);
+		if (!mme) {
+			LOGP(DRIM, LOGL_ERROR, "Unable to find MME for destination cell %s\n",
+			       bssgp_rim_ri_name(&pdu->routing_info_dest));
+			return -EINVAL;
+		}
 	}
+
+	return sgsn_mme_ran_info_req(mme, pdu);
+}
+
+/* Receive a RIM PDU from BSSGP (GERAN) */
+int sgsn_rim_rx_from_gb(struct osmo_bssgp_prim *bp, struct msgb *msg)
+{
+	uint16_t nsei = msgb_nsei(msg);
+	struct bssgp_ran_information_pdu *pdu = &bp->u.rim_pdu;
+
 	if (pdu->routing_info_src.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
 		LOGP(DRIM, LOGL_ERROR,
-		     "BSSGP RIM (NSEI=%u) only GERAN supported, source cell is not a GERAN cell -- rejected.\n", nsei);
-		/* See comment above */
-		return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
+		     "Rx BSSGP RIM (NSEI=%u): Expected src %s, got %s\n", nsei,
+		     bssgp_rim_routing_info_discr_str(BSSGP_RIM_ROUTING_INFO_GERAN),
+		     bssgp_rim_routing_info_discr_str(pdu->routing_info_src.discr));
+		goto err;
 	}
 
-	d_nsei = find_dest_nsei_geran(&pdu->routing_info_dest, nsei);
-	if (d_nsei < 0) {
-		LOGP(DRIM, LOGL_NOTICE, "BSSGP RIM (NSEI=%u) Cell %s unknown to this sgsn\n",
-		     nsei, bssgp_rim_ri_name(&pdu->routing_info_dest));
-		/* In case of an invalid destination address we respond with
-		 * a BSSGP STATUS PDU, see also: 3GPP TS 48.018, section 8c.3.1.3 */
-		return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
+	switch (pdu->routing_info_dest.discr) {
+	case BSSGP_RIM_ROUTING_INFO_GERAN:
+		return sgsn_bssgp_fwd_rim_to_geran(pdu);
+	case BSSGP_RIM_ROUTING_INFO_EUTRAN:
+		return sgsn_bssgp_fwd_rim_to_eutran(pdu);
+	default:
+		/* At the moment we can only handle GERAN/EUTRAN addresses, any
+		 * other type of address will be considered as an invalid
+		 * address. see also: 3GPP TS 48.018, section 8c.3.1.3
+		 */
+		LOGP(DRIM, LOGL_ERROR,
+		     "Rx BSSGP RIM (NSEI=%u): Unsupported dst %s\n", nsei,
+		     bssgp_rim_routing_info_discr_str(pdu->routing_info_dest.discr));
 	}
 
-	/* Forward PDU as it is to the correct interface */
-	return bssgp_tx_rim(pdu, (uint16_t) d_nsei);
+	LOGP(DRIM, LOGL_INFO, "Rx BSSGP RIM (NSEI=%u): for dest cell %s\n", nsei,
+	     bssgp_rim_ri_name(&pdu->routing_info_dest));
+
+err:
+	/* In case of an invalid destination address we respond with
+	 * a BSSGP STATUS PDU, see also: 3GPP TS 48.018, section 8c.3.1.3 */
+	bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
+	return -1;
+}
+
+/* Receive a RIM PDU from GTPvC1 (EUTRAN) */
+int sgsn_rim_rx_from_gtp(struct bssgp_ran_information_pdu *pdu, struct sgsn_mme_ctx *mme)
+{
+	struct sgsn_mme_ctx *mme_tmp;
+	if (pdu->routing_info_src.discr != BSSGP_RIM_ROUTING_INFO_EUTRAN) {
+		LOGMME(mme, DRIM, LOGL_ERROR, "Rx GTP RAN Information Relay: Expected src %s, got %s\n",
+		       bssgp_rim_routing_info_discr_str(BSSGP_RIM_ROUTING_INFO_EUTRAN),
+		       bssgp_rim_routing_info_discr_str(pdu->routing_info_src.discr));
+		return -EINVAL;
+	}
+
+	if (pdu->routing_info_dest.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
+		LOGMME(mme, DRIM, LOGL_ERROR, "Rx GTP RAN Information Relay: Expected dst %s, got %s\n",
+		       bssgp_rim_routing_info_discr_str(BSSGP_RIM_ROUTING_INFO_GERAN),
+		       bssgp_rim_routing_info_discr_str(pdu->routing_info_dest.discr));
+		return -EINVAL;
+	}
+
+	mme_tmp = sgsn_mme_ctx_by_route(sgsn, &pdu->routing_info_src.eutran.tai);
+	if (!mme_tmp)/* See if we have a default route configured */
+		mme_tmp = sgsn_mme_ctx_by_default_route(sgsn);
+	if (mme != mme_tmp) {
+		LOGP(DRIM, LOGL_ERROR, "Rx GTP RAN Information Relay: "
+		     "Source MME doesn't have RIM routing configured for TAI: %s\n",
+		     bssgp_rim_ri_name(&pdu->routing_info_src));
+		return -EINVAL;
+	}
+
+	LOGMME(mme, DRIM, LOGL_INFO, "Rx GTP RAN Information Relay for dest cell %s\n",
+	       bssgp_rim_ri_name(&pdu->routing_info_dest));
+
+	return sgsn_bssgp_fwd_rim_to_geran(pdu);
 }