From efabeac301ab93e2e2303e8be6aa5c0c64822a83 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Wed, 16 Nov 2016 10:38:39 +0100 Subject: [PATCH] ss7ops: Decode additional ISUP fields useful for analysis Decode additional fields and provide them in the JSON result. These include: * Detailed decode of called/calling party number * Hop counter * Calling partys category * Nature of Connection indicators * Forward call indicators * Transmission medium requirement * User service information --- modules/ss7ops/isup_parsed.c | 506 ++++++++++++++++++++++++++++++++++- 1 file changed, 503 insertions(+), 3 deletions(-) diff --git a/modules/ss7ops/isup_parsed.c b/modules/ss7ops/isup_parsed.c index df82cb4f730..dc3727ee30c 100644 --- a/modules/ss7ops/isup_parsed.c +++ b/modules/ss7ops/isup_parsed.c @@ -155,6 +155,251 @@ static const struct key_val event_info[] = { { 0, }, }; +static const struct key_val nai_vals[] = { + { 0x00, "spare" }, + { 0x01, "subscriber number (national use)" }, + { 0x02, "unknown (national use)" }, + { 0x03, "national (significant) number" }, + { 0x04, "international number" }, + { 0x05, "network-specific number (national use)" }, + { 0x06, "network routing number in national (significant) number format (national use)" }, + { 0x07, "network routing number in network-specific number format (national use)" }, + { 0x08, "network routing number concatenated with Called Directory Number (national use)" }, + { 0, }, +}; + +static const struct key_val inn_vals[] = { + { 0x00, "routing to internal network number allowed" }, + { 0x01, "routing to internal network number not allowed" }, + { 0, }, +}; + +static const struct key_val ni_vals[] = { + { 0x00, "complete" }, + { 0x01, "incomplete" }, + { 0, }, +}; + +static const struct key_val npi_vals[] = { + { 0x00, "spare" }, + { 0x01, "ISDN (Telephony) numbering plan (ITU-T Recommendation E.164)" }, + { 0x02, "spare" }, + { 0x03, "Data numbering plan (ITU-T Recommendation X.121) (national use)" }, + { 0x04, "Telex numbering plan (ITU-T Recommendation F.69) (national use)" }, + { 0x05, "reserved for national use" }, + { 0x06, "reserved for national use" }, + { 0, }, +}; + +static const struct key_val restrict_vals[] = { + { 0x00, "presentation allowed" }, + { 0x01, "presentation restricted" }, + { 0x02, "address not available (Note 1) (national use)" }, + { 0x03, "reserved for restriction by the network" }, + { 0, }, +}; + +static const struct key_val screened_vals[] = { + { 0x00, "reserved (Note 2)" }, + { 0x01, "user provided, verified and passed" }, + { 0x02, "reserved (Note 2)" }, + { 0x03, "network provided" }, + { 0, }, +}; + +static const struct key_val calling_cat_vals[] = { + { 0x00, "calling party's category unknown at this time (national use)" }, + { 0x01, "operator, language French" }, + { 0x02, "operator, language English" }, + { 0x03, "operator, language German" }, + { 0x04, "operator, language Russian" }, + { 0x05, "operator, language Spanish" }, + { 0x09, "reserved (see ITU-T Recommendation Q.104) (Note) (national use)" }, + { 0x0A, "ordinary calling subscriber" }, + { 0x0B, "calling subscriber with priority" }, + { 0x0C, "data call (voice band data)" }, + { 0x0D, "test call" }, + { 0x0E, "spare" }, + { 0x0F, "payphone" }, + { 0, }, +}; + +static const struct key_val nci_sat_vals[] = { + { 0x00, "no satellite circuit in the connection" }, + { 0x01, "one satellite circuit in the connection" }, + { 0x02, "two satellite circuits in the connection" }, + { 0x03, "spare" }, + { 0, }, +}; + +static const struct key_val nci_con_vals[] = { + { 0x00, "continuity check not required" }, + { 0x01, "continuity check required on this circuit" }, + { 0x02, "continuity check performed on a previous circuit" }, + { 0x03, "spare" }, + { 0, }, +}; + +static const struct key_val nci_echo_vals[] = { + { 0x00, "outgoing echo control device not included" }, + { 0x01, "outgoing echo control device included" }, + { 0, }, +}; + +/* Forward call indicators National/international call indicator */ +static const struct key_val fwc_nic_vals[] = { + { 0x00, "call to be treated as a national call" }, + { 0x01, "call to be treated as an international call" }, + { 0, }, +}; + +/* End-to-end method indicator */ +static const struct key_val fwc_etem_vals[] = { + { 0x00, "no end-to-end method available (only link-by-link method available)" }, + { 0x01, "pass-along method available (national use)" }, + { 0x02, "SCCP method available" }, + { 0x03, "pass-along and SCCP methods available (national use)" }, + { 0, }, +}; + +/* Interworking indicator */ +static const struct key_val fwc_iw_vals[] = { + { 0x00, "no interworking encountered (No. 7 signalling all the way)" }, + { 0x01, "interworking encountered" }, + { 0, }, +}; + +/* End-to-end information indicator */ +static const struct key_val fwc_etei_vals[] = { + { 0x00, "no end-to-end information available" }, + { 0x01, "end-to-end information available" }, + { 0, }, +}; + +/* ISDN user part indicator */ +static const struct key_val fwc_isup_vals[] = { + { 0x00, "ISDN user part not used all the way" }, + { 0x01, "ISDN user part used all the way" }, + { 0, }, +}; + +/* ISDN user part preference indicator */ +static const struct key_val fwc_isup_pref_vals[] = { + { 0x00, "ISDN user part preferred all the way" }, + { 0x01, "ISDN user part not required all the way" }, + { 0x02, "ISDN user part required all the way" }, + { 0x03, "spare" }, + { 0, }, +}; + +/* ISDN access indicator */ +static const struct key_val fwc_ia_vals[] = { + { 0x00, "originating access non-ISDN" }, + { 0x01, "originating access ISD" }, + { 0, }, +}; + +/* SCCP method indicator */ +static const struct key_val fwc_sccpm_vals[] = { + { 0x00, "no indication" }, + { 0x01, "connectionless method available (national use)" }, + { 0x02, "connection oriented method available" }, + { 0x03, "connectionless and connection oriented methods available (national use)" }, + { 0, }, +}; + +/* Transmission medium */ +static const struct key_val trans_medium_vals[] = { + { 0x00, "speech" }, + { 0x01, "spare" }, + { 0x02, "64 kbit/s unrestricted" }, + { 0x03, "3.1 kHz audio" }, + { 0x04, "reserved for alternate speech (service 2)/64 kbit/s unrestricted (service 1)" }, + { 0x05, "reserved for alternate 64 kbit/s unrestricted (service 1)/speech (service 2)" }, + { 0x06, "64 kbit/s preferred" }, + { 0x07, "2 × 64 kbit/s unrestricted" }, + { 0x08, "384 kbit/s unrestricted" }, + { 0x09, "1536 kbit/s unrestricted" }, + { 0x0A, "1920 kbit/s unrestricted" }, + { 0x10, "3 × 64 kbit/s unrestricted" }, + { 0x11, "4 × 64 kbit/s unrestricted" }, + { 0x12, "5 × 64 kbit/s unrestricted" }, + { 0x13, "spare" }, + { 0x14, "7 × 64 kbit/s unrestricted" }, + { 0x15, "8 × 64 kbit/s unrestricted" }, + { 0x16, "9 × 64 kbit/s unrestricted" }, + { 0x17, "10 × 64 kbit/s unrestricted" }, + { 0x18, "11 × 64 kbit/s unrestricted" }, + { 0x19, "12 × 64 kbit/s unrestricted" }, + { 0x1A, "13 × 64 kbit/s unrestricted" }, + { 0x1B, "14 × 64 kbit/s unrestricted" }, + { 0x1C, "15 × 64 kbit/s unrestricted" }, + { 0x1D, "16 × 64 kbit/s unrestricted" }, + { 0x1E, "17 × 64 kbit/s unrestricted" }, + { 0x1F, "18 × 64 kbit/s unrestricted" }, + { 0x20, "19 × 64 kbit/s unrestricted" }, + { 0x21, "20 × 64 kbit/s unrestricted" }, + { 0x22, "21 × 64 kbit/s unrestricted" }, + { 0x23, "22 × 64 kbit/s unrestricted" }, + { 0x24, "23 × 64 kbit/s unrestricted" }, + { 0x25, "spare" }, + { 0x26, "25 × 64 kbit/s unrestricted" }, + { 0x27, "26 × 64 kbit/s unrestricted" }, + { 0x28, "27 × 64 kbit/s unrestricted" }, + { 0x29, "28 × 64 kbit/s unrestricted" }, + { 0x2A, "29 × 64 kbit/s unrestricted" }, + { 0, }, +}; + +/* Q931 bearer capabilities */ +static const struct key_val q931_cstd_vals[] = { + { 0x00, "ITU-T standardized coding as described below" }, + { 0x01, "ISO/IEC Standard" }, + { 0x02, "National standard" }, + { 0x03, "Standard defined for the network (either public or private) present on the network side of the interface" }, + { 0, }, +}; + +static const struct key_val q931_trs_cap_vals[] = { + { 0x00, "Speech" }, + { 0x08, "Unrestricted digital information" }, + { 0x09, "Restricted digital information" }, + { 0x10, "3.1 kHz audio" }, + { 0x11, "Unrestricted digital information with tones/announcements (Note 2)" }, + { 0x18, "Video" }, + { 0, }, +}; + +static const struct key_val q931_trs_mde_vals[] = { + { 0x00, "Circuit mode" }, + { 0x01, "Packet mode" }, + { 0, }, +}; + +static const struct key_val q931_trs_rte_vals[] = { + { 0x00, "packet-mode calls" }, + { 0x10, "64 kbit/s" }, + { 0x11, "2 × 64 kbit/s" }, + { 0x13, "384 kbit/s" }, + { 0x15, "1536 kbit/s" }, + { 0x17, "1920 kbit/s" }, + { 0x18, "Multirate (64 kbit/s base rate)" }, + { 0, }, +}; + +static const struct key_val q931_usr_info_vals[] = { + { 0x00, "ITU-T standardized rate adaption V.110, I.460 and X.30. This implies the presence of octet 5a and optionally octets 5b, 5c and 5d as defined below" }, + { 0x02, "G.711 u-law" }, + { 0x03, "G.711 a-law" }, + { 0x04, "G.721 ADPCM" }, + { 0x05, "H.221 and H.242" }, + { 0x06, "H.223 and H.245" }, + { 0x07, "Non-ITU-T standardized rate adaption" }, + { 0x08, "V.120" }, + { 0x09, "X.31" }, + { 0, }, +}; + static const char *lookup(const struct key_val *table, const uint8_t val, const char *deflt) { while (1) { @@ -190,7 +435,7 @@ static inline void decode_bcd(char *dest, const uint8_t *data, size_t len, int o *dest = '\0'; } -static void append_e164(srjson_doc_t *doc, const char *name, const uint8_t *data, uint8_t len) +static void append_e164(srjson_doc_t *doc, const char *name, const uint8_t *data, uint8_t len, const uint8_t type) { char num[17] = { 0, }; srjson_t *obj; @@ -215,8 +460,30 @@ static void append_e164(srjson_doc_t *doc, const char *name, const uint8_t *data } odd = !!(data[0] & 0x80); + + if (type == ISUPCalledPartyNumber) { + uint8_t inn = data[1] >> 7; + srjson_AddNumberToObject(doc, obj, "inn", inn); + srjson_AddStringToObject(doc, obj, "inn_name", lookup(inn_vals, inn, "Unknown")); + } else { + uint8_t ni = data[1] >> 7; + uint8_t ap = (data[1] & 0x0C) >> 2; + uint8_t si = (data[1] & 0x03); + + srjson_AddNumberToObject(doc, obj, "ni", ni); + srjson_AddStringToObject(doc, obj, "ni_name", lookup(ni_vals, ni, "Unknown")); + + srjson_AddNumberToObject(doc, obj, "restrict", ap); + srjson_AddStringToObject(doc, obj, "restrict_name", lookup(restrict_vals, ap, "Unknown")); + + srjson_AddNumberToObject(doc, obj, "screened", si); + srjson_AddStringToObject(doc, obj, "screened_name", lookup(screened_vals, si, "Unknown")); + } + srjson_AddNumberToObject(doc, obj, "ton", data[0] & 0x7F); + srjson_AddStringToObject(doc, obj, "ton_name", lookup(nai_vals, data[0] & 0x7F, "Unknown")); srjson_AddNumberToObject(doc, obj, "npi", (data[1] >> 4) & 0x07); + srjson_AddStringToObject(doc, obj, "npi_name", lookup(npi_vals, (data[1] >> 4) & 0x07, "Unknown")); decode_bcd(num, &data[2], len - 2, odd); srjson_AddStringToObject(doc, obj, "num", num); @@ -302,14 +569,232 @@ static void append_event_information(srjson_doc_t *doc, const char *name, const srjson_AddItemToObject(doc, doc->root, name, obj); } +static void append_hop_counter(srjson_doc_t *doc, const char *name, const uint8_t *data, uint8_t len) +{ + uint8_t hop; + + if (len < 1) { + LM_ERR("Not enough data for hop counter\n"); + return; + } + + memcpy(&hop, data, 1); + srjson_AddNumberToObject(doc, doc->root, name, hop); +} + +static void append_calling_party_category(srjson_doc_t *doc, const uint8_t *data, uint8_t len) +{ + srjson_t *obj; + uint8_t cat; + + if (len < 1) { + LM_ERR("Not enough data for transport medium requirement\n"); + return; + } + + obj = srjson_CreateObject(doc); + if (!obj) { + LM_ERR("Can not allocate json object for transport medium requirement\n"); + return; + } + + memcpy(&cat, data, 1); + srjson_AddNumberToObject(doc, obj, "num", cat); + srjson_AddStringToObject(doc, obj, "name", lookup(calling_cat_vals, cat, "Unknown")); + + srjson_AddItemToObject(doc, doc->root, "calling_party", obj); +} + +static void append_nci(srjson_doc_t *doc, const char *name, const uint8_t *data, uint8_t len) +{ + uint8_t sat, con, ech; + srjson_t *obj; + + if (len != 1) { + LM_ERR("Unpexected size(%u) for nature of connection indicators\n", len); + return; + } + + obj = srjson_CreateObject(doc); + if (!obj) { + LM_ERR("Can not allocate json object for %s\n", name); + return; + } + + sat = data[0] & 0x03; + con = (data[0] & 0x0C) >> 2; + ech = (data[0] & 0x10) >> 4; + + srjson_AddNumberToObject(doc, obj, "satellite", sat); + srjson_AddStringToObject(doc, obj, "satellite_name", lookup(nci_sat_vals, sat, "Unknown")); + srjson_AddNumberToObject(doc, obj, "continuity_check", con); + srjson_AddStringToObject(doc, obj, "continuity_check_name", lookup(nci_con_vals, sat, "Unknown")); + srjson_AddNumberToObject(doc, obj, "echo_device", ech); + srjson_AddStringToObject(doc, obj, "echo_device_name", lookup(nci_echo_vals, ech, "Unknown")); + + srjson_AddItemToObject(doc, doc->root, name, obj); +} + +static void append_forward_call(srjson_doc_t *doc, const char *name, const uint8_t *data, uint8_t len) +{ + uint16_t val; + srjson_t *obj; + size_t i; + uint8_t off = 0; + + static const struct bit_masks { + uint8_t num_bits; + const struct key_val *vals; + const char *name; + const char *bit_names; + } bits[] = { + { 1, fwc_nic_vals, "national_international_call", "A" }, + { 2, fwc_etem_vals, "end_to_end_method", "CB" }, + { 1, fwc_iw_vals, "interworking", "D" }, + { 1, fwc_etei_vals, "end_to_end_information", "E" }, + { 1, fwc_isup_vals, "isup", "F" }, + { 2, fwc_isup_pref_vals, "isup_preference", "HG" }, + { 1, fwc_ia_vals, "isdn_access", "I" }, + { 2, fwc_sccpm_vals, "sccp_method", "KJ" }, + }; + + if (len != 2) { + LM_ERR("Unpexected size(%u) for forward call indicators\n", len); + return; + } + + obj = srjson_CreateObject(doc); + if (!obj) { + LM_ERR("Can not allocate json object for %s\n", name); + return; + } + + memcpy(&val, data, sizeof(val)); + + for (i = 0; i < (sizeof(bits)/sizeof(bits[0])); ++i) { + char buf[128]; + const struct bit_masks *mask_info = &bits[i]; + uint8_t mask = 0, tmp; + int b; + + /* build a mask */ + for (b = 0; b < mask_info->num_bits; ++b) + mask = (mask << 1) | 0x01; + + snprintf(buf, sizeof(buf), "%s_name", mask_info->name); + + tmp = (val >> off) & mask; + srjson_AddNumberToObject(doc, obj, mask_info->name, tmp); + srjson_AddStringToObject(doc, obj, buf, lookup(mask_info->vals, tmp, mask_info->bit_names)); + off += mask_info->num_bits; + } + + srjson_AddItemToObject(doc, doc->root, name, obj); +} + +static void append_transmission_medium(srjson_doc_t *doc, const uint8_t *data, const uint8_t len) +{ + srjson_t *obj; + + if (len != 1) { + LM_ERR("Unpexected size(%u)\n", len); + return; + } + + obj = srjson_CreateObject(doc); + if (!obj) { + LM_ERR("Can not allocate json object\n"); + return; + } + + srjson_AddNumberToObject(doc, obj, "num", data[0]); + srjson_AddStringToObject(doc, obj, "name", lookup(trans_medium_vals, data[0], "Unknown")); + + srjson_AddItemToObject(doc, doc->root, "transmission_medium", obj); +} + +/* + * Decode the Q.931 bearer capabilities now. At least three octets + * but for some channels it will be four. Also only ITU caps will + * be parsed. + */ +static void append_user_information(srjson_doc_t *doc, const uint8_t *data, const uint8_t len) +{ + srjson_t *obj; + uint8_t coding_standard, transfer_capability; + uint8_t transfer_mode, transfer_rate; + uint8_t layer1_ident, layer1_protocol; + uint8_t octet5; + int rate_multiplier = -1; + + if (len < 3) { + LM_ERR("Insufficient size(%u)\n", len); + return; + } + + coding_standard = (data[0] & 0x60) >> 5; + transfer_capability = data[0] & 0x1F; + transfer_mode = (data[1] & 0x60) >> 5; + transfer_rate = data[1] & 0x1F; + + if (transfer_rate == 0x18) { + if (len < 4) { + LM_ERR("Insufficient size(%u) for multirate\n", len); + return; + } + rate_multiplier = data[2] & 0x7F; + octet5 = data[3]; + } else + octet5 = data[2]; + + layer1_ident = (octet5 & 0x60) >> 5; + layer1_protocol = octet5 & 0x1F; + + obj = srjson_CreateObject(doc); + if (!obj) { + LM_ERR("Can not allocate json object\n"); + return; + } + + /* numbers first and maybe names if it is ITU */ + srjson_AddStringToObject(doc, obj, "coding_standard_name", lookup(q931_cstd_vals, coding_standard, "Unknown")); + srjson_AddNumberToObject(doc, obj, "coding_standard", coding_standard); + srjson_AddNumberToObject(doc, obj, "transfer_capability", transfer_capability); + + srjson_AddNumberToObject(doc, obj, "transfer_mode", transfer_mode); + srjson_AddNumberToObject(doc, obj, "transfer_rate", transfer_rate); + if (rate_multiplier >= 0) + srjson_AddNumberToObject(doc, obj, "rate_multiplier", rate_multiplier); + srjson_AddNumberToObject(doc, obj, "layer1_ident", layer1_ident); + srjson_AddNumberToObject(doc, obj, "layer1_protocol", layer1_protocol); + + + /* ITU-T coding values */ + if (coding_standard == 0x00) { + srjson_AddStringToObject(doc, obj, "transfer_capability_name", + lookup(q931_trs_cap_vals, transfer_capability, "Unknown")); + srjson_AddStringToObject(doc, obj, "transfer_mode_name", + lookup(q931_trs_mde_vals, transfer_mode, "Unknown")); + srjson_AddStringToObject(doc, obj, "transfer_rate_name", + lookup(q931_trs_rte_vals, transfer_rate, "Unknown")); + srjson_AddStringToObject(doc, obj, "layer1_protocol_name", + lookup(q931_usr_info_vals, layer1_protocol, "Unknown")); + } + + srjson_AddItemToObject(doc, doc->root, "user_information", obj); +} + static void isup_visitor(uint8_t type, const uint8_t *data, uint8_t len, struct isup_state *ptrs) { switch (type) { case ISUPCalledPartyNumber: - append_e164(ptrs->json, "called_number", data, len); + append_e164(ptrs->json, "called_number", data, len, ISUPCalledPartyNumber); break; case ISUPCallingPartyNumber: - append_e164(ptrs->json, "calling_number", data, len); + append_e164(ptrs->json, "calling_number", data, len, ISUPCallingPartyNumber); + break; + case ISUPCallingPartysCategory: + append_calling_party_category(ptrs->json, data, len); break; case ISUPCauseIndicators: append_cause(ptrs->json, "cause", data, len); @@ -317,6 +802,21 @@ static void isup_visitor(uint8_t type, const uint8_t *data, uint8_t len, struct case ISUPEventInformation: append_event_information(ptrs->json, "event", data, len); break; + case ISUPHopCounter: + append_hop_counter(ptrs->json, "hop_counter", data, len); + break; + case ISUPNatureOfConnectionIndicators: + append_nci(ptrs->json, "nature_of_connnection", data, len); + break; + case ISUPForwardCallIndicators: + append_forward_call(ptrs->json, "forward_call", data, len); + break; + case ISUPTransmissionMediumRequirement: + append_transmission_medium(ptrs->json, data, len); + break; + case ISUPUserServiceInformation: + append_user_information(ptrs->json, data, len); + break; } }