diff options
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/emu/openbsc_clone.c | 216 | ||||
-rw-r--r-- | tests/emu/openbsc_clone.h | 64 | ||||
-rw-r--r-- | tests/emu/pcu_emu.cpp | 7 | ||||
-rw-r--r-- | tests/emu/test_replay_gprs_attach.cpp | 39 |
5 files changed, 328 insertions, 1 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index 7216d3b5..0131e47a 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 00000000..707819bb --- /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 00000000..d62ff22a --- /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 337c0c8f..f4d38cdb 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 500b16e1..fb1e77f7 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); + } +} |