/* Test routines for the BSSGP implementation in libosmogb * * (C) 2014 by sysmocom - s.f.m.c. GmbH * Author: Jacob Erlbeck * * Skeleton based on bssgp_fc_test.c * (C) 2012 by Harald Welte * * SPDX-License-Identifier: GPL-2.0+ */ #undef _GNU_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 %zu\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 %td, bvci 0x%04x\n%s\n\n", event, msgb_bssgp_len(msg), bvci, osmo_hexdump(msgb_bssgph(msg), msgb_bssgp_len(msg))); return 0; } struct msgb *last_ns_tx_msg = NULL; /* override */ int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) { msgb_free(last_ns_tx_msg); last_ns_tx_msg = msg; return msgb_length(msg); } 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(); uint8_t rai[] = {0x0f, 0xf1, 0x80, 0x20, 0x37, 0x00}; msgb_v_put(msg, pdu_type); bssgp_msgb_tlli_put(msg, tlli); 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(); uint8_t rai[] = {0x0f, 0xf1, 0x80, 0x20, 0x37, 0x00}; uint8_t suspend_ref = 1; msgb_v_put(msg, pdu_type); bssgp_msgb_tlli_put(msg, tlli); 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 void send_bssgp_status(enum gprs_bssgp_cause cause, uint16_t *bvci) { struct msgb *msg = bssgp_msgb_alloc(); uint8_t cause_ = cause; msgb_v_put(msg, BSSGP_PDUT_STATUS); msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause_); if (bvci) { uint16_t bvci_ = htons(*bvci); msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &bvci_); } msgb_bssgp_send_and_free(msg); } static void test_bssgp_status(void) { uint16_t bvci; printf("----- %s START\n", __func__); send_bssgp_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL); OSMO_ASSERT(last_oph.primitive == PRIM_NM_STATUS); /* Enforce prim != PRIM_NM_STATUS */ last_oph.primitive = PRIM_NM_LLC_DISCARDED; bvci = 1234; send_bssgp_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci); OSMO_ASSERT(last_oph.primitive == PRIM_NM_STATUS); printf("----- %s END\n", __func__); } static void test_bssgp_bad_reset() { struct msgb *msg; uint16_t bvci_be = htons(2); uint8_t cause = BSSGP_CAUSE_OML_INTERV; printf("----- %s START\n", __func__); msg = bssgp_msgb_alloc(); msgb_v_put(msg, BSSGP_PDUT_BVC_RESET); msgb_tvlv_put(msg, BSSGP_IE_BVCI, sizeof(bvci_be), (uint8_t *)&bvci_be); msgb_tvlv_put(msg, BSSGP_IE_CAUSE, sizeof(cause), &cause); msgb_bvci(msg) = 0xbad; msgb_bssgp_send_and_free(msg); printf("----- %s END\n", __func__); } static void test_bssgp_flow_control_bvc(void) { struct bssgp_bvc_ctx bctx = { .nsei = 0x1234, .bvci = 0x5678, }; const uint8_t tag = 42; const uint32_t bmax = 0x1022 * 100; const uint32_t rate = 0xc040 / 8 * 100; const uint32_t bmax_ms = bmax / 2; const uint32_t rate_ms = rate / 2; uint8_t ratio = 0x78; uint32_t qdelay = 0x1144 * 10; int rc; static uint8_t expected_simple_msg[] = { 0x26, 0x1e, 0x81, 0x2a, /* tag */ 0x05, 0x82, 0x10, 0x22, /* Bmax */ 0x03, 0x82, 0xc0, 0x40, /* R */ 0x01, 0x82, 0x08, 0x11, /* Bmax_MS */ 0x1c, 0x82, 0x60, 0x20, /* R_MS */ }; static uint8_t expected_ext_msg[] = { 0x26, 0x1e, 0x81, 0x2a, /* tag */ 0x05, 0x82, 0x10, 0x22, /* Bmax */ 0x03, 0x82, 0xc0, 0x40, /* R */ 0x01, 0x82, 0x08, 0x11, /* Bmax_MS */ 0x1c, 0x82, 0x60, 0x20, /* R_MS */ 0x3c, 0x81, 0x78, /* ratio */ 0x06, 0x82, 0x11, 0x44, /* Qdelay */ }; printf("----- %s START\n", __func__); rc = bssgp_tx_fc_bvc(&bctx, tag, bmax, rate, bmax_ms, rate_ms, NULL, NULL); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(last_ns_tx_msg != NULL); printf("Got message: %s\n", msgb_hexdump(last_ns_tx_msg)); OSMO_ASSERT(msgb_length(last_ns_tx_msg) == sizeof(expected_simple_msg)); OSMO_ASSERT(0 == memcmp(msgb_data(last_ns_tx_msg), expected_simple_msg, sizeof(expected_simple_msg))); rc = bssgp_tx_fc_bvc(&bctx, tag, bmax, rate, bmax_ms, rate_ms, &ratio, &qdelay); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(last_ns_tx_msg != NULL); printf("Got message: %s\n", msgb_hexdump(last_ns_tx_msg)); OSMO_ASSERT(msgb_length(last_ns_tx_msg) == sizeof(expected_ext_msg)); OSMO_ASSERT(0 == memcmp(msgb_data(last_ns_tx_msg), expected_ext_msg, sizeof(expected_ext_msg))); msgb_free(last_ns_tx_msg); last_ns_tx_msg = NULL; printf("----- %s END\n", __func__); } static void test_bssgp_msgb_copy() { struct msgb *msg, *msg2; uint16_t bvci_be = htons(2); uint8_t cause = BSSGP_CAUSE_OML_INTERV; printf("----- %s START\n", __func__); msg = bssgp_msgb_alloc(); msg->l3h = msgb_data(msg); msgb_v_put(msg, BSSGP_PDUT_BVC_RESET); msgb_tvlv_put(msg, BSSGP_IE_BVCI, sizeof(bvci_be), (uint8_t *)&bvci_be); msgb_tvlv_put(msg, BSSGP_IE_CAUSE, sizeof(cause), &cause); msgb_bvci(msg) = 0xbad; msgb_nsei(msg) = 0xbee; printf("Old msgb: %s\n", msgb_hexdump(msg)); msg2 = bssgp_msgb_copy(msg, "test"); printf("New msgb: %s\n", msgb_hexdump(msg2)); OSMO_ASSERT(msgb_bvci(msg2) == 0xbad); OSMO_ASSERT(msgb_nsei(msg2) == 0xbee); OSMO_ASSERT(msgb_l3(msg2) == msgb_data(msg2)); OSMO_ASSERT(msgb_bssgph(msg2) == msgb_data(msg2)); OSMO_ASSERT(msgb_bssgp_len(msg2) == msgb_length(msg2)); msgb_free(msg); msgb_free(msg2); printf("----- %s END\n", __func__); } void dump_rim_ri(struct bssgp_rim_routing_info *ri) { switch (ri->discr) { case BSSGP_RIM_ROUTING_INFO_GERAN: printf("GERAN cell identifier\n"); printf(" * mcc: %u\n", ri->geran.raid.mcc); printf(" mnc: %u\n", ri->geran.raid.mnc); printf(" mnc 3 digits: %u\n", ri->geran.raid.mnc_3_digits); printf(" lac: %u\n", ri->geran.raid.lac); printf(" rac: %u\n", ri->geran.raid.rac); printf(" * cell id: %04x\n", ri->geran.cid); break; case BSSGP_RIM_ROUTING_INFO_UTRAN: printf("UTRAN RNC identifier\n"); printf(" * mcc: %u\n", ri->utran.raid.mcc); printf(" mnc: %u\n", ri->utran.raid.mnc); printf(" mnc 3 digits: %u\n", ri->utran.raid.mnc_3_digits); printf(" lac: %u\n", ri->utran.raid.lac); printf(" rac: %u\n", ri->utran.raid.rac); printf(" * rnc id: %04x\n", ri->utran.rncid); break; case BSSGP_RIM_ROUTING_INFO_EUTRAN: printf("EUTRAN eNB identifier\n"); printf(" * mcc: %u\n", ri->eutran.tai.mcc); printf(" mnc: %u\n", ri->eutran.tai.mnc); printf(" mnc 3 digits: %u\n", ri->eutran.tai.mnc_3_digits); printf(" tac: %u\n", ri->eutran.tai.tac); printf(" * global_enb_id: %s\n", osmo_hexdump_nospc(ri->eutran.global_enb_id, ri->eutran.global_enb_id_len)); break; default: OSMO_ASSERT(false); } } static void test_bssgp_parse_rim_ri() { int rc; struct bssgp_rim_routing_info result; uint8_t testvec_geran[] = { 0x00, 0x62, 0xf2, 0x24, 0x33, 0x90, 0x00, 0x51, 0xe1 }; uint8_t testvec_utran[] = { 0x01, 0x62, 0xf2, 0x24, 0x33, 0x90, 0x00, 0x51, 0xe1 }; uint8_t testvec_eutran[] = { 0x02, 0x62, 0xf2, 0x24, 0x33, 0x90, 0x00, 0x51, 0xe1 }; printf("----- %s START\n", __func__); rc = bssgp_parse_rim_ri(&result, testvec_geran, sizeof(testvec_geran)); printf("rc=%d\n", rc); dump_rim_ri(&result); printf("\n"); rc = bssgp_parse_rim_ri(&result, testvec_utran, sizeof(testvec_utran)); printf("rc=%d\n", rc); dump_rim_ri(&result); printf("\n"); rc = bssgp_parse_rim_ri(&result, testvec_eutran, sizeof(testvec_eutran)); printf("rc=%d\n", rc); dump_rim_ri(&result); printf("\n"); printf("----- %s END\n", __func__); } static void test_bssgp_create_rim_ri() { int rc; struct bssgp_rim_routing_info ri; uint8_t result[15]; printf("----- %s START\n", __func__); memset(&ri, 0, sizeof(ri)); memset(result, 0, sizeof(result)); ri.discr = BSSGP_RIM_ROUTING_INFO_GERAN; ri.geran.raid.mcc = 262; ri.geran.raid.mnc = 42; ri.geran.raid.mnc_3_digits = false; ri.geran.raid.lac = 13200; ri.geran.raid.rac = 0; ri.geran.cid = 0x51e1; dump_rim_ri(&ri); rc = bssgp_create_rim_ri(result, &ri); printf("rc=%d, ", rc); if (rc > 0) printf("result=%s", osmo_hexdump_nospc(result, rc)); printf("\n\n"); memset(&ri, 0, sizeof(ri)); memset(result, 0, sizeof(result)); ri.discr = BSSGP_RIM_ROUTING_INFO_UTRAN; ri.utran.raid.mcc = 262; ri.utran.raid.mnc = 42; ri.utran.raid.mnc_3_digits = 0; ri.utran.raid.lac = 13200; ri.utran.raid.rac = 0; ri.utran.rncid = 0x51e1; dump_rim_ri(&ri); rc = bssgp_create_rim_ri(result, &ri); printf("rc=%d, ", rc); if (rc > 0) printf("result=%s", osmo_hexdump_nospc(result, rc)); printf("\n\n"); memset(&ri, 0, sizeof(ri)); memset(result, 0, sizeof(result)); ri.discr = BSSGP_RIM_ROUTING_INFO_EUTRAN; ri.eutran.tai.mcc = 262; ri.eutran.tai.mnc = 42; ri.eutran.tai.mnc_3_digits = 0; ri.eutran.tai.tac = 13200; ri.eutran.global_enb_id[0] = 0x00; ri.eutran.global_enb_id[1] = 0x51; ri.eutran.global_enb_id[2] = 0xe1; ri.eutran.global_enb_id_len = 3; dump_rim_ri(&ri); rc = bssgp_create_rim_ri(result, &ri); printf("rc=%d, ", rc); if (rc > 0) printf("result=%s", osmo_hexdump_nospc(result, rc)); printf("\n\n"); printf("----- %s END\n", __func__); } static struct log_info info = {}; int main(int argc, char **argv) { struct sockaddr_in bss_peer= {0}; void *ctx = talloc_named_const(NULL, 0, "gprs_bssgp_test"); osmo_init_logging2(ctx, &info); log_set_use_color(osmo_stderr_target, 0); log_set_print_filename(osmo_stderr_target, 0); msgb_talloc_ctx_init(ctx, 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(); test_bssgp_status(); test_bssgp_bad_reset(); test_bssgp_flow_control_bvc(); test_bssgp_msgb_copy(); test_bssgp_parse_rim_ri(); test_bssgp_create_rim_ri(); printf("===== BSSGP test END\n\n"); exit(EXIT_SUCCESS); }