#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 * All rights reserved. * * Released under the terms of GNU General Public License, Version 2 or * (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later */ 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; if (!in.blocks().is_bound()) { /* we don't have nay blocks: Add length value (zero) */ in.mac__hdr().e() = false; /* E=0: extension octet follows */ } else if (in.blocks().size_of() == 1 && in.blocks()[0].hdr() == OMIT_VALUE) { /* If there's only a single block, and that block has no HDR value defined, */ in.mac__hdr().e() = true; /* E=0: extension octet follows */ } else { /* Length value */ in.mac__hdr().e() = false; } /* Fix other presence indications */ in.mac__hdr().tlli__ind() = in.tlli().is_bound() && in.tlli() != OMIT_VALUE; in.mac__hdr().pfi__ind() = in.pfi().is_bound() && in.pfi() != OMIT_VALUE; /* use automatic/generated encoder for header */ in.mac__hdr().encode(UlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); if (in.mac__hdr().e() == false) { /* Add LI octets, if any */ if (!in.blocks().is_bound()) { ttcn_buffer.put_c(0x01); /* M=0, E=1 LEN=0 */ } else { for (i = 0; i < in.blocks().size_of(); i++) { #if 0 /* check for penultimate block */ if (i == in.blocks().size_of()-2) { /* if last block has no header, no more LI */ if (in.blocks()[i+1].hdr() == OMIT_VALUE) { in.blocks()[i].hdr()().more() = true; } else { /* header present, we have to encode LI */ in.blocks()[i].hdr()().more() = false; in.blocks()[i].hdr()().length__ind() = in.blocks()[i+1].payload().lengthof(); } } else if (i < in.blocks().size_of()-2) { /* one of the first blocks, before the penultimate or last */ in.blocks()[i].hdr()().e() = false; /* LI present */ /* re-compute length */ in.blocks()[i].hdr()().length__ind() = in.blocks()[i+1].payload().lengthof(); } /* Encode LI octet if E=0 */ } #endif if (in.blocks()[i].hdr() != OMIT_VALUE) { in.blocks()[i].hdr()().encode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW); } } } } if (in.mac__hdr().tlli__ind()) { ttcn_buffer.put_string(in.tlli()); } 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()) { ret_val.tlli() = OCTETSTRING(4, ttcn_buffer.get_read_data()); 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; } OCTETSTRING enc__RlcmacUlBlock(const RlcmacUlBlock& si) { if (si.ischosen(RlcmacUlBlock::ALT_data)) return enc__RlcmacUlDataBlock(si.data()); else return enc__RlcmacUlCtrlBlock(si.ctrl()); } OCTETSTRING enc__RlcmacDlBlock(const RlcmacDlBlock& si) { if (si.ischosen(RlcmacDlBlock::ALT_data)) return enc__RlcmacDlDataBlock(si.data()); else return enc__RlcmacDlCtrlBlock(si.ctrl()); } RlcmacUlBlock dec__RlcmacUlBlock(const OCTETSTRING& stream) { RlcmacUlBlock ret_val; unsigned char pt = stream[0].get_octet() >> 6; if (pt == MacPayloadType::MAC__PT__RLC__DATA) ret_val.data() = dec__RlcmacUlDataBlock(stream); else ret_val.ctrl() = dec__RlcmacUlCtrlBlock(stream); return ret_val; } RlcmacDlBlock dec__RlcmacDlBlock(const OCTETSTRING& stream) { RlcmacDlBlock ret_val; unsigned char pt = stream[0].get_octet() >> 6; if (pt == MacPayloadType::MAC__PT__RLC__DATA) ret_val.data() = dec__RlcmacDlDataBlock(stream); else ret_val.ctrl() = dec__RlcmacDlCtrlBlock(stream); return ret_val; } } // namespace