/* 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 Bjørlykke , Thales Norway AS * * $Id$ * * Ethereal - Network traffic analyzer * By Gerald Combs * 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 #include #include #include #include "packet-cdt.h" #define PNAME "P_Mul (ACP142)" #define PSNAME "P_MUL" #define PFNAME "p_mul" /* 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 /* Type of content to decode from Data_PDU */ #define DECODE_NONE 0 #define DECODE_CDT 1 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_data_fragment = -1; static int hf_msg_fragments = -1; static int hf_msg_fragment = -1; static int hf_msg_fragment_overlap = -1; static int hf_msg_fragment_overlap_conflicts = -1; static int hf_msg_fragment_multiple_tails = -1; static int hf_msg_fragment_too_long_fragment = -1; static int hf_msg_fragment_error = -1; static int hf_msg_reassembled_in = -1; static gint ett_p_mul = -1; static gint ett_pdu_type = -1; static gint ett_entry = -1; static gint ett_msg_fragment = -1; static gint ett_msg_fragments = -1; /* User definable values to use for dissection */ static gboolean p_mul_reassemble = TRUE; static gint decode_option = DECODE_NONE; 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 GHashTable *p_mul_fragment_table = NULL; static GHashTable *p_mul_reassembled_table = NULL; static const fragment_items p_mul_frag_items = { /* Fragment subtrees */ &ett_msg_fragment, &ett_msg_fragments, /* Fragment fields */ &hf_msg_fragments, &hf_msg_fragment, &hf_msg_fragment_overlap, &hf_msg_fragment_overlap_conflicts, &hf_msg_fragment_multiple_tails, &hf_msg_fragment_too_long_fragment, &hf_msg_fragment_error, /* Reassembled in field */ &hf_msg_reassembled_in, /* Tag */ "Message fragments" }; 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 enum_val_t decode_options[] = { { "none", "No decoding", DECODE_NONE }, { "cdt", "Compressed Data Type", DECODE_CDT }, { NULL, NULL, 0 } }; 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_reassembled_data (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) { if (tvb == NULL || tree == NULL) { return; } if (decode_option == DECODE_CDT) { dissect_cdt (tvb, pinfo, tree); } } 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, *len_en = NULL; gboolean save_fragmented; fragment_data *frag_msg = NULL; guint32 message_id = 0, no_pdus = 0, seq_no = 0; guint16 no_dest = 0, count = 0, len = 0, data_len = 0; guint16 checksum1, checksum2, pdu_length = 0; 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; ti = proto_tree_add_item (tree, proto_p_mul, tvb, offset, -1, FALSE); proto_item_append_text (ti, ", %s", get_type (pdu_type)); p_mul_tree = proto_item_add_subtree (ti, ett_p_mul); if (check_col (pinfo->cinfo, COL_INFO)) col_append_fstr (pinfo->cinfo, COL_INFO, "%s", get_type (pdu_type)); /* Length of PDU */ pdu_length = tvb_get_ntohs (tvb, offset); len_en = 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); proto_item_append_text (ti, ", No PDUs: %u", no_pdus); if (check_col (pinfo->cinfo, COL_INFO)) col_append_fstr (pinfo->cinfo, COL_INFO, ", No PDUs: %u", no_pdus); 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); proto_item_append_text (ti, ", Seq no: %u", seq_no); if (check_col (pinfo->cinfo, COL_INFO)) col_append_fstr (pinfo->cinfo, COL_INFO, ", Seq no: %u", seq_no); 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; proto_item_append_text (ti, ", MSID: %u", message_id); if (check_col (pinfo->cinfo, COL_INFO)) col_append_fstr (pinfo->cinfo, COL_INFO, ", MSID: %u", message_id); } 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; } save_fragmented = pinfo->fragmented; 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; } } proto_item_append_text (ti, ", Count of Dest: %u", no_dest); if (check_col (pinfo->cinfo, COL_INFO)) col_append_fstr (pinfo->cinfo, COL_INFO, ", Count of Dest: %u", no_dest); if (p_mul_reassemble) { /* Add fragment to fragment table */ frag_msg = fragment_add_seq_check (tvb, offset, pinfo, message_id, p_mul_fragment_table, p_mul_reassembled_table, 0, 0, TRUE); fragment_set_tot_len (pinfo, message_id, p_mul_fragment_table, no_pdus); } break; case Data_PDU: /* Fragment of Data (variable length) */ data_len = tvb_length_remaining (tvb, offset); proto_tree_add_none_format (p_mul_tree, hf_data_fragment, tvb, offset, data_len, "Fragment %d of Data (%d byte%s)", seq_no, data_len, plurality (data_len, "", "s")); if (p_mul_reassemble) { tvbuff_t *new_tvb = NULL; pinfo->fragmented = TRUE; /* Add fragment to fragment table */ frag_msg = fragment_add_seq_check (tvb, offset, pinfo, message_id, p_mul_fragment_table, p_mul_reassembled_table, seq_no, data_len, TRUE); new_tvb = process_reassembled_data (tvb, offset, pinfo, "Reassembled Data", frag_msg, &p_mul_frag_items, NULL, tree); if (check_col (pinfo->cinfo, COL_INFO) && frag_msg) col_append_fstr (pinfo->cinfo, COL_INFO, " (Message Reassembled)"); if (new_tvb) { dissect_reassembled_data (new_tvb, pinfo, tree); } } 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; } if (no_missing) { proto_item_append_text (ti, ", Missing seq numbers: %u", no_missing); if (check_col (pinfo->cinfo, COL_INFO)) col_append_fstr (pinfo->cinfo, COL_INFO, ", Missing seq numbers: %u", no_missing); } } 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; } pinfo->fragmented = save_fragmented; /* Update length of P_Mul packet and check length values */ proto_item_set_len (ti, offset); if (pdu_length != (offset + data_len)) { proto_item_append_text (len_en, " (incorrect, should be: %d)", offset + data_len); } else if ((len = tvb_length_remaining (tvb, pdu_length)) > 0) { proto_item_append_text (len_en, " (more data in packet: %d)", len); } } static void p_mul_reassemble_init (void) { fragment_table_init (&p_mul_fragment_table); reassembled_table_init (&p_mul_reassembled_table); } 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_data_fragment, { "Fragment of Data", "p_mul.data_fragment", FT_NONE, BASE_NONE, NULL, 0x0, "Fragment of Data", HFILL } }, /* Fragment entries */ { &hf_msg_fragments, { "Message fragments", "p_mul.fragments", FT_NONE, BASE_NONE, NULL, 0x00, "Message fragments", HFILL } }, { &hf_msg_fragment, { "Message fragment", "p_mul.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x00, "Message fragment", HFILL } }, { &hf_msg_fragment_overlap, { "Message fragment overlap", "p_mul.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x00, "Message fragment overlap", HFILL } }, { &hf_msg_fragment_overlap_conflicts, { "Message fragment overlapping with conflicting data", "p_mul.fragment.overlap.conflicts", FT_BOOLEAN, BASE_NONE, NULL, 0x00, "Message fragment overlapping with conflicting data", HFILL } }, { &hf_msg_fragment_multiple_tails, { "Message has multiple tail fragments", "p_mul.fragment.multiple_tails", FT_BOOLEAN, BASE_NONE, NULL, 0x00, "Message has multiple tail fragments", HFILL } }, { &hf_msg_fragment_too_long_fragment, { "Message fragment too long", "p_mul.fragment.too_long_fragment", FT_BOOLEAN, BASE_NONE, NULL, 0x00, "Message fragment too long", HFILL } }, { &hf_msg_fragment_error, { "Message defragmentation error", "p_mul.fragment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x00, "Message defragmentation error", HFILL } }, { &hf_msg_reassembled_in, { "Reassembled in", "p_mul.reassembled.in", FT_FRAMENUM, BASE_NONE, NULL, 0x00, "Reassembled in", HFILL } }, }; static gint *ett[] = { &ett_p_mul, &ett_pdu_type, &ett_entry, &ett_msg_fragment, &ett_msg_fragments }; module_t *p_mul_module; proto_p_mul = proto_register_protocol (PNAME, PSNAME, PFNAME); proto_register_field_array (proto_p_mul, hf, array_length (hf)); proto_register_subtree_array (ett, array_length (ett)); register_init_routine (&p_mul_reassemble_init); /* Register our configuration options */ p_mul_module = prefs_register_protocol (proto_p_mul, proto_reg_handoff_p_mul); prefs_register_bool_preference (p_mul_module, "reassemble", "Reassemble fragmented P_Mul packets", "Reassemble fragmented P_Mul packets", &p_mul_reassemble); prefs_register_enum_preference (p_mul_module, "decode", "Decode Data PDU as", "Type of content in Data_PDU", &decode_option, decode_options, FALSE); 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); }