diff --git a/gprs_gb/gen_links.sh b/gprs_gb/gen_links.sh index 20b40bea9..e93969050 100755 --- a/gprs_gb/gen_links.sh +++ b/gprs_gb/gen_links.sh @@ -30,5 +30,5 @@ gen_links $DIR $FILES DIR=../library -FILES="General_Types.ttcn GSM_Types.ttcn GSM_RR_Types.ttcn Osmocom_Types.ttcn RLCMAC_Types.ttcn L1CTL_Types.ttcn L1CTL_PortType.ttcn" +FILES="General_Types.ttcn GSM_Types.ttcn GSM_RR_Types.ttcn Osmocom_Types.ttcn RLCMAC_Types.ttcn RLCMAC_EncDec.cc L1CTL_Types.ttcn L1CTL_PortType.ttcn" gen_links $DIR $FILES diff --git a/library/RLCMAC_EncDec.cc b/library/RLCMAC_EncDec.cc new file mode 100644 index 000000000..b2f05250e --- /dev/null +++ b/library/RLCMAC_EncDec.cc @@ -0,0 +1,265 @@ +#include "RLCMAC_Types.hh" +#include "GSM_Types.hh" +/* Decoding of TS 44.060 GPRS RLC/MAC blocks, portions requiring manual functions + * beyond what TITAN RAW coder can handle internally. + * + * (C) 2017 by Harald Welte + */ + +namespace RLCMAC__Types { + +OCTETSTRING enc__RlcmacDlDataBlock(const RlcmacDlDataBlock& si) +{ + RlcmacDlDataBlock in = si; + OCTETSTRING ret_val; + TTCN_Buffer ttcn_buffer; + int i; + + /* Fix 'e' bit of initial header based on following blocks */ + if (!in.blocks().is_bound() || (in.blocks().size_of() == 1 && !in.blocks()[0].hdr().is_bound())) + in.mac__hdr().hdr__ext().e() = true; + else + in.mac__hdr().hdr__ext().e() = false; + + /* use automatic/generated decoder for header */ + in.mac__hdr().encode(DlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); + + /* Add LI octets, if any */ + if (in.blocks().is_bound() && + (in.blocks().size_of() != 1 || in.blocks()[0].hdr().is_bound())) { + /* first write LI octets */ + for (i = 0; i < in.blocks().size_of(); i++) { + /* fix the 'E' bit in case it is not clear */ + if (i < in.blocks().size_of()-1) + in.blocks()[i].hdr().e() = false; + else + in.blocks()[i].hdr().e() = true; + in.blocks()[i].hdr().encode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); + } + } + if (in.blocks().is_bound()) { + for (i = 0; i < in.blocks().size_of(); i++) { + if (!in.blocks()[i].is_bound()) + continue; + ttcn_buffer.put_string(in.blocks()[i].payload()); + } + } + + ttcn_buffer.get_string(ret_val); + return ret_val; +} + +RlcmacDlDataBlock dec__RlcmacDlDataBlock(const OCTETSTRING& stream) +{ + RlcmacDlDataBlock ret_val; + TTCN_Buffer ttcn_buffer(stream); + int num_llc_blocks = 0; + + /* use automatic/generated decoder for header */ + ret_val.mac__hdr().decode(DlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); + + /* optional extension octets, containing LI+M+E of Llc blocks */ + if (ret_val.mac__hdr().hdr__ext().e() == false) { + /* extension octet follows, i.e. optional Llc length octets */ + while (1) { + /* decode one more extension octet with LlcBlocHdr inside */ + LlcBlock lb; + lb.hdr().decode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); + ret_val.blocks()[num_llc_blocks++] = lb; + + /* if E == '1'B, we can proceed further */ + if (lb.hdr().e() == true) + break; + } + } + + /* RLC blocks at end */ + if (ret_val.mac__hdr().hdr__ext().e() == true) { + LlcBlock lb; + unsigned int length = ttcn_buffer.get_read_len(); + /* LI not present: The Upper Layer PDU that starts with the current RLC data block either + * fills the current RLC data block precisely or continues in the following in-sequence RLC + * data block */ + lb.payload() = OCTETSTRING(length, ttcn_buffer.get_read_data()); + ttcn_buffer.increase_pos(length); + ret_val.blocks()[0] = lb; + } else { + if (ret_val.blocks().is_bound()) { + for (int i = 0; i < ret_val.blocks().size_of(); i++) { + unsigned int length = ret_val.blocks()[i].hdr().length__ind(); + if (length > ttcn_buffer.get_read_len()) + length = ttcn_buffer.get_read_len(); + ret_val.blocks()[i].payload() = OCTETSTRING(length, ttcn_buffer.get_read_data()); + ttcn_buffer.increase_pos(length); + } + } + } + + return ret_val; +} + + +OCTETSTRING enc__RlcmacUlDataBlock(const RlcmacUlDataBlock& si) +{ + RlcmacUlDataBlock in = si; + OCTETSTRING ret_val; + TTCN_Buffer ttcn_buffer; + int i; + + /* Fix 'e' bit of initial header based on following blocks */ + if (!in.blocks().is_bound() || (in.blocks().size_of() == 1 && !in.blocks()[0].hdr().is_bound())) + in.mac__hdr().e() = true; + else + in.mac__hdr().e() = false; + + /* Fix other presence indications */ + in.mac__hdr().tlli__ind() = in.tlli().is_bound(); + in.mac__hdr().pfi__ind() = in.pfi().is_bound(); + + /* use automatic/generated decoder for header */ + in.mac__hdr().encode(UlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); + + /* Add LI octets, if any */ + if (in.blocks().is_bound() && + (in.blocks().size_of() != 1 || in.blocks()[0].hdr().is_bound())) { + /* first write LI octets */ + for (i = 0; i < in.blocks().size_of(); i++) { + /* fix the 'E' bit in case it is not clear */ + if (i < in.blocks().size_of()-1) + in.blocks()[i].hdr().e() = false; + else + in.blocks()[i].hdr().e() = true; + in.blocks()[i].hdr().encode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); + } + } + + if (in.mac__hdr().tlli__ind()) { + /* FIXME */ + //in.tlli().encode(GSM__Types::GprsTlli_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); + INTEGER t = in.tlli(); + unsigned int tmp = t.get_long_long_val(); + ttcn_buffer.put_c(tmp >> 24); + ttcn_buffer.put_c(tmp >> 16); + ttcn_buffer.put_c(tmp >> 8); + ttcn_buffer.put_c(tmp); + } + + if (in.mac__hdr().pfi__ind()) { + in.pfi().encode(RlcmacUlDataBlock_pfi_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); + } + + if (in.blocks().is_bound()) { + for (i = 0; i < in.blocks().size_of(); i++) { + if (!in.blocks()[i].is_bound()) + continue; + ttcn_buffer.put_string(in.blocks()[i].payload()); + } + } + + ttcn_buffer.get_string(ret_val); + return ret_val; +} + +RlcmacUlDataBlock dec__RlcmacUlDataBlock(const OCTETSTRING& stream) +{ + RlcmacUlDataBlock ret_val; + TTCN_Buffer ttcn_buffer(stream); + int num_llc_blocks = 0; + + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC); + TTCN_Logger::log_event_str("==================================\n" + "dec_RlcmacUlDataBlock(): Stream before decoding: "); + stream.log(); + TTCN_Logger::end_event(); + + /* use automatic/generated decoder for header */ + ret_val.mac__hdr().decode(UlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); + + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC); + TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): Stream after decoding hdr: "); + ttcn_buffer.log(); + TTCN_Logger::end_event(); + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC); + TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): ret_val after decoding hdr: "); + ret_val.log(); + TTCN_Logger::end_event(); + + /* Manually decoder remainder of ttcn_buffer, containing optional header octets, + * optional tlli, optional pfi and LLC Blocks */ + + /* optional extension octets, containing LI+M+E of Llc blocks */ + if (ret_val.mac__hdr().e() == false) { + /* extension octet follows, i.e. optional Llc length octets */ + while (1) { + /* decode one more extension octet with LlcBlocHdr inside */ + LlcBlock lb; + lb.hdr().decode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); + ret_val.blocks()[num_llc_blocks++] = lb; + + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC); + TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): Stream after decoding ExtOct: "); + ttcn_buffer.log(); + TTCN_Logger::end_event(); + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC); + TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): ret_val after decoding ExtOct: "); + ret_val.log(); + TTCN_Logger::end_event(); + + /* if E == '1'B, we can proceed further */ + if (lb.hdr().e() == true) + break; + } + } + + /* parse optional TLLI */ + if (ret_val.mac__hdr().tlli__ind()) { + /* FIXME: Why is this not working ?!? */ + //ret_val.tlli().decode(GSM__Types::GprsTlli_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); + const unsigned char *cur = ttcn_buffer.get_read_data(); + unsigned int tmp = cur[0] << 24 | cur[1] << 16 | cur[2] << 8 | cur[3]; + INTEGER t; + t.set_long_long_val(tmp); + ret_val.tlli() = t; + ttcn_buffer.increase_pos(4); + } + /* parse optional PFI */ + if (ret_val.mac__hdr().pfi__ind()) { + ret_val.pfi().decode(RlcmacUlDataBlock_pfi_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); + } + + /* RLC blocks at end */ + if (ret_val.mac__hdr().e() == true) { + LlcBlock lb; + unsigned int length = ttcn_buffer.get_read_len(); + /* LI not present: The Upper Layer PDU that starts with the current RLC data block either + * fills the current RLC data block precisely or continues in the following in-sequence RLC + * data block */ + lb.payload() = OCTETSTRING(length, ttcn_buffer.get_read_data()); + ttcn_buffer.increase_pos(length); + ret_val.blocks()[0] = lb; + } else { + if (ret_val.blocks().is_bound()) { + for (int i = 0; i < ret_val.blocks().size_of(); i++) { + unsigned int length = ret_val.blocks()[i].hdr().length__ind(); + if (length > ttcn_buffer.get_read_len()) + length = ttcn_buffer.get_read_len(); + ret_val.blocks()[i].payload() = OCTETSTRING(length, ttcn_buffer.get_read_data()); + ttcn_buffer.increase_pos(length); + } + } + } + + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC); + TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): Stream before return: "); + ttcn_buffer.log(); + TTCN_Logger::end_event(); + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC); + TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): ret_val before return: "); + ret_val.log(); + TTCN_Logger::end_event(); + + return ret_val; +} + + +} // namespace diff --git a/library/RLCMAC_Types.ttcn b/library/RLCMAC_Types.ttcn index f73721c00..6cd6e49b4 100644 --- a/library/RLCMAC_Types.ttcn +++ b/library/RLCMAC_Types.ttcn @@ -104,66 +104,74 @@ module RLCMAC_Types { with { extension "prototype(convert) decode(RAW)" }; /* a single RLC block / LLC-segment */ - - type record RlcBlockHdr { + type record LlcBlockHdr { uint6_t length_ind, /* 1 = new LLC PDU starts */ BIT1 more, /* 0 = another extension octet after LLC PDU, 1 = no more extension octets */ - BIT1 e - } with { variant "" }; - - type record RlcBlock { - uint6_t length_ind, - BIT1 more, - BIT1 e, - octetstring rlc optional + boolean e } with { - variant (rlc) "PRESENCE (more = '1'B)" - variant (length_ind) "LENGTHTO(length_ind, more, e, rlc)" + variant (e) "FIELDLENGTH(1)" }; - - type record of RlcBlock RlcBlocks; + type record LlcBlock { + /* Header is only present if LI field was present */ + LlcBlockHdr hdr, + octetstring payload + } with { variant "" }; + type record of LlcBlock LlcBlocks; /* TS 44.060 10.2.1 Downlink RLC data block */ - type record RlcmacDlDataBlock { + type record DlMacHdrDataExt { /* Octet 1 */ - DlMacHeader mac_hdr, - /* Octet 2 */ PowerReduction pr, BIT1 spare, uint4_t tfi, /* 3 or 4? */ boolean fbi, - /* Octet 3 */ + /* Octet 2 */ uint7_t bsn, - BIT1 e ('1'B), - RlcBlocks rlc_blocks + boolean e + } with { + variant (e) "FIELDLENGTH(1)" + }; + type record DlMacDataHeader { + DlMacHeader mac_hdr, + DlMacHdrDataExt hdr_ext } with { variant "" }; + type record RlcmacDlDataBlock { + DlMacDataHeader mac_hdr, + /* Octet 3..M / N: manual C++ Decoder */ + LlcBlocks blocks + } with { + variant "" + }; - external function enc_RlcmacDlDataBlock(in RlcmacDlDataBlock si) return octetstring - with { extension "prototype(convert) encode(RAW)" }; - external function dec_RlcmacDlDataBlock(in octetstring stream) return RlcmacDlDataBlock - with { extension "prototype(convert) decode(RAW)" }; + external function enc_RlcmacDlDataBlock(in RlcmacDlDataBlock si) return octetstring; + external function dec_RlcmacDlDataBlock(in octetstring stream) return RlcmacDlDataBlock; /* TS 44.060 10.2.2 */ type record UlMacDataHeader { + /* Octet 0 */ MacPayloadType pt, uint4_t countdown, boolean stall_ind, - boolean retry + boolean retry, + /* Octet 1 */ + BIT1 spare, + boolean pfi_ind, + uint5_t tfi, + boolean tlli_ind, + /* Octet 2 */ + uint7_t bsn, + boolean e } with { - variant (stall_ind) "FIELDLENGTH(1)" - variant (retry) "FIELDLENGTH(1)" + variant (stall_ind) "FIELDLENGTH(1)" + variant (retry) "FIELDLENGTH(1)" + variant (pfi_ind) "FIELDLENGTH(1)" + variant (tlli_ind) "FIELDLENGTH(1)" + variant (e) "FIELDLENGTH(1)" }; - type record RlcMacUlTlli { - RlcBlockHdr hdr, - uint32_t tlli - } with { - variant "" - } - type record RlcMacUlPfi { uint7_t pfi, boolean m @@ -175,26 +183,16 @@ module RLCMAC_Types { type record RlcmacUlDataBlock { /* MAC header */ UlMacDataHeader mac_hdr, - /* Octet 1 */ - BIT1 spare, - boolean pfi_ind, - uint5_t tfi, - boolean tlli_ind, - /* Octet 2 */ - uint7_t bsn, - BIT1 e ('1'B), - /* Octet 3 (optional) */ - RlcMacUlTlli tlli, - RlcMacUlPfi pfi, - RlcBlocks blocks + /* Octet 3 ... M (optional): manual C++ Decoder */ + GprsTlli tlli optional, + RlcMacUlPfi pfi optional, + LlcBlocks blocks } with { - variant (tlli) "PRESENCE(tlli_ind = true)" - variant (pfi) "PRESENCE(pfi_ind = true)" + variant (tlli) "PRESENCE(mac_hdr.tlli_ind = true)" + variant (pfi) "PRESENCE(mac_hdr.pfi_ind = true)" }; - external function enc_RlcmacUlDataBlock(in RlcmacUlDataBlock si) return octetstring - with { extension "prototype(convert) encode(RAW)" }; - external function dec_RlcmacUlDataBlock(in octetstring stream) return RlcmacUlDataBlock - with { extension "prototype(convert) decode(RAW)" }; + external function enc_RlcmacUlDataBlock(in RlcmacUlDataBlock si) return octetstring; + external function dec_RlcmacUlDataBlock(in octetstring stream) return RlcmacUlDataBlock; } with { encode "RAW"; variant "FIELDORDER(msb)" }