WIP: BSSGP related hepler functions; towards tests
diff --git a/gprs_gb/BSSGP_Helper.cc b/gprs_gb/BSSGP_Helper.cc
new file mode 100644
index 0000000..8cf97d2
--- /dev/null
+++ b/gprs_gb/BSSGP_Helper.cc
@@ -0,0 +1,96 @@
+
+#include "Octetstring.hh"
+#include "Error.hh"
+#include "Logger.hh"
+
+#include <stdint.h>
+
+namespace BSSGP__Helper__Functions {
+
+/* convert a buffer filled with TLVs that have variable-length "length" fields (Osmocom TvLV) into a
+ * buffer filled with TLVs that have fixed 16-bit length values (TL16V format) */
+static OCTETSTRING transcode_tlv_part(OCTETSTRING const &in)
+{
+ const unsigned char *in_ptr = (const unsigned char *)in;
+ int in_len = in.lengthof();
+ int ofs = 0;
+ uint16_t data_len;
+ OCTETSTRING out(0, (const unsigned char *)"");
+
+ while (ofs < in_len) {
+ int remain_len = in_len - ofs;
+ int tl_length;
+
+ if (remain_len < 2) {
+ TTCN_error("Remaining input length (%d) insufficient for Tag+Length", remain_len);
+ break;
+ }
+
+ /* copy over tag */
+ if (in_ptr[ofs+1] & 0x80) {
+ /* E bit is set, 7-bit length field */
+ data_len = in_ptr[ofs+1] & 0x7F;
+ tl_length = 2;
+ } else {
+ /* E bit is not set, 15 bit length field */
+ if (in_len < 3) {
+ TTCN_error("Remaining input length insufficient for 2-octet length");
+ break;
+ }
+ data_len = in_ptr[ofs+1] << 8 | in_ptr[ofs+2];
+ tl_length = 3;
+ }
+ if (in_len < tl_length + data_len) {
+ TTCN_error("Remaining input length insufficient for TLV value length");
+ break;
+ }
+
+ /* Tag + 16bit length */
+ uint8_t hdr_buf[3];
+ hdr_buf[0] = in_ptr[ofs+0];
+ hdr_buf[1] = data_len >> 8;
+ hdr_buf[2] = data_len & 0xff;
+
+ OCTETSTRING tlv_hdr(3, hdr_buf);
+ out += tlv_hdr;
+
+ if (data_len) {
+ /* append octet string of current TLV to output octetstring */
+ OCTETSTRING tlv_val(data_len, in_ptr + ofs + tl_length);
+ out += tlv_val;
+ }
+
+ /* advance input offset*/
+ ofs += data_len + tl_length;
+ }
+
+ return out;
+}
+
+#define BSSGP_PDUT_DL_UNITDATA 0x00
+#define BSSGP_PDUT_UL_UNITDATA 0x01
+
+/* expand all the variable-length "length" fields of a BSSGP message (Osmocom TvLV) into
+ * statlc TL16V format */
+OCTETSTRING f__BSSGP__preprocess__pdu(OCTETSTRING const &in)
+{
+ const unsigned char *in_ptr = (const unsigned char *)in;
+ int in_len = in.lengthof();
+ uint8_t pdu_type = in_ptr[0];
+ uint8_t static_hdr_len = 1;
+
+ if (pdu_type == BSSGP_PDUT_DL_UNITDATA || pdu_type == BSSGP_PDUT_UL_UNITDATA)
+ static_hdr_len = 8;
+
+ if (in_len < static_hdr_len)
+ TTCN_error("BSSGP message is shorter (%u bytes) than minimum header length (%u bytes) for msg_type 0x%02x",
+ in_len, static_hdr_len, pdu_type);
+
+ /* prefix = non-TLV section of header */
+ OCTETSTRING prefix(static_hdr_len, in_ptr);
+ OCTETSTRING tlv_part_in(in_len - static_hdr_len, in_ptr + static_hdr_len);
+
+ return prefix + transcode_tlv_part(tlv_part_in);
+}
+
+}
diff --git a/gprs_gb/BSSGP_Helper_Functions.ttcn b/gprs_gb/BSSGP_Helper_Functions.ttcn
new file mode 100644
index 0000000..9c8af92
--- /dev/null
+++ b/gprs_gb/BSSGP_Helper_Functions.ttcn
@@ -0,0 +1,3 @@
+module BSSGP_Helper_Functions {
+ external function f_BSSGP_preprocess_pdu(in octetstring inp) return octetstring;
+};
diff --git a/gprs_gb/BSSGP_Types.ttcn b/gprs_gb/BSSGP_Types.ttcn
new file mode 100644
index 0000000..94d9cd0
--- /dev/null
+++ b/gprs_gb/BSSGP_Types.ttcn
@@ -0,0 +1,279 @@
+module BSSGP_Types {
+
+ import from General_Types all;
+ import from Osmocom_Types all;
+ import from GSM_Types all;
+
+ type enumerated BssgpPduType {
+ DL_UNITDATA ('00'H),
+ UL_UNITDATA ('01'H),
+ RA_CAPABILITY ('02'H),
+ DL_MBMS_UNITDATA ('04'H),
+ UL_MBMS_UNITDATA ('05'H),
+ /* between GMM SAPs */
+ PAGING_PS ('06'H),
+ PAGING_CS ('07'H),
+ RA_CAPABILITY_UPDATE ('08'H),
+ RA_CAPABILITY_UPDATE_ACK ('09'H),
+ RADIO_STATUS ('0A'H),
+ SUSPEND ('0B'H),
+ SUSPEND_ACK ('0C'H),
+ SUSPEND_NACK ('0D'H),
+ RESUME ('0E'H),
+ RESUME_ACK ('0F'H),
+ RESUME_NACK ('10'H),
+ /* between NM SAPs */
+ BVC_BLOCK ('20'H),
+ BVC_BLOCK_ACK ('21'H),
+ BVC_RESET ('22'H),
+ BVC_RESET_ACK ('23'H),
+ BVC_UNBLOCK ('24'H),
+ BVC_UNBLOCK_ACK ('25'H),
+ FLOW_CONTROL_BVC ('26'H),
+ FLOW_CONTROL_BVC_ACK ('27'H),
+ FLOW_CONTROL_MS ('28'H),
+ FLOW_CONTROL_MS_ACK ('29'H),
+ FLUSH_LL ('2A'H),
+ FLUSH_LL_ACK ('2B'H),
+ LLC_DISCARDED ('2C'H),
+ FLOW_CONTROL_PFC ('2D'H),
+ FLOW_CONTROL_PFC_ACK ('2E'H),
+ SGSN_INVOKE_TRACE ('40'H),
+ STATUS ('41'H)
+ /* between PFM SAPs : TODO */
+ /* between LCS SAPs : TODO */
+ /* between RIM SAPs : TODO */
+ /* between MBMS SAPs : TODO */
+ } with { variant "FIELDLENGTH(8)" };
+
+ type enumerated BssgpIEI {
+ ALIGNMENT_OCTETS ('00'H),
+ BMAX_DEFAULT_MS ('01'H),
+ BSS_AREA_INDICATION ('02'H),
+ BUCKET_LEAK_RATE ('03'H),
+ BVCI ('04'H),
+ BVC_BUCKET_SIZE ('05'H),
+ BVC_MEASUREMENT ('06'H),
+ CAUSE ('07'H),
+ CELL_ID ('08'H),
+ CHENNEL_NEEDED ('09'H),
+ DRX_PARAMETERS ('0A'H),
+ EMLPP_PRIORITY ('0B'H),
+ FLUSH_ACTION ('0C'H),
+ IMSI ('0D'H),
+ LLC_PDU ('0E'H),
+ LLC_FRAMES_DISCARDED ('0F'H),
+ LOCATION_AREA ('10'H),
+ MOBILE_IDENTITY ('11'H),
+ MS_BUCKET_SIZE ('12'H),
+ MS_RADIO_ACCESS_CAPABILITY ('13'H),
+ OMC_ID ('14'H),
+ PDU_IN_ERROR ('15'H),
+ PDU_LIFETIME ('16'H),
+ PRIORITY ('17'H),
+ QOS_PROFILE ('18'H),
+ RADIO_CAUSE ('19'H),
+ RA_CAP_UPD_CAUSE ('1A'H),
+ ROUTEING_AREA ('1B'H),
+ R_DEFAULT_MS ('1C'H),
+ SUSPE_DN_REFERENCE_NR ('1D'H),
+ TAG ('1E'H),
+ TLLI ('1F'H),
+ TMSI ('20'H),
+ TRACE_REFERENCE ('21'H),
+ TRACE_TYPE ('22'H),
+ TRANSACTION_ID ('23'H),
+ TRIGGER_ID ('24'H),
+ NUMBER_OF_OCTETS_AFFECTED ('25'H),
+ LSA_IDENTIFIER_LIST ('26'H),
+ LSA_INFORMATION ('27'H),
+ PACKET_FLOW_IDENTIFIER ('28'H),
+ PACKET_FLOW_TIMER ('29'H),
+ AGGREGATE_BSS_QOS_PROFILE ('3a'H),
+ FEATURE_BITMAP ('3b'H),
+ BUCKET_FILL_RATIO ('3c'H),
+ SERVICE_UTRAN_CCO ('3d'H),
+ NSEI ('3e'H),
+ RRLP_APDU ('3f'H),
+ LCS_QOS ('40'H),
+ LCS_CLIENT_TYPE ('41'H),
+ REQUESTED_GPS_ASSIST_DATA ('42'H),
+ LOCATION_TYPE ('43'H),
+ LOCATION_ESTIMATE ('44'H),
+ POSITIONING_DATA ('45'H),
+ DECIPHERING_KEYS ('46'H),
+ LCS_PRIORITY ('47'H),
+ LCS_CAUSE ('48'H),
+ LCS_CAPABILITY ('49'H),
+ RRLP_FLAGS ('4a'H),
+ RIM_APPLICATION_IDENTITY ('4b'H),
+ RIM_SEQUENCE_NUMBER ('4c'H),
+ RAN_INFO_REUEST_AC ('4d'H),
+ RAN_INFO_AC ('4e'H),
+ RIM_PDU_INDICATIONS ('4f'H),
+ PFC_FLOC_CONTROL_PARAMETERS ('52'H),
+ GLOBAL_CN_ID ('53'H),
+ RIM_ROUTING_INFORMATION ('54'H),
+ RIM_PROTOCOL_VERSION_NUMBER ('55'H),
+ APP_ERROR_CONTAINER ('56'H),
+ /* FIXME */
+ EXTENDED_FEATURE_BITMAP ('69'H)
+ } with { variant "FIELDLENGTH(8)" };
+
+ /* 11.3.28 */
+ type record BssgpQosProfile {
+ uint16_t r,
+ BIT2 spare,
+ boolean c_r,
+ boolean t,
+ boolean a,
+ uint3_t precedence
+ } with { variant (c_r) "FIELDLENGTH(1)"
+ variant (t) "FIELDLENGTH(1)"
+ variant (a) "FIELDLENGTH(1)"
+ };
+
+ /* 11.3.84 */
+ type record BssgpFeatureBitmap {
+ boolean mbms,
+ boolean enh_radio_status,
+ boolean pfc_fc,
+ boolean rim,
+ boolean lcs,
+ boolean inr,
+ boolean cbl,
+ boolean pfc
+ } with { variant "" };
+
+ /* 11.3.47 */
+ type record BssgpServiceUtranCco {
+ uint5_t spare,
+ uint3_t value_part
+ } with { variant "" };
+
+ /* 11.3.84 */
+ type record BssgpExtendedFeatureBitmap {
+ BIT7 spare,
+ BIT1 ps_handover
+ } with { variant "" };
+
+ type uint16_t BssgpPduLifetime;
+
+ /* TS 48.008 3.2.2.18 */
+ type record BssmapPriority {
+ BIT1 spare,
+ boolean pci,
+ uint4_t level,
+ boolean qa,
+ boolean pvi
+ } with { variant "" };
+
+ type BssmapPriority BssgpPriority;
+
+ type uint32_t BssgpTlli;
+
+ type uint16_t BssgpBvci;
+ type uint8_t BssgpCause;
+
+ type record BssgpCellId {
+ RoutingAreaIdentification ra_id,
+ CellIdentity cell_id
+ } with { variant "" };
+
+ type union BssgpIeUnion {
+ uint16_t bmax_default_ms, /* 11.3.2 */
+ uint16_t bucket_leak_rate, /* 11.3.4 */
+ uint16_t bvc_bucket_size, /* 11.3.5 */
+ BssgpBvci bvci, /* 11.3.6 */
+ uint16_t bvc_measurement, /* 11.3.7 */
+ BssgpCause cause, /* 11.3.8 */
+ BssgpCellId cell_id, /* 11.3.9 */
+ DrxParameter drx_parameter, /* 11.3.11 */
+ LocationAreaIdentification lai, /* 11.3.17 */
+ MobileIdentity mobile_id, /* 11.3.20 */
+ BssgpPduLifetime pdu_lifetime, /* 11.3.25 */
+ BssgpPriority priority, /* 11.3.27 */
+ BssgpQosProfile qos_profile, /* 11.3.28 */
+ BssgpTlli tlli, /* 11.3.25 */
+ uint16_t r_default_ms, /* 11.3.32 */
+ BssgpServiceUtranCco svc_utran_cco, /* 11.3.47 */
+ BssgpFeatureBitmap feature_bitmap, /* 11.3.40 */
+ BssgpExtendedFeatureBitmap ext_feature_bitmap, /* 11.3.84 */
+ octetstring other
+ };
+
+ type record BssgpTLV {
+ BssgpIEI iei,
+ /* we cannot express a variable-length "length" field with extension octets in the TTCN-3
+ * syntax, so we simply assume a plain 16 bit length value here and have a 'pseudl-BSSGP'
+ * translator in front which explands all variable-length "length" fields to 16bits */
+ uint16_t len,
+ BssgpIeUnion u
+ } with {
+ variant (u) "CROSSTAG(
+ bmax_default_ms, iei = BMAX_DEFAULT_MS;
+ bucket_leak_rate, iei = BUCKET_LEAK_RATE;
+ bvc_bucket_size, iei = BVC_BUCKET_SIZE;
+ bvci, iei = BVCI;
+ bvc_measurement, iei = BVC_MEASUREMENT;
+ cause, iei = CAUSE;
+ cell_id, iei = CELL_ID;
+ drx_parameter, iei = DRX_PARAMETERS;
+ lai, iei = LOCATION_AREA;
+ priority, iei = PRIORITY;
+ mobile_id, iei = MOBILE_IDENTITY;
+ pdu_lifetime, iei = PDU_LIFETIME;
+ qos_profile, iei = QOS_PROFILE;
+ tlli, iei = TLLI;
+ r_default_ms, iei = R_DEFAULT_MS;
+ svc_utran_cco, iei = SERVICE_UTRAN_CCO;
+ feature_bitmap, iei = FEATURE_BITMAP;
+ ext_feature_bitmap, iei = EXTENDED_FEATURE_BITMAP;
+ other, OTHERWISE)"
+ variant (len) "LENGTHTO(u)"
+ };
+
+ type record of BssgpTLV BssgpTLVs;
+
+ /* 10.2.1 */
+ type record BssgpDlUnitdata {
+ BssgpTlli tlli,
+ BssgpQosProfile qos_profile,
+ BssgpTLV pdu_lifetime,
+ /* optional parts */
+ BssgpTLVs tlvs
+ } with { variant "" };
+
+ /* 10.2.2 */
+ type record BssgpUlUnitdata {
+ BssgpTlli tlli,
+ BssgpQosProfile qos_profile,
+ BssgpTLV cell_id,
+ /* optional parts */
+ BssgpTLVs tlvs
+ } with { variant "" };
+
+ type record BssgpNormalPdu {
+ BssgpTLVs tlvs optional
+ } with { variant "" };
+
+ type union BssgpPduUnion {
+ BssgpDlUnitdata dl_unitdata,
+ BssgpUlUnitdata ul_unitdata,
+ BssgpNormalPdu other
+ };
+
+ type record BssgpPdu {
+ BssgpPduType pdu_type,
+ BssgpPduUnion u
+ } with {
+ variant (u) "CROSSTAG(
+ dl_unitdata, pdu_type = DL_UNITDATA;
+ ul_unitdata, pdu_type = UL_UNITDATA;
+ other, OTHERWISE)"
+ }
+
+ external function dec_BssgpPdu(in octetstring stream) return BssgpPdu
+ with { extension "prototype(convert) decode(RAW)" };
+
+} with { encode "RAW" };
diff --git a/gprs_gb/Test.ttcn b/gprs_gb/Test.ttcn
new file mode 100644
index 0000000..88a78a8
--- /dev/null
+++ b/gprs_gb/Test.ttcn
@@ -0,0 +1,79 @@
+module Test {
+
+ import from BSSGP_Helper_Functions all;
+ import from BSSGP_Types all;
+
+ type component dummy_CT {
+ }
+
+ function f_assert_prepr(in octetstring a, in octetstring b) {
+ log ("Input: ", a);
+ log ("Expected: ", b);
+ var octetstring a_preprocessed := f_BSSGP_preprocess_pdu(a);
+ log ("Preprocessed: ", a_preprocessed);
+
+ if (a_preprocessed != b) {
+ setverdict(fail);
+ } else {
+ setverdict(pass);
+ }
+ }
+
+ function f_dec_and_log(in octetstring inp) {
+ log("Input: ", inp);
+ var octetstring inp_p := f_BSSGP_preprocess_pdu(inp);
+ log ("Preprocessed: ", inp_p);
+ var BssgpPdu dec := dec_BssgpPdu(inp_p);
+ log("Decoded: ", dec);
+ }
+
+ testcase TC_selftest() runs on dummy_CT {
+ const octetstring c_bvc_reset_pcu := '2204820000078108088832f44000c80051e0'O;
+ const octetstring c_bvc_reset_q := '2204820000078100'O;
+ const octetstring c_status_pcu := '4107810515882204820000078103'O;
+ const octetstring c_reset_ack_q := '2304820000'O;
+ const octetstring c_reset_ack_pcu := '23048200c4'O;
+ const octetstring c_unblock_pcu := '24048200c4'O;
+ const octetstring c_unblock_ack_q := '25048200c4'O;
+ const octetstring c_fc_bvc_pcu := '261e8101058200fa038200c8018200fa1c8200c806820000'O;
+ const octetstring c_fc_bvc_ack_q := '271e8101'O;
+ const octetstring c_gmm_mo_att_req := '01bb146ddd000004088832f44000c80051e000800e003b01c001080103e5e000110a0005f4fb146ddd32f44000c8001d1b53432b37159ef9090070000dd9c6321200e00019b32c642401c0002017057bf0ec'O;
+ const octetstring c_gmm_mt_ac_req := '00bb146ddd0050001682ffff0a8204030e9c41c001081200102198c72477ea104895e8b959acc58b108182f4d045'O;
+ const octetstring c_gmm_mo_ac_resp := '01bb146ddd000004088832f44000c80051e000800e000e01c00508130122fa361f5fdd623d'O;
+ const octetstring c_gmm_mt_att_acc := '00bb146ddd0050001682ffff0a8204030e9841c005080201340432f44000c8001805f4fb146ddd0967d0'O;
+ const octetstring c_gmm_mt_det_req := '00bb146ddd0050001682ffff0a8204030e8941c00908050215f0b6'O;
+ const octetstring c_gmm_mo_att_cpl := '01fb146ddd000004088832f44000c80051e000800e000801c009080339d7bc'O;
+
+ /* single byte length to two byte length */
+ f_assert_prepr('04058101'O, '0405000101'O);
+ f_assert_prepr('040589000102030405060708'O, '04050009000102030405060708'O);
+ /* two byte length to two byte length */
+ f_assert_prepr('0405000101'O, '0405000101'O);
+ /* special case: DL-UD + UL-UD */
+ f_assert_prepr('00aabbccddeeffaa29822342'O, '00aabbccddeeffaa2900022342'O);
+ f_assert_prepr('01aabbccddeeffaa29822342'O, '01aabbccddeeffaa2900022342'O);
+ /* multiple TLVs */
+ f_assert_prepr('234281aa4382bbbb'O, '23420001aa430002bbbb'O);
+ f_assert_prepr('230080'O, '23000000'O);
+
+ f_dec_and_log(c_bvc_reset_pcu);
+ f_dec_and_log(c_bvc_reset_q);
+ f_dec_and_log(c_status_pcu);
+ f_dec_and_log(c_reset_ack_q);
+ f_dec_and_log(c_reset_ack_pcu);
+ f_dec_and_log(c_unblock_pcu);
+ f_dec_and_log(c_unblock_ack_q);
+ f_dec_and_log(c_fc_bvc_pcu);
+ f_dec_and_log(c_fc_bvc_ack_q);
+ f_dec_and_log(c_gmm_mo_att_req);
+ f_dec_and_log(c_gmm_mt_ac_req);
+ f_dec_and_log(c_gmm_mo_ac_resp);
+ f_dec_and_log(c_gmm_mt_att_acc);
+ f_dec_and_log(c_gmm_mt_det_req);
+ f_dec_and_log(c_gmm_mo_att_cpl);
+ }
+
+ control {
+ execute(TC_selftest());
+ }
+};
diff --git a/gprs_gb/gen_links.sh b/gprs_gb/gen_links.sh
new file mode 100755
index 0000000..e8aa176
--- /dev/null
+++ b/gprs_gb/gen_links.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+BASEDIR=~/projects/git
+
+gen_links() {
+ DIR=$1
+ FILES=$*
+ for f in $FILES; do
+ echo "Linking $f"
+ ln -sf $DIR/$f $f
+ done
+}
+
+#DIR=$BASEDIR/titan.TestPorts.UNIX_DOMAIN_SOCKETasp/src
+#FILES="UD_PT.cc UD_PT.hh UD_PortType.ttcn UD_Types.ttcn"
+#gen_links $DIR $FILES
+
+
+
+DIR=../library
+FILES="General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn"
+gen_links $DIR $FILES
diff --git a/gprs_gb/regen_makefile.sh b/gprs_gb/regen_makefile.sh
new file mode 100755
index 0000000..9a9abb8
--- /dev/null
+++ b/gprs_gb/regen_makefile.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+FILES="*.ttcn BSSGP_Helper.cc"
+
+ttcn3_makefilegen -f Test.ttcn $FILES
+sed -i -e 's/# TTCN3_DIR = /TTCN3_DIR = \/usr/' Makefile
+sed -i -e 's/LDFLAGS = /LDFLAGS = -L \/usr\/lib\/titan `pkg-config --libs libnetfilter_conntrack`/' Makefile
+sed -i -e 's/TTCN3_LIB = ttcn3-parallel/TTCN3_LIB = ttcn3/' Makefile
+sed -i -e 's/CPPFLAGS = -D$(PLATFORM) -I$(TTCN3_DIR)\/include/CPPFLAGS = -D$(PLATFORM) -I$(TTCN3_DIR)\/include -I\/usr\/include\/titan/' Makefile