This patch adds a test suite for the BSSGP protocol. The first (and only) test checks the handling of BSSGP SUSPEND/RESUME.
Sponsored-by: On-Waves ehf --- tests/Makefile.am | 8 ++- tests/gb/gprs_bssgp_test.c | 151 ++++++++++++++++++++++++++++++++++++++++++++ tests/gb/gprs_bssgp_test.ok | 5 ++ tests/testsuite.at | 6 ++ 4 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 tests/gb/gprs_bssgp_test.c create mode 100644 tests/gb/gprs_bssgp_test.ok
diff --git a/tests/Makefile.am b/tests/Makefile.am index 3f7db1f..b7ae607 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -5,8 +5,8 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \ smscb/smscb_test bits/bitrev_test a5/a5_test \ conv/conv_test auth/milenage_test lapd/lapd_test \ gsm0808/gsm0808_test gsm0408/gsm0408_test \ - gb/bssgp_fc_test gb/gprs_ns_test kasumi/kasumi_test \ - logging/logging_test fr/fr_test \ + gb/bssgp_fc_test gb/gprs_bssgp_test gb/gprs_ns_test \ + kasumi/kasumi_test logging/logging_test fr/fr_test \ loggingrb/loggingrb_test strrb/strrb_test \ vty/vty_test comp128/comp128_test utils/utils_test
@@ -62,6 +62,9 @@ ussd_ussd_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gs gb_bssgp_fc_test_SOURCES = gb/bssgp_fc_test.c gb_bssgp_fc_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la
+gb_gprs_bssgp_test_SOURCES = gb/gprs_bssgp_test.c +gb_gprs_bssgp_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DL) + gb_gprs_ns_test_SOURCES = gb/gprs_ns_test.c gb_gprs_ns_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DL)
@@ -106,6 +109,7 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \ lapd/lapd_test.ok gsm0408/gsm0408_test.ok \ gsm0808/gsm0808_test.ok gb/bssgp_fc_tests.err \ gb/bssgp_fc_tests.ok gb/bssgp_fc_tests.sh \ + gb/gprs_bssgp_test.ok \ gb/gprs_ns_test.ok kasumi/kasumi_test.ok \ msgfile/msgfile_test.ok msgfile/msgconfig.cfg \ logging/logging_test.ok logging/logging_test.err \ diff --git a/tests/gb/gprs_bssgp_test.c b/tests/gb/gprs_bssgp_test.c new file mode 100644 index 0000000..a2a4068 --- /dev/null +++ b/tests/gb/gprs_bssgp_test.c @@ -0,0 +1,151 @@ +/* Test routines for the BSSGP implementation in libosmogb + * + * (C) 2014 by sysmocom s.f.m.c. GmbH + * Author: Jacob Erlbeck jerlbeck@sysmocom.de + * + * Skeleton based on bssgp_fc_test.c + * (C) 2012 by Harald Welte laforge@gnumonks.org + */ + +#undef _GNU_SOURCE +#define _GNU_SOURCE + +#include <osmocom/core/application.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/prim.h> +#include <osmocom/gprs/gprs_bssgp.h> +#include <osmocom/gprs/gprs_ns.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <netinet/ip.h> +#include <dlfcn.h> + +#define BSS_NSEI 0x0b55 + +static struct osmo_prim_hdr last_oph = {0}; + +/* override */ +ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen) +{ + typedef ssize_t (*sendto_t)(int, const void *, size_t, int, + const struct sockaddr *, socklen_t); + static sendto_t real_sendto = NULL; + uint32_t dest_host = htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr); + + if (!real_sendto) + real_sendto = dlsym(RTLD_NEXT, "sendto"); + + fprintf(stderr, "MESSAGE to 0x%08x, msg length %d\n%s\n", + dest_host, len, osmo_hexdump(buf, len)); + + return len; +} + +/* override */ +int gprs_ns_callback(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, + struct msgb *msg, uint16_t bvci) +{ + fprintf(stderr, "CALLBACK, event %d, msg length %d, bvci 0x%04x\n%s\n\n", + event, msgb_bssgp_len(msg), bvci, + osmo_hexdump(msgb_bssgph(msg), msgb_bssgp_len(msg))); + return 0; +} + +int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) +{ + printf("BSSGP primitive, SAP %d, prim = %d, op = %d, msg = %s\n", + oph->sap, oph->primitive, oph->operation, msgb_hexdump(oph->msg)); + + last_oph.sap = oph->sap; + last_oph.primitive = oph->primitive; + last_oph.operation = oph->operation; + last_oph.msg = NULL; + return -1; +} + +static void msgb_bssgp_send_and_free(struct msgb *msg) +{ + msgb_nsei(msg) = BSS_NSEI; + + bssgp_rcvmsg(msg); + + msgb_free(msg); +} + +static void send_bssgp_supend(enum bssgp_pdu_type pdu_type, uint32_t tlli) +{ + struct msgb *msg = bssgp_msgb_alloc(); + uint32_t tlli_be = htonl(tlli); + uint8_t rai[] = {0x0f, 0xf1, 0x80, 0x20, 0x37, 0x00}; + + msgb_v_put(msg, pdu_type); + msgb_tvlv_put(msg, BSSGP_IE_TLLI, sizeof(tlli_be), (uint8_t *)&tlli_be); + msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, sizeof(rai), &rai[0]); + + msgb_bssgp_send_and_free(msg); +} + +static void send_bssgp_resume(enum bssgp_pdu_type pdu_type, uint32_t tlli) +{ + struct msgb *msg = bssgp_msgb_alloc(); + uint32_t tlli_be = htonl(tlli); + uint8_t rai[] = {0x0f, 0xf1, 0x80, 0x20, 0x37, 0x00}; + uint8_t suspend_ref = 1; + + msgb_v_put(msg, pdu_type); + msgb_tvlv_put(msg, BSSGP_IE_TLLI, sizeof(tlli_be), (uint8_t *)&tlli_be); + msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, sizeof(rai), &rai[0]); + msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref); + + msgb_bssgp_send_and_free(msg); +} + +static void test_bssgp_suspend_resume(void) +{ + const uint32_t tlli = 0xf0123456; + + printf("----- %s START\n", __func__); + memset(&last_oph, 0, sizeof(last_oph)); + + send_bssgp_supend(BSSGP_PDUT_SUSPEND, tlli); + /* OSMO_ASSERT(last_oph.primitive == PRIM_BSSGP_GMM_SUSPEND); */ + + send_bssgp_resume(BSSGP_PDUT_RESUME, tlli); + /* OSMO_ASSERT(last_oph.primitive == PRIM_BSSGP_GMM_RESUME); */ + + printf("----- %s END\n", __func__); +} + +static struct log_info info = {}; + +int main(int argc, char **argv) +{ + struct sockaddr_in bss_peer= {0}; + + osmo_init_logging(&info); + log_set_use_color(osmo_stderr_target, 0); + log_set_print_filename(osmo_stderr_target, 0); + + bssgp_nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); + + bss_peer.sin_family = AF_INET; + bss_peer.sin_port = htons(32000); + bss_peer.sin_addr.s_addr = htonl(0x7f0000ff); + + gprs_ns_nsip_connect(bssgp_nsi, &bss_peer, BSS_NSEI, BSS_NSEI+1); + + + printf("===== BSSGP test START\n"); + test_bssgp_suspend_resume(); + printf("===== BSSGP test END\n\n"); + + exit(EXIT_SUCCESS); +} diff --git a/tests/gb/gprs_bssgp_test.ok b/tests/gb/gprs_bssgp_test.ok new file mode 100644 index 0000000..9c7b4c0 --- /dev/null +++ b/tests/gb/gprs_bssgp_test.ok @@ -0,0 +1,5 @@ +===== BSSGP test START +----- test_bssgp_suspend_resume START +----- test_bssgp_suspend_resume END +===== BSSGP test END + diff --git a/tests/testsuite.at b/tests/testsuite.at index 7ce2ee8..fe30363 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -118,6 +118,12 @@ cat $abs_srcdir/vty/vty_test.ok > expout AT_CHECK([$abs_top_builddir/tests/vty/vty_test], [0], [expout], [ignore]) AT_CLEANUP
+AT_SETUP([gprs-bssgp]) +AT_KEYWORDS([gprs-bssgp]) +cat $abs_srcdir/gb/gprs_bssgp_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/gb/gprs_bssgp_test], [0], [expout], [ignore]) +AT_CLEANUP + AT_SETUP([gprs-ns]) AT_KEYWORDS([gprs-ns]) cat $abs_srcdir/gb/gprs_ns_test.ok > expout
Currently sending SUSPEND/RESUME messages to this function (like it is done in the osmo-sgsn) results in STATUS messages complaining about an unknown BVCI. The reason is, that these messages rely on a TLLI/RAI pair to identify the context and do not contain an explicit BVCI.
This patch modifies bssgp_rcvmsg() to only complain about and unknown BVCI if one is given but a matching context is not found (except for RESET messages). The ctx argument is removed from the functions handling SUSPEND and RESUME since it will always be NULL then.
Sponsored-by: On-Waves ehf --- src/gb/gprs_bssgp.c | 44 ++++++++++++++++++++++++++------------------ tests/gb/gprs_bssgp_test.c | 4 ++-- tests/gb/gprs_bssgp_test.ok | 2 ++ 3 files changed, 30 insertions(+), 20 deletions(-)
diff --git a/src/gb/gprs_bssgp.c b/src/gb/gprs_bssgp.c index 7a5d628..506efdf 100644 --- a/src/gb/gprs_bssgp.c +++ b/src/gb/gprs_bssgp.c @@ -398,32 +398,32 @@ static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp, return bssgp_prim_cb(&gbp.oph, NULL); }
-static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp, - struct bssgp_bvc_ctx *ctx) +static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp) { struct osmo_bssgp_prim gbp; struct gprs_ra_id raid; uint32_t tlli; + uint16_t ns_bvci = msgb_bvci(msg); int rc;
if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) || !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND " - "missing mandatory IE\n", ctx->bvci); + "missing mandatory IE\n", ns_bvci); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); }
tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n", - ctx->bvci, tlli); + ns_bvci, tlli);
gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
/* Inform GMM about the SUSPEND request */ memset(&gbp, 0, sizeof(gbp)); gbp.nsei = msgb_nsei(msg); - gbp.bvci = ctx->bvci; + gbp.bvci = ns_bvci; gbp.tlli = tlli; gbp.ra_id = &raid; osmo_prim_init(&gbp.oph, SAP_BSSGP_GMM, PRIM_BSSGP_GMM_SUSPEND, @@ -438,34 +438,34 @@ static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp, return 0; }
-static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp, - struct bssgp_bvc_ctx *ctx) +static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp) { struct osmo_bssgp_prim gbp; struct gprs_ra_id raid; uint32_t tlli; uint8_t suspend_ref; + uint16_t ns_bvci = msgb_bvci(msg); int rc;
if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) || !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) || !TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME " - "missing mandatory IE\n", ctx->bvci); + "missing mandatory IE\n", ns_bvci); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); }
tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI)); suspend_ref = *TLVP_VAL(tp, BSSGP_IE_SUSPEND_REF_NR);
- DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx RESUME\n", ctx->bvci, tlli); + DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx RESUME\n", ns_bvci, tlli);
gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
/* Inform GMM about the RESUME request */ memset(&gbp, 0, sizeof(gbp)); gbp.nsei = msgb_nsei(msg); - gbp.bvci = ctx->bvci; + gbp.bvci = ns_bvci; gbp.tlli = tlli; gbp.ra_id = &raid; gbp.u.resume.suspend_ref = suspend_ref; @@ -886,23 +886,29 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp, uint8_t pdu_type = bgph->pdu_type; int rc = 0; uint16_t ns_bvci = msgb_bvci(msg); + uint16_t bvci = bctx ? bctx->bvci : ns_bvci;
switch (bgph->pdu_type) { case BSSGP_PDUT_SUSPEND: /* MS wants to suspend */ - rc = bssgp_rx_suspend(msg, tp, bctx); + rc = bssgp_rx_suspend(msg, tp); break; case BSSGP_PDUT_RESUME: /* MS wants to resume */ - rc = bssgp_rx_resume(msg, tp, bctx); + rc = bssgp_rx_resume(msg, tp); break; case BSSGP_PDUT_FLUSH_LL_ACK: /* BSS informs us it has performed LL FLUSH */ - DEBUGP(DBSSGP, "BSSGP Rx BVCI=%u FLUSH LL ACK\n", bctx->bvci); + DEBUGP(DBSSGP, "BSSGP Rx BVCI=%u FLUSH LL ACK\n", bvci); /* FIXME: send NM_FLUSH_LL.res to NM */ break; case BSSGP_PDUT_LLC_DISCARD: /* BSS informs that some LLC PDU's have been discarded */ + if (!bctx) { + LOGP(DBSSGP, LOGL_ERROR, + "BSSGP Rx LLC-DISCARD missing mandatory BVCI\n"); + goto err_mand_ie; + } rc = bssgp_rx_llc_disc(msg, tp, bctx); break; case BSSGP_PDUT_BVC_BLOCK: @@ -936,7 +942,7 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp, break; case BSSGP_PDUT_STATUS: /* Some exception has occurred */ - DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci); + DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bvci); /* FIXME: send NM_STATUS.ind to NM */ break; /* those only exist in the SGSN -> BSS direction */ @@ -951,13 +957,13 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp, case BSSGP_PDUT_BVC_UNBLOCK_ACK: case BSSGP_PDUT_SGSN_INVOKE_TRACE: DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x only exists " - "in DL\n", bctx->bvci, pdu_type); + "in DL\n", bvci, pdu_type); bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); rc = -EINVAL; break; default: DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", - bctx->bvci, pdu_type); + bvci, pdu_type); rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); break; } @@ -998,8 +1004,10 @@ int bssgp_rcvmsg(struct msgb *msg)
/* look-up or create the BTS context for this BVC */ bctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg)); - /* Only a RESET PDU can create a new BVC context */ - if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET) { + /* Only a RESET PDU can create a new BVC context, + * otherwise it must be registered if a BVCI is given */ + if (!bctx && bvci != BVCI_SIGNALLING && + pdu_type != BSSGP_PDUT_BVC_RESET) { LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU " "type %u for unknown BVCI\n", msgb_nsei(msg), bvci, pdu_type); diff --git a/tests/gb/gprs_bssgp_test.c b/tests/gb/gprs_bssgp_test.c index a2a4068..d24250d 100644 --- a/tests/gb/gprs_bssgp_test.c +++ b/tests/gb/gprs_bssgp_test.c @@ -116,10 +116,10 @@ static void test_bssgp_suspend_resume(void) memset(&last_oph, 0, sizeof(last_oph));
send_bssgp_supend(BSSGP_PDUT_SUSPEND, tlli); - /* OSMO_ASSERT(last_oph.primitive == PRIM_BSSGP_GMM_SUSPEND); */ + OSMO_ASSERT(last_oph.primitive == PRIM_BSSGP_GMM_SUSPEND);
send_bssgp_resume(BSSGP_PDUT_RESUME, tlli); - /* OSMO_ASSERT(last_oph.primitive == PRIM_BSSGP_GMM_RESUME); */ + OSMO_ASSERT(last_oph.primitive == PRIM_BSSGP_GMM_RESUME);
printf("----- %s END\n", __func__); } diff --git a/tests/gb/gprs_bssgp_test.ok b/tests/gb/gprs_bssgp_test.ok index 9c7b4c0..c9ec83d 100644 --- a/tests/gb/gprs_bssgp_test.ok +++ b/tests/gb/gprs_bssgp_test.ok @@ -1,5 +1,7 @@ ===== BSSGP test START ----- test_bssgp_suspend_resume START +BSSGP primitive, SAP 16777219, prim = 3, op = 0, msg = 0b 1f 84 f0 12 34 56 1b 86 0f f1 80 20 37 00 +BSSGP primitive, SAP 16777219, prim = 4, op = 0, msg = 0e 1f 84 f0 12 34 56 1b 86 0f f1 80 20 37 00 1d 81 01 ----- test_bssgp_suspend_resume END ===== BSSGP test END