diff options
Diffstat (limited to 'epan/dissectors/packet-bvlc.c')
-rw-r--r-- | epan/dissectors/packet-bvlc.c | 1589 |
1 files changed, 1589 insertions, 0 deletions
diff --git a/epan/dissectors/packet-bvlc.c b/epan/dissectors/packet-bvlc.c new file mode 100644 index 00000000..58acc3a9 --- /dev/null +++ b/epan/dissectors/packet-bvlc.c @@ -0,0 +1,1589 @@ +/* packet-bvlc.c + * Routines for BACnet/IP (BVLL, BVLC) dissection + * Copyright 2001, Hartmut Mueller <hartmut@abmlinux.org>, FH Dortmund + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * Copied from README.developer,v 1.23 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include <epan/packet.h> +#include "packet-bacnet.h" + +void proto_register_bvlc(void); +void proto_reg_handoff_bvlc(void); + +#define BVLC_UDP_PORT 0xBAC0 + +/* Network Layer Wrapper Control Information */ +#define BAC_WRAPPER_CONTROL_NET 0x80 +#define BAC_WRAPPER_MSG_ENCRYPED 0x40 +#define BAC_WRAPPER_RESERVED 0x20 +#define BAC_WRAPPER_AUTHD_PRESENT 0x10 +#define BAC_WRAPPER_DO_NOT_UNWRAP 0x08 +#define BAC_WRAPPER_DO_NOT_DECRPT 0x04 +#define BAC_WRAPPER_NO_TRUST_SRC 0x02 +#define BAC_WRAPPER_SECURE_BY_RTR 0x01 + +static int proto_bvlc = -1; +static int proto_bscvlc = -1; +static int hf_bvlc_type = -1; +static int hf_bvlc_function = -1; +static int hf_bvlc_ipv6_function = -1; +static int hf_bvlc_length = -1; +static int hf_bvlc_result_ip4 = -1; +static int hf_bvlc_result_ip6 = -1; +static int hf_bvlc_bdt_ip = -1; +static int hf_bvlc_bdt_mask = -1; +static int hf_bvlc_bdt_port = -1; +static int hf_bvlc_reg_ttl = -1; +static int hf_bvlc_fdt_ip = -1; +static int hf_bvlc_fdt_ipv6 = -1; +static int hf_bvlc_fdt_port = -1; +static int hf_bvlc_fdt_ttl = -1; +static int hf_bvlc_fdt_timeout = -1; +static int hf_bvlc_fwd_ip = -1; +static int hf_bvlc_fwd_port = -1; +static int hf_bvlc_virt_source = -1; +static int hf_bvlc_virt_dest = -1; +static int hf_bvlc_orig_source_addr = -1; +static int hf_bvlc_orig_source_port = -1; +static int hf_bscvlc_control = -1; +static int hf_bscvlc_control_data_option = -1; +static int hf_bscvlc_control_destination_option = -1; +static int hf_bscvlc_control_destination_address = -1; +static int hf_bscvlc_control_origin_address = -1; +static int hf_bscvlc_control_reserved = -1; +static int hf_bscvlc_header = -1; +static int hf_bscvlc_header_marker = -1; +static int hf_bscvlc_header_length = -1; +static int hf_bscvlc_header_data = -1; +static int hf_bscvlc_header_opt_type = -1; +static int hf_bscvlc_header_opt_data = -1; +static int hf_bscvlc_header_opt_must_understand = -1; +static int hf_bscvlc_header_opt_more = -1; +static int hf_bscvlc_vendor_id = -1; +static int hf_bscvlc_proprietary_opt_type = -1; +static int hf_bscvlc_proprietary_data = -1; +static int hf_bscvlc_hub_conn_state = -1; +static int hf_bscvlc_accept_conns = -1; +static int hf_bscvlc_max_bvlc_length = -1; +static int hf_bscvlc_max_npdu_length = -1; +static int hf_bscvlc_function = -1; +static int hf_bscvlc_result = -1; +static int hf_bscvlc_error_class = -1; +static int hf_bscvlc_error_code = -1; +static int hf_bscvlc_result_data = -1; +static int hf_bscvlc_uris = -1; +static int hf_bscvlc_msg_id = -1; +static int hf_bscvlc_orig_vmac = -1; +static int hf_bscvlc_dest_vmac = -1; +static int hf_bscvlc_connect_vmac = -1; +static int hf_bscvlc_connect_uuid = -1; + +static dissector_table_t bvlc_dissector_table; +static dissector_table_t bscvlc_dissector_table; +static dissector_table_t bvlc_ipv6_dissector_table; +static dissector_handle_t bvlc_handle = NULL; +static dissector_handle_t bscvlc_handle = NULL; + +static const value_string bvlc_function_names[] = { + { 0x00, "BVLC-Result" }, + { 0x01, "Write-Broadcast-Distribution-Table" }, + { 0x02, "Read-Broadcast-Distribution-Table" }, + { 0x03, "Read-Broadcast-Distribution-Table-Ack" }, + { 0x04, "Forwarded-NPDU" }, + { 0x05, "Register-Foreign-Device" }, + { 0x06, "Read-Foreign-Device-Table" }, + { 0x07, "Read-Foreign-Device-Table-Ack" }, + { 0x08, "Delete-Foreign-Device-Table-Entry" }, + { 0x09, "Distribute-Broadcast-To-Network" }, + { 0x0a, "Original-Unicast-NPDU" }, + { 0x0b, "Original-Broadcast-NPDU" }, + { 0x0c, "Secured-BVLL" }, + { 0, NULL } +}; + +static const value_string bscvlc_function_names[] = { + { 0x00, "BVLC-Result" }, + { 0x01, "Encapsulated-NPDU" }, + { 0x02, "Address-Resolution" }, + { 0x03, "Address-Resolution-ACK" }, + { 0x04, "Advertisement" }, + { 0x05, "Advertisement-Solicitation" }, + { 0x06, "Connect-Request" }, + { 0x07, "Connect-Accept" }, + { 0x08, "Disconnect-Request" }, + { 0x09, "Disconnect-ACK" }, + { 0x0A, "Heartbeat-Request" }, + { 0x0B, "Heartbeat-ACK" }, + { 0x0C, "Proprietary-Message" }, + { 0, NULL } +}; + +static const value_string bvlc_result_names[] = { + { 0x00, "Successful completion" }, + { 0x10, "Write-Broadcast-Distribution-Table NAK" }, + { 0x20, "Read-Broadcast-Distribution-Table NAK" }, + { 0x30, "Register-Foreign-Device NAK" }, + { 0x40, "Read-Foreign-Device-Table NAK" }, + { 0x50, "Delete-Foreign-Device-Table-Entry NAK" }, + { 0x60, "Distribute-Broadcast-To-Network NAK" }, + { 0, NULL } +}; + +static const value_string bscvlc_result_names[] = { + { 0x00, "Successful completion (ACK)" }, + { 0x01, "Completion failed (NAK)" }, + { 0, NULL } +}; + +static const value_string bvlc_ipv6_function_names[] = { + { 0x00, "BVLC-Result", }, + { 0x01, "Original-Unicast-NPDU", }, + { 0x02, "Original-Broadcast-NPDU", }, + { 0x03, "Address-Resolution", }, + { 0x04, "Forwarded-Address-Resolution", }, + { 0x05, "Address-Resolution-ACK", }, + { 0x06, "Virtual-Address-Resolution", }, + { 0x07, "Virtual-Address-Resolution-ACK", }, + { 0x08, "Forwarded-NPDU", }, + { 0x09, "Register-Foreign-Device", }, + { 0x0A, "Delete-Foreign-Device-Table-Entry", }, + { 0x0B, "Secure-BVLL", }, + { 0x0C, "Distribute-Broadcast-To-Network", }, + { 0, NULL } +}; + +static const value_string bvlc_ipv6_result_names[] = { + { 0x00, "Successful completion" }, + { 0x30, "Address-Resolution NAK" }, + { 0x60, "Virtual-Address-Resolution NAK" }, + { 0x90, "Register-Foreign-Device NAK" }, + { 0xA0, "Delete-Foreign-Device-Table-Entry NAK" }, + { 0xC0, "Distribute-Broadcast-To-Network NAK" }, + { 0, NULL } +}; + +static const value_string bscvlc_header_type_names[] = { + { 0x01, "Secure Path" }, + { 0x1F, "Proprietary Header Option" }, + { 0, NULL } +}; + +static const value_string bscvlc_hub_conn_state_names[] = { + { 0x00, "No hub connection" }, + { 0x01, "Connected to primary hub" }, + { 0x02, "Connected to failover hub" }, + { 0, NULL } +}; + +static const value_string bscvlc_hub_accept_conns_names[] = { + { 0x00, "The node does not support accepting direct connections" }, + { 0x01, "The node supports accepting direct connections" }, + { 0, NULL } +}; + +static gint ett_bvlc = -1; +static gint ett_bscvlc = -1; +static gint ett_bscvlc_ctrl = -1; +static gint ett_bscvlc_hdr = -1; +static gint ett_bdt = -1; +static gint ett_fdt = -1; + +#define BACNET_IP_ANNEX_J 0x81 +#define BACNET_IPV6_ANNEX_U 0x82 + +static const value_string bvlc_types[] = { + { BACNET_IP_ANNEX_J, "BACnet/IP (Annex J)" }, + { BACNET_IPV6_ANNEX_U, "BACnet/IPV6 (Annex U)" }, + { 0, NULL } +}; + +#define BSCVLC_CONTROL_DATA_OPTION 0x01 +#define BSCVLC_CONTROL_DEST_OPTION 0x02 +#define BSCVLC_CONTROL_DEST_ADDRESS 0x04 +#define BSCVLC_CONTROL_ORIG_ADDRESS 0x08 +#define BSCVLC_CONTROL_RESERVED 0xF0 + +static const true_false_string control_data_option_set_high = { + "Data Options field is present.", + "Data Options field is absent." +}; + +static const true_false_string control_destination_option_set_high = { + "Destination Options field is present.", + "Destination Options field is absent." +}; + +static const true_false_string control_destination_address_set_high = { + "Destination Virtual Address is present.", + "Destination Virtual Address is absent." +}; + +static const true_false_string control_orig_address_set_high = { + "Originating Virtual Address is present.", + "Originating Virtual Address is absent." +}; + +static const true_false_string control_reserved_set_high = { + "Shall be zero, but is not.", + "Shall be zero and is zero." +}; + +#define BSCVLC_HEADER_OPTION_TYPE 0x1F +#define BSCVLC_HEADER_OPTION_DATA 0x20 +#define BSCVLC_HEADER_OPTION_MUST_UNDERSTAND 0x40 +#define BSCVLC_HEADER_OPTION_MORE_OPTIONS 0x80 + +#define BSCVLC_HEADER_TYPE_SECURE_PATH 0x01 +#define BSCVLC_HEADER_TYPE_PROPRIETARY 0x1F + + +static const true_false_string header_opt_data_set_high = { + "The 'Header Length' and 'Header Data' fields are present.", + "The 'Header Length' and 'Header Data' fields are absent." +}; + +static const true_false_string header_opt_must_understand_set_high = { + "This header option must be understood for consuming the message.", + "This header option can be ignored if not understood." +}; + +static const true_false_string header_opt_more_set_high = { + "Another header option follows in the current header option list.", + "This is the last header option in the current header option list." +}; + +static const value_string +BACnetErrorClass [] = { + { 0, "device" }, + { 1, "object" }, + { 2, "property" }, + { 3, "resources" }, + { 4, "security" }, + { 5, "services" }, + { 6, "vt" }, + { 7, "communication" }, + { 0, NULL } +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values64-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetErrorCode[] = { + { 0, "other"}, + { 1, "authentication-failed"}, + { 2, "configuration-in-progress"}, + { 3, "device-busy"}, + { 4, "dynamic-creation-not-supported"}, + { 5, "file-access-denied"}, + { 6, "incompatible-security-levels"}, + { 7, "inconsistent-parameters"}, + { 8, "inconsistent-selection-criterion"}, + { 9, "invalid-data-type"}, + { 10, "invalid-file-access-method"}, + { 11, "invalid-file-start-position"}, + { 12, "invalid-operator-name"}, + { 13, "invalid-parameter-data-type"}, + { 14, "invalid-time-stamp"}, + { 15, "key-generation-error"}, + { 16, "missing-required-parameter"}, + { 17, "no-objects-of-specified-type"}, + { 18, "no-space-for-object"}, + { 19, "no-space-to-add-list-element"}, + { 20, "no-space-to-write-property"}, + { 21, "no-vt-sessions-available"}, + { 22, "property-is-not-a-list"}, + { 23, "object-deletion-not-permitted"}, + { 24, "object-identifier-already-exists"}, + { 25, "operational-problem"}, + { 26, "password-failure"}, + { 27, "read-access-denied"}, + { 28, "security-not-supported"}, + { 29, "service-request-denied"}, + { 30, "timeout"}, + { 31, "unknown-object"}, + { 32, "unknown-property"}, + { 33, "removed enumeration"}, + { 34, "unknown-vt-class"}, + { 35, "unknown-vt-session"}, + { 36, "unsupported-object-type"}, + { 37, "value-out-of-range"}, + { 38, "vt-session-already-closed"}, + { 39, "vt-session-termination-failure"}, + { 40, "write-access-denied"}, + { 41, "character-set-not-supported"}, + { 42, "invalid-array-index"}, + { 43, "cov-subscription-failed"}, + { 44, "not-cov-property"}, + { 45, "optional-functionality-not-supported"}, + { 46, "invalid-configuration-data"}, + { 47, "datatype-not-supported"}, + { 48, "duplicate-name"}, + { 49, "duplicate-object-id"}, + { 50, "property-is-not-an-array"}, + { 51, "abort - buffer - overflow" }, + { 52, "abort - invalid - apdu - in - this - state" }, + { 53, "abort - preempted - by - higher - priority - task" }, + { 54, "abort - segmentation - not - supported" }, + { 55, "abort - proprietary" }, + { 56, "abort - other" }, + { 57, "reject - invalid - tag" }, + { 58, "reject - network - down" }, + { 59, "reject - buffer - overflow" }, + { 60, "reject - inconsistent - parameters" }, + { 61, "reject - invalid - parameter - data - type" }, + { 62, "reject - invalid - tag" }, + { 63, "reject - missing - required - parameter" }, + { 64, "reject - parameter - out - of - range" }, + { 65, "reject - too - many - arguments" }, + { 66, "reject - undefined - enumeration" }, + { 67, "reject - unrecognized - service" }, + { 68, "reject - proprietary" }, + { 69, "reject - other" }, + { 70, "unknown - device" }, + { 71, "unknown - route" }, + { 72, "value - not - initialized" }, + { 73, "invalid-event-state"}, + { 74, "no-alarm-configured"}, + { 75, "log-buffer-full"}, + { 76, "logged-value-purged"}, + { 77, "no-property-specified"}, + { 78, "not-configured-for-triggered-logging"}, + { 79, "unknown-subscription"}, + { 80, "parameter-out-of-range"}, + { 81, "list-element-not-found"}, + { 82, "busy"}, + { 83, "communication-disabled"}, + { 84, "success"}, + { 85, "access-denied"}, + { 86, "bad-destination-address"}, + { 87, "bad-destination-device-id"}, + { 88, "bad-signature"}, + { 89, "bad-source-address"}, + { 90, "bad-timestamp"}, + { 91, "cannot-use-key"}, + { 92, "cannot-verify-message-id"}, + { 93, "correct-key-revision"}, + { 94, "destination-device-id-required"}, + { 95, "duplicate-message"}, + { 96, "encryption-not-configured"}, + { 97, "encryption-required"}, + { 98, "incorrect-key"}, + { 99, "invalid-key-data"}, + { 100, "key-update-in-progress"}, + { 101, "malformed-message"}, + { 102, "not-key-server"}, + { 103, "security-not-configured"}, + { 104, "source-security-required"}, + { 105, "too-many-keys"}, + { 106, "unknown-authentication-type"}, + { 107, "unknown-key"}, + { 108, "unknown-key-revision"}, + { 109, "unknown-source-message"}, + { 110, "not-router-to-dnet"}, + { 111, "router-busy"}, + { 112, "unknown-network-message"}, + { 113, "message-too-long"}, + { 114, "security-error"}, + { 115, "addressing-error"}, + { 116, "write-bdt-failed"}, + { 117, "read-bdt-failed"}, + { 118, "register-foreign-device-failed"}, + { 119, "read-fdt-failed"}, + { 120, "delete-fdt-entry-failed"}, + { 121, "distribute-broadcast-failed"}, + { 122, "unknown-file-size"}, + { 123, "abort-apdu-too-long"}, + { 124, "abort-application-exceeded-reply-time"}, + { 125, "abort-out-of-resources"}, + { 126, "abort-tsm-timeout"}, + { 127, "abort-window-size-out-of-range"}, + { 128, "file-full"}, + { 129, "inconsistent-configuration"}, + { 130, "inconsistent-object-type"}, + { 131, "internal-error"}, + { 132, "not-configured"}, + { 133, "out-of-memory"}, + { 134, "value-too-long"}, + { 135, "abort-insufficient-security"}, + { 136, "abort-security-error"}, + { 137, "duplicate-entry"}, + { 138, "invalid-value-in-this-state"}, + { 139, "invalid-operation-in-this-state"}, + { 140, "list-item-not-numbered"}, + { 141, "list-item-not-timestamped"}, + { 142, "invalid-data-encoding"}, + { 143, "bvlc-function-unknown"}, + { 144, "bvlc-proprietary-function-unknown"}, + { 145, "header-encoding-error"}, + { 146, "header-not-understood"}, + { 147, "message-incomplete"}, + { 148, "not-a-bacnet-sc-hub"}, + { 149, "payload-expected"}, + { 150, "unexpected-data"}, + { 151, "node-duplicate-vmac"}, + { 152, "http-unexpected-response-code"}, + { 153, "http-no-upgrade"}, + { 154, "http-resource-not-local"}, + { 155, "http-proxy-authentication-failed"}, + { 156, "http-response-timeout"}, + { 157, "http-response-syntax-error"}, + { 158, "http-response-value-error"}, + { 159, "http-response-missing-header"}, + { 160, "http-websocket-header-error"}, + { 161, "http-upgrade-required"}, + { 162, "http-upgrade-error"}, + { 163, "http-temporary-unavailable"}, + { 164, "http-not-a-server"}, + { 165, "http-error"}, + { 166, "websocket-scheme-not-supported"}, + { 167, "websocket-unknown-control-message"}, + { 168, "websocket-close-error"}, + { 169, "websocket-closed-by-peer"}, + { 170, "websocket-endpoint-leaves"}, + { 171, "websocket-protocol-error"}, + { 172, "websocket-data-not-accepted"}, + { 173, "websocket-closed-abnormally"}, + { 174, "websocket-data-inconsistent"}, + { 175, "websocket-data-against-policy"}, + { 176, "websocket-frame-too-long"}, + { 177, "websocket-extension-missing"}, + { 178, "websocket-request-unavailable"}, + { 179, "websocket-error"}, + { 180, "tls-client-certificate-error"}, + { 181, "tls-server-certificate-error"}, + { 182, "tls-client-authentication-failed"}, + { 183, "tls-server-authentication-failed"}, + { 184, "tls-client-certificate-expired"}, + { 185, "tls-server-certificate-expired"}, + { 186, "tls-client-certificate-revoked"}, + { 187, "tls-server-certificate-revoked"}, + { 188, "tls-error"}, + { 189, "dns-unavailable"}, + { 190, "dns-name-resolution-failed"}, + { 191, "dns-resolver-failure"}, + { 192, "dns-error"}, + { 193, "tcp-connect-timeout"}, + { 194, "tcp-connection-refused"}, + { 195, "tcp-closed-by-local"}, + { 196, "tcp-closed-other"}, + { 197, "tcp-error"}, + { 198, "ip-address-not-reachable"}, + { 199, "ip-error"}, + { 0, NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to the + procedures and constraints described in Clause 23. */ +}; + +static int * const bscvlc_control_flags[] = { + &hf_bscvlc_control_data_option, + &hf_bscvlc_control_destination_option, + &hf_bscvlc_control_destination_address, + &hf_bscvlc_control_origin_address, + &hf_bscvlc_control_reserved, + NULL +}; + +static int * const bscvlc_header_flags[] = { + &hf_bscvlc_header_opt_type, + &hf_bscvlc_header_opt_data, + &hf_bscvlc_header_opt_must_understand, + &hf_bscvlc_header_opt_more, + NULL +}; + +static int +dissect_ipv4_bvlc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + + proto_item *ti; + proto_item *ti_bdt; + proto_item *ti_fdt; + proto_tree *bvlc_tree; + proto_tree *bdt_tree; /* Broadcast Distribution Table */ + proto_tree *fdt_tree; /* Foreign Device Table */ + + gint offset; + guint8 bvlc_type; + guint8 bvlc_function; + guint16 bvlc_length; + guint16 packet_length; + guint npdu_length; + guint length_remaining; + tvbuff_t *next_tvb; + + offset = 0; + + bvlc_type = tvb_get_guint8(tvb, offset); + bvlc_function = tvb_get_guint8(tvb, offset + 1); + packet_length = tvb_get_ntohs(tvb, offset + 2); + length_remaining = tvb_reported_length_remaining(tvb, offset); + + if (bvlc_function > 0x08) { + /* We have a constant header length of BVLC of 4 in every + * BVLC-packet forewarding an NPDU. Beware: Changes in the + * BACnet-IP-standard may break this. + */ + bvlc_length = 4; + } else if (bvlc_function == 0x04) { + /* 4 Bytes + 6 Bytes for B/IP Address of Originating Device */ + bvlc_length = 10; + } else { + /* BVLC-packets with function below 0x09 contain + * routing-level data (e.g. Broadcast Distribution) + * but no NPDU for BACnet, so bvlc_length goes up to the end + * of the captured frame. + */ + bvlc_length = packet_length; + } + + if (bvlc_length < 4 || bvlc_length > packet_length) { + return 0; /* reject */ + } + + /* Put the BVLC Type in the info column */ + col_append_fstr(pinfo->cinfo, COL_INFO, " BVLC Function %s ", + val_to_str_const(bvlc_function, bvlc_function_names, "unknown")); + + ti = proto_tree_add_item(tree, proto_bvlc, tvb, 0, bvlc_length, ENC_NA); + bvlc_tree = proto_item_add_subtree(ti, ett_bvlc); + proto_tree_add_uint(bvlc_tree, hf_bvlc_type, tvb, offset, 1, + bvlc_type); + offset++; + proto_tree_add_uint(bvlc_tree, hf_bvlc_function, tvb, + offset, 1, bvlc_function); + offset++; + if (length_remaining != packet_length) + proto_tree_add_uint_format_value(bvlc_tree, hf_bvlc_length, tvb, offset, + 2, bvlc_length, + "%d of %d bytes (invalid length - expected %d bytes)", + bvlc_length, packet_length, length_remaining); + else + proto_tree_add_uint_format_value(bvlc_tree, hf_bvlc_length, tvb, offset, + 2, bvlc_length, "%d of %d bytes BACnet packet length", + bvlc_length, packet_length); + offset += 2; + switch (bvlc_function) { + case 0x00: /* BVLC-Result */ + /* I don't know why the result code is encoded in 4 nibbles, + * but only using one: 0x00r0. Shifting left 4 bits. + */ + /* We should bitmask the result correctly when we have a + * packet to dissect, see README.developer, 1.6.2, FID */ + proto_tree_add_item(bvlc_tree, hf_bvlc_result_ip4, tvb, + offset, 2, ENC_BIG_ENDIAN); + /*offset += 2;*/ + break; + case 0x01: /* Write-Broadcast-Distribution-Table */ + case 0x03: /* Read-Broadcast-Distribution-Table-Ack */ + /* List of BDT Entries: N*10-octet */ + ti_bdt = proto_tree_add_item(bvlc_tree, proto_bvlc, tvb, + offset, bvlc_length-4, ENC_NA); + bdt_tree = proto_item_add_subtree(ti_bdt, ett_bdt); + /* List of BDT Entries: N*10-octet */ + while ((bvlc_length - offset) > 9) { + proto_tree_add_item(bdt_tree, hf_bvlc_bdt_ip, + tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(bdt_tree, hf_bvlc_bdt_port, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(bdt_tree, + hf_bvlc_bdt_mask, tvb, offset, 4, + ENC_NA); + offset += 4; + } + /* We check this if we get a BDT-packet somewhere */ + break; + case 0x02: /* Read-Broadcast-Distribution-Table */ + /* nothing to do here */ + break; + case 0x05: /* Register-Foreign-Device */ + /* Time-to-Live 2-octets T, Time-to-Live T, in seconds */ + proto_tree_add_item(bvlc_tree, hf_bvlc_reg_ttl, + tvb, offset, 2, ENC_BIG_ENDIAN); + /*offset += 2;*/ + break; + case 0x06: /* Read-Foreign-Device-Table */ + /* nothing to do here */ + break; + case 0x07: /* Read-Foreign-Device-Table-Ack */ + /* List of FDT Entries: N*10-octet */ + /* N indicates the number of entries in the FDT whose + * contents are being returned. Each returned entry + * consists of the 6-octet B/IP address of the registrant; + * the 2-octet Time-to-Live value supplied at the time of + * registration; and a 2-octet value representing the + * number of seconds remaining before the BBMD will purge + * the registrant's FDT entry if no re-registration occurs. + */ + ti_fdt = proto_tree_add_item(bvlc_tree, proto_bvlc, tvb, + offset, bvlc_length -4, ENC_NA); + fdt_tree = proto_item_add_subtree(ti_fdt, ett_fdt); + /* List of FDT Entries: N*10-octet */ + while ((bvlc_length - offset) > 9) { + proto_tree_add_item(fdt_tree, hf_bvlc_fdt_ip, + tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(fdt_tree, hf_bvlc_fdt_port, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(fdt_tree, + hf_bvlc_fdt_ttl, tvb, offset, 2, + ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(fdt_tree, + hf_bvlc_fdt_timeout, tvb, offset, 2, + ENC_BIG_ENDIAN); + offset += 2; + } + /* We check this if we get a FDT-packet somewhere */ + break; + case 0x08: /* Delete-Foreign-Device-Table-Entry */ + /* FDT Entry: 6-octets */ + proto_tree_add_item(bvlc_tree, hf_bvlc_fdt_ip, + tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(bvlc_tree, hf_bvlc_fdt_port, + tvb, offset, 2, ENC_BIG_ENDIAN); + /*offset += 2;*/ + break; + case 0x0C: /* Secure-BVLL */ + offset = bacnet_dissect_sec_wrapper(tvb, pinfo, tree, offset, NULL); + if (offset < 0) { + call_data_dissector(tvb, pinfo, tree); + return tvb_captured_length(tvb); + } + dissect_ipv4_bvlc(tvb, pinfo, tree, data); + break; + /* We check this if we get a FDT-packet somewhere */ + case 0x04: /* Forwarded-NPDU + * Why is this 0x04? It would have been a better + * idea to append all forewarded NPDUs at the + * end of the function table in the B/IP-standard! + */ + /* proto_tree_add_bytes_format(); */ + proto_tree_add_item(bvlc_tree, hf_bvlc_fwd_ip, + tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(bvlc_tree, hf_bvlc_fwd_port, + tvb, offset, 2, ENC_BIG_ENDIAN); + /*offset += 2;*/ + break; + default: + /* Distribute-Broadcast-To-Network + * Original-Unicast-NPDU + * Original-Broadcast-NPDU + * Going to the next dissector... + */ + break; + } + + /* Ok, no routing information BVLC packet. Dissect as + * BACnet NPDU + */ + npdu_length = packet_length - bvlc_length; + next_tvb = tvb_new_subset_length(tvb, bvlc_length, npdu_length); + /* Code from Guy Harris */ + if (!dissector_try_uint(bvlc_dissector_table, + bvlc_function, next_tvb, pinfo, tree)) { + /* Unknown function - dissect the paylod as data */ + call_data_dissector(next_tvb, pinfo, tree); + } + return tvb_reported_length(tvb); +} + +static int +dissect_ipv6_bvlc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + proto_item *ti; + proto_tree *bvlc_tree; + + gint offset; + guint8 bvlc_type; + guint8 bvlc_function; + guint16 bvlc_length = 0; + guint16 packet_length; + guint npdu_length; + guint length_remaining; + tvbuff_t *next_tvb; + + offset = 0; + + bvlc_type = tvb_get_guint8(tvb, offset); + bvlc_function = tvb_get_guint8(tvb, offset + 1); + packet_length = tvb_get_ntohs(tvb, offset + 2); + length_remaining = tvb_reported_length_remaining(tvb, offset); + + switch (bvlc_function) { + case 0x00: + case 0x09: + bvlc_length = 9; + break; + case 0x01: + bvlc_length = 10; + break; + case 0x02: + case 0x06: + case 0x0C: + bvlc_length = 7; + break; + case 0x03: + case 0x05: + case 0x07: + bvlc_length = 10; + break; + case 0x04: + bvlc_length = 28; + break; + case 0x08: + case 0x0A: + bvlc_length = 25; + break; + case 0x0B: + bvlc_length = 4; + break; + default: + break; + } + + if (bvlc_length > packet_length) { + return 0; /* reject */ + } + + /* Put the BVLC Type in the info column */ + col_append_fstr(pinfo->cinfo, COL_INFO, " BVLC Function %s ", + val_to_str_const(bvlc_function, bvlc_function_names, "unknown")); + + ti = proto_tree_add_item(tree, proto_bvlc, tvb, 0, + bvlc_length, ENC_NA); + bvlc_tree = proto_item_add_subtree(ti, ett_bvlc); + /* add the BVLC type */ + proto_tree_add_uint(bvlc_tree, hf_bvlc_type, tvb, offset, 1, + bvlc_type); + offset++; + /* add the BVLC function */ + proto_tree_add_uint(bvlc_tree, hf_bvlc_ipv6_function, tvb, + offset, 1, bvlc_function); + offset++; + /* add the length information */ + if (length_remaining != packet_length) + proto_tree_add_uint_format_value(bvlc_tree, hf_bvlc_length, tvb, offset, + 2, bvlc_length, + "%d of %d bytes (invalid length - expected %d bytes)", + bvlc_length, packet_length, length_remaining); + else + proto_tree_add_uint_format_value(bvlc_tree, hf_bvlc_length, tvb, offset, + 2, bvlc_length, + "%d of %d bytes BACnet packet length", + bvlc_length, packet_length); + offset += 2; + + /* add the optional present virtual source address */ + if (bvlc_function != 0x0B) { + proto_tree_add_item(bvlc_tree, hf_bvlc_virt_source, tvb, offset, + 3, ENC_BIG_ENDIAN); + offset += 3; + } + + /* handle additional function parameters */ + switch (bvlc_function) { + case 0x00: /* BVLC-Result */ + proto_tree_add_item(bvlc_tree, hf_bvlc_result_ip6, tvb, + offset, 2, ENC_BIG_ENDIAN); + offset += 2; + break; + case 0x01: /* Original-Unicast-NPDU */ + case 0x03: /* Address-Resolution */ + case 0x05: /* Address-Resolution-ACK */ + case 0x07: /* Virtual-Address-Resolution-ACK */ + proto_tree_add_item(bvlc_tree, hf_bvlc_virt_dest, tvb, offset, + 3, ENC_BIG_ENDIAN); + offset += 3; + break; + case 0x04: /* Forwarded-Address-Resolution */ + proto_tree_add_item(bvlc_tree, hf_bvlc_virt_dest, tvb, offset, + 3, ENC_BIG_ENDIAN); + offset += 3; + proto_tree_add_item(bvlc_tree, hf_bvlc_orig_source_addr, + tvb, offset, 16, ENC_NA); + offset += 16; + proto_tree_add_item(bvlc_tree, hf_bvlc_orig_source_port, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + break; + case 0x08: /* Forwarded-NPDU */ + proto_tree_add_item(bvlc_tree, hf_bvlc_orig_source_addr, + tvb, offset, 16, ENC_NA); + offset += 16; + proto_tree_add_item(bvlc_tree, hf_bvlc_orig_source_port, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + break; + case 0x06: /* Virtual-Address-Resolution */ + break; + case 0x09: /* Register-Foreign-Device */ + proto_tree_add_item(bvlc_tree, hf_bvlc_reg_ttl, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + break; + case 0x0A: /* Delete-Foreign-Device-Table-Entry */ + proto_tree_add_item(bvlc_tree, hf_bvlc_fdt_ipv6, + tvb, offset, 16, ENC_NA); + offset += 16; + proto_tree_add_item(bvlc_tree, hf_bvlc_fdt_port, + tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + break; + case 0x0B: /* Secure-BVLL */ + offset = bacnet_dissect_sec_wrapper(tvb, pinfo, tree, offset, NULL); + if (offset < 0) { + call_data_dissector(tvb, pinfo, tree); + return tvb_captured_length(tvb); + } + dissect_ipv6_bvlc(tvb, pinfo, tree, data); + break; + case 0x02: /* Original-Broadcast-NPDU */ + case 0x0c: /* Distribute-Broadcast-To-Network */ + default: + /* + * Going to the next dissector... + */ + break; + } + + /* Ok, no routing information BVLC packet. Dissect as + * BACnet NPDU + */ + npdu_length = packet_length - offset; + next_tvb = tvb_new_subset_length(tvb, offset, npdu_length); + /* Code from Guy Harris */ + if ( ! dissector_try_uint(bvlc_ipv6_dissector_table, + bvlc_function, next_tvb, pinfo, tree)) { + /* Unknown function - dissect the paylod as data */ + call_data_dissector(next_tvb, pinfo, tree); + } + + return tvb_reported_length(tvb); +} + +static int +dissect_bvlc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + guint8 bvlc_type; + guint ret = 0; + + bvlc_type = tvb_get_guint8(tvb, 0); + + /* + * Simple sanity check - make sure the type is one we know about. + */ + if (try_val_to_str(bvlc_type, bvlc_types) == NULL) + return 0; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "BVLC"); + col_set_str(pinfo->cinfo, COL_INFO, "BACnet Virtual Link Control"); + + switch (bvlc_type) + { + case BACNET_IP_ANNEX_J: + ret = dissect_ipv4_bvlc(tvb, pinfo, tree, data); + break; + case BACNET_IPV6_ANNEX_U: + ret = dissect_ipv6_bvlc(tvb, pinfo, tree, data); + break; + } + + return ret; +} + +static int +dissect_bscvlc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + proto_item *ti; + proto_tree *bvlc_tree; + tvbuff_t *next_tvb; + gint offset; + gint start; + gint bvlc_length; + gint packet_length; + gint npdu_length; + guint8 bvlc_function; + guint8 bvlc_control; + guint8 bvlc_result; + guint8 hdr_byte; + gint8 mac_buffer[16]; + guint bvlc_message_id; + guint idx; + gboolean bMoreFlag; + gboolean bDataFlag; + proto_tree *subtree; + + /* Calculate length of BSCVLC block to get remaining payload length */ + offset = 0; + + packet_length = tvb_reported_length_remaining(tvb, offset); + if(packet_length < 4) + return 0; /* reject */ + + /* Fix part of the header first */ + bvlc_function = tvb_get_guint8(tvb, offset++); + bvlc_control = tvb_get_guint8(tvb, offset++); + bvlc_message_id = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN); + offset += 2; + + /* Variable part of the header next */ + bvlc_length = offset; + + if ((bvlc_control & BSCVLC_CONTROL_ORIG_ADDRESS) != 0) + bvlc_length += 6; + + if ((bvlc_control & BSCVLC_CONTROL_DEST_ADDRESS) != 0) + bvlc_length += 6; + + if ((bvlc_control & BSCVLC_CONTROL_DEST_OPTION) != 0) + { + bMoreFlag = TRUE; + + while(tvb_reported_length_remaining(tvb, bvlc_length) > 0 && + (hdr_byte = tvb_get_guint8(tvb, bvlc_length)) != 0 && bMoreFlag) + { + /* get flags and type... */ + bMoreFlag= (hdr_byte & BSCVLC_HEADER_OPTION_MORE_OPTIONS); + bDataFlag= (hdr_byte & BSCVLC_HEADER_OPTION_DATA); + bvlc_length++; + + if(bDataFlag) + { + npdu_length = (gint)(tvb_get_guint8(tvb, bvlc_length++) << 8); + npdu_length += (gint)tvb_get_guint8(tvb, bvlc_length++); + bvlc_length += npdu_length; + } + } + } + + if ((bvlc_control & BSCVLC_CONTROL_DATA_OPTION) != 0) + { + bMoreFlag = TRUE; + + while(tvb_reported_length_remaining(tvb, bvlc_length) > 0 && + (hdr_byte = tvb_get_guint8(tvb, bvlc_length)) != 0 && bMoreFlag) + { + /* get flags and type... */ + bMoreFlag= (hdr_byte & BSCVLC_HEADER_OPTION_MORE_OPTIONS); + bDataFlag= (hdr_byte & BSCVLC_HEADER_OPTION_DATA); + bvlc_length++; + + if(bDataFlag) + { + npdu_length = (gint)(tvb_get_guint8(tvb, bvlc_length++) << 8); + npdu_length += (gint)tvb_get_guint8(tvb, bvlc_length++); + bvlc_length += npdu_length; + } + } + } + + /* Now add the BSCVLC payload size for specified function */ + switch (bvlc_function) + { + case 0x00: /* BVLC-Result */ + case 0x03: /* Address-Resolution-ACK */ + case 0x0C: /* Proprietary-Message */ + /* complete packet length because of optional present variable length error data + but no length encoded for it in the structure of this frame */ + bvlc_length = packet_length; + break; + case 0x02: /* Address-Resolution */ + case 0x05: /* Advertisement-Solicitation */ + case 0x08: /* Disconnect-Request */ + case 0x09: /* Disconnect-ACK */ + case 0x0A: /* Heartbeat-Request */ + case 0x0B: /* Heartbeat-ACK */ + /* No additional payload here */ + break; + case 0x04: /* Advertisement */ + bvlc_length += 6; + break; + case 0x06: /* Connect-Request */ + case 0x07: /* Connect-Accept */ + bvlc_length += 26; + break; + case 0x01: /* Encapsulated-NPDU */ + default: + /* The additional payload will be decoded elsewhere */ + break; + } + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "BSCVLC"); + col_set_str(pinfo->cinfo, COL_INFO, "BACnet Secure Connect Virtual Link Control"); + + /* Put the BSCVLC Type and Message ID in the info column */ + col_append_fstr(pinfo->cinfo, COL_INFO, " BSCVLC Function %s Message-ID %u", + val_to_str_const(bvlc_function, bscvlc_function_names, "unknown"), bvlc_message_id); + + /* Fill the tree... */ + offset = 0; + ti = proto_tree_add_item(tree, proto_bscvlc, tvb, 0, bvlc_length, ENC_NA); + bvlc_tree = proto_item_add_subtree(ti, ett_bvlc); + + proto_tree_add_uint(bvlc_tree, hf_bscvlc_function, tvb, + offset, 1, bvlc_function); + offset++; + proto_tree_add_bitmask(bvlc_tree, tvb, offset, hf_bscvlc_control, + ett_bscvlc_ctrl, bscvlc_control_flags, ENC_NA); + offset ++; + proto_tree_add_uint(bvlc_tree, hf_bscvlc_msg_id, tvb, + offset, 2, bvlc_message_id); + offset += 2; + + if ((bvlc_control & BSCVLC_CONTROL_ORIG_ADDRESS) != 0) + { + for(idx = 0; idx < 6; idx++) + snprintf(&mac_buffer[idx * 2], sizeof(mac_buffer) - (idx * 2), "%02X", tvb_get_guint8(tvb, offset + idx)); + col_append_fstr(pinfo->cinfo, COL_INFO, " SMAC %s", mac_buffer); + + proto_tree_add_item(bvlc_tree, hf_bscvlc_orig_vmac, tvb, offset, 6, ENC_NA); + offset += 6; + } + + if ((bvlc_control & BSCVLC_CONTROL_DEST_ADDRESS) != 0) + { + for(idx = 0; idx < 6; idx++) + snprintf(&mac_buffer[idx * 2], sizeof(mac_buffer) - (idx * 2), "%02X", tvb_get_guint8(tvb, offset + idx)); + col_append_fstr(pinfo->cinfo, COL_INFO, " DMAC %s", mac_buffer); + + proto_tree_add_item(bvlc_tree, hf_bscvlc_dest_vmac, tvb, offset, 6, ENC_NA); + offset += 6; + } + + if ((bvlc_control & BSCVLC_CONTROL_DEST_OPTION) != 0) + { + bMoreFlag = TRUE; + + while(tvb_reported_length_remaining(tvb, offset) > 0 && + (hdr_byte = tvb_get_guint8(tvb, offset)) != 0 && bMoreFlag) + { + /* get flags and type... */ + bMoreFlag= (hdr_byte & BSCVLC_HEADER_OPTION_MORE_OPTIONS); + bDataFlag= (hdr_byte & BSCVLC_HEADER_OPTION_DATA); + start = offset; + + offset++; + + if(bDataFlag) + { + npdu_length = (gint)(tvb_get_guint8(tvb, offset++) << 8); + npdu_length += (gint)tvb_get_guint8(tvb, offset++); + offset += npdu_length; + } + + subtree = proto_tree_add_subtree_format(bvlc_tree, tvb, start, offset - start, + ett_bscvlc_hdr, NULL, "%s", "Destination Options"); + proto_tree_add_bitmask_value(subtree, tvb, start, hf_bscvlc_header, + ett_bscvlc_hdr, bscvlc_header_flags, hdr_byte); + + if(bDataFlag) + { + proto_tree_add_item(subtree, hf_bscvlc_header_length, tvb, start + 1, 2, ENC_NA); + proto_tree_add_item(subtree, hf_bscvlc_header_data, tvb, start + 3, npdu_length, ENC_NA); + } + } + } + + if ((bvlc_control & BSCVLC_CONTROL_DATA_OPTION) != 0) + { + bMoreFlag = TRUE; + + while(tvb_reported_length_remaining(tvb, offset) > 0 && + (hdr_byte = tvb_get_guint8(tvb, offset)) != 0 && bMoreFlag) + { + /* get flags and type... */ + bMoreFlag= (hdr_byte & BSCVLC_HEADER_OPTION_MORE_OPTIONS); + bDataFlag= (hdr_byte & BSCVLC_HEADER_OPTION_DATA); + start = offset; + + offset++; + + if(bDataFlag) + { + npdu_length = (gint)(tvb_get_guint8(tvb, offset++) << 8); + npdu_length += (gint)tvb_get_guint8(tvb, offset++); + offset += npdu_length; + } + + subtree = proto_tree_add_subtree_format(bvlc_tree, tvb, start, offset - start, + ett_bscvlc_hdr, NULL, "%s", "Data Options"); + proto_tree_add_bitmask_value(subtree, tvb, start, hf_bscvlc_header, + ett_bscvlc_hdr, bscvlc_header_flags, hdr_byte); + + if(bDataFlag) + { + proto_tree_add_item(subtree, hf_bscvlc_header_length, tvb, start + 1, 2, ENC_NA); + proto_tree_add_item(subtree, hf_bscvlc_header_data, tvb, start + 3, npdu_length, ENC_NA); + } + } + } + + switch (bvlc_function) + { + case 0x02: /* Address-Resolution */ + case 0x05: /* Advertisement-Solicitation */ + case 0x08: /* Disconnect-Request */ + case 0x09: /* Disconnect-ACK */ + case 0x0A: /* Heartbeat-Request */ + case 0x0B: /* Heartbeat-ACK */ + break; + case 0x00: /* BVLC-Result */ + subtree = proto_tree_add_subtree_format(bvlc_tree, tvb, offset, packet_length - offset, + ett_bscvlc_hdr, NULL, "%s", "BVLC-Result"); + proto_tree_add_item(subtree, hf_bscvlc_function, tvb, + offset, 1, ENC_NA); + offset++; + proto_tree_add_item(subtree, hf_bscvlc_result, tvb, + offset, 1, ENC_NA); + bvlc_result = tvb_get_guint8(tvb, offset); + offset++; + + col_append_fstr(pinfo->cinfo, COL_INFO, " %s", + val_to_str_const(bvlc_result, bscvlc_result_names, "unknown")); + + if(bvlc_result) + { + proto_tree_add_item(subtree, hf_bscvlc_header_marker, tvb, + offset, 1, ENC_NA); + offset++; + proto_tree_add_item(subtree, hf_bscvlc_error_class, tvb, + offset, 2, ENC_NA); + offset += 2; + proto_tree_add_item(subtree, hf_bscvlc_error_code, tvb, + offset, 2, ENC_NA); + offset += 2; + proto_tree_add_item(subtree, hf_bscvlc_result_data, tvb, + offset, packet_length - offset, ENC_NA); + } + /* Force and of packet */ + offset = packet_length; + break; + case 0x03: /* Address-Resolution-ACK */ + subtree = proto_tree_add_subtree_format(bvlc_tree, tvb, offset, packet_length - offset, + ett_bscvlc_hdr, NULL, "%s", "Address-Resolution-ACK"); + proto_tree_add_item(subtree, hf_bscvlc_uris, tvb, + offset, packet_length - offset, ENC_NA); + /* Force and of packet */ + offset = packet_length; + break; + case 0x04: /* Advertisement */ + subtree = proto_tree_add_subtree_format(bvlc_tree, tvb, offset, packet_length - offset, + ett_bscvlc_hdr, NULL, "%s", "Advertisement"); + proto_tree_add_item(subtree, hf_bscvlc_hub_conn_state, tvb, + offset, 1, ENC_NA); + offset++; + proto_tree_add_item(subtree, hf_bscvlc_accept_conns, tvb, + offset, 1, ENC_NA); + offset++; + proto_tree_add_item(subtree, hf_bscvlc_max_bvlc_length, tvb, + offset, 2, ENC_NA); + offset += 2; + proto_tree_add_item(subtree, hf_bscvlc_max_npdu_length, tvb, + offset, 2, ENC_NA); + offset += 2; + break; + case 0x06: /* Connect-Request */ + subtree = proto_tree_add_subtree_format(bvlc_tree, tvb, offset, packet_length - offset, + ett_bscvlc_hdr, NULL, "%s", "Connect-Request"); + proto_tree_add_item(subtree, hf_bscvlc_connect_vmac, tvb, + offset, 6, ENC_NA); + offset += 6; + proto_tree_add_item(subtree, hf_bscvlc_connect_uuid, tvb, + offset, 16, ENC_NA); + offset += 16; + proto_tree_add_item(subtree, hf_bscvlc_max_bvlc_length, tvb, + offset, 2, ENC_NA); + offset += 2; + proto_tree_add_item(subtree, hf_bscvlc_max_npdu_length, tvb, + offset, 2, ENC_NA); + offset += 2; + break; + case 0x07: /* Connect-Accept */ + subtree = proto_tree_add_subtree_format(bvlc_tree, tvb, offset, packet_length - offset, + ett_bscvlc_hdr, NULL, "%s", "Connect-Accept"); + proto_tree_add_item(subtree, hf_bscvlc_connect_vmac, tvb, + offset, 6, ENC_NA); + offset += 6; + proto_tree_add_item(subtree, hf_bscvlc_connect_uuid, tvb, + offset, 16, ENC_NA); + offset += 16; + proto_tree_add_item(subtree, hf_bscvlc_max_bvlc_length, tvb, + offset, 2, ENC_NA); + offset += 2; + proto_tree_add_item(subtree, hf_bscvlc_max_npdu_length, tvb, + offset, 2, ENC_NA); + offset += 2; + break; + case 0x0C: /* Proprietary-Message */ + subtree = proto_tree_add_subtree_format(bvlc_tree, tvb, offset, packet_length - offset, + ett_bscvlc_hdr, NULL, "%s", "Proprietary-Message"); + proto_tree_add_item(subtree, hf_bscvlc_vendor_id, tvb, + offset, 2, ENC_NA); + offset += 2; + proto_tree_add_item(subtree, hf_bscvlc_proprietary_opt_type, tvb, + offset, 1, ENC_NA); + offset++; + proto_tree_add_item(subtree, hf_bscvlc_proprietary_data, tvb, + offset, packet_length - offset, ENC_NA); + /* Force and of packet */ + offset = packet_length; + break; + case 0x01: /* Encapsulated-NPDU */ + default: + /* Here we assume additional payload belongs to upper layers and will be decoded later */ + break; + } + + /* Let the remaining frame to be decoded elsewhere */ + npdu_length = packet_length - offset; + next_tvb = tvb_new_subset_length(tvb, offset, npdu_length); + /* Code from Guy Harris */ + if (!dissector_try_uint(bscvlc_dissector_table, + bvlc_function, next_tvb, pinfo, tree)) { + /* Unknown function - dissect the paylod as data */ + call_data_dissector(next_tvb, pinfo, tree); + } + + return tvb_reported_length(tvb); +} + +void +proto_register_bvlc(void) +{ + static hf_register_info hf[] = { + { &hf_bvlc_type, + { "Type", "bvlc.type", + FT_UINT8, BASE_HEX, VALS(bvlc_types), 0, + NULL, HFILL } + }, + { &hf_bvlc_function, + { "Function", "bvlc.function", + FT_UINT8, BASE_HEX, VALS(bvlc_function_names), 0, + "BVLC Function", HFILL } + }, + { &hf_bvlc_ipv6_function, + { "Function", "bvlc.function_ipv6", + FT_UINT8, BASE_HEX, VALS(bvlc_ipv6_function_names), 0, + "BVLC Function IPV6", HFILL } + }, + { &hf_bvlc_length, + { "BVLC-Length", "bvlc.length", + FT_UINT16, BASE_DEC, NULL, 0, + "Length of BVLC", HFILL } + }, + { &hf_bvlc_virt_source, + { "BVLC-Virtual-Source", "bvlc.virtual_source", + FT_UINT24, BASE_DEC_HEX, NULL, 0, + "Virtual source address of BVLC", HFILL } + }, + { &hf_bvlc_virt_dest, + { "BVLC-Virtual-Destination", "bvlc.virtual_dest", + FT_UINT24, BASE_DEC_HEX, NULL, 0, + "Virtual destination address of BVLC", HFILL } + }, + { &hf_bvlc_result_ip4, + { "Result", "bvlc.result", + FT_UINT16, BASE_HEX, VALS(bvlc_result_names), 0, + "Result Code", HFILL } + }, + { &hf_bvlc_result_ip6, + { "Result", "bvlc.result", + FT_UINT16, BASE_HEX, VALS(bvlc_ipv6_result_names), 0, + "Result Code", HFILL } + }, + { &hf_bvlc_bdt_ip, + { "IP", "bvlc.bdt_ip", + FT_IPv4, BASE_NONE, NULL, 0, + "BDT IP", HFILL } + }, + { &hf_bvlc_bdt_port, + { "Port", "bvlc.bdt_port", + FT_UINT16, BASE_DEC, NULL, 0, + "BDT Port", HFILL } + }, + { &hf_bvlc_bdt_mask, + { "Mask", "bvlc.bdt_mask", + FT_BYTES, BASE_NONE, NULL, 0, + "BDT Broadcast Distribution Mask", HFILL } + }, + { &hf_bvlc_reg_ttl, + { "TTL", "bvlc.reg_ttl", + FT_UINT16, BASE_DEC, NULL, 0, + "Foreign Device Time To Live", HFILL } + }, + { &hf_bvlc_fdt_ip, + { "IP", "bvlc.fdt_ip", + FT_IPv4, BASE_NONE, NULL, 0, + "FDT IP", HFILL } + }, + { &hf_bvlc_fdt_ipv6, + { "IP", "bvlc.fdt_ipv6", + FT_IPv6, BASE_NONE, NULL, 0, + "FDT IP", HFILL } + }, + { &hf_bvlc_fdt_port, + { "Port", "bvlc.fdt_port", + FT_UINT16, BASE_DEC, NULL, 0, + "FDT Port", HFILL } + }, + { &hf_bvlc_fdt_ttl, + { "TTL", "bvlc.fdt_ttl", + FT_UINT16, BASE_DEC, NULL, 0, + "Foreign Device Time To Live", HFILL } + }, + { &hf_bvlc_fdt_timeout, + { "Timeout", "bvlc.fdt_timeout", + FT_UINT16, BASE_DEC, NULL, 0, + "Foreign Device Timeout (seconds)", HFILL } + }, + { &hf_bvlc_fwd_ip, + { "IP", "bvlc.fwd_ip", + FT_IPv4, BASE_NONE, NULL, 0, + "FWD IP", HFILL } + }, + { &hf_bvlc_fwd_port, + { "Port", "bvlc.fwd_port", + FT_UINT16, BASE_DEC, NULL, 0, + "FWD Port", HFILL } + }, + { &hf_bvlc_orig_source_addr, + { "IP", "bvlc.orig_source_addr", + FT_IPv6, BASE_NONE, NULL, 0, + "ORIG IP", HFILL } + }, + { &hf_bvlc_orig_source_port, + { "Port", "bvlc.orig_source_port", + FT_UINT16, BASE_DEC, NULL, 0, + "ORIG Port", HFILL } + }, + }; + + static gint *ett[] = { + &ett_bvlc, + &ett_bdt, + &ett_fdt, + }; + + static hf_register_info bsc_hf[] = { + { &hf_bscvlc_control, + { "Control", "bscvlc.control", + FT_UINT8, BASE_HEX, NULL, 0, + "BSCVLC Control", HFILL } + }, + { &hf_bscvlc_control_data_option, + { "Data Option", "bscvlc.control_data_option", + FT_BOOLEAN, 8, TFS(&control_data_option_set_high), + BSCVLC_CONTROL_DATA_OPTION, "BSCVLC Control", HFILL } + }, + { &hf_bscvlc_control_destination_option, + { "Destination Option", "bscvlc.control_dest_option", + FT_BOOLEAN, 8, TFS(&control_destination_option_set_high), + BSCVLC_CONTROL_DEST_OPTION, "BSCVLC Control", HFILL } + }, + { &hf_bscvlc_control_destination_address, + { "Destination Address","bscvlc.control_dest_address", + FT_BOOLEAN, 8, TFS(&control_destination_address_set_high), + BSCVLC_CONTROL_DEST_ADDRESS, "BSCVLC Control", HFILL } + }, + { &hf_bscvlc_control_origin_address, + { "Origin Address", "bscvlc.control_orig_address", + FT_BOOLEAN, 8, TFS(&control_orig_address_set_high), + BSCVLC_CONTROL_ORIG_ADDRESS, "BSCVLC Control", HFILL } + }, + { &hf_bscvlc_control_reserved, + { "Reserved", "bscvlc.control_reserved", + FT_BOOLEAN, 8, TFS(&control_reserved_set_high), + BSCVLC_CONTROL_RESERVED, "BSCVLC Control", HFILL } + }, + { &hf_bscvlc_header, + { "Header Data Length", "bscvlc.header", + FT_UINT8, BASE_HEX, NULL, 0, + "BSCVLC Header Control Data", HFILL } + }, + { &hf_bscvlc_header_marker, + { "Header Error Marker","bscvlc.header_error_marker", + FT_UINT8, BASE_HEX, NULL, 0, + "BSCVLC Header Error Marker", HFILL } + }, + { &hf_bscvlc_header_length, + { "Header Data Length", "bscvlc.header_length", + FT_UINT16, BASE_DEC, NULL, 0, + "BSCVLC Header Data Length", HFILL } + }, + { &hf_bscvlc_header_data, + { "Header Data", "bscvlc.header_data", + FT_BYTES, BASE_NONE, NULL, 0, + "BSCVLC Header Option", HFILL } + }, + { &hf_bscvlc_header_opt_type, + { "Header Type", "bscvlc.header_type", + FT_UINT8, BASE_HEX, VALS(bscvlc_header_type_names), + BSCVLC_HEADER_OPTION_TYPE, "BSCVLC Header Option", HFILL } + }, + { &hf_bscvlc_header_opt_data, + { "Header Data", "bscvlc.header_data_present", + FT_BOOLEAN, 8, TFS(&header_opt_data_set_high), + BSCVLC_HEADER_OPTION_DATA, "BSCVLC Header Option", HFILL } + }, + { &hf_bscvlc_header_opt_must_understand, + { "Header Must Understand","bscvlc.header_understand", + FT_BOOLEAN, 8, TFS(&header_opt_must_understand_set_high), + BSCVLC_HEADER_OPTION_MUST_UNDERSTAND, "BSCVLC Header Option", HFILL } + }, + { &hf_bscvlc_header_opt_more, + { "Header More", "bscvlc.header_more", + FT_BOOLEAN, 8, TFS(&header_opt_more_set_high), + BSCVLC_HEADER_OPTION_MORE_OPTIONS, "BSCVLC Header Option", HFILL } + }, + { &hf_bscvlc_vendor_id, + { "Vendor ID", "bscvlc.vendor_id", + FT_UINT16, BASE_HEX, NULL, 0, + "BSCVLC Vendor ID", HFILL } + }, + { &hf_bscvlc_proprietary_opt_type, + { "Proprietary Type", "bscvlc.proprietary_type", + FT_UINT8, BASE_HEX, NULL, 0, + "BSCVLC Proprietary Type", HFILL } + }, + { &hf_bscvlc_proprietary_data, + { "Proprietary Data", "bscvlc.proprietary_data", + FT_BYTES, BASE_NONE, NULL, 0, + "BSCVLC Proprietary Data", HFILL } + }, + { &hf_bscvlc_hub_conn_state, + { "Hub Connection Status","bscvlc.hub_conn_state", + FT_UINT8, BASE_HEX, VALS(bscvlc_hub_conn_state_names), 0, + "BSCVLC Hub Connection Status", HFILL } + }, + { &hf_bscvlc_accept_conns, + { "Hub Accepts Connections","bscvlc.accept_conns", + FT_UINT8, BASE_HEX, VALS(bscvlc_hub_accept_conns_names), 0, + "BSCVLC Accepts Connections", HFILL } + }, + { &hf_bscvlc_max_bvlc_length, + { "Max. BVLC Length", "bscvlc.max_bvlc_length", + FT_UINT16, BASE_DEC, NULL, 0, + "Max Supported BVLC Length", HFILL } + }, + { &hf_bscvlc_max_npdu_length, + { "Max. NPDU Length", "bscvlc.max_npdu_length", + FT_UINT16, BASE_DEC, NULL, 0, + "Max Supported NPDU Length", HFILL } + }, + { &hf_bscvlc_function, + { "Function", "bscvlc.function", + FT_UINT8, BASE_HEX, VALS(bscvlc_function_names), 0, + "BSCVLC Function", HFILL } + }, + { &hf_bscvlc_result, + { "Result", "bscvlc.result", + FT_UINT8, BASE_HEX, VALS(bscvlc_result_names), 0, + "Result Code", HFILL } + }, + { &hf_bscvlc_error_class, + { "Error Class", "bscvlc.error_class", + FT_UINT32, BASE_DEC, VALS(BACnetErrorClass), 0, NULL, HFILL } + }, + { &hf_bscvlc_error_code, + { "Error Code", "bscvlc.error_code", + FT_UINT32, BASE_DEC, VALS(BACnetErrorCode), 0, NULL, HFILL } + }, + { &hf_bscvlc_result_data, + { "Result Data", "bscvlc.result_data", + FT_BYTES, BASE_NONE, NULL, 0, + "BSCVLC Result Data", HFILL } + }, + { &hf_bscvlc_uris, + { "URI's", "bscvlc.uris", + FT_BYTES, BASE_NONE, NULL, 0, + "BSCVLC Address URI's", HFILL } + }, + { &hf_bscvlc_msg_id, + { "Message ID", "bscvlc.msgid", + FT_UINT16, BASE_DEC, NULL, 0, + "BSCVLC Message ID", HFILL } + }, + { &hf_bscvlc_orig_vmac, + { "SVMAC", "bscvlc.orig_virtual_address", + FT_BYTES, BASE_NONE, NULL, 0, + "ORIG VMAC", HFILL } + }, + { &hf_bscvlc_dest_vmac, + { "DVMAC", "bscvlc.dest_virtual_address", + FT_BYTES, BASE_NONE, NULL, 0, + "DEST VMAC", HFILL } + }, + { &hf_bscvlc_connect_vmac, + { "Connecting VMAC", "bscvlc.connect_virtual_address", + FT_BYTES, BASE_NONE, NULL, 0, + "BSCVLC Connecting VMAC", HFILL } + }, + { &hf_bscvlc_connect_uuid, + { "Connecting UUID", "bscvlc.connect_uuid", + FT_BYTES, BASE_NONE, NULL, 0, + "BSCVLC Connecting UUID", HFILL } + }, + }; + + static gint *bsc_ett[] = { + &ett_bscvlc, + &ett_bscvlc_ctrl, + &ett_bscvlc_hdr + }; + + proto_bvlc = proto_register_protocol("BACnet Virtual Link Control", "BVLC", "bvlc"); + + proto_register_field_array(proto_bvlc, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + bvlc_handle = register_dissector("bvlc", dissect_bvlc, proto_bvlc); + + bvlc_dissector_table = register_dissector_table("bvlc.function", "BVLC Function", proto_bvlc, FT_UINT8, BASE_HEX); + bvlc_ipv6_dissector_table = register_dissector_table("bvlc.function_ipv6", "BVLC Function IPV6", proto_bvlc, FT_UINT8, BASE_HEX); + + proto_bscvlc = proto_register_protocol("BACnet Secure Connect Virtual Link Control", "BSCVLC", "bscvlc"); + + proto_register_field_array(proto_bscvlc, bsc_hf, array_length(bsc_hf)); + proto_register_subtree_array(bsc_ett, array_length(bsc_ett)); + + bscvlc_handle = register_dissector("bscvlc", dissect_bscvlc, proto_bscvlc); + + bscvlc_dissector_table = register_dissector_table("bscvlc.function", "BSCVLC Function", proto_bscvlc, FT_UINT8, BASE_HEX); +} + +void +proto_reg_handoff_bvlc(void) +{ + dissector_add_uint_with_preference("udp.port", BVLC_UDP_PORT, bvlc_handle); + dissector_add_string("ws.protocol", "hub.bsc.bacnet.org", bscvlc_handle); + dissector_add_string("ws.protocol", "dc.bsc.bacnet.org", bscvlc_handle); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ |