[PATCH 09/10] emu: Use OpenBSC code to decode the LLC and add assertions

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/osmocom-net-gprs@lists.osmocom.org/.

Holger Freyther hfreyther at sysmocom.de
Thu Aug 22 07:17:03 UTC 2013


From: Holger Hans Peter Freyther <holger at moiji-mobile.com>

Use the OpenBSC SGSN code to parse the LLC data and look into
the data we receive. Add assertions to verify the the sequence
number is increasing.
---
 tests/Makefile.am                     |   3 +-
 tests/emu/openbsc_clone.c             | 216 ++++++++++++++++++++++++++++++++++
 tests/emu/openbsc_clone.h             |  64 ++++++++++
 tests/emu/pcu_emu.cpp                 |   7 ++
 tests/emu/test_replay_gprs_attach.cpp |  39 ++++++
 5 files changed, 328 insertions(+), 1 deletion(-)
 create mode 100644 tests/emu/openbsc_clone.c
 create mode 100644 tests/emu/openbsc_clone.h

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 7216d3b..0131e47 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -9,7 +9,8 @@ rlcmac_RLCMACTest_LDADD = \
 	$(LIBOSMOCORE_LIBS) \
 	$(COMMON_LA)
 
-emu_pcu_emu_SOURCES = emu/pcu_emu.cpp emu/test_replay_gprs_attach.cpp
+emu_pcu_emu_SOURCES = emu/pcu_emu.cpp emu/test_replay_gprs_attach.cpp \
+	emu/openbsc_clone.c emu/openbsc_clone.h
 emu_pcu_emu_LDADD = \
 	$(top_builddir)/src/libgprs.la \
 	$(LIBOSMOGB_LIBS) \
diff --git a/tests/emu/openbsc_clone.c b/tests/emu/openbsc_clone.c
new file mode 100644
index 0000000..707819b
--- /dev/null
+++ b/tests/emu/openbsc_clone.c
@@ -0,0 +1,216 @@
+/* (C) 2009-2010 by Harald Welte <laforge at gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include "openbsc_clone.h"
+
+#include <gprs_debug.h>
+
+#include <osmocom/core/utils.h>
+
+#include <errno.h>
+
+/* Section 6.4 Commands and Responses */
+enum gprs_llc_u_cmd {
+	GPRS_LLC_U_DM_RESP		= 0x01,
+	GPRS_LLC_U_DISC_CMD		= 0x04,
+	GPRS_LLC_U_UA_RESP		= 0x06,
+	GPRS_LLC_U_SABM_CMD		= 0x07,
+	GPRS_LLC_U_FRMR_RESP		= 0x08,
+	GPRS_LLC_U_XID			= 0x0b,
+	GPRS_LLC_U_NULL_CMD		= 0x00,
+};
+
+#define LLC_ALLOC_SIZE 16384
+#define UI_HDR_LEN	3
+#define N202		4
+#define CRC24_LENGTH	3
+
+static const struct value_string llc_cmd_strs[] = {
+	{ GPRS_LLC_NULL,	"NULL" },
+	{ GPRS_LLC_RR,		"RR" },
+	{ GPRS_LLC_ACK,		"ACK" },
+	{ GPRS_LLC_RNR,		"RNR" },
+	{ GPRS_LLC_SACK,	"SACK" },
+	{ GPRS_LLC_DM,		"DM" },
+	{ GPRS_LLC_DISC,	"DISC" },
+	{ GPRS_LLC_UA,		"UA" },
+	{ GPRS_LLC_SABM,	"SABM" },
+	{ GPRS_LLC_FRMR,	"FRMR" },
+	{ GPRS_LLC_XID,		"XID" },
+	{ GPRS_LLC_UI,		"UI" },
+	{ 0, NULL }
+};
+
+int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp,
+			      const uint8_t *llc_hdr, int len)
+{
+	const uint8_t *ctrl = llc_hdr+1;
+
+	if (len <= CRC24_LENGTH)
+		return -EIO;
+
+	ghp->crc_length = len - CRC24_LENGTH;
+
+	ghp->ack_req = 0;
+
+	/* Section 5.5: FCS */
+	ghp->fcs = *(llc_hdr + len - 3);
+	ghp->fcs |= *(llc_hdr + len - 2) << 8;
+	ghp->fcs |= *(llc_hdr + len - 1) << 16;
+
+	/* Section 6.2.1: invalid PD field */
+	if (llc_hdr[0] & 0x80)
+		return -EIO;
+
+	/* This only works for the MS->SGSN direction */
+	if (llc_hdr[0] & 0x40)
+		ghp->is_cmd = 0;
+	else
+		ghp->is_cmd = 1;
+
+	ghp->sapi = llc_hdr[0] & 0xf;
+
+	/* Section 6.2.3: check for reserved SAPI */
+	switch (ghp->sapi) {
+	case 0:
+	case 4:
+	case 6:
+	case 0xa:
+	case 0xc:
+	case 0xd:
+	case 0xf:
+		return -EINVAL;
+	}
+
+	if ((ctrl[0] & 0x80) == 0) {
+		/* I (Information transfer + Supervisory) format */
+		uint8_t k;
+
+		ghp->data = ctrl + 3;
+
+		if (ctrl[0] & 0x40)
+			ghp->ack_req = 1;
+
+		ghp->seq_tx  = (ctrl[0] & 0x1f) << 4;
+		ghp->seq_tx |= (ctrl[1] >> 4);
+
+		ghp->seq_rx  = (ctrl[1] & 0x7) << 6;
+		ghp->seq_rx |= (ctrl[2] >> 2);
+
+		switch (ctrl[2] & 0x03) {
+		case 0:
+			ghp->cmd = GPRS_LLC_RR;
+			break;
+		case 1:
+			ghp->cmd = GPRS_LLC_ACK;
+			break;
+		case 2:
+			ghp->cmd = GPRS_LLC_RNR;
+			break;
+		case 3:
+			ghp->cmd = GPRS_LLC_SACK;
+			k = ctrl[3] & 0x1f;
+			ghp->data += 1 + k;
+			break;
+		}
+		ghp->data_len = (llc_hdr + len - 3) - ghp->data;
+	} else if ((ctrl[0] & 0xc0) == 0x80) {
+		/* S (Supervisory) format */
+		ghp->data = NULL;
+		ghp->data_len = 0;
+
+		if (ctrl[0] & 0x20)
+			ghp->ack_req = 1;
+		ghp->seq_rx  = (ctrl[0] & 0x7) << 6;
+		ghp->seq_rx |= (ctrl[1] >> 2);
+
+		switch (ctrl[1] & 0x03) {
+		case 0:
+			ghp->cmd = GPRS_LLC_RR;
+			break;
+		case 1:
+			ghp->cmd = GPRS_LLC_ACK;
+			break;
+		case 2:
+			ghp->cmd = GPRS_LLC_RNR;
+			break;
+		case 3:
+			ghp->cmd = GPRS_LLC_SACK;
+			break;
+		}
+	} else if ((ctrl[0] & 0xe0) == 0xc0) {
+		/* UI (Unconfirmed Inforamtion) format */
+		ghp->cmd = GPRS_LLC_UI;
+		ghp->data = ctrl + 2;
+		ghp->data_len = (llc_hdr + len - 3) - ghp->data;
+
+		ghp->seq_tx  = (ctrl[0] & 0x7) << 6;
+		ghp->seq_tx |= (ctrl[1] >> 2);
+		if (ctrl[1] & 0x02) {
+			ghp->is_encrypted = 1;
+			/* FIXME: encryption */
+		}
+		if (ctrl[1] & 0x01) {
+			/* FCS over hdr + all inf fields */
+		} else {
+			/* FCS over hdr + N202 octets (4) */
+			if (ghp->crc_length > UI_HDR_LEN + N202)
+				ghp->crc_length = UI_HDR_LEN + N202;
+		}
+	} else {
+		/* U (Unnumbered) format: 1 1 1 P/F M4 M3 M2 M1 */
+		ghp->data = NULL;
+		ghp->data_len = 0;
+
+		switch (ctrl[0] & 0xf) {
+		case GPRS_LLC_U_NULL_CMD:
+			ghp->cmd = GPRS_LLC_NULL;
+			break;
+		case GPRS_LLC_U_DM_RESP:
+			ghp->cmd = GPRS_LLC_DM;
+			break;
+		case GPRS_LLC_U_DISC_CMD:
+			ghp->cmd = GPRS_LLC_DISC;
+			break;
+		case GPRS_LLC_U_UA_RESP:
+			ghp->cmd = GPRS_LLC_UA;
+			break;
+		case GPRS_LLC_U_SABM_CMD:
+			ghp->cmd = GPRS_LLC_SABM;
+			break;
+		case GPRS_LLC_U_FRMR_RESP:
+			ghp->cmd = GPRS_LLC_FRMR;
+			break;
+		case GPRS_LLC_U_XID:
+			ghp->cmd = GPRS_LLC_XID;
+			ghp->data = ctrl + 1;
+			ghp->data_len = (llc_hdr + len - 3) - ghp->data;
+			break;
+		default:
+			return -EIO;
+		}
+	}
+
+	/* FIXME: parse sack frame */
+	if (ghp->cmd == GPRS_LLC_SACK) {
+		LOGP(DPCU, LOGL_NOTICE, "Unsupported SACK frame\n");
+		return -EIO;
+	}
+
+	return 0;
+}
diff --git a/tests/emu/openbsc_clone.h b/tests/emu/openbsc_clone.h
new file mode 100644
index 0000000..d62ff22
--- /dev/null
+++ b/tests/emu/openbsc_clone.h
@@ -0,0 +1,64 @@
+/* (C) 2009-2010 by Harald Welte <laforge at gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef OPENBSC_CLONE_H
+#define OPENBSC_CLONE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+enum gprs_llc_cmd {
+	GPRS_LLC_NULL,
+	GPRS_LLC_RR,
+	GPRS_LLC_ACK,
+	GPRS_LLC_RNR,
+	GPRS_LLC_SACK,
+	GPRS_LLC_DM,
+	GPRS_LLC_DISC,
+	GPRS_LLC_UA,
+	GPRS_LLC_SABM,
+	GPRS_LLC_FRMR,
+	GPRS_LLC_XID,
+	GPRS_LLC_UI,
+};
+
+struct gprs_llc_hdr_parsed {
+	uint8_t sapi;
+	uint8_t is_cmd:1,
+		 ack_req:1,
+		 is_encrypted:1;
+	uint32_t seq_rx;
+	uint32_t seq_tx;
+	uint32_t fcs;
+	uint32_t fcs_calc;
+	const uint8_t *data;
+	uint16_t data_len;
+	uint16_t crc_length;
+	enum gprs_llc_cmd cmd;
+};
+
+int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp, const uint8_t *llc_hdr, int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/tests/emu/pcu_emu.cpp b/tests/emu/pcu_emu.cpp
index 337c0c8..f4d38cd 100644
--- a/tests/emu/pcu_emu.cpp
+++ b/tests/emu/pcu_emu.cpp
@@ -37,6 +37,7 @@ struct gprs_rlcmac_bts *gprs_rlcmac_bts;
 int16_t spoof_mnc = 0, spoof_mcc = 0;
 
 extern void test_replay_gprs_attach(struct gprs_bssgp_pcu *pcu);
+extern void test_replay_gprs_data(struct gprs_bssgp_pcu *, struct msgb *, struct tlv_parsed *);
 
 struct gprs_rlcmac_bts *create_bts()
 {
@@ -70,6 +71,11 @@ static void bvci_unblocked(struct gprs_bssgp_pcu *pcu)
 	test_replay_gprs_attach(pcu);
 }
 
+static void bssgp_data(struct gprs_bssgp_pcu *pcu, struct msgb *msg, struct tlv_parsed *tp)
+{
+	test_replay_gprs_data(pcu, msg, tp);
+}
+
 void create_and_connect_bssgp(struct gprs_rlcmac_bts *bts,
 			uint32_t sgsn_ip, uint16_t sgsn_port)
 {
@@ -78,6 +84,7 @@ void create_and_connect_bssgp(struct gprs_rlcmac_bts *bts,
 	pcu = gprs_bssgp_create_and_connect(bts, 0, sgsn_ip, sgsn_port,
 					20, 20, 20, 0x901, 0x99, 1, 0, 0);
 	pcu->on_unblock_ack = bvci_unblocked;
+	pcu->on_dl_unit_data = bssgp_data;
 }
 
 int main(int argc, char **argv)
diff --git a/tests/emu/test_replay_gprs_attach.cpp b/tests/emu/test_replay_gprs_attach.cpp
index 500b16e..fb1e77f 100644
--- a/tests/emu/test_replay_gprs_attach.cpp
+++ b/tests/emu/test_replay_gprs_attach.cpp
@@ -19,13 +19,18 @@
 
 extern "C" {
 #include <osmocom/core/msgb.h>
+#include <osmocom/core/backtrace.h>
+#include <osmocom/gsm/gsm_utils.h>
 }
 
+#include "openbsc_clone.h"
+
 #include <gprs_bssgp_pcu.h>
 
 #include <stdint.h>
 #include <string.h>
 
+/* GPRS attach with a foreign TLLI */
 static const uint8_t gprs_attach_llc[] = {
 	/* LLC-PDU IE */
 	0x0e, 0x00, 0x2e,
@@ -38,6 +43,8 @@ static const uint8_t gprs_attach_llc[] = {
 	0x42, 0x00, 0x40, 0xaa, 0xf3, 0x18
 };
 
+static int next_wanted_nu;
+
 struct msgb *create_msg(const uint8_t *data, size_t len)
 {
 	struct msgb *msg = msgb_alloc_headroom(4096, 128, "create msg");
@@ -51,6 +58,38 @@ void test_replay_gprs_attach(struct gprs_bssgp_pcu *pcu)
 	uint32_t tlli = 0xadf11820;
 	const uint8_t qos_profile[] = { 0x0, 0x0, 0x04 };
 
+	next_wanted_nu = 0;
 	struct msgb *msg = create_msg(gprs_attach_llc, ARRAY_SIZE(gprs_attach_llc));
 	bssgp_tx_ul_ud(pcu->bctx, tlli, qos_profile, msg);
 }
+
+void test_replay_gprs_data(struct gprs_bssgp_pcu *pcu, struct msgb *msg, struct tlv_parsed *tp)
+{
+	struct bssgp_ud_hdr *budh;
+	struct gprs_llc_hdr_parsed ph;
+	uint32_t tlli;
+
+	if (!TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU))
+		return;
+
+
+	gprs_llc_hdr_parse(&ph, TLVP_VAL(tp, BSSGP_IE_LLC_PDU),
+				TLVP_LEN(tp, BSSGP_IE_LLC_PDU));
+
+	budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
+	tlli = ntohl(budh->tlli);
+
+	/* all messages we should get, should be for a foreign tlli */
+	OSMO_ASSERT(gprs_tlli_type(tlli) == TLLI_FOREIGN);
+	printf("TLLI(0x%08x) is foreign!\n", tlli);
+
+	OSMO_ASSERT(ph.cmd == GPRS_LLC_UI);
+	OSMO_ASSERT(ph.sapi == 1);
+	OSMO_ASSERT(ph.seq_tx == next_wanted_nu++);
+
+	/* this test just wants to see messages... no further data is sent */
+	if (next_wanted_nu == 4) {
+		printf("Test done.\n");
+		exit(EXIT_SUCCESS);
+	}
+}
-- 
1.8.3.2





More information about the osmocom-net-gprs mailing list