Try to determine MTP3 Standard heuristically, original code
from Jeff Morris refactored a bit by me. svn path=/trunk/; revision=40776
This commit is contained in:
parent
0ee76da516
commit
ed5c6cfd71
|
@ -49,6 +49,7 @@
|
|||
#include <epan/prefs.h>
|
||||
#include <epan/emem.h>
|
||||
#include "packet-q708.h"
|
||||
#include "packet-sccp.h"
|
||||
|
||||
/* Initialize the protocol and registered fields */
|
||||
static int proto_mtp3 = -1;
|
||||
|
@ -120,6 +121,7 @@ gint mtp3_standard = ITU_STANDARD;
|
|||
static gboolean mtp3_use_ansi_5_bit_sls = FALSE;
|
||||
static gboolean mtp3_use_japan_5_bit_sls = FALSE;
|
||||
static gboolean mtp3_show_itu_priority = FALSE;
|
||||
static gboolean mtp3_heuristic_standard = FALSE;
|
||||
static gint mtp3_addr_fmt = MTP3_ADDR_FMT_DASHED;
|
||||
static mtp3_addr_pc_t* mtp3_addr_dpc;
|
||||
static mtp3_addr_pc_t* mtp3_addr_opc;
|
||||
|
@ -644,69 +646,127 @@ dissect_mtp3_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|||
call_dissector(data_handle, payload_tvb, pinfo, tree);
|
||||
}
|
||||
|
||||
#define HEURISTIC_FAILED_STANDARD 0xffff
|
||||
static guint
|
||||
heur_mtp3_standard(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint8 si)
|
||||
{
|
||||
|
||||
guint32 len;
|
||||
tvbuff_t *payload;
|
||||
|
||||
len = tvb_length(tvb);
|
||||
switch (si) {
|
||||
case 3:
|
||||
{
|
||||
payload = tvb_new_subset(tvb, ITU_HEADER_LENGTH, len-ITU_HEADER_LENGTH, len-ITU_HEADER_LENGTH);
|
||||
if (looks_like_valid_sccp(payload, ITU_STANDARD)) {
|
||||
return ITU_STANDARD;
|
||||
}
|
||||
payload = tvb_new_subset(tvb, ANSI_HEADER_LENGTH, len-ANSI_HEADER_LENGTH, len-ANSI_HEADER_LENGTH);
|
||||
if (looks_like_valid_sccp(payload, ANSI_STANDARD)) {
|
||||
return ANSI_STANDARD;
|
||||
}
|
||||
payload = tvb_new_subset(tvb, ANSI_HEADER_LENGTH, len-ANSI_HEADER_LENGTH, len-ANSI_HEADER_LENGTH);
|
||||
if (looks_like_valid_sccp(payload, CHINESE_ITU_STANDARD)) {
|
||||
return CHINESE_ITU_STANDARD;
|
||||
}
|
||||
payload = tvb_new_subset(tvb, JAPAN_HEADER_LENGTH, len-JAPAN_HEADER_LENGTH, len-JAPAN_HEADER_LENGTH);
|
||||
if (looks_like_valid_sccp(payload, JAPAN_STANDARD)) {
|
||||
return JAPAN_STANDARD;
|
||||
}
|
||||
|
||||
return HEURISTIC_FAILED_STANDARD;
|
||||
|
||||
}
|
||||
default:
|
||||
return HEURISTIC_FAILED_STANDARD;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static const value_string mtp3_standard_vals[] = {
|
||||
{ ITU_STANDARD, "ITU_STANDARD" },
|
||||
{ ANSI_STANDARD, "ANSI_STANDARD" },
|
||||
{ CHINESE_ITU_STANDARD, "CHINESE_ITU_STANDARD" },
|
||||
{ JAPAN_STANDARD, "JAPAN_STANDARD" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/* Code to actually dissect the packets */
|
||||
static void
|
||||
dissect_mtp3(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
||||
{
|
||||
void* pd_save;
|
||||
mtp3_tap_rec_t* tap_rec = ep_alloc0(sizeof(mtp3_tap_rec_t));
|
||||
void* pd_save;
|
||||
mtp3_tap_rec_t* tap_rec = ep_alloc0(sizeof(mtp3_tap_rec_t));
|
||||
guint heuristic_standard, pref_mtp3_standard;
|
||||
guint8 si;
|
||||
|
||||
/* Set up structures needed to add the protocol subtree and manage it */
|
||||
proto_item *mtp3_item = NULL;
|
||||
proto_tree *mtp3_tree = NULL;
|
||||
/* Set up structures needed to add the protocol subtree and manage it */
|
||||
proto_item *mtp3_item = NULL, *gen_item;
|
||||
proto_tree *mtp3_tree = NULL;
|
||||
|
||||
/* Make entries in Protocol column on summary display */
|
||||
switch(mtp3_standard) {
|
||||
pref_mtp3_standard = mtp3_standard;
|
||||
|
||||
mtp3_item = proto_tree_add_item(tree, proto_mtp3, tvb, 0, 0, ENC_NA);
|
||||
|
||||
si = tvb_get_guint8(tvb, SIO_OFFSET) & SERVICE_INDICATOR_MASK;
|
||||
if (mtp3_heuristic_standard) {
|
||||
heuristic_standard = heur_mtp3_standard(tvb, pinfo, tree, si);
|
||||
if(heuristic_standard==HEURISTIC_FAILED_STANDARD){
|
||||
gen_item = proto_tree_add_text(tree, tvb, 0, 0, "Could not determine Heuristic using %s", val_to_str(mtp3_standard, mtp3_standard_vals, "unknown"));
|
||||
}else{
|
||||
gen_item = proto_tree_add_text(tree, tvb, 0, 0, "%s", val_to_str(heuristic_standard, mtp3_standard_vals, "unknown"));
|
||||
mtp3_standard = heuristic_standard;
|
||||
}
|
||||
PROTO_ITEM_SET_GENERATED(gen_item);
|
||||
}
|
||||
|
||||
/* Make entries in Protocol column on summary display */
|
||||
switch(mtp3_standard) {
|
||||
case ITU_STANDARD:
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "MTP3 (Int. ITU)");
|
||||
break;
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "MTP3 (Int. ITU)");
|
||||
proto_item_set_len(mtp3_item, ITU_HEADER_LENGTH);
|
||||
break;
|
||||
case ANSI_STANDARD:
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "MTP3 (ANSI)");
|
||||
break;
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "MTP3 (ANSI)");
|
||||
proto_item_set_len(mtp3_item, ANSI_HEADER_LENGTH);
|
||||
break;
|
||||
case CHINESE_ITU_STANDARD:
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "MTP3 (Chin. ITU)");
|
||||
break;
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "MTP3 (Chin. ITU)");
|
||||
proto_item_set_len(mtp3_item, ANSI_HEADER_LENGTH);
|
||||
break;
|
||||
case JAPAN_STANDARD:
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "MTP3 (Japan)");
|
||||
break;
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, "MTP3 (Japan)");
|
||||
proto_item_set_len(mtp3_item, JAPAN_HEADER_LENGTH);
|
||||
break;
|
||||
};
|
||||
|
||||
if (tree) {
|
||||
/* create display subtree for the protocol */
|
||||
switch (mtp3_standard) {
|
||||
case ITU_STANDARD:
|
||||
mtp3_item = proto_tree_add_item(tree, proto_mtp3, tvb, 0, ITU_HEADER_LENGTH, ENC_NA);
|
||||
break;
|
||||
case ANSI_STANDARD:
|
||||
case CHINESE_ITU_STANDARD:
|
||||
mtp3_item = proto_tree_add_item(tree, proto_mtp3, tvb, 0, ANSI_HEADER_LENGTH, ENC_NA);
|
||||
break;
|
||||
case JAPAN_STANDARD:
|
||||
mtp3_item = proto_tree_add_item(tree, proto_mtp3, tvb, 0, JAPAN_HEADER_LENGTH, ENC_NA);
|
||||
break;
|
||||
if (tree) {
|
||||
/* create display subtree for the protocol */
|
||||
mtp3_tree = proto_item_add_subtree(mtp3_item, ett_mtp3);
|
||||
}
|
||||
mtp3_tree = proto_item_add_subtree(mtp3_item, ett_mtp3);
|
||||
|
||||
}
|
||||
mtp3_addr_opc = ep_alloc0(sizeof(mtp3_addr_pc_t));
|
||||
mtp3_addr_dpc = ep_alloc0(sizeof(mtp3_addr_pc_t));
|
||||
|
||||
mtp3_addr_opc = ep_alloc0(sizeof(mtp3_addr_pc_t));
|
||||
mtp3_addr_dpc = ep_alloc0(sizeof(mtp3_addr_pc_t));
|
||||
/* Dissect the packet (even if !tree so can call sub-dissectors and update
|
||||
* the source and destination address columns) */
|
||||
dissect_mtp3_sio(tvb, pinfo, mtp3_tree, &pd_save);
|
||||
dissect_mtp3_routing_label(tvb, pinfo, mtp3_tree);
|
||||
|
||||
/* Dissect the packet (even if !tree so can call sub-dissectors and update
|
||||
* the source and destination address columns) */
|
||||
dissect_mtp3_sio(tvb, pinfo, mtp3_tree, &pd_save);
|
||||
dissect_mtp3_routing_label(tvb, pinfo, mtp3_tree);
|
||||
memcpy(&(tap_rec->addr_opc),mtp3_addr_opc,sizeof(mtp3_addr_pc_t));
|
||||
memcpy(&(tap_rec->addr_dpc),mtp3_addr_dpc,sizeof(mtp3_addr_pc_t));
|
||||
|
||||
memcpy(&(tap_rec->addr_opc),mtp3_addr_opc,sizeof(mtp3_addr_pc_t));
|
||||
memcpy(&(tap_rec->addr_dpc),mtp3_addr_dpc,sizeof(mtp3_addr_pc_t));
|
||||
tap_rec->si_code = (tvb_get_guint8(tvb, SIO_OFFSET) & SERVICE_INDICATOR_MASK);
|
||||
tap_rec->size = tvb_length(tvb);
|
||||
|
||||
tap_rec->si_code = (tvb_get_guint8(tvb, SIO_OFFSET) & SERVICE_INDICATOR_MASK);
|
||||
tap_rec->size = tvb_length(tvb);
|
||||
tap_queue_packet(mtp3_tap, pinfo, tap_rec);
|
||||
|
||||
tap_queue_packet(mtp3_tap, pinfo, tap_rec);
|
||||
dissect_mtp3_payload(tvb, pinfo, tree);
|
||||
pinfo->private_data = pd_save;
|
||||
|
||||
mtp3_standard = pref_mtp3_standard;
|
||||
|
||||
dissect_mtp3_payload(tvb, pinfo, tree);
|
||||
pinfo->private_data = pd_save;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -806,6 +866,11 @@ proto_register_mtp3(void)
|
|||
|
||||
mtp3_module = prefs_register_protocol(proto_mtp3, NULL);
|
||||
|
||||
prefs_register_bool_preference(mtp3_module, "heuristic_standard",
|
||||
"Try to determine the MTP3 standard heuristically",
|
||||
"This only works for SCCP traffic for now",
|
||||
&mtp3_heuristic_standard);
|
||||
|
||||
prefs_register_enum_preference(mtp3_module, "standard", "MTP3 standard",
|
||||
"The SS7 standard used in MTP3 packets",
|
||||
&mtp3_standard, mtp3_options, FALSE);
|
||||
|
|
|
@ -818,6 +818,171 @@ static const value_string assoc_protos[] = {
|
|||
{ 0, NULL }
|
||||
};
|
||||
|
||||
gboolean
|
||||
sccp_called_calling_looks_valid(tvbuff_t *tvb, guint8 my_mtp3_standard)
|
||||
{
|
||||
guint8 ai, ri, gti, ssni, pci;
|
||||
|
||||
/* TVB starts with parameter length */
|
||||
ai = tvb_get_guint8(tvb, 1);
|
||||
if (my_mtp3_standard == ANSI_STANDARD && (ai & ANSI_NATIONAL_MASK) == 0)
|
||||
return FALSE;
|
||||
|
||||
gti = (ai & GTI_MASK) >> GTI_SHIFT;
|
||||
if (my_mtp3_standard == ANSI_STANDARD) {
|
||||
if (gti > 2)
|
||||
return FALSE;
|
||||
} else {
|
||||
if (gti > 4)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ri = ai & ROUTING_INDICATOR_MASK >> ROUTING_INDICATOR_SHIFT;
|
||||
if (my_mtp3_standard == ANSI_STANDARD) {
|
||||
pci = ai & ANSI_PC_INDICATOR_MASK;
|
||||
ssni = ai & ANSI_SSN_INDICATOR_MASK;
|
||||
} else {
|
||||
ssni = ai & ITU_SSN_INDICATOR_MASK;
|
||||
pci = ai & ITU_PC_INDICATOR_MASK;
|
||||
}
|
||||
|
||||
/* Route on SSN with no SSN? */
|
||||
if (ri == ROUTE_ON_SSN && ssni == 0)
|
||||
return FALSE;
|
||||
/* Route on GT with no GT? */
|
||||
if (ri != ROUTE_ON_SSN && gti == AI_GTI_NO_GT)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
looks_like_valid_sccp(tvbuff_t *tvb, guint8 my_mtp3_standard)
|
||||
{
|
||||
guint8 msgtype, msg_class, cause, offset;
|
||||
guint16 called_ptr = 0;
|
||||
guint16 calling_ptr = 0;
|
||||
guint16 data_ptr = 0;
|
||||
guint16 opt_ptr = 0;
|
||||
guint32 len = tvb_length(tvb);
|
||||
|
||||
/* Ensure we can do some basic checks without throwing an exception.
|
||||
* Accesses beyond this length need to check the length first because
|
||||
* we don't want to throw an exception in here...
|
||||
*/
|
||||
if (len < 6)
|
||||
return FALSE;
|
||||
|
||||
msgtype = tvb_get_guint8(tvb, SCCP_MSG_TYPE_OFFSET);
|
||||
if (!match_strval(msgtype, sccp_message_type_acro_values)) {
|
||||
return FALSE;
|
||||
}
|
||||
offset = SCCP_MSG_TYPE_LENGTH;
|
||||
|
||||
/*
|
||||
Still to be done:
|
||||
SCCP_MSG_TYPE_RLSD
|
||||
SCCP_MSG_TYPE_RLC
|
||||
SCCP_MSG_TYPE_DT1
|
||||
SCCP_MSG_TYPE_DT2
|
||||
SCCP_MSG_TYPE_AK
|
||||
SCCP_MSG_TYPE_UDTS
|
||||
SCCP_MSG_TYPE_ED
|
||||
SCCP_MSG_TYPE_EA
|
||||
SCCP_MSG_TYPE_RSR
|
||||
SCCP_MSG_TYPE_RSC
|
||||
SCCP_MSG_TYPE_ERR
|
||||
SCCP_MSG_TYPE_IT
|
||||
SCCP_MSG_TYPE_XUDTS
|
||||
SCCP_MSG_TYPE_LUDT
|
||||
SCCP_MSG_TYPE_LUDTS
|
||||
*/
|
||||
|
||||
switch (msgtype) {
|
||||
case SCCP_MSG_TYPE_DT1: /* 6 */
|
||||
if(len<8){
|
||||
/* Mandatory parameter(data)+ at least one data */
|
||||
return FALSE;
|
||||
}
|
||||
data_ptr = tvb_get_guint8(tvb, offset+DESTINATION_LOCAL_REFERENCE_LENGTH+1);
|
||||
if(tvb_get_guint8(tvb, data_ptr) > len){
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case SCCP_MSG_TYPE_DT2: /* 7 */
|
||||
g_warning("Unhandled msg type %u", msgtype);
|
||||
return FALSE;
|
||||
break;
|
||||
case SCCP_MSG_TYPE_UDT:
|
||||
case SCCP_MSG_TYPE_XUDT: /* 0x11 */
|
||||
{
|
||||
/* Class lower four bits */
|
||||
msg_class = tvb_get_guint8(tvb, offset)&0x0f;
|
||||
if (msg_class > 1)
|
||||
return FALSE;
|
||||
offset += PROTOCOL_CLASS_LENGTH;
|
||||
|
||||
if (msgtype == SCCP_MSG_TYPE_XUDT)
|
||||
offset += HOP_COUNTER_LENGTH;
|
||||
|
||||
called_ptr = tvb_get_guint8(tvb, offset) + offset;
|
||||
calling_ptr = tvb_get_guint8(tvb, offset+1) + offset+1;
|
||||
data_ptr = tvb_get_guint8(tvb, offset+2) + offset+2;
|
||||
if (msgtype == SCCP_MSG_TYPE_XUDT)
|
||||
opt_ptr = tvb_get_guint8(tvb, offset+3) + offset+3;
|
||||
|
||||
/* Check that all the pointers are within bounds */
|
||||
if (called_ptr > len || calling_ptr > len || data_ptr > len || opt_ptr > len)
|
||||
return FALSE;
|
||||
|
||||
/* Check that the lengths of the variable parameters are within bounds */
|
||||
if (tvb_get_guint8(tvb, called_ptr) > len ||
|
||||
tvb_get_guint8(tvb, calling_ptr) > len ||
|
||||
tvb_get_guint8(tvb, data_ptr) > len)
|
||||
return FALSE;
|
||||
if (msgtype == SCCP_MSG_TYPE_XUDT && tvb_get_guint8(tvb, opt_ptr) > len)
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case SCCP_MSG_TYPE_CR:
|
||||
{
|
||||
/* Class lower four bits */
|
||||
msg_class = tvb_get_guint8(tvb, SCCP_MSG_TYPE_LENGTH+3) & 0x0f;
|
||||
if (msg_class != 2)
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case SCCP_MSG_TYPE_CC: /* 2 */
|
||||
{
|
||||
/* Class lower four bits */
|
||||
msg_class = tvb_get_guint8(tvb, SCCP_MSG_TYPE_LENGTH+6)&0x0f;
|
||||
if (msg_class != 2)
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case SCCP_MSG_TYPE_CREF:
|
||||
{
|
||||
cause = tvb_get_guint8(tvb, SCCP_MSG_TYPE_LENGTH+3);
|
||||
if (!match_strval(cause, sccp_release_cause_values))
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_warning("Unhandled msg type %u", msgtype);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (called_ptr) {
|
||||
guint8 param_len = tvb_get_guint8(tvb, called_ptr);
|
||||
tvbuff_t *param_tvb = tvb_new_subset(tvb, called_ptr, param_len, param_len);
|
||||
|
||||
if (!sccp_called_calling_looks_valid(param_tvb, my_mtp3_standard))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static sccp_assoc_info_t *
|
||||
new_assoc(guint32 calling, guint32 called)
|
||||
{
|
||||
|
@ -1546,7 +1711,7 @@ dissect_sccp_calling_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|||
static void
|
||||
dissect_sccp_class_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint length)
|
||||
{
|
||||
guint8 class;
|
||||
guint8 msg_class;
|
||||
proto_item *pi;
|
||||
gboolean invalid_class = FALSE;
|
||||
|
||||
|
@ -1557,12 +1722,12 @@ dissect_sccp_class_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gu
|
|||
return;
|
||||
}
|
||||
|
||||
class = tvb_get_guint8(tvb, 0) & CLASS_CLASS_MASK;
|
||||
pi = proto_tree_add_uint(tree, hf_sccp_class, tvb, 0, length, class);
|
||||
msg_class = tvb_get_guint8(tvb, 0) & CLASS_CLASS_MASK;
|
||||
pi = proto_tree_add_uint(tree, hf_sccp_class, tvb, 0, length, msg_class);
|
||||
|
||||
switch(message_type) {
|
||||
case SCCP_MSG_TYPE_DT1:
|
||||
if (class != 2)
|
||||
if (msg_class != 2)
|
||||
invalid_class = TRUE;
|
||||
break;
|
||||
case SCCP_MSG_TYPE_DT2:
|
||||
|
@ -1571,7 +1736,7 @@ dissect_sccp_class_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gu
|
|||
case SCCP_MSG_TYPE_EA:
|
||||
case SCCP_MSG_TYPE_RSR:
|
||||
case SCCP_MSG_TYPE_RSC:
|
||||
if (class != 3)
|
||||
if (msg_class != 3)
|
||||
invalid_class = TRUE;
|
||||
break;
|
||||
case SCCP_MSG_TYPE_CR:
|
||||
|
@ -1581,7 +1746,7 @@ dissect_sccp_class_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gu
|
|||
case SCCP_MSG_TYPE_RLC:
|
||||
case SCCP_MSG_TYPE_ERR:
|
||||
case SCCP_MSG_TYPE_IT:
|
||||
if (class != 2 && class != 3)
|
||||
if (msg_class != 2 && msg_class != 3)
|
||||
invalid_class = TRUE;
|
||||
break;
|
||||
case SCCP_MSG_TYPE_UDT:
|
||||
|
@ -1590,7 +1755,7 @@ dissect_sccp_class_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gu
|
|||
case SCCP_MSG_TYPE_XUDTS:
|
||||
case SCCP_MSG_TYPE_LUDT:
|
||||
case SCCP_MSG_TYPE_LUDTS:
|
||||
if (class != 0 && class != 1)
|
||||
if (msg_class != 0 && msg_class != 1)
|
||||
invalid_class = TRUE;
|
||||
break;
|
||||
}
|
||||
|
@ -1598,7 +1763,7 @@ dissect_sccp_class_param(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gu
|
|||
if (invalid_class)
|
||||
expert_add_info_format(pinfo, pi, PI_MALFORMED, PI_ERROR, "Unexpected message class for this message type");
|
||||
|
||||
if (class == 0 || class == 1) {
|
||||
if (msg_class == 0 || msg_class == 1) {
|
||||
guint8 handling = tvb_get_guint8(tvb, 0) & CLASS_SPARE_HANDLING_MASK;
|
||||
|
||||
pi = proto_tree_add_item(tree, hf_sccp_handling, tvb, 0, length, ENC_NA);
|
||||
|
|
|
@ -105,6 +105,7 @@ typedef struct _sccp_assoc_info_t {
|
|||
|
||||
extern void reset_sccp_assoc(void);
|
||||
extern sccp_assoc_info_t* get_sccp_assoc(packet_info* pinfo, guint offset, guint32 src_lr, guint32 dst_lr, guint msg_type);
|
||||
extern gboolean looks_like_valid_sccp(tvbuff_t *tvb, guint8 my_mtp3_standard);
|
||||
|
||||
#define GT_SIGNAL_LENGTH 1
|
||||
#define GT_ODD_SIGNAL_MASK 0x0f
|
||||
|
|
Loading…
Reference in New Issue