libosmo-gprs/tests/rlcmac/rlcmac_prim_test.c

481 lines
17 KiB
C

/* rlcmac_prim_test.c
*
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*/
#include <stdint.h>
#include <stdio.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/fsm.h>
#include <osmocom/gprs/rlcmac/rlcmac.h>
#include <osmocom/gprs/rlcmac/csn1_defs.h>
#include <osmocom/gprs/rlcmac/gre.h>
#include <osmocom/gprs/rlcmac/rlc.h>
#include <osmocom/gprs/rlcmac/rlc_window.h>
#include <osmocom/gprs/rlcmac/types_private.h>
#include <osmocom/gprs/rlcmac/sched.h>
static void *tall_ctx = NULL;
uint8_t last_rach_req_ra = 0;
/**
MS-SGSN LLC (Mobile Station - Serving GPRS Support Node Logical Link Control) SAPI: GPRS Mobility Management
Address field SAPI: LLGMM
0... .... = Protocol Discriminator_bit: OK
.0.. .... = Command/Response bit: DownLink/UpLink = Response/Command
.... 0001 = SAPI: GPRS Mobility Management (1)
Unconfirmed Information format - UI: UI format: 0x6, Spare bits: 0x0, N(U): 0, E bit: non encrypted frame, PM bit: FCS covers only the frame header and first N202 octets of the information field
110. .... .... .... = UI format: 0x6
...0 0... .... .... = Spare bits: 0x0
.... .000 0000 00.. = N(U): 0
.... .... .... ..0. = E bit: non encrypted frame
.... .... .... ...0 = PM bit: FCS covers only the frame header and first N202 octets of the information field
FCS: 0xf218e2 (correct)
GSM A-I/F DTAP - Attach Request
Protocol Discriminator: GPRS mobility management messages (8)
DTAP GPRS Mobility Management Message Type: Attach Request (0x01)
MS Network Capability
Attach Type
Ciphering Key Sequence Number
DRX Parameter
Mobile Identity - IMSI (262420000000017)
Routing Area Identification - Old routing area identification - RAI: 262-42-27780-68
MS Radio Access Capability
*/
static uint8_t pdu_llc_gmm_att_req[] = {
0x01, 0xc0, 0x00, 0x08, 0x01, 0x01, 0xd5, 0x71, 0x00, 0x00, 0x08, 0x29, 0x26,
0x24, 0x00, 0x00, 0x00, 0x00, 0x71, 0x62, 0xf2, 0x24, 0x6c, 0x84, 0x44, 0x04,
0x11, 0xe5, 0x10, 0x00, 0xe2, 0x18, 0xf2
};
/**
GSM CCCH - Immediate Assignment
L2 Pseudo Length
0010 11.. = L2 Pseudo Length value: 11
.... 0110 = Protocol discriminator: Radio Resources Management messages (0x6)
.... 0110 = Protocol discriminator: Radio Resources Management messages (0x6)
0000 .... = Skip Indicator: No indication of selected PLMN (0)
Message Type: Immediate Assignment
Page Mode
.... 0000 = Page Mode: Normal paging (0)
Dedicated mode or TBF
0001 .... = Dedicated mode or TBF: This message assigns an uplink TBF or is the second message of two in a two-message assignment of an uplink or downlink TBF (1)
Packet Channel Description
0000 1... = Channel Type: 1
.... .111 = Timeslot: 7
111. .... = Training Sequence: 7
.... .0.. = Spare: 0x00
.... ..11 0110 0111 = Single channel ARFCN: 871
Request Reference
Random Access Information (RA): 120
0000 1... = T1': 1
.... .001 011. .... = T3: 11
...0 1011 = T2: 11
[RFN: 1337]
Timing Advance
Timing advance value: 0
Mobile Allocation
Length: 0
IA Rest Octets
H... .... = First Discriminator Bit: High
.H.. .... = Second Discriminator Bit: High
..0. .... = Discriminator Bit: Packet Assignment
...0 .... = Discriminator Bit: Packet Uplink Assignment
Packet Uplink Assignment
.... 1... = Packet Uplink Assignment: Normal
.... .000 00.. .... = TFI_Assignment: 0
..0. .... = Polling: no action is required from MS
...0 .... = Allocation Type: Dynamic Allocation (mandatory after Rel-4)
.... 000. = USF: 0
.... ...0 = USF_granularity: the mobile station shall transmit one RLC/MAC block
0... .... = P0: Not Present
.01. .... = Channel_Coding_Command: CS-2 (1)
...1 .... = TLLI_Block_Channel_Coding: mobile station shall use coding scheme as specified by the corresponding CHANNEL CODING COMMAND or EGPRS CHANNEL CODING COMMAND field
.... 0... = Alpha: Not Present
.... .000 00.. .... = Gamma: 0 dB (0)
..0. .... = Timing Advance Index: Not Present
...0 .... = TBF Starting Time: Not Present
.... L... = Additions in R99: Not Present
.... .L.. = Additions in Rel-6: Not Present
.... ..L. = Additions in Rel-10: Not Present
.... ...L = Additions in Rel-13: Not Present
Padding Bits: default padding
*/
static uint8_t ccch_imm_ass_pkt_ul_tbf_normal[] = {
0x2d, 0x06, 0x3f, 0x10, 0x0f, 0xe3, 0x67, 0x78, 0x09, 0x6b,
0x00, 0x00, 0xc8, 0x00, 0x30, 0x0b,
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
};
/*
GSM CCCH - Immediate Assignment
L2 Pseudo Length
0010 11.. = L2 Pseudo Length value: 11
.... 0110 = Protocol discriminator: Radio Resources Management messages (0x6)
.... 0110 = Protocol discriminator: Radio Resources Management messages (0x6)
0000 .... = Skip Indicator: No indication of selected PLMN (0)
Message Type: Immediate Assignment
Page Mode
.... 0000 = Page Mode: Normal paging (0)
Dedicated mode or TBF
0011 .... = Dedicated mode or TBF: This message assigns a downlink TBF to the mobile station identified in the IA Rest Octets IE (3)
Packet Channel Description
0000 1... = Channel Type: 1
.... .111 = Timeslot: 7
111. .... = Training Sequence: 7
.... .0.. = Spare: 0x00
.... ..11 0110 0111 = Single channel ARFCN: 871
Request Reference
Random Access Information (RA): 125
1000 0... = T1': 16
.... .000 000. .... = T3: 0
...0 0000 = T2: 0
[RFN: 21216]
Timing Advance
Timing advance value: 28
Mobile Allocation
Length: 0
IA Rest Octets
H... .... = First Discriminator Bit: High
.H.. .... = Second Discriminator Bit: High
..0. .... = Discriminator Bit: Packet Assignment
...1 .... = Discriminator Bit: Packet Downlink Assignment
Packet Downlink Assignment
.... 0000 0000 0000 0000 0000 0000 0000 0001 .... = TLLI: 0x00000001
.... 1... = TFI Assignment (etc): Present
.... .000 00.. .... = TFI_Assignment: 0
..0. .... = RLC_Mode: RLC acknowledged mode
...0 .... = Alpha: Not Present
.... 0000 0... .... = Gamma: 0 dB (0)
.0.. .... = Polling: no action is required from MS
..0. .... = TA_Valid: the timing advance value is not valid
...0 .... = Timing Advance Index: Not Present
.... 0... = TBF Starting Time: Not Present
.... .0.. = P0: Not Present
.... ..L. = Additions in R99: Not Present
.... ...L = Additions in Rel-6: Not Present
L... .... = Additions in Rel-7: Not Present
.L.. .... = Additions in Rel-10: Not Present
..L. .... = Additions in Rel-13: Not Present
Padding Bits: default padding
*/
static uint8_t ccch_imm_ass_pkt_dl_tbf[] = {
0x2d, 0x06, 0x3f, 0x30, 0x0f, 0xe3, 0x67, 0x7d, 0x80, 0x00,
0x1c, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03,
0x2b, 0x2b, 0x2b, 0x2b
};
static inline unsigned fn2bn(unsigned fn)
{
return (fn % 52) / 4;
}
static inline unsigned fn_next_block(unsigned fn)
{
unsigned bn = fn2bn(fn) + 1;
fn = fn - (fn % 52);
fn += bn * 4 + bn / 3;
return fn % GSM_MAX_FN;
}
static struct osmo_gprs_rlcmac_prim *create_dl_ctrl_block_buf(uint8_t *buf, int num_bytes, uint8_t tn, uint32_t fn)
{
struct osmo_gprs_rlcmac_prim *rlcmac_prim;
rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_data_ind(tn, fn, 0, 0, 0,
NULL, num_bytes);
rlcmac_prim->l1ctl.pdch_data_ind.data = msgb_put(rlcmac_prim->oph.msg, num_bytes);
memcpy(rlcmac_prim->l1ctl.pdch_data_ind.data, buf, num_bytes);
return rlcmac_prim;
}
static struct osmo_gprs_rlcmac_prim *create_dl_ctrl_block(RlcMacDownlink_t *dl_block, uint8_t tn, uint32_t fn)
{
struct bitvec *rlc_block;
uint8_t buf[64];
int num_bytes;
rlc_block = bitvec_alloc(23, tall_ctx);
OSMO_ASSERT(osmo_gprs_rlcmac_encode_downlink(rlc_block, dl_block) == 0);
num_bytes = bitvec_pack(rlc_block, &buf[0]);
OSMO_ASSERT((size_t)num_bytes < sizeof(buf));
bitvec_free(rlc_block);
return create_dl_ctrl_block_buf(&buf[0], num_bytes, tn, fn);
}
static void ul_ack_nack_init(RlcMacDownlink_t *dl_block, uint8_t ul_tfi, enum gprs_rlcmac_coding_scheme cs)
{
Packet_Uplink_Ack_Nack_t *ack = &dl_block->u.Packet_Uplink_Ack_Nack;
PU_AckNack_GPRS_t *gprs = &ack->u.PU_AckNack_GPRS_Struct;
memset(dl_block, 0, sizeof(*dl_block));
dl_block->PAYLOAD_TYPE = GPRS_RLCMAC_PT_CONTROL_BLOCK;
dl_block->RRBP = 0;
dl_block->SP = 0;
dl_block->USF = 0x00;
dl_block->u.MESSAGE_TYPE = OSMO_GPRS_RLCMAC_DL_MSGT_PACKET_UPLINK_ACK_NACK;
ack->MESSAGE_TYPE = OSMO_GPRS_RLCMAC_DL_MSGT_PACKET_UPLINK_ACK_NACK;
ack->PAGE_MODE = GPRS_RLCMAC_PAGE_MODE_NORMAL;
ack->UPLINK_TFI = ul_tfi;
ack->UnionType = 0; /* GPRS */
gprs->CHANNEL_CODING_COMMAND = cs;
}
static void ul_ack_nack_mark(Ack_Nack_Description_t *ack_desc, unsigned int idx, bool received)
{
//ack_desc->RECEIVED_BLOCK_BITMAP[sizeof(ack_desc->RECEIVED_BLOCK_BITMAP) - 1] = 0xff;
//memset(ack_desc->RECEIVED_BLOCK_BITMAP, 0xff, sizeof(ack_desc->RECEIVED_BLOCK_BITMAP));
if (received)
ack_desc->RECEIVED_BLOCK_BITMAP[sizeof(ack_desc->RECEIVED_BLOCK_BITMAP) - idx/8 - 1] |= (1 << (idx & 0x03));
else
ack_desc->RECEIVED_BLOCK_BITMAP[sizeof(ack_desc->RECEIVED_BLOCK_BITMAP) - idx/8 - 1] &= ~(1 << (idx & 0x03));
}
static int test_rlcmac_prim_up_cb(struct osmo_gprs_rlcmac_prim *rlcmac_prim, void *user_data)
{
const char *pdu_name = osmo_gprs_rlcmac_prim_name(rlcmac_prim);
switch (rlcmac_prim->oph.sap) {
case OSMO_GPRS_RLCMAC_SAP_GMMRR:
printf("%s(): Rx %s TLLI=0x%08x\n", __func__, pdu_name, rlcmac_prim->gmmrr.page_ind.tlli);
break;
case OSMO_GPRS_RLCMAC_SAP_GRR:
printf("%s(): Rx %s TLLI=0x%08x ll=[%s]\n", __func__, pdu_name,
rlcmac_prim->grr.tlli,
osmo_hexdump(rlcmac_prim->grr.ll_pdu, rlcmac_prim->grr.ll_pdu_len));
break;
default:
printf("%s(): Unexpected Rx %s\n", __func__, pdu_name);
OSMO_ASSERT(0);
}
return 0;
}
static int test_rlcmac_prim_down_cb(struct osmo_gprs_rlcmac_prim *rlcmac_prim, void *user_data)
{
const char *pdu_name = osmo_gprs_rlcmac_prim_name(rlcmac_prim);
switch (rlcmac_prim->oph.sap) {
case OSMO_GPRS_RLCMAC_SAP_L1CTL:
switch (OSMO_PRIM_HDR(&rlcmac_prim->oph)) {
case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_PDCH_DATA, PRIM_OP_REQUEST):
printf("%s(): Rx %s fn=%u ts=%u data_len=%u data=[%s]\n", __func__, pdu_name,
rlcmac_prim->l1ctl.pdch_data_req.fn,
rlcmac_prim->l1ctl.pdch_data_req.ts_nr,
rlcmac_prim->l1ctl.pdch_data_req.data_len,
osmo_hexdump(rlcmac_prim->l1ctl.pdch_data_req.data, rlcmac_prim->l1ctl.pdch_data_req.data_len));
break;
case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_RACH, PRIM_OP_REQUEST):
last_rach_req_ra = rlcmac_prim->l1ctl.rach_req.ra;
printf("%s(): Rx %s ra=0x%02x\n", __func__, pdu_name, last_rach_req_ra);
break;
case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_UL_TBF, PRIM_OP_REQUEST):
printf("%s(): Rx %s ul_tbf_nr=%u ul_slotmask=0x%02x\n", __func__, pdu_name,
rlcmac_prim->l1ctl.cfg_ul_tbf_req.ul_tbf_nr,
rlcmac_prim->l1ctl.cfg_ul_tbf_req.ul_slotmask);
break;
case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_DL_TBF, PRIM_OP_REQUEST):
printf("%s(): Rx %s dl_tbf_nr=%u dl_slotmask=0x%02x dl_tfi=%u\n", __func__, pdu_name,
rlcmac_prim->l1ctl.cfg_dl_tbf_req.dl_tbf_nr,
rlcmac_prim->l1ctl.cfg_dl_tbf_req.dl_slotmask,
rlcmac_prim->l1ctl.cfg_dl_tbf_req.dl_tfi);
break;
default:
printf("%s(): Rx %s\n", __func__, pdu_name);
}
break;
default:
printf("%s(): Unexpected Rx %s\n", __func__, pdu_name);
OSMO_ASSERT(0);
}
return 0;
}
static const uint8_t llc_dummy_command[] = {
0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b
};
static struct msgb *create_dl_data_block(uint8_t dl_tfi, uint8_t usf, enum gprs_rlcmac_coding_scheme cs, uint8_t bsn, bool fbi, bool s_p, uint8_t rrbp)
{
struct msgb *msg = msgb_alloc(128, __func__);
struct gprs_rlcmac_rlc_dl_data_header *hdr;
struct gprs_rlcmac_rlc_li_field *lime;
hdr = (struct gprs_rlcmac_rlc_dl_data_header *)msgb_put(msg, gprs_rlcmac_mcs_size_dl(cs));
hdr->pt = 0; /* RLC/MAC block contains an RLC data block */
hdr->rrbp = rrbp;
hdr->s_p = s_p ? 1 : 0;
hdr->usf = usf;
hdr->pr = 0;
hdr->tfi = dl_tfi;
hdr->fbi = fbi ? 1 : 0;
hdr->tfi = dl_tfi;
hdr->bsn = bsn;
hdr->e = 0;
lime = &hdr->lime[0];
lime->li = sizeof(llc_dummy_command);
lime->m = 0;
lime->e = 1;
msg->l3h = &lime->ll_pdu[0];
memset(msg->l3h, 0x2b, msgb_l3len(msg));
memcpy(msg->l3h, llc_dummy_command, sizeof(llc_dummy_command));
return msg;
}
void prepare_test(void)
{
int rc;
rc = osmo_gprs_rlcmac_init(OSMO_GPRS_RLCMAC_LOCATION_MS);
OSMO_ASSERT(rc == 0);
osmo_gprs_rlcmac_prim_set_up_cb(test_rlcmac_prim_up_cb, NULL);
osmo_gprs_rlcmac_prim_set_down_cb(test_rlcmac_prim_down_cb, NULL);
}
void cleanup_test(void)
{
/* Reinit the RLCMAC layer so that data generated during the test is freed within the test context: */
osmo_gprs_rlcmac_init(OSMO_GPRS_RLCMAC_LOCATION_MS);
}
static void test_ul_tbf_attach(void)
{
struct osmo_gprs_rlcmac_prim *rlcmac_prim;
int rc;
printf("=== %s start ===\n", __func__);
prepare_test();
RlcMacDownlink_t dl_block;
Ack_Nack_Description_t *ack_desc = &dl_block.u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description;
uint32_t tlli = 0x2342;
uint8_t ul_tfi = 0;
uint8_t ts_nr = 7;
uint8_t usf = 0;
uint32_t rts_fn = 4;
rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_grr_unitdata_req(tlli, pdu_llc_gmm_att_req,
sizeof(pdu_llc_gmm_att_req));
rlcmac_prim->grr.unitdata_req.sapi = OSMO_GPRS_RLCMAC_LLC_SAPI_GMM;
rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim);
OSMO_ASSERT(sizeof(ccch_imm_ass_pkt_ul_tbf_normal) == GSM_MACBLOCK_LEN);
ccch_imm_ass_pkt_ul_tbf_normal[7] = last_rach_req_ra; /* Update RA to match */
rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal);
rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
/* Trigger transmission of LLC data (GMM Attach) (first part) */
rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf);
rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
OSMO_ASSERT(rc == 0);
/* Trigger transmission of LLC data (GMM Attach) (second part) */
rts_fn = fn_next_block(rts_fn);
rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf);
rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
OSMO_ASSERT(rc == 0);
/* PCU acks it: */
ul_ack_nack_init(&dl_block, ul_tfi, GPRS_RLCMAC_CS_2);
ack_desc->STARTING_SEQUENCE_NUMBER = 1;
ack_desc->FINAL_ACK_INDICATION = 1;
ul_ack_nack_mark(ack_desc, 0, true);
ul_ack_nack_mark(ack_desc, 1, true);
rlcmac_prim = create_dl_ctrl_block(&dl_block, ts_nr, rts_fn);
rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
OSMO_ASSERT(rc == 0);
printf("=== %s end ===\n", __func__);
cleanup_test();
}
/* PCU allocates a DL TBF through PCH ImmAss for MS (when in packet-idle) */
static void test_dl_tbf_ccch_assign(void)
{
struct osmo_gprs_rlcmac_prim *rlcmac_prim;
int rc;
struct msgb *dl_data_msg;
printf("=== %s start ===\n", __func__);
prepare_test();
uint32_t tlli = 0x0000001;
uint8_t ts_nr = 7;
uint8_t usf = 0;
uint32_t rts_fn = 4;
uint8_t dl_tfi = 0;
uint8_t rrbp = GPRS_RLCMAC_RRBP_N_plus_17_18;
/* Notify RLCMAC about our TLLI */
rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_gmmrr_assign_req(tlli);
rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim);
OSMO_ASSERT(sizeof(ccch_imm_ass_pkt_dl_tbf) == GSM_MACBLOCK_LEN);
rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_dl_tbf);
rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
OSMO_ASSERT(rc == 0);
/* Transmit some DL LLC data MS<-PCU */
dl_data_msg = create_dl_data_block(dl_tfi, usf, GPRS_RLCMAC_CS_1, 0, true, true, rrbp);
rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_data_ind(ts_nr, rts_fn, 0, 0, 0,
msgb_data(dl_data_msg),
msgb_length(dl_data_msg));
rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
OSMO_ASSERT(rc == 0);
msgb_free(dl_data_msg);
/* Trigger transmission of DL ACK/NACK */
rts_fn = rrbp2fn(rts_fn, rrbp);
rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf);
rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
OSMO_ASSERT(rc == 0);
printf("=== %s end ===\n", __func__);
cleanup_test();
}
static const struct log_info_cat test_log_categories[] = { };
static const struct log_info test_log_info = {
.cat = test_log_categories,
.num_cat = ARRAY_SIZE(test_log_categories),
};
int main(int argc, char *argv[])
{
tall_ctx = talloc_named_const(NULL, 1, __FILE__);
osmo_init_logging2(tall_ctx, &test_log_info);
log_parse_category_mask(osmo_stderr_target, "DLGLOBAL,1:");
osmo_fsm_log_addr(false);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_set_print_level(osmo_stderr_target, 1);
log_set_use_color(osmo_stderr_target, 0);
test_ul_tbf_attach();
test_dl_tbf_ccch_assign();
talloc_free(tall_ctx);
}