2
0
Эх сурвалжийг харах

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
Holger Hans Peter Freyther 8 жил өмнө
parent
commit
efabeac301

+ 503 - 3
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;
 	}
 }