gbproxy: Refactor gbprox_process_bssgp_ul into smaller functions

gbprox_process_bssgp_ul has grown quite large mainly by the addition
of IMSI acquisition.

This patch moves that code into several smaller functions. In
addition, the peer resolution which is similar to that in
gbprox_process_bssgp_dl is moved into a separate function, too.

Sponsored-by: On-Waves ehf
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c
index f7afe94..514898a 100644
--- a/openbsc/src/gprs/gb_proxy.c
+++ b/openbsc/src/gprs/gb_proxy.c
@@ -254,14 +254,16 @@
 }
 
 /* Returns != 0 iff IMSI acquisition was in progress */
-int gbproxy_reset_imsi_acquisition(struct gbproxy_tlli_info* tlli_info)
+static int gbproxy_restart_imsi_acquisition(struct gbproxy_tlli_info* tlli_info)
 {
 	int in_progress = 0;
 	if (!tlli_info)
 		return 0;
 
-	if (tlli_info->imsi_acq_pending)
+	if (tlli_info->imsi_acq_pending) {
 		in_progress = 1;
+		tlli_info->imsi_acq_retries += 1;
+	}
 
 	gbproxy_tlli_info_discard_messages(tlli_info);
 	tlli_info->imsi_acq_pending = 0;
@@ -269,6 +271,187 @@
 	return in_progress;
 }
 
+static void gbproxy_reset_imsi_acquisition(struct gbproxy_tlli_info* tlli_info)
+{
+	gbproxy_restart_imsi_acquisition(tlli_info);
+	tlli_info->imsi_acq_retries = 0;
+}
+
+static void gbproxy_flush_stored_messages(struct gbproxy_peer *peer,
+					  struct msgb *msg,
+					  uint16_t sgsn_nsei,
+					  time_t now,
+					  struct gbproxy_tlli_info* tlli_info,
+					  struct gprs_gb_parse_context *parse_ctx)
+{
+	int rc;
+	struct msgb *stored_msg;
+	/* Got identity response with IMSI, assuming the request had
+	 * been generated by the gbproxy */
+
+	LOGP(DLLC, LOGL_DEBUG,
+	     "NSEI=%d(BSS) IMSI acquisition succeeded, "
+	     "flushing stored messages\n",
+	     msgb_nsei(msg));
+
+	/* Patch and flush stored messages towards the SGSN */
+	while ((stored_msg = msgb_dequeue(&tlli_info->stored_msgs))) {
+		struct gprs_gb_parse_context tmp_parse_ctx = {0};
+		tmp_parse_ctx.to_bss = 0;
+		int len_change = 0;
+
+		gprs_gb_parse_bssgp(msgb_bssgph(stored_msg),
+				    msgb_bssgp_len(stored_msg),
+				    &tmp_parse_ctx);
+		gbproxy_patch_bssgp(msg, msgb_bssgph(stored_msg),
+				    msgb_bssgp_len(stored_msg),
+				    peer, tlli_info, &len_change,
+				    &tmp_parse_ctx);
+
+		gbproxy_update_tlli_state_after(peer, tlli_info, now,
+						&tmp_parse_ctx);
+
+		rc = gbprox_relay2sgsn(peer->cfg, stored_msg,
+				       msgb_bvci(msg), sgsn_nsei);
+
+		if (rc < 0)
+			LOGP(DLLC, LOGL_ERROR,
+			     "NSEI=%d(BSS) failed to send stored message "
+			     "(%s)\n",
+			     msgb_nsei(msg),
+			     parse_ctx->llc_msg_name ?
+			     parse_ctx->llc_msg_name : "BSSGP");
+		msgb_free(stored_msg);
+	}
+}
+
+static void gbproxy_acquire_imsi(struct gbproxy_peer *peer,
+				 struct gbproxy_tlli_info* tlli_info,
+				 uint16_t bvci)
+{
+	struct msgb *idreq_msg;
+
+	/* Send IDENT REQ */
+	idreq_msg = gsm48_msgb_alloc();
+	gprs_put_identity_req(idreq_msg, GSM_MI_TYPE_IMSI);
+	/* Workaround to avoid N(U) collisions and to enable a restart
+	 * of the IMSI acquisition procedure. This will work unless the
+	 * SGSN has an initial V(UT) within [256-32, 256+n_retries]
+	 * (see GSM 04.64, 8.4.2).
+	 * TODO: Check whether it is sensible to rely on this, it might
+	 * be an issue when P-TMSI patching is _not_ enabled and the SGSN
+	 * doesn't reset V(UT) after a detach. */
+	gprs_push_llc_ui(idreq_msg, 0, GPRS_SAPI_GMM,
+			 (tlli_info->imsi_acq_retries + 256) % 512);
+	gprs_push_bssgp_dl_unitdata(idreq_msg, tlli_info->tlli.current);
+	gbprox_relay2peer(idreq_msg, peer, bvci);
+	msgb_free(idreq_msg);
+}
+
+
+/* Return != 0 iff msg still needs to be processed */
+static int gbproxy_imsi_acquisition(struct gbproxy_peer *peer,
+				    struct msgb *msg,
+				    uint16_t sgsn_nsei,
+				    time_t now,
+				    struct gbproxy_tlli_info* tlli_info,
+				    struct gprs_gb_parse_context *parse_ctx)
+{
+	if (!tlli_info)
+		return 1;
+
+	if (parse_ctx->g48_hdr &&
+	    parse_ctx->g48_hdr->msg_type == GSM48_MT_GMM_ATTACH_REQ)
+	{
+		if (gbproxy_restart_imsi_acquisition(tlli_info)) {
+			LOGP(DLLC, LOGL_INFO,
+			     "NSEI=%d(BSS) IMSI acquisition was in progress "
+			     "when receiving an ATTACH_REQ.\n",
+			     msgb_nsei(msg));
+		}
+	}
+
+	if (tlli_info->imsi_acq_pending && parse_ctx->g48_hdr &&
+	    parse_ctx->g48_hdr->proto_discr == GSM48_PDISC_MM_GPRS &&
+	    parse_ctx->g48_hdr->msg_type == GSM48_MT_GMM_ID_RESP &&
+	    parse_ctx->imsi_len > 0) {
+		/* The IMSI is now available */
+		gbproxy_flush_stored_messages(peer, msg, sgsn_nsei, now,
+					      tlli_info, parse_ctx);
+
+		gbproxy_reset_imsi_acquisition(tlli_info);
+		return 0;
+	}
+
+	if (tlli_info->imsi_acq_pending) {
+		struct msgb *stored_msg;
+
+		LOGP(DLLC, LOGL_DEBUG,
+		     "NSEI=%d(BSS) IMSI acquisition in progess, "
+		     "storing message (%s)\n",
+		     msgb_nsei(msg),
+		     parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP");
+
+		/* Enqueue unpatched messages */
+		stored_msg = gprs_msgb_copy(msg, "process_bssgp_ul");
+		msgb_enqueue(&tlli_info->stored_msgs, stored_msg);
+
+		return 0;
+	}
+
+	if (tlli_info->mi_data_len == 0) {
+		struct msgb *stored_msg = gprs_msgb_copy(msg, "process_bssgp_ul");
+
+		LOGP(DLLC, LOGL_DEBUG,
+		     "NSEI=%d(BSS) IMSI is required but not available, "
+		     "storing message (%s)\n",
+		     msgb_nsei(msg),
+		     parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP");
+
+		/* Enqueue message */
+		msgb_enqueue(&tlli_info->stored_msgs, stored_msg);
+
+		gbproxy_acquire_imsi(peer, tlli_info, msgb_bvci(msg));
+
+		tlli_info->imsi_acq_pending = 1;
+
+		/* There is no explicit retransmission handling, the
+		 * implementation relies on the MS doing proper retransmissions
+		 * of the triggering message instead */
+
+		return 0;
+	}
+
+	return 1;
+}
+
+struct gbproxy_peer *gbproxy_find_peer(struct gbproxy_config *cfg,
+				       struct msgb *msg,
+				       struct gprs_gb_parse_context *parse_ctx)
+{
+	struct gbproxy_peer *peer = NULL;
+
+	if (msgb_bvci(msg) >= 2)
+		peer = gbproxy_peer_by_bvci(cfg, msgb_bvci(msg));
+
+	if (!peer && !parse_ctx->to_bss)
+		peer = gbproxy_peer_by_nsei(cfg, msgb_nsei(msg));
+
+	if (!peer)
+		peer = gbproxy_peer_by_bssgp_tlv(cfg, &parse_ctx->bssgp_tp);
+
+	if (!peer) {
+		LOGP(DLLC, LOGL_INFO,
+		     "NSEI=%d(%s) patching: didn't find peer for message, "
+		     "PDU %d\n",
+		     msgb_nsei(msg), parse_ctx->to_bss ? "BSS" : "SGSN",
+		     parse_ctx->pdu_type);
+		/* Increment counter */
+		rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]);
+	}
+	return peer;
+}
+
 /* patch BSSGP message */
 static int gbprox_process_bssgp_ul(struct gbproxy_config *cfg,
 				   struct msgb *msg,
@@ -303,24 +486,11 @@
 	}
 
 	/* Get peer */
-	if (!peer && msgb_bvci(msg) >= 2)
-		peer = gbproxy_peer_by_bvci(cfg, msgb_bvci(msg));
+	if (!peer)
+		peer = gbproxy_find_peer(cfg, msg, &parse_ctx);
 
 	if (!peer)
-		peer = gbproxy_peer_by_nsei(cfg, msgb_nsei(msg));
-
-	if (!peer)
-		peer = gbproxy_peer_by_bssgp_tlv(cfg, &parse_ctx.bssgp_tp);
-
-	if (!peer) {
-		LOGP(DLLC, LOGL_INFO,
-		     "NSEI=%d(BSS) patching: didn't find peer for message, "
-		     "PDU %d\n",
-		     msgb_nsei(msg), parse_ctx.pdu_type);
-		/* Increment counter */
-		rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]);
 		return 0;
-	}
 
 	now = time(NULL);
 
@@ -339,13 +509,6 @@
 		switch (parse_ctx.g48_hdr->msg_type) {
 		case GSM48_MT_GMM_ATTACH_REQ:
 			rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REQS]);
-			if (gbproxy_reset_imsi_acquisition(tlli_info)) {
-				LOGP(DLLC, LOGL_INFO,
-				     "NSEI=%d(BSS) IMSI acquisition was in progress "
-				     "when receiving an ATTACH_REQ.\n",
-				     msgb_nsei(msg));
-				tlli_info->imsi_acq_retries += 1;
-			}
 			break;
 
 		default:
@@ -358,105 +521,14 @@
 		send_msg_directly = 1;
 	}
 
-	if (tlli_info && tlli_info->imsi_acq_pending && parse_ctx.g48_hdr &&
-	    parse_ctx.g48_hdr->proto_discr == GSM48_PDISC_MM_GPRS &&
-	    parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_ID_RESP &&
-	    parse_ctx.imsi_len > 0) {
-		struct msgb *stored_msg;
-		/* Got identity response with IMSI, assuming the request had
-		 * been generated by the gbproxy */
-
-		LOGP(DLLC, LOGL_DEBUG,
-		     "NSEI=%d(BSS) IMSI acquisition succeeded, "
-		     "flushing stored messages\n",
-		     msgb_nsei(msg));
-
-		/* TODO: Patch and flush stored messages towards the SGSN */
-		while ((stored_msg = msgb_dequeue(&tlli_info->stored_msgs))) {
-			struct gprs_gb_parse_context tmp_parse_ctx = {0};
-			tmp_parse_ctx.to_bss = 0;
-			len_change = 0;
-
-			gprs_gb_parse_bssgp(msgb_bssgph(stored_msg),
-					    msgb_bssgp_len(stored_msg),
-					    &tmp_parse_ctx);
-			gbproxy_patch_bssgp(msg, msgb_bssgph(stored_msg),
-					    msgb_bssgp_len(stored_msg),
-					    peer, tlli_info, &len_change,
-					    &tmp_parse_ctx);
-
-			gbproxy_update_tlli_state_after(peer, tlli_info, now,
-							&tmp_parse_ctx);
-
-			rc = gbprox_relay2sgsn(cfg, stored_msg, msgb_bvci(msg),
-					       sgsn_nsei);
-
-			if (rc < 0)
-				LOGP(DLLC, LOGL_ERROR,
-				     "NSEI=%d(BSS) failed to send stored message "
-				     "(%s)\n",
-				     msgb_nsei(msg),
-				     parse_ctx.llc_msg_name ? parse_ctx.llc_msg_name : "BSSGP");
-			msgb_free(stored_msg);
-		}
-
-		tlli_info->imsi_acq_pending = 0;
-
-		return 0;
+	/* Handle IMSI acquisition */
+	if (cfg->acquire_imsi) {
+		rc = gbproxy_imsi_acquisition(peer, msg, sgsn_nsei, now,
+					      tlli_info, &parse_ctx);
+		if (rc <= 0)
+			return rc;
 	}
 
-	if (tlli_info && tlli_info->imsi_acq_pending) {
-		struct msgb *stored_msg;
-
-		LOGP(DLLC, LOGL_DEBUG,
-		     "NSEI=%d(BSS) IMSI acquisition in progess, "
-		     "storing message (%s)\n",
-		     msgb_nsei(msg),
-		     parse_ctx.llc_msg_name ? parse_ctx.llc_msg_name : "BSSGP");
-
-		/* Enqueue unpatched messages */
-		stored_msg = gprs_msgb_copy(msg, "process_bssgp_ul");
-		msgb_enqueue(&tlli_info->stored_msgs, stored_msg);
-
-		/* TODO: Timeout, retransmit IDENT REQ if expired */
-		return 0;
-	}
-
-	if (cfg->acquire_imsi && tlli_info && tlli_info->mi_data_len == 0) {
-		struct msgb *idreq_msg;
-		struct msgb *stored_msg = gprs_msgb_copy(msg, "process_bssgp_ul");
-
-		LOGP(DLLC, LOGL_DEBUG,
-		     "NSEI=%d(BSS) IMSI is required but not available, "
-		     "storing message (%s)\n",
-		     msgb_nsei(msg),
-		     parse_ctx.llc_msg_name ? parse_ctx.llc_msg_name : "BSSGP");
-
-		/* Enqueue message */
-		msgb_enqueue(&tlli_info->stored_msgs, stored_msg);
-
-		/* Send IDENT REQ */
-		idreq_msg = gsm48_msgb_alloc();
-		gprs_put_identity_req(idreq_msg, GSM_MI_TYPE_IMSI);
-		/* Workaround to avoid N(U) collisions and to enable a restart
-		 * of the IMSI acquisition procedure. This will work unless the
-		 * SGSN has an initial V(UT) within [256-32, 256+n_retries]
-		 * (see GSM 04.64, 8.4.2).
-		 * TODO: Check whether it is sensible to rely on this, it might
-		 * be an issue when P-TMSI patching is _not_ enabled and the SGSN
-		 * doesn't reset V(UT) after a detach. */
-		gprs_push_llc_ui(idreq_msg, 0, GPRS_SAPI_GMM,
-				 (tlli_info->imsi_acq_retries + 256) % 512);
-		gprs_push_bssgp_dl_unitdata(idreq_msg, tlli_info->tlli.current);
-		gbprox_relay2peer(idreq_msg, peer, msgb_bvci(msg));
-		msgb_free(idreq_msg);
-
-		tlli_info->imsi_acq_pending = 1;
-
-		return 0;
-	}
-
-
 	gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg),
 			    peer, tlli_info, &len_change, &parse_ctx);
 
@@ -502,21 +574,12 @@
 		}
 	}
 
-	if (!peer && msgb_bvci(msg) >= 2)
-		peer = gbproxy_peer_by_bvci(cfg, msgb_bvci(msg));
+	/* Get peer */
+	if (!peer)
+		peer = gbproxy_find_peer(cfg, msg, &parse_ctx);
 
 	if (!peer)
-		peer = gbproxy_peer_by_bssgp_tlv(cfg, &parse_ctx.bssgp_tp);
-
-	if (!peer) {
-		LOGP(DLLC, LOGL_INFO,
-		     "NSEI=%d(SGSN) patching: didn't find peer for message, "
-		     "PDU %d\n",
-		     msgb_nsei(msg), parse_ctx.pdu_type);
-		/* Increment counter */
-		rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]);
 		return;
-	}
 
 	now = time(NULL);