760 lines
20 KiB
C
760 lines
20 KiB
C
/* test routines for gbproxy
|
|
* send NS messages to the gbproxy and dumps what happens
|
|
* (C) 2013-2020 by sysmocom s.f.m.c. GmbH
|
|
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
|
*/
|
|
#undef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
#include <dlfcn.h>
|
|
#include <time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <talloc.h>
|
|
#include <osmocom/core/msgb.h>
|
|
#include <osmocom/core/application.h>
|
|
#include <osmocom/core/utils.h>
|
|
#include <osmocom/core/logging.h>
|
|
#include <osmocom/core/talloc.h>
|
|
#include <osmocom/core/signal.h>
|
|
#include <osmocom/core/socket.h>
|
|
#include <osmocom/core/rate_ctr.h>
|
|
#include <osmocom/gsm/tlv.h>
|
|
#include <osmocom/gsm/gsm_utils.h>
|
|
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
|
#include <osmocom/gprs/gprs_msgb.h>
|
|
#include <osmocom/gprs/gprs_ns2.h>
|
|
#include <osmocom/gprs/gprs_bssgp.h>
|
|
#include <osmocom/core/prim.h>
|
|
#include <osmocom/vty/command.h>
|
|
#include <osmocom/sgsn/gb_proxy.h>
|
|
#include <osmocom/sgsn/gprs_utils.h>
|
|
#include <osmocom/sgsn/gprs_llc.h>
|
|
#include <osmocom/sgsn/gprs_gb_parse.h>
|
|
#include <osmocom/sgsn/debug.h>
|
|
|
|
#define REMOTE_BSS_ADDR 0x01020304
|
|
#define REMOTE_SGSN_ADDR 0x05060708
|
|
|
|
#define SGSN_NSEI 0x0100
|
|
|
|
#define REMOTE_SGSN2_ADDR 0x15161718
|
|
#define SGSN2_NSEI 0x0102
|
|
|
|
#define MATCH_ANY (-1)
|
|
|
|
void *tall_sgsn_ctx = NULL;
|
|
|
|
struct gbproxy_config gbcfg = {0};
|
|
|
|
struct llist_head *received_messages = NULL;
|
|
|
|
/* override, requires '-Wl,--wrap=osmo_get_rand_id' */
|
|
int __real_osmo_get_rand_id(uint8_t *data, size_t len);
|
|
int mock_osmo_get_rand_id(uint8_t *data, size_t len);
|
|
int (*osmo_get_rand_id_cb)(uint8_t *, size_t) =
|
|
&mock_osmo_get_rand_id;
|
|
|
|
int __wrap_osmo_get_rand_id(uint8_t *buf, size_t num)
|
|
{
|
|
return (*osmo_get_rand_id_cb)(buf, num);
|
|
}
|
|
|
|
static int rand_seq_num = 0;
|
|
int mock_osmo_get_rand_id(uint8_t *buf, size_t num)
|
|
{
|
|
uint32_t val;
|
|
|
|
OSMO_ASSERT(num == sizeof(val));
|
|
|
|
val = 0x00dead00 + rand_seq_num;
|
|
|
|
rand_seq_num++;
|
|
|
|
memcpy(buf, &val, num);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void cleanup_test()
|
|
{
|
|
rand_seq_num = 0;
|
|
}
|
|
|
|
static int dump_global(FILE *stream, int indent)
|
|
{
|
|
unsigned int i;
|
|
const struct rate_ctr_group_desc *desc;
|
|
int rc;
|
|
|
|
rc = fprintf(stream, "%*sGbproxy global:\n", indent, "");
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
desc = gbcfg.ctrg->desc;
|
|
|
|
for (i = 0; i < desc->num_ctr; i++) {
|
|
struct rate_ctr *ctr = &gbcfg.ctrg->ctr[i];
|
|
if (ctr->current) {
|
|
rc = fprintf(stream, "%*s %s: %llu\n",
|
|
indent, "",
|
|
desc->ctr_desc[i].description,
|
|
(long long)ctr->current);
|
|
|
|
if (rc < 0)
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dump_peers(FILE *stream, int indent, time_t now,
|
|
struct gbproxy_config *cfg)
|
|
{
|
|
struct gbproxy_nse *nse;
|
|
struct gprs_ra_id raid;
|
|
unsigned int i, _nse;
|
|
const struct rate_ctr_group_desc *desc;
|
|
int rc;
|
|
|
|
rc = fprintf(stream, "%*sPeers:\n", indent, "");
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
|
|
hash_for_each(cfg->bss_nses, _nse, nse, list) {
|
|
struct gbproxy_bvc *peer;
|
|
int _peer;
|
|
hash_for_each(nse->bvcs, _peer, peer, list) {
|
|
gsm48_parse_ra(&raid, peer->ra);
|
|
|
|
rc = fprintf(stream, "%*s NSEI %u, BVCI %u, %sblocked, RAI %s\n",
|
|
indent, "",
|
|
nse->nsei, peer->bvci,
|
|
peer->blocked ? "" : "not ",
|
|
osmo_rai_name(&raid));
|
|
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
desc = peer->ctrg->desc;
|
|
|
|
for (i = 0; i < desc->num_ctr; i++) {
|
|
struct rate_ctr *ctr = &peer->ctrg->ctr[i];
|
|
if (ctr->current) {
|
|
rc = fprintf(stream, "%*s %s: %llu\n",
|
|
indent, "",
|
|
desc->ctr_desc[i].description,
|
|
(long long)ctr->current);
|
|
|
|
if (rc < 0)
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const uint8_t *convert_ra(struct gprs_ra_id *raid)
|
|
{
|
|
static struct gsm48_ra_id r;
|
|
gsm48_encode_ra(&r, raid);
|
|
return (const uint8_t *)&r;
|
|
}
|
|
|
|
#define NS_ALLOC_HEADROOM 128
|
|
#define NS_ALLOC_SIZE 3072
|
|
static int gprs_ns2_callback(struct osmo_prim_hdr *oph, void *ctx);
|
|
static void send_ns_unitdata(struct gprs_ns2_inst *nsi, const char *text,
|
|
uint16_t nsei, uint16_t nsbvci,
|
|
const unsigned char *bssgp_msg, size_t bssgp_msg_size)
|
|
{
|
|
struct msgb *msg;
|
|
struct osmo_gprs_ns2_prim nsp = {};
|
|
nsp.nsei = nsei;
|
|
nsp.bvci = nsbvci;
|
|
//nsp.u.unitdata.change
|
|
|
|
if (bssgp_msg_size > NS_ALLOC_SIZE - NS_ALLOC_HEADROOM) {
|
|
fprintf(stderr, "message too long: %zu\n", bssgp_msg_size);
|
|
return;
|
|
}
|
|
|
|
msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM,
|
|
"GPRS/NS");
|
|
OSMO_ASSERT(msg);
|
|
memmove(msg->data, bssgp_msg, bssgp_msg_size);
|
|
msgb_bssgph(msg) = msg->data;
|
|
msg->l2h = msg->data;
|
|
msg->l3h = msg->data;
|
|
msgb_put(msg, bssgp_msg_size);
|
|
|
|
printf("PROCESSING %s from NSEI %d\n%s\n\n",
|
|
text, nsei,
|
|
osmo_hexdump(bssgp_msg, bssgp_msg_size));
|
|
|
|
|
|
//gprs_process_message(nsi, text ? text : "UNITDATA", nsei, msg, bssgp_msg_size + 4);
|
|
osmo_prim_init(&nsp.oph, SAP_NS, PRIM_NS_UNIT_DATA,
|
|
PRIM_OP_INDICATION, msg);
|
|
|
|
gprs_ns2_callback(&nsp.oph, &gbcfg);
|
|
}
|
|
static int gbprox_test_bssgp_send_cb(void *ctx, struct msgb *msg);
|
|
|
|
/* wrap */
|
|
int gprs_ns2_recv_prim(struct gprs_ns2_inst *nsi, struct osmo_prim_hdr *oph)
|
|
{
|
|
struct osmo_gprs_ns2_prim *nsp;
|
|
|
|
if (oph->sap != SAP_NS)
|
|
return 0;
|
|
|
|
nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
|
|
|
|
if (oph->operation != PRIM_OP_REQUEST) {
|
|
LOGP(DPCU, LOGL_NOTICE, "NS: %s Unknown prim %d from NS\n",
|
|
get_value_string(osmo_prim_op_names, oph->operation),
|
|
oph->operation);
|
|
return 0;
|
|
}
|
|
|
|
switch (oph->primitive) {
|
|
case PRIM_NS_UNIT_DATA:
|
|
/* hand the message into the BSSGP implementation */
|
|
msgb_bssgph(oph->msg) = oph->msg->l3h;
|
|
msgb_bvci(oph->msg) = nsp->bvci;
|
|
msgb_nsei(oph->msg) = nsp->nsei;
|
|
printf("NS2 UD REQUEST, prim %d, msg length %zu, bvci 0x%04x\n%s\n\n",
|
|
oph->primitive, msgb_bssgp_len(oph->msg), nsp->bvci,
|
|
osmo_hexdump(msgb_l3(oph->msg), msgb_l3len(oph->msg)));
|
|
return gbprox_test_bssgp_send_cb(&gbcfg, oph->msg);
|
|
break;
|
|
default:
|
|
printf("NS2 REQUEST, prim %d, bvci 0x%04x\n\n",
|
|
oph->primitive, nsp->bvci);
|
|
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void send_bssgp_reset(struct gprs_ns2_inst *nsi,
|
|
uint16_t nsei, uint16_t bvci)
|
|
{
|
|
/* GPRS Network Service, PDU type: NS_UNITDATA, BVCI 0
|
|
* BSSGP RESET */
|
|
unsigned char msg[18] = {
|
|
0x22, 0x04, 0x82, 0x4a,
|
|
0x2e, 0x07, 0x81, 0x08, 0x08, 0x88, 0x11, 0x22,
|
|
0x33, 0x40, 0x50, 0x60, 0x10, 0x00
|
|
};
|
|
|
|
msg[3] = bvci / 256;
|
|
msg[4] = bvci % 256;
|
|
|
|
send_ns_unitdata(nsi, "BVC_RESET", nsei, 0, msg, sizeof(msg));
|
|
}
|
|
|
|
static void send_bssgp_reset_ack(struct gprs_ns2_inst *nsi,
|
|
uint16_t nsei, uint16_t bvci)
|
|
{
|
|
/* GPRS Network Service, PDU type: NS_UNITDATA, BVCI 0
|
|
* BSSGP RESET_ACK */
|
|
static unsigned char msg[5] = {
|
|
0x23, 0x04, 0x82, 0x00,
|
|
0x00
|
|
};
|
|
|
|
msg[3] = bvci / 256;
|
|
msg[4] = bvci % 256;
|
|
|
|
send_ns_unitdata(nsi, "BVC_RESET_ACK", nsei, 0, msg, sizeof(msg));
|
|
}
|
|
|
|
/* STATUS indications */
|
|
static void send_ns_avail(struct gprs_ns2_inst *nsi,
|
|
uint16_t sgsn_nsei)
|
|
{
|
|
struct osmo_gprs_ns2_prim nsp = {};
|
|
nsp.nsei = sgsn_nsei;
|
|
nsp.bvci = 0;
|
|
nsp.u.status.cause = NS_AFF_CAUSE_RECOVERY;
|
|
nsp.u.status.transfer = -1;
|
|
nsp.u.status.first = true;
|
|
osmo_prim_init(&nsp.oph, SAP_NS, PRIM_NS_STATUS,
|
|
PRIM_OP_INDICATION, NULL);
|
|
|
|
gprs_ns2_callback(&nsp.oph, &gbcfg);
|
|
}
|
|
|
|
static void setup_ns(struct gprs_ns2_inst *nsi,
|
|
uint16_t nsei)
|
|
{
|
|
printf("Setup NS-VC: "
|
|
"NSEI 0x%04x(%d)\n\n",
|
|
nsei, nsei);
|
|
send_ns_avail(nsi, nsei);
|
|
send_bssgp_reset(nsi, nsei, 0);
|
|
}
|
|
|
|
static void setup_bssgp(struct gprs_ns2_inst *nsi,
|
|
uint16_t nsei, uint16_t bvci)
|
|
{
|
|
printf("Setup BSSGP: "
|
|
"BVCI 0x%04x(%d)\n\n",
|
|
bvci, bvci);
|
|
|
|
send_bssgp_reset(nsi, nsei, bvci);
|
|
}
|
|
|
|
static void connect_sgsn(struct gprs_ns2_inst *nsi,
|
|
uint32_t sgsn_nsei)
|
|
{
|
|
send_ns_avail(nsi, sgsn_nsei);
|
|
}
|
|
|
|
/* Function used to send a BSSGP message through NS */
|
|
static int gbprox_test_bssgp_send_cb(void *ctx, struct msgb *msg)
|
|
{
|
|
int rc;
|
|
struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
|
|
|
|
uint16_t nsei = msgb_nsei(msg);
|
|
uint16_t bvci = msgb_bvci(msg);
|
|
|
|
if (received_messages) {
|
|
struct msgb *msg_copy;
|
|
msg_copy = bssgp_msgb_copy(msg, "received_messages");
|
|
llist_add_tail(&msg_copy->list, received_messages);
|
|
}
|
|
|
|
if (nsei == cfg->nsip_sgsn_nsei)
|
|
printf("Message for SGSN");
|
|
else
|
|
printf("Message for BSS");
|
|
printf(" (NSEI=%d BVCI=%d):\n%s\n\n", nsei, bvci, msgb_hexdump(msg));
|
|
|
|
rc = msgb_length(msg);
|
|
msgb_free(msg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void gprs_ns2_test_prim_status_cb(struct gbproxy_config *cfg, struct osmo_gprs_ns2_prim *nsp)
|
|
{
|
|
enum gprs_ns2_affecting_cause cause = nsp->u.status.cause;
|
|
|
|
switch (cause) {
|
|
case NS_AFF_CAUSE_RECOVERY:
|
|
LOGP(DPCU, LOGL_NOTICE, "NS-NSE %d became available\n", nsp->nsei);
|
|
break;
|
|
case NS_AFF_CAUSE_FAILURE:
|
|
LOGP(DPCU, LOGL_NOTICE, "NS-NSE %d became unavailable\n", nsp->nsei);
|
|
break;
|
|
case NS_AFF_CAUSE_VC_RECOVERY:
|
|
LOGP(DPCU, LOGL_NOTICE, "NS-NSE %d NS-VC %s became available\n", nsp->nsei, nsp->u.status.nsvc);
|
|
break;
|
|
case NS_AFF_CAUSE_VC_FAILURE:
|
|
LOGP(DPCU, LOGL_NOTICE, "NS-NSE %d NS-VC %s became unavailable\n", nsp->nsei, nsp->u.status.nsvc);
|
|
break;
|
|
default:
|
|
LOGP(DPCU, LOGL_NOTICE, "Unhandled status %d (NS-NSE %d)\n", cause, nsp->nsei);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int gprs_ns2_prim_cb(struct osmo_prim_hdr *oph, void *ctx);
|
|
|
|
/* override */
|
|
static int gprs_ns2_callback(struct osmo_prim_hdr *oph, void *ctx)
|
|
{
|
|
struct osmo_gprs_ns2_prim *nsp;
|
|
struct gbproxy_config *cfg = (struct gbproxy_config *) ctx;
|
|
|
|
if (oph->sap != SAP_NS)
|
|
return 0;
|
|
|
|
nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
|
|
|
|
if (oph->operation != PRIM_OP_INDICATION) {
|
|
LOGP(DPCU, LOGL_NOTICE, "NS: %s Unknown prim %d from NS\n",
|
|
get_value_string(osmo_prim_op_names, oph->operation),
|
|
oph->operation);
|
|
return 0;
|
|
}
|
|
|
|
switch (oph->primitive) {
|
|
case PRIM_NS_UNIT_DATA:
|
|
/* hand the message into the BSSGP implementation */
|
|
msgb_bssgph(oph->msg) = oph->msg->l3h;
|
|
msgb_bvci(oph->msg) = nsp->bvci;
|
|
msgb_nsei(oph->msg) = nsp->nsei;
|
|
printf("NS2 CALLBACK, prim %d, msg length %zu, bvci 0x%04x\n%s\n\n",
|
|
oph->primitive, msgb_bssgp_len(oph->msg), nsp->bvci,
|
|
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
|
|
break;
|
|
case PRIM_NS_STATUS:
|
|
gprs_ns2_test_prim_status_cb(cfg, nsp);
|
|
default:
|
|
printf("NS2 CALLBACK, prim %d, bvci 0x%04x\n\n",
|
|
oph->primitive, nsp->bvci);
|
|
|
|
break;
|
|
}
|
|
|
|
/* Hand off to gbproxy which will free the msg */
|
|
return gprs_ns2_prim_cb(oph, ctx);
|
|
}
|
|
|
|
static void test_gbproxy()
|
|
{
|
|
struct gprs_ns2_inst *nsi = gprs_ns2_instantiate(tall_sgsn_ctx, gprs_ns2_callback, &gbcfg);
|
|
uint16_t bss_nsei[2] = {0x1000, 0x2000};
|
|
|
|
gbcfg.nsi = nsi;
|
|
gbcfg.nsip_sgsn_nsei = SGSN_NSEI;
|
|
|
|
bssgp_set_bssgp_callback(gbprox_test_bssgp_send_cb, &gbcfg);
|
|
|
|
printf("=== %s ===\n", __func__);
|
|
printf("--- Initialise SGSN ---\n\n");
|
|
|
|
connect_sgsn(nsi, SGSN_NSEI);
|
|
|
|
printf("--- Initialise BSS 1 ---\n\n");
|
|
|
|
setup_ns(nsi, bss_nsei[0]);
|
|
setup_bssgp(nsi, bss_nsei[0], 0x1002);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
send_bssgp_reset_ack(nsi, SGSN_NSEI, 0x1002);
|
|
|
|
printf("--- Initialise BSS 2 ---\n\n");
|
|
|
|
setup_ns(nsi, bss_nsei[1]);
|
|
setup_bssgp(nsi, bss_nsei[1], 0x2002);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
send_bssgp_reset_ack(nsi, SGSN_NSEI, 0x2002);
|
|
|
|
printf("--- Reset BSS 1 with a new BVCI ---\n\n");
|
|
|
|
setup_bssgp(nsi, bss_nsei[0], 0x1012);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
send_bssgp_reset_ack(nsi, SGSN_NSEI, 0x1012);
|
|
|
|
printf("--- Reset BSS 1 with the old BVCI ---\n\n");
|
|
|
|
setup_bssgp(nsi, bss_nsei[0], 0x1002);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
send_bssgp_reset_ack(nsi, SGSN_NSEI, 0x1002);
|
|
|
|
printf("--- Reset BSS 1 with the old BVCI again ---\n\n");
|
|
|
|
setup_bssgp(nsi, bss_nsei[0], 0x1002);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
send_bssgp_reset_ack(nsi, SGSN_NSEI, 0x1002);
|
|
|
|
printf("--- Send message from BSS 1 to SGSN, BVCI 0x1012 ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, bss_nsei[0], 0x1012, (uint8_t *)"", 0);
|
|
|
|
printf("--- Send message from SGSN to BSS 1, BVCI 0x1012 ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, SGSN_NSEI, 0x1012, (uint8_t *)"", 0);
|
|
|
|
printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, bss_nsei[0], 0x1012, (uint8_t *)"", 0);
|
|
|
|
printf("--- Send message from SGSN to BSS 1, BVCI 0x1002 ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, SGSN_NSEI, 0x1012, (uint8_t *)"", 0);
|
|
|
|
printf("--- Send message from BSS 2 to SGSN, BVCI 0x2002 ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, bss_nsei[0], 0x2002, (uint8_t *)"", 0);
|
|
|
|
printf("--- Send message from SGSN to BSS 2, BVCI 0x2002 ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, SGSN_NSEI, 0x2002, (uint8_t *)"", 0);
|
|
|
|
printf("--- Reset BSS 1 with the old BVCI on BSS2's link ---\n\n");
|
|
|
|
setup_bssgp(nsi, bss_nsei[0], 0x1002);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
dump_global(stdout, 0);
|
|
|
|
send_bssgp_reset_ack(nsi, SGSN_NSEI, 0x1002);
|
|
|
|
printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, bss_nsei[0], 0x1012, (uint8_t *)"", 0);
|
|
|
|
printf("--- Send message from SGSN to BSS 1, BVCI 0x1002 ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, SGSN_NSEI, 0x1012, (uint8_t *)"", 0);
|
|
|
|
printf("--- Send message from SGSN to BSS 1, BVCI 0x10ff (invalid) ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, SGSN_NSEI, 0x10ff, (uint8_t *)"", 0);
|
|
|
|
/* Find peer */
|
|
OSMO_ASSERT(gbproxy_bvc_by_bvci(&gbcfg, 0xeeee) == NULL);
|
|
OSMO_ASSERT(gbproxy_bvc_by_bvci(&gbcfg, 0x1000) == NULL);
|
|
OSMO_ASSERT(gbproxy_bvc_by_bvci(&gbcfg, 0x1012) != NULL);
|
|
OSMO_ASSERT(gbproxy_bvc_by_nsei(&gbcfg, 0xeeee) == NULL);
|
|
OSMO_ASSERT(gbproxy_bvc_by_nsei(&gbcfg, 0x1012) == NULL);
|
|
OSMO_ASSERT(gbproxy_bvc_by_nsei(&gbcfg, 0x1000) != NULL);
|
|
|
|
|
|
/* Cleanup */
|
|
OSMO_ASSERT(gbproxy_cleanup_bvcs(&gbcfg, 0, 0) == 0);
|
|
OSMO_ASSERT(gbproxy_cleanup_bvcs(&gbcfg, 0x1000, 0xeeee) == 0);
|
|
OSMO_ASSERT(gbproxy_cleanup_bvcs(&gbcfg, 0, 0x1002) == 0);
|
|
OSMO_ASSERT(gbproxy_cleanup_bvcs(&gbcfg, 0x1000, 0x1012) == 1);
|
|
OSMO_ASSERT(gbproxy_cleanup_bvcs(&gbcfg, 0x1000, 0x1012) == 0);
|
|
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
dump_global(stdout, 0);
|
|
|
|
gbprox_reset(&gbcfg);
|
|
gprs_ns2_free(nsi);
|
|
nsi = NULL;
|
|
}
|
|
|
|
static void test_gbproxy_ident_changes()
|
|
{
|
|
struct gprs_ns2_inst *nsi = gprs_ns2_instantiate(tall_sgsn_ctx, gprs_ns2_callback, &gbcfg);
|
|
uint16_t bss_nsei[2] = {0x1000, 0x2000};
|
|
uint16_t bvci[4] = {0x1002, 0x2002, 0x3002};
|
|
|
|
gbcfg.nsi = nsi;
|
|
gbcfg.nsip_sgsn_nsei = SGSN_NSEI;
|
|
|
|
bssgp_set_bssgp_callback(gbprox_test_bssgp_send_cb, &gbcfg);
|
|
|
|
printf("=== %s ===\n", __func__);
|
|
printf("--- Initialise SGSN ---\n\n");
|
|
|
|
connect_sgsn(nsi, SGSN_NSEI);
|
|
|
|
printf("--- Initialise BSS 1 ---\n\n");
|
|
|
|
setup_ns(nsi, bss_nsei[0]);
|
|
|
|
printf("--- Setup BVCI 1 ---\n\n");
|
|
|
|
setup_bssgp(nsi, bss_nsei[0], bvci[0]);
|
|
send_bssgp_reset_ack(nsi, SGSN_NSEI, bvci[0]);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
printf("--- Setup BVCI 2 ---\n\n");
|
|
|
|
setup_bssgp(nsi, bss_nsei[0], bvci[1]);
|
|
send_bssgp_reset_ack(nsi, SGSN_NSEI, bvci[1]);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
printf("--- Send message from BSS 1 to SGSN and back, BVCI 1 ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, bss_nsei[0], bvci[0], (uint8_t *)"", 0);
|
|
send_ns_unitdata(nsi, NULL, SGSN_NSEI, bvci[0], (uint8_t *)"", 0);
|
|
|
|
printf("--- Send message from BSS 1 to SGSN and back, BVCI 2 ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, bss_nsei[0], bvci[1], (uint8_t *)"", 0);
|
|
send_ns_unitdata(nsi, NULL, SGSN_NSEI, bvci[1], (uint8_t *)"", 0);
|
|
|
|
printf("--- Change NSEI ---\n\n");
|
|
|
|
setup_ns(nsi, bss_nsei[1]);
|
|
|
|
printf("--- Setup BVCI 1 ---\n\n");
|
|
|
|
setup_bssgp(nsi, bss_nsei[1], bvci[0]);
|
|
send_bssgp_reset_ack(nsi, SGSN_NSEI, bvci[0]);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
printf("--- Setup BVCI 3 ---\n\n");
|
|
|
|
setup_bssgp(nsi, bss_nsei[1], bvci[2]);
|
|
send_bssgp_reset_ack(nsi, SGSN_NSEI, bvci[2]);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
printf("--- Send message from BSS 1 to SGSN and back, BVCI 1 ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, bss_nsei[1], bvci[0], (uint8_t *)"", 0);
|
|
send_ns_unitdata(nsi, NULL, SGSN_NSEI, bvci[0], (uint8_t *)"", 0);
|
|
|
|
printf("--- Send message from BSS 1 to SGSN and back, BVCI 2 "
|
|
" (should fail) ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, bss_nsei[1], bvci[1], (uint8_t *)"", 0);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
send_ns_unitdata(nsi, NULL, SGSN_NSEI, bvci[1], (uint8_t *)"", 0);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
printf("--- Send message from BSS 1 to SGSN and back, BVCI 3 ---\n\n");
|
|
|
|
send_ns_unitdata(nsi, NULL, bss_nsei[0], bvci[2], (uint8_t *)"", 0);
|
|
send_ns_unitdata(nsi, NULL, SGSN_NSEI, bvci[2], (uint8_t *)"", 0);
|
|
|
|
dump_global(stdout, 0);
|
|
dump_peers(stdout, 0, 0, &gbcfg);
|
|
|
|
gbprox_reset(&gbcfg);
|
|
gprs_ns2_free(nsi);
|
|
nsi = NULL;
|
|
}
|
|
|
|
/* See OS#3178 "gbproxy: failed to parse invalid BSSGP-UNITDATA message" */
|
|
static void test_gbproxy_parse_bssgp_unitdata()
|
|
{
|
|
const char *hex = "0000239401e155cfea000004088872f4801018009c4000800e000601c0416c4338";
|
|
struct msgb *msg = msgb_alloc(1034, "bssgp_unitdata");
|
|
struct gprs_gb_parse_context parse_ctx;
|
|
int rc;
|
|
|
|
memset(&parse_ctx, 0, sizeof(parse_ctx));
|
|
|
|
OSMO_ASSERT(msg);
|
|
msgb_bssgph(msg) = msg->head;
|
|
msgb_put(msg, osmo_hexparse(hex, msg->head, msgb_tailroom(msg)));
|
|
|
|
parse_ctx.to_bss = 0;
|
|
parse_ctx.peer_nsei = msgb_nsei(msg);
|
|
|
|
rc = gprs_gb_parse_bssgp(msg->data, msg->len, &parse_ctx);
|
|
if (!rc)
|
|
fprintf(stderr, "%s: Test passed; Failed to parse invalid message %s\n", __func__, msgb_hexdump(msg));
|
|
else
|
|
fprintf(stderr, "%s: Test failed; invalid message was accepted by parser: %s\n", __func__, msgb_hexdump(msg));
|
|
|
|
OSMO_ASSERT(!rc);
|
|
|
|
/* Manually decoded message according to:
|
|
ETSI TS 148 018 V10.6.0 (2012 07) 96
|
|
3GPP TS 48.018 version 10.6.0 Release 10
|
|
Table 10.2.2: UL-UNITDATA PDU content
|
|
|
|
00 - PDU type UL-UNITDATA (ok)
|
|
|
|
11.3.35 Temporary logical link Identity (TLLI)
|
|
00 - TLLI[0]
|
|
23 - TLLI[1]
|
|
94 - TLLI[2]
|
|
01 - TLLI[3]
|
|
TLLI == "00239401"
|
|
|
|
e1 - QOS[0] (bit rate MSB)
|
|
55 - QOS[1] (bit rate LSB)
|
|
bit rate = "57685" (57685*100000 bit/s per PBRG)
|
|
cf - QOS[2] PBRG = 11 (bit rate is expressed in 100000 bit/s increments),
|
|
C/R 0 (contains LLC ACK/SACK),
|
|
T 0 (contains signalling),
|
|
A 1 (radio if uses MAC/UNITDATA,
|
|
Precedence 111 (reserved value)
|
|
|
|
ea - CELL_ID[0] (TLV IEI: wrong, should be 0x08)
|
|
00 - CELL_ID[1] (length 1)
|
|
00 - CELL_ID[2] (length 2)
|
|
lenth == 0
|
|
04 -- CELL_ID[3]
|
|
08 -- CELL_ID[4]
|
|
88 -- CELL_ID[5]
|
|
72 -- CELL_ID[6]
|
|
f4 -- CELL_ID[7]
|
|
80 -- CELL_ID[8]
|
|
10 -- CELL_DI[9]
|
|
|
|
18 -- QOSP[0] OoS Profile IEI
|
|
not allowed in BSSGP Userdata
|
|
00 -- QOSP[1]
|
|
9c -- QOSP[2]
|
|
40 -- QOSP[3]
|
|
00 -- QOSP[4]
|
|
|
|
80 -- IEI for "E-UTRAN Inter RAT Handover Info"
|
|
not allowed in BSSGP Userdata
|
|
0e -- length (14 bytes -- only 8 bytes remain)
|
|
00 06 01 c0 41 6c 43 38 */
|
|
|
|
msgb_free(msg);
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
static struct log_info_cat gprs_categories[] = {
|
|
[DGPRS] = {
|
|
.name = "DGPRS",
|
|
.description = "GPRS Packet Service",
|
|
.enabled = 1, .loglevel = LOGL_DEBUG,
|
|
},
|
|
[DNS] = {
|
|
.name = "DNS",
|
|
.description = "GPRS Network Service (NS)",
|
|
.enabled = 1, .loglevel = LOGL_INFO,
|
|
},
|
|
[DBSSGP] = {
|
|
.name = "DBSSGP",
|
|
.description = "GPRS BSS Gateway Protocol (BSSGP)",
|
|
.enabled = 1, .loglevel = LOGL_DEBUG,
|
|
},
|
|
};
|
|
|
|
static struct log_info info = {
|
|
.cat = gprs_categories,
|
|
.num_cat = ARRAY_SIZE(gprs_categories),
|
|
};
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
talloc_enable_leak_report();
|
|
tall_sgsn_ctx = talloc_named_const(NULL, 0, "gbproxy_test");
|
|
void *log_ctx = talloc_named_const(tall_sgsn_ctx, 0, "log");
|
|
|
|
msgb_talloc_ctx_init(tall_sgsn_ctx, 0);
|
|
|
|
osmo_init_logging2(log_ctx, &info);
|
|
log_set_use_color(osmo_stderr_target, 0);
|
|
log_set_print_filename(osmo_stderr_target, 0);
|
|
log_set_log_level(osmo_stderr_target, LOGL_DEBUG);
|
|
log_set_all_filter(osmo_stderr_target, 1);
|
|
|
|
rate_ctr_init(tall_sgsn_ctx);
|
|
|
|
setlinebuf(stdout);
|
|
|
|
printf("===== GbProxy test START\n");
|
|
gbproxy_init_config(&gbcfg);
|
|
test_gbproxy();
|
|
test_gbproxy_ident_changes();
|
|
test_gbproxy_parse_bssgp_unitdata();
|
|
gbprox_reset(&gbcfg);
|
|
/* gbprox_reset() frees the rate_ctr, but re-allocates it again. */
|
|
rate_ctr_group_free(gbcfg.ctrg);
|
|
printf("===== GbProxy test END\n\n");
|
|
|
|
talloc_free(log_ctx);
|
|
/* expecting root and msgb ctx, empty */
|
|
OSMO_ASSERT(talloc_total_blocks(tall_sgsn_ctx) == 2);
|
|
talloc_free(tall_sgsn_ctx);
|
|
|
|
return 0;
|
|
}
|