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.
This commit is contained in:
Holger Hans Peter Freyther 2013-08-22 08:44:38 +02:00 committed by Ivan Kluchnikov
parent 741481d3e0
commit bc1e52cfbf
5 changed files with 328 additions and 1 deletions

View File

@ -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) \

216
tests/emu/openbsc_clone.c Normal file
View File

@ -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;
}

64
tests/emu/openbsc_clone.h Normal file
View File

@ -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

View File

@ -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)

View File

@ -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);
}
}