| module IuUP_Emulation { |
| |
| /* IuUP emulation, uses the encoding/decoding from IuUP_Types. |
| * |
| * rather than running in a separate component with related primitives, |
| * we just implement a set of functions and data types which can be used |
| * by other code (such as an RTP endpoint) to implement IuUP support. |
| * |
| * (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 |
| */ |
| |
| import from Osmocom_Types all; |
| import from IuUP_Types all; |
| |
| |
| type record IuUP_RabFlowCombination { |
| IuUP_RFCI rfci, |
| /* number of bits per sub-flow */ |
| RecOfU8 sub_flow_bits, |
| /* IPTI value in number of ITIs for the corresponding RFCI */ |
| uint8_t ipti |
| }; |
| |
| template (value) IuUP_RabFlowCombination t_IuUP_RFC(IuUP_RFCI rfci, RecOfU8 subflow_bits, uint8_t ipti) := { |
| rfci := rfci, |
| sub_flow_bits := subflow_bits, |
| ipti := ipti |
| } |
| |
| template (value) IuUP_RabFlowCombination t_IuUP_RFC_AMR_12_2(IuUP_RFCI rfci) := t_IuUP_RFC(rfci, {81, 103, 60}, 1); |
| template (value) IuUP_RabFlowCombination t_IuUP_RFC_AMR_SID(IuUP_RFCI rfci) := t_IuUP_RFC(rfci, {34, 0, 0}, 7); |
| |
| type record IuUP_Config { |
| /* actively send INIT (true) or only passively respond (false) */ |
| boolean active_init, |
| boolean data_pdu_type_0, |
| /* RAB Flow Combinations */ |
| record of IuUP_RabFlowCombination rab_flow_combs |
| }; |
| |
| type enumerated IuUP_Em_State { |
| ST_INIT, |
| ST_DATA_TRANSFER_READY |
| }; |
| |
| |
| |
| type record IuUP_Entity { |
| IuUP_Config cfg, |
| IuUP_Em_State state, |
| IuUP_FrameNr tx_next_frame_nr, |
| IuUP_FrameNr rx_last_frame_nr optional, |
| IuUP_PDU pending_tx_pdu optional |
| }; |
| |
| template (value) IuUP_Entity t_IuUP_Entity(boolean act_init) := { |
| cfg := { |
| active_init := act_init, |
| data_pdu_type_0 := true, |
| rab_flow_combs := { t_IuUP_RFC_AMR_12_2(0), t_IuUP_RFC_AMR_SID(1) } |
| }, |
| state := ST_INIT, |
| tx_next_frame_nr := 0, |
| rx_last_frame_nr := omit, |
| pending_tx_pdu := omit |
| } |
| |
| |
| function f_IuUP_Em_rx_decaps(inout IuUP_Entity st, octetstring inp) return octetstring { |
| var IuUP_PDU pdu := dec_IuUP_PDU(inp); |
| if (ischosen(pdu.type_0)) { |
| if (st.cfg.data_pdu_type_0) { |
| /* FIXME: check header / CRC */ |
| st.rx_last_frame_nr := pdu.type_0.frame_nr; |
| return pdu.type_0.payload; |
| } else { |
| setverdict(fail, "PDU Type 0 received but 1 configured"); |
| mtc.stop; |
| } |
| } else if (ischosen(pdu.type_1)) { |
| if (st.cfg.data_pdu_type_0 == false) { |
| /* FIXME: check header / CRC */ |
| st.rx_last_frame_nr := pdu.type_1.frame_nr; |
| return pdu.type_1.payload; |
| } else { |
| setverdict(fail, "PDU Type 1 received but 0 configured"); |
| mtc.stop; |
| } |
| } else if (ischosen(pdu.type_14)) { |
| if (match(pdu, tr_IuUP_INIT)) { |
| if (st.cfg.active_init == true) { |
| setverdict(fail, "INIT received in ACTIVE role"); |
| mtc.stop; |
| } else { |
| /* store an INIT_ACK to be transmitted later */ |
| st.pending_tx_pdu := valueof(ts_IuUP_INIT_ACK(pdu.type_14.frame_nr, |
| pdu.type_14.iuup_version)); |
| } |
| } else if (match(pdu, tr_IuUP_INIT_ACK)) { |
| if (st.cfg.active_init == true) { |
| log("IuUP INIT_ACK Received"); |
| st.pending_tx_pdu := omit; /* Drop pending Init retrans */ |
| st.state := ST_DATA_TRANSFER_READY; |
| } else { |
| setverdict(fail, "INIT_ACK received in PASSIVE role"); |
| mtc.stop; |
| } |
| } |
| return ''O; |
| } else { |
| setverdict(fail, "Impossible IuUP PDU decoded from ", inp); |
| mtc.stop; |
| } |
| self.stop; |
| } |
| |
| function f_IuUP_Em_tx_encap(inout IuUP_Entity st, in octetstring payload) return octetstring { |
| var IuUP_PDU pdu; |
| select (st.state) { |
| case (ST_INIT) { |
| if (st.cfg.active_init) { |
| if (not isvalue(st.pending_tx_pdu)) { |
| /* send INIT */ |
| pdu := valueof(ts_IuUP_INIT('160051673C01270000820000001710000100'O)); |
| st.pending_tx_pdu := pdu; |
| } /* else: wait for INIT-ACK return ''O at the end */ |
| |
| } else { |
| /* wait for INIT */ |
| if (isvalue(st.pending_tx_pdu)) { |
| /* we're waiting to transmit the INIT_ACK in response to an |
| * init (passive) */ |
| pdu := st.pending_tx_pdu; |
| st.pending_tx_pdu := omit; |
| st.state := ST_DATA_TRANSFER_READY; |
| } |
| } |
| } |
| case (ST_DATA_TRANSFER_READY) { |
| if (st.cfg.data_pdu_type_0) { |
| pdu := valueof(ts_IuUP_Type0(st.tx_next_frame_nr, 0, payload)); |
| } else { |
| pdu := valueof(ts_IuUP_Type1(st.tx_next_frame_nr, 0, payload)); |
| } |
| st.tx_next_frame_nr := (st.tx_next_frame_nr + 1) mod 16; |
| } |
| } |
| if (isvalue(pdu)) { |
| return f_enc_IuUP_PDU(pdu); |
| } else { |
| return ''O; |
| } |
| } |
| |
| |
| } |