330 lines
10 KiB
C++
330 lines
10 KiB
C++
#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 <laforge@gnumonks.org>
|
|
* 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
|