| #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> |
| */ |
| |
| 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 |