gbproxy: Refactor gbprox_patch_raid(), use different RAI types properly

Currently gbprox_patch_raid() updates the local MCC/MNC with every
BSS originated message, even if the RAI is an 'old' one.

This patch separates state updating and patching into 2 functions
gbprox_update_current_raid and gbprox_patch_raid. In addition, a
field named old_raid_enc is added to gbproxy_parse_context, which is
used for 'old RAI' IEs in Attach Requests and RA Update Requests.
Only the bssg_raid_enc in BSS originated message is used to update
the BSS side 'local' MCC/MNC.

Sponsored-by: On-Waves ehf
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c
index 0d738aa..eb1a699 100644
--- a/openbsc/src/gprs/gb_proxy.c
+++ b/openbsc/src/gprs/gb_proxy.c
@@ -409,6 +409,7 @@
 	uint8_t *ptmsi_enc;
 	uint8_t *new_ptmsi_enc;
 	uint8_t *raid_enc;
+	uint8_t *old_raid_enc;
 	uint8_t *bssgp_raid_enc;
 	uint8_t *bssgp_ptimsi;
 
@@ -418,6 +419,7 @@
 	int need_decryption;
 	uint32_t tlli;
 	int pdu_type;
+	int old_raid_matches;
 };
 
 struct gbproxy_tlli_info *gbprox_find_tlli(struct gbproxy_peer *peer,
@@ -821,13 +823,51 @@
 	return need_at_least <= peer->cfg->patch_mode;
 }
 
-/* patch RA identifier in place, update peer accordingly */
-static void gbprox_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer,
-			      int to_bss, const char *log_text)
+/* update peer according to the BSS message */
+static void gbprox_update_current_raid(uint8_t *raid_enc,
+				       struct gbproxy_peer *peer,
+				       const char *log_text)
 {
 	struct gbproxy_patch_state *state = &peer->patch_state;
 	const int old_local_mcc = state->local_mcc;
 	const int old_local_mnc = state->local_mnc;
+	struct gprs_ra_id raid;
+
+	if (!raid_enc)
+		return;
+
+	gsm48_parse_ra(&raid, raid_enc);
+
+	/* save source side MCC/MNC */
+	if (!peer->cfg->core_mcc || raid.mcc == peer->cfg->core_mcc) {
+		state->local_mcc = 0;
+	} else {
+		state->local_mcc = raid.mcc;
+	}
+
+	if (!peer->cfg->core_mnc || raid.mnc == peer->cfg->core_mnc) {
+		state->local_mnc = 0;
+	} else {
+		state->local_mnc = raid.mnc;
+	}
+
+	if (old_local_mcc != state->local_mcc ||
+	    old_local_mnc != state->local_mnc)
+		LOGP(DGPRS, LOGL_NOTICE,
+		     "Patching RAID %sactivated, msg: %s, "
+		     "local: %d-%d, core: %d-%d\n",
+		     state->local_mcc || state->local_mnc ?
+		     "" : "de",
+		     log_text,
+		     state->local_mcc, state->local_mnc,
+		     peer->cfg->core_mcc, peer->cfg->core_mnc);
+}
+
+/* patch RA identifier in place */
+static void gbprox_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer,
+			      int to_bss, const char *log_text)
+{
+	struct gbproxy_patch_state *state = &peer->patch_state;
 	int old_mcc;
 	int old_mnc;
 	struct gprs_ra_id raid;
@@ -839,20 +879,11 @@
 
 	if (!to_bss) {
 		/* BSS -> SGSN */
-		/* save BSS side MCC/MNC */
-		if (!peer->cfg->core_mcc || raid.mcc == peer->cfg->core_mcc) {
-			state->local_mcc = 0;
-		} else {
-			state->local_mcc = raid.mcc;
+		if (state->local_mcc)
 			raid.mcc = peer->cfg->core_mcc;
-		}
 
-		if (!peer->cfg->core_mnc || raid.mnc == peer->cfg->core_mnc) {
-			state->local_mnc = 0;
-		} else {
-			state->local_mnc = raid.mnc;
+		if (state->local_mnc)
 			raid.mnc = peer->cfg->core_mnc;
-		}
 	} else {
 		/* SGSN -> BSS */
 		if (state->local_mcc)
@@ -862,18 +893,6 @@
 			raid.mnc = state->local_mnc;
 	}
 
-	if (old_local_mcc != state->local_mcc ||
-	    old_local_mnc != state->local_mnc)
-		LOGP(DGPRS, LOGL_NOTICE,
-		     "Patching RAID %sactivated, msg: %s, "
-		     "local: %d-%d, core: %d-%d, to %s\n",
-		     state->local_mcc || state->local_mnc ?
-		     "" : "de",
-		     log_text,
-		     state->local_mcc, state->local_mnc,
-		     peer->cfg->core_mcc, peer->cfg->core_mnc,
-		     to_bss ? "BSS" : "SGSN");
-
 	if (state->local_mcc || state->local_mnc) {
 		enum gbprox_peer_ctr counter =
 			to_bss ?
@@ -979,7 +998,7 @@
 	if (v_fixed_shift(&data, &data_len, 6, &value) <= 0)
 		return 0;
 
-	parse_ctx->raid_enc = value;
+	parse_ctx->old_raid_enc = value;
 
 	return 1;
 }
@@ -1069,7 +1088,7 @@
 	if (v_fixed_shift(&data, &data_len, 6, &value) <= 0)
 		return 0;
 
-	parse_ctx->raid_enc = value;
+	parse_ctx->old_raid_enc = value;
 
 	return 1;
 }
@@ -1339,14 +1358,18 @@
 		return have_patched;
 
 	if (parse_ctx->raid_enc) {
-		/* TODO: BSS->SGSN: Only patch if matches original BSSGP,
-		 * don't update internal mapping, patch to invalid if P-TMSI
-		 * unknown. */
 		gbprox_patch_raid(parse_ctx->raid_enc, peer, parse_ctx->to_bss,
 				  parse_ctx->llc_msg_name);
 		have_patched = 1;
 	}
 
+	if (parse_ctx->old_raid_enc && parse_ctx->old_raid_matches) {
+		/* TODO: Patch to invalid if P-TMSI unknown. */
+		gbprox_patch_raid(parse_ctx->old_raid_enc, peer, parse_ctx->to_bss,
+				  parse_ctx->llc_msg_name);
+		have_patched = 1;
+	}
+
 	if (parse_ctx->apn_ie &&
 	    peer->cfg->core_apn &&
 	    !parse_ctx->to_bss &&
@@ -1399,6 +1422,30 @@
 		sep = ",";
 	}
 
+	if (parse_ctx->bssgp_raid_enc) {
+		struct gprs_ra_id raid;
+		gsm48_parse_ra(&raid, parse_ctx->bssgp_raid_enc);
+		LOGP(DGPRS, LOGL_DEBUG, "%s BSSGP RAID %u-%u-%u-%u", sep,
+		     raid.mcc, raid.mnc, raid.lac, raid.rac);
+		sep = ",";
+	}
+
+	if (parse_ctx->raid_enc) {
+		struct gprs_ra_id raid;
+		gsm48_parse_ra(&raid, parse_ctx->raid_enc);
+		LOGP(DGPRS, LOGL_DEBUG, "%s RAID %u-%u-%u-%u", sep,
+		     raid.mcc, raid.mnc, raid.lac, raid.rac);
+		sep = ",";
+	}
+
+	if (parse_ctx->old_raid_enc) {
+		struct gprs_ra_id raid;
+		gsm48_parse_ra(&raid, parse_ctx->old_raid_enc);
+		LOGP(DGPRS, LOGL_DEBUG, "%s old RAID %u-%u-%u-%u", sep,
+		     raid.mcc, raid.mnc, raid.lac, raid.rac);
+		sep = ",";
+	}
+
 	if (parse_ctx->ptmsi_enc) {
 		uint32_t ptmsi = GSM_RESERVED_TMSI;
 		int ok;
@@ -1840,6 +1887,13 @@
 
 	now = time(NULL);
 
+	if (parse_ctx.bssgp_raid_enc && parse_ctx.old_raid_enc &&
+	    memcmp(parse_ctx.bssgp_raid_enc, parse_ctx.old_raid_enc, 6) == 0)
+		parse_ctx.old_raid_matches = 1;
+
+	gbprox_update_current_raid(parse_ctx.bssgp_raid_enc, peer,
+				   parse_ctx.llc_msg_name);
+
 	tlli_info = gbprox_update_state_ul(peer, now, &parse_ctx);
 
 	gbprox_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg),