aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <holger@moiji-mobile.com>2013-08-22 08:44:38 +0200
committerIvan Kluchnikov <kluchnikovi@gmail.com>2013-09-04 21:28:53 +0400
commitbc1e52cfbf89dc9c669281181f99dc5098ea8364 (patch)
treed939d9936e8141d0890a5f77266b103352ac4b19 /tests
parent741481d3e007f6cc38b3e8d090a49be1caf99b12 (diff)
emu: Use OpenBSC code to decode the LLC and add assertions
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.
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/emu/openbsc_clone.c216
-rw-r--r--tests/emu/openbsc_clone.h64
-rw-r--r--tests/emu/pcu_emu.cpp7
-rw-r--r--tests/emu/test_replay_gprs_attach.cpp39
5 files changed, 328 insertions, 1 deletions
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@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@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);
+ }
+}