summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-bvlc.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-bvlc.c')
-rw-r--r--epan/dissectors/packet-bvlc.c1589
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:
+ */