nat: Parse the id response, extract the IMSI, compare it

Add a test case and also add a basic check that we got some
size checks correct. The next step is to act on the result.
diff --git a/openbsc/src/nat/bsc_nat_utils.c b/openbsc/src/nat/bsc_nat_utils.c
index 7387b51..a591737 100644
--- a/openbsc/src/nat/bsc_nat_utils.c
+++ b/openbsc/src/nat/bsc_nat_utils.c
@@ -369,6 +369,35 @@
 	return auth_imsi(bsc, mi_string);
 }
 
+static int _dt_check_id_resp(struct bsc_connection *bsc,
+			     uint8_t *data, unsigned int length,
+			     struct sccp_connections *con)
+{
+	char mi_string[GSM48_MI_SIZE];
+	uint8_t mi_type;
+	int ret;
+
+	if (length < 2) {
+		LOGP(DNAT, LOGL_ERROR, "mi does not fit.\n");
+		return -1;
+	}
+
+	if (data[0] < length - 1) {
+		LOGP(DNAT, LOGL_ERROR, "mi length too big.\n");
+		return -2;
+	}
+
+	mi_type = data[1] & GSM_MI_TYPE_MASK;
+	gsm48_mi_to_string(mi_string, sizeof(mi_string), &data[1], data[0]);
+
+	if (mi_type != GSM_MI_TYPE_IMSI)
+		return 0;
+
+	ret = auth_imsi(bsc, mi_string);
+	con->imsi_checked = 1;
+	return ret;
+}
+
 /* Filter out CR data... */
 int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed, int *con_type)
 {
@@ -434,10 +463,38 @@
 int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg,
 		      struct sccp_connections *con, struct bsc_nat_parsed *parsed)
 {
+	uint32_t len;
+	struct gsm48_hdr *hdr48;
+
 	if (con->imsi_checked)
 		return 0;
 
-	return 0;
+	/* only care about DTAP messages */
+	if (parsed->bssap != BSSAP_MSG_DTAP)
+		return 0;
+
+	/* gsm_type is actually the size of the dtap */
+	len = parsed->gsm_type;
+	if (len < msgb_l3len(msg) - 3) {
+		LOGP(DNAT, LOGL_ERROR, "Not enough space for DTAP.\n");
+		return -1;
+	}
+
+	if (len < sizeof(*hdr48)) {
+		LOGP(DNAT, LOGL_ERROR, "GSM48 header does not fit.\n");
+		return -1;
+	}
+
+	msg->l4h = &msg->l3h[3];
+	hdr48 = (struct gsm48_hdr *) msg->l4h;
+
+	if (hdr48->proto_discr == GSM48_PDISC_MM &&
+	    (hdr48->msg_type & 0xbf) == GSM48_MT_MM_ID_RESP) {
+		return _dt_check_id_resp(bsc, &hdr48->data[0], len - sizeof(*hdr48), con);
+	} else {
+		printf("%d %x\n", hdr48->proto_discr, hdr48->msg_type);
+		return 0;
+	}
 }
 
 void bsc_parse_reg(void *ctx, regex_t *reg, char **imsi, int argc, const char **argv)
diff --git a/openbsc/tests/bsc-nat/bsc_data.c b/openbsc/tests/bsc-nat/bsc_data.c
index a323979..1bc15c8 100644
--- a/openbsc/tests/bsc-nat/bsc_data.c
+++ b/openbsc/tests/bsc-nat/bsc_data.c
@@ -88,6 +88,14 @@
 0x01, 0x0b, 0x03, 0x01, 0x0a, 0x11, 0x01, 0x00,
 0x15 };
 
+/* identity response */
+static const uint8_t id_resp[] = {
+0x00, 0x15, 0xfd, 0x06, 0x01, 0x1c, 0xdc,
+0x00, 0x01, 0x0e, 0x01, 0x00, 0x0b, 0x05, 0x59,
+0x08, 0x29, 0x40, 0x21, 0x03, 0x07, 0x48, 0x66,
+0x31
+};
+
 /*
  * MGCP messages
  */
diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.c b/openbsc/tests/bsc-nat/bsc_nat_test.c
index 0e936ed..926c97c 100644
--- a/openbsc/tests/bsc-nat/bsc_nat_test.c
+++ b/openbsc/tests/bsc-nat/bsc_nat_test.c
@@ -31,6 +31,7 @@
 #include <osmocore/talloc.h>
 
 #include <osmocom/sccp/sccp.h>
+#include <osmocore/protocol/gsm_08_08.h>
 
 #include <stdio.h>
 
@@ -696,6 +697,58 @@
 	msgb_free(msg);
 }
 
+static void test_dt_filter()
+{
+	int i;
+	struct msgb *msg = msgb_alloc(4096, "test_dt_filter");
+	struct bsc_nat_parsed *parsed;
+
+	struct bsc_nat *nat = bsc_nat_alloc();
+	struct bsc_connection *bsc = bsc_connection_alloc(nat);
+	struct sccp_connections *con = talloc_zero(0, struct sccp_connections);
+
+	bsc->cfg = bsc_config_alloc(nat, "foo", 23);
+	con->bsc = bsc;
+
+	msgb_reset(msg);
+	copy_to_msg(msg, id_resp, ARRAY_SIZE(id_resp));
+
+	parsed = bsc_nat_parse(msg);
+	if (!parsed) {
+		fprintf(stderr, "FAIL: Could not parse ID resp\n");
+		abort();
+	}
+
+	if (parsed->bssap != BSSAP_MSG_DTAP) {
+		fprintf(stderr, "FAIL: It should be dtap\n");
+		abort();
+	}
+
+	/* gsm_type is actually the size of the dtap */
+	if (parsed->gsm_type < msgb_l3len(msg) - 3) {
+		fprintf(stderr, "FAIL: Not enough space for the content\n");
+		abort();
+	}
+
+	if (bsc_nat_filter_dt(bsc, msg, con, parsed) != 1) {
+		fprintf(stderr, "FAIL: Should have passed..\n");
+		abort();
+	}
+
+	/* just some basic length checking... */
+	for (i = ARRAY_SIZE(id_resp); i >= 0; --i) {
+		msgb_reset(msg);
+		copy_to_msg(msg, id_resp, ARRAY_SIZE(id_resp));
+
+		parsed = bsc_nat_parse(msg);
+		if (!parsed)
+			continue;
+
+		con->imsi_checked = 0;
+		bsc_nat_filter_dt(bsc, msg, con, parsed);
+	}
+}
+
 int main(int argc, char **argv)
 {
 	struct log_target *stderr_target;
@@ -714,6 +767,7 @@
 	test_mgcp_rewrite();
 	test_mgcp_parse();
 	test_cr_filter();
+	test_dt_filter();
 	return 0;
 }