wireshark/epan/dissectors/packet-p_mul.c

578 lines
19 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* packet-p_mul.c
*
* Routines for P_Mul (ACP142) packet disassembly.
* A protocol for reliable multicast messaging in bandwidth constrained
* and delayed acknowledgement (EMCON) environments.
*
* Copyright 2005, Stig Bjrlykke <stig@bjorlykke.org>, Thales Norway AS
*
* $Id$
*
* 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.
*
* Ref: http://www.jcs.mil/j6/cceb/acps/Acp142.pdf
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <epan/packet.h>
#include <epan/to_str.h>
#include <epan/prefs.h>
/* Recommended UDP Port Numbers */
#define P_MUL_TPORT 2751
#define P_MUL_RPORT 2752
#define P_MUL_DPORT 2753
#define P_MUL_APORT 2754
/* PDU Types */
#define Data_PDU 0x00
#define Ack_PDU 0x01
#define Address_PDU 0x02
#define Discard_Message_PDU 0x03
#define Announce_PDU 0x04
#define Request_PDU 0x05
#define Reject_PDU 0x06
#define Release_PDU 0x07
void proto_reg_handoff_p_mul (void);
static int proto_p_mul = -1;
static int hf_length = -1;
static int hf_priority = -1;
static int hf_map_first = -1;
static int hf_map_last = -1;
static int hf_map_unused = -1;
static int hf_pdu_type = -1;
static int hf_no_pdus = -1;
static int hf_seq_no = -1;
static int hf_unused8 = -1;
static int hf_unused16 = -1;
static int hf_checksum = -1;
static int hf_source_id_ack = -1;
static int hf_source_id = -1;
static int hf_message_id = -1;
static int hf_expiry_time = -1;
static int hf_mc_group = -1;
static int hf_ann_mc_group = -1;
static int hf_count_of_dest = -1;
static int hf_length_of_res = -1;
static int hf_ack_count = -1;
static int hf_ack_entry = -1;
static int hf_ack_length = -1;
static int hf_miss_seq_no = -1;
static int hf_dest_entry = -1;
static int hf_dest_id = -1;
static int hf_msg_seq_no = -1;
static int hf_sym_key = -1;
static int hf_fragment = -1;
static int hf_error = -1;
static gint ett_p_mul = -1;
static gint ett_pdu_type = -1;
static gint ett_entry = -1;
/* User definable port numbers to use for dissection */
static guint global_p_mul_tport = P_MUL_TPORT;
static guint global_p_mul_rport = P_MUL_RPORT;
static guint global_p_mul_dport = P_MUL_DPORT;
static guint global_p_mul_aport = P_MUL_APORT;
static guint p_mul_tport = 0;
static guint p_mul_rport = 0;
static guint p_mul_dport = 0;
static guint p_mul_aport = 0;
static const value_string pdu_vals[] = {
{ Data_PDU, "Data PDU" },
{ Ack_PDU, "Ack PDU" },
{ Address_PDU, "Address PDU" },
{ Discard_Message_PDU, "Discard Message PDU" },
{ Announce_PDU, "Announce PDU" },
{ Request_PDU, "Request PDU" },
{ Reject_PDU, "Reject PDU" },
{ Release_PDU, "Release PDU" },
{ 0, NULL } };
static const true_false_string yes_no = {
"No", "Yes"
};
static const gchar *get_type (guint8 value)
{
return val_to_str (value, pdu_vals, "Unknown");
}
/*Function checksum, found in ACP142 annex B-3 */
static guint16 checksum (guint8 *buffer, gint len, gint offset)
{
guint16 c0 = 0, c1 = 0, ret, ctmp;
gint16 cs;
guint8 *hpp, *pls;
buffer[offset] = 0;
buffer[offset+1] = 0;
ctmp = len - offset - 1;
pls = buffer + len;
hpp = buffer;
while (hpp < pls) {
if ((c0 += *hpp++) > 254) { c0 -= 255; }
if ((c1 += c0) > 254) { c1 -= 255; }
}
if ((cs = ((ctmp * c0) - c1) % 255L) < 0) { cs += 255; }
ret = cs << 8;
if ((cs = (c1 - ((ctmp + 1L) * c0)) % 255L) < 0) { cs += 255; }
ret |= cs;
return ret;
}
static void dissect_p_mul (tvbuff_t *tvb, packet_info *pinfo _U_,
proto_tree *tree)
{
proto_tree *p_mul_tree = NULL, *field_tree = NULL;
proto_item *ti = NULL, *en = NULL;
guint32 message_id = 0, no_pdus = 0, seq_no = 0;
guint16 no_dest = 0, count = 0, len = 0, checksum1, checksum2;
guint8 pdu_type = 0, *value = NULL, map = 0;
gint i, no_missing = 0, offset = 0;
nstime_t ts;
if (check_col(pinfo->cinfo, COL_PROTOCOL))
col_set_str(pinfo->cinfo, COL_PROTOCOL, "P_MUL");
if (check_col(pinfo->cinfo, COL_INFO))
col_clear(pinfo->cinfo, COL_INFO);
/* First fetch PDU Type */
pdu_type = tvb_get_guint8 (tvb, offset + 3) & 0x3F;
proto_item_append_text (ti, ", %s", get_type (pdu_type));
if (check_col(pinfo->cinfo, COL_INFO)) {
col_add_fstr(pinfo->cinfo, COL_INFO, "%s", get_type (pdu_type));
}
ti = proto_tree_add_item (tree, proto_p_mul, tvb, offset, -1, FALSE);
p_mul_tree = proto_item_add_subtree (ti, ett_p_mul);
/* Length of PDU */
proto_tree_add_item (p_mul_tree, hf_length, tvb, offset, 2, FALSE);
offset += 2;
switch (pdu_type) {
case Data_PDU:
case Ack_PDU:
case Address_PDU:
case Discard_Message_PDU:
/* Priority */
proto_tree_add_item (p_mul_tree, hf_priority, tvb, offset, 1, FALSE);
break;
default:
/* Unused */
proto_tree_add_item (p_mul_tree, hf_unused8, tvb, offset, 1, FALSE);
}
offset += 1;
/* MAP / PDU_Type */
en = proto_tree_add_uint_format (p_mul_tree, hf_pdu_type, tvb, offset, 1,
pdu_type, "PDU Type: %s (0x%02x)",
get_type (pdu_type), pdu_type);
field_tree = proto_item_add_subtree (en, ett_pdu_type);
switch (pdu_type) {
case Address_PDU:
case Announce_PDU:
map = tvb_get_guint8 (tvb, offset);
proto_tree_add_item (field_tree, hf_map_first, tvb, offset, 1, FALSE);
proto_tree_add_item (field_tree, hf_map_last, tvb, offset, 1, FALSE);
if ((map & 0x80) || (map & 0x40)) {
proto_item_append_text (en, ", %s / %s",
(map & 0x80) ? "Not first" : "First",
(map & 0x40) ? "Not last" : "Last");
} else {
proto_item_append_text (en, ", Only one PDU");
}
break;
default:
proto_tree_add_item (field_tree, hf_map_unused, tvb, offset, 1, FALSE);
break;
}
proto_tree_add_item (field_tree, hf_pdu_type, tvb, offset, 1, FALSE);
offset += 1;
switch (pdu_type) {
case Address_PDU:
/* Total Number of PDUs */
no_pdus = tvb_get_ntohs (tvb, offset);
proto_tree_add_item (p_mul_tree, hf_no_pdus, tvb, offset, 2, FALSE);
break;
case Data_PDU:
/* Sequence Number of PDUs */
seq_no = tvb_get_ntohs (tvb, offset);
proto_tree_add_item (p_mul_tree, hf_seq_no, tvb, offset, 2, FALSE);
break;
case Announce_PDU:
/* Count of Destination Entries */
count = tvb_get_ntohs (tvb, offset);
proto_tree_add_item (p_mul_tree, hf_count_of_dest, tvb, offset, 2,FALSE);
break;
default:
/* Unused */
proto_tree_add_item (p_mul_tree, hf_unused16, tvb, offset, 2, FALSE);
break;
}
offset += 2;
/* Checksum */
len = tvb_length (tvb);
value = tvb_get_string (tvb, 0, len);
checksum1 = checksum (value, len, offset);
checksum2 = tvb_get_ntohs (tvb, offset);
g_free (value);
en = proto_tree_add_item (p_mul_tree, hf_checksum, tvb, offset, 2, FALSE);
if (checksum1 == checksum2) {
proto_item_append_text (en, " (correct)");
} else {
proto_item_append_text (en, " (incorrect, should be 0x%04x)", checksum1);
}
offset += 2;
if (pdu_type == Ack_PDU) {
/* Source ID of Ack Sender */
proto_tree_add_item (p_mul_tree, hf_source_id_ack, tvb, offset, 4, FALSE);
offset += 4;
/* Count of Ack Info Entries */
count = tvb_get_ntohs (tvb, offset);
proto_tree_add_item (p_mul_tree, hf_ack_count, tvb, offset, 2, FALSE);
offset += 2;
} else {
/* Source Id */
proto_tree_add_item (p_mul_tree, hf_source_id, tvb, offset, 4, FALSE);
offset += 4;
/* Message Id */
message_id = tvb_get_ntohl (tvb, offset);
proto_tree_add_item (p_mul_tree, hf_message_id, tvb, offset, 4, FALSE);
offset += 4;
}
if (pdu_type == Address_PDU || pdu_type == Announce_PDU) {
/* Expiry Time */
ts.secs = tvb_get_ntohl (tvb, offset);
ts.nsecs = 0;
proto_tree_add_time (p_mul_tree, hf_expiry_time, tvb, offset, 4, &ts);
offset += 4;
}
switch (pdu_type) {
case Address_PDU:
/* Count of Destination Entries */
no_dest = tvb_get_ntohs (tvb, offset);
proto_tree_add_item (p_mul_tree, hf_count_of_dest, tvb, offset, 2, FALSE);
offset += 2;
/* Length of Reserved Field */
len = tvb_get_ntohs (tvb, offset);
proto_tree_add_item (p_mul_tree, hf_length_of_res, tvb, offset, 2, FALSE);
offset += 2;
for (i = 0; i < no_dest; i++) {
/* Destination Entry */
en = proto_tree_add_none_format (p_mul_tree, hf_dest_entry, tvb,
offset, no_dest * (8 + len),
"Destination Entry #%d", i + 1);
field_tree = proto_item_add_subtree (en, ett_entry);
/* Destination Id */
proto_tree_add_item (field_tree, hf_dest_id, tvb, offset, 4, FALSE);
offset += 4;
/* Message Sequence Number */
proto_tree_add_item (field_tree, hf_msg_seq_no, tvb, offset, 4, FALSE);
offset += 4;
if (len > 0) {
/* Reserved Field (variable length) */
proto_tree_add_none_format (field_tree, hf_sym_key, tvb, offset,
len, "Symmetric Key (%d byte%s)",
len, plurality (len, "", "s"));
offset += len;
}
}
break;
case Data_PDU:
/* Fragment of Data (variable length) */
len = tvb_length_remaining (tvb, offset);
if (len > 0) {
proto_tree_add_none_format (tree, hf_fragment, tvb, offset,
len, "Fragment of Data (%d byte%s)",
len, plurality (len, "", "s"));
proto_item_set_len (ti, offset);
}
offset += len;
break;
case Ack_PDU:
for (i = 0; i < count; i++) {
/* Ack Info Entry */
len = tvb_get_ntohs (tvb, offset);
en = proto_tree_add_none_format (p_mul_tree, hf_ack_entry, tvb,
offset, count * len,
"Ack Info Entry #%d", i + 1);
field_tree = proto_item_add_subtree (en, ett_entry);
/* Length of Ack Info Entry */
proto_tree_add_item (field_tree, hf_ack_length, tvb, offset, 2, FALSE);
offset += 2;
/* Source Id */
proto_tree_add_item (field_tree, hf_source_id, tvb, offset, 4, FALSE);
offset += 4;
/* Message Id */
proto_tree_add_item (field_tree, hf_message_id, tvb, offset, 4, FALSE);
offset += 4;
for (no_missing = 0; no_missing < (len - 10) / 2; no_missing++) {
/* Missing Data PDU Seq Number */
proto_tree_add_item (field_tree, hf_miss_seq_no, tvb,offset, 2, FALSE);
offset += 2;
}
}
break;
case Announce_PDU:
/* Announced Multicast Group */
proto_tree_add_item (p_mul_tree, hf_ann_mc_group, tvb, offset, 4, FALSE);
offset += 4;
for (i = 0; i < count; i++) {
/* Destination Id */
proto_tree_add_item (p_mul_tree, hf_dest_id, tvb, offset, 4, FALSE);
offset += 4;
}
break;
case Request_PDU:
case Reject_PDU:
case Release_PDU:
/* Multicast Group */
proto_tree_add_item (p_mul_tree, hf_mc_group, tvb, offset, 4, FALSE);
offset += 4;
break;
default:
/* Nothing */
break;
}
if (check_col(pinfo->cinfo, COL_INFO)) {
if (seq_no) {
col_append_fstr (pinfo->cinfo, COL_INFO, ", Seq no: %u", seq_no);
} else if (no_pdus) {
col_append_fstr (pinfo->cinfo, COL_INFO, ", No PDUs: %u", no_pdus);
}
if (no_missing) {
col_append_fstr (pinfo->cinfo, COL_INFO, ", Missing seq numbers: %u",
no_missing);
} else if (pdu_type == Address_PDU) {
col_append_fstr (pinfo->cinfo, COL_INFO, ", Count of Dest: %u",
no_dest);
}
if (message_id) {
col_append_fstr (pinfo->cinfo, COL_INFO, ", MSID: %u", message_id);
}
}
}
void proto_register_p_mul (void)
{
static hf_register_info hf[] = {
{ &hf_length,
{ "Length of PDU", "p_mul.length", FT_UINT16, BASE_DEC,
NULL, 0x0, "Length of PDU", HFILL } },
{ &hf_priority,
{ "Priority", "p_mul.priority", FT_UINT8, BASE_DEC,
NULL, 0x0, "Priority", HFILL } },
{ &hf_map_first,
{ "First", "p_mul.first", FT_BOOLEAN, 8,
TFS (&yes_no), 0x80, "First", HFILL } },
{ &hf_map_last,
{ "Last", "p_mul.last", FT_BOOLEAN, 8,
TFS (&yes_no), 0x40, "Last", HFILL } },
{ &hf_map_unused,
{ "MAP unused", "p_mul.unused", FT_UINT8, BASE_DEC,
NULL, 0xC0, "MAP unused", HFILL } },
{ &hf_pdu_type,
{ "PDU Type", "p_mul.pdu_type", FT_UINT8, BASE_DEC,
VALS (pdu_vals), 0x3F, "PDU Type", HFILL } },
{ &hf_no_pdus,
{ "Total Number of PDUs", "p_mul.no_pdus", FT_UINT16, BASE_DEC,
NULL, 0x0, "Total Number of PDUs", HFILL } },
{ &hf_seq_no,
{ "Sequence Number of PDUs", "p_mul.seq_no", FT_UINT16, BASE_DEC,
NULL, 0x0, "Sequence Number of PDUs", HFILL } },
{ &hf_unused8,
{ "Unused", "p_mul.unused", FT_UINT8, BASE_DEC,
NULL, 0x0, "Unused", HFILL } },
{ &hf_unused16,
{ "Unused", "p_mul.unused", FT_UINT16, BASE_DEC,
NULL, 0x0, "Unused", HFILL } },
{ &hf_checksum,
{ "Checksum", "p_mul.checksum", FT_UINT16, BASE_HEX,
NULL, 0x0, "Checksum", HFILL } },
{ &hf_source_id_ack,
{ "Source ID of Ack Sender", "p_mul.source_id_ack", FT_IPv4, BASE_DEC,
NULL, 0x0, "Source ID of Ack Sender", HFILL } },
{ &hf_source_id,
{ "Source ID", "p_mul.source_id", FT_IPv4, BASE_DEC,
NULL, 0x0, "Source ID", HFILL } },
{ &hf_message_id,
{ "Message ID (MSID)", "p_mul.message_id", FT_UINT32, BASE_DEC,
NULL, 0x0, "Message ID", HFILL } },
{ &hf_expiry_time,
{ "Expiry Time", "p_mul.expiry_time", FT_ABSOLUTE_TIME, BASE_DEC,
NULL, 0x0, "Expiry Time", HFILL } },
{ &hf_mc_group,
{ "Multicast Group", "p_mul.mc_group", FT_UINT32, BASE_DEC,
NULL, 0x0, "Multicast Group", HFILL } },
{ &hf_ann_mc_group,
{ "Announced Multicast Group", "p_mul.ann_mc_group", FT_UINT32, BASE_DEC,
NULL, 0x0, "Announced Multicast Group", HFILL } },
{ &hf_count_of_dest,
{ "Count of Destination Entries", "p_mul.dest_count", FT_UINT16,BASE_DEC,
NULL, 0x0, "Count of Destination Entries", HFILL } },
{ &hf_length_of_res,
{ "Length of Reserved Field", "p_mul.reserved_length",FT_UINT16,BASE_DEC,
NULL, 0x0, "Length of Reserved Field", HFILL } },
{ &hf_ack_count,
{ "Count of Ack Info Entries", "p_mul.ack_count", FT_UINT16, BASE_DEC,
NULL, 0x0, "Count of Ack Info Entries", HFILL } },
{ &hf_ack_entry,
{ "Ack Info Entry", "p_mul.ack_info_entry", FT_NONE, BASE_NONE,
NULL, 0x0, "Ack Info Entry", HFILL } },
{ &hf_ack_length,
{ "Length of Ack Info Entry", "p_mul.ack_length", FT_UINT16, BASE_DEC,
NULL, 0x0, "Length of Ack Info Entry", HFILL } },
{ &hf_miss_seq_no,
{ "Missing Data PDU Seq Number", "p_mul.missing_seq_no", FT_UINT16,
BASE_DEC, NULL, 0x0, "Missing Data PDU Seq Number", HFILL } },
{ &hf_dest_entry,
{ "Destination Entry", "p_mul.dest_entry", FT_NONE, BASE_NONE,
NULL, 0x0, "Destination Entry", HFILL } },
{ &hf_dest_id,
{ "Destination ID", "p_mul.dest_id", FT_IPv4, BASE_DEC,
NULL, 0x0, "Destination ID", HFILL } },
{ &hf_msg_seq_no,
{ "Message Sequence Number", "p_mul.msg_seq_no", FT_UINT16, BASE_DEC,
NULL, 0x0, "Message Sequence Number", HFILL } },
{ &hf_sym_key,
{ "Symmetric Key", "p_mul.sym_key", FT_NONE, BASE_NONE,
NULL, 0x0, "Symmetric Key", HFILL } },
{ &hf_fragment,
{ "Fragment of Data", "p_mul.fragment", FT_NONE, BASE_NONE,
NULL, 0x0, "Fragment of Data", HFILL } },
{ &hf_error,
{ "Error reading data", "p_mul.error", FT_NONE, BASE_NONE,
NULL, 0x0, "Error reading data", HFILL } },
};
static gint *ett[] = {
&ett_p_mul,
&ett_pdu_type,
&ett_entry
};
module_t *p_mul_module;
proto_p_mul = proto_register_protocol ("P_Mul (ACP142)", "P_MUL", "p_mul");
proto_register_field_array (proto_p_mul, hf, array_length (hf));
proto_register_subtree_array (ett, array_length (ett));
/* Register our configuration options */
p_mul_module = prefs_register_protocol (proto_p_mul,
proto_reg_handoff_p_mul);
prefs_register_uint_preference (p_mul_module, "tport", "TPORT",
"Used for transmission of Request_PDUs, "
"Reject_PDUs and Release_PDUs between"
"the transmitters",
10, &global_p_mul_tport);
prefs_register_uint_preference (p_mul_module, "rport", "RPORT",
"Used for transmission of Announce_PDUs "
"to inform the receiver(s)",
10, &global_p_mul_rport);
prefs_register_uint_preference (p_mul_module, "dport", "DPORT",
"Used for the data traffic from the "
"transmitters to the receiver(s)",
10, &global_p_mul_dport);
prefs_register_uint_preference (p_mul_module, "aport", "APORT",
"Used for the data traffic from the "
"receiver(s) to the transmitter",
10, &global_p_mul_aport);
}
void proto_reg_handoff_p_mul (void)
{
static int p_mul_prefs_initialized = FALSE;
static dissector_handle_t p_mul_handle;
if (!p_mul_prefs_initialized) {
p_mul_handle = create_dissector_handle (dissect_p_mul, proto_p_mul);
p_mul_prefs_initialized = TRUE;
} else {
dissector_delete ("udp.port", p_mul_tport, p_mul_handle);
dissector_delete ("udp.port", p_mul_rport, p_mul_handle);
dissector_delete ("udp.port", p_mul_dport, p_mul_handle);
dissector_delete ("udp.port", p_mul_aport, p_mul_handle);
}
/* Save port numbers for later deletion */
p_mul_tport = global_p_mul_tport;
p_mul_rport = global_p_mul_rport;
p_mul_dport = global_p_mul_dport;
p_mul_aport = global_p_mul_aport;
/* We convert all P_Mul ports */
dissector_add ("udp.port", global_p_mul_tport, p_mul_handle);
dissector_add ("udp.port", global_p_mul_rport, p_mul_handle);
dissector_add ("udp.port", global_p_mul_dport, p_mul_handle);
dissector_add ("udp.port", global_p_mul_aport, p_mul_handle);
}