nat: Extract the LAC/CI from the Complete Layer3 Information

Find the Cell Identifier from the Complete Layer3 Information and
store it for future reference. We could begin to verify that the
LAC/CI used really belongs to the BSC.
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h
index c5234d4..1393945 100644
--- a/openbsc/include/openbsc/bsc_nat.h
+++ b/openbsc/include/openbsc/bsc_nat.h
@@ -502,4 +502,7 @@
 void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending);
 int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg);
 
+int bsc_nat_extract_lac(struct bsc_connection *bsc, struct nat_sccp_connection *con,
+				struct bsc_nat_parsed *parsed, struct msgb *msg);
+
 #endif
diff --git a/openbsc/include/openbsc/bsc_nat_sccp.h b/openbsc/include/openbsc/bsc_nat_sccp.h
index a21684b..34aa632 100644
--- a/openbsc/include/openbsc/bsc_nat_sccp.h
+++ b/openbsc/include/openbsc/bsc_nat_sccp.h
@@ -80,6 +80,9 @@
 	int imsi_checked;
 	char *imsi;
 
+	uint16_t lac;
+	uint16_t ci;
+
 	/* remember which Transactions we run over the bypass */
 	char ussd_ti[8];
 
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c
index 21f5287..0496802 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_nat.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c
@@ -1,8 +1,8 @@
 /* BSC Multiplexer/NAT */
 
 /*
- * (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010-2012 by On-Waves
+ * (C) 2010-2013 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010-2013 by On-Waves
  * (C) 2009 by Harald Welte <laforge@gnumonks.org>
  * All Rights Reserved
  *
@@ -1056,6 +1056,7 @@
 			con_msc = con->msc_con;
 			con->con_type = con_type;
 			con->imsi_checked = filter;
+			bsc_nat_extract_lac(bsc, con, parsed, msg);
 			if (imsi)
 				con->imsi = talloc_steal(con, imsi);
 			imsi = NULL;
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
index b4c87d0..45c224c 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
@@ -502,3 +502,65 @@
 	return rc;
 }
 
+static void extract_lac(const uint8_t *data, uint16_t *lac, uint16_t *ci)
+{
+	memcpy(lac, &data[0], sizeof(*lac));
+	memcpy(ci, &data[2], sizeof(*ci));
+
+	*lac = ntohs(*lac);
+	*ci = ntohs(*ci);
+}
+
+int bsc_nat_extract_lac(struct bsc_connection *bsc,
+			struct nat_sccp_connection *con,
+			struct bsc_nat_parsed *parsed, struct msgb *msg)
+{
+	int data_length;
+	const uint8_t *data;
+	struct tlv_parsed tp;
+	uint16_t lac, ci;
+
+	if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) {
+		LOGP(DNAT, LOGL_ERROR, "Can only extract LAC from Complete Layer3\n");
+		return -1;
+	}
+
+	if (!msg->l3h || msgb_l3len(msg) < 3) {
+		LOGP(DNAT, LOGL_ERROR, "Complete Layer3 mssage is too short.\n");
+		return -1;
+	}
+
+	tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0);
+	if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER)) {
+		LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n");
+		return -2;
+	}
+
+	data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER);
+	data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER);
+
+	/* Attemt to get the LAC/CI from it */
+	if (data[0] == CELL_IDENT_WHOLE_GLOBAL) {
+		if (data_length != 8) {
+			LOGP(DNAT, LOGL_ERROR,
+				"Ident too short: %d\n", data_length);
+			return -3;
+		}
+		extract_lac(&data[1 + 3], &lac, &ci);
+	} else if (data[0] == CELL_IDENT_LAC_AND_CI) {
+		if (data_length != 5) {
+			LOGP(DNAT, LOGL_ERROR,
+				"Ident too short: %d\n", data_length);
+			return -3;
+		}
+		extract_lac(&data[1], &lac, &ci);
+	} else {
+		LOGP(DNAT, LOGL_ERROR,
+			"Unhandled cell identifier: %d\n", data[0]);
+		return -1;
+	}
+
+	con->lac = lac;
+	con->ci = ci;
+	return 0;
+}
diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.c b/openbsc/tests/bsc-nat/bsc_nat_test.c
index 18668bb..cbc1f18 100644
--- a/openbsc/tests/bsc-nat/bsc_nat_test.c
+++ b/openbsc/tests/bsc-nat/bsc_nat_test.c
@@ -28,6 +28,7 @@
 #include <openbsc/bsc_nat_sccp.h>
 
 #include <osmocom/core/application.h>
+#include <osmocom/core/backtrace.h>
 #include <osmocom/core/talloc.h>
 
 #include <osmocom/sccp/sccp.h>
@@ -1276,6 +1277,39 @@
 	}
 }
 
+static void test_nat_extract_lac()
+{
+	int res;
+	struct bsc_connection *bsc;
+	struct bsc_nat *nat;
+	struct nat_sccp_connection con;
+	struct bsc_nat_parsed *parsed;
+	struct msgb *msg = msgb_alloc(4096, "test-message");
+
+	printf("Testing LAC extraction from SCCP CR\n");
+
+	/* initialize the testcase */
+	nat = bsc_nat_alloc();
+	bsc = bsc_connection_alloc(nat);
+	bsc->cfg = bsc_config_alloc(nat, "foo");
+
+	memset(&con, 0, sizeof(con));
+	con.bsc = bsc;
+
+	/* create the SCCP CR */
+	msg->l2h = msgb_put(msg, ARRAY_SIZE(bssmap_cr));
+	memcpy(msg->l2h, bssmap_cr, ARRAY_SIZE(bssmap_cr));
+
+	/* parse it and pass it on */
+	parsed = bsc_nat_parse(msg);
+	res = bsc_nat_extract_lac(bsc, &con, parsed, msg);
+	OSMO_ASSERT(res == 0);
+
+	/* verify the LAC */
+	OSMO_ASSERT(con.lac == 8210);
+	OSMO_ASSERT(con.ci == 50000);
+}
+
 int main(int argc, char **argv)
 {
 	sccp_set_log_area(DSCCP);
@@ -1295,6 +1329,7 @@
 	test_sms_number_rewrite();
 	test_mgcp_allocations();
 	test_barr_list_parsing();
+	test_nat_extract_lac();
 
 	printf("Testing execution completed.\n");
 	return 0;
diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.ok b/openbsc/tests/bsc-nat/bsc_nat_test.ok
index cbedc85..ab04f42 100644
--- a/openbsc/tests/bsc-nat/bsc_nat_test.ok
+++ b/openbsc/tests/bsc-nat/bsc_nat_test.ok
@@ -35,4 +35,5 @@
 IMSI: 12123127 CM: 3 LU: 5
 IMSI: 12123128 CM: 3 LU: 6
 IMSI: 12123124 CM: 3 LU: 2
+Testing LAC extraction from SCCP CR
 Testing execution completed.