wireshark/packet-ipdc.c

407 lines
10 KiB
C

/* packet-ipdc.c
* Routines for IP Device Control (SS7 over IP) dissection
* Copyright Lucent Technologies 2004
* Josh Bailey <joshbailey@lucent.com> and Ruud Linders <ruud@lucent.com>
*
* $Id: packet-ipdc.c,v 1.4 2004/03/21 19:57:14 jmayer Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <math.h>
#ifdef NEED_SNPRINTF_H
# include "snprintf.h"
#endif
#include <epan/packet.h>
#include "packet-ipdc.h"
#include "packet-tcp.h"
#include <epan/packet.h>
#include "ipproto.h"
#include "prefs.h"
static int proto_ipdc = -1;
static int hf_ipdc_nr = -1;
static int hf_ipdc_ns = -1;
static int hf_ipdc_payload_len = -1;
static int hf_ipdc_protocol_id = -1;
static int hf_ipdc_trans_id_size = -1;
static int hf_ipdc_trans_id = -1;
static int hf_ipdc_message_code = -1;
static gint ett_ipdc = -1;
static gint ett_ipdc_tag = -1;
static gboolean ipdc_desegment = TRUE;
static gint ipdc_port_pref = TCP_PORT_IPDC;
static dissector_handle_t q931_handle;
void proto_reg_handoff_ipdc(void);
static guint
get_ipdc_pdu_len(tvbuff_t *tvb, int offset)
{
return tvb_get_ntohs(tvb,offset+2)+4;
}
static void
dissect_ipdc_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
proto_item *ti;
proto_tree *ipdc_tree;
proto_item *ipdc_tag;
proto_tree *tag_tree;
tvbuff_t *q931_tvb;
char *des;
char *enum_val;
char *tmp_str;
char tmp_tag_text[255+1];
const value_string *val_ptr;
guint32 type;
guint len;
guint i;
guint status;
gshort tag;
guint32 tmp_tag;
gshort nr = tvb_get_guint8(tvb,0);
gshort ns = tvb_get_guint8(tvb,1);
guint16 payload_len = (guint16) get_ipdc_pdu_len(tvb,0);
gshort protocol_id;
gshort trans_id_size;
guint32 trans_id;
guint16 message_code;
guint16 offset;
/* display IPDC protocol ID */
if (check_col(pinfo->cinfo, COL_PROTOCOL))
col_set_str(pinfo->cinfo, COL_PROTOCOL, "IPDC");
/* short frame... */
if (payload_len < 4)
return;
/* clear info column and display send/receive sequence numbers */
if (check_col(pinfo->cinfo, COL_INFO)) {
col_set_str(pinfo->cinfo, COL_INFO, "");
col_append_fstr(pinfo->cinfo, COL_INFO, "N(r)=%u N(s)=%u ",
nr, ns);
}
if (payload_len == 4) {
if (!tree)
return;
ti = proto_tree_add_item(tree, proto_ipdc, tvb, 0, -1, FALSE);
ipdc_tree = proto_item_add_subtree(ti, ett_ipdc);
proto_tree_add_item(ipdc_tree, hf_ipdc_nr, tvb, 0, 1, nr);
proto_tree_add_item(ipdc_tree, hf_ipdc_ns, tvb, 1, 1, ns);
proto_tree_add_uint(ipdc_tree, hf_ipdc_payload_len, tvb, 2, 2,
payload_len);
return;
}
/* IPDC tags present - display message code and trans. ID */
protocol_id = tvb_get_guint8(tvb,4);
trans_id_size = TRANS_ID_SIZE_IPDC;
trans_id = tvb_get_ntohl(tvb,6);
message_code = tvb_get_ntohs(tvb,6+trans_id_size);
offset = 6 + trans_id_size + 2; /* past message_code */
if (check_col(pinfo->cinfo, COL_INFO))
col_append_fstr(pinfo->cinfo, COL_INFO,
"TransID=%x %s ",
trans_id,
val_to_str(message_code, message_code_vals,
TEXT_UNDEFINED));
if (!tree)
return;
ti = proto_tree_add_item(tree, proto_ipdc, tvb, 0, -1, FALSE);
ipdc_tree = proto_item_add_subtree(ti, ett_ipdc);
proto_tree_add_item(ipdc_tree, hf_ipdc_nr, tvb, 0, 1, nr);
proto_tree_add_item(ipdc_tree, hf_ipdc_ns, tvb, 1, 1, ns);
proto_tree_add_uint(ipdc_tree, hf_ipdc_payload_len, tvb,
2, 2, payload_len);
proto_tree_add_item(ipdc_tree, hf_ipdc_protocol_id, tvb,
4, 1, protocol_id);
proto_tree_add_item(ipdc_tree, hf_ipdc_trans_id_size, tvb,
5, 1, trans_id_size);
proto_tree_add_item(ipdc_tree, hf_ipdc_trans_id, tvb,
6, trans_id_size, trans_id);
proto_tree_add_item(ipdc_tree, hf_ipdc_message_code, tvb,
6 + trans_id_size + 1, 1, message_code);
ipdc_tag = proto_tree_add_text(ipdc_tree, tvb, offset,
payload_len - offset, "IPDC tags");
tag_tree = proto_item_add_subtree(ipdc_tag, ett_ipdc_tag);
/* iterate through tags. first byte is tag, second is length,
in bytes, following is tag data. tag of 0x0 should be
end of tags. */
for (;;) {
tag = tvb_get_guint8(tvb, offset);
if (tag == 0x0) {
if (offset == payload_len - 1) {
proto_tree_add_text(tag_tree, tvb,
offset, 1, "end of tags");
} else {
proto_tree_add_text(tag_tree, tvb,
offset, 1, "data trailing end of tags");
}
break;
}
len = tvb_get_guint8(tvb,offset+1);
des = val_to_str(tag, tag_description, TEXT_UNDEFINED);
/* lookup tag type */
for (i = 0; (ipdc_tag_types[i].tag != tag &&
ipdc_tag_types[i].type != IPDC_UNKNOWN); i++)
;
type = ipdc_tag_types[i].type;
tmp_tag = 0;
switch (type) {
/* simple IPDC_ASCII strings */
case IPDC_ASCII:
tmp_str = tvb_memdup(tvb, offset + 2, len);
strncpy(tmp_tag_text, tmp_str, len);
tmp_tag_text[len] = 0;
free(tmp_str);
proto_tree_add_text(tag_tree, tvb, offset,
len + 2, "0x%2.2x: %s: %s", tag, des,
tmp_tag_text);
break;
/* unsigned integers, or bytes */
case IPDC_UINT:
case IPDC_BYTE:
for (i = 0; i < len; i++)
tmp_tag += tvb_get_guint8(tvb,
offset + 2 + i) * (guint32)
pow(256, len - (i + 1));
if (len == 1)
enum_val =
val_to_str( IPDC_TAG(tag) +
tmp_tag,
tag_enum_type, TEXT_UNDEFINED);
if (len == 1 &&
strcmp(enum_val, TEXT_UNDEFINED) != 0) {
proto_tree_add_text(tag_tree, tvb,
offset, len + 2,
"0x%2.2x: %s: %s",
tag, des, enum_val);
} else {
proto_tree_add_text(tag_tree, tvb,
offset, len + 2,
"0x%2.2x: %s: %u",
tag, des, tmp_tag);
}
break;
/* IP addresses */
case IPDC_IPA:
if (len == 4) {
sprintf(tmp_tag_text, "%u.%u.%u.%u",
tvb_get_guint8(tvb, offset + 2),
tvb_get_guint8(tvb, offset + 3),
tvb_get_guint8(tvb, offset + 4),
tvb_get_guint8(tvb, offset + 5));
} else if (len == 6) {
sprintf(tmp_tag_text, "%u.%u.%u.%u:%u",
tvb_get_guint8(tvb, offset + 2),
tvb_get_guint8(tvb, offset + 3),
tvb_get_guint8(tvb, offset + 4),
tvb_get_guint8(tvb, offset + 5),
tvb_get_ntohs(tvb, offset + 6));
} else {
sprintf(tmp_tag_text,
"Invalid IP address length %u", len);
}
proto_tree_add_text(tag_tree, tvb,
offset, len + 2,
"0x%2.2x: %s: %s",
tag, des, tmp_tag_text);
break;
/* Line status arrays */
case IPDC_LINESTATUS:
case IPDC_CHANNELSTATUS:
proto_tree_add_text(tag_tree, tvb, offset,
len + 2, "0x%2.2x: %s", tag, des);
val_ptr = (type == IPDC_LINESTATUS) ?
line_status_vals : channel_status_vals;
for (i = 0; i < len; i++) {
status = tvb_get_guint8(tvb,offset+2+i);
proto_tree_add_text(tag_tree, tvb,
offset + 2 + i, 1,
" %.2u: %.2x (%s)",
i + 1, status,
val_to_str(status,
val_ptr,
TEXT_UNDEFINED));
}
break;
case IPDC_Q931:
q931_tvb =
tvb_new_subset(tvb, offset+2, len, len);
call_dissector(q931_handle,q931_tvb,pinfo,tree);
break;
/* default */
default:
proto_tree_add_text(tag_tree, tvb, offset,
len + 2, "0x%2.2x: %s", tag, des);
break;
} /* switch */
offset += len + 2;
}
}
static void
dissect_ipdc_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
dissect_ipdc_common(tvb, pinfo, tree);
}
static void
dissect_ipdc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
tcp_dissect_pdus(tvb, pinfo, tree, ipdc_desegment, 4,
get_ipdc_pdu_len, dissect_ipdc_tcp_pdu);
}
void
proto_register_ipdc(void)
{
static hf_register_info hf[] = {
{ &hf_ipdc_nr,
{ "N(r)", "ipdc.nr",
FT_UINT8, BASE_DEC, NULL, 0x0,
"Receive sequence number", HFILL }
},
{ &hf_ipdc_ns,
{ "N(s)", "ipdc.ns",
FT_UINT8, BASE_DEC, NULL, 0x0,
"Transmit sequence number", HFILL }
},
{ &hf_ipdc_payload_len,
{ "Payload length", "ipdc.length",
FT_UINT16, BASE_DEC, NULL, 0x0,
"Payload length", HFILL }
},
{ &hf_ipdc_protocol_id,
{ "Protocol ID", "ipdc.protocol_id",
FT_UINT8, BASE_HEX, NULL, 0x0,
"Protocol ID", HFILL }
},
{ &hf_ipdc_trans_id_size,
{ "Transaction ID size", "ipdc.trans_id_size",
FT_UINT8, BASE_DEC, NULL, 0x0,
"Transaction ID size", HFILL }
},
{ &hf_ipdc_trans_id,
{ "Transaction ID", "ipdc.trans_id",
FT_BYTES, BASE_HEX, NULL, 0x0,
"Transaction ID", HFILL }
},
{ &hf_ipdc_message_code,
{ "Message code", "ipdc.message_code",
FT_UINT16, BASE_HEX, VALS(message_code_vals), 0x0,
"Message Code", HFILL }
},
};
static gint *ett[] = {
&ett_ipdc,
&ett_ipdc_tag,
};
module_t *ipdc_module;
proto_ipdc = proto_register_protocol("IP Device Control (SS7 over IP)",
"IPDC", "ipdc");
proto_register_field_array(proto_ipdc, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
ipdc_module = prefs_register_protocol(proto_ipdc, proto_reg_handoff_ipdc);
prefs_register_bool_preference(ipdc_module, "desegment_ipdc_messages",
"Desegment all IPDC messages spanning multiple TCP segments",
"Whether the IPDC dissector should desegment all messages spanning multiple TCP segments",
&ipdc_desegment);
prefs_register_uint_preference(ipdc_module, "tcp.port",
"IPDC monitoring port",
"Set the IPDC monitoring port", 10,
&ipdc_port_pref);
}
void
proto_reg_handoff_ipdc(void)
{
static gint last_ipdc_port_pref = 0;
static dissector_handle_t ipdc_tcp_handle = NULL;
if (ipdc_tcp_handle) {
dissector_delete("tcp.port", last_ipdc_port_pref,
ipdc_tcp_handle);
} else {
ipdc_tcp_handle =
create_dissector_handle(dissect_ipdc_tcp, proto_ipdc);
q931_handle = find_dissector("q931");
}
last_ipdc_port_pref = ipdc_port_pref;
dissector_add("tcp.port", ipdc_port_pref, ipdc_tcp_handle);
}