/* packet-acn.c * Routines for ACN packet disassembly * * $Id: packet-acn.c 27783 2009-03-18 20:45:57Z guy $ * * Copyright (c) 2003 by Erwin Rol * Copyright (c) 2006 by Electronic Theatre Controls, Inc. * Bill Florac * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1999 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. */ /* Todo: Add reading of DDL files so we can futher explode DMP packets For some of the Set/Get properties where we have a range of data it would be better to show the block of data rather and address-data pair on each line... Build CID to "Name" table from file so we can display real names rather than CIDs */ /* Include files */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "packet-acn.h" /* * See * ANSI BSR E1.17 Architecture for Control Networks * ANSI BSR E1.31 */ #define ACTUAL_ADDRESS 0 /* forward reference */ static gboolean dissect_acn_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ); static guint32 acn_add_channel_owner_info_block(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, int offset); static guint32 acn_add_channel_member_info_block(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, int offset); static guint32 acn_add_expiry(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, const char *label); static guint32 acn_add_channel_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset); static guint32 acn_add_address(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, const char *label); static guint32 acn_add_dmp_address_type(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, acn_dmp_adt_type *adt); static guint32 acn_add_dmp_address(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, acn_dmp_adt_type *adt); static guint32 dissect_acn_dmp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, acn_pdu_offsets *last_pdu_offsets); static guint32 dissect_acn_sdt_wrapped_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, int offset, acn_pdu_offsets *last_pdu_offsets); static guint32 dissect_acn_sdt_client_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, acn_pdu_offsets *last_pdu_offsets); static guint32 dissect_acn_dmx_data_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, acn_pdu_offsets *last_pdu_offsets); static guint32 dissect_acn_dmx_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, acn_pdu_offsets *last_pdu_offsets); static guint32 dissect_acn_sdt_base_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, acn_pdu_offsets *last_pdu_offsets); static guint32 dissect_acn_root_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, acn_pdu_offsets *last_pdu_offsets); static int dissect_acn(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); static gboolean is_acn(tvbuff_t *tvb); /* Global variables */ static int proto_acn = -1; static gint ett_acn = -1; static gint ett_acn_channel_owner_info_block = -1; static gint ett_acn_channel_member_info_block = -1; static gint ett_acn_channel_parameter = -1; static gint ett_acn_address = -1; static gint ett_acn_address_type = -1; static gint ett_acn_pdu_flags = -1; static gint ett_acn_dmp_pdu = -1; static gint ett_acn_sdt_pdu = -1; static gint ett_acn_sdt_client_pdu = -1; static gint ett_acn_sdt_base_pdu = -1; static gint ett_acn_root_pdu = -1; static gint ett_acn_dmx_address = -1; static gint ett_acn_dmx_data_pdu = -1; static gint ett_acn_dmx_pdu = -1; /* Register fields */ /* In alphabetical order */ static int hf_acn_association = -1; static int hf_acn_channel_number = -1; static int hf_acn_cid = -1; static int hf_acn_client_protocol_id = -1; static int hf_acn_data = -1; static int hf_acn_data8 = -1; static int hf_acn_data16 = -1; static int hf_acn_data24 = -1; static int hf_acn_data32 = -1; static int hf_acn_dmp_address1 = -1; static int hf_acn_dmp_address2 = -1; static int hf_acn_dmp_address4 = -1; static int hf_acn_dmp_adt = -1; /* address and data type*/ static int hf_acn_dmp_adt_a = -1; static int hf_acn_dmp_adt_v = -1; static int hf_acn_dmp_adt_r = -1; static int hf_acn_dmp_adt_d = -1; static int hf_acn_dmp_adt_x = -1; static int hf_acn_dmp_reason_code = -1; static int hf_acn_dmp_vector = -1; static int hf_acn_dmp_address_data_pairs = -1; static int hf_acn_expiry = -1; static int hf_acn_first_memeber_to_ack = -1; static int hf_acn_first_missed_sequence = -1; static int hf_acn_ip_address_type = -1; static int hf_acn_ipv4 = -1; static int hf_acn_ipv6 = -1; static int hf_acn_last_memeber_to_ack = -1; static int hf_acn_last_missed_sequence = -1; static int hf_acn_mak_threshold = -1; static int hf_acn_member_id = -1; static int hf_acn_nak_holdoff = -1; static int hf_acn_nak_max_wait = -1; static int hf_acn_nak_modulus = -1; static int hf_acn_nak_outbound_flag = -1; static int hf_acn_oldest_available_wrapper = -1; static int hf_acn_packet_identifier = -1; static int hf_acn_pdu = -1; static int hf_acn_pdu_flag_d = -1; static int hf_acn_pdu_flag_h = -1; static int hf_acn_pdu_flag_l = -1; static int hf_acn_pdu_flag_v = -1; static int hf_acn_pdu_flags = -1; static int hf_acn_pdu_length = -1; static int hf_acn_port = -1; static int hf_acn_postamble_size = -1; static int hf_acn_preamble_size = -1; static int hf_acn_protocol_id = -1; static int hf_acn_reason_code = -1; static int hf_acn_reciprocal_channel = -1; static int hf_acn_refuse_code = -1; static int hf_acn_reliable_sequence_number = -1; /* static int hf_acn_sdt_pdu = -1; */ static int hf_acn_sdt_vector = -1; static int hf_acn_dmx_vector = -1; static int hf_acn_session_count = -1; static int hf_acn_total_sequence_number = -1; static int hf_acn_dmx_source_name = -1; static int hf_acn_dmx_priority = -1; static int hf_acn_dmx_sequence_number = -1; static int hf_acn_dmx_universe = -1; static int hf_acn_dmx_start_code = -1; static int hf_acn_dmx_increment = -1; static int hf_acn_dmx_count = -1; /* static int hf_acn_dmx_dmp_vector = -1; */ /* Try heuristic ACN decode */ static gboolean global_acn_heur = FALSE; static gboolean global_acn_dmx_enable = FALSE; static gint global_acn_dmx_display_view = 0; static gint global_acn_dmx_display_line_format = 0; static gboolean global_acn_dmx_display_zeros = FALSE; static gboolean global_acn_dmx_display_leading_zeros = FALSE; static const value_string acn_protocol_id_vals[] = { { ACN_PROTOCOL_ID_SDT, "SDT Protocol" }, { ACN_PROTOCOL_ID_DMP, "DMP Protocol" }, { ACN_PROTOCOL_ID_DMX, "DMX Protocol" }, { 0, NULL }, }; static const value_string acn_dmp_adt_r_vals[] = { { 0, "Relative" }, { 1, "Absolute" }, { 0, NULL }, }; static const value_string acn_dmp_adt_v_vals[] = { { 0, "Actual" }, { 1, "Virtual" }, { 0, NULL }, }; static const value_string acn_dmp_adt_d_vals[] = { { ACN_DMP_ADT_D_NS, "Non-range, single data item" }, { ACN_DMP_ADT_D_RS, "Range, single data item" }, { ACN_DMP_ADT_D_RE, "Range, array of equal size data items" }, { ACN_DMP_ADT_D_RM, "Range, series of mixed size data items" }, { 0, NULL }, }; static const value_string acn_dmp_adt_a_vals[] = { { ACN_DMP_ADT_A_1, "1 octet" }, { ACN_DMP_ADT_A_2, "2 octets" }, { ACN_DMP_ADT_A_4, "4 octets" }, { ACN_DMP_ADT_A_R, "reserved" }, { 0, NULL }, }; static const value_string acn_sdt_vector_vals[] = { {ACN_SDT_VECTOR_UNKNOWN, "Unknown"}, {ACN_SDT_VECTOR_REL_WRAP, "Reliable Wrapper"}, {ACN_SDT_VECTOR_UNREL_WRAP, "Unreliable Wrapper"}, {ACN_SDT_VECTOR_CHANNEL_PARAMS, "Channel Parameters"}, {ACN_SDT_VECTOR_JOIN, "Join"}, {ACN_SDT_VECTOR_JOIN_REFUSE, "Join Refuse"}, {ACN_SDT_VECTOR_JOIN_ACCEPT, "Join Accept"}, {ACN_SDT_VECTOR_LEAVE, "Leave"}, {ACN_SDT_VECTOR_LEAVING, "Leaving"}, {ACN_SDT_VECTOR_CONNECT, "Connect"}, {ACN_SDT_VECTOR_CONNECT_ACCEPT, "Connect Accept"}, {ACN_SDT_VECTOR_CONNECT_REFUSE, "Connect Refuse"}, {ACN_SDT_VECTOR_DISCONNECT, "Disconnect"}, {ACN_SDT_VECTOR_DISCONNECTING, "Disconnecting"}, {ACN_SDT_VECTOR_ACK, "Ack"}, {ACN_SDT_VECTOR_NAK, "Nak"}, {ACN_SDT_VECTOR_GET_SESSION, "Get Session"}, {ACN_SDT_VECTOR_SESSIONS, "Sessions"}, { 0, NULL }, }; static const value_string acn_dmx_vector_vals[] = { {ACN_DMX_VECTOR, "Streaming DMX"}, { 0, NULL }, }; static const value_string acn_dmp_vector_vals[] = { {ACN_DMP_VECTOR_UNKNOWN, "Unknown"}, {ACN_DMP_VECTOR_GET_PROPERTY, "Get Property"}, {ACN_DMP_VECTOR_SET_PROPERTY, "Set Property"}, {ACN_DMP_VECTOR_GET_PROPERTY_REPLY, "Get property reply"}, {ACN_DMP_VECTOR_EVENT, "Event"}, {ACN_DMP_VECTOR_MAP_PROPERTY, "Map Property"}, {ACN_DMP_VECTOR_UNMAP_PROPERTY, "Unmap Property"}, {ACN_DMP_VECTOR_SUBSCRIBE, "Subscribe"}, {ACN_DMP_VECTOR_UNSUBSCRIBE, "Unsubscribe"}, {ACN_DMP_VECTOR_GET_PROPERTY_FAIL, "Get Property Fail"}, {ACN_DMP_VECTOR_SET_PROPERTY_FAIL, "Set Property Fail"}, {ACN_DMP_VECTOR_MAP_PROPERTY_FAIL, "Map Property Fail"}, {ACN_DMP_VECTOR_SUBSCRIBE_ACCEPT, "Subscribe Accept"}, {ACN_DMP_VECTOR_SUBSCRIBE_REJECT, "Subscribe Reject"}, {ACN_DMP_VECTOR_ALLOCATE_MAP, "Allocate Map"}, {ACN_DMP_VECTOR_ALLOCATE_MAP_REPLY, "Allocate Map Reply"}, {ACN_DMP_VECTOR_DEALLOCATE_MAP, "Deallocate Map"}, { 0, NULL }, }; static const value_string acn_ip_address_type_vals[] = { { ACN_ADDR_NULL, "Null"}, { ACN_ADDR_IPV4, "IPv4"}, { ACN_ADDR_IPV6, "IPv6"}, { ACN_ADDR_IPPORT, "Port"}, { 0, NULL }, }; static const value_string acn_refuse_code_vals[] = { { ACN_REFUSE_CODE_NONSPECIFIC, "Nonspecific" }, { ACN_REFUSE_CODE_ILLEGAL_PARAMS, "Illegal Parameters" }, { ACN_REFUSE_CODE_LOW_RESOURCES, "Low Resources" }, { ACN_REFUSE_CODE_ALREADY_MEMBER, "Already Member" }, { ACN_REFUSE_CODE_BAD_ADDR_TYPE, "Bad Address Type" }, { ACN_REFUSE_CODE_NO_RECIP_CHAN, "No Reciprocal Channel" }, { 0, NULL }, }; static const value_string acn_reason_code_vals[] = { { ACN_REASON_CODE_NONSPECIFIC, "Nonspecific" }, { ACN_REASON_CODE_NO_RECIP_CHAN, "No Reciprocal Channel" }, { ACN_REASON_CODE_CHANNEL_EXPIRED, "Channel Expired" }, { ACN_REASON_CODE_LOST_SEQUENCE, "Lost Sequence" }, { ACN_REASON_CODE_SATURATED, "Saturated" }, { ACN_REASON_CODE_TRANS_ADDR_CHANGING, "Transport Address Changing" }, { ACN_REASON_CODE_ASKED_TO_LEAVE, "Asked to Leave" }, { ACN_REASON_CODE_NO_RECIPIENT, "No Recipient"}, { 0, NULL }, }; static const value_string acn_dmp_reason_code_vals[] = { { ACN_DMP_REASON_CODE_NONSPECIFIC, "Nonspecific" }, { ACN_DMP_REASON_CODE_NOT_A_PROPERTY, "Not a Property" }, { ACN_DMP_REASON_CODE_WRITE_ONLY, "Write Only" }, { ACN_DMP_REASON_CODE_NOT_WRITABLE, "Not Writable" }, { ACN_DMP_REASON_CODE_DATA_ERROR, "Data Error" }, { ACN_DMP_REASON_CODE_MAPS_NOT_SUPPORTED, "Maps not Supported" }, { ACN_DMP_REASON_CODE_SPACE_NOT_AVAILABLE, "Space not Available" }, { ACN_DMP_REASON_CODE_PROP_NOT_MAPPABLE, "Property not Mappable"}, { ACN_DMP_REASON_CODE_MAP_NOT_ALLOCATED, "Map not Allocated"}, { ACN_DMP_REASON_CODE_SUBSCRIPTION_NOT_SUPPORTED, "Subscription not Supported"}, { ACN_DMP_REASON_CODE_NO_SUBSCRIPTIONS_SUPPORTED, "No Subscriptions Supported"}, { 0, NULL }, }; static const enum_val_t dmx_display_view[] = { { "hex" , "Hex ", ACN_PREF_DMX_DISPLAY_HEX }, { "decimal", "Decimal", ACN_PREF_DMX_DISPLAY_DEC }, { "percent", "Percent", ACN_PREF_DMX_DISPLAY_PER }, { NULL, NULL, 0 } }; static const enum_val_t dmx_display_line_format[] = { { "20 per line", "20 per line", ACN_PREF_DMX_DISPLAY_20PL }, { "16 per line", "16 per line", ACN_PREF_DMX_DISPLAY_16PL }, { NULL, NULL, 0 } }; /******************************************************************************/ /* Test to see if it is an ACN Packet */ static gboolean is_acn(tvbuff_t *tvb) { static char acn_packet_id[] = "ASC-E1.17\0\0\0"; /* must be 12 bytes */ guint8 *packet_id; /* Get the fields in octets 2 - 12 octet */ packet_id = tvb_get_ephemeral_string(tvb, 4, 12); if (memcmp(packet_id, &acn_packet_id, 12) == 0) { return TRUE; } return FALSE; } /******************************************************************************/ /* Heuristic dissector */ static gboolean dissect_acn_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ) { /* This is a heuristic dissector, which means we get all the UDP * traffic not sent to a known dissector and not claimed by * a heuristic dissector called before us! */ /* abort if not enabled! */ if (!global_acn_heur) return FALSE; /* abort if it is NOT an ACN packet */ if (!is_acn(tvb)) return FALSE; /* else, dissect it */ dissect_acn(tvb, pinfo, tree); return TRUE; } /******************************************************************************/ /* Adds tree branch for channel owner info block */ static guint32 acn_add_channel_owner_info_block(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, int offset) { proto_item *pi; proto_tree *this_tree = NULL; guint32 session_count; guint32 x; pi = proto_tree_add_text(this_tree, tvb, offset, 8, "Channel Owner Info Block"); this_tree = proto_item_add_subtree(pi, ett_acn_channel_owner_info_block); proto_tree_add_item(this_tree, hf_acn_member_id, tvb, offset, 2, FALSE); offset += 2; proto_tree_add_item(this_tree, hf_acn_channel_number, tvb, offset, 2, FALSE); offset += 2; offset += acn_add_address(tvb, pinfo, this_tree, offset, "Destination Address:"); offset += acn_add_address(tvb, pinfo, this_tree, offset, "Source Address:"); session_count = tvb_get_ntohs(tvb, offset); for (x=0; xflags = tvb_get_guint8(tvb, offset); D = ACN_DMP_ADT_EXTRACT_D(adt->flags); name = val_to_str(D, acn_dmp_adt_d_vals, "not valid (%d)"); pi = proto_tree_add_text(tree, tvb, offset, 1, "Address and Data Type: %s", name); this_tree = proto_item_add_subtree(pi, ett_acn_address_type); proto_tree_add_uint(this_tree, hf_acn_dmp_adt_v, tvb, offset, 1, adt->flags); proto_tree_add_uint(this_tree, hf_acn_dmp_adt_r, tvb, offset, 1, adt->flags); proto_tree_add_uint(this_tree, hf_acn_dmp_adt_d, tvb, offset, 1, adt->flags); proto_tree_add_uint(this_tree, hf_acn_dmp_adt_x, tvb, offset, 1, adt->flags); proto_tree_add_uint(this_tree, hf_acn_dmp_adt_a, tvb, offset, 1, adt->flags); offset++; return offset; /* bytes used */ } /******************************************************************************/ /* Add an dmp address */ static guint32 acn_add_dmp_address(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, acn_dmp_adt_type *adt) { guint32 start_offset; guint32 bytes_used; guint8 D, A; start_offset = offset; D = ACN_DMP_ADT_EXTRACT_D(adt->flags); A = ACN_DMP_ADT_EXTRACT_A(adt->flags); switch (D) { case ACN_DMP_ADT_D_NS: /* Non-range address, Single data item */ adt->increment = 1; adt->count = 1; switch (A) { /* address */ case ACN_DMP_ADT_A_1: /* One octet address, (range: one octet address, increment, and count). */ adt->address = tvb_get_guint8(tvb, offset); offset += 1; bytes_used = 1; break; case ACN_DMP_ADT_A_2: /* Two octet address, (range: two octet address, increment, and count). */ adt->address = tvb_get_ntohs(tvb, offset); offset += 2; bytes_used = 2; break; case ACN_DMP_ADT_A_4: /* Four octet address, (range: one octet address, increment, and count). */ adt->address = tvb_get_ntohl(tvb, offset); offset += 4; bytes_used = 4; break; default: /* and ACN_DMP_ADT_A_R (Four octet address, (range: four octet address, increment, and count)*/ return offset; } /* of switch (A) */ if (adt->flags & ACN_DMP_ADT_FLAG_V) { proto_tree_add_text(tree, tvb, start_offset, bytes_used, "Virtual Address: 0x%X", adt->address); } else { proto_tree_add_text(tree, tvb, start_offset, bytes_used, "Actual Address: 0x%X", adt->address); } break; case ACN_DMP_ADT_D_RS: /* Range address, Single data item */ switch (A) { case ACN_DMP_ADT_A_1: /* One octet address, (range: one octet address, increment, and count). */ adt->address = tvb_get_guint8(tvb, offset); offset += 1; adt->increment = tvb_get_guint8(tvb, offset); offset += 1; adt->count = tvb_get_guint8(tvb, offset); offset += 1; bytes_used = 3; break; case ACN_DMP_ADT_A_2: /* Two octet address, (range: two octet address, increment, and count). */ adt->address = tvb_get_ntohs(tvb, offset); offset += 2; adt->increment = tvb_get_ntohs(tvb, offset); offset += 2; adt->count = tvb_get_ntohs(tvb, offset); offset += 2; bytes_used = 6; break; case ACN_DMP_ADT_A_4: /* Four octet address, (range: four octet address, increment, and count). */ adt->address = tvb_get_ntohl(tvb, offset); offset += 4; adt->increment = tvb_get_ntohl(tvb, offset); offset += 4; adt->count = tvb_get_ntohl(tvb, offset); offset += 4; bytes_used = 12; break; default: /* and ACN_DMP_ADT_A_R, this reserved....so it has no meaning yet */ return offset; } /* of switch (A) */ if (adt->flags & ACN_DMP_ADT_FLAG_V) { proto_tree_add_text(tree, tvb, start_offset, bytes_used, "Virtual Address first: 0x%X, inc: %d, count: %d", adt->address, adt->increment, adt->count); } else { proto_tree_add_text(tree, tvb, start_offset, bytes_used, "Actual Address first: 0x%X, inc: %d, count: %d", adt->address, adt->increment, adt->count); } break; case ACN_DMP_ADT_D_RE: /* Range address, Array of equal size data items */ switch (A) { case ACN_DMP_ADT_A_1: /* One octet address, (range: one octet address, increment, and count). */ adt->address = tvb_get_guint8(tvb, offset); offset += 1; adt->increment = tvb_get_guint8(tvb, offset); offset += 1; adt->count = tvb_get_guint8(tvb, offset); offset += 1; bytes_used = 3; break; case ACN_DMP_ADT_A_2: /* Two octet address, (range: two octet address, increment, and count). */ adt->address = tvb_get_ntohs(tvb, offset); offset += 2; adt->increment = tvb_get_ntohs(tvb, offset); offset += 2; adt->count = tvb_get_ntohs(tvb, offset); offset += 2; bytes_used = 6; break; case ACN_DMP_ADT_A_4: /* Four octet address, (range: four octet address, increment, and count). */ adt->address = tvb_get_ntohl(tvb, offset); offset += 4; adt->increment = tvb_get_ntohl(tvb, offset); offset += 4; adt->count = tvb_get_ntohl(tvb, offset); offset += 4; bytes_used = 12; break; default: /* and ACN_DMP_ADT_A_R, this reserved....so it has no meaning yet */ return offset; } /* of switch (A) */ if (adt->flags & ACN_DMP_ADT_FLAG_V) { proto_tree_add_text(tree, tvb, start_offset, bytes_used, "Virtual Address first: 0x%X, inc: %d, count: %d", adt->address, adt->increment, adt->count); } else { proto_tree_add_text(tree, tvb, start_offset, bytes_used, "Actual Address first: 0x%X, inc: %d, count: %d", adt->address, adt->increment, adt->count); } break; case ACN_DMP_ADT_D_RM: /* Range address, Series of mixed size data items */ switch (A) { case ACN_DMP_ADT_A_1: /* One octet address, (range: one octet address, increment, and count). */ adt->address = tvb_get_guint8(tvb, offset); offset += 1; adt->increment = tvb_get_guint8(tvb, offset); offset += 1; adt->count = tvb_get_guint8(tvb, offset); offset += 1; bytes_used = 3; break; case ACN_DMP_ADT_A_2: /* Two octet address, (range: two octet address, increment, and count). */ adt->address = tvb_get_ntohs(tvb, offset); offset += 2; adt->increment = tvb_get_ntohs(tvb, offset); offset += 2; adt->count = tvb_get_ntohs(tvb, offset); offset += 2; bytes_used = 6; break; case ACN_DMP_ADT_A_4: /* Four octet address, (range: four octet address, increment, and count). */ adt->address = tvb_get_ntohl(tvb, offset); offset += 4; adt->increment = tvb_get_ntohl(tvb, offset); offset += 4; adt->count = tvb_get_ntohl(tvb, offset); offset += 4; bytes_used = 12; break; default: /* and ACN_DMP_ADT_A_R, this reserved....so it has no meaning yet */ return offset; } /* of switch (A) */ if (adt->flags & ACN_DMP_ADT_FLAG_V) { proto_tree_add_text(tree, tvb, start_offset, bytes_used, "Virtual Address first: 0x%X, inc: %d, count: %d", adt->address, adt->increment, adt->count); } else { proto_tree_add_text(tree, tvb, start_offset, bytes_used, "Actual Address first: 0x%X, inc: %d, count: %d", adt->address, adt->increment, adt->count); } break; } /* of switch (D) */ return offset; } /*******************************************************************************/ /* Display DMP Data */ #define BUFFER_SIZE 128 static guint32 acn_add_dmp_data(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, acn_dmp_adt_type *adt) { guint8 D, A; guint32 start_offset; guint32 data_size; guint32 data_value; guint32 data_address; guint32 x,y; gchar buffer[BUFFER_SIZE]; proto_item *ti; guint32 ok_to_process = FALSE; start_offset = offset; buffer[0] = 0; /* We would like to rip through Property Address-Data pairs */ /* but since we don't now how many there are nor how big the data size is, */ /* it not possible. So, we just show the whole thing as a block of date! */ /* */ /* There are a few exceptions however */ /* 1) if the address type is ACN_DMP_ADT_D_NS or ACN_DMP_ADT_D_RS and */ /* or ACN_DMP_ADT_D_RE */ /* then number of bytes is <= count + 4. Each value is at least one byte */ /* and another address/data pair is at least 4 bytes so if the remaining */ /* bytes is less than the count plus 4 then the remaining data */ /* must be all data */ /* */ /* 2) if the address type is ACN_DMP_ADT_D_RE and the number of bytes */ /* equals the number of bytes in remaining in the pdu then there is */ /* a 1 to one match */ D = ACN_DMP_ADT_EXTRACT_D(adt->flags); switch (D) { case ACN_DMP_ADT_D_NS: case ACN_DMP_ADT_D_RS: if (adt->data_length <= adt->count + 4) { ok_to_process = TRUE; } break; case ACN_DMP_ADT_D_RE: if (adt->data_length == adt->count) { ok_to_process = TRUE; } if (adt->data_length <= adt->count + 4) { ok_to_process = TRUE; } break; } if (!ok_to_process) { data_size = adt->data_length; ti = proto_tree_add_item(tree, hf_acn_data, tvb, offset, data_size, FALSE); offset += data_size; proto_item_set_text(ti, "Data and more Address-Data Pairs (further dissection not possible)"); return offset; } A = ACN_DMP_ADT_EXTRACT_A(adt->flags); switch (D) { case ACN_DMP_ADT_D_NS: /* Non-range address, Single data item */ /* calculate data size */ data_size = adt->data_length; data_address = adt->address; switch (A) { case ACN_DMP_ADT_A_1: /* One octet address, (range: one octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%2.2X ->", data_address); break; case ACN_DMP_ADT_A_2: /* Two octet address, (range: two octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%4.4X ->", data_address); break; case ACN_DMP_ADT_A_4: /* Four octet address, (range: four octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%8.8X ->", data_address); break; default: /* and ACN_DMP_ADT_A_R, this reserved....so it has no meaning yet */ offset += data_size; return offset; } switch (data_size) { case 1: data_value = tvb_get_guint8(tvb, offset); proto_tree_add_uint_format(tree, hf_acn_data8, tvb, offset, 1, data_value, "%s %2.2X", buffer, data_value); break; case 2: data_value = tvb_get_ntohs(tvb, offset); proto_tree_add_uint_format(tree, hf_acn_data16, tvb, offset, 2, data_value, "%s %4.4X", buffer, data_value); break; case 3: data_value = tvb_get_ntoh24(tvb, offset); proto_tree_add_uint_format(tree, hf_acn_data24, tvb, offset, 3, data_value, "%s %6.6X", buffer, data_value); break; case 4: data_value = tvb_get_ntohl(tvb, offset); proto_tree_add_uint_format(tree, hf_acn_data32, tvb, offset, 4, data_value, "%s %8.8X", buffer, data_value); break; default: /* build string of values */ for (y=0;y<20 && ydata_length; data_address = adt->address; for (x=0;xcount;x++) { switch (A) { case ACN_DMP_ADT_A_1: /* One octet address, (range: one octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%2.2X ->", data_address); break; case ACN_DMP_ADT_A_2: /* Two octet address, (range: two octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%4.4X ->", data_address); break; case ACN_DMP_ADT_A_4: /* Four octet address, (range: four octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%8.8X ->", data_address); break; default: /* and ACN_DMP_ADT_A_R, this reserved....so it has no meaning yet */ return offset; } switch (data_size) { case 1: data_value = tvb_get_guint8(tvb, offset); proto_tree_add_uint_format(tree, hf_acn_data8, tvb, offset, 1, data_value, "%s %2.2X", buffer, data_value); break; case 2: data_value = tvb_get_ntohs(tvb, offset); proto_tree_add_uint_format(tree, hf_acn_data8, tvb, offset, 2, data_value, "%s %4.4X", buffer, data_value); break; case 3: data_value = tvb_get_ntoh24(tvb, offset); proto_tree_add_uint_format(tree, hf_acn_data8, tvb, offset, 3, data_value, "%s %6.6X", buffer, data_value); break; case 4: data_value = tvb_get_ntohl(tvb, offset); proto_tree_add_uint_format(tree, hf_acn_data8, tvb, offset, 4, data_value, "%s %8.8X", buffer, data_value); break; default: /* build string of values */ for (y=0;y<20 && yincrement; } /* of (x=0;xcount;x++) */ offset += data_size; break; case ACN_DMP_ADT_D_RE: /* Range address, Array of equal size data items */ /* calculate data size */ data_size = adt->data_length / adt->count; data_address = adt->address; for (x=0;xcount;x++) { switch (A) { case ACN_DMP_ADT_A_1: /* One octet address, (range: one octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%2.2X ->", data_address); break; case ACN_DMP_ADT_A_2: /* Two octet address, (range: two octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%4.4X ->", data_address); break; case ACN_DMP_ADT_A_4: /* Four octet address, (range: four octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%8.8X ->", data_address); break; default: /* and ACN_DMP_ADT_A_R, this reserved....so it has no meaning yet */ return offset; } switch (data_size) { case 1: data_value = tvb_get_guint8(tvb, offset); proto_tree_add_uint_format(tree, hf_acn_data8, tvb, offset, 1, data_value, "%s %2.2X", buffer, data_value); break; case 2: data_value = tvb_get_ntohs(tvb, offset); proto_tree_add_uint_format(tree, hf_acn_data8, tvb, offset, 2, data_value, "%s %4.4X", buffer, data_value); break; case 3: data_value = tvb_get_ntoh24(tvb, offset); proto_tree_add_uint_format(tree, hf_acn_data8, tvb, offset, 3, data_value, "%s %6.6X", buffer, data_value); break; case 4: data_value = tvb_get_ntohl(tvb, offset); proto_tree_add_uint_format(tree, hf_acn_data8, tvb, offset, 4, data_value, "%s %8.8X", buffer, data_value); break; default: /* build string of values */ for (y=0;y<20 && yincrement; } /* of (x=0;xcount;x++) */ break; case ACN_DMP_ADT_D_RM: /* Range address, Series of mixed size data items */ data_size = adt->data_length; ti = proto_tree_add_item(tree, hf_acn_data, tvb, offset, data_size, FALSE); offset += data_size; /* change the text */ proto_item_set_text(ti, "Mixed size data items"); break; } /* of switch (D) */ return offset; } /*******************************************************************************/ /* Display DMP Reason codes */ #define BUFFER_SIZE 128 static guint32 acn_add_dmp_reason_codes(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, acn_dmp_adt_type *adt) { guint8 D, A; guint32 start_offset; guint32 data_value; guint32 data_address; guint32 x; gchar buffer[BUFFER_SIZE]; const gchar *name; start_offset = offset; buffer[0] = 0; D = ACN_DMP_ADT_EXTRACT_D(adt->flags); A = ACN_DMP_ADT_EXTRACT_A(adt->flags); switch (D) { case ACN_DMP_ADT_D_NS: /* Non-range address, Single data item */ data_address = adt->address; switch (A) { case ACN_DMP_ADT_A_1: /* One octet address, (range: one octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%2.2X ->", data_address); break; case ACN_DMP_ADT_A_2: /* Two octet address, (range: two octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%4.4X ->", data_address); break; case ACN_DMP_ADT_A_4: /* Four octet address, (range: four octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%8.8X ->", data_address); break; default: /* and ACN_DMP_ADT_A_R, this reserved....so it has no meaning yet */ return offset; } /* Get reason */ data_value = tvb_get_guint8(tvb, offset); /* convert to string */ name = val_to_str(data_value, acn_dmp_reason_code_vals, "reason not valid (%d)"); /* Add item */ proto_tree_add_uint_format(tree, hf_acn_data8, tvb, offset, 1, data_value, "%s %s", buffer, name); offset++; break; case ACN_DMP_ADT_D_RS: /* Range address, Single data item */ data_address = adt->address; for (x=0;xcount;x++) { switch (A) { case ACN_DMP_ADT_A_1: /* One octet address, (range: one octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%2.2X ->", data_address); break; case ACN_DMP_ADT_A_2: /* Two octet address, (range: two octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%4.4X ->", data_address); break; case ACN_DMP_ADT_A_4: /* Four octet address, (range: four octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%8.8X ->", data_address); break; default: /* and ACN_DMP_ADT_A_R, this reserved....so it has no meaning yet */ return offset; } /* Get reason */ data_value = tvb_get_guint8(tvb, offset); /* convert to string */ name = val_to_str(data_value, acn_dmp_reason_code_vals, "reason not valid (%d)"); /* Add item */ proto_tree_add_uint_format(tree, hf_acn_data8, tvb, offset, 1, data_value, "%s %s", buffer, name); data_address += adt->increment; } /* of (x=0;xcount;x++) */ offset++; break; case ACN_DMP_ADT_D_RE: /* Range address, Array of equal size data items */ case ACN_DMP_ADT_D_RM: /* Range address, Series of mixed size data items */ data_address = adt->address; for (x=0;xcount;x++) { switch (A) { case ACN_DMP_ADT_A_1: /* One octet address, (range: one octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%2.2X ->", data_address); break; case ACN_DMP_ADT_A_2: /* Two octet address, (range: two octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%4.4X ->", data_address); break; case ACN_DMP_ADT_A_4: /* Four octet address, (range: four octet address, increment, and count). */ g_snprintf(buffer, BUFFER_SIZE, "Addr 0x%8.8X ->", data_address); break; default: /* and ACN_DMP_ADT_A_R, this reserved....so it has no meaning yet */ return offset; } /* Get reason */ data_value = tvb_get_guint8(tvb, offset); /* convert to string */ name = val_to_str(data_value, acn_dmp_reason_code_vals, "reason not valid (%d)"); /* Add item */ proto_tree_add_uint_format(tree, hf_acn_data8, tvb, offset, 1, data_value, "%s %s", buffer, name); data_address += adt->increment; offset++; } /* of (x=0;xcount;x++) */ break; } /* of switch (D) */ return offset; } /******************************************************************************/ /* Dissect wrapped SDT PDU */ static guint32 dissect_acn_dmp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, acn_pdu_offsets *last_pdu_offsets) { /* common to all pdu */ guint8 pdu_flags; guint32 pdu_start; guint32 pdu_length; guint32 pdu_flvh_length; /* flags, length, vector, header */ acn_pdu_offsets pdu_offsets = {0,0,0,0,0}; guint8 D; guint8 octet; guint32 length1; guint32 length2; guint32 length3; guint32 vector_offset; guint32 header_offset; guint32 data_offset; guint32 old_offset; guint32 end_offset; guint32 data_length; guint32 address_count; proto_item *ti, *pi; proto_tree *pdu_tree = NULL; proto_tree *flag_tree = NULL; /* this pdu */ const gchar *name; acn_dmp_adt_type adt = {0,0,0,0,0,0}; acn_dmp_adt_type adt2 = {0,0,0,0,0,0}; guint32 vector; /* save start of pdu block */ pdu_start = offset; pdu_offsets.start = pdu_start; /* get PDU flags and length flag first */ octet = tvb_get_guint8(tvb, offset++); pdu_flags = octet & 0xf0; length1 = octet & 0x0f; /* bottom 4 bits only */ length2 = tvb_get_guint8(tvb, offset++); /* if length flag is set, then we have a 20 bit length else we have a 12 bit */ /* flvh = flags, length, vector, header */ if (pdu_flags & ACN_PDU_FLAG_L) { length3 = tvb_get_guint8(tvb, offset); offset++; pdu_length = length3 | (length2 << 8) | (length1 << 16); pdu_flvh_length = 3; } else { pdu_length = length2 | (length1 << 8); pdu_flvh_length = 2; } /* offset should now be pointing to vector (if one exists) */ /* Add pdu item and tree */ ti = proto_tree_add_item(tree, hf_acn_pdu, tvb, pdu_start, pdu_length, FALSE); pdu_tree = proto_item_add_subtree(ti, ett_acn_dmp_pdu); /* Add flag item and tree */ pi = proto_tree_add_uint(pdu_tree, hf_acn_pdu_flags, tvb, pdu_start, 1, pdu_flags); flag_tree = proto_item_add_subtree(pi, ett_acn_pdu_flags); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_l, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_v, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_h, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_d, tvb, pdu_start, 1, FALSE); /* Add PDU Length item */ proto_tree_add_uint(pdu_tree, hf_acn_pdu_length, tvb, pdu_start, pdu_flvh_length, pdu_length); /* Set vector offset */ if (pdu_flags & ACN_PDU_FLAG_V) { /* use new values */ vector_offset = offset; last_pdu_offsets->vector = offset; offset++; pdu_flvh_length++; } else { /* use last values */ vector_offset = last_pdu_offsets->vector; } /* offset should now be pointing to header (if one exists) */ /* Add Vector item */ vector = tvb_get_guint8(tvb, vector_offset); proto_tree_add_uint(pdu_tree, hf_acn_dmp_vector, tvb, vector_offset, 1, vector); /* Add Vector item to tree*/ name = val_to_str(vector, acn_dmp_vector_vals, "not valid (%d)"); proto_item_append_text(ti, ": "); proto_item_append_text(ti, "%s", name); /* Set header offset */ if (pdu_flags & ACN_PDU_FLAG_H) { /* use new values */ header_offset = offset; last_pdu_offsets->header = offset; offset++; pdu_flvh_length++; } else { /* use last values */ header_offset = last_pdu_offsets->header; } /* offset should now be pointing to data (if one exists) */ /* header contains address and data type */ acn_add_dmp_address_type(tvb, pinfo, pdu_tree, header_offset, &adt); /* Adjust data */ if (pdu_flags & ACN_PDU_FLAG_D) { /* use new values */ data_offset = offset; data_length = pdu_length - pdu_flvh_length; last_pdu_offsets->data = offset; last_pdu_offsets->data_length = data_length; } else { /* use last values */ data_offset = last_pdu_offsets->data; data_length = last_pdu_offsets->data_length; } end_offset = data_offset + data_length; switch (vector) { case ACN_DMP_VECTOR_UNKNOWN: break; case ACN_DMP_VECTOR_GET_PROPERTY: /* Rip trough property address */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; } break; case ACN_DMP_VECTOR_SET_PROPERTY: /* Rip through Property Address-Data pairs */ /* But, in reality, this generally won't work as we have know way of */ /* calculating the next Address-Data pair */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; adt.data_length = data_length - (data_offset - old_offset); old_offset = data_offset; data_offset = acn_add_dmp_data(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; } break; case ACN_DMP_VECTOR_GET_PROPERTY_REPLY: /* Rip through Property Address-Data pairs */ /* But, in reality, this generally won't work as we have know way of */ /* calculating the next Address-Data pair */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; adt.data_length = data_length - (data_offset - old_offset); old_offset = data_offset; data_offset = acn_add_dmp_data(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; } break; case ACN_DMP_VECTOR_EVENT: /* Rip through Property Address-Data pairs */ /* But, in reality, this generally won't work as we have know way of */ /* calculating the next Address-Data pair */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; adt.data_length = data_length - (data_offset - old_offset); old_offset = data_offset; data_offset = acn_add_dmp_data(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; } break; case ACN_DMP_VECTOR_MAP_PROPERTY: /* Virtual Address type */ data_offset = acn_add_dmp_address_type(tvb, pinfo, pdu_tree, data_offset, &adt2); /* Rip through Actual-Virtual Address Pairs */ while (data_offset < end_offset) { /* actual */ old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; D = ACN_DMP_ADT_EXTRACT_D(adt.flags); switch (D) { case ACN_DMP_ADT_D_NS: address_count = 1; break; case ACN_DMP_ADT_D_RS: address_count = 1; break; case ACN_DMP_ADT_D_RE: address_count = adt.count; break; /*case ACN_DMP_ADT_D_RM: */ default: /* OUCH */ return pdu_start + pdu_length; break; } /* virtual */ while (address_count > 0) { data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt2); address_count--; } } break; case ACN_DMP_VECTOR_UNMAP_PROPERTY: /* Rip trough Actaul Proptery Address */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; } break; case ACN_DMP_VECTOR_SUBSCRIBE: /* Rip trough Proptery Address */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; } break; case ACN_DMP_VECTOR_UNSUBSCRIBE: /* Rip trough Proptery Address */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; } break; case ACN_DMP_VECTOR_GET_PROPERTY_FAIL: /* Rip trough Address-Reason Code Pairs */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; adt.data_length = data_length - (data_offset - old_offset); old_offset = data_offset; data_offset = acn_add_dmp_reason_codes(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; } break; case ACN_DMP_VECTOR_SET_PROPERTY_FAIL: /* Rip trough Address-Reason Code Pairs */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; adt.data_length = data_length - (data_offset - old_offset); old_offset = data_offset; data_offset = acn_add_dmp_reason_codes(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; } break; case ACN_DMP_VECTOR_MAP_PROPERTY_FAIL: /* Rip trough Address-Reason Code Pairs */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; adt.data_length = data_length - (data_offset - old_offset); old_offset = data_offset; data_offset = acn_add_dmp_reason_codes(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; } break; case ACN_DMP_VECTOR_SUBSCRIBE_ACCEPT: /* Rip through Property Addrsses */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; } break; case ACN_DMP_VECTOR_SUBSCRIBE_REJECT: /* Rip trough Address-Reason Code Pairs */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = acn_add_dmp_address(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; adt.data_length = data_length - (data_offset - old_offset); old_offset = data_offset; data_offset = acn_add_dmp_reason_codes(tvb, pinfo, pdu_tree, data_offset, &adt); if (old_offset == data_offset) break; } break; case ACN_DMP_VECTOR_ALLOCATE_MAP: /* No data for this */ break; case ACN_DMP_VECTOR_ALLOCATE_MAP_REPLY: /* Single reason code */ proto_tree_add_item(pdu_tree, hf_acn_dmp_reason_code, tvb, data_offset, 1, FALSE); data_offset++; case ACN_DMP_VECTOR_DEALLOCATE_MAP: /* No data for this */ break; } return pdu_start + pdu_length; } /******************************************************************************/ /* Dissect wrapped SDT PDU */ static guint32 dissect_acn_sdt_wrapped_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, int offset, acn_pdu_offsets *last_pdu_offsets) { /* common to all pdu */ guint8 pdu_flags; guint32 pdu_start; guint32 pdu_length; guint32 pdu_flvh_length; /* flags, length, vector, header */ acn_pdu_offsets pdu_offsets = {0,0,0,0,0}; guint8 octet; guint32 length1; guint32 length2; guint32 length3; guint32 vector_offset; guint32 data_offset; guint32 end_offset; guint32 data_length; proto_item *ti, *pi; proto_tree *pdu_tree = NULL; proto_tree *flag_tree = NULL; /* this pdu */ const gchar *name; guint32 vector; /* save start of pdu block */ pdu_start = offset; pdu_offsets.start = pdu_start; /* get PDU flags and length flag first */ octet = tvb_get_guint8(tvb, offset++); pdu_flags = octet & 0xf0; length1 = octet & 0x0f; /* bottom 4 bits only */ length2 = tvb_get_guint8(tvb, offset++); /* if length flag is set, then we have a 20 bit length else we have a 12 bit */ /* flvh = flags, length, vector, header */ if (pdu_flags & ACN_PDU_FLAG_L) { length3 = tvb_get_guint8(tvb, offset); offset++; pdu_length = length3 | (length2 << 8) | (length1 << 16); pdu_flvh_length = 3; } else { pdu_length = length2 | (length1 << 8); pdu_flvh_length = 2; } /* offset should now be pointing to vector (if one exists) */ /* Add pdu item and tree */ ti = proto_tree_add_item(tree, hf_acn_pdu, tvb, pdu_start, pdu_length, FALSE); pdu_tree = proto_item_add_subtree(ti, ett_acn_sdt_pdu); /* Add flag item and tree */ pi = proto_tree_add_uint(pdu_tree, hf_acn_pdu_flags, tvb, pdu_start, 1, pdu_flags); flag_tree = proto_item_add_subtree(pi, ett_acn_pdu_flags); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_l, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_v, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_h, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_d, tvb, pdu_start, 1, FALSE); /* Add PDU Length item */ proto_tree_add_uint(pdu_tree, hf_acn_pdu_length, tvb, pdu_start, pdu_flvh_length, pdu_length); /* Set vector offset */ if (pdu_flags & ACN_PDU_FLAG_V) { /* use new values */ vector_offset = offset; last_pdu_offsets->vector = offset; offset++; pdu_flvh_length++; } else { /* use last values */ vector_offset = last_pdu_offsets->vector; } /* offset should now be pointing to header (if one exists) */ /* Add Vector item */ vector = tvb_get_guint8(tvb, vector_offset); proto_tree_add_uint(pdu_tree, hf_acn_sdt_vector, tvb, vector_offset, 1, vector); /* Add Vector item to tree*/ name = val_to_str(vector, acn_sdt_vector_vals, "not valid (%d)"); proto_item_append_text(ti, ": "); proto_item_append_text(ti, "%s", name); /* NO HEADER DATA ON THESE* (at least so far) */ /* Adjust data */ if (pdu_flags & ACN_PDU_FLAG_D) { /* use new values */ data_offset = offset; data_length = pdu_length - pdu_flvh_length; last_pdu_offsets->data = offset; last_pdu_offsets->data_length = data_length; } else { /* use last values */ data_offset = last_pdu_offsets->data; data_length = last_pdu_offsets->data_length; } end_offset = data_offset + data_length; switch (vector) { case ACN_SDT_VECTOR_ACK: proto_tree_add_item(pdu_tree, hf_acn_reliable_sequence_number, tvb, data_offset, 4, FALSE); data_offset += 4; break; case ACN_SDT_VECTOR_CHANNEL_PARAMS: data_offset = acn_add_channel_parameter(tvb, pinfo, pdu_tree, data_offset); data_offset = acn_add_address(tvb, pinfo, pdu_tree, data_offset, "Ad-hoc Address:"); data_offset = acn_add_expiry(tvb, pinfo, pdu_tree, data_offset, "Ad-hoc Expiry:"); break; case ACN_SDT_VECTOR_LEAVE: /* nothing more */ break; case ACN_SDT_VECTOR_CONNECT: /* Protocol ID item */ proto_tree_add_item(pdu_tree, hf_acn_protocol_id, tvb, data_offset, 4, FALSE); data_offset += 4; break; case ACN_SDT_VECTOR_CONNECT_ACCEPT: /* Protocol ID item */ proto_tree_add_item(pdu_tree, hf_acn_protocol_id, tvb, data_offset, 4, FALSE); data_offset += 4; break; case ACN_SDT_VECTOR_CONNECT_REFUSE: /* Protocol ID item */ proto_tree_add_item(pdu_tree, hf_acn_protocol_id, tvb, data_offset, 4, FALSE); data_offset += 4; proto_tree_add_item(pdu_tree, hf_acn_refuse_code, tvb, data_offset, 1, FALSE); data_offset++; break; case ACN_SDT_VECTOR_DISCONNECT: /* Protocol ID item */ proto_tree_add_item(pdu_tree, hf_acn_protocol_id, tvb, data_offset, 4, FALSE); data_offset += 4; break; case ACN_SDT_VECTOR_DISCONNECTING: /* Protocol ID item */ proto_tree_add_item(pdu_tree, hf_acn_protocol_id, tvb, data_offset, 4, FALSE); data_offset += 4; proto_tree_add_item(pdu_tree, hf_acn_reason_code, tvb, data_offset, 1, FALSE); data_offset++; break; } return pdu_start + pdu_length; } /******************************************************************************/ /* Dissect SDT Client PDU */ static guint32 dissect_acn_sdt_client_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, acn_pdu_offsets *last_pdu_offsets) { /* common to all pdu */ guint8 pdu_flags; guint32 pdu_start; guint32 pdu_length; guint32 pdu_flvh_length; /* flags, length, vector, header */ acn_pdu_offsets pdu_offsets = {0,0,0,0,0}; guint8 octet; guint32 length1; guint32 length2; guint32 length3; guint32 vector_offset; guint32 header_offset; guint32 data_offset; guint32 data_length; guint32 old_offset; guint32 end_offset; proto_item *ti, *pi; proto_tree *pdu_tree = NULL; proto_tree *flag_tree = NULL; /* this pdu */ const gchar *name; guint32 member_id; guint32 protocol_id; guint16 association; /* save start of pdu block */ pdu_start = offset; pdu_offsets.start = pdu_start; /* get PDU flags and length flag first */ octet = tvb_get_guint8(tvb, offset++); pdu_flags = octet & 0xf0; length1 = octet & 0x0f; /* bottom 4 bits only */ length2 = tvb_get_guint8(tvb, offset++); /* if length flag is set, then we have a 20 bit length else we have a 12 bit */ /* flvh = flags, length, vector, header */ if (pdu_flags & ACN_PDU_FLAG_L) { length3 = tvb_get_guint8(tvb, offset); offset++; pdu_length = length3 | (length2 << 8) | (length1 << 16); pdu_flvh_length = 3; } else { pdu_length = length2 | (length1 << 8); pdu_flvh_length = 2; } /* offset should now be pointing to vector (if one exists) */ /* Add pdu item and tree */ ti = proto_tree_add_item(tree, hf_acn_pdu, tvb, pdu_start, pdu_length, FALSE); pdu_tree = proto_item_add_subtree(ti, ett_acn_sdt_client_pdu); /* Add flag item and tree */ pi = proto_tree_add_uint(pdu_tree, hf_acn_pdu_flags, tvb, pdu_start, 1, pdu_flags); flag_tree = proto_item_add_subtree(pi, ett_acn_pdu_flags); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_l, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_v, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_h, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_d, tvb, pdu_start, 1, FALSE); /* Add PDU Length item */ proto_tree_add_uint(pdu_tree, hf_acn_pdu_length, tvb, pdu_start, pdu_flvh_length, pdu_length); /* Set vector offset */ if (pdu_flags & ACN_PDU_FLAG_V) { /* use new values */ vector_offset = offset; last_pdu_offsets->vector = offset; offset += 2; pdu_flvh_length += 2; } else { /* use last values */ vector_offset = last_pdu_offsets->vector; } /* offset should now be pointing to header (if one exists) */ /* add Member ID item */ member_id = tvb_get_ntohs(tvb, vector_offset); proto_tree_add_uint(pdu_tree, hf_acn_member_id, tvb, vector_offset, 2, member_id); /* Set header offset */ if (pdu_flags & ACN_PDU_FLAG_H) { /* use new values */ header_offset = offset; last_pdu_offsets->header = offset; offset += 6; pdu_flvh_length += 6; } else { /* use last values */ header_offset = last_pdu_offsets->header; } /* offset should now be pointing to data (if one exists) */ /* add Protocol ID item (Header)*/ protocol_id = tvb_get_ntohl(tvb, header_offset); proto_tree_add_uint(pdu_tree, hf_acn_protocol_id, tvb, header_offset, 4, protocol_id); header_offset += 4; /* Add protocol to tree*/ name = val_to_str(protocol_id, acn_protocol_id_vals, "id not valid (%d)"); proto_item_append_text(ti, ": "); proto_item_append_text(ti, "%s", name); /* add association item */ association = tvb_get_ntohs(tvb, header_offset); proto_tree_add_uint(pdu_tree, hf_acn_association, tvb, header_offset, 2, association); header_offset += 2; /* Adjust data */ if (pdu_flags & ACN_PDU_FLAG_D) { /* use new values */ data_offset = offset; data_length = pdu_length - pdu_flvh_length; last_pdu_offsets->data = offset; last_pdu_offsets->data_length = data_length; } else { /* use last values */ data_offset = last_pdu_offsets->data; data_length = last_pdu_offsets->data_length; } end_offset = data_offset + data_length; switch (protocol_id) { case ACN_PROTOCOL_ID_SDT: while (data_offset < end_offset) { old_offset = data_offset; data_offset = dissect_acn_sdt_wrapped_pdu(tvb, pinfo, pdu_tree, data_offset, &pdu_offsets); if (old_offset == data_offset) break; } break; case ACN_PROTOCOL_ID_DMP: while (data_offset < end_offset) { old_offset = data_offset; data_offset = dissect_acn_dmp_pdu(tvb, pinfo, pdu_tree, data_offset, &pdu_offsets); if (data_offset == old_offset) break; } break; } return pdu_start + pdu_length; } /******************************************************************************/ /* level to string (ascii) */ /* level : 8 bit value */ /* string : pointer to buffer to fill */ /* leading_char: character to buffer left of digits */ /* min_char : mininum number of characters (for filling, not including space)*/ /* show_zero: show zeros or dots */ /* also adds a space to right end */ /* */ /* returns end of string */ /* faster than printf() */ static char * ltos(guint8 level, gchar *string, guint8 base, gchar leading_char, guint8 min_chars, gboolean show_zero) { guint8 i; /* verify base */ if (base < 2 || base > 16) { *string = '\0'; return(string); } /* deal with zeros */ if ((level == 0) && (!show_zero)) { for (i=0;i 0); /* expand to needed character */ for (;ivector = offset; offset += 1; pdu_flvh_length += 1; } else { /* use last values */ vector_offset = last_pdu_offsets->vector; } /* offset should now be pointing to header (if one exists) */ /* Add Vector item */ vector = tvb_get_guint8(tvb, vector_offset); proto_tree_add_uint(pdu_tree, hf_acn_dmp_vector, tvb, vector_offset, 1, vector); /* Add Vector item to tree*/ name = val_to_str(vector, acn_dmp_vector_vals, "not valid (%d)"); proto_item_append_text(ti, ": "); proto_item_append_text(ti, "%s", name); /* Set header offset */ if (pdu_flags & ACN_PDU_FLAG_H) { /* use new values */ header_offset = offset; last_pdu_offsets->header = offset; offset++; pdu_flvh_length++; } else { /* use last values */ header_offset = last_pdu_offsets->header; } /* offset should now be pointing to data (if one exists) */ /* process based on vector */ acn_add_dmp_address_type(tvb, pinfo, pdu_tree, header_offset, &adt); /* Adjust data */ if (pdu_flags & ACN_PDU_FLAG_D) { /* use new values */ data_offset = offset; data_length = pdu_length - pdu_flvh_length; last_pdu_offsets->data = offset; last_pdu_offsets->data_length = data_length; } else { /* use last values */ data_offset = last_pdu_offsets->data; data_length = last_pdu_offsets->data_length; } end_offset = data_offset + data_length; switch (vector) { case ACN_DMP_VECTOR_SET_PROPERTY: dmx_start_code = tvb_get_ntohs(tvb, data_offset); proto_tree_add_item(pdu_tree, hf_acn_dmx_start_code, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_dmx_increment, tvb, data_offset, 2, FALSE); data_offset += 2; dmx_count = tvb_get_ntohs(tvb, data_offset); proto_tree_add_item(pdu_tree, hf_acn_dmx_count, tvb, data_offset, 2, FALSE); data_offset += 2; buf_ptr = buffer; switch (global_acn_dmx_display_line_format) { case ACN_PREF_DMX_DISPLAY_16PL: perline = 16; halfline = 8; break; default: perline = 20; halfline = 10; } /* values base on display mode */ switch ((guint)global_acn_dmx_display_view) { case ACN_PREF_DMX_DISPLAY_HEX: min_char = 2; base = 16; break; /* case ACN_PREF_DMX_DISPLAY_PER: */ default: min_char = 3; base = 10; } /* do we display leading zeros */ if (global_acn_dmx_display_leading_zeros) { leading_char = '0'; } else { leading_char = ' '; } /* add a snippet to info (this may be slow) */ if(check_col(pinfo->cinfo,COL_INFO)){ col_append_fstr(pinfo->cinfo,COL_INFO, ", Sc %02x, [%02x %02x %02x %02x %02x %02x...]", dmx_start_code, tvb_get_guint8(tvb, data_offset), tvb_get_guint8(tvb, data_offset+1), tvb_get_guint8(tvb, data_offset+2), tvb_get_guint8(tvb, data_offset+3), tvb_get_guint8(tvb, data_offset+4), tvb_get_guint8(tvb, data_offset+5)); } /* add a header line */ g_snprintf(buffer, BUFFER_SIZE, "%-10s: ", "Data..."); buf_ptr += 9; for (x=1;x<=perline;x++) { buf_ptr = ltos((guint8)x, buf_ptr, 10, ' ', min_char, FALSE); if (x==halfline) { *buf_ptr++ = '|'; *buf_ptr++ = ' '; } } *buf_ptr = '\0'; proto_tree_add_text(pdu_tree, tvb, data_offset, dmx_count, "%s", buffer); /* start our line */ g_snprintf(buffer, BUFFER_SIZE, "001-%03d: ", perline); buf_ptr = buffer + 9; total_cnt = 0; item_cnt = 0; for (x=data_offset; x < end_offset; x++) { level = tvb_get_guint8(tvb, x); if (global_acn_dmx_display_view==ACN_PREF_DMX_DISPLAY_PER) { if ((level > 0) && (level < 3)) { level = 1; } else { level = level * 100 / 255; } } buf_ptr = ltos(level, buf_ptr, base, leading_char, min_char, global_acn_dmx_display_zeros); total_cnt++; item_cnt++; if (item_cnt == perline || x == (end_offset-1)) { /* add leader... */ proto_tree_add_text(pdu_tree, tvb, data_offset, item_cnt, "%s", buffer); data_offset += perline; g_snprintf(buffer, BUFFER_SIZE, "%03d-%03d: ",total_cnt, total_cnt+perline); buf_ptr = buffer + 9; item_cnt = 0; } else { /* add separater character */ if (item_cnt == halfline) { *buf_ptr++ = '|'; *buf_ptr++ = ' '; *buf_ptr = '\0'; } } } /* NOTE: address data type (fixed at 0xA2) start code - 1 byte, reserved (should be 0) - 1 byte, start code (0x255) - 2 bytes, packet offset (should be 0000) address increment - 4 bytes (ignore) number of dmx values - 4 bytes (0-512) dmx values 0-512 bytes (data) */ break; } return pdu_start + pdu_length; } /******************************************************************************/ /* Dissect DMX Base PDU */ static guint32 dissect_acn_dmx_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, acn_pdu_offsets *last_pdu_offsets) { /* common to all pdu */ guint8 pdu_flags; guint32 pdu_start; guint32 pdu_length; guint32 pdu_flvh_length; /* flags, length, vector, header */ acn_pdu_offsets pdu_offsets = {0,0,0,0,0}; guint8 octet; guint32 length1; guint32 length2; guint32 length3; guint32 vector_offset; guint32 data_offset; guint32 end_offset; guint32 data_length; proto_item *ti, *pi; proto_tree *pdu_tree = NULL; proto_tree *flag_tree = NULL; const char *name; /* this pdu */ guint32 vector; guint32 universe; guint32 priority; guint32 sequence; /* save start of pdu block */ pdu_start = offset; pdu_offsets.start = pdu_start; /* get PDU flags and length flag first */ octet = tvb_get_guint8(tvb, offset++); pdu_flags = octet & 0xf0; length1 = octet & 0x0f; /* bottom 4 bits only */ length2 = tvb_get_guint8(tvb, offset++); /* if length flag is set, then we have a 20 bit length else we have a 12 bit */ /* flvh = flags, length, vector, header */ if (pdu_flags & ACN_PDU_FLAG_L) { length3 = tvb_get_guint8(tvb, offset); offset++; pdu_length = length3 | (length2 << 8) | (length1 << 16); pdu_flvh_length = 3; } else { pdu_length = length2 | (length1 << 8); pdu_flvh_length = 2; } /* offset should now be pointing to vector (if one exists) */ /* Add pdu item and tree */ ti = proto_tree_add_item(tree, hf_acn_pdu, tvb, pdu_start, pdu_length, FALSE); pdu_tree = proto_item_add_subtree(ti, ett_acn_dmx_pdu); /* Add flag item and tree */ pi = proto_tree_add_uint(pdu_tree, hf_acn_pdu_flags, tvb, pdu_start, 1, pdu_flags); flag_tree = proto_item_add_subtree(pi, ett_acn_pdu_flags); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_l, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_v, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_h, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_d, tvb, pdu_start, 1, FALSE); /* Add PDU Length item */ proto_tree_add_uint(pdu_tree, hf_acn_pdu_length, tvb, pdu_start, pdu_flvh_length, pdu_length); /* Set vector offset */ if (pdu_flags & ACN_PDU_FLAG_V) { /* use new values */ vector_offset = offset; last_pdu_offsets->vector = offset; offset += 4; pdu_flvh_length += 4; } else { /* use last values */ vector_offset = last_pdu_offsets->vector; } /* offset should now be pointing to header (if one exists) */ /* Add Vector item */ vector = tvb_get_ntohl(tvb, vector_offset); proto_tree_add_item(pdu_tree, hf_acn_dmx_vector, tvb, vector_offset, 4, FALSE); /* vector_offset +=4; */ /* Add Vector item to tree*/ name = val_to_str(vector, acn_dmx_vector_vals, "not valid (%d)"); proto_item_append_text(ti, ": %s", name); /* NO HEADER DATA ON THESE* (at least so far) */ /* Adjust data */ if (pdu_flags & ACN_PDU_FLAG_D) { /* use new values */ data_offset = offset; data_length = pdu_length - pdu_flvh_length; last_pdu_offsets->data = offset; last_pdu_offsets->data_length = data_length; } else { /* use last values */ data_offset = last_pdu_offsets->data; data_length = last_pdu_offsets->data_length; } end_offset = data_offset + data_length; /* process based on vector */ switch (vector) { case 0x02: proto_tree_add_item(pdu_tree, hf_acn_dmx_source_name, tvb, data_offset, 32, FALSE); data_offset += 32; priority = tvb_get_guint8(tvb, data_offset); proto_tree_add_item(pdu_tree, hf_acn_dmx_priority, tvb, data_offset, 1, FALSE); data_offset += 1; sequence = tvb_get_guint8(tvb, data_offset); proto_tree_add_item(pdu_tree, hf_acn_dmx_sequence_number, tvb, data_offset, 1, FALSE); data_offset += 1; universe = tvb_get_ntohs(tvb, data_offset); proto_tree_add_item(pdu_tree, hf_acn_dmx_universe , tvb, data_offset, 2, FALSE); data_offset += 2; /* add universe to info */ if(check_col(pinfo->cinfo,COL_INFO)){ col_append_fstr(pinfo->cinfo,COL_INFO, ", Universe %d, Seq %3d", universe, sequence ); } proto_item_append_text(ti, ", Universe: %d, Priority: %d", universe, priority); data_offset = dissect_acn_dmx_data_pdu(tvb, pinfo, pdu_tree, data_offset, &pdu_offsets); break; } return pdu_start + pdu_length; } /******************************************************************************/ /* Dissect SDT Base PDU */ static guint32 dissect_acn_sdt_base_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, acn_pdu_offsets *last_pdu_offsets) { /* common to all pdu */ guint8 pdu_flags; guint32 pdu_start; guint32 pdu_length; guint32 pdu_flvh_length; /* flags, length, vector, header */ acn_pdu_offsets pdu_offsets = {0,0,0,0,0}; guint8 octet; guint32 length1; guint32 length2; guint32 length3; guint32 vector_offset; guint32 data_offset; guint32 end_offset; guint32 old_offset; guint32 data_length; proto_item *ti, *pi; proto_tree *pdu_tree = NULL; proto_tree *flag_tree = NULL; /* this pdu */ const gchar *name; guint32 vector; guint32 member_id; /* save start of pdu block */ pdu_start = offset; pdu_offsets.start = pdu_start; /* get PDU flags and length flag first */ octet = tvb_get_guint8(tvb, offset++); pdu_flags = octet & 0xf0; length1 = octet & 0x0f; /* bottom 4 bits only */ length2 = tvb_get_guint8(tvb, offset++); /* if length flag is set, then we have a 20 bit length else we have a 12 bit */ /* flvh = flags, length, vector, header */ if (pdu_flags & ACN_PDU_FLAG_L) { length3 = tvb_get_guint8(tvb, offset); offset++; pdu_length = length3 | (length2 << 8) | (length1 << 16); pdu_flvh_length = 3; } else { pdu_length = length2 | (length1 << 8); pdu_flvh_length = 2; } /* offset should now be pointing to vector (if one exists) */ /* Add pdu item and tree */ ti = proto_tree_add_item(tree, hf_acn_pdu, tvb, pdu_start, pdu_length, FALSE); pdu_tree = proto_item_add_subtree(ti, ett_acn_sdt_base_pdu); /* Add flag item and tree */ pi = proto_tree_add_uint(pdu_tree, hf_acn_pdu_flags, tvb, pdu_start, 1, pdu_flags); flag_tree = proto_item_add_subtree(pi, ett_acn_pdu_flags); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_l, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_v, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_h, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_d, tvb, pdu_start, 1, FALSE); /* Add PDU Length item */ proto_tree_add_uint(pdu_tree, hf_acn_pdu_length, tvb, pdu_start, pdu_flvh_length, pdu_length); /* Set vector offset */ if (pdu_flags & ACN_PDU_FLAG_V) { /* use new values */ vector_offset = offset; last_pdu_offsets->vector = offset; offset++; pdu_flvh_length++; } else { /* use last values */ vector_offset = last_pdu_offsets->vector; } /* offset should now be pointing to header (if one exists) */ /* Add Vector item */ vector = tvb_get_guint8(tvb, vector_offset); proto_tree_add_uint(pdu_tree, hf_acn_sdt_vector, tvb, vector_offset, 1, vector); /* Add Vector item to tree*/ name = val_to_str(vector, acn_sdt_vector_vals, "not valid (%d)"); proto_item_append_text(ti, ": "); proto_item_append_text(ti, "%s", name); /* NO HEADER DATA ON THESE* (at least so far) */ /* Adjust data */ if (pdu_flags & ACN_PDU_FLAG_D) { /* use new values */ data_offset = offset; data_length = pdu_length - pdu_flvh_length; last_pdu_offsets->data = offset; last_pdu_offsets->data_length = data_length; } else { /* use last values */ data_offset = last_pdu_offsets->data; data_length = last_pdu_offsets->data_length; } end_offset = data_offset + data_length; /* process based on vector */ switch (vector) { case ACN_SDT_VECTOR_UNKNOWN: break; case ACN_SDT_VECTOR_REL_WRAP: case ACN_SDT_VECTOR_UNREL_WRAP: proto_tree_add_item(pdu_tree, hf_acn_channel_number, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_total_sequence_number, tvb, data_offset, 4, FALSE); data_offset += 4; proto_tree_add_item(pdu_tree, hf_acn_reliable_sequence_number, tvb, data_offset, 4, FALSE); data_offset += 4; proto_tree_add_item(pdu_tree, hf_acn_oldest_available_wrapper, tvb, data_offset, 4, FALSE); data_offset += 4; proto_tree_add_item(pdu_tree, hf_acn_first_memeber_to_ack, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_last_memeber_to_ack, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_mak_threshold, tvb, data_offset, 2, FALSE); data_offset += 2; while (data_offset < end_offset) { old_offset = data_offset; data_offset = dissect_acn_sdt_client_pdu(tvb, pinfo, pdu_tree, data_offset, &pdu_offsets); if (data_offset == old_offset) break; } break; case ACN_SDT_VECTOR_CHANNEL_PARAMS: break; case ACN_SDT_VECTOR_JOIN: proto_tree_add_item(pdu_tree, hf_acn_cid, tvb, data_offset, 16, FALSE); data_offset += 16; proto_tree_add_item(pdu_tree, hf_acn_member_id, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_channel_number, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_reciprocal_channel, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_total_sequence_number, tvb, data_offset, 4, FALSE); data_offset += 4; proto_tree_add_item(pdu_tree, hf_acn_reliable_sequence_number, tvb, data_offset, 4, FALSE); data_offset += 4; data_offset = acn_add_address(tvb, pinfo, pdu_tree, data_offset, "Destination Address:"); data_offset = acn_add_channel_parameter(tvb, pinfo, pdu_tree, data_offset); data_offset = acn_add_expiry(tvb, pinfo, pdu_tree, data_offset, "Ad-hoc Expiry:"); break; case ACN_SDT_VECTOR_JOIN_REFUSE: pi = proto_tree_add_item(pdu_tree, hf_acn_cid, tvb, data_offset, 16, FALSE); data_offset += 16; proto_item_append_text(pi, "(Leader)"); proto_tree_add_item(pdu_tree, hf_acn_channel_number, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_member_id, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_reliable_sequence_number, tvb, data_offset, 4, FALSE); data_offset += 4; proto_tree_add_item(pdu_tree, hf_acn_refuse_code, tvb, data_offset, 1, FALSE); data_offset ++; break; case ACN_SDT_VECTOR_JOIN_ACCEPT: pi = proto_tree_add_item(pdu_tree, hf_acn_cid, tvb, data_offset, 16, FALSE); data_offset += 16; proto_item_append_text(pi, "(Leader)"); proto_tree_add_item(pdu_tree, hf_acn_channel_number, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_member_id, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_reliable_sequence_number, tvb, data_offset, 4, FALSE); data_offset += 4; proto_tree_add_item(pdu_tree, hf_acn_reciprocal_channel, tvb, data_offset, 2, FALSE); data_offset += 2; break; case ACN_SDT_VECTOR_LEAVE: break; case ACN_SDT_VECTOR_LEAVING: pi = proto_tree_add_item(pdu_tree, hf_acn_cid, tvb, data_offset, 16, FALSE); data_offset += 16; proto_item_append_text(pi, "(Leader)"); proto_tree_add_item(pdu_tree, hf_acn_channel_number, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_member_id, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_reliable_sequence_number, tvb, data_offset, 4, FALSE); data_offset += 4; proto_tree_add_item(pdu_tree, hf_acn_reason_code, tvb, data_offset, 1, FALSE); offset++; break; case ACN_SDT_VECTOR_CONNECT: break; case ACN_SDT_VECTOR_CONNECT_ACCEPT: break; case ACN_SDT_VECTOR_CONNECT_REFUSE: break; case ACN_SDT_VECTOR_DISCONNECT: break; case ACN_SDT_VECTOR_DISCONNECTING: break; case ACN_SDT_VECTOR_ACK: break; case ACN_SDT_VECTOR_NAK: pi = proto_tree_add_item(pdu_tree, hf_acn_cid, tvb, data_offset, 16, FALSE); data_offset += 16; proto_item_append_text(pi, "(Leader)"); proto_tree_add_item(pdu_tree, hf_acn_channel_number, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_member_id, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(pdu_tree, hf_acn_reliable_sequence_number, tvb, data_offset, 4, FALSE); data_offset += 4; proto_tree_add_item(pdu_tree, hf_acn_first_missed_sequence, tvb, data_offset, 4, FALSE); data_offset += 4; proto_tree_add_item(pdu_tree, hf_acn_last_missed_sequence, tvb, data_offset, 4, FALSE); data_offset += 4; break; case ACN_SDT_VECTOR_GET_SESSION: proto_tree_add_item(pdu_tree, hf_acn_cid, tvb, data_offset, 16, FALSE); data_offset += 16; break; case ACN_SDT_VECTOR_SESSIONS: member_id = tvb_get_ntohs(tvb, data_offset); switch (member_id) { case 0: data_offset = acn_add_channel_owner_info_block(tvb, pinfo, pdu_tree, data_offset); break; case 1: data_offset = acn_add_channel_member_info_block(tvb, pinfo, pdu_tree, data_offset); break; } break; } return pdu_start + pdu_length; } /******************************************************************************/ /* Dissect Root PDU */ static guint32 dissect_acn_root_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, acn_pdu_offsets *last_pdu_offsets) { /* common to all pdu */ guint8 pdu_flags; guint32 pdu_start; guint32 pdu_length; guint32 pdu_flvh_length; /* flags, length, vector, header */ acn_pdu_offsets pdu_offsets = {0,0,0,0,0}; guint8 octet; guint32 length1; guint32 length2; guint32 length3; guint32 vector_offset; guint32 header_offset; guint32 data_offset; guint32 end_offset; guint32 old_offset; guint32 data_length; proto_item *ti, *pi; proto_tree *pdu_tree = NULL; proto_tree *flag_tree = NULL; /* this pdu */ guint32 protocol_id; e_guid_t guid; /* save start of pdu block */ pdu_start = offset; pdu_offsets.start = pdu_start; /* get PDU flags and length flag first */ octet = tvb_get_guint8(tvb, offset++); pdu_flags = octet & 0xf0; length1 = octet & 0x0f; /* bottom 4 bits only */ length2 = tvb_get_guint8(tvb, offset++); /* if length flag is set, then we have a 20 bit length else we have a 12 bit */ /* flvh = flags, length, vector, header */ if (pdu_flags & ACN_PDU_FLAG_L) { length3 = tvb_get_guint8(tvb, offset); offset++; pdu_length = length3 | (length2 << 8) | (length1 << 16); pdu_flvh_length = 3; } else { pdu_length = length2 | (length1 << 8); pdu_flvh_length = 2; } /* offset should now be pointing to vector (if one exists) */ /* Add pdu item and tree */ ti = proto_tree_add_item(tree, hf_acn_pdu, tvb, pdu_start, pdu_length, FALSE); pdu_tree = proto_item_add_subtree(ti, ett_acn_root_pdu); /* Add flag item and tree */ pi = proto_tree_add_uint(pdu_tree, hf_acn_pdu_flags, tvb, pdu_start, 1, pdu_flags); flag_tree = proto_item_add_subtree(pi, ett_acn_pdu_flags); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_l, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_v, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_h, tvb, pdu_start, 1, FALSE); proto_tree_add_item(flag_tree, hf_acn_pdu_flag_d, tvb, pdu_start, 1, FALSE); /* Add PDU Length item */ proto_tree_add_uint(pdu_tree, hf_acn_pdu_length, tvb, pdu_start, pdu_flvh_length, pdu_length); /* Set vector offset */ if (pdu_flags & ACN_PDU_FLAG_V) { /* use new values */ vector_offset = offset; last_pdu_offsets->vector = offset; offset += 4; pdu_flvh_length += 4; } else { /* use last values */ vector_offset = last_pdu_offsets->vector; } /* offset should now be pointing to header (if one exists) */ /* Get Protocol ID (vector) */ protocol_id = tvb_get_ntohl(tvb, vector_offset); proto_tree_add_uint(pdu_tree, hf_acn_protocol_id, tvb, vector_offset, 4, protocol_id); /* process based on protocol_id */ switch (protocol_id) { case ACN_PROTOCOL_ID_DMX: if (global_acn_dmx_enable) { proto_item_append_text(ti,": Root DMX"); /* Set header offset */ if (pdu_flags & ACN_PDU_FLAG_H) { /* use new values */ header_offset = offset; last_pdu_offsets->header = offset; offset += 16; pdu_flvh_length += 16; } else { /* use last values */ header_offset = last_pdu_offsets->header; } /* offset should now be pointing to data (if one exists) */ /* get Header (CID) 16 bytes */ tvb_get_guid(tvb, header_offset, &guid, FALSE); proto_item_append_text(ti, ", Src: %s", guid_to_str(&guid)); /* add cid to info */ if(check_col(pinfo->cinfo,COL_INFO)){ col_clear(pinfo->cinfo,COL_INFO); col_add_fstr(pinfo->cinfo,COL_INFO, "CID %s", guid_to_str(&guid)); } proto_tree_add_item(pdu_tree, hf_acn_cid, tvb, header_offset, 16, FALSE); header_offset += 16; /* Adjust data */ if (pdu_flags & ACN_PDU_FLAG_D) { /* use new values */ data_offset = offset; data_length = pdu_length - pdu_flvh_length; last_pdu_offsets->data = offset; last_pdu_offsets->data_length = data_length; } else { /* use last values */ data_offset = last_pdu_offsets->data; data_length = last_pdu_offsets->data_length; } end_offset = data_offset + data_length; /* adjust for what we used */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = dissect_acn_dmx_pdu(tvb, pinfo, pdu_tree, data_offset, &pdu_offsets); if (data_offset == old_offset) break; } } break; case ACN_PROTOCOL_ID_SDT: /* Adjust header */ proto_item_append_text(ti,": Root SDT"); /* Set header offset */ if (pdu_flags & ACN_PDU_FLAG_H) { /* use new values */ header_offset = offset; last_pdu_offsets->header = offset; offset += 16; pdu_flvh_length += 16; } else { /* use last values */ header_offset = last_pdu_offsets->header; } /* offset should now be pointing to data (if one exists) */ /* get Header (CID) 16 bytes */ tvb_get_guid(tvb, header_offset, &guid, FALSE); proto_item_append_text(ti, ", Src: %s", guid_to_str(&guid)); proto_tree_add_item(pdu_tree, hf_acn_cid, tvb, header_offset, 16, FALSE); header_offset += 16; /* Adjust data */ if (pdu_flags & ACN_PDU_FLAG_D) { /* use new values */ data_offset = offset; data_length = pdu_length - pdu_flvh_length; last_pdu_offsets->data = offset; last_pdu_offsets->data_length = data_length; } else { /* use last values */ data_offset = last_pdu_offsets->data; data_length = last_pdu_offsets->data_length; } end_offset = data_offset + data_length; /* adjust for what we used */ while (data_offset < end_offset) { old_offset = data_offset; data_offset = dissect_acn_sdt_base_pdu(tvb, pinfo, pdu_tree, data_offset, &pdu_offsets); if (data_offset == old_offset) break; } break; } return pdu_start + pdu_length; } /******************************************************************************/ /* Dissect ACN */ static int dissect_acn(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_item *ti = NULL; proto_tree *acn_tree = NULL; guint32 data_offset = 0; guint32 old_offset; guint32 end_offset; acn_pdu_offsets pdu_offsets = {0,0,0,0,0}; /* if (!is_acn(tvb)) { */ /* return 0; */ /* } */ /* Set the protocol column */ if (check_col(pinfo->cinfo, COL_PROTOCOL)) { col_set_str(pinfo->cinfo, COL_PROTOCOL, "ACN"); } /* Clear out stuff in the info column */ if(check_col(pinfo->cinfo,COL_INFO)){ /* col_clear(pinfo->cinfo,COL_INFO); */ col_add_fstr(pinfo->cinfo,COL_INFO, "ACN [Src Port: %d, Dst Port: %d]", pinfo->srcport, pinfo->destport ); } if (tree) { /* we are being asked for details */ ti = proto_tree_add_item(tree, proto_acn, tvb, 0, -1, FALSE); acn_tree = proto_item_add_subtree(ti, ett_acn); pdu_offsets.start = data_offset; /* add preamble, postamble and ACN Packet ID */ proto_tree_add_item(acn_tree, hf_acn_preamble_size, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(acn_tree, hf_acn_postamble_size, tvb, data_offset, 2, FALSE); data_offset += 2; proto_tree_add_item(acn_tree, hf_acn_packet_identifier, tvb, data_offset, 12, FALSE); data_offset += 12; /* one past the last byte */ end_offset = data_offset + tvb_reported_length_remaining(tvb, data_offset); while (data_offset < end_offset) { old_offset = data_offset; data_offset = dissect_acn_root_pdu(tvb, pinfo, acn_tree, data_offset, &pdu_offsets); if (data_offset == old_offset) break; } } return tvb_length(tvb); } /******************************************************************************/ /* Register protocol */ void proto_register_acn(void) { static hf_register_info hf[] = { /**************************************************************************/ /* In alphabetical order */ /* Address Type */ /* PDU flags*/ { &hf_acn_ip_address_type, { "Addr Type", "acn.ip_address_type", FT_UINT8, BASE_DEC, VALS(acn_ip_address_type_vals), 0x0, NULL, HFILL } }, /* Association */ { &hf_acn_association, { "Association", "acn.association", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* Channel Number */ { &hf_acn_channel_number, { "Channel Number", "acn.channel_number", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* CID */ { &hf_acn_cid, { "CID", "acn.cid", FT_GUID, BASE_NONE, NULL, 0x0, NULL, HFILL } }, /* Client Protocol ID */ { &hf_acn_client_protocol_id, { "Client Protocol ID", "acn.client_protocol_id", FT_UINT32, BASE_DEC, VALS(acn_protocol_id_vals), 0x0, NULL, HFILL } }, /* DMP data */ { &hf_acn_data, { "Data", "acn.dmp_data", FT_BYTES, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_acn_data8, { "Addr", "acn.dmp_data8", FT_UINT8, BASE_DEC_HEX, NULL, 0x0, "Data8", HFILL } }, { &hf_acn_data16, { "Addr", "acn.dmp_data16", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, "Data16", HFILL } }, { &hf_acn_data24, { "Addr", "acn.dmp_data24", FT_UINT24, BASE_DEC_HEX, NULL, 0x0, "Data24", HFILL } }, { &hf_acn_data32, { "Addr", "acn.dmp_data32", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, "Data32", HFILL } }, { &hf_acn_dmp_address_data_pairs, { "Address-Data Pairs", "acn.dmp_address_data_pairs", FT_BYTES, BASE_DEC, NULL, 0x0, "More address-data pairs", HFILL } }, /* DMP Address */ { &hf_acn_dmp_address1, { "Address", "acn.dmp_address", FT_UINT8, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_acn_dmp_address2, { "Address", "acn.dmp_address", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_acn_dmp_address4, { "Address", "acn.dmp_address", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* DMP Address type*/ { &hf_acn_dmp_adt, { "Address and Data Type", "acn.dmp_adt", FT_UINT8, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_acn_dmp_adt_a, { "Size", "acn.dmp_adt_a", FT_UINT8, BASE_DEC, VALS(acn_dmp_adt_a_vals), 0x03, NULL, HFILL } }, { &hf_acn_dmp_adt_d, { "Data Type", "acn.dmp_adt_d", FT_UINT8, BASE_DEC, VALS(acn_dmp_adt_d_vals), 0x30, NULL, HFILL } }, { &hf_acn_dmp_adt_r, { "Relative", "acn.dmp_adt_r", FT_UINT8, BASE_DEC, VALS(acn_dmp_adt_r_vals), 0x40, NULL, HFILL } }, { &hf_acn_dmp_adt_v, { "Virtual", "acn.dmp_adt_v", FT_UINT8, BASE_DEC, VALS(acn_dmp_adt_v_vals), 0x80, NULL, HFILL } }, { &hf_acn_dmp_adt_x, { "Reserved", "acn.dmp_adt_x", FT_UINT8, BASE_DEC, NULL, 0x0c, NULL, HFILL } }, /* DMP Reason Code */ { &hf_acn_dmp_reason_code, { "Reason Code", "acn.dmp_reason_code", FT_UINT8, BASE_DEC, VALS(acn_dmp_reason_code_vals), 0x0, NULL, HFILL } }, /* DMP Vector */ { &hf_acn_dmp_vector, { "DMP Vector", "acn.dmp_vector", FT_UINT8, BASE_DEC, VALS(acn_dmp_vector_vals), 0x0, NULL, HFILL } }, /* Expiry */ { &hf_acn_expiry, { "Expiry", "acn.expiry", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* First Member to ACK */ { &hf_acn_first_memeber_to_ack, { "First Member to ACK", "acn.first_member_to_ack", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* First Missed Sequence */ { &hf_acn_first_missed_sequence, { "First Missed Sequence", "acn.first_missed_sequence", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* IPV4 */ { &hf_acn_ipv4, { "IPV4", "acn.ipv4", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL } }, /* IPV6 */ { &hf_acn_ipv6, { "IPV6", "acn.ipv6", FT_IPv6, BASE_NONE, NULL, 0x0, NULL, HFILL } }, /* Last Member to ACK */ { &hf_acn_last_memeber_to_ack, { "Last Member to ACK", "acn.last_member_to_ack", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* Last Missed Sequence */ { &hf_acn_last_missed_sequence, { "Last Missed Sequence", "acn.last_missed_sequence", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* MAK threshold */ { &hf_acn_mak_threshold, { "MAK Threshold", "acn.mak_threshold", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* MemberID */ { &hf_acn_member_id, { "Member ID", "acn.member_id", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* NAK Holdoff */ { &hf_acn_nak_holdoff, { "NAK holdoff (ms)", "acn.nak_holdoff", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* NAK Max Wait */ { &hf_acn_nak_max_wait, { "NAK Max Wait (ms)", "acn.nak_max_wait", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* NAK Modulus */ { &hf_acn_nak_modulus, { "NAK Modulus", "acn.nak_modulus", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* NAK Outbound Flag */ { &hf_acn_nak_outbound_flag, { "NAK Outbound Flag", "acn.nak_outbound_flag", FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL } }, /* Oldest Available Wrapper */ { &hf_acn_oldest_available_wrapper, { "Oldest Available Wrapper", "acn.oldest_available_wrapper", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* Preamble Sizet */ { &hf_acn_preamble_size, { "Size of preamble", "acn.preamble_size", FT_UINT16, BASE_DEC, NULL, 0x0, "Preamble size in bytes", HFILL } }, /* Packet Identifier */ { &hf_acn_packet_identifier, { "Packet Identifier", "acn.packet_identifier", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, /* PDU */ { &hf_acn_pdu, { "PDU", "acn.pdu", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, /* PDU flags*/ { &hf_acn_pdu_flags, { "Flags", "acn.pdu.flags", FT_UINT8, BASE_HEX, NULL, 0x0, "PDU Flags", HFILL } }, { &hf_acn_pdu_flag_d, { "Data", "acn.pdu.flag_d", FT_BOOLEAN, 8, NULL, ACN_PDU_FLAG_D, "Data flag", HFILL } }, { &hf_acn_pdu_flag_h, { "Header", "acn.pdu.flag_h", FT_BOOLEAN, 8, NULL, ACN_PDU_FLAG_H, "Header flag", HFILL } }, { &hf_acn_pdu_flag_l, { "Length", "acn.pdu.flag_l", FT_BOOLEAN, 8, NULL, ACN_PDU_FLAG_L, "Length flag", HFILL } }, { &hf_acn_pdu_flag_v, { "Vector", "acn.pdu.flag_v", FT_BOOLEAN, 8, NULL, ACN_PDU_FLAG_V, "Vector flag", HFILL } }, /* PDU Length */ { &hf_acn_pdu_length, { "Length", "acn.pdu.flag_d", FT_UINT32, BASE_DEC, NULL, 0x0, "PDU Length", HFILL } }, /* Port */ { &hf_acn_port, { "Port", "acn.port", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* Postamble Size */ { &hf_acn_postamble_size, { "Size of postamble", "acn.postamble_size", FT_UINT16, BASE_DEC, NULL, 0x0, "Postamble size in bytes", HFILL } }, /* Protocol ID */ { &hf_acn_protocol_id, { "Protocol ID", "acn.protocol_id", FT_UINT32, BASE_DEC, VALS(acn_protocol_id_vals), 0x0, NULL, HFILL } }, /* Reason Code */ { &hf_acn_reason_code, { "Reason Code", "acn.reason_code", FT_UINT8, BASE_DEC, VALS(acn_reason_code_vals), 0x0, NULL, HFILL } }, /* Reciprocal Channel */ { &hf_acn_reciprocal_channel, { "Reciprocal Channel Number", "acn.acn_reciprocal_channel", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, "Reciprocal Channel", HFILL } }, /* Refuse Code */ { &hf_acn_refuse_code, { "Refuse Code", "acn.acn_refuse_code", FT_UINT8, BASE_DEC, VALS(acn_refuse_code_vals), 0x0, NULL, HFILL } }, /* Reliable Sequence Number */ { &hf_acn_reliable_sequence_number, { "Reliable Sequence Number", "acn.reliable_sequence_number", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* SDT Vector */ { &hf_acn_sdt_vector, { "STD Vector", "acn.sdt_vector", FT_UINT8, BASE_DEC, VALS(acn_sdt_vector_vals), 0x0, NULL, HFILL } }, /* DMX Vector */ { &hf_acn_dmx_vector, { "Vector", "acn.dmx_vector", FT_UINT32, BASE_DEC, VALS(acn_dmx_vector_vals), 0x0, "DMX Vector", HFILL } }, /* DMX Source Name */ { &hf_acn_dmx_source_name, { "Source", "acn.dmx.source_name", FT_STRING, BASE_NONE, NULL, 0x0, "DMX Source Name", HFILL } }, /* DMX priority */ { &hf_acn_dmx_priority, { "Priority", "acn.dmx.priority", FT_UINT8, BASE_DEC, NULL, 0x0, "DMX Priority", HFILL } }, /* DMX Sequence number */ { &hf_acn_dmx_sequence_number, { "Seq No", "acn.dmx.seq_number", FT_UINT8, BASE_DEC, NULL, 0x0, "DMX Sequence Number", HFILL } }, /* DMX Universe */ { &hf_acn_dmx_universe, { "Universe", "acn.dmx.universe", FT_UINT16, BASE_DEC, NULL, 0x0, "DMX Universe", HFILL } }, /* DMX Start Code */ { &hf_acn_dmx_start_code, { "Start Code", "acn.dmx.start_code", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, "DMX Start Code", HFILL } }, /* DMX Address Increment */ { &hf_acn_dmx_increment, { "Increment", "acn.dmx.increment", FT_UINT16, BASE_DEC, NULL, 0x0, "DMX Increment", HFILL } }, /* DMX Packet Count */ { &hf_acn_dmx_count, { "Count", "acn.dmx.count", FT_UINT16, BASE_DEC, NULL, 0x0, "DMX Count", HFILL } }, /* Session Count */ { &hf_acn_session_count, { "Session Count", "acn.session_count", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } }, /* Total Sequence Number */ { &hf_acn_total_sequence_number, { "Total Sequence Number", "acn.total_sequence_number", FT_UINT32, BASE_DEC_HEX, NULL, 0x0, NULL, HFILL } } }; /* Setup protocol subtree array */ static gint *ett[] = { &ett_acn, &ett_acn_channel_owner_info_block, &ett_acn_channel_member_info_block, &ett_acn_channel_parameter, &ett_acn_address, &ett_acn_address_type, &ett_acn_pdu_flags, &ett_acn_dmp_pdu, &ett_acn_sdt_pdu, &ett_acn_sdt_client_pdu, &ett_acn_sdt_base_pdu, &ett_acn_root_pdu, &ett_acn_dmx_address, &ett_acn_dmx_data_pdu, &ett_acn_dmx_pdu }; module_t *acn_module; proto_acn = proto_register_protocol ( "Architecture for Control Networks", /* name */ "ACN", /* short name */ "acn" /* abbrev */ ); proto_register_field_array(proto_acn, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); acn_module = prefs_register_protocol(proto_acn, NULL); prefs_register_bool_preference(acn_module, "heuristic_acn", "Decode ACN", "Enable Architecture for Control Networks dissector (ANSI BSR E1.17)", &global_acn_heur); prefs_register_bool_preference(acn_module, "dmx_enable", "Streaming DMX", "Enable Streaming DMX extension dissector (ANSI BSR E1.31)", &global_acn_dmx_enable); prefs_register_enum_preference(acn_module, "dmx_display_view", "DMX, display format", "Display format", &global_acn_dmx_display_view, dmx_display_view, TRUE); prefs_register_bool_preference(acn_module, "dmx_display_zeros", "DMX, display zeros", "Display zeros instead of dots", &global_acn_dmx_display_zeros); prefs_register_bool_preference(acn_module, "dmx_display_leading_zeros", "DMX, display leading zeros", "Display leading zeros on levels", &global_acn_dmx_display_leading_zeros); prefs_register_enum_preference(acn_module, "dmx_display_line_format", "DMX, display line format", "Display line format", &global_acn_dmx_display_line_format, dmx_display_line_format, TRUE); } /******************************************************************************/ /* Register handoff */ void proto_reg_handoff_acn(void) { /* dissector_handle_t acn_handle; */ /* acn_handle = new_create_dissector_handle(dissect_acn, proto_acn); */ /* dissector_add_handle("udp.port", acn_handle); */ heur_dissector_add("udp", dissect_acn_heur, proto_acn); }