From 3a1944091e0af9182b95b791c34c5d439b905e4b Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 22 Jul 2017 17:07:51 +0200 Subject: [PATCH] WIP: BSSGP related hepler functions; towards tests --- gprs_gb/BSSGP_Helper.cc | 96 ++++++++++ gprs_gb/BSSGP_Helper_Functions.ttcn | 3 + gprs_gb/BSSGP_Types.ttcn | 279 ++++++++++++++++++++++++++++ gprs_gb/Test.ttcn | 79 ++++++++ gprs_gb/gen_links.sh | 22 +++ gprs_gb/regen_makefile.sh | 9 + 6 files changed, 488 insertions(+) create mode 100644 gprs_gb/BSSGP_Helper.cc create mode 100644 gprs_gb/BSSGP_Helper_Functions.ttcn create mode 100644 gprs_gb/BSSGP_Types.ttcn create mode 100644 gprs_gb/Test.ttcn create mode 100755 gprs_gb/gen_links.sh create mode 100755 gprs_gb/regen_makefile.sh diff --git a/gprs_gb/BSSGP_Helper.cc b/gprs_gb/BSSGP_Helper.cc new file mode 100644 index 000000000..8cf97d2fd --- /dev/null +++ b/gprs_gb/BSSGP_Helper.cc @@ -0,0 +1,96 @@ + +#include "Octetstring.hh" +#include "Error.hh" +#include "Logger.hh" + +#include + +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 000000000..9c8af922e --- /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 000000000..94d9cd0f7 --- /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 000000000..88a78a88e --- /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 000000000..e8aa176a5 --- /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 000000000..9a9abb8d2 --- /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