diff options
Diffstat (limited to 'epan/dissectors/packet-bacapp.c')
-rw-r--r-- | epan/dissectors/packet-bacapp.c | 17073 |
1 files changed, 17073 insertions, 0 deletions
diff --git a/epan/dissectors/packet-bacapp.c b/epan/dissectors/packet-bacapp.c new file mode 100644 index 00000000..1d8b5cd5 --- /dev/null +++ b/epan/dissectors/packet-bacapp.c @@ -0,0 +1,17073 @@ +/* packet-bacapp.c + * Routines for BACnet (APDU) dissection + * Copyright 2001, Hartmut Mueller <hartmut[AT]abmlinux.org>, FH Dortmund + * Enhanced by Steve Karg, 2005, <skarg[AT]users.sourceforge.net>, Atlanta + * Enhanced by Herbert Lischka, 2005, <lischka[AT]kieback-peter.de>, Berlin + * Enhanced by Felix Kraemer, 2010, <sauter-cumulus[AT]de.sauter-bc.com>, + * Sauter-Cumulus GmbH, Freiburg + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald[AT]wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include <epan/packet.h> +#include <epan/to_str.h> +#include <epan/strutil.h> +#include <epan/reassemble.h> +#include <epan/expert.h> +#include <epan/proto_data.h> +#include <epan/stats_tree.h> +#include "packet-bacapp.h" + +static int bacapp_tap = -1; + +/* formerly bacapp.h contains definitions and forward declarations */ + +/* BACnet PDU Types */ +#define BACAPP_TYPE_CONFIRMED_SERVICE_REQUEST 0 +#define BACAPP_TYPE_UNCONFIRMED_SERVICE_REQUEST 1 +#define BACAPP_TYPE_SIMPLE_ACK 2 +#define BACAPP_TYPE_COMPLEX_ACK 3 +#define BACAPP_TYPE_SEGMENT_ACK 4 +#define BACAPP_TYPE_ERROR 5 +#define BACAPP_TYPE_REJECT 6 +#define BACAPP_TYPE_ABORT 7 +#define MAX_BACAPP_TYPE 8 + +#define BACAPP_SEGMENTED_REQUEST 0x08 +#define BACAPP_MORE_SEGMENTS 0x04 +#define BACAPP_SEGMENTED_RESPONSE 0x02 +#define BACAPP_SEGMENT_NAK 0x02 +#define BACAPP_SENT_BY 0x01 + +#define BACAPP_MAX_RECURSION_DEPTH 100 // Arbitrary + +/** + * dissect_bacapp ::= CHOICE { + * confirmed-request-PDU [0] BACnet-Confirmed-Request-PDU, + * unconfirmed-request-PDU [1] BACnet-Unconfirmed-Request-PDU, + * simpleACK-PDU [2] BACnet-SimpleACK-PDU, + * complexACK-PDU [3] BACnet-ComplexACK-PDU, + * segmentACK-PDU [4] BACnet-SegmentACK-PDU, + * error-PDU [5] BACnet-Error-PDU, + * reject-PDU [6] BACnet-Reject-PDU, + * abort-PDU [7] BACnet-Abort-PDU + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + **/ + +/** + * ConfirmedRequest-PDU ::= SEQUENCE { + * pdu-type [0] Unsigned (0..15), -- 0 for this PDU Type + * segmentedMessage [1] BOOLEAN, + * moreFollows [2] BOOLEAN, + * segmented-response-accepted [3] BOOLEAN, + * reserved [4] Unsigned (0..3), -- must be set zero + * max-segments-accepted [5] Unsigned (0..7), -- as per 20.1.2.4 + * max-APDU-length-accepted [5] Unsigned (0..15), -- as per 20.1.2.5 + * invokeID [6] Unsigned (0..255), + * sequence-number [7] Unsigned (0..255) OPTIONAL, -- only if segmented msg + * proposed-window-size [8] Unsigned (0..127) OPTIONAL, -- only if segmented msg + * service-choice [9] BACnetConfirmedServiceChoice, + * service-request [10] BACnet-Confirmed-Service-Request OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fConfirmedRequestPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param ack - indocates whether working on request or ack + * @param svc - output variable to return service choice + * @param tt - output varable to return service choice item + * @return modified offset + */ +static guint +fStartConfirmed(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint8 ack, + gint *svc, proto_item **tt); + +/** + * Unconfirmed-Request-PDU ::= SEQUENCE { + * pdu-type [0] Unsigned (0..15), -- 1 for this PDU type + * reserved [1] Unsigned (0..15), -- must be set zero + * service-choice [2] BACnetUnconfirmedServiceChoice, + * service-request [3] BACnetUnconfirmedServiceRequest -- Context-specific tags 0..3 are NOT used in header encoding + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fUnconfirmedRequestPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * SimpleACK-PDU ::= SEQUENCE { + * pdu-type [0] Unsigned (0..15), -- 2 for this PDU type + * reserved [1] Unsigned (0..15), -- must be set zero + * invokeID [2] Unsigned (0..255), + * service-ACK-choice [3] BACnetUnconfirmedServiceChoice -- Context-specific tags 0..3 are NOT used in header encoding + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSimpleAckPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ComplexACK-PDU ::= SEQUENCE { + * pdu-type [0] Unsigned (0..15), -- 3 for this PDU Type + * segmentedMessage [1] BOOLEAN, + * moreFollows [2] BOOLEAN, + * reserved [3] Unsigned (0..3), -- must be set zero + * invokeID [4] Unsigned (0..255), + * sequence-number [5] Unsigned (0..255) OPTIONAL, -- only if segmented msg + * proposed-window-size [6] Unsigned (0..127) OPTIONAL, -- only if segmented msg + * service-ACK-choice [7] BACnetConfirmedServiceChoice, + * service-ACK [8] BACnet-Confirmed-Service-Request -- Context-specific tags 0..8 are NOT used in header encoding + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fComplexAckPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * SegmentACK-PDU ::= SEQUENCE { + * pdu-type [0] Unsigned (0..15), -- 4 for this PDU Type + * reserved [1] Unsigned (0..3), -- must be set zero + * negative-ACK [2] BOOLEAN, + * server [3] BOOLEAN, + * original-invokeID [4] Unsigned (0..255), + * sequence-number [5] Unsigned (0..255), + * actual-window-size [6] Unsigned (0..127) + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSegmentAckPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * Error-PDU ::= SEQUENCE { + * pdu-type [0] Unsigned (0..15), -- 5 for this PDU Type + * reserved [1] Unsigned (0..3), -- must be set zero + * original-invokeID [2] Unsigned (0..255), + * error-choice [3] BACnetConfirmedServiceChoice, + * error [4] BACnet-Error + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fErrorPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * Reject-PDU ::= SEQUENCE { + * pdu-type [0] Unsigned (0..15), -- 6 for this PDU Type + * reserved [1] Unsigned (0..3), -- must be set zero + * original-invokeID [2] Unsigned (0..255), + * reject-reason [3] BACnetRejectReason + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fRejectPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * Abort-PDU ::= SEQUENCE { + * pdu-type [0] Unsigned (0..15), -- 7 for this PDU Type + * reserved [1] Unsigned (0..3), -- must be set zero + * server [2] BOOLEAN, + * original-invokeID [3] Unsigned (0..255), + * abort-reason [4] BACnetAbortReason + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fAbortPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * 20.2.4, adds the label with max 64Bit unsigned Integer Value to tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param label the label of this item + * @return modified offset + */ +static guint +fUnsignedTag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +/** + * 20.2.5, adds the label with max 64Bit signed Integer Value to tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param label the label of this item + * @return modified offset + */ +static guint +fSignedTag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +/** + * 20.2.8, adds the label with Octet String to tree; if lvt == 0 then lvt = restOfFrame + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param label the label of this item + * @param lvt length of String + * @return modified offset + */ +static guint +fOctetString(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label, guint32 lvt); + +/** + * 20.2.12, adds the label with Date Value to tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param label the label of this item + * @return modified offset + */ +static guint +fDate(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +/** + * 20.2.13, adds the label with Time Value to tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param label the label of this item + * @return modified offset + */ +static guint +fTime(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +/** + * 20.2.14, adds Object Identifier to tree + * use BIG ENDIAN: Bits 31..22 Object Type, Bits 21..0 Instance Number + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param label the label of this item + * @return modified offset + */ +static guint +fObjectIdentifier(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +/** + * BACnet-Confirmed-Service-Request ::= CHOICE { + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param service_choice the service choice + * @return offset + */ +static guint +fConfirmedServiceRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, gint service_choice); + +/** + * BACnet-Confirmed-Service-ACK ::= CHOICE { + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param service_choice the service choice + * @return offset + */ +static guint +fConfirmedServiceAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, gint service_choice); + +/** + * AcknowledgeAlarm-Request ::= SEQUENCE { + * acknowledgingProcessIdentifier [0] Unsigned32, + * eventObjectIdentifier [1] BACnetObjectIdentifer, + * eventStateAcknowledge [2] BACnetEventState, + * timeStamp [3] BACnetTimeStamp, + * acknowledgementSource [4] Character String, + * timeOfAcknowledgement [5] BACnetTimeStamp + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fAcknowledgeAlarmRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ConfirmedCOVNotification-Request ::= SEQUENCE { + * subscriberProcessIdentifier [0] Unsigned32, + * initiatingDeviceIdentifier [1] BACnetObjectIdentifer, + * monitoredObjectIdentifier [2] BACnetObjectIdentifer, + * timeRemaining [3] unsigned, + * listOfValues [4] SEQUENCE OF BACnetPropertyValues + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fConfirmedCOVNotificationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ConfirmedEventNotification-Request ::= SEQUENCE { + * ProcessIdentifier [0] Unsigned32, + * initiatingDeviceIdentifier [1] BACnetObjectIdentifer, + * eventObjectIdentifier [2] BACnetObjectIdentifer, + * timeStamp [3] BACnetTimeStamp, + * notificationClass [4] unsigned, + * priority [5] unsigned8, + * eventType [6] BACnetEventType, + * messageText [7] CharacterString OPTIONAL, + * notifyType [8] BACnetNotifyType, + * ackRequired [9] BOOLEAN OPTIONAL, + * fromState [10] BACnetEventState OPTIONAL, + * toState [11] BACnetEventState, + * eventValues [12] BACnetNotificationParameters OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fConfirmedEventNotificationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * GetAlarmSummary-ACK ::= SEQUENCE OF SEQUENCE { + * objectIdentifier BACnetObjectIdentifer, + * alarmState BACnetEventState, + * acknowledgedTransitions BACnetEventTransitionBits + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fGetAlarmSummaryAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * GetEnrollmentSummary-Request ::= SEQUENCE { + * acknowledgmentFilter [0] ENUMERATED { + * all (0), + * acked (1), + * not-acked (2) + * }, + * enrollmentFilter [1] BACnetRecipientProcess OPTIONAL, + * eventStateFilter [2] ENUMERATED { + * offnormal (0), + * fault (1), + * normal (2), + * all (3), + * active (4) + * }, + * eventTypeFilter [3] BACnetEventType OPTIONAL, + * priorityFilter [4] SEQUENCE { + * minPriority [0] Unsigned8, + * maxPriority [1] Unsigned8 + * } OPTIONAL, + * notificationClassFilter [5] Unsigned OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fGetEnrollmentSummaryRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * GetEnrollmentSummary-ACK ::= SEQUENCE OF SEQUENCE { + * objectIdentifier BACnetObjectIdentifer, + * eventType BACnetEventType, + * eventState BACnetEventState, + * priority Unsigned8, + * notificationClass Unsigned OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fGetEnrollmentSummaryAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * GetEventInformation-Request ::= SEQUENCE { + * lastReceivedObjectIdentifier [0] BACnetObjectIdentifer + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fGetEventInformationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * GetEventInformation-ACK ::= SEQUENCE { + * listOfEventSummaries [0] listOfEventSummaries, + * moreEvents [1] BOOLEAN + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fGetEventInformationACK(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * LifeSafetyOperation-Request ::= SEQUENCE { + * requestingProcessIdentifier [0] Unsigned32 + * requestingSource [1] CharacterString + * request [2] BACnetLifeSafetyOperation + * objectIdentifier [3] BACnetObjectIdentifier OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fLifeSafetyOperationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +/** + * SubscribeCOV-Request ::= SEQUENCE { + * subscriberProcessIdentifier [0] Unsigned32 + * monitoredObjectIdentifier [1] BACnetObjectIdentifier + * issueConfirmedNotifications [2] BOOLEAN OPTIONAL + * lifetime [3] Unsigned OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSubscribeCOVRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * SubscribeCOVProperty-Request ::= SEQUENCE { + * subscriberProcessIdentifier [0] Unsigned32 + * monitoredObjectIdentifier [1] BACnetObjectIdentifier + * issueConfirmedNotifications [2] BOOLEAN OPTIONAL + * lifetime [3] Unsigned OPTIONAL + * monitoredPropertyIdentifier [4] BACnetPropertyReference OPTIONAL + * covIncrement [5] Unsigned OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSubscribeCOVPropertyRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * AtomicReadFile-Request ::= SEQUENCE { + * fileIdentifier BACnetObjectIdentifier, + * accessMethod CHOICE { + * streamAccess [0] SEQUENCE { + * fileStartPosition INTEGER, + * requestedOctetCount Unsigned + * }, + * recordAccess [1] SEQUENCE { + * fileStartRecord INTEGER, + * requestedRecordCount Unsigned + * } + * } + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fAtomicReadFileRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * AtomicWriteFile-ACK ::= SEQUENCE { + * endOfFile BOOLEAN, + * accessMethod CHOICE { + * streamAccess [0] SEQUENCE { + * fileStartPosition INTEGER, + * fileData OCTET STRING + * }, + * recordAccess [1] SEQUENCE { + * fileStartRecord INTEGER, + * returnedRecordCount Unsigned, + * fileRecordData SEQUENCE OF OCTET STRING + * } + * } + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fAtomicReadFileAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * AtomicWriteFile-Request ::= SEQUENCE { + * fileIdentifier BACnetObjectIdentifier, + * accessMethod CHOICE { + * streamAccess [0] SEQUENCE { + * fileStartPosition INTEGER, + * fileData OCTET STRING + * }, + * recordAccess [1] SEQUENCE { + * fileStartRecord INTEGER, + * recordCount Unsigned, + * fileRecordData SEQUENCE OF OCTET STRING + * } + * } + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fAtomicWriteFileRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * AtomicWriteFile-ACK ::= SEQUENCE { + * fileStartPosition [0] INTEGER, + * fileStartRecord [1] INTEGER, + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fAtomicWriteFileAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * AddListElement-Request ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + * listOfElements [3] ABSTRACT-SYNTAX.&Type + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fAddListElementRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * CreateObject-Request ::= SEQUENCE { + * objectSpecifier [0] ObjectSpecifier, + * listOfInitialValues [1] SEQUENCE OF BACnetPropertyValue OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param subtree the sub tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fCreateObjectRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *subtree, guint offset); + +/** + * CreateObject-Request ::= BACnetObjectIdentifier + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fCreateObjectAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * DeleteObject-Request ::= SEQUENCE { + * ObjectIdentifier BACnetObjectIdentifer + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fDeleteObjectRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ReadProperty-Request ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fReadPropertyRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ReadProperty-ACK ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + * propertyValue [3] ABSTRACT-SYNTAX.&Type + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fReadPropertyAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ReadPropertyConditional-Request ::= SEQUENCE { + * objectSelectionCriteria [0] objectSelectionCriteria, + * listOfPropertyReferences [1] SEQUENCE OF BACnetPropertyReference OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param subtree the sub tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fReadPropertyConditionalRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *subtree, guint offset); + +/** + * ReadPropertyConditional-ACK ::= SEQUENCE { + * listOfPReadAccessResults SEQUENCE OF ReadAccessResult OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fReadPropertyConditionalAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ReadPropertyMultiple-Request ::= SEQUENCE { + * listOfReadAccessSpecs SEQUENCE OF ReadAccessSpecification + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param subtree the sub tree to append this item to + * @param offset the offset in the tvb + * @return offset modified + */ +static guint +fReadPropertyMultipleRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *subtree, guint offset); + +/** + * ReadPropertyMultiple-Ack ::= SEQUENCE { + * listOfReadAccessResults SEQUENCE OF ReadAccessResult + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return offset modified + */ +static guint +fReadPropertyMultipleAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ReadRange-Request ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + * range CHOICE { + * byPosition [3] SEQUENCE { + * referencedIndex Unsigned, + * count INTEGER + * }, + * byTime [4] SEQUENCE { + * referenceTime BACnetDateTime, + * count INTEGER + * }, + * timeRange [5] SEQUENCE { + * beginningTime BACnetDateTime, + * endingTime BACnetDateTime + * }, + * } OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fReadRangeRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ReadRange-ACK ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + * resultFlags [3] BACnetResultFlags, + * itemCount [4] Unsigned, + * itemData [5] SEQUENCE OF ABSTRACT-SYNTAX.&Type + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fReadRangeAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * RemoveListElement-Request ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + * listOfElements [3] ABSTRACT-SYNTAX.&Type + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fRemoveListElementRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * WriteProperty-Request ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + * propertyValue [3] ABSTRACT-SYNTAX.&Type + * priority [4] Unsigned8 (1..16) OPTIONAL --used only when property is commandable + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fWritePropertyRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * WritePropertyMultiple-Request ::= SEQUENCE { + * listOfWriteAccessSpecifications SEQUENCE OF WriteAccessSpecification + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fWritePropertyMultipleRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * DeviceCommunicationControl-Request ::= SEQUENCE { + * timeDuration [0] Unsigned16 OPTIONAL, + * enable-disable [1] ENUMERATED { + * enable (0), + * disable (1) + * }, + * password [2] CharacterString (SIZE(1..20)) OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fDeviceCommunicationControlRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ConfirmedPrivateTransfer-Request ::= SEQUENCE { + * vendorID [0] Unsigned, + * serviceNumber [1] Unsigned, + * serviceParameters [2] ABSTRACT-SYNTAX.&Type OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fConfirmedPrivateTransferRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ConfirmedPrivateTransfer-ACK ::= SEQUENCE { + * vendorID [0] Unsigned, + * serviceNumber [1] Unsigned, + * resultBlock [2] ABSTRACT-SYNTAX.&Type OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fConfirmedPrivateTransferAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ConfirmedTextMessage-Request ::= SEQUENCE { + * textMessageSourceDevice [0] BACnetObjectIdentifier, + * messageClass [1] CHOICE { + * numeric [0] Unsigned, + * character [1] CharacterString + * } OPTIONAL, + * messagePriority [2] ENUMERATED { + * normal (0), + * urgent (1) + * }, + * message [3] CharacterString + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fConfirmedTextMessageRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ReinitializeDevice-Request ::= SEQUENCE { + * reinitializedStateOfDevice [0] ENUMERATED { + * coldstart (0), + * warmstart (1), + * startbackup (2), + * endbackup (3), + * startrestore (4), + * endrestore (5), + * abortrestor (6) + * }, + * password [1] CharacterString (SIZE(1..20)) OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fReinitializeDeviceRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * VTOpen-Request ::= SEQUENCE { + * vtClass BACnetVTClass, + * localVTSessionIdentifier Unsigned8 + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fVtOpenRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * VTOpen-ACK ::= SEQUENCE { + * remoteVTSessionIdentifier Unsigned8 + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fVtOpenAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * VTClose-Request ::= SEQUENCE { + * listOfRemoteVTSessionIdentifiers SEQUENCE OF Unsigned8 + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fVtCloseRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * VTData-Request ::= SEQUENCE { + * vtSessionIdentifier Unsigned8, + * vtNewData OCTET STRING, + * vtDataFlag Unsigned (0..1) + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fVtDataRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * VTData-ACK ::= SEQUENCE { + * allNewDataAccepted [0] BOOLEAN, + * acceptedOctetCount [1] Unsigned OPTIONAL -- present only if allNewDataAccepted = FALSE + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fVtDataAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * Authenticate-Request ::= SEQUENCE { + * pseudoRandomNumber [0] Unsigned32, + * excpectedInvokeID [1] Unsigned8 OPTIONAL, + * operatorName [2] CharacterString OPTIONAL, + * operatorPassword [3] CharacterString (SIZE(1..20)) OPTIONAL, + * startEncypheredSession [4] BOOLEAN OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fAuthenticateRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * Authenticate-ACK ::= SEQUENCE { + * modifiedRandomNumber Unsigned32, + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fAuthenticateAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * RequestKey-Request ::= SEQUENCE { + * requestingDeviceIdentifier BACnetObjectIdentifier, + * requestingDeviceAddress BACnetAddress, + * remoteDeviceIdentifier BACnetObjectIdentifier, + * remoteDeviceAddress BACnetAddress + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fRequestKeyRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * Unconfirmed-Service-Request ::= CHOICE { + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param service_choice the service choice + * @return modified offset + */ +static guint +fUnconfirmedServiceRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, gint service_choice); + +/** + * UnconfirmedCOVNotification-Request ::= SEQUENCE { + * subscriberProcessIdentifier [0] Unsigned32, + * initiatingDeviceIdentifier [1] BACnetObjectIdentifer, + * monitoredObjectIdentifier [2] BACnetObjectIdentifer, + * timeRemaining [3] unsigned, + * listOfValues [4] SEQUENCE OF BACnetPropertyValues + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fUnconfirmedCOVNotificationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * UnconfirmedEventNotification-Request ::= SEQUENCE { + * ProcessIdentifier [0] Unsigned32, + * initiatingDeviceIdentifier [1] BACnetObjectIdentifer, + * eventObjectIdentifier [2] BACnetObjectIdentifer, + * timeStamp [3] BACnetTimeStamp, + * notificationClass [4] unsigned, + * priority [5] unsigned8, + * eventType [6] BACnetEventType, + * messageText [7] CharacterString OPTIONAL, + * notifyType [8] BACnetNotifyType, + * ackRequired [9] BOOLEAN OPTIONAL, + * fromState [10] BACnetEventState OPTIONAL, + * toState [11] BACnetEventState, + * eventValues [12] BACnetNotificationParameters OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fUnconfirmedEventNotificationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * I-Am-Request ::= SEQUENCE { + * aAmDeviceIdentifier BACnetObjectIdentifier, + * maxAPDULengthAccepted Unsigned, + * segmentationSupported BACnetSegmentation, + * vendorID Unsigned + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fIAmRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + + +/** + * I-Have-Request ::= SEQUENCE { + * deviceIdentifier BACnetObjectIdentifier, + * objectIdentifier BACnetObjectIdentifier, + * objectName CharacterString + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fIHaveRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * UnconfirmedPrivateTransfer-Request ::= SEQUENCE { + * vendorID [0] Unsigned, + * serviceNumber [1] Unsigned, + * serviceParameters [2] ABSTRACT-SYNTAX.&Type OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fUnconfirmedPrivateTransferRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * UnconfirmedTextMessage-Request ::= SEQUENCE { + * textMessageSourceDevice [0] BACnetObjectIdentifier, + * messageClass [1] CHOICE { + * numeric [0] Unsigned, + * character [1] CharacterString + * } OPTIONAL, + * messagePriority [2] ENUMERATED { + * normal (0), + * urgent (1) + * }, + * message [3] CharacterString + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fUnconfirmedTextMessageRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * TimeSynchronization-Request ::= SEQUENCE { + * BACnetDateTime + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fTimeSynchronizationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * UTCTimeSynchronization-Request ::= SEQUENCE { + * BACnetDateTime + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fUTCTimeSynchronizationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * Who-Has-Request ::= SEQUENCE { + * limits SEQUENCE { + * deviceInstanceRangeLowLimit [0] Unsigned (0..4194303), + * deviceInstanceRangeHighLimit [1] Unsigned (0..4194303) + * } OPTIONAL, + * object CHOICE { + * objectIdentifier [2] BACnetObjectIdentifier, + * objectName [3] CharacterString + * } + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fWhoHas(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * Who-Is-Request ::= SEQUENCE { + * deviceInstanceRangeLowLimit [0] Unsigned (0..4194303) OPTIONAL, -- must be used as a pair, see 16.9, + * deviceInstanceRangeHighLimit [0] Unsigned (0..4194303) OPTIONAL, -- must be used as a pair, see 16.9, + * } + * @param tvb the tv buffer of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fWhoIsRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnet-Error ::= CHOICE { + * addListElement [8] ChangeList-Error, + * removeListElement [9] ChangeList-Error, + * writePropertyMultiple [16] WritePropertyMultiple-Error, + * confirmedPrivatTransfer [18] ConfirmedPrivateTransfer-Error, + * vtClose [22] VTClose-Error, + * readRange [26] ObjectAccessService-Error + * [default] Error + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param service the service + * @return modified offset + */ +static guint +fBACnetError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint service); + +/** + * Dissect a BACnetError in a context tag + * + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint fContextTaggedError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ChangeList-Error ::= SEQUENCE { + * errorType [0] Error, + * firstFailedElementNumber [1] Unsigned + * } + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fChangeListError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * CreateObject-Error ::= SEQUENCE { + * errorType [0] Error, + * firstFailedElementNumber [1] Unsigned + * } + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fCreateObjectError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ConfirmedPrivateTransfer-Error ::= SEQUENCE { + * errorType [0] Error, + * vendorID [1] Unsigned, + * serviceNumber [2] Unsigned, + * errorParameters [3] ABSTRACT-SYNTAX.&Type OPTIONAL + * } + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fConfirmedPrivateTransferError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * WritePropertyMultiple-Error ::= SEQUENCE { + * errorType [0] Error, + * firstFailedWriteAttempt [1] Unsigned + * } + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fWritePropertyMultipleError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * VTClose-Error ::= SEQUENCE { + * errorType [0] Error, + * listOfVTSessionIdentifiers [1] SEQUENCE OF Unsigned8 OPTIONAL + * } + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fVTCloseError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnet Application Types chapter 20.2.1 + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param label the label of this item + * @return modified offset + */ +static guint +fApplicationTypes(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +/** + * BACnetActionCommand ::= SEQUENCE { + * deviceIdentifier [0] BACnetObjectIdentifier OPTIONAL, + * objectIdentifier [1] BACnetObjectIdentifier, + * propertyIdentifier [2] BACnetPropertyIdentifier, + * propertyArrayIndex [3] Unsigned OPTIONAL, -- used only with array datatype + * propertyValue [4] ABSTRACT-SYNTAX.&Type, + * priority [5] Unsigned (1..16) OPTIONAL, -- used only when property is commandable + * postDelay [6] Unsigned OPTIONAL, + * quitOnFailure [7] BOOLEAN, + * writeSuccessful [8] BOOLEAN + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param tag_match the tag number + * @return modified offset + */ +static guint +fActionCommand(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint8 tag_match); + +/** + * BACnetActionList ::= SEQUENCE { + * action [0] SEQUENCE of BACnetActionCommand + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fActionList(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** BACnetAddress ::= SEQUENCE { + * network-number Unsigned16, -- A value 0 indicates the local network + * mac-address OCTET STRING -- A string of length 0 indicates a broadcast + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fAddress(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetAddressBinding ::= SEQUENCE { + * deviceObjectID BACnetObjectIdentifier + * deviceAddress BacnetAddress + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fAddressBinding(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetCalendarEntry ::= CHOICE { + * date [0] Date, + * dateRange [1] BACnetDateRange, + * weekNDay [2] BacnetWeekNday + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fCalendarEntry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetClientCOV ::= CHOICE { + * real-increment REAL, + * default-increment NULL + * } + * @param tvb the tv buffer of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fClientCOV(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + + +/** + * BACnetDailySchedule ::= SEQUENCE { + * day-schedule [0] SENQUENCE OF BACnetTimeValue + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fDailySchedule(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetHealth ::= SEQUENCE { + * timestamp [0] BACnetDateTime, + * result [1] Error, + * property [2] BACnetPropertiyIdentifier OPTIONAL, + * details [3] CharacterString OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fHealth(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetSCFailedConnectionRequest ::= SEQUENCE { + * timestamp [0] BACnetDateTime, + * peer-address [1] BACnetHostNPort, + * peer-vmac [2] OCTET STRING (SIZE(6)) + * peer-uuid [3] OCTET STRING (SIZE(16)) + * error [4] Error OPTIONAL + * error-details [5] CharacterString OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSCFailedConnectionRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetSCDirectConnection ::= SEQUENCE { + * uri [0] CharacterString + * connection-state [1] BACnetSCConnectionState, + * connect-timestamp [2] BACnetDateTime, + * disconnect-timestamp [3] BACnetDateTime, + * peer-address [4] BACnetHostNPort, + * peer-vmac [5] OCTET STRING (SIZE(6)) + * peer-uuid [6] OCTET STRING (SIZE(16)) + * error [7] Error OPTIONAL + * error-details [8] CharacterString OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSCDirectConnection(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetSCHubConnection ::= SEQUENCE { + * connection-state [0] BACnetSCConnectionState, + * connect-timestamp [1] BACnetDateTime, + * disconnect-timestamp [2] BACnetDateTime, + * error [3] Error OPTIONAL + * error-details [4] CharacterString OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSCHubConnection(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetSCHubFunctionConnection ::= SEQUENCE { + * connection-state [0] BACnetSCConnectionState, + * connect-timestamp [1] BACnetDateTime, + * disconnect-timestamp [2] BACnetDateTime, + * peer-address [3] BACnetHostNPort, + * peer-vmac [4] OCTET STRING (SIZE(6)) + * peer-uuid [5] OCTET STRING (SIZE(16)) + * error [6] Error OPTIONAL + * error-details [7] CharacterString OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSCHubFunctionConnection(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetWeeklySchedule ::= SEQUENCE { + * week-schedule SENQUENCE SIZE (7) OF BACnetDailySchedule + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fWeeklySchedule(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetDateRange ::= SEQUENCE { + * StartDate Date, + * EndDate Date + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fDateRange(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetDateTime ::= SEQUENCE { + * date Date, + * time Time + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param label the label of this item + * @return modified offset + */ +static guint +fDateTime(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +/** + * BACnetDestination ::= SEQUENCE { + * validDays BACnetDaysOfWeek, + * fromTime Time, + * toTime Time, + * recipient BACnetRecipient, + * processIdentifier Unsigned32, + * issueConfirmedNotifications BOOLEAN, + * transitions BACnetEventTransitionBits + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fDestination(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetDeviceObjectPropertyReference ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigend OPTIONAL, + * deviceIdentifier [3] BACnetObjectIdentifier OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fDeviceObjectPropertyReference(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetObjectPropertyReference ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigend OPTIONAL, + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fObjectPropertyReference(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetDeviceObjectReference ::= SEQUENCE { + * deviceIdentifier [0] BACnetObjectIdentifier OPTIONAL, + * objectIdentifier [1] BACnetObjectIdentifier + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fDeviceObjectReference(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetEventParameter ::= CHOICE { + * change-of-bitstring [0] SEQUENCE { + * time-delay [0] Unsigned, + * bitmask [1] BIT STRING, + * list-of-bitstring-values [2] SEQUENCE OF BIT STRING + * }, + * change-of-state [1] SEQUENCE { + * time-delay [0] Unsigned, + * list-of-values [1] SEQUENCE OF BACnetPropertyStates + * }, + * change-of-value [2] SEQUENCE { + * time-delay [0] Unsigned, + * cov-criteria [1] CHOICE { + * bitmask [0] BIT STRING, + * referenced-property-increment [1] REAL + * } + * }, + * command-failure [3] SEQUENCE { + * time-delay [0] Unsigned, + * feedback-property-reference [1] BACnetDeviceObjectPropertyReference + * }, + * floating-limit [4] SEQUENCE { + * time-delay [0] Unsigned, + * setpoint-reference [1] BACnetDeviceObjectPropertyReference, + * low-diff-limit [2] REAL, + * high-diff-limit [3] REAL, + * deadband [4] REAL + * }, + * out-of-range [5] SEQUENCE { + * time-delay [0] Unsigned, + * low-limit [1] REAL, + * high-limit [2] REAL, + * deadband [3] REAL + * }, + * -- context tag 7 is deprecated + * change-of-life-safety [8] SEQUENCE { + * time-delay [0] Unsigned, + * list-of-life-safety-alarm-values [1] SEQUENCE OF BACnetLifeSafetyState, + * list-of-alarm-values [2] SEQUENCE OF BACnetLifeSafetyState, + * mode-property-reference [3] BACnetDeviceObjectPropertyReference + * }, + * extended [9] SEQUENCE { + * vendor-id [0] Unsigned16, + * extended-event-type [1] Unsigned, + * parameters [2] SEQUENCE OF CHOICE { + * null NULL, + * real REAL, + * integer Unsigned, + * boolean BOOLEAN, + * double Double, + * octet OCTET STRING, + * bitstring BIT STRING, + * enum ENUMERATED, + * reference [0] BACnetDeviceObjectPropertyReference + * } + * }, + * buffer-ready [10] SEQUENCE { + * notification-threshold [0] Unsigned, + * previous-notification-count [1] Unsigned32 + * }, + * unsigned-range [11] SEQUENCE { + * time-delay [0] Unsigned, + * low-limit [1] Unsigned, + * high-limit [2] Unsigned, + * } + * -- context tag 12 is reserved for future addenda + * access-event [13] SEQUENCE { + * list-of-access-events [0] SEQUENCE OF BACnetAccessEvent, + * access-event-time-reference [1] BACnetDeviceObjectPropertyReference + * } + * double-out-of-range [14] SEQUENCE { + * time-delay [0] Unsigned, + * low-limit [1] Double, + * high-limit [2] Double, + * deadband [3] Double + * } + * signed-out-of-range [15] SEQUENCE { + * time-delay [0] Unsigned, + * low-limit [1] INTEGER, + * high-limit [2] INTEGER, + * deadband [3] Unsigned + * } + * unsigned-out-of-range [16] SEQUENCE { + * time-delay [0] Unsigned, + * low-limit [1] Unsigned, + * high-limit [2] Unsigned, + * deadband [3] Unsigned + * } + * change-of-characterstring [17] SEQUENCE { + * time-delay [0] Unsigned, + * list-of-alarm-values [1] SEQUENCE OF CharacterString, + * } + * change-of-status-flags [18] SEQUENCE { + * time-delay [0] Unsigned, + * selected-flags [1] BACnetStatusFlags + * } + * } + * @param tvb the tv buffer of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fEventParameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + + + +/** + * BACnetLogRecord ::= SEQUENCE { + * timestamp [0] BACnetDateTime, + * logDatum [1] CHOICE { + * log-status [0] BACnetLogStatus, + * boolean-value [1] BOOLEAN, + * real-value [2] REAL, + * enum-value [3] ENUMERATED, -- Optionally limited to 32 bits + * unsigned-value [4] Unsigned, -- Optionally limited to 32 bits + * signed-value [5] INTEGER, -- Optionally limited to 32 bits + * bitstring-value [6] BIT STRING, -- Optionally limited to 32 bits + * null-value [7] NULL, + * failure [8] Error, + * time-change [9] REAL, + * any-value [10] ABSTRACT-SYNTAX.&Type -- Optional + * } + * statusFlags [2] BACnetStatusFlags OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fLogRecord(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetEventLogRecord ::= SEQUENCE { + * timestamp [0] BACnetDateTime, + * logDatum [1] CHOICE { + * log-status [0] BACnetLogStatus, + * notification [1] ConfirmedEventNotification-Request, + * time-change [2] REAL, + * } + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fEventLogRecord(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fLogMultipleRecord(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetNotificationParameters ::= CHOICE { + * change-of-bitstring [0] SEQUENCE { + * referenced-bitstring [0] BIT STRING, + * status-flags [1] BACnetStatusFlags + * }, + * change-of-state [1] SEQUENCE { + * new-state [0] BACnetPropertyStatus, + * status-flags [1] BACnetStatusFlags + * }, + * change-of-value [2] SEQUENCE { + * new-value [0] CHOICE { + * changed-bits [0] BIT STRING, + * changed-value [1] REAL + * }, + * status-flags [1] BACnetStatusFlags + * }, + * command-failure [3] SEQUENCE { + * command-value [0] ABSTRACT-SYNTAX.&Type, -- depends on ref property + * status-flags [1] BACnetStatusFlags + * feedback-value [2] ABSTRACT-SYNTAX.&Type -- depends on ref property + * }, + * floating-limit [4] SEQUENCE { + * reference-value [0] REAL, + * status-flags [1] BACnetStatusFlags + * setpoint-value [2] REAL, + * error-limit [3] REAL + * }, + * out-of-range [5] SEQUENCE { + * exceeding-value [0] REAL, + * status-flags [1] BACnetStatusFlags + * deadband [2] REAL, + * exceeded-limit [3] REAL + * }, + * complex-event-type [6] SEQUENCE OF BACnetPropertyValue, + * -- complex tag 7 is deprecated + * change-of-life-safety [8] SEQUENCE { + * new-state [0] BACnetLifeSafetyState, + * new-mode [1] BACnetLifeSafetyState + * status-flags [2] BACnetStatusFlags, + * operation-expected [3] BACnetLifeSafetyOperation + * }, + * extended [9] SEQUENCE { + * vendor-id [0] Unsigned16, + * extended-event-type [1] Unsigned, + * parameters [2] SEQUENCE OF CHOICE { + * null NULL, + * real REAL, + * integer Unsigned, + * boolean BOOLEAN, + * double Double, + * octet OCTET STRING, + * bitstring BIT STRING, + * enum ENUMERATED, + * propertyValue [0] BACnetDeviceObjectPropertyValue + * } + * }, + * buffer-ready [10] SEQUENCE { + * buffer-property [0] BACnetDeviceObjectPropertyReference, + * previous-notification[1] Unsigned32, + * current-notification [2] BACneUnsigned32tDateTime + * }, + * unsigned-range [11] SEQUENCE { + * exceeding-value [0] Unsigned, + * status-flags [1] BACnetStatusFlags, + * exceeded-limit [2] Unsigned + * }, + * -- context tag 12 is reserved for future addenda + * access-event [13] SEQUENCE { + * access-event [0] BACnetAccessEvent, + * status-flags [1] BACnetStatusFlags, + * access-event-tag [2] Unsigned, + * access-event-time [3] BACnetTimeStamp, + * access-credential [4] BACnetDeviceObjectReference, + * authentication-factor [5] BACnetAuthenticationFactor OPTIONAL + * }, + * double-out-of-range [14] SEQUENCE { + * exceeding-value [0] Double, + * status-flags [1] BACnetStatusFlags + * deadband [2] Double, + * exceeded-limit [3] Double + * }, + * signed-out-of-range [15] SEQUENCE { + * exceeding-value [0] INTEGER, + * status-flags [1] BACnetStatusFlags + * deadband [2] Unsigned, + * exceeded-limit [3] INTEGER + * }, + * unsigned-out-of-range [16] SEQUENCE { + * exceeding-value [0] Unsigned, + * status-flags [1] BACnetStatusFlags + * deadband [2] Unsigned, + * exceeded-limit [3] Unsigned + * }, + * change-of-characterstring [17] SEQUENCE { + * changed-value [0] CharacterString, + * status-flags [1] BACnetStatusFlags + * alarm-value [2] CharacterString + * }, + * change-of-status-flags [18] SEQUENCE { + * present-value [0] ABSTRACT-SYNTAX.&Type OPTIONAL, + * -- depends on referenced property + * referenced-flags [1] BACnetStatusFlags + * }, + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fNotificationParameters(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetObjectPropertyReference ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fBACnetObjectPropertyReference(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +#if 0 +/** + * BACnetObjectPropertyValue ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * propertyIdentifier [1] BACnetPropertyIdentifier, + * propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + * -- if omitted with an array the entire array is referenced + * value [3] ABSTRACT-SYNTAX.&Type, --any datatype appropriate for the specified property + * priority [4] Unsigned (1..16) OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fObjectPropertyValue(tvbuff_t *tvb, proto_tree *tree, guint offset); +#endif + +/** + * BACnetPriorityArray ::= SEQUENCE SIZE (16) OF BACnetPriorityValue + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fPriorityArray(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fPropertyReference(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint8 tagoffset, guint8 list); + +/** + * BACnetPropertyReference ::= SEQUENCE { + * propertyIdentifier [0] BACnetPropertyIdentifier, + * propertyArrayIndex [1] Unsigned OPTIONAL, -- used only with array datatype + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fBACnetPropertyReference(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint8 list); + +static guint +fLOPR(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fRestartReason(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetPropertyValue ::= SEQUENCE { + * PropertyIdentifier [0] BACnetPropertyIdentifier, + * propertyArrayIndex [1] Unsigned OPTIONAL, -- used only with array datatypes + * -- if omitted with an array the entire array is referenced + * value [2] ABSTRACT-SYNTAX.&Type, -- any datatype appropriate for the specified property + * priority [3] Unsigned (1..16) OPTIONAL -- used only when property is commandable + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fBACnetPropertyValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fPropertyValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint8 tagoffset); + +/** + * BACnet Application PDUs chapter 21 + * BACnetRecipient::= CHOICE { + * device [0] BACnetObjectIdentifier + * address [1] BACnetAddress + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fRecipient(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnet Application PDUs chapter 21 + * BACnetRecipientProcess::= SEQUENCE { + * recipient [0] BACnetRecipient + * processID [1] Unsigned32 + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fRecipientProcess(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fCOVSubscription(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +#if 0 +/** + * BACnetSessionKey ::= SEQUENCE { + * sessionKey OCTET STRING (SIZE(8)), -- 56 bits for key, 8 bits for checksum + * peerAddress BACnetAddress + * } + * @param tvb the tv buffer of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + * @todo check if checksum is displayed correctly + */ +static guint +fSessionKey(tvbuff_t *tvb, proto_tree *tree, guint offset); +#endif + +/** + * BACnetSpecialEvent ::= SEQUENCE { + * period CHOICE { + * calendarEntry [0] BACnetCalendarEntry, + * calendarRefernce [1] BACnetObjectIdentifier + * }, + * listOfTimeValues [2] SEQUENCE OF BACnetTimeValue, + * eventPriority [3] Unsigned (1..16) + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSpecialEvent(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetTimeStamp ::= CHOICE { + * time [0] Time, + * sequenceNumber [1] Unsigned (0..65535), + * dateTime [2] BACnetDateTime + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param label the label of this item + * @return modified offset + */ +static guint +fTimeStamp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +static guint +fEventTimeStamps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnetTimeValue ::= SEQUENCE { + * time Time, + * value ABSTRACT-SYNTAX.&Type -- any primitive datatype, complex types cannot be decoded + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fTimeValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +#if 0 +/** + * BACnetVTSession ::= SEQUENCE { + * local-vtSessionID Unsigned8, + * remote-vtSessionID Unsigned8, + * remote-vtAddress BACnetAddress + * } + * @param tvb the tv buffer of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fVTSession(tvbuff_t *tvb, proto_tree *tree, guint offset); +#endif + +/** + * BACnetWeekNDay ::= OCTET STRING (SIZE (3)) + * -- first octet month (1..12) January = 1, X'FF' = any month + * -- second octet weekOfMonth where: 1 = days numbered 1-7 + * -- 2 = days numbered 8-14 + * -- 3 = days numbered 15-21 + * -- 4 = days numbered 22-28 + * -- 5 = days numbered 29-31 + * -- 6 = last 7 days of this month + * -- X'FF' = any week of this month + * -- third octet dayOfWeek (1..7) where 1 = Monday + * -- 7 = Sunday + * -- X'FF' = any day of week + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fWeekNDay(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ReadAccessResult ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * listOfResults [1] SEQUENCE OF SEQUENCE { + * propertyIdentifier [2] BACnetPropertyIdentifier, + * propertyArrayIndex [3] Unsigned OPTIONAL, -- used only with array datatype if omitted with an array the entire array is referenced + * readResult CHOICE { + * propertyValue [4] ABSTRACT-SYNTAX.&Type, + * propertyAccessError [5] Error + * } + * } OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fReadAccessResult(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * ReadAccessSpecification ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * listOfPropertyReferences [1] SEQUENCE OF BACnetPropertyReference + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param subtree the subtree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fReadAccessSpecification(tvbuff_t *tvb, packet_info *pinfo, proto_tree *subtree, guint offset); + +/** + * WriteAccessSpecification ::= SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * listOfProperty [1] SEQUENCE OF BACnetPropertyValue + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param subtree the sub tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fWriteAccessSpecification(tvbuff_t *tvb, packet_info *pinfo, proto_tree *subtree, guint offset); + + +/********************************************************* Helper functions *******************************************/ + +/** + * extracts the tag number from the tag header. + * @param tvb the tv buffer of the current data "TestyVirtualBuffer" + * @param offset the offset in the tvb in actual tvb + * @return Tag Number corresponding to BACnet 20.2.1.2 Tag Number + */ +static guint +fTagNo(tvbuff_t *tvb, guint offset); + +/** + * splits Tag Header coresponding to 20.2.1 General Rules For BACnet Tags + * @param tvb the tv buffer of the current data = "TestyVirtualBuffer" + * @param pinfo the packet info of the current data = packet info + * @param offset the offset in the tvb = offset in actual tvb + * @return tag_no BACnet 20.2.1.2 Tag Number + * @return class_tag BACnet 20.2.1.1 Class + * @return lvt BACnet 20.2.1.3 Length/Value/Type + * @return offs = length of this header + */ + +static guint +fTagHeader(tvbuff_t *tvb, packet_info *pinfo, guint offset, guint8 *tag_no, guint8* class_tag, guint32 *lvt); + + +/** + * adds processID with max 32Bit unsigned Integer Value to tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fProcessId(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * adds present value to the tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param vs enum of string values when applicable + * @param split_val enum index + * @param type present value datatype enum + * @return modified offset + */ +static guint +fPresentValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const value_string *vs, guint32 split_val, BacappPresentValueType type); + +/** + * adds event type to the tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fEventType(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * adds notify type to the tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fNotifyType(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * adds next_state with max 32Bit unsigned Integer Value to tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fToState(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * adds from_state with max 32Bit unsigned Integer Value to tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fFromState(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * adds object_name string value to tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fObjectName(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * wrapper function for fCharacterStringBase + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fCharacterString(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +/** + * adds string value to tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @param present_val_dissect exposes string as present_value property + * @param object_name_dissect exposes string as object_name property + * @return modified offset + */ +static guint +fCharacterStringBase(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label, + gboolean present_val_dissect, gboolean object_name_dissect); + +/** + * adds timeSpan with max 32Bit unsigned Integer Value to tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fTimeSpan(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +/** + * BACnet Application PDUs chapter 21 + * BACnetPropertyIdentifier::= ENUMERATED { + * @see bacapp_property_identifier + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fPropertyIdentifier(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * BACnet Application PDUs chapter 21 + * BACnetPropertyArrayIndex::= ENUMERATED { + * @see bacapp_property_array_index + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fPropertyArrayIndex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * listOfEventSummaries ::= SEQUENCE OF SEQUENCE { + * objectIdentifier [0] BACnetObjectIdentifier, + * eventState [1] BACnetEventState, + * acknowledgedTransitions [2] BACnetEventTransitionBits, + * eventTimeStamps [3] SEQURNCE SIZE (3) OF BACnetTimeStamps, + * notifyType [4] BACnetNotifyType, + * eventEnable [5] BACnetEventTransitionBits, + * eventPriorities [6] SEQUENCE SIZE (3) OF Unsigned + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +flistOfEventSummaries(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * SelectionCriteria ::= SEQUENCE { + * propertyIdentifier [0] BACnetPropertyIdentifier, + * propertyArrayIndex [1] Unsigned OPTIONAL, -- used only with array datatype + * relationSpecifier [2] ENUMERATED { bacapp_relationSpecifier }, + * comparisonValue [3] ABSTRACT-SYNTAX.&Type + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSelectionCriteria(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * objectSelectionCriteria ::= SEQUENCE { + * selectionLogic [0] ENUMERATED { bacapp_selectionLogic }, + * listOfSelectionCriteria [1] SelectionCriteria + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param subtree the sub tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fObjectSelectionCriteria(tvbuff_t *tvb, packet_info *pinfo, proto_tree *subtree, guint offset); + +/** + * BACnet-Error ::= SEQUENCE { + * error-class ENUMERATED {}, + * error-code ENUMERATED {} + * } + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * Adds error-code from BACnet-Error to the tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fErrorCode(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * Adds error-class from BACnet-Error to the tree + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fErrorClass(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +/** + * Generic handler for context tagged values. Mostly for handling + * vendor-defined properties and services. + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + * @todo beautify this ugly construct + */ +static guint +fContextTaggedValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +/** + * realizes some ABSTRACT-SYNTAX.&Type + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + * @todo beautify this ugly construct + */ +static guint +fAbstractSyntaxNType(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + + +static guint +fBitStringTagVS(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label, + const value_string *src); + +static guint +fBitStringTagVSBase(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label, + const value_string *src, gboolean present_val_dissect); + +static guint +fFaultParameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fEventNotificationSubscription(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fLightingCommand(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *lable); + +static guint +fColorCommand(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, guint offset, const gchar* lable); + +static guint +fXyColor(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, guint offset, const gchar* lable); + +static guint +fTimerStateChangeValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fHostNPort(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *lable); + +static guint +fBDTEntry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *lable); + +static guint +fFDTEntry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *lable); + +static guint +fRouterEntry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fVMACEntry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fValueSource(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fAssignedLandingCalls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fLandingCallStatus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fLandingDoorStatus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fCOVMultipleSubscription(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fNameValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fNameValueCollection(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fAuthenticationFactor(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fAuthenticationFactorFormat(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fAuthenticationPolicy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fAccessRule(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fChannelValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label); + +static guint +fPropertyAccessResult(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fNetworkSecurityPolicy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fSecurityKeySet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fAuditLogRecord(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fStageLimitValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + +static guint +fObjectSelector(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset); + + +/** + * register_bacapp + */ +void +proto_register_bacapp(void); + +/* <<<< formerly bacapp.h */ + +/* reassembly table for segmented messages */ +static reassembly_table msg_reassembly_table; + +/* some necessary forward function prototypes */ +static guint +fApplicationTypesEnumerated(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, + const gchar *label, const value_string *vs); + +static const char *bacapp_unknown_service_str = "unknown service"; /* Usage: no format specifiers */ +static const char ASHRAE_Reserved_Fmt[] = "(%d) Reserved for Use by ASHRAE"; +static const char Vendor_Proprietary_Fmt[] = "(%d) Vendor Proprietary Value"; + +static const value_string +BACnetTypeName[] = { + { 0, "Confirmed-REQ"}, + { 1, "Unconfirmed-REQ"}, + { 2, "Simple-ACK"}, + { 3, "Complex-ACK"}, + { 4, "Segment-ACK"}, + { 5, "Error"}, + { 6, "Reject"}, + { 7, "Abort"}, + { 0, NULL } +}; + +static const true_false_string segments_follow = { + "Segmented Request", + "Unsegmented Request" +}; + +static const true_false_string more_follow = { + "More Segments Follow", + "No More Segments Follow" +}; + +static const true_false_string segmented_accept = { + "Segmented Response accepted", + "Segmented Response not accepted" +}; + +static const true_false_string +BACnetTagClass = { + "Context Specific Tag", + "Application Tag" +}; + +static const value_string +BACnetMaxSegmentsAccepted [] = { + { 0, "Unspecified"}, + { 1, "2 segments"}, + { 2, "4 segments"}, + { 3, "8 segments"}, + { 4, "16 segments"}, + { 5, "32 segments"}, + { 6, "64 segments"}, + { 7, "Greater than 64 segments"}, + { 0, NULL } +}; + +static const value_string +BACnetMaxAPDULengthAccepted [] = { + { 0, "Up to MinimumMessageSize (50 octets)"}, + { 1, "Up to 128 octets"}, + { 2, "Up to 206 octets (fits in a LonTalk frame)"}, + { 3, "Up to 480 octets (fits in an ARCNET frame)"}, + { 4, "Up to 1024 octets"}, + { 5, "Up to 1476 octets (fits in an ISO 8802-3 frame)"}, + { 6, "reserved by ASHRAE"}, + { 7, "reserved by ASHRAE"}, + { 8, "reserved by ASHRAE"}, + { 9, "reserved by ASHRAE"}, + { 10, "reserved by ASHRAE"}, + { 11, "reserved by ASHRAE"}, + { 12, "reserved by ASHRAE"}, + { 13, "reserved by ASHRAE"}, + { 14, "reserved by ASHRAE"}, + { 15, "reserved by ASHRAE"}, + { 0, NULL} +}; + +static const value_string +BACnetRejectReason [] = { + { 0, "other"}, + { 1, "buffer-overflow"}, + { 2, "inconsistent-parameters"}, + { 3, "invalid-parameter-data-type"}, + { 4, "invalid-tag"}, + { 5, "missing-required-parameter"}, + { 6, "parameter-out-of-range"}, + { 7, "too-many-arguments"}, + { 8, "undefined-enumeration"}, + { 9, "unrecognized-service"}, + { 0, NULL} +}; + +static const value_string +BACnetRestartReason [] = { + { 0, "unknown"}, + { 1, "coldstart"}, + { 2, "warmstart"}, + { 3, "detected-power-lost"}, + { 4, "detected-powered-off"}, + { 5, "hardware-watchdog"}, + { 6, "software-watchdog"}, + { 7, "suspended"}, + { 8, "activate-changes"}, + { 0, NULL} +}; + +static const value_string +BACnetApplicationTagNumber [] = { + { 0, "Null"}, + { 1, "Boolean"}, + { 2, "Unsigned Integer"}, + { 3, "Signed Integer (2's complement notation)"}, + { 4, "Real (ANSI/IEE-754 floating point)"}, + { 5, "Double (ANSI/IEE-754 double precision floating point)"}, + { 6, "Octet String"}, + { 7, "Character String"}, + { 8, "Bit String"}, + { 9, "Enumerated"}, + { 10, "Date"}, + { 11, "Time"}, + { 12, "BACnetObjectIdentifier"}, + { 13, "reserved by ASHRAE"}, + { 14, "reserved by ASHRAE"}, + { 15, "reserved by ASHRAE"}, + { 0, NULL} +}; + +static const value_string +BACnetAction [] = { + { 0, "direct"}, + { 1, "reverse"}, + { 0, NULL} +}; + +static const value_string +BACnetAccessEvent [] = { + { 0, "none"}, + { 1, "granted"}, + { 2, "muster"}, + { 3, "passback-detected"}, + { 4, "duress"}, + { 5, "trace"}, + { 6, "lockout-max-attempts"}, + { 7, "lockout-other"}, + { 8, "lockout-relinquished"}, + { 9, "lockout-by-higher-priority"}, + { 10, "out-of-service"}, + { 11, "out-of-service-relinquished"}, + { 12, "accompaniment-by"}, + { 13, "authentication-factor-read"}, + { 14, "authorization-delayed"}, + { 15, "verification-required"}, + /* Enumerated values 128-511 are used for events + * which indicate that access has been denied. */ + { 128, "denied-deny-all"}, + { 129, "denied-unknown-credential"}, + { 130, "denied-authentication-unavailable"}, + { 131, "denied-authentication-factor-timeout"}, + { 132, "denied-incorrect-authentication-factor"}, + { 133, "denied-zone-no-access-rights"}, + { 134, "denied-point-no-access-rights"}, + { 135, "denied-no-access-rights"}, + { 136, "denied-out-of-time-range"}, + { 137, "denied-threat-level"}, + { 138, "denied-passback"}, + { 139, "denied-unexpected-location-usage"}, + { 140, "denied-max-attempts"}, + { 141, "denied-lower-occupancy-limit"}, + { 142, "denied-upper-occupancy-limit"}, + { 143, "denied-authentication-factor-lost"}, + { 144, "denied-authentication-factor-stolen"}, + { 145, "denied-authentication-factor-damaged"}, + { 146, "denied-authentication-factor-destroyed"}, + { 147, "denied-authentication-factor-disabled"}, + { 148, "denied-authentication-factor-error"}, + { 149, "denied-credential-unassigned"}, + { 150, "denied-credential-not-provisioned"}, + { 151, "denied-credential-not-yet-active"}, + { 152, "denied-credential-expired"}, + { 153, "denied-credential-manual-disable"}, + { 154, "denied-credential-lockout"}, + { 155, "denied-credential-max-days"}, + { 156, "denied-credential-max-uses"}, + { 157, "denied-credential-inactivity"}, + { 158, "denied-credential-disabled"}, + { 159, "denied-no-accompaniment"}, + { 160, "denied-incorrect-accompaniment"}, + { 161, "denied-lockout"}, + { 162, "denied-verification-failed"}, + { 163, "denied-verification-timeout"}, + { 164, "denied-other"}, + { 0, NULL} +/* Enumerated values 0-512 are reserved for definition by ASHRAE. + Enumerated values 512-65535 may be used by others subject to + procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetAccessZoneOccupancyState[] = { + { 0, "normal"}, + { 1, "below-lower-limit"}, + { 2, "at-lower-limit"}, + { 3, "at-upper-limit"}, + { 4, "above-upper-limit"}, + { 5, "disabled"}, + { 6, "not-supported"}, + { 0, NULL} +}; + +static const value_string +BACnetAccessPassbackMode[] = { + { 0, "passback-off" }, + { 1, "hard-passback" }, + { 2, "soft-passback" }, + { 0, NULL } +}; + +static const value_string +BACnetAccessCredentialDisableReason[] = { + { 0, "disabled" }, + { 1, "disabled-needs-provisioning" }, + { 2, "disabled-unassigned" }, + { 3, "disabled-not-yet-active" }, + { 4, "disabled-expired" }, + { 5, "disabled-lockout" }, + { 6, "disabled-max-days" }, + { 7, "disabled-max-uses" }, + { 8, "disabled-inactivity" }, + { 9, "disabled-manual" }, + { 0, NULL } +}; + +static const value_string +BACnetAccessUserType[] = { + { 0, "asset" }, + { 1, "group" }, + { 2, "person" }, + { 0, NULL } +}; + +static const value_string +BACnetWriteStatus[] = { + { 0, "idle" }, + { 1, "in-progress" }, + { 2, "successful" }, + { 3, "failed" }, + { 0, NULL } +}; + +static const value_string +BACnetLightingTransition[] = { + { 0, "none" }, + { 1, "fade" }, + { 2, "ramp" }, + { 0, NULL } +}; + +static const value_string +BACnetSecurityLevel[] = { + { 0, "incapable" }, + { 1, "plain" }, + { 2, "signed" }, + { 3, "encrypted" }, + { 4, "signed-end-to-end" }, + { 5, "encrypted-end-to-end" }, + { 0, NULL } +}; + +static const value_string +BACnetAccessCredentialDisable[] = { + { 0, "none" }, + { 1, "disable" }, + { 2, "disable-manual" }, + { 3, "disable-lockout" }, + { 0, NULL } +}; + +static const value_string +BACnetAuthenticationStatus[] = { + { 0, "not-ready" }, + { 1, "ready" }, + { 2, "disabled" }, + { 3, "waiting-for-authentication-factor" }, + { 4, "waiting-for-accompaniment" }, + { 5, "waiting-for-verification" }, + { 6, "in-progress" }, + { 0, NULL } +}; + +static const value_string +BACnetAuthorizationMode[] = { + { 0, "authorize" }, + { 1, "grant-active" }, + { 2, "deny-all" }, + { 3, "verification-required" }, + { 4, "authorization-delayed" }, + { 5, "none" }, + { 0, NULL } +}; + +static const value_string +BACnetAuthorizationExemption[] = { + { 0, "passback" }, + { 1, "occupancy-check" }, + { 2, "access-rights" }, + { 3, "lockout" }, + { 4, "deny" }, + { 5, "verification" }, + { 6, "authorization-delay" }, + { 0, NULL } +}; + +static const value_string +BACnetLightingInProgress[] = { + { 0, "idle" }, + { 1, "fade-active" }, + { 2, "ramp-active" }, + { 3, "not-controlled" }, + { 4, "other" }, + { 5, "trim-active" }, + { 0, NULL } +}; + +static const value_string +BACnetColorOperationInProgress[] = { + { 0, "idle" }, + { 1, "fade-active" }, + { 2, "ramp-active" }, + { 3, "not-controlled" }, + { 4, "other" }, + { 0, NULL } +}; + +static const value_string +BACnetColorTransition[] = { + { 0, "none" }, + { 1, "fade" }, + { 2, "ramp" }, + { 0, NULL } +}; + +static const value_string +BACnetBinaryLightingPV[] = { + { 0, "off" }, + { 1, "on" }, + { 2, "warn" }, + { 3, "warn-off" }, + { 4, "warn-relinquish" }, + { 5, "stop" }, + { 0, NULL } +}; + +static const value_string +BACnetBackupState[] = { + { 0, "idle"}, + { 1, "preparing-for-backup"}, + { 2, "preparing-for-restore"}, + { 3, "performing-a-backup"}, + { 4, "performing-a-restore"}, + { 5, "backup-failure"}, + { 6, "restore-failure"}, + { 0, NULL} +}; + +static const value_string +BACnetAcknowledgedTransitions[] = { + { 0, "to-offnormal" }, + { 1, "to-fault" }, + { 2, "to-normal" }, + { 0, NULL } +}; + +static const value_string +BACnetFileAccessMethod [] = { + { 0, "record-access"}, + { 1, "stream-access"}, + { 0, NULL} +}; + +/* For some reason, BACnet defines the choice parameter + in the file read and write services backwards from the + BACnetFileAccessMethod enumeration. +*/ +static const value_string +BACnetFileAccessOption [] = { + { 0, "stream access"}, + { 1, "record access"}, + { 0, NULL} +}; + +static const value_string +BACnetFileStartOption [] = { + { 0, "File Start Position: "}, + { 1, "File Start Record: "}, + { 0, NULL} +}; + +static const value_string +BACnetFileRequestCount [] = { + { 0, "Requested Octet Count: "}, + { 1, "Requested Record Count: "}, + { 0, NULL} +}; + +static const value_string +BACnetFileWriteInfo [] = { + { 0, "File Data: "}, + { 1, "Record Count: "}, + { 0, NULL} +}; + +static const value_string +BACnetAbortReason [] = { + { 0, "other"}, + { 1, "buffer-overflow"}, + { 2, "invalid-apdu-in-this-state"}, + { 3, "preempted-by-higher-priority-task"}, + { 4, "segmentation-not-supported"}, + { 5, "security-error"}, + { 6, "insufficient-security"}, + { 7, "window-size-out-of-range"}, + { 8, "application-exceeded-reply-time"}, + { 9, "out-of-resources"}, + { 10, "tsm-timeout"}, + { 11, "apdu-too-long"}, + { 0, NULL} +}; + +static const value_string +BACnetIpMode [] = { + { 0, "normal"}, + { 1, "foreign"}, + { 2, "bbmd"}, + { 0, NULL} +}; + +static const value_string +BACnetNetworkPortCommand [] = { + { 0, "idle"}, + { 1, "discard-changes"}, + { 2, "renew-fd-registration"}, + { 3, "restart-slave-discovery"}, + { 4, "renew-dhcp"}, + { 5, "restart-autonegotiation"}, + { 6, "disconnect"}, + { 7, "restart-port"}, + { 8, "generate-csr-file"}, + { 9, "validate-changes"}, + { 0, NULL} +}; + +static const value_string +BACnetNetworkNumberQuality [] = { + { 0, "unknown"}, + { 1, "learned"}, + { 2, "learned-configured"}, + { 3, "configured"}, + { 0, NULL} +}; + +static const value_string +BACnetNetworkType [] = { + { 0, "ethernet" }, + { 1, "arcnet" }, + { 2, "mstp" }, + { 3, "ptp" }, + { 4, "lontalk" }, + { 5, "bacnet-ipv4" }, + { 6, "zigbee" }, + { 7, "virtual" }, + { 8, "non-bacnet" }, + { 9, "bacnet-ipv6" }, + {10, "serial" }, + {11, "secure-connect" }, + { 0, NULL} +}; + +static const value_string +BACnetSCConnectionState [] = { + { 0, "not-connected" }, + { 1, "connected" }, + { 2, "disconnected-with-errors" }, + { 3, "failed-to-connect" }, + { 0, NULL} +}; + +static const value_string +BACnetSCHubConnectorState [] = { + { 0, "no-hub-connection" }, + { 1, "connected-to-primary" }, + { 2, "connected-to-failover" }, + { 0, NULL} +}; + +static const value_string +BACnetLifeSafetyMode [] = { + { 0, "off"}, + { 1, "on"}, + { 2, "test"}, + { 3, "manned"}, + { 4, "unmanned"}, + { 5, "armed"}, + { 6, "disarmed"}, + { 7, "prearmed"}, + { 8, "slow"}, + { 9, "fast"}, + { 10, "disconnected"}, + { 11, "enabled"}, + { 12, "disabled"}, + { 13, "atomic-release-disabled"}, + { 14, "default"}, + { 15, "activated-oeo-alarm"}, + { 16, "activated-oeo-evacuate"}, + { 17, "activated-oeo-phase1-recall"}, + { 18, "activated-oeo-unavailable"}, + { 19, "deactivated"}, + { 0, NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetLifeSafetyOperation [] = { + { 0, "none"}, + { 1, "silence"}, + { 2, "silence-audible"}, + { 3, "silence-visual"}, + { 4, "reset"}, + { 5, "reset-alarm"}, + { 6, "reset-fault"}, + { 7, "unsilence"}, + { 8, "unsilence-audible"}, + { 9, "unsilence-visual"}, + { 0, NULL} +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-65535 may be used by others subject to + procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetLifeSafetyState [] = { + { 0, "quiet"}, + { 1, "pre-alarm"}, + { 2, "alarm"}, + { 3, "fault"}, + { 4, "fault-pre-alarm"}, + { 5, "fault-alarm"}, + { 6, "not-ready"}, + { 7, "active"}, + { 8, "tamper"}, + { 9, "test-alarm"}, + { 10, "test-active"}, + { 11, "test-fault"}, + { 12, "test-fault-alarm"}, + { 13, "holdup"}, + { 14, "duress"}, + { 15, "tamper-alarm"}, + { 16, "abnormal"}, + { 17, "emergency-power"}, + { 18, "delayed"}, + { 19, "blocked"}, + { 20, "local-alarm"}, + { 21, "general-alarm"}, + { 22, "supervisory"}, + { 23, "test-supervisory"}, + { 24, "non-default-mode"}, + { 25, "oeo-unavailable"}, + { 26, "oeo-alarm"}, + { 27, "oeo-phase1-recall"}, + { 28, "oeo-evacuate"}, + { 29, "oeo-unaffected"}, + { 30, "test-oeo-unavailable"}, + { 31, "test-oeo-alarm"}, + { 32, "test-oeo-phase1-recall"}, + { 33, "test-oeo-evacuate"}, + { 34, "test-oeo-unaffected"}, + { 0, NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetLimitEnable[] = { + { 0, "low-limit" }, + { 1, "high-limit" }, + { 0, NULL } +}; + +static const value_string +BACnetTimerState [] = { + { 0, "idle"}, + { 1, "running"}, + { 2, "expired"}, + { 0, NULL} +}; + +static const value_string +BACnetTimerTransition [] = { + { 0, "none"}, + { 1, "idle-to-running"}, + { 2, "running-to-idle"}, + { 3, "running-to-running"}, + { 4, "running-to-expired"}, + { 5, "forced-to-expired"}, + { 6, "expired-to-idle"}, + { 7, "expired-to-running"}, + { 0, NULL} +}; + +static const value_string +BACnetEscalatorFault [] = { + { 0, "controller-fault"}, + { 1, "drive-and-motor-fault"}, + { 2, "mechanical-component-fault"}, + { 3, "overspeed-fault"}, + { 4, "power-supply-fault"}, + { 5, "safety-device-fault"}, + { 6, "controller-supply-fault"}, + { 7, "drive-temperature-exceeded"}, + { 8, "comb-plate-fault"}, + { 0, NULL} +}; + +static const value_string +BACnetEscalatorMode [] = { + { 0, "unknown"}, + { 1, "stop"}, + { 2, "up"}, + { 3, "down"}, + { 4, "inspection"}, + { 5, "out-of-service"}, + { 0, NULL} +}; + +static const value_string +BACnetEscalatorOperationDirection [] = { + { 0, "unknown"}, + { 1, "stopped"}, + { 2, "up-rated-speed"}, + { 3, "up-reduced-speed"}, + { 4, "down-rated-speed"}, + { 5, "down-reduced-speed"}, + { 0, NULL} +}; + +static const value_string +BACnetLiftCarDirection [] = { + { 0, "unknown"}, + { 1, "none"}, + { 2, "stopped"}, + { 3, "up"}, + { 4, "down"}, + { 5, "up-and-down"}, + { 0, NULL} +}; + +static const value_string +BACnetLiftCarDoorCommand [] = { + { 0, "none"}, + { 1, "open"}, + { 2, "close"}, + { 0, NULL} +}; + +static const value_string +BACnetLiftCarDriveStatus [] = { + { 0, "unknown"}, + { 1, "stationary"}, + { 2, "braking"}, + { 3, "accelerate"}, + { 4, "decelerate"}, + { 5, "rated-speed"}, + { 6, "single-floor-jump"}, + { 7, "two-floor-jump"}, + { 8, "three-floor-jump"}, + { 9, "multi-floor-jump"}, + { 0, NULL} +}; + +static const value_string +BACnetLiftCarMode [] = { + { 0, "unknown"}, + { 1, "normal"}, + { 2, "vip"}, + { 3, "homing"}, + { 4, "parking"}, + { 5, "attendant-control"}, + { 6, "firefighter-control"}, + { 7, "emergency-power"}, + { 8, "inspection"}, + { 9, "cabinet-recall"}, + { 10, "earthquake-operation"}, + { 11, "fire-operation"}, + { 12, "out-of-service"}, + { 13, "occupant-evacuation"}, + { 0, NULL} +}; + +static const value_string +BACnetLiftFault [] = { + { 0, "controller-fault"}, + { 1, "drive-and-motor-fault"}, + { 2, "governor-and-safety-gear-fault"}, + { 3, "lift-shaft-device-fault"}, + { 4, "power-supply-fault"}, + { 5, "safety-interlock-fault"}, + { 6, "door-closing-fault"}, + { 7, "door-opening-fault"}, + { 8, "car-stopped-outside-landing-zone"}, + { 9, "call-button-stuck"}, + { 10, "start-failure"}, + { 11, "controller-supply-fault"}, + { 12, "self-test-failure"}, + { 13, "runtime-limit-exceeded"}, + { 14, "position-lost"}, + { 15, "drive-temperature-exceeded"}, + { 16, "load-measurement-fault"}, + { 0, NULL} +}; + +static const value_string +BACnetLiftGroupMode [] = { + { 0, "unknown"}, + { 1, "normal"}, + { 2, "down-peak"}, + { 3, "two-way"}, + { 4, "four-way"}, + { 5, "emergency-power"}, + { 6, "up-peak"}, + { 0, NULL} +}; + +static const value_string +BACnetProtocolLevel [] = { + { 0, "physical"}, + { 1, "protocol"}, + { 2, "bacnet-application"}, + { 3, "non-bacnet-application"}, + { 0, NULL} +}; + +static const value_string +BACnetRelationship [] = { + { 0, "unknown"}, + { 1, "default"}, + { 2, "contains"}, + { 3, "contained-by"}, + { 4, "uses"}, + { 5, "used-by"}, + { 6, "commands"}, + { 7, "commanded-by"}, + { 8, "adjusts"}, + { 9, "adjusted-by"}, + { 10, "ingress"}, + { 11, "egress"}, + { 12, "supplies-air"}, + { 13, "receives-air"}, + { 14, "supplies-hot-air"}, + { 15, "receives-hot-air"}, + { 16, "supplies-cool-air"}, + { 17, "receives-cool-air"}, + { 18, "supplies-power"}, + { 19, "receives-power"}, + { 20, "supplies-gas"}, + { 21, "receives-gas"}, + { 22, "supplies-water"}, + { 23, "receives-water"}, + { 24, "supplies-hot-water"}, + { 25, "receives-hot-water"}, + { 26, "supplies-cool-water"}, + { 27, "receives-cool-water"}, + { 28, "supplies-steam"}, + { 29, "receives-steam"}, + { 0, NULL} +}; + +static const value_string +BACnetLightingOperation[] = { + { 0, "none" }, + { 1, "fade-to" }, + { 2, "ramp-to" }, + { 3, "step-up" }, + { 4, "step-down" }, + { 5, "step-on" }, + { 6, "step-off" }, + { 7, "warn" }, + { 8, "warn-off" }, + { 9, "warn-relinquish" }, + { 10, "stop" }, + { 0, NULL } +}; + +static const value_string +BACnetColorOperation[] = { + { 0, "none" }, + { 1, "fade-to-color" }, + { 2, "fade-to-cct" }, + { 3, "ramp-to-cct" }, + { 4, "step-up-cct" }, + { 5, "step-down-cct" }, + { 6, "stop" }, + { 0, NULL } +}; + +static const value_string +BACnetConfirmedServiceChoice[] = { + { 0, "acknowledgeAlarm"}, + { 1, "confirmedCOVNotification"}, + { 2, "confirmedEventNotification"}, + { 3, "getAlarmSummary"}, + { 4, "getEnrollmentSummary"}, + { 5, "subscribeCOV"}, + { 6, "atomicReadFile"}, + { 7, "atomicWriteFile"}, + { 8, "addListElement"}, + { 9, "removeListElement"}, + { 10, "createObject"}, + { 11, "deleteObject"}, + { 12, "readProperty"}, + { 13, "readPropertyConditional"}, + { 14, "readPropertyMultiple"}, + { 15, "writeProperty"}, + { 16, "writePropertyMultiple"}, + { 17, "deviceCommunicationControl"}, + { 18, "confirmedPrivateTransfer"}, + { 19, "confirmedTextMessage"}, + { 20, "reinitializeDevice"}, + { 21, "vtOpen"}, + { 22, "vtClose"}, + { 23, "vtData"}, + { 24, "authenticate"}, + { 25, "requestKey"}, + { 26, "readRange"}, + { 27, "lifeSafetyOperation"}, + { 28, "subscribeCOVProperty"}, + { 29, "getEventInformation"}, + { 30, "subscribeCovPropertyMultiple"}, + { 31, "confirmedCovNotificationMultiple"}, + { 32, "confirmedAuditNotification"}, + { 33, "auditLogQuery"}, + { 0, NULL} +}; + +static const value_string +BACnetReliability [] = { + { 0, "no-fault-detected"}, + { 1, "no-sensor"}, + { 2, "over-range"}, + { 3, "under-range"}, + { 4, "open-loop"}, + { 5, "shorted-loop"}, + { 6, "no-output"}, + { 7, "unreliable-other"}, + { 8, "process-error"}, + { 9, "multi-state-fault"}, + { 10, "configuration-error"}, + { 11, "reserved for a future addendum"}, + { 12, "communication-failure"}, + { 13, "member-fault"}, + { 14, "monitored-object-fault" }, + { 15, "tripped"}, + { 16, "lamp-failure"}, + { 17, "activation-failure"}, + { 18, "renew-dhcp-failure"}, + { 19, "renew-fd-registration-failure"}, + { 20, "restart-auto-negotiation-failure"}, + { 21, "restart-failure"}, + { 22, "proprietary-command-failure"}, + { 23, "faults-listed"}, + { 24, "referenced-object-fault"}, + { 0, NULL} +}; + +static const value_string +BACnetRouterStatus[] = { + { 0, "available" }, + { 1, "busy" }, + { 2, "disconnected" }, + { 0, NULL } +}; + +static const value_string +BACnetUnconfirmedServiceChoice [] = { + { 0, "i-Am"}, + { 1, "i-Have"}, + { 2, "unconfirmedCOVNotification"}, + { 3, "unconfirmedEventNotification"}, + { 4, "unconfirmedPrivateTransfer"}, + { 5, "unconfirmedTextMessage"}, + { 6, "timeSynchronization"}, + { 7, "who-Has"}, + { 8, "who-Is"}, + { 9, "utcTimeSynchronization"}, + { 10, "writeGroup"}, + { 11, "unconfirmedCovNotificationMultiple"}, + { 12, "unconfirmedAuditNotification"}, + { 13, "who-am-I" }, + { 14, "you-are" }, + { 0, NULL} +}; + +static const value_string +BACnetObjectType [] = { + { 0, "analog-input"}, + { 1, "analog-output"}, + { 2, "analog-value"}, + { 3, "binary-input"}, + { 4, "binary-output"}, + { 5, "binary-value"}, + { 6, "calendar"}, + { 7, "command"}, + { 8, "device"}, + { 9, "event-enrollment"}, + { 10, "file"}, + { 11, "group"}, + { 12, "loop"}, + { 13, "multi-state-input"}, + { 14, "multi-state-output"}, + { 15, "notification-class"}, + { 16, "program"}, + { 17, "schedule"}, + { 18, "averaging"}, + { 19, "multi-state-value"}, + { 20, "trend-log"}, + { 21, "life-safety-point"}, + { 22, "life-safety-zone"}, + { 23, "accumulator"}, + { 24, "pulse-converter"}, + { 25, "event-log"}, + { 26, "global-group"}, + { 27, "trend-log-multiple"}, + { 28, "load-control"}, + { 29, "structured-view"}, + { 30, "access-door"}, /* 30-37 added with addanda 135-2008j */ + { 31, "timer"}, + { 32, "access-credential"}, + { 33, "access-point"}, + { 34, "access-rights"}, + { 35, "access-user"}, + { 36, "access-zone"}, + { 37, "credential-data-input"}, + { 38, "network-security"}, + { 39, "bitstring-value"}, /* 39-50 added with addenda 135-2008w */ + { 40, "characterstring-value"}, + { 41, "date-pattern-value"}, + { 42, "date-value"}, + { 43, "datetime-pattern-value"}, + { 44, "datetime-value"}, + { 45, "integer-value"}, + { 46, "large-analog-value"}, + { 47, "octetstring-value"}, + { 48, "positive-integer-value"}, + { 49, "time-pattern-value"}, + { 50, "time-value"}, + { 51, "notification-forwarder"}, + { 52, "alert-enrollment"}, + { 53, "channel"}, + { 54, "lighting-output"}, + { 55, "binary-lighting-output"}, + { 56, "network-port"}, + { 57, "elevator-group"}, + { 58, "escalator"}, + { 59, "lift"}, + { 60, "staging"}, + { 61, "audit-log"}, + { 62, "audit-reporter"}, + { 63, "color"}, + { 64, "color-temperature"}, + { 0, NULL} +/* Enumerated values 0-127 are reserved for definition by ASHRAE. + Enumerated values 128-1023 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetEngineeringUnits [] = { + { 0, "Sq Meters"}, + { 1, "Sq Feet"}, + { 2, "Milliamperes"}, + { 3, "Amperes"}, + { 4, "Ohms"}, + { 5, "Volts"}, + { 6, "Kilovolts"}, + { 7, "Megavolts"}, + { 8, "Volt Amperes"}, + { 9, "Kilovolt Amperes"}, + { 10, "Megavolt Amperes"}, + { 11, "Volt Amperes Reactive"}, + { 12, "Kilovolt Amperes Reactive"}, + { 13, "Megavolt Amperes Reactive"}, + { 14, "Degrees Phase"}, + { 15, "Power Factor"}, + { 16, "Joules"}, + { 17, "Kilojoules"}, + { 18, "Watt Hours"}, + { 19, "Kilowatt Hours"}, + { 20, "BTUs"}, + { 21, "Therms"}, + { 22, "Ton Hours"}, + { 23, "Joules Per Kg Dry Air"}, + { 24, "BTUs Per Pound Dry Air"}, + { 25, "Cycles Per Hour"}, + { 26, "Cycles Per Minute"}, + { 27, "Hertz"}, + { 28, "Grams Of Water Per Kilogram Dry Air"}, + { 29, "Relative Humidity"}, + { 30, "Millimeters"}, + { 31, "Meters"}, + { 32, "Inches"}, + { 33, "Feed"}, + { 34, "Watts Per Sq Foot"}, + { 35, "Watts Per Sq meter"}, + { 36, "Lumens"}, + { 37, "Lux"}, + { 38, "Foot Candles"}, + { 39, "Kilograms"}, + { 40, "Pounds Mass"}, + { 41, "Tons"}, + { 42, "Kgs per Second"}, + { 43, "Kgs Per Minute"}, + { 44, "Kgs Per Hour"}, + { 45, "Pounds Mass Per Minute"}, + { 46, "Pounds Mass Per Hour"}, + { 47, "Watt"}, + { 48, "Kilowatts"}, + { 49, "Megawatts"}, + { 50, "BTUs Per Hour"}, + { 51, "Horsepower"}, + { 52, "Tons Refrigeration"}, + { 53, "Pascals"}, + { 54, "Kilopascals"}, + { 55, "Bars"}, + { 56, "Pounds Force Per Square Inch"}, + { 57, "Centimeters Of Water"}, + { 58, "Inches Of Water"}, + { 59, "Millimeters Of Mercury"}, + { 60, "Centimeters Of Mercury"}, + { 61, "Inches Of Mercury"}, + { 62, "Degrees Celsius"}, + { 63, "Degrees Kelvin"}, + { 64, "Degrees Fahrenheit"}, + { 65, "Degree Days Celsius"}, + { 66, "Degree Days Fahrenheit"}, + { 67, "Years"}, + { 68, "Months"}, + { 69, "Weeks"}, + { 70, "Days"}, + { 71, "Hours"}, + { 72, "Minutes"}, + { 73, "Seconds"}, + { 74, "Meters Per Second"}, + { 75, "Kilometers Per Hour"}, + { 76, "Feed Per Second"}, + { 77, "Feet Per Minute"}, + { 78, "Miles Per Hour"}, + { 79, "Cubic Feet"}, + { 80, "Cubic Meters"}, + { 81, "Imperial Gallons"}, + { 82, "Liters"}, + { 83, "US Gallons"}, + { 84, "Cubic Feet Per Minute"}, + { 85, "Cubic Meters Per Second"}, + { 86, "Imperial Gallons Per Minute"}, + { 87, "Liters Per Second"}, + { 88, "Liters Per Minute"}, + { 89, "US Gallons Per Minute"}, + { 90, "Degrees Angular"}, + { 91, "Degrees Celsius Per Hour"}, + { 92, "Degrees Celsius Per Minute"}, + { 93, "Degrees Fahrenheit Per Hour"}, + { 94, "Degrees Fahrenheit Per Minute"}, + { 95, "No Units"}, + { 96, "Parts Per Million"}, + { 97, "Parts Per Billion"}, + { 98, "Percent"}, + { 99, "Percent Per Second"}, + { 100, "Per Minute"}, + { 101, "Per Second"}, + { 102, "Psi Per Degree Fahrenheit"}, + { 103, "Radians"}, + { 104, "Revolutions Per Min"}, + { 105, "Currency1"}, + { 106, "Currency2"}, + { 107, "Currency3"}, + { 108, "Currency4"}, + { 109, "Currency5"}, + { 110, "Currency6"}, + { 111, "Currency7"}, + { 112, "Currency8"}, + { 113, "Currency9"}, + { 114, "Currency10"}, + { 115, "Sq Inches"}, + { 116, "Sq Centimeters"}, + { 117, "BTUs Per Pound"}, + { 118, "Centimeters"}, + { 119, "Pounds Mass Per Second"}, + { 120, "Delta Degrees Fahrenheit"}, + { 121, "Delta Degrees Kelvin"}, + { 122, "Kilohms"}, + { 123, "Megohms"}, + { 124, "Millivolts"}, + { 125, "Kilojoules Per Kg"}, + { 126, "Megajoules"}, + { 127, "Joules Per Degree Kelvin"}, + { 128, "Joules Per Kg Degree Kelvin"}, + { 129, "Kilohertz"}, + { 130, "Megahertz"}, + { 131, "Per Hour"}, + { 132, "Milliwatts"}, + { 133, "Hectopascals"}, + { 134, "Millibars"}, + { 135, "Cubic Meters Per Hour"}, + { 136, "Liters Per Hour"}, + { 137, "KWatt Hours Per Square Meter"}, + { 138, "KWatt Hours Per Square Foot"}, + { 139, "Megajoules Per Square Meter"}, + { 140, "Megajoules Per Square Foot"}, + { 141, "Watts Per Sq Meter Degree Kelvin"}, + { 142, "Cubic Feet Per Second"}, + { 143, "Percent Obstruction Per Foot"}, + { 144, "Percent Obstruction Per Meter"}, + { 145, "milliohms"}, + { 146, "megawatt-hours"}, + { 147, "kilo-btus"}, + { 148, "mega-btus"}, + { 149, "kilojoules-per-kilogram-dry-air"}, + { 150, "megajoules-per-kilogram-dry-air"}, + { 151, "kilojoules-per-degree-Kelvin"}, + { 152, "megajoules-per-degree-Kelvin"}, + { 153, "newton"}, + { 154, "grams-per-second"}, + { 155, "grams-per-minute"}, + { 156, "tons-per-hour"}, + { 157, "kilo-btus-per-hour"}, + { 158, "hundredths-seconds"}, + { 159, "milliseconds"}, + { 160, "newton-meters"}, + { 161, "millimeters-per-second"}, + { 162, "millimeters-per-minute"}, + { 163, "meters-per-minute"}, + { 164, "meters-per-hour"}, + { 165, "cubic-meters-per-minute"}, + { 166, "meters-per-second-per-second"}, + { 167, "amperes-per-meter"}, + { 168, "amperes-per-square-meter"}, + { 169, "ampere-square-meters"}, + { 170, "farads"}, + { 171, "henrys"}, + { 172, "ohm-meters"}, + { 173, "siemens"}, + { 174, "siemens-per-meter"}, + { 175, "teslas"}, + { 176, "volts-per-degree-Kelvin"}, + { 177, "volts-per-meter"}, + { 178, "webers"}, + { 179, "candelas"}, + { 180, "candelas-per-square-meter"}, + { 181, "degrees-Kelvin-per-hour"}, + { 182, "degrees-Kelvin-per-minute"}, + { 183, "joule-seconds"}, + { 184, "radians-per-second"}, + { 185, "square-meters-per-Newton"}, + { 186, "kilograms-per-cubic-meter"}, + { 187, "newton-seconds"}, + { 188, "newtons-per-meter"}, + { 189, "watts-per-meter-per-degree-Kelvin"}, + { 190, "micro-siemens"}, + { 191, "cubic-feet-per-hour"}, + { 192, "us-gallons-per-hour"}, + { 193, "kilometers"}, + { 194, "micrometers"}, + { 195, "grams"}, + { 196, "milligrams"}, + { 197, "milliliters"}, + { 198, "milliliters-per-second"}, + { 199, "decibels"}, + { 200, "decibels-millivolt"}, + { 201, "decibels-volt"}, + { 202, "millisiemens"}, + { 203, "watt-hours-reactive"}, + { 204, "kilowatt-hours-reactive"}, + { 205, "megawatt-hours-reactive"}, + { 206, "millimeters-of-water"}, + { 207, "per-mille"}, + { 208, "grams-per-gram"}, + { 209, "kilograms-per-kilogram"}, + { 210, "grams-per-kilogram"}, + { 211, "milligrams-per-gram"}, + { 212, "milligrams-per-kilogram"}, + { 213, "grams-per-milliliter"}, + { 214, "grams-per-liter"}, + { 215, "milligrams-per-liter"}, + { 216, "micrograms-per-liter"}, + { 217, "grams-per-cubic-meter"}, + { 218, "milligrams-per-cubic-meter"}, + { 219, "micrograms-per-cubic-meter"}, + { 220, "nanograms-per-cubic-meter"}, + { 221, "grams-per-cubic-centimeter"}, + { 222, "becquerels"}, + { 223, "kilobecquerels"}, + { 224, "megabecquerels"}, + { 225, "gray"}, + { 226, "milligray"}, + { 227, "microgray"}, + { 228, "sieverts"}, + { 229, "millisieverts"}, + { 230, "microsieverts"}, + { 231, "microsieverts-per-hour"}, + { 232, "decibels-a"}, + { 233, "nephelometric-turbidity-unit"}, + { 234, "pH"}, + { 235, "grams-per-square-meter"}, + { 236, "minutes-per-degree-kelvin"}, + { 237, "ohm-meter-squared-per-meter"}, + { 238, "ampere-seconds"}, + { 239, "volt-ampere-hours"}, + { 240, "kilovolt-ampere-hours"}, + { 241, "megavolt-ampere-hours"}, + { 242, "volt-ampere-hours-reactive"}, + { 243, "kilovolt-ampere-hours-reactive"}, + { 244, "megavolt-ampere-hours-reactive"}, + { 245, "volt-square-hours"}, + { 246, "ampere-square-hours"}, + { 247, "joule-per-hours"}, + { 248, "cubic-feet-per-day"}, + { 249, "cubic-meters-per-day"}, + { 250, "watt-hours-per-cubic-meter"}, + { 251, "joules-per-cubic-meter"}, + { 252, "mole-percent"}, + { 253, "pascal-seconds"}, + { 254, "million-standard-cubic-feet-per-minute"}, + { 255, "unassigned-unit-value-255"}, + { 47808, "standard-cubic-feet-per-day"}, + { 47809, "million-standard-cubic-feet-per-day"}, + { 47810, "thousand-cubic-feet-per-day"}, + { 47811, "thousand-standard-cubic-feet-per-day"}, + { 47812, "pounds-mass-per-day"}, + { 47813, "reserved-unit-47813"}, + { 47814, "millirems"}, + { 47815, "millirems-per-hour"}, + { 47816, "degrees-lovibond"}, + { 47817, "alcohol-by-volume"}, + { 47818, "international-bittering-units"}, + { 47819, "european-bitterness-units"}, + { 47820, "degrees-plato"}, + { 47821, "specific-gravity"}, + { 47822, "european-brewing-convention"}, + { 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 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, "invalid - tag" }, + { 58, "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 const value_string +BACnetPropertyIdentifier [] = { + { 0, "acked-transition"}, + { 1, "ack-required"}, + { 2, "action"}, + { 3, "action-text"}, + { 4, "active-text"}, + { 5, "active-vt-session"}, + { 6, "alarm-value"}, + { 7, "alarm-values"}, + { 8, "all"}, + { 9, "all-writes-successful"}, + { 10, "apdu-segment-timeout"}, + { 11, "apdu-timeout"}, + { 12, "application-software-version"}, + { 13, "archive"}, + { 14, "bias"}, + { 15, "change-of-state-count"}, + { 16, "change-of-state-time"}, + { 17, "notification-class"}, + { 18, "the property in this place was deleted"}, + { 19, "controlled-variable-reference"}, + { 20, "controlled-variable-units"}, + { 21, "controlled-variable-value"}, + { 22, "cov-increment"}, + { 23, "datelist"}, + { 24, "daylights-savings-status"}, + { 25, "deadband"}, + { 26, "derivative-constant"}, + { 27, "derivative-constant-units"}, + { 28, "description"}, + { 29, "description-of-halt"}, + { 30, "device-address-binding"}, + { 31, "device-type"}, + { 32, "effective-period"}, + { 33, "elapsed-active-time"}, + { 34, "error-limit"}, + { 35, "event-enable"}, + { 36, "event-state"}, + { 37, "event-type"}, + { 38, "exception-schedule"}, + { 39, "fault-values"}, + { 40, "feedback-value"}, + { 41, "file-access-method"}, + { 42, "file-size"}, + { 43, "file-type"}, + { 44, "firmware-revision"}, + { 45, "high-limit"}, + { 46, "inactive-text"}, + { 47, "in-process"}, + { 48, "instance-of"}, + { 49, "integral-constant"}, + { 50, "integral-constant-units"}, + { 51, "issue-confirmed-notifications"}, + { 52, "limit-enable"}, + { 53, "list-of-group-members"}, + { 54, "list-of-object-property-references"}, + { 55, "list-of-session-keys"}, + { 56, "local-date"}, + { 57, "local-time"}, + { 58, "location"}, + { 59, "low-limit"}, + { 60, "manipulated-variable-reference"}, + { 61, "maximum-output"}, + { 62, "max-apdu-length-accepted"}, + { 63, "max-info-frames"}, + { 64, "max-master"}, + { 65, "max-pres-value"}, + { 66, "minimum-off-time"}, + { 67, "minimum-on-time"}, + { 68, "minimum-output"}, + { 69, "min-pres-value"}, + { 70, "model-name"}, + { 71, "modification-date"}, + { 72, "notify-type"}, + { 73, "number-of-APDU-retries"}, + { 74, "number-of-states"}, + { 75, "object-identifier"}, + { 76, "object-list"}, + { 77, "object-name"}, + { 78, "object-property-reference"}, + { 79, "object-type"}, + { 80, "optional"}, + { 81, "out-of-service"}, + { 82, "output-units"}, + { 83, "event-parameters"}, + { 84, "polarity"}, + { 85, "present-value"}, + { 86, "priority"}, + { 87, "priority-array"}, + { 88, "priority-for-writing"}, + { 89, "process-identifier"}, + { 90, "program-change"}, + { 91, "program-location"}, + { 92, "program-state"}, + { 93, "proportional-constant"}, + { 94, "proportional-constant-units"}, + { 95, "protocol-conformance-class"}, + { 96, "protocol-object-types-supported"}, + { 97, "protocol-services-supported"}, + { 98, "protocol-version"}, + { 99, "read-only"}, + { 100, "reason-for-halt"}, + { 101, "recipient"}, + { 102, "recipient-list"}, + { 103, "reliability"}, + { 104, "relinquish-default"}, + { 105, "required"}, + { 106, "resolution"}, + { 107, "segmentation-supported"}, + { 108, "setpoint"}, + { 109, "setpoint-reference"}, + { 110, "state-text"}, + { 111, "status-flags"}, + { 112, "system-status"}, + { 113, "time-delay"}, + { 114, "time-of-active-time-reset"}, + { 115, "time-of-state-count-reset"}, + { 116, "time-synchronization-recipients"}, + { 117, "units"}, + { 118, "update-interval"}, + { 119, "utc-offset"}, + { 120, "vendor-identifier"}, + { 121, "vendor-name"}, + { 122, "vt-class-supported"}, + { 123, "weekly-schedule"}, + { 124, "attempted-samples"}, + { 125, "average-value"}, + { 126, "buffer-size"}, + { 127, "client-cov-increment"}, + { 128, "cov-resubscription-interval"}, + { 129, "current-notify-time"}, + { 130, "event-time-stamp"}, + { 131, "log-buffer"}, + { 132, "log-device-object-property"}, + { 133, "enable"}, /* per ANSI/ASHRAE 135-2004 addendum B */ + { 134, "log-interval"}, + { 135, "maximum-value"}, + { 136, "minimum-value"}, + { 137, "notification-threshold"}, + { 138, "previous-notify-time"}, + { 139, "protocol-revision"}, + { 140, "records-since-notification"}, + { 141, "record-count"}, + { 142, "start-time"}, + { 143, "stop-time"}, + { 144, "stop-when-full"}, + { 145, "total-record-count"}, + { 146, "valid-samples"}, + { 147, "window-interval"}, + { 148, "window-samples"}, + { 149, "maximum-value-time-stamp"}, + { 150, "minimum-value-time-stamp"}, + { 151, "variance-value"}, + { 152, "active-cov-subscriptions"}, + { 153, "backup-failure-timeout"}, + { 154, "configuration-files"}, + { 155, "database-revision"}, + { 156, "direct-reading"}, + { 157, "last-restore-time"}, + { 158, "maintenance-required"}, + { 159, "member-of"}, + { 160, "mode"}, + { 161, "operation-expected"}, + { 162, "setting"}, + { 163, "silenced"}, + { 164, "tracking-value"}, + { 165, "zone-members"}, + { 166, "life-safety-alarm-values"}, + { 167, "max-segments-accepted"}, + { 168, "profile-name"}, + { 169, "auto-slave-discovery"}, + { 170, "manual-slave-address-binding"}, + { 171, "slave-address-binding"}, + { 172, "slave-proxy-enable"}, + { 173, "last-notify-record"}, /* bug 4117 */ + { 174, "schedule-default"}, + { 175, "accepted-modes"}, + { 176, "adjust-value"}, + { 177, "count"}, + { 178, "count-before-change"}, + { 179, "count-change-time"}, + { 180, "cov-period"}, + { 181, "input-reference"}, + { 182, "limit-monitoring-interval"}, + { 183, "logging-object"}, + { 184, "logging-record"}, + { 185, "prescale"}, + { 186, "pulse-rate"}, + { 187, "scale"}, + { 188, "scale-factor"}, + { 189, "update-time"}, + { 190, "value-before-change"}, + { 191, "value-set"}, + { 192, "value-change-time"}, + { 193, "align-intervals"}, + { 194, "group-member-names"}, + { 195, "interval-offset"}, + { 196, "last-restart-reason"}, + { 197, "logging-type"}, + { 198, "member-status-flags"}, + { 199, "notification-period"}, + { 200, "previous-notify-record"}, + { 201, "requested-update-interval"}, + { 202, "restart-notification-recipients"}, + { 203, "time-of-device-restart"}, + { 204, "time-synchronization-interval"}, + { 205, "trigger"}, + { 206, "UTC-time-synchronization-recipients"}, + { 207, "node-subtype"}, + { 208, "node-type"}, + { 209, "structured-object-list"}, + { 210, "subordinate-annotations"}, + { 211, "subordinate-list"}, + { 212, "actual-shed-level"}, + { 213, "duty-window"}, + { 214, "expected-shed-level"}, + { 215, "full-duty-baseline"}, + { 216, "node-subtype"}, + { 217, "node-type"}, + { 218, "requested-shed-level"}, + { 219, "shed-duration"}, + { 220, "shed-level-descriptions"}, + { 221, "shed-levels"}, + { 222, "state-description"}, + /* enumeration values 223-225 are unassigned */ + { 226, "door-alarm-state"}, + { 227, "door-extended-pulse-time"}, + { 228, "door-members"}, + { 229, "door-open-too-long-time"}, + { 230, "door-pulse-time"}, + { 231, "door-status"}, + { 232, "door-unlock-delay-time"}, + { 233, "lock-status"}, + { 234, "masked-alarm-values"}, + { 235, "secured-status"}, + /* enumeration values 236-243 are unassigned */ + { 244, "absentee-limit"}, /* added with addenda 135-2008j */ + { 245, "access-alarm-events"}, + { 246, "access-doors"}, + { 247, "access-event"}, + { 248, "access-event-authentication-factor"}, + { 249, "access-event-credential"}, + { 250, "access-event-time"}, + { 251, "access-transaction-events"}, + { 252, "accompaniment"}, + { 253, "accompaniment-time"}, + { 254, "activation-time"}, + { 255, "active-authentication-policy"}, + { 256, "assigned-access-rights"}, + { 257, "authentication-factors"}, + { 258, "authentication-policy-list"}, + { 259, "authentication-policy-names"}, + { 260, "authentication-status"}, + { 261, "authorization-mode"}, + { 262, "belongs-to"}, + { 263, "credential-disable"}, + { 264, "credential-status"}, + { 265, "credentials"}, + { 266, "credentials-in-zone"}, + { 267, "days-remaining"}, + { 268, "entry-points"}, + { 269, "exit-points"}, + { 270, "expiration-time"}, + { 271, "extended-time-enable"}, + { 272, "failed-attempt-events"}, + { 273, "failed-attempts"}, + { 274, "failed-attempts-time"}, + { 275, "last-access-event"}, + { 276, "last-access-point"}, + { 277, "last-credential-added"}, + { 278, "last-credential-added-time"}, + { 279, "last-credential-removed"}, + { 280, "last-credential-removed-time"}, + { 281, "last-use-time"}, + { 282, "lockout"}, + { 283, "lockout-relinquish-time"}, + { 284, "master-exemption"}, + { 285, "max-failed-attempts"}, + { 286, "members"}, + { 287, "muster-point"}, + { 288, "negative-access-rules"}, + { 289, "number-of-authentication-policies"}, + { 290, "occupancy-count"}, + { 291, "occupancy-count-adjust"}, + { 292, "occupancy-count-enable"}, + { 293, "occupancy-exemption"}, + { 294, "occupancy-lower-limit"}, + { 295, "occupancy-lower-limit-enforced"}, + { 296, "occupancy-state"}, + { 297, "occupancy-upper-limit"}, + { 298, "occupancy-upper-limit-enforced"}, + { 299, "passback-exemption"}, + { 300, "passback-mode"}, + { 301, "passback-timeout"}, + { 302, "positive-access-rules"}, + { 303, "reason-for-disable"}, + { 304, "supported-formats"}, + { 305, "supported-format-classes"}, + { 306, "threat-authority"}, + { 307, "threat-level"}, + { 308, "trace-flag"}, + { 309, "transaction-notification-class"}, + { 310, "user-external-identifier"}, + { 311, "user-information-reference"}, + /* enumeration values 312-316 are unassigned */ + { 317, "user-name"}, + { 318, "user-type"}, + { 319, "uses-remaining"}, + { 320, "zone-from"}, + { 321, "zone-to"}, + { 322, "access-event-tag"}, + { 323, "global-identifier"}, + /* enumeration values 324-325 reserved for future addenda */ + { 326, "verification-time"}, + { 327, "base-device-security-policy"}, + { 328, "distribution-key-revision"}, + { 329, "do-not-hide"}, + { 330, "key-sets"}, + { 331, "last-key-server"}, + { 332, "network-access-security-policies"}, + { 333, "packet-reorder-time"}, + { 334, "security-pdu-timeout"}, + { 335, "security-time-window"}, + { 336, "supported-security-algorithms"}, + { 337, "update-key-set-timeout"}, + { 338, "backup-and-restore-state"}, + { 339, "backup-preparation-time"}, + { 340, "restore-completion-time"}, + { 341, "restore-preparation-time"}, + { 342, "bit-mask"}, /* addenda 135-2008w */ + { 343, "bit-text"}, + { 344, "is-utc"}, + { 345, "group-members"}, + { 346, "group-member-names"}, + { 347, "member-status-flags"}, + { 348, "requested-update-interval"}, + { 349, "covu-period"}, + { 350, "covu-recipients"}, + { 351, "event-message-texts"}, + { 352, "event-message-texts-config"}, + { 353, "event-detection-enable"}, + { 354, "event-algorithm-inhibit"}, + { 355, "event-algorithm-inhibit-ref"}, + { 356, "time-delay-normal"}, + { 357, "reliability-evaluation-inhibit"}, + { 358, "fault-parameters"}, + { 359, "fault-type"}, + { 360, "local-forwarding-only"}, + { 361, "process-identifier-filter"}, + { 362, "subscribed-recipients"}, + { 363, "port-filter"}, + { 364, "authorization-exemptions"}, + { 365, "allow-group-delay-inhibit"}, + { 366, "channel-number"}, + { 367, "control-groups"}, + { 368, "execution-delay"}, + { 369, "last-priority"}, + { 370, "write-status"}, + { 371, "property-list"}, + { 372, "serial-number"}, + { 373, "blink-warn-enable"}, + { 374, "default-fade-time"}, + { 375, "default-ramp-rate"}, + { 376, "default-step-increment"}, + { 377, "egress-time"}, + { 378, "in-progress"}, + { 379, "instantaneous-power"}, + { 380, "lighting-command"}, + { 381, "lighting-command-default-priority"}, + { 382, "max-actual-value"}, + { 383, "min-actual-value"}, + { 384, "power"}, + { 385, "transition"}, + { 386, "egress-active"}, + { 387, "interface-value"}, + { 388, "fault-high-limit"}, + { 389, "fault-low-limit"}, + { 390, "low-diff-limit"}, + { 391, "strike-count"}, + { 392, "time-of-strike-count-reset"}, + { 393, "default-timeout"}, + { 394, "initial-timeout"}, + { 395, "last-state-change"}, + { 396, "state-change-values"}, + { 397, "timer-running"}, + { 398, "timer-state"}, + { 399, "apdu-length"}, + { 400, "bacnet-ip-address"}, + { 401, "bacnet-ip-default-gateway"}, + { 402, "bacnet-ip-dhcp-enable"}, + { 403, "bacnet-ip-dhcp-lease-time"}, + { 404, "bacnet-ip-dhcp-lease-time-remaining"}, + { 405, "bacnet-ip-dhcp-server"}, + { 406, "bacnet-ip-dns-server"}, + { 407, "bacnet-ip-global-address"}, + { 408, "bacnet-ip-mode"}, + { 409, "bacnet-ip-multicast-address"}, + { 410, "bacnet-ip-nat-traversal"}, + { 411, "bacnet-ip-subnet-mask"}, + { 412, "bacnet-ip-udp-port"}, + { 413, "bbmd-accept-fd-registrations"}, + { 414, "bbmd-broadcast-distribution-table"}, + { 415, "bbmd-foreign-device-table"}, + { 416, "changes-pending"}, + { 417, "command"}, + { 418, "fd-bbmd-address"}, + { 419, "fd-subscription-lifetime"}, + { 420, "link-speed"}, + { 421, "link-speeds"}, + { 422, "link-speed-autonegotiate"}, + { 423, "mac-address"}, + { 424, "network-interface-name"}, + { 425, "network-number"}, + { 426, "network-number-quality"}, + { 427, "network-type"}, + { 428, "routing-table"}, + { 429, "virtual-mac-address-table"}, + { 430, "command-time-array"}, + { 431, "current-command-priority"}, + { 432, "last-command-time"}, + { 433, "value-source"}, + { 434, "value-source-array"}, + { 435, "bacnet-ipv6-mode"}, + { 436, "ipv6-address"}, + { 437, "ipv6-prefix-length"}, + { 438, "bacnet-ipv6-udp-port"}, + { 439, "ipv6-default-gateway"}, + { 440, "bacnet-ipv6-multicast-address"}, + { 441, "ipv6-dns-server"}, + { 442, "ipv6-auto-addressing-enable"}, + { 443, "ipv6-dhcp-lease-time"}, + { 444, "ipv6-dhcp-lease-time-remaining"}, + { 445, "ipv6-dhcp-server"}, + { 446, "ipv6-zone-index"}, + { 447, "assigned-landing-calls"}, + { 448, "car-assigned-direction"}, + { 449, "car-door-command"}, + { 450, "car-door-status"}, + { 451, "car-door-text"}, + { 452, "car-door-zone"}, + { 453, "car-drive-status"}, + { 454, "car-load"}, + { 455, "car-load-units"}, + { 456, "car-mode"}, + { 457, "car-moving-direction"}, + { 458, "car-position"}, + { 459, "elevator-group"}, + { 460, "energy-meter"}, + { 461, "energy-meter-ref"}, + { 462, "escalator-mode"}, + { 463, "fault-signals"}, + { 464, "floor-text"}, + { 465, "group-id"}, + { 466, "enumeration value 466 is unassigned"}, + { 467, "group-mode"}, + { 468, "higher-deck"}, + { 469, "installation-id"}, + { 470, "landing-calls"}, + { 471, "landing-call-control"}, + { 472, "landing-door-status"}, + { 473, "lower-deck"}, + { 474, "machine-room-id"}, + { 475, "making-car-call"}, + { 476, "next-stopping-floor"}, + { 477, "operation-direction"}, + { 478, "passenger-alarm"}, + { 479, "power-mode"}, + { 480, "registered-car-call"}, + { 481, "active-cov-multiple-subscriptions"}, + { 482, "protocol-level"}, + { 483, "reference-port"}, + { 484, "deployed-profile-location"}, + { 485, "profile-location"}, + { 486, "tags"}, + { 487, "subordinate-node-types"}, + { 488, "subordinate-tags"}, + { 489, "subordinate-relationship"}, + { 490, "default-subordinate-relationship"}, + { 491, "represents"}, + { 492, "default-present-value"}, + { 493, "present-stage"}, + { 494, "stages"}, + { 495, "stage-names"}, + { 496, "target-references"}, + { 497, "audit-source-reporter"}, + { 498, "audit-level"}, + { 499, "audit-notification-recipient"}, + { 500, "audit-priority-filter"}, + { 501, "auditable-operations"}, + { 502, "delete-on-forward"}, + { 503, "maximum-send-delay"}, + { 504, "monitored-objects"}, + { 505, "send-now"}, + { 506, "floor-number"}, + { 507, "device-uuid"}, + { 508, "additional-reference-ports"}, + { 509, "certificate-signing-request-file"}, + { 510, "command-validation-result"}, + { 511, "issuer-certificate-files"}, + { 4194304, "max-bvlc-length-accepted"}, + { 4194305, "max-npdu-length-accepted"}, + { 4194306, "operational-certificate-file"}, + { 4194307, "current-health"}, + { 4194308, "sc-connect-wait-timeout"}, + { 4194309, "sc-direct-connect-accept-enable"}, + { 4194310, "sc-direct-connect-accept-uris"}, + { 4194311, "ssc-direct-connect-binding"}, + { 4194312, "sc-direct-connect-connection-status"}, + { 4194313, "sc-direct-connect-initiate-enable"}, + { 4194314, "sc-disconnect-wait-timeout"}, + { 4194315, "sc-failed-connection-request"}, + { 4194316, "sc-failover-hub-connection-status"}, + { 4194317, "sc-failover-hub-uri"}, + { 4194318, "sc-hub-connector-state"}, + { 4194319, "sc-hub-function-accept-uris"}, + { 4194320, "sc-hub-function-binding"}, + { 4194321, "sc-hub-function-connection-status"}, + { 4194322, "sc-hub-function-enable"}, + { 4194323, "sc-heartbeat-timeout"}, + { 4194324, "sc-primary-hub-connection-status"}, + { 4194325, "sc-primary-hub-uri"}, + { 4194326, "sc-maximum-reconnect-time"}, + { 4194327, "sc-minimum-reconnect-time"}, + { 4194328, "color-override"}, + { 4194329, "color-reference"}, + { 4194330, "default-color"}, + { 4194331, "default-color-temperature"}, + { 4194332, "override-color-reference"}, + { 4194334, "color-command"}, + { 4194335, "high_end_trim"}, + { 4194336, "low_end_trim"}, + { 4194337, "trim_fade_time"}, + { 0, NULL} +/* Enumerated values 0-511 are reserved for definition by ASHRAE. + Enumerated values 512-4194303 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetBinaryPV [] = { + { 0, "inactive"}, + { 1, "active"}, + { 0, NULL} +}; + + +#define ANSI_X3_4 0 /* ANSI X3.4, a/k/a "ASCII"; full UTF-8 since 2010 */ + /* See, for example, ANSI/ASHRAE Addendum k to ANSI/ASHRAE Standard 135-2008 */ + /* XXX - I've seen captures using this for ISO 8859-1 */ +#define IBM_MS_DBCS 1 /* "IBM/Microsoft DBCS"; was there only one such DBCS? */ +#define JIS_C_6226 2 /* JIS C 6226 */ +#define ISO_10646_UCS4 3 /* ISO 10646 (UCS-4) - 4-byte Unicode */ +#define ISO_10646_UCS2 4 /* ISO 10646 (UCS-2) - 2-byte Unicode Basic Multilingual Plane (not UTF-16, presumably) */ +#define ISO_8859_1 5 /* ISO 8859-1 */ +static const value_string +BACnetCharacterSet [] = { + { ANSI_X3_4, "ANSI X3.4 / UTF-8 (since 2010)"}, + { IBM_MS_DBCS, "IBM/Microsoft DBCS"}, + { JIS_C_6226, "JIS C 6226"}, + { ISO_10646_UCS4, "ISO 10646 (UCS-4)"}, + { ISO_10646_UCS2, "ISO 10646 (UCS-2)"}, + { ISO_8859_1, "ISO 8859-1"}, + { 0, NULL} +}; + +static const value_string +BACnetStatusFlags [] = { + { 0, "in-alarm"}, + { 1, "fault"}, + { 2, "overridden"}, + { 3, "out-of-service"}, + { 0, NULL} +}; + +static const value_string +BACnetMessagePriority [] = { + { 0, "normal"}, + { 1, "urgent"}, + { 0, NULL} +}; + +static const value_string +BACnetAcknowledgementFilter [] = { + { 0, "all"}, + { 1, "acked"}, + { 2, "not-acked"}, + { 0, NULL} +}; + +static const value_string +BACnetResultFlags [] = { + { 0, "firstitem"}, + { 1, "lastitem"}, + { 2, "moreitems"}, + { 0, NULL} +}; + +static const value_string +BACnetRelationSpecifier [] = { + { 0, "equal"}, + { 1, "not-equal"}, + { 2, "less-than"}, + { 3, "greater-than"}, + { 4, "less-than-or-equal"}, + { 5, "greater-than-or-equal"}, + { 0, NULL} +}; + +static const value_string +BACnetSelectionLogic [] = { + { 0, "and"}, + { 1, "or"}, + { 2, "all"}, + { 0, NULL} +}; + +static const value_string +BACnetEventStateFilter [] = { + { 0, "offnormal"}, + { 1, "fault"}, + { 2, "normal"}, + { 3, "all"}, + { 4, "active"}, + { 0, NULL} +}; + +static const value_string +BACnetEventTransitionBits [] = { + { 0, "to-offnormal"}, + { 1, "to-fault"}, + { 2, "to-normal"}, + { 0, NULL} +}; + +static const value_string +BACnetSegmentation [] = { + { 0, "segmented-both"}, + { 1, "segmented-transmit"}, + { 2, "segmented-receive"}, + { 3, "no-segmentation"}, + { 0, NULL} +}; + +static const value_string +BACnetSilencedState [] = { + { 0, "unsilenced"}, + { 1, "audible-silenced"}, + { 2, "visible-silenced"}, + { 3, "all-silenced"}, + { 0, NULL} +}; + +static const value_string +BACnetDeviceStatus [] = { + { 0, "operational"}, + { 1, "operational-read-only"}, + { 2, "download-required"}, + { 3, "download-in-progress"}, + { 4, "non-operational"}, + { 5, "backup-in-progress"}, + { 0, NULL} +}; + +static const value_string +BACnetEnableDisable [] = { + { 0, "enable"}, + { 1, "disable"}, + { 2, "disable-initiation"}, + { 0, NULL} +}; + +static const value_string +months [] = { + { 1, "January" }, + { 2, "February" }, + { 3, "March" }, + { 4, "April" }, + { 5, "May" }, + { 6, "June" }, + { 7, "July" }, + { 8, "August" }, + { 9, "September" }, + { 10, "October" }, + { 11, "November" }, + { 12, "December" }, + { 13, "odd month" }, + { 14, "even month" }, + { 255, "any month" }, + { 0, NULL } +}; + +static const value_string +weekofmonth [] = { + { 1, "days numbered 1-7" }, + { 2, "days numbered 8-14" }, + { 3, "days numbered 15-21" }, + { 4, "days numbered 22-28" }, + { 5, "days numbered 29-31" }, + { 6, "last 7 days of this month" }, + { 7, "any of 7 days prior to last 7 days of this month" }, + { 8, "any of 7 days prior to last 14 days of this month" }, + { 9, "any of 7 days prior to last 21 days of this month" }, + { 255, "any week of this month" }, + { 0, NULL } +}; + +/* note: notification class object recipient-list uses + different day-of-week enum */ +static const value_string +day_of_week [] = { + { 1, "Monday" }, + { 2, "Tuesday" }, + { 3, "Wednesday" }, + { 4, "Thursday" }, + { 5, "Friday" }, + { 6, "Saturday" }, + { 7, "Sunday" }, + { 255, "any day of week" }, + { 0, NULL } +}; + +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 +BACnetVTClass [] = { + { 0, "default-terminal" }, + { 1, "ansi-x3-64" }, + { 2, "dec-vt52" }, + { 3, "dec-vt100" }, + { 4, "dec-vt200" }, + { 5, "hp-700-94" }, + { 6, "ibm-3130" }, + { 0, NULL } +}; + +static const value_string +BACnetEventType [] = { + { 0, "change-of-bitstring" }, + { 1, "change-of-state" }, + { 2, "change-of-value" }, + { 3, "command-failure" }, + { 4, "floating-limit" }, + { 5, "out-of-range" }, + { 6, "complex-event-type" }, + { 7, "(deprecated)buffer-ready" }, + { 8, "change-of-life-safety" }, + { 9, "extended" }, + { 10, "buffer-ready" }, + { 11, "unsigned-range" }, + { 13, "access-event" }, + { 14, "double-out-of-range"}, /* added with addenda 135-2008w */ + { 15, "signed-out-of-range"}, + { 16, "unsigned-out-of-range"}, + { 17, "change-of-characterstring"}, + { 18, "change-of-status-flags"}, + { 19, "change-of-reliability" }, + { 20, "none" }, + { 21, "change-of-discrete-value"}, + { 22, "change-of-timer"}, + { 0, NULL } +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-65535 may be used by others subject to + the procedures and constraints described in Clause 23. + It is expected that these enumerated values will correspond + to the use of the complex-event-type CHOICE [6] of the + BACnetNotificationParameters production. */ +}; + +static const value_string +BACnetEventState [] = { + { 0, "normal" }, + { 1, "fault" }, + { 2, "offnormal" }, + { 3, "high-limit" }, + { 4, "low-limit" }, + { 5, "life-safety-alarm" }, + { 0, NULL } +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetLogStatus [] = { + { 0, "log-disabled" }, + { 1, "buffer-purged" }, + { 2, "log-interrupted"}, + { 0, NULL } +}; + +static const value_string +BACnetMaintenance [] = { + { 0, "none" }, + { 1, "periodic-test" }, + { 2, "need-service-operational" }, + { 3, "need-service-inoperative" }, + { 0, NULL } +}; + +static const value_string +BACnetNotifyType [] = { + { 0, "alarm" }, + { 1, "event" }, + { 2, "ack-notification" }, + { 0, NULL } +}; + +static const value_string +BACnetServicesSupported [] = { + { 0, "acknowledgeAlarm"}, + { 1, "confirmedCOVNotification"}, + { 2, "confirmedEventNotification"}, + { 3, "getAlarmSummary"}, + { 4, "getEnrollmentSummary"}, + { 5, "subscribeCOV"}, + { 6, "atomicReadFile"}, + { 7, "atomicWriteFile"}, + { 8, "addListElement"}, + { 9, "removeListElement"}, + { 10, "createObject"}, + { 11, "deleteObject"}, + { 12, "readProperty"}, + { 13, "readPropertyConditional"}, + { 14, "readPropertyMultiple"}, + { 15, "writeProperty"}, + { 16, "writePropertyMultiple"}, + { 17, "deviceCommunicationControl"}, + { 18, "confirmedPrivateTransfer"}, + { 19, "confirmedTextMessage"}, + { 20, "reinitializeDevice"}, + { 21, "vtOpen"}, + { 22, "vtClose"}, + { 23, "vtData"}, + { 24, "authenticate"}, + { 25, "requestKey"}, + { 26, "i-Am"}, + { 27, "i-Have"}, + { 28, "unconfirmedCOVNotification"}, + { 29, "unconfirmedEventNotification"}, + { 30, "unconfirmedPrivateTransfer"}, + { 31, "unconfirmedTextMessage"}, + { 32, "timeSynchronization"}, + { 33, "who-Has"}, + { 34, "who-Is"}, + { 35, "readRange"}, + { 36, "utcTimeSynchronization"}, + { 37, "lifeSafetyOperation"}, + { 38, "subscribeCOVProperty"}, + { 39, "getEventInformation"}, + { 40, "write-group"}, + { 41, "subscribe-cov-property-multiple"}, + { 42, "confirmed-cov-notification-multiple"}, + { 43, "unconfirmed-cov-notification-multiple"}, + { 44, "confirmed-audit-notification" }, + { 45, "audit-log-query" }, + { 46, "unconfirmed-audit-notification" }, + { 0, NULL} +}; + +static const value_string +BACnetPropertyStates [] = { + { 0, "boolean-value"}, + { 1, "binary-value"}, + { 2, "event-type"}, + { 3, "polarity"}, + { 4, "program-change"}, + { 5, "program-state"}, + { 6, "reason-for-halt"}, + { 7, "reliability"}, + { 8, "state"}, + { 9, "system-status"}, + { 10, "units"}, + { 11, "unsigned-value"}, + { 12, "life-safety-mode"}, + { 13, "life-safety-state"}, + { 14, "restart-reason"}, + { 15, "door-alarm-state"}, + { 16, "action"}, + { 17, "door-secured-status"}, + { 18, "door-status"}, + { 19, "door-value"}, + { 20, "file-access-method"}, + { 21, "lock-status"}, + { 22, "life-safety-operation"}, + { 23, "maintenance"}, + { 24, "node-type"}, + { 25, "notify-type"}, + { 26, "security-level"}, + { 27, "shed-state"}, + { 28, "silenced-state"}, + { 29, "unknown-29"}, + { 30, "access-event"}, + { 31, "zone-occupancy-state"}, + { 32, "access-credential-disable-reason"}, + { 33, "access-credential-disable"}, + { 34, "authentication-status"}, + { 35, "unknown-35"}, + { 36, "backup-state"}, + { 37, "write-status"}, + { 38, "lighting-in-progress"}, + { 39, "lighting-operation"}, + { 40, "lighting-transition"}, + { 41, "signed-value"}, + { 42, "unknown-42"}, + { 43, "timer-state"}, + { 44, "timer-transition"}, + { 45, "bacnet-ip-mode"}, + { 46, "network-port-command"}, + { 47, "network-type"}, + { 48, "network-number-quality"}, + { 49, "escalator-operation-direction"}, + { 50, "escalator-fault"}, + { 51, "escalator-mode"}, + { 52, "lift-car-direction"}, + { 53, "lift-car-door-command"}, + { 54, "lift-car-drive-status"}, + { 55, "lift-car-mode"}, + { 56, "lift-group-mode"}, + { 57, "lift-fault"}, + { 58, "protocol-level"}, + { 59, "audit-level"}, + { 60, "audit-operation"}, + { 63, "extended-value"}, + {256, "-- example-one"}, + {257, "-- example-two"}, + {258, "sc-connection-state"}, + {259, "sc-hub-connecto-state"}, + { 0, NULL} +/* Tag values 0-63 are reserved for definition by ASHRAE. + Tag values of 64-254 may be used by others to accommodate + vendor specific properties that have discrete or enumerated values, + subject to the constraints described in Clause 23. */ +}; + +static const value_string +BACnetProgramError [] = { + { 0, "normal"}, + { 1, "load-failed"}, + { 2, "internal"}, + { 3, "program"}, + { 4, "other"}, + { 0, NULL} +/* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +static const value_string +BACnetProgramRequest [] = { + { 0, "ready"}, + { 1, "load"}, + { 2, "run"}, + { 3, "halt"}, + { 4, "restart"}, + { 5, "unload"}, + { 0, NULL} +}; + +static const value_string +BACnetProgramState [] = { + { 0, "idle"}, + { 1, "loading"}, + { 2, "running"}, + { 3, "waiting"}, + { 4, "halted"}, + { 5, "unloading"}, + { 0, NULL} +}; + +static const value_string +BACnetReinitializedStateOfDevice [] = { + { 0, "coldstart"}, + { 1, "warmstart"}, + { 2, "start-backup"}, + { 3, "end-backup"}, + { 4, "start-restore"}, + { 5, "end-restore"}, + { 6, "abort-restore"}, + { 7, "activate-changes"}, + { 0, NULL} +}; + +static const value_string +BACnetPolarity [] = { + { 0, "normal"}, + { 1, "reverse"}, + { 0, NULL} +}; + +static const value_string +BACnetTagNames[] = { + { 5, "Extended Value" }, + { 6, "Opening Tag" }, + { 7, "Closing Tag" }, + { 0, NULL } +}; + +static const value_string +BACnetReadRangeOptions[] = { + { 3, "range byPosition" }, + { 4, "range byTime" }, + { 5, "range timeRange" }, + { 6, "range bySequenceNumber" }, + { 7, "range byTime" }, + { 0, NULL } +}; + +/* Present_Value for Load Control Object */ +static const value_string +BACnetShedState[] = { + { 0, "shed-inactive" }, + { 1, "shed-request-pending" }, + { 2, "shed-compliant" }, + { 3, "shed-non-compliant" }, + { 0, NULL } +}; + +static const value_string +BACnetFaultType[] = { + { 0, "none" }, + { 1, "fault-characterstring" }, + { 2, "fault-extended" }, + { 3, "fault-life-safety" }, + { 4, "fault-state" }, + { 5, "fault-status-flags" }, + { 6, "fault-out-of-range" }, + { 7, "fault-listed" }, + { 0, NULL } +}; + +static const value_string +BACnetNodeType [] = { + { 0, "unknown" }, + { 1, "system" }, + { 2, "network" }, + { 3, "device" }, + { 4, "organizational" }, + { 5, "area" }, + { 6, "equipment" }, + { 7, "point" }, + { 8, "collection" }, + { 9, "property" }, + { 10, "functional" }, + { 11, "other" }, + { 12, "subsystem" }, + { 13, "building" }, + { 14, "floor" }, + { 15, "section" }, + { 16, "module" }, + { 17, "tree" }, + { 18, "member" }, + { 19, "protocol" }, + { 20, "room" }, + { 21, "zone" }, + { 0, NULL } +}; + +static const value_string +BACnetLoggingType [] = { + { 0, "polled" }, + { 1, "cov" }, + { 2, "triggered" }, + { 0, NULL } +}; + +static const value_string +BACnetDoorStatus [] = { + { 0, "closed" }, + { 1, "opened" }, + { 2, "unknown" }, + { 3, "door-fault" }, + { 4, "unused" }, + { 5, "none" }, + { 6, "closing" }, + { 7, "opening" }, + { 8, "safety-locked" }, + { 9, "limited-opened" }, + { 0, NULL } +}; + +static const value_string +BACnetDoorValue[] = { + { 0, "lock" }, + { 1, "unlock" }, + { 2, "pulse-unlock" }, + { 3, "extended-pulse-unlock" }, + { 0, NULL } +}; + +static const value_string +BACnetLockStatus [] = { + { 0, "locked" }, + { 1, "unlocked" }, + { 2, "fault" }, + { 3, "unknown" }, + { 0, NULL } +}; + +static const value_string +BACnetDoorSecuredStatus [] = { + { 0, "secured" }, + { 1, "unsecured" }, + { 2, "unknown" }, + { 0, NULL } +}; + +static const value_string +BACnetDoorAlarmState [] = { + { 0, "normal" }, + { 1, "alarm" }, + { 2, "door-open-too-long" }, + { 3, "forced-open" }, + { 4, "tamper" }, + { 5, "door-fault" }, + { 6, "lock-down" }, + { 7, "free-access" }, + { 8, "egress-open" }, + { 0, NULL } +}; + +static const value_string +BACnetSecurityPolicy [] = { + { 0, "plain-non-trusted"}, + { 1, "plain-trusted"}, + { 2, "signed-trusted"}, + { 3, "encrypted-trusted"}, + { 0, NULL } +}; + +static const value_string +BACnetAccumulatorStatus [] = { + { 0, "normal" }, + { 1, "starting" }, + { 2, "recovered" }, + { 3, "abnormal" }, + { 4, "failed" }, + { 0, NULL } +}; + +static const value_string +BACnetAuditLevel [] = { + { 0, "none" }, + { 1, "audit-all" }, + { 2, "audit-config" }, + { 3, "default" }, + { 0, NULL } +}; + +static const value_string +BACnetAuditPriorityFilter [] = { + { 1, "manual-life-safety" }, + { 2, "automatic-life-safety" }, + { 3, "priority-3" }, + { 4, "priority-4" }, + { 5, "critical-equipment-controls" }, + { 6, "minimum-on-off" }, + { 7, "priority-7" }, + { 8, "manual-operator" }, + { 9, "priority-9" }, + { 10, "priority-10" }, + { 11, "priority-11" }, + { 12, "priority-12" }, + { 13, "priority-13" }, + { 14, "priority-14" }, + { 15, "priority-15" }, + { 16, "priority-16" }, + { 0, NULL } +}; + +static const value_string +BACnetAuditOperation [] = { + { 0, "read" }, + { 1, "write" }, + { 2, "create" }, + { 3, "delete" }, + { 4, "life-safety" }, + { 5, "acknowledge-alarm" }, + { 6, "device-disable-comm" }, + { 7, "device-enable-comm" }, + { 8, "device-reset" }, + { 9, "device-backup" }, + { 10, "device-restore" }, + { 11, "subscription" }, + { 12, "notification" }, + { 13, "auditing-failure" }, + { 14, "network-changes" }, + { 15, "general" }, + { 0, NULL } +}; + +static const value_string +BACnetSuccessFilter [] = { + { 0, "all" }, + { 1, "successes-only" }, + { 2, "failures-only" }, + { 0, NULL } +}; + + +/* These values are generated by tools/generate-bacnet-vendors.py from + * https://bacnet.org/assigned-vendor-ids/ + * Version: "As of November 27, 2023" + */ + +static const value_string +BACnetVendorIdentifiers [] = { + { 0, "ASHRAE" }, + { 1, "NIST" }, + { 2, "The Trane Company" }, + { 3, "Daikin Applied Americas" }, + { 4, "PolarSoft" }, + { 5, "Johnson Controls, Inc." }, + { 6, "ABB (Formerly American Auto-Matrix)" }, + { 7, "Siemens Schweiz AG (Formerly: Landis & Staefa Division Europe)" }, + { 8, "Delta Controls" }, + { 9, "Siemens Schweiz AG" }, + { 10, "Schneider Electric" }, + { 11, "TAC" }, + { 12, "Orion Analysis Corporation" }, + { 13, "Teletrol Systems Inc." }, + { 14, "Cimetrics Technology" }, + { 15, "Cornell University" }, + { 16, "United Technologies Carrier" }, + { 17, "Honeywell Inc." }, + { 18, "Alerton / Honeywell" }, + { 19, "TAC AB" }, + { 20, "Hewlett-Packard Company" }, + { 21, "Dorsette’s Inc." }, + { 22, "Siemens Schweiz AG (Formerly: Cerberus AG)" }, + { 23, "York Controls Group" }, + { 24, "Automated Logic Corporation" }, + { 25, "CSI Control Systems International" }, + { 26, "Phoenix Controls Corporation" }, + { 27, "Innovex Technologies, Inc." }, + { 28, "KMC Controls, Inc." }, + { 29, "Xn Technologies, Inc." }, + { 30, "Hyundai Information Technology Co., Ltd." }, + { 31, "Tokimec Inc." }, + { 32, "Simplex" }, + { 33, "North Building Technologies Limited" }, + { 34, "Notifier" }, + { 35, "Reliable Controls Corporation" }, + { 36, "Tridium Inc." }, + { 37, "MSA Safety" }, + { 38, "Silicon Energy" }, + { 39, "Kieback & Peter GmbH & Co KG" }, + { 40, "Anacon Systems, Inc." }, + { 41, "Systems Controls & Instruments, LLC" }, + { 42, "Acuity Brands Lighting, Inc." }, + { 43, "Micropower Manufacturing" }, + { 44, "Matrix Controls" }, + { 45, "METALAIRE" }, + { 46, "ESS Engineering" }, + { 47, "Sphere Systems Pty Ltd." }, + { 48, "Walker Technologies Corporation" }, + { 49, "H I Solutions, Inc." }, + { 50, "MBS GmbH" }, + { 51, "SAMSON AG" }, + { 52, "Badger Meter Inc." }, + { 53, "DAIKIN Industries Ltd." }, + { 54, "NARA Controls Inc." }, + { 55, "Mammoth Inc." }, + { 56, "Liebert Corporation" }, + { 57, "SEMCO Incorporated" }, + { 58, "Air Monitor Corporation" }, + { 59, "TRIATEK, LLC" }, + { 60, "NexLight" }, + { 61, "Multistack" }, + { 62, "TSI Incorporated" }, + { 63, "Weather-Rite, Inc." }, + { 64, "Dunham-Bush" }, + { 65, "Reliance Electric" }, + { 66, "LCS Inc." }, + { 67, "Regulator Australia PTY Ltd." }, + { 68, "Touch-Plate Lighting Controls" }, + { 69, "Amann GmbH" }, + { 70, "RLE Technologies" }, + { 71, "Cardkey Systems" }, + { 72, "SECOM Co., Ltd." }, + { 73, "ABB Gebäudetechnik AG Bereich NetServ" }, + { 74, "KNX Association cvba" }, + { 75, "Institute of Electrical Installation Engineers of Japan (IEIEJ)" }, + { 76, "Nohmi Bosai, Ltd." }, + { 77, "Carel Industries S.p.A." }, + { 78, "UTC Fire & Security España, S.L." }, + { 79, "Hochiki Corporation" }, + { 80, "Fr. Sauter AG" }, + { 81, "Matsushita Electric Works, Ltd." }, + { 82, "Mitsubishi Electric Corporation, Inazawa Works" }, + { 83, "Mitsubishi Heavy Industries, Ltd." }, + { 84, "Xylem, Inc." }, + { 85, "Yamatake Building Systems Co., Ltd." }, + { 86, "The Watt Stopper, Inc." }, + { 87, "Aichi Tokei Denki Co., Ltd." }, + { 88, "Activation Technologies, LLC" }, + { 89, "Saia-Burgess Controls, Ltd." }, + { 90, "Hitachi, Ltd." }, + { 91, "Novar Corp./Trend Control Systems Ltd." }, + { 92, "Mitsubishi Electric Lighting Corporation" }, + { 93, "Argus Control Systems, Ltd." }, + { 94, "Kyuki Corporation" }, + { 95, "Richards-Zeta Building Intelligence, Inc." }, + { 96, "Scientech R&D, Inc." }, + { 97, "VCI Controls, Inc." }, + { 98, "Toshiba Corporation" }, + { 99, "Mitsubishi Electric Corporation Air Conditioning & Refrigeration Systems Works" }, + { 100, "Custom Mechanical Equipment, LLC" }, + { 101, "ClimateMaster" }, + { 102, "ICP Panel-Tec, Inc." }, + { 103, "D-Tek Controls" }, + { 104, "NEC Engineering, Ltd." }, + { 105, "PRIVA BV" }, + { 106, "Meidensha Corporation" }, + { 107, "JCI Systems Integration Services" }, + { 108, "Freedom Corporation" }, + { 109, "Neuberger Gebäudeautomation GmbH" }, + { 110, "eZi Controls" }, + { 111, "Leviton Manufacturing" }, + { 112, "Fujitsu Limited" }, + { 113, "Vertiv (Formerly Emerson Network Power)" }, + { 114, "S. A. Armstrong, Ltd." }, + { 115, "Visonet AG" }, + { 116, "M&M Systems, Inc." }, + { 117, "Custom Software Engineering" }, + { 118, "Nittan Company, Limited" }, + { 119, "Elutions Inc. (Wizcon Systems SAS)" }, + { 120, "Pacom Systems Pty., Ltd." }, + { 121, "Unico, Inc." }, + { 122, "Ebtron, Inc." }, + { 123, "Scada Engine" }, + { 124, "Lenze Americas (Formerly: AC Technology Corporation)" }, + { 125, "Eagle Technology" }, + { 126, "Data Aire, Inc." }, + { 127, "ABB, Inc." }, + { 128, "Transbit Sp. z o. o." }, + { 129, "Toshiba Carrier Corporation" }, + { 130, "Shenzhen Junzhi Hi-Tech Co., Ltd." }, + { 131, "Tokai Soft" }, + { 132, "Blue Ridge Technologies" }, + { 133, "Veris Industries" }, + { 134, "Centaurus Prime" }, + { 135, "Sand Network Systems" }, + { 136, "Regulvar, Inc." }, + { 137, "AFDtek Division of Fastek International Inc." }, + { 138, "PowerCold Comfort Air Solutions, Inc." }, + { 139, "I Controls" }, + { 140, "Viconics Electronics, Inc." }, + { 141, "Yaskawa America, Inc." }, + { 142, "DEOS control systems GmbH" }, + { 143, "Digitale Mess- und Steuersysteme AG" }, + { 144, "Fujitsu General Limited" }, + { 145, "Project Engineering S.r.l." }, + { 146, "Sanyo Electric Co., Ltd." }, + { 147, "Integrated Information Systems, Inc." }, + { 148, "Temco Controls, Ltd." }, + { 149, "Airtek International Inc." }, + { 150, "Advantech Corporation" }, + { 151, "Titan Products, Ltd." }, + { 152, "Regel Partners" }, + { 153, "National Environmental Product" }, + { 154, "Unitec Corporation" }, + { 155, "Kanden Engineering Company" }, + { 156, "Messner Gebäudetechnik GmbH" }, + { 157, "Integrated.CH" }, + { 158, "Price Industries" }, + { 159, "SE-Elektronic GmbH" }, + { 160, "Rockwell Automation" }, + { 161, "Enflex Corp." }, + { 162, "ASI Controls" }, + { 163, "SysMik GmbH Dresden" }, + { 164, "HSC Regelungstechnik GmbH" }, + { 165, "Smart Temp Australia Pty. Ltd." }, + { 166, "Cooper Controls" }, + { 167, "Duksan Mecasys Co., Ltd." }, + { 168, "Fuji IT Co., Ltd." }, + { 169, "Vacon Plc" }, + { 170, "Leader Controls" }, + { 171, "ABB (Formerly Cylon Controls, Ltd)" }, + { 172, "Compas" }, + { 173, "Mitsubishi Electric Building Techno-Service Co., Ltd." }, + { 174, "Building Control Integrators" }, + { 175, "ITG Worldwide (M) Sdn Bhd" }, + { 176, "Lutron Electronics Co., Inc." }, + { 177, "Cooper-Atkins Corporation" }, + { 178, "LOYTEC Electronics GmbH" }, + { 179, "ProLon" }, + { 180, "Mega Controls Limited" }, + { 181, "Micro Control Systems, Inc." }, + { 182, "Kiyon, Inc." }, + { 183, "Dust Networks" }, + { 184, "Advanced Building Automation Systems" }, + { 185, "Hermos AG" }, + { 186, "CEZIM" }, + { 187, "Softing" }, + { 188, "Lynxspring, Inc." }, + { 189, "Schneider Toshiba Inverter Europe" }, + { 190, "Danfoss Drives A/S" }, + { 191, "Eaton Corporation" }, + { 192, "Matyca S.A." }, + { 193, "Botech AB" }, + { 194, "Noveo, Inc." }, + { 195, "AMEV" }, + { 196, "Yokogawa Electric Corporation" }, + { 197, "Bosch Building Automation GmbH" }, + { 198, "Exact Logic" }, + { 199, "Mass Electronics Pty Ltd dba Innotech Control Systems Australia" }, + { 200, "Kandenko Co., Ltd." }, + { 201, "DTF, Daten-Technik Fries" }, + { 202, "Klimasoft, Ltd." }, + { 203, "Toshiba Schneider Inverter Corporation" }, + { 204, "Control Applications, Ltd." }, + { 205, "CIMON CO., Ltd." }, + { 206, "Onicon Incorporated" }, + { 207, "Automation Displays, Inc." }, + { 208, "Control Solutions, Inc." }, + { 209, "Remsdaq Limited" }, + { 210, "NTT Facilities, Inc." }, + { 211, "VIPA GmbH" }, + { 212, "TSC21 Association of Japan" }, + { 213, "Strato Automation" }, + { 214, "HRW Limited" }, + { 215, "Lighting Control & Design, Inc." }, + { 216, "Mercy Electronic and Electrical Industries" }, + { 217, "Samsung SDS Co., Ltd" }, + { 218, "Impact Facility Solutions, Inc." }, + { 219, "Aircuity" }, + { 220, "Control Techniques, Ltd." }, + { 221, "OpenGeneral Pty., Ltd." }, + { 222, "WAGO Kontakttechnik GmbH & Co. KG" }, + { 223, "Franklin Electric" }, + { 224, "Chloride Power Protection Company" }, + { 225, "Computrols, Inc." }, + { 226, "Phoenix Contact GmbH & Co. KG" }, + { 227, "Grundfos Management A/S" }, + { 228, "Ridder Drive Systems" }, + { 229, "Soft Device SDN BHD" }, + { 230, "Integrated Control Technology Limited" }, + { 231, "AIRxpert Systems, Inc." }, + { 232, "Microtrol Limited" }, + { 233, "Red Lion Controls" }, + { 234, "Digital Electronics Corporation" }, + { 235, "Ennovatis GmbH" }, + { 236, "Serotonin Software Technologies, Inc." }, + { 237, "LS Industrial Systems Co., Ltd." }, + { 238, "Square D Company" }, + { 239, "S Squared Innovations, Inc." }, + { 240, "Aricent Ltd." }, + { 241, "EtherMetrics, LLC" }, + { 242, "Industrial Control Communications, Inc." }, + { 243, "Paragon Controls, Inc." }, + { 244, "A. O. Smith Corporation" }, + { 245, "Contemporary Control Systems, Inc." }, + { 246, "HMS Industrial Networks SLU" }, + { 247, "Ingenieurgesellschaft N. Hartleb mbH" }, + { 248, "Heat-Timer Corporation" }, + { 249, "Ingrasys Technology, Inc." }, + { 250, "Costerm Building Automation" }, + { 251, "WILO SE" }, + { 252, "Embedia Technologies Corp." }, + { 253, "Technilog" }, + { 254, "HR Controls Ltd. & Co. KG" }, + { 255, "Lennox International, Inc." }, + { 256, "RK-Tec Rauchklappen-Steuerungssysteme GmbH & Co. KG" }, + { 257, "Thermomax, Ltd." }, + { 258, "ELCON Electronic Control, Ltd." }, + { 259, "Larmia Control AB" }, + { 260, "BACnet Stack at SourceForge" }, + { 261, "G4S Security Services A/S" }, + { 262, "Exor International S.p.A." }, + { 263, "Cristal Controles" }, + { 264, "Regin AB" }, + { 265, "Dimension Software, Inc." }, + { 266, "SynapSense Corporation" }, + { 267, "Beijing Nantree Electronic Co., Ltd." }, + { 268, "Camus Hydronics Ltd." }, + { 269, "Kawasaki Heavy Industries, Ltd." }, + { 270, "Critical Environment Technologies" }, + { 271, "ILSHIN IBS Co., Ltd." }, + { 272, "ELESTA Energy Control AG" }, + { 273, "KROPMAN Installatietechniek" }, + { 274, "Baldor Electric Company" }, + { 275, "INGA mbH" }, + { 276, "GE Consumer & Industrial" }, + { 277, "Functional Devices, Inc." }, + { 278, "StudioSC" }, + { 279, "M-System Co., Ltd." }, + { 280, "Yokota Co., Ltd." }, + { 281, "Hitranse Technology Co., LTD" }, + { 282, "Vigilent Corporation" }, + { 283, "Kele, Inc." }, + { 284, "BELIMO Automation AG" }, + { 285, "Gentec" }, + { 286, "Embedded Science Labs, LLC" }, + { 287, "Parker Hannifin Corporation" }, + { 288, "MaCaPS International Limited" }, + { 289, "Link4 Corporation" }, + { 290, "Romutec Steuer-u. Regelsysteme GmbH" }, + { 291, "Pribusin, Inc." }, + { 292, "Advantage Controls" }, + { 293, "Critical Room Control" }, + { 294, "LEGRAND" }, + { 295, "Tongdy Control Technology Co., Ltd." }, + { 296, "ISSARO Integrierte Systemtechnik" }, + { 297, "Pro-Dev Industries" }, + { 298, "DRI-STEEM" }, + { 299, "Creative Electronic GmbH" }, + { 300, "Swegon AB" }, + { 301, "FIRVENA s.r.o." }, + { 302, "Hitachi Appliances, Inc." }, + { 303, "Real Time Automation, Inc." }, + { 304, "ITEC Hankyu-Hanshin Co." }, + { 305, "Cyrus E&M Engineering Co., Ltd." }, + { 306, "Badger Meter" }, + { 307, "Cirrascale Corporation" }, + { 308, "Elesta GmbH Building Automation" }, + { 309, "Securiton" }, + { 310, "OSlsoft, Inc." }, + { 311, "Hanazeder Electronic GmbH" }, + { 312, "Honeywell Security Deutschland, Novar GmbH" }, + { 313, "Siemens Industry, Inc." }, + { 314, "ETM Professional Control GmbH" }, + { 315, "Meitav-tec, Ltd." }, + { 316, "Janitza Electronics GmbH" }, + { 317, "MKS Nordhausen" }, + { 318, "De Gier Drive Systems B.V." }, + { 319, "Cypress Envirosystems" }, + { 320, "SMARTron s.r.o." }, + { 321, "Verari Systems, Inc." }, + { 322, "K-W Electronic Service, Inc." }, + { 323, "ALFA-SMART Energy Management" }, + { 324, "Telkonet, Inc." }, + { 325, "Securiton GmbH" }, + { 326, "Cemtrex, Inc." }, + { 327, "Performance Technologies, Inc." }, + { 328, "Xtralis (Aust) Pty Ltd" }, + { 329, "TROX GmbH" }, + { 330, "Beijing Hysine Technology Co., Ltd" }, + { 331, "RCK Controls, Inc." }, + { 332, "Distech Controls SAS" }, + { 333, "Novar/Honeywell" }, + { 334, "S4 Integration Solutions" }, + { 335, "Schneider Electric" }, + { 336, "LHA Systems" }, + { 337, "GHM engineering Group, Inc." }, + { 338, "Cllimalux S.A." }, + { 339, "VAISALA Oyj" }, + { 340, "COMPLEX (Beijing) Technology, Co., LTD." }, + { 341, "SCADAmetrics" }, + { 342, "POWERPEG NSI Limited" }, + { 343, "BACnet Interoperability Testing Services, Inc." }, + { 344, "Teco a.s." }, + { 345, "Plexus Technology, Inc." }, + { 346, "Energy Focus, Inc." }, + { 347, "Powersmiths International Corp." }, + { 348, "Nichibei Co., Ltd." }, + { 349, "HKC Technology Ltd." }, + { 350, "Ovation Networks, Inc." }, + { 351, "Setra Systems" }, + { 352, "AVG Automation" }, + { 353, "ZXC Ltd." }, + { 354, "Byte Sphere" }, + { 355, "Generiton Co., Ltd." }, + { 356, "Holter Regelarmaturen GmbH & Co. KG" }, + { 357, "Bedford Instruments, LLC" }, + { 358, "Standair Inc." }, + { 359, "WEG Automation – R&D" }, + { 360, "Prolon Control Systems ApS" }, + { 361, "Inneasoft" }, + { 362, "ConneXSoft GmbH" }, + { 363, "CEAG Notlichtsysteme GmbH" }, + { 364, "Distech Controls Inc." }, + { 365, "Industrial Technology Research Institute" }, + { 366, "ICONICS, Inc." }, + { 367, "IQ Controls s.c." }, + { 368, "OJ Electronics A/S" }, + { 369, "Rolbit Ltd." }, + { 370, "Synapsys Solutions Ltd." }, + { 371, "ACME Engineering Prod. Ltd." }, + { 372, "Zener Electric Pty, Ltd." }, + { 373, "Selectronix, Inc." }, + { 374, "Gorbet & Banerjee, LLC." }, + { 375, "IME" }, + { 376, "Stephen H. Dawson Computer Service" }, + { 377, "Accutrol, LLC" }, + { 378, "Schneider Elektronik GmbH" }, + { 379, "Alpha-Inno Tec GmbH" }, + { 380, "ADMMicro, Inc." }, + { 381, "Greystone Energy Systems, Inc." }, + { 382, "CAP Technologie" }, + { 383, "KeRo Systems" }, + { 384, "Domat Control System s.r.o." }, + { 385, "Efektronics Pty. Ltd." }, + { 386, "Hekatron Vertriebs GmbH" }, + { 387, "Securiton AG" }, + { 388, "Carlo Gavazzi Controls SpA" }, + { 389, "Chipkin Automation Systems" }, + { 390, "Savant Systems, LLC" }, + { 391, "Simmtronic Lighting Controls" }, + { 392, "Abelko Innovation AB" }, + { 393, "Seresco Technologies Inc." }, + { 394, "IT Watchdogs" }, + { 395, "Automation Assist Japan Corp." }, + { 396, "Thermokon Sensortechnik GmbH" }, + { 397, "EGauge Systems, LLC" }, + { 398, "Quantum Automation (ASIA) PTE, Ltd." }, + { 399, "Toshiba Lighting & Technology Corp." }, + { 400, "SPIN Engenharia de Automação Ltda." }, + { 401, "Logistics Systems & Software Services India PVT. Ltd." }, + { 402, "Delta Controls Integration Products" }, + { 403, "Focus Media" }, + { 404, "LUMEnergi Inc." }, + { 405, "Kara Systems" }, + { 406, "RF Code, Inc." }, + { 407, "Fatek Automation Corp." }, + { 408, "JANDA Software Company, LLC" }, + { 409, "Open System Solutions Limited" }, + { 410, "Intelec Systems PTY Ltd." }, + { 411, "Ecolodgix, LLC" }, + { 412, "Douglas Lighting Controls" }, + { 413, "iSAtech GmbH" }, + { 414, "AREAL" }, + { 415, "Beckhoff Automation" }, + { 416, "IPAS GmbH" }, + { 417, "KE2 Therm Solutions" }, + { 418, "Base2Products" }, + { 419, "DTL Controls, LLC" }, + { 420, "INNCOM International, Inc." }, + { 421, "METZ CONNECT GmbH" }, + { 422, "Greentrol Automation, Inc" }, + { 423, "BELIMO Automation AG" }, + { 424, "Samsung Heavy Industries Co, Ltd" }, + { 425, "Triacta Power Technologies, Inc." }, + { 426, "Globestar Systems" }, + { 427, "MLB Advanced Media, LP" }, + { 428, "SWG Stuckmann Wirtschaftliche Gebäudesysteme GmbH" }, + { 429, "SensorSwitch" }, + { 430, "Multitek Power Limited" }, + { 431, "Aquametro AG" }, + { 432, "LG Electronics Inc." }, + { 433, "Electronic Theatre Controls, Inc." }, + { 434, "Mitsubishi Electric Corporation Nagoya Works" }, + { 435, "Delta Electronics, Inc." }, + { 436, "Elma Kurtalj, Ltd." }, + { 437, "Tyco Fire & Security GmbH" }, + { 438, "Nedap Security Management" }, + { 439, "ESC Automation Inc." }, + { 440, "DSP4YOU Ltd." }, + { 441, "GE Sensing and Inspection Technologies" }, + { 442, "Embedded Systems SIA" }, + { 443, "BEFEGA GmbH" }, + { 444, "Baseline Inc." }, + { 445, "Key2Act" }, + { 446, "OEMCtrl" }, + { 447, "Clarkson Controls Limited" }, + { 448, "Rogerwell Control System Limited" }, + { 449, "SCL Elements" }, + { 450, "Hitachi Ltd." }, + { 451, "Newron System SA" }, + { 452, "BEVECO Gebouwautomatisering BV" }, + { 453, "Streamside Solutions" }, + { 454, "Yellowstone Soft" }, + { 455, "Oztech Intelligent Systems Pty Ltd." }, + { 456, "Novelan GmbH" }, + { 457, "Flexim Americas Corporation" }, + { 458, "ICP DAS Co., Ltd." }, + { 459, "CARMA Industries Inc." }, + { 460, "Log-One Ltd." }, + { 461, "TECO Electric & Machinery Co., Ltd." }, + { 462, "ConnectEx, Inc." }, + { 463, "Turbo DDC Südwest" }, + { 464, "Quatrosense Environmental Ltd." }, + { 465, "Fifth Light Technology Ltd." }, + { 466, "Scientific Solutions, Ltd." }, + { 467, "Controller Area Network Solutions (M) Sdn Bhd" }, + { 468, "RESOL – Elektronische Regelungen GmbH" }, + { 469, "RPBUS LLC" }, + { 470, "BRS Sistemas Eletronicos" }, + { 471, "WindowMaster A/S" }, + { 472, "Sunlux Technologies Ltd." }, + { 473, "Measurlogic" }, + { 474, "Frimat GmbH" }, + { 475, "Spirax Sarco" }, + { 476, "Luxtron" }, + { 477, "Raypak Inc" }, + { 478, "Air Monitor Corporation" }, + { 479, "Regler Och Webbteknik Sverige (ROWS)" }, + { 480, "Intelligent Lighting Controls Inc." }, + { 481, "Sanyo Electric Industry Co., Ltd" }, + { 482, "E-Mon Energy Monitoring Products" }, + { 483, "Digital Control Systems" }, + { 484, "ATI Airtest Technologies, Inc." }, + { 485, "SCS SA" }, + { 486, "HMS Industrial Networks AB" }, + { 487, "Shenzhen Universal Intellisys Co Ltd" }, + { 488, "EK Intellisys Sdn Bhd" }, + { 489, "SysCom" }, + { 490, "Firecom, Inc." }, + { 491, "ESA Elektroschaltanlagen Grimma GmbH" }, + { 492, "Kumahira Co Ltd" }, + { 493, "Hotraco" }, + { 494, "SABO Elektronik GmbH" }, + { 495, "Equip’Trans" }, + { 496, "Temperature Control Specialities Co., Inc (TCS)" }, + { 497, "FlowCon International A/S" }, + { 498, "ThyssenKrupp Elevator Americas" }, + { 499, "Abatement Technologies" }, + { 500, "Continental Control Systems, LLC" }, + { 501, "WISAG Automatisierungstechnik GmbH & Co KG" }, + { 502, "EasyIO" }, + { 503, "EAP-Electric GmbH" }, + { 504, "Hardmeier" }, + { 505, "Mircom Group of Companies" }, + { 506, "Quest Controls" }, + { 507, "Mestek, Inc" }, + { 508, "Pulse Energy" }, + { 509, "Tachikawa Corporation" }, + { 510, "University of Nebraska-Lincoln" }, + { 511, "Redwood Systems" }, + { 512, "PASStec Industrie-Elektronik GmbH" }, + { 513, "NgEK, Inc." }, + { 514, "t-mac Technologies" }, + { 515, "Jireh Energy Tech Co., Ltd." }, + { 516, "Enlighted Inc." }, + { 517, "El-Piast Sp. Z o.o" }, + { 518, "NetxAutomation Software GmbH" }, + { 519, "Invertek Drives" }, + { 520, "Deutschmann Automation GmbH & Co. KG" }, + { 521, "EMU Electronic AG" }, + { 522, "Phaedrus Limited" }, + { 523, "Sigmatek GmbH & Co KG" }, + { 524, "Marlin Controls" }, + { 525, "Circutor, SA" }, + { 526, "UTC Fire & Security" }, + { 527, "DENT Instruments, Inc." }, + { 528, "FHP Manufacturing Company – Bosch Group" }, + { 529, "GE Intelligent Platforms" }, + { 530, "Inner Range Pty Ltd" }, + { 531, "GLAS Energy Technology" }, + { 532, "MSR-Electronic-GmbH" }, + { 533, "Energy Control Systems, Inc." }, + { 534, "EMT Controls" }, + { 535, "Daintree" }, + { 536, "EURO ICC d.o.o" }, + { 537, "TE Connectivity Energy" }, + { 538, "GEZE GmbH" }, + { 539, "NEC Corporation" }, + { 540, "Ho Cheung International Company Limited" }, + { 541, "Sharp Manufacturing Systems Corporation" }, + { 542, "DOT CONTROLS a.s." }, + { 543, "BeaconMedæs" }, + { 544, "Midea Commercial Aircon" }, + { 545, "WattMaster Controls" }, + { 546, "Kamstrup A/S" }, + { 547, "CA Computer Automation GmbH" }, + { 548, "Laars Heating Systems Company" }, + { 549, "Hitachi Systems, Ltd." }, + { 550, "Fushan AKE Electronic Engineering Co., Ltd." }, + { 551, "Toshiba International Corporation" }, + { 552, "Starman Systems, LLC" }, + { 553, "Samsung Techwin Co., Ltd." }, + { 554, "ISAS-Integrated Switchgear and Systems P/L" }, + { 555, "Reserved for ASHRAE" }, + { 556, "Obvius" }, + { 557, "Marek Guzik" }, + { 558, "Vortek Instruments, LLC" }, + { 559, "Universal Lighting Technologies" }, + { 560, "Myers Power Products, Inc." }, + { 561, "Vector Controls GmbH" }, + { 562, "Crestron Electronics, Inc." }, + { 563, "A&E Controls Limited" }, + { 564, "Projektomontaza A.D." }, + { 565, "Freeaire Refrigeration" }, + { 566, "Aqua Cooler Pty Limited" }, + { 567, "Basic Controls" }, + { 568, "GE Measurement and Control Solutions Advanced Sensors" }, + { 569, "EQUAL Networks" }, + { 570, "Millennial Net" }, + { 571, "APLI Ltd" }, + { 572, "Electro Industries/GaugeTech" }, + { 573, "SangMyung University" }, + { 574, "Coppertree Analytics, Inc." }, + { 575, "CoreNetiX GmbH" }, + { 576, "Acutherm" }, + { 577, "Dr. Riedel Automatisierungstechnik GmbH" }, + { 578, "Shina System Co., Ltd" }, + { 579, "Iqapertus" }, + { 580, "PSE Technology" }, + { 581, "BA Systems" }, + { 582, "BTICINO" }, + { 583, "Monico, Inc." }, + { 584, "iCue" }, + { 585, "tekmar Control Systems Ltd." }, + { 586, "Control Technology Corporation" }, + { 587, "GFAE GmbH" }, + { 588, "BeKa Software GmbH" }, + { 589, "Isoil Industria SpA" }, + { 590, "Home Systems Consulting SpA" }, + { 591, "Socomec" }, + { 592, "Everex Communications, Inc." }, + { 593, "Ceiec Electric Technology" }, + { 594, "Atrila GmbH" }, + { 595, "WingTechs" }, + { 596, "Shenzhen Mek Intellisys Pte Ltd." }, + { 597, "Nestfield Co., Ltd." }, + { 598, "Swissphone Telecom AG" }, + { 599, "PNTECH JSC" }, + { 600, "Horner APG, LLC" }, + { 601, "PVI Industries, LLC" }, + { 602, "Ela-compil" }, + { 603, "Pegasus Automation International LLC" }, + { 604, "Wight Electronic Services Ltd." }, + { 605, "Marcom" }, + { 606, "Exhausto A/S" }, + { 607, "Dwyer Instruments, Inc." }, + { 608, "Link GmbH" }, + { 609, "Oppermann Regelgerate GmbH" }, + { 610, "NuAire, Inc." }, + { 611, "Nortec Humidity, Inc." }, + { 612, "Bigwood Systems, Inc." }, + { 613, "Enbala Power Networks" }, + { 614, "Inter Energy Co., Ltd." }, + { 615, "ETC" }, + { 616, "COMELEC S.A.R.L" }, + { 617, "Pythia Technologies" }, + { 618, "TrendPoint Systems, Inc." }, + { 619, "AWEX" }, + { 620, "Eurevia" }, + { 621, "Kongsberg E-lon AS" }, + { 622, "FlaktWoods" }, + { 623, "E + E Elektronik GES M.B.H." }, + { 624, "ARC Informatique" }, + { 625, "SKIDATA AG" }, + { 626, "WSW Solutions" }, + { 627, "Trefon Electronic GmbH" }, + { 628, "Dongseo System" }, + { 629, "Kanontec Intelligence Technology Co., Ltd." }, + { 630, "EVCO S.p.A." }, + { 631, "Accuenergy (Canada) Inc." }, + { 632, "SoftDEL" }, + { 633, "Orion Energy Systems, Inc." }, + { 634, "Roboticsware" }, + { 635, "DOMIQ Sp. z o.o." }, + { 636, "Solidyne" }, + { 637, "Elecsys Corporation" }, + { 638, "Conditionaire International Pty. Limited" }, + { 639, "Quebec, Inc." }, + { 640, "Homerun Holdings" }, + { 641, "Murata Americas" }, + { 642, "Comptek" }, + { 643, "Westco Systems, Inc." }, + { 644, "Advancis Software & Services GmbH" }, + { 645, "Intergrid, LLC" }, + { 646, "Markerr Controls, Inc." }, + { 647, "Toshiba Elevator and Building Systems Corporation" }, + { 648, "Spectrum Controls, Inc." }, + { 649, "Mkservice" }, + { 650, "Fox Thermal Instruments" }, + { 651, "SyxthSense Ltd" }, + { 652, "DUHA System S R.O." }, + { 653, "NIBE" }, + { 654, "Melink Corporation" }, + { 655, "Fritz-Haber-Institut" }, + { 656, "MTU Onsite Energy GmbH, Gas Power Systems" }, + { 657, "Omega Engineering, Inc." }, + { 658, "Avelon" }, + { 659, "Ywire Technologies, Inc." }, + { 660, "M.R. Engineering Co., Ltd." }, + { 661, "Lochinvar, LLC" }, + { 662, "Sontay Limited" }, + { 663, "GRUPA Slawomir Chelminski" }, + { 664, "Arch Meter Corporation" }, + { 665, "Senva, Inc." }, + { 666, "Reserved for ASHRAE" }, + { 667, "FM-Tec" }, + { 668, "Systems Specialists, Inc." }, + { 669, "SenseAir" }, + { 670, "AB IndustrieTechnik Srl" }, + { 671, "Cortland Research, LLC" }, + { 672, "MediaView" }, + { 673, "VDA Elettronica" }, + { 674, "CSS, Inc." }, + { 675, "Tek-Air Systems, Inc." }, + { 676, "ICDT" }, + { 677, "The Armstrong Monitoring Corporation" }, + { 678, "DIXELL S.r.l" }, + { 679, "Lead System, Inc." }, + { 680, "ISM EuroCenter S.A." }, + { 681, "TDIS" }, + { 682, "Trade FIDES" }, + { 683, "Knürr GmbH (Emerson Network Power)" }, + { 684, "Resource Data Management" }, + { 685, "Abies Technology, Inc." }, + { 686, "UAB Komfovent" }, + { 687, "MIRAE Electrical Mfg. Co., Ltd." }, + { 688, "HunterDouglas Architectural Projects Scandinavia ApS" }, + { 689, "RUNPAQ Group Co., Ltd" }, + { 690, "Unicard SA" }, + { 691, "IE Technologies" }, + { 692, "Ruskin Manufacturing" }, + { 693, "Calon Associates Limited" }, + { 694, "Contec Co., Ltd." }, + { 695, "iT GmbH" }, + { 696, "Autani Corporation" }, + { 697, "Christian Fortin" }, + { 698, "HDL" }, + { 699, "IPID Sp. Z.O.O Limited" }, + { 700, "Fuji Electric Co., Ltd" }, + { 701, "View, Inc." }, + { 702, "Samsung S1 Corporation" }, + { 703, "New Lift" }, + { 704, "VRT Systems" }, + { 705, "Motion Control Engineering, Inc." }, + { 706, "Weiss Klimatechnik GmbH" }, + { 707, "Elkon" }, + { 708, "Eliwell Controls S.r.l." }, + { 709, "Japan Computer Technos Corp" }, + { 710, "Rational Network ehf" }, + { 711, "Magnum Energy Solutions, LLC" }, + { 712, "MelRok" }, + { 713, "VAE Group" }, + { 714, "LGCNS" }, + { 715, "Berghof Automationstechnik GmbH" }, + { 716, "Quark Communications, Inc." }, + { 717, "Sontex" }, + { 718, "mivune AG" }, + { 719, "Panduit" }, + { 720, "Smart Controls, LLC" }, + { 721, "Compu-Aire, Inc." }, + { 722, "Sierra" }, + { 723, "ProtoSense Technologies" }, + { 724, "Eltrac Technologies Pvt Ltd" }, + { 725, "Bektas Invisible Controls GmbH" }, + { 726, "Entelec" }, + { 727, "INNEXIV" }, + { 728, "Covenant" }, + { 729, "Davitor AB" }, + { 730, "TongFang Technovator" }, + { 731, "Building Robotics, Inc." }, + { 732, "HSS-MSR UG" }, + { 733, "FramTack LLC" }, + { 734, "B. L. Acoustics, Ltd." }, + { 735, "Traxxon Rock Drills, Ltd" }, + { 736, "Franke" }, + { 737, "Wurm GmbH & Co" }, + { 738, "AddENERGIE" }, + { 739, "Mirle Automation Corporation" }, + { 740, "Ibis Networks" }, + { 741, "ID-KARTA s.r.o." }, + { 742, "Anaren, Inc." }, + { 743, "Span, Incorporated" }, + { 744, "Bosch Thermotechnology Corp" }, + { 745, "DRC Technology S.A." }, + { 746, "Shanghai Energy Building Technology Co, Ltd" }, + { 747, "Fraport AG" }, + { 748, "Flowgroup" }, + { 749, "Skytron Energy, GmbH" }, + { 750, "ALTEL Wicha, Golda Sp. J." }, + { 751, "Drupal" }, + { 752, "Axiomatic Technology, Ltd" }, + { 753, "Bohnke + Partner" }, + { 754, "Function1" }, + { 755, "Optergy Pty, Ltd" }, + { 756, "LSI Virticus" }, + { 757, "Konzeptpark GmbH" }, + { 758, "NX Lighting Controls" }, + { 759, "eCurv, Inc." }, + { 760, "Agnosys GmbH" }, + { 761, "Shanghai Sunfull Automation Co., LTD" }, + { 762, "Kurz Instruments, Inc." }, + { 763, "Cias Elettronica S.r.l." }, + { 764, "Multiaqua, Inc." }, + { 765, "BlueBox" }, + { 766, "Sensidyne" }, + { 767, "Viessmann Elektronik GmbH" }, + { 768, "ADFweb.com srl" }, + { 769, "Gaylord Industries" }, + { 770, "Majur Ltd." }, + { 771, "Shanghai Huilin Technology Co., Ltd." }, + { 772, "Exotronic" }, + { 773, "SAFECONTROL s.r.o." }, + { 774, "Amatis" }, + { 775, "Universal Electric Corporation" }, + { 776, "iBACnet" }, + { 777, "Reserved for ASHRAE" }, + { 778, "Smartrise Engineering, Inc." }, + { 779, "Miratron, Inc." }, + { 780, "SmartEdge" }, + { 781, "Mitsubishi Electric Australia Pty Ltd" }, + { 782, "Triangle Research International Ptd Ltd" }, + { 783, "Produal Oy" }, + { 784, "Milestone Systems A/S" }, + { 785, "Trustbridge" }, + { 786, "Feedback Solutions" }, + { 787, "IES" }, + { 788, "ABB Power Protection SA" }, + { 789, "Riptide IO" }, + { 790, "Messerschmitt Systems AG" }, + { 791, "Dezem Energy Controlling" }, + { 792, "MechoSystems" }, + { 793, "evon GmbH" }, + { 794, "CS Lab GmbH" }, + { 795, "8760 Enterprises, Inc." }, + { 796, "Touche Controls" }, + { 797, "Ontrol Teknik Malzeme San. ve Tic. A.S." }, + { 798, "Uni Control System Sp. Z o.o." }, + { 799, "Weihai Ploumeter Co., Ltd" }, + { 800, "Elcom International Pvt. Ltd" }, + { 801, "Signify" }, + { 802, "AutomationDirect" }, + { 803, "Paragon Robotics" }, + { 804, "SMT System & Modules Technology AG" }, + { 805, "Radix IoT LLC" }, + { 806, "CMR Controls Ltd" }, + { 807, "Innovari, Inc." }, + { 808, "ABB Control Products" }, + { 809, "Gesellschaft fur Gebäudeautomation mbH" }, + { 810, "RODI Systems Corp." }, + { 811, "Nextek Power Systems" }, + { 812, "Creative Lighting" }, + { 813, "WaterFurnace International" }, + { 814, "Mercury Security" }, + { 815, "Hisense (Shandong) Air-Conditioning Co., Ltd." }, + { 816, "Layered Solutions, Inc." }, + { 817, "Leegood Automatic System, Inc." }, + { 818, "Shanghai Restar Technology Co., Ltd." }, + { 819, "Reimann Ingenieurbüro" }, + { 820, "LynTec" }, + { 821, "HTP" }, + { 822, "Elkor Technologies, Inc." }, + { 823, "Bentrol Pty Ltd" }, + { 824, "Team-Control Oy" }, + { 825, "NextDevice, LLC" }, + { 826, "iSMA CONTROLLI S.p.a." }, + { 827, "King I Electronics Co., Ltd" }, + { 828, "SAMDAV" }, + { 829, "Next Gen Industries Pvt. Ltd." }, + { 830, "Entic LLC" }, + { 831, "ETAP" }, + { 832, "Moralle Electronics Limited" }, + { 833, "Leicom AG" }, + { 834, "Watts Regulator Company" }, + { 835, "S.C. Orbtronics S.R.L." }, + { 836, "Gaussan Technologies" }, + { 837, "WEBfactory GmbH" }, + { 838, "Ocean Controls" }, + { 839, "Messana Air-Ray Conditioning s.r.l." }, + { 840, "Hangzhou BATOWN Technology Co. Ltd." }, + { 841, "Reasonable Controls" }, + { 842, "Servisys, Inc." }, + { 843, "halstrup-walcher GmbH" }, + { 844, "SWG Automation Fuzhou Limited" }, + { 845, "KSB Aktiengesellschaft" }, + { 846, "Hybryd Sp. z o.o." }, + { 847, "Helvatron AG" }, + { 848, "Oderon Sp. Z.O.O." }, + { 849, "mikolab" }, + { 850, "Exodraft" }, + { 851, "Hochhuth GmbH" }, + { 852, "Integrated System Technologies Ltd." }, + { 853, "Shanghai Cellcons Controls Co., Ltd" }, + { 854, "Emme Controls, LLC" }, + { 855, "Field Diagnostic Services, Inc." }, + { 856, "Ges Teknik A.S." }, + { 857, "Global Power Products, Inc." }, + { 858, "Option NV" }, + { 859, "BV-Control AG" }, + { 860, "Sigren Engineering AG" }, + { 861, "Shanghai Jaltone Technology Co., Ltd." }, + { 862, "MaxLine Solutions Ltd" }, + { 863, "Kron Instrumentos Elétricos Ltda" }, + { 864, "Thermo Matrix" }, + { 865, "Infinite Automation Systems, Inc." }, + { 866, "Vantage" }, + { 867, "Elecon Measurements Pvt Ltd" }, + { 868, "TBA" }, + { 869, "Carnes Company" }, + { 870, "Harman Professional" }, + { 871, "Nenutec Asia Pacific Pte Ltd" }, + { 872, "Gia NV" }, + { 873, "Kepware Tehnologies" }, + { 874, "Temperature Electronics Ltd" }, + { 875, "Packet Power" }, + { 876, "Project Haystack Corporation" }, + { 877, "DEOS Controls Americas Inc." }, + { 878, "Senseware Inc" }, + { 879, "MST Systemtechnik AG" }, + { 880, "Lonix Ltd" }, + { 881, "Gossen Metrawatt GmbH" }, + { 882, "Aviosys International Inc." }, + { 883, "Efficient Building Automation Corp." }, + { 884, "Accutron Instruments Inc." }, + { 885, "Vermont Energy Control Systems LLC" }, + { 886, "DCC Dynamics" }, + { 887, "B.E.G. Brück Electronic GmbH" }, + { 888, "Reserved for ASHRAE" }, + { 889, "NGBS Hungary Ltd." }, + { 890, "ILLUM Technology, LLC" }, + { 891, "Delta Controls Germany Limited" }, + { 892, "S+T Service & Technique S.A." }, + { 893, "SimpleSoft" }, + { 894, "Altair Engineering" }, + { 895, "EZEN Solution Inc." }, + { 896, "Fujitec Co. Ltd." }, + { 897, "Terralux" }, + { 898, "Annicom" }, + { 899, "Bihl+Wiedemann GmbH" }, + { 900, "Draper, Inc." }, + { 901, "Schüco International KG" }, + { 902, "Otis Elevator Company" }, + { 903, "Fidelix Oy" }, + { 904, "RAM GmbH Mess- und Regeltechnik" }, + { 905, "WEMS" }, + { 906, "Ravel Electronics Pvt Ltd" }, + { 907, "OmniMagni" }, + { 908, "Echelon" }, + { 909, "Intellimeter Canada, Inc." }, + { 910, "Bithouse Oy" }, + { 911, "Reserved for ASHRAE" }, + { 912, "BuildPulse" }, + { 913, "Shenzhen 1000 Building Automation Co. Ltd" }, + { 914, "AED Engineering GmbH" }, + { 915, "Güntner GmbH & Co. KG" }, + { 916, "KNXlogic" }, + { 917, "CIM Environmental Group" }, + { 918, "Flow Control" }, + { 919, "Lumen Cache, Inc." }, + { 920, "Ecosystem" }, + { 921, "Potter Electric Signal Company, LLC" }, + { 922, "Tyco Fire & Security S.p.A." }, + { 923, "Watanabe Electric Industry Co., Ltd." }, + { 924, "Causam Energy" }, + { 925, "W-tec AG" }, + { 926, "IMI Hydronic Engineering International SA" }, + { 927, "ARIGO Software" }, + { 928, "MSA Safety" }, + { 929, "Smart Solucoes Ltda – MERCATO" }, + { 930, "PIATRA Engineering" }, + { 931, "ODIN Automation Systems, LLC" }, + { 932, "Belparts NV" }, + { 933, "UAB, SALDA" }, + { 934, "Alre-IT Regeltechnik GmbH" }, + { 935, "Ingenieurbüro H. Lertes GmbH & Co. KG" }, + { 936, "Breathing Buildings" }, + { 937, "eWON SA" }, + { 938, "Cav. Uff. Giacomo Cimberio S.p.A" }, + { 939, "PKE Electronics AG" }, + { 940, "Allen" }, + { 941, "Kastle Systems" }, + { 942, "Logical Electro-Mechanical (EM) Systems, Inc." }, + { 943, "ppKinetics Instruments, LLC" }, + { 944, "Cathexis Technologies" }, + { 945, "Sylop sp. Z o.o. sp.k" }, + { 946, "Brauns Control GmbH" }, + { 947, "OMRON SOCIAL SOLUTIONS CO., LTD." }, + { 948, "Wildeboer Bauteile Gmbh" }, + { 949, "Shanghai Biens Technologies Ltd" }, + { 950, "Beijing HZHY Technology Co., Ltd" }, + { 951, "Building Clouds" }, + { 952, "The University of Sheffield-Department of Electronic and Electrical Engineering" }, + { 953, "Fabtronics Australia Pty Ltd" }, + { 954, "SLAT" }, + { 955, "Software Motor Corporation" }, + { 956, "Armstrong International Inc." }, + { 957, "Steril-Aire, Inc." }, + { 958, "Infinique" }, + { 959, "Arcom" }, + { 960, "Argo Performance, Ltd" }, + { 961, "Dialight" }, + { 962, "Ideal Technical Solutions" }, + { 963, "Neurobat AG" }, + { 964, "Neyer Software Consulting LLC" }, + { 965, "SCADA Technology Development Co., Ltd." }, + { 966, "Demand Logic Limited" }, + { 967, "GWA Group Limited" }, + { 968, "Occitaline" }, + { 969, "NAO Digital Co., Ltd." }, + { 970, "Shenzhen Chanslink Network Technology Co., Ltd." }, + { 971, "Samsung Electronics Co., Ltd." }, + { 972, "Mesa Laboratories, Inc." }, + { 973, "Fischer" }, + { 974, "OpSys Solutions Ltd." }, + { 975, "Advanced Devices Limited" }, + { 976, "Condair" }, + { 977, "INELCOM Ingenieria Electronica Comercial S.A." }, + { 978, "GridPoint, Inc." }, + { 979, "ADF Technologies Sdn Bhd" }, + { 980, "EPM, Inc." }, + { 981, "Lighting Controls Ltd" }, + { 982, "Perix Controls Ltd." }, + { 983, "AERCO International, Inc." }, + { 984, "KONE Inc." }, + { 985, "Ziehl-Abegg SE" }, + { 986, "Robot, S.A." }, + { 987, "Optigo Networks, Inc." }, + { 988, "Openmotics BVBA" }, + { 989, "Metropolitan Industries, Inc." }, + { 990, "Huawei Technologies Co., Ltd." }, + { 991, "Digital Lumens, Inc." }, + { 992, "Vanti" }, + { 993, "Cree Lighting" }, + { 994, "Richmond Heights SDN BHD" }, + { 995, "Payne-Sparkman Lighting Mangement" }, + { 996, "Ashcroft" }, + { 997, "Jet Controls Corp" }, + { 998, "Zumtobel Lighting GmbH" }, + { 999, "Reserved for ASHRAE" }, + { 1000, "Ekon GmbH" }, + { 1001, "Molex" }, + { 1002, "Maco Lighting Pty Ltd." }, + { 1003, "Axecon Corp." }, + { 1004, "Tensor plc" }, + { 1005, "Kaseman Environmental Control Equipment (Shanghai) Limited" }, + { 1006, "AB Axis Industries" }, + { 1007, "Netix Controls" }, + { 1008, "Eldridge Products, Inc." }, + { 1009, "Micronics" }, + { 1010, "Fortecho Solutions Ltd" }, + { 1011, "Sellers Manufacturing Company" }, + { 1012, "Rite-Hite Doors, Inc." }, + { 1013, "Violet Defense LLC" }, + { 1014, "Simna" }, + { 1015, "Multi-Énergie Best Inc." }, + { 1016, "Mega System Technologies, Inc." }, + { 1017, "Rheem" }, + { 1018, "Ing. Punzenberger COPA-DATA GmbH" }, + { 1019, "MEC Electronics GmbH" }, + { 1020, "Taco Comfort Solutions" }, + { 1021, "Alexander Maier GmbH" }, + { 1022, "Ecorithm, Inc." }, + { 1023, "Accurro Ltd" }, + { 1024, "ROMTECK Australia Pty Ltd" }, + { 1025, "Splash Monitoring Limited" }, + { 1026, "Light Application" }, + { 1027, "Logical Building Automation" }, + { 1028, "Exilight Oy" }, + { 1029, "Hager Electro SAS" }, + { 1030, "KLIF Co., LTD" }, + { 1031, "HygroMatik" }, + { 1032, "Daniel Mousseau Programmation & Electronique" }, + { 1033, "Aerionics Inc." }, + { 1034, "M2S Electronique Ltee" }, + { 1035, "Automation Components, Inc." }, + { 1036, "Niobrara Research & Development Corporation" }, + { 1037, "Netcom Sicherheitstechnik GmbH" }, + { 1038, "Lumel S.A." }, + { 1039, "Great Plains Industries, Inc." }, + { 1040, "Domotica Labs S.R.L" }, + { 1041, "Energy Cloud, Inc." }, + { 1042, "Vomatec" }, + { 1043, "Demma Companies" }, + { 1044, "Valsena" }, + { 1045, "Comsys Bärtsch AG" }, + { 1046, "bGrid" }, + { 1047, "MDJ Software Pty Ltd" }, + { 1048, "Dimonoff, Inc." }, + { 1049, "Edomo Systems, GmbH" }, + { 1050, "Effektiv, LLC" }, + { 1051, "SteamOVap" }, + { 1052, "grandcentrix GmbH" }, + { 1053, "Weintek Labs, Inc." }, + { 1054, "Intefox GmbH" }, + { 1055, "Radius22 Automation Company" }, + { 1056, "Ringdale, Inc." }, + { 1057, "Iwaki America" }, + { 1058, "Bractlet" }, + { 1059, "STULZ Air Technology Systems, Inc." }, + { 1060, "Climate Ready Engineering Pty Ltd" }, + { 1061, "Genea Energy Partners" }, + { 1062, "IoTall Chile" }, + { 1063, "IKS Co., Ltd." }, + { 1064, "Yodiwo AB" }, + { 1065, "TITAN electronic GmbH" }, + { 1066, "IDEC Corporation" }, + { 1067, "SIFRI SL" }, + { 1068, "Thermal Gas Systems Inc." }, + { 1069, "Building Automation Products, Inc." }, + { 1070, "Asset Mapping" }, + { 1071, "Smarteh Company" }, + { 1072, "Datapod (Australia) Pty Ltd." }, + { 1073, "Buildings Alive Pty Ltd" }, + { 1074, "Digital Elektronik" }, + { 1075, "Talent Automação e Tecnologia Ltda" }, + { 1076, "Norposh Limited" }, + { 1077, "Merkur Funksysteme AG" }, + { 1078, "Faster CZ spol. S.r.o" }, + { 1079, "Eco-Adapt" }, + { 1080, "Energocentrum Plus, s.r.o" }, + { 1081, "amBX UK Ltd" }, + { 1082, "Western Reserve Controls, Inc." }, + { 1083, "LayerZero Power Systems, Inc." }, + { 1084, "CIC Jan Hřebec s.r.o." }, + { 1085, "Sigrov BV" }, + { 1086, "ISYS-Intelligent Systems" }, + { 1087, "Gas Detection (Australia) Pty Ltd" }, + { 1088, "Kinco Automation (Shanghai) Ltd." }, + { 1089, "Lars Energy, LLC" }, + { 1090, "Flamefast (UK) Ltd." }, + { 1091, "Royal Service Air Conditioning" }, + { 1092, "Ampio Sp. Z o.o." }, + { 1093, "Inovonics Wireless Corporation" }, + { 1094, "Nvent Thermal Management" }, + { 1095, "Sinowell Control System Ltd" }, + { 1096, "Moxa Inc." }, + { 1097, "Matrix iControl SDN BHD" }, + { 1098, "PurpleSwift" }, + { 1099, "OTIM Technologies" }, + { 1100, "FlowMate Limited" }, + { 1101, "Degree Controls, Inc." }, + { 1102, "Fei Xing (Shanghai) Software Technologies Co., Ltd." }, + { 1103, "Berg GmbH" }, + { 1104, "ARENZ.IT" }, + { 1105, "Edelstrom Electronic Devices & Designing LLC" }, + { 1106, "Drive Connect, LLC" }, + { 1107, "DevelopNow" }, + { 1108, "Poort" }, + { 1109, "VMEIL Information (Shanghai) Ltd" }, + { 1110, "Rayleigh Instruments" }, + { 1111, "Reserved for ASHRAE" }, + { 1112, "CODESYS Development" }, + { 1113, "Smartware Technologies Group, LLC" }, + { 1114, "Polar Bear Solutions" }, + { 1115, "Codra" }, + { 1116, "Pharos Architectural Controls Ltd" }, + { 1117, "EngiNear Ltd." }, + { 1118, "Ad Hoc Electronics" }, + { 1119, "Unified Microsystems" }, + { 1120, "Industrieelektronik Brandenburg GmbH" }, + { 1121, "Hartmann GmbH" }, + { 1122, "Piscada" }, + { 1123, "KMB systems, s.r.o." }, + { 1124, "PowerTech Engineering AS" }, + { 1125, "Telefonbau Arthur Schwabe GmbH & Co. KG" }, + { 1126, "Wuxi Fistwelove Technology Co., Ltd." }, + { 1127, "Prysm" }, + { 1128, "STEINEL GmbH" }, + { 1129, "Georg Fischer JRG AG" }, + { 1130, "Make Develop SL" }, + { 1131, "Monnit Corporation" }, + { 1132, "Mirror Life Corporation" }, + { 1133, "Secure Meters Limited" }, + { 1134, "PECO" }, + { 1135, ".CCTECH, Inc." }, + { 1136, "LightFi Limited" }, + { 1137, "Nice Spa" }, + { 1138, "Fiber SenSys, Inc." }, + { 1139, "B&D Buchta und Degeorgi" }, + { 1140, "Ventacity Systems, Inc." }, + { 1141, "Hitachi-Johnson Controls Air Conditioning, Inc." }, + { 1142, "Sage Metering, Inc." }, + { 1143, "Andel Limited" }, + { 1144, "ECOSmart Technologies" }, + { 1145, "S.E.T." }, + { 1146, "Protec Fire Detection Spain SL" }, + { 1147, "AGRAMER UG" }, + { 1148, "Anylink Electronic GmbH" }, + { 1149, "Schindler, Ltd" }, + { 1150, "Jibreel Abdeen Est." }, + { 1151, "Fluidyne Control Systems Pvt. Ltd" }, + { 1152, "Prism Systems, Inc." }, + { 1153, "Enertiv" }, + { 1154, "Mirasoft GmbH & Co. KG" }, + { 1155, "DUALTECH IT" }, + { 1156, "Countlogic, LLC" }, + { 1157, "Kohler" }, + { 1158, "Chen Sen Controls Co., Ltd." }, + { 1159, "Greenheck" }, + { 1160, "Intwine Connect, LLC" }, + { 1161, "Karlborgs Elkontroll" }, + { 1162, "Datakom" }, + { 1163, "Hoga Control AS" }, + { 1164, "Cool Automation" }, + { 1165, "Inter Search Co., Ltd" }, + { 1166, "DABBEL-Automation Intelligence GmbH" }, + { 1167, "Gadgeon Engineering Smartness" }, + { 1168, "Coster Group S.r.l." }, + { 1169, "Walter Müller AG" }, + { 1170, "Fluke" }, + { 1171, "Quintex Systems Ltd" }, + { 1172, "Senfficient SDN BHD" }, + { 1173, "Nube iO Operations Pty Ltd" }, + { 1174, "DAS Integrator Pte Ltd" }, + { 1175, "CREVIS Co., Ltd" }, + { 1176, "iSquared software inc." }, + { 1177, "KTG GmbH" }, + { 1178, "POK Group Oy" }, + { 1179, "Adiscom" }, + { 1180, "Incusense" }, + { 1181, "75F" }, + { 1182, "Anord Mardix, Inc." }, + { 1183, "HOSCH Gebäudeautomation Neue Produkte GmbH" }, + { 1184, "Bosch.IO GmbH" }, + { 1185, "Royal Boon Edam International B.V." }, + { 1186, "Clack Corporation" }, + { 1187, "Unitex Controls LLC" }, + { 1188, "KTC Göteborg AB" }, + { 1189, "Interzon AB" }, + { 1190, "ISDE ING SL" }, + { 1191, "ABM automation building messaging GmbH" }, + { 1192, "Kentec Electronics Ltd" }, + { 1193, "Emerson Commercial and Residential Solutions" }, + { 1194, "Powerside" }, + { 1195, "SMC Group" }, + { 1196, "EOS Weather Instruments" }, + { 1197, "Zonex Systems" }, + { 1198, "Generex Systems Computervertriebsgesellschaft mbH" }, + { 1199, "Energy Wall LLC" }, + { 1200, "Thermofin" }, + { 1201, "SDATAWAY SA" }, + { 1202, "Biddle Air Systems Limited" }, + { 1203, "Kessler Ellis Products" }, + { 1204, "Thermoscreens" }, + { 1205, "Modio" }, + { 1206, "Newron Solutions" }, + { 1207, "Unitronics" }, + { 1208, "TRILUX GmbH & Co. KG" }, + { 1209, "Kollmorgen Steuerungstechnik GmbH" }, + { 1210, "Bosch Rexroth AG" }, + { 1211, "Alarko Carrier" }, + { 1212, "Verdigris Technologies" }, + { 1213, "Shanghai SIIC-Longchuang Smartech So., Ltd." }, + { 1214, "Quinda Co." }, + { 1215, "GRUNER AG" }, + { 1216, "BACMOVE" }, + { 1217, "PSIDAC AB" }, + { 1218, "ISICON-Control Automation" }, + { 1219, "Big Ass Fans" }, + { 1220, "din – Dietmar Nocker Facility Management GmbH" }, + { 1221, "Teldio" }, + { 1222, "MIKROKLIMA s.r.o." }, + { 1223, "Density" }, + { 1224, "ICONAG-Leittechnik GmbH" }, + { 1225, "Awair" }, + { 1226, "T&D Engineering, Ltd" }, + { 1227, "Sistemas Digitales" }, + { 1228, "Loxone Electronics GmbH" }, + { 1229, "ActronAir" }, + { 1230, "Inductive Automation" }, + { 1231, "Thor Engineering GmbH" }, + { 1232, "Berner International, LLC" }, + { 1233, "Potsdam Sensors LLC" }, + { 1234, "Kohler Mira Ltd" }, + { 1235, "Tecomon GmbH" }, + { 1236, "Two Dimensional Instruments, LLC" }, + { 1237, "LEFA Technologies Pte. Ltd." }, + { 1238, "EATON CEAG Notlichtsysteme GmbH" }, + { 1239, "Commbox Tecnologia" }, + { 1240, "IPVideo Corporation" }, + { 1241, "Bender GmbH & Co. KG" }, + { 1242, "Rhymebus Corporation" }, + { 1243, "Axon Systems Ltd" }, + { 1244, "Engineered Air" }, + { 1245, "Elipse Software Ltda" }, + { 1246, "Simatix Building Technologies Pvt. Ltd." }, + { 1247, "W.A. Benjamin Electric Co." }, + { 1248, "TROX Air Conditioning Components (Suzhou) Co. Ltd." }, + { 1249, "SC Medical Pty Ltd." }, + { 1250, "Elcanic A/S" }, + { 1251, "Obeo AS" }, + { 1252, "Tapa, Inc." }, + { 1253, "ASE Smart Energy, Inc." }, + { 1254, "Performance Services, Inc." }, + { 1255, "Veridify Security" }, + { 1256, "CD Innovation LTD" }, + { 1257, "Ben Peoples Industries, LLC" }, + { 1258, "UNICOMM Sp. z o.o" }, + { 1259, "Thing Technologies GmbH" }, + { 1260, "Beijing Hailin Control Technology, Inc." }, + { 1261, "Digital Realty" }, + { 1262, "Agrowtek Inc." }, + { 1263, "DSP Innovation BV" }, + { 1264, "STV Electronic GmbH" }, + { 1265, "Elmeasure India Pvt Ltd." }, + { 1266, "Pineshore Energy LLC" }, + { 1267, "Brasch Environmental Technologies, LLC" }, + { 1268, "Lion Controls Co., LTD" }, + { 1269, "Sinux" }, + { 1270, "Avnet Inc." }, + { 1271, "Somfy Activites SA" }, + { 1272, "Amico" }, + { 1273, "SageGlass" }, + { 1274, "AuVerte" }, + { 1275, "Agile Connects Pvt. Ltd." }, + { 1276, "Locimation Pty Ltd" }, + { 1277, "Envio Systems GmbH" }, + { 1278, "Voytech Systems Limited" }, + { 1279, "Davidsmeyer und Paul GmbH" }, + { 1280, "Lusher Engineering Services" }, + { 1281, "CHNT Nanjing Techsel Intelligent Company LTD" }, + { 1282, "Threetronics Pty Ltd" }, + { 1283, "SkyFoundry, LLC" }, + { 1284, "HanilProTech" }, + { 1285, "Sensorscall" }, + { 1286, "Shanghai Jingpu Information Technology, Co., Ltd." }, + { 1287, "Lichtmanufaktur Berlin GmbH" }, + { 1288, "Eco Parking Technologies" }, + { 1289, "Envision Digital International Pte Ltd" }, + { 1290, "Antony Developpement Electronique" }, + { 1291, "i2systems" }, + { 1292, "Thureon International Limited" }, + { 1293, "Pulsafeeder" }, + { 1294, "MegaChips Corporation" }, + { 1295, "TES Controls" }, + { 1296, "Cermate" }, + { 1297, "Grand Valley State University" }, + { 1298, "Symcon Gmbh" }, + { 1299, "The Chicago Faucet Company" }, + { 1300, "Geberit AG" }, + { 1301, "Rex Controls" }, + { 1302, "IVMS GmbH" }, + { 1303, "MNPP Saturn Ltd." }, + { 1304, "Regal Beloit" }, + { 1305, "ACS-Air Conditioning Solutions" }, + { 1306, "GBX Technology, LLC" }, + { 1307, "Kaiterra" }, + { 1308, "ThinKuan loT Technology (Shanghai) Co., Ltd" }, + { 1309, "HoCoSto B.V." }, + { 1310, "Shenzhen AS-AI Technology Co., Ltd." }, + { 1311, "RPS S.p.a." }, + { 1312, "Esmé solutions" }, + { 1313, "IOTech Systems Limited" }, + { 1314, "i-AutoLogic Co., Ltd." }, + { 1315, "New Age Micro, LLC" }, + { 1316, "Guardian Glass" }, + { 1317, "Guangzhou Zhaoyu Information Technology" }, + { 1318, "ACE IoT Solutions LLC" }, + { 1319, "Poris Electronics Co., Ltd." }, + { 1320, "Terminus Technologies Group" }, + { 1321, "Intech 21, Inc." }, + { 1322, "Accurate Electronics" }, + { 1323, "Fluence Bioengineering" }, + { 1324, "Mun Hean Singapore Pte Ltd" }, + { 1325, "Katronic AG & Co. KG" }, + { 1326, "Suzhou XinAo Information Technology Co. Ltd" }, + { 1327, "Linktekk Technology, JSC." }, + { 1328, "Stirling Ultracold" }, + { 1329, "UV Partners, Inc." }, + { 1330, "ProMinent GmbH" }, + { 1331, "Multi-Tech Systems, Inc." }, + { 1332, "JUMO GmbH & Co. KG" }, + { 1333, "Qingdao Huarui Technology Co. Ltd.," }, + { 1334, "Cairn Systemes" }, + { 1335, "NeuroLogic Research Corp." }, + { 1336, "Transition Technologies Advanced Solutions Sp. z o.o" }, + { 1337, "Xxter bv" }, + { 1338, "PassiveLogic" }, + { 1339, "EnSmart Controls" }, + { 1340, "Watts Heating and Hot Water Solutions, dba Lync" }, + { 1341, "Troposphaira Technologies LLP" }, + { 1342, "Network Thermostat" }, + { 1343, "Titanium Intelligent Solutions, LLC" }, + { 1344, "Numa Products, LLC" }, + { 1345, "WAREMA Renkhoff SE" }, + { 1346, "Frese A/S" }, + { 1347, "Mapped" }, + { 1348, "ELEKTRODESIGN ventilatory s.r.o" }, + { 1349, "AirCare Automation, Inc." }, + { 1350, "Antrum" }, + { 1351, "Bao Linh Connect Technology" }, + { 1352, "Virginia Controls, LLC" }, + { 1353, "Duosys SDN BHD" }, + { 1354, "Onsen SAS" }, + { 1355, "Vaughn Thermal Corporation" }, + { 1356, "Thermoplastic Engineering Ltd (TPE)" }, + { 1357, "Wirth Research Ltd." }, + { 1358, "SST Automation" }, + { 1359, "Shanghai Bencol Electronic Technology Co., Ltd" }, + { 1360, "AIWAA Systems Private Limited" }, + { 1361, "Enless Wireless" }, + { 1362, "Ozuno Engineering Pty Ltd" }, + { 1363, "Hubbell, The Electric Heater Company" }, + { 1364, "Industrial Turnaround Corporation (ITAC)" }, + { 1365, "Wadsworth Control Systems" }, + { 1366, "Services Hilo Inc." }, + { 1367, "iDM Energiesysteme GmbH" }, + { 1368, "BeNext B.V." }, + { 1369, "CleanAir.ai Corporation" }, + { 1369, "CleanAir.ai Corporation" }, + { 1370, "Revolution Microelectronics (America) Inc." }, + { 1371, "Real-Time Systems GmbH" }, + { 1372, "ZedBee Technologies Pvt Ltd" }, + { 1373, "Winmate Technology Solutions Pvt. Ltd." }, + { 1373, "Winmate Technology Solutions Pvt. Ltd." }, + { 1374, "Senticon Ltd." }, + { 1375, "Rossaker AB" }, + { 1376, "OPIT Solutions Ltd" }, + { 1377, "Hotowell International Co., Limited" }, + { 1378, "Inim Electronics S.R.L. Unipersonale" }, + { 1379, "Airthings ASA" }, + { 1380, "Analog Devices, Inc." }, + { 1381, "AIDirections DMCC" }, + { 1382, "Prima Electro S.p.A." }, + { 1383, "KLT Control System Ltd." }, + { 1384, "Evolution Controls Inc." }, + { 1385, "Bever Innovations" }, + { 1386, "Pelican Wireless Systems" }, + { 1387, "Control Concepts Inc." }, + { 1388, "Augmatic Technologies Pvt. Ltd." }, + { 1389, "Xiamen Milesight loT Co., Ltd" }, + { 1390, "Tianjin Anjie loT Schience and Technology Co., Ltd" }, + { 1391, "Guangzhou S. Energy Electronics Technology Co. Ltd." }, + { 1392, "AKVO Atmospheric Water Systems Pvt. Ltd." }, + { 1393, "EmFirst Co. Ltd." }, + { 1394, "Iion Systems ApS" }, + { 1396, "SAF Tehnika JSC" }, + { 1397, "Komfort IQ, Inc." }, + { 1398, "CoolTera Limited" }, + { 1399, "Hadron Solutions S.r.l.s" }, + { 1401, "Bitpool" }, + { 1402, "Sonicu, LLC" }, + { 1403, "Rishabh Instruments Limited" }, + { 1404, "Thing Warehouse LLC" }, + { 1405, "Innofriends GmbH" }, + { 1406, "Metronic AKP Sp. J." }, + { 1407, "Techknave" }, + { 1408, "Elsner Elektronik" }, + { 1409, "LEFOO Industrial (Hangzhou) Co., Ltd." }, + { 1410, "Calibration Technologies, Inc." }, + { 1411, "Allorado" }, + { 1412, "Verkada" }, + { 1413, "Wattsense" }, + { 1414, "Emerson Automation Solutions" }, + { 1415, "Growlink" }, + { 1416, "Olympia Electronics" }, + { 1417, "Normal Software, Inc." }, + { 1418, "ST Engineering Solution JSC" }, + { 1419, "Industrial Flow Solutions" }, + { 1420, "Ubiqisense ApS" }, + { 1421, "Tiger-Soft" }, + { 1422, "Ecodom Srl" }, + { 1423, "Bilgipro IoT Systems" }, + { 1424, "planspur netdesign GmbH" }, + { 1425, "Dolphin Solutions Ltd" }, + { 1426, "Mitsubishi Electric Corporation, Kobe Works" }, + { 1427, "Ecovena" }, + { 1428, "Gree Electric Appliances Inc of Zhuhai" }, + { 1429, "Conspec Controls" }, + { 1430, "Hangzhou Hikvision Digital Technology Co., Ltd." }, + { 1431, "Crystal Peak Security" }, + { 1432, "PermAlert" }, + { 1433, "Zhejiang Misilin Technology Co., Ltd." }, + { 1434, "Dekker Vacuum Technologies" }, + { 1435, "Edwards Limited" }, + { 1436, "Leybold GmbH" }, + { 1437, "International Gas Detectors" }, + { 1438, "Atlas Copco Airpower NV" }, + { 1439, "Air Sentry Limited" }, + { 1440, "Aelsys" }, + { 1441, "Granby Consulting LLC" }, + { 1442, "Clever Relay" }, + { 1443, "Monico Monitoring, Inc." }, + { 1444, "Oqdo" }, + { 1445, "Matrix Comsec Private Limited" }, + { 1446, "Resource Solutions" }, + { 1447, "American Gas Safety, LLC" }, + { 1448, "S&S Northern Ltd." }, + { 1449, "Ulbios Techsens" }, + { 1450, "Bowery Farming, Inc." }, + { 1451, "Ryobi Limited" }, + { 1452, "EkkoSense Ltd" }, + { 1453, "ClimaCool" }, + { 0, NULL } +}; +static value_string_ext BACnetVendorIdentifiers_ext = VALUE_STRING_EXT_INIT(BACnetVendorIdentifiers); + +static int proto_bacapp = -1; +static int hf_bacapp_type = -1; +static int hf_bacapp_pduflags = -1; +static int hf_bacapp_SEG = -1; +static int hf_bacapp_MOR = -1; +static int hf_bacapp_SA = -1; +static int hf_bacapp_response_segments = -1; +static int hf_bacapp_max_adpu_size = -1; +static int hf_bacapp_invoke_id = -1; +static int hf_bacapp_objectType = -1; +static int hf_bacapp_object_name = -1; +static int hf_bacapp_instanceNumber = -1; +static int hf_bacapp_sequence_number = -1; +static int hf_bacapp_window_size = -1; +static int hf_bacapp_service = -1; +static int hf_bacapp_NAK = -1; +static int hf_bacapp_SRV = -1; +static int hf_bacapp_notify_type = -1; +static int hf_bacapp_event_type = -1; +static int hf_bacapp_error_class = -1; +static int hf_bacapp_error_code = -1; +static int hf_Device_Instance_Range_Low_Limit = -1; +static int hf_Device_Instance_Range_High_Limit = -1; +static int hf_BACnetRejectReason = -1; +static int hf_BACnetAbortReason = -1; +static int hf_BACnetApplicationTagNumber = -1; +static int hf_BACnetContextTagNumber = -1; +static int hf_BACnetExtendedTagNumber = -1; +static int hf_BACnetNamedTag = -1; +static int hf_BACnetTagClass = -1; +static int hf_BACnetCharacterSet = -1; +static int hf_BACnetCodePage = -1; +static int hf_bacapp_tag_lvt = -1; +static int hf_bacapp_tag_ProcessId = -1; +static int hf_bacapp_tag_to_state = -1; +static int hf_bacapp_tag_from_state = -1; +static int hf_bacapp_uservice = -1; +static int hf_BACnetPropertyIdentifier = -1; +static int hf_BACnetVendorIdentifier = -1; +static int hf_BACnetRestartReason = -1; +static int hf_bacapp_tag_IPV4 = -1; +static int hf_bacapp_tag_IPV6 = -1; +static int hf_bacapp_tag_PORT = -1; +static int hf_bacapp_tag_mac_address_broadcast = -1; +static int hf_bacapp_reserved_ashrea = -1; +static int hf_bacapp_unused_bits = -1; +static int hf_bacapp_bit = -1; +static int hf_bacapp_complete_bitstring = -1; + +/* present value */ +static int hf_bacapp_present_value_null = -1; +static int hf_bacapp_present_value_bool = -1; +static int hf_bacapp_present_value_unsigned = -1; +static int hf_bacapp_present_value_signed = -1; +static int hf_bacapp_present_value_real = -1; +static int hf_bacapp_present_value_double = -1; +static int hf_bacapp_present_value_octet_string = -1; +static int hf_bacapp_present_value_char_string = -1; +static int hf_bacapp_present_value_bit_string = -1; +static int hf_bacapp_present_value_enum_index = -1; + +/* some more variables for segmented messages */ +static int hf_msg_fragments = -1; +static int hf_msg_fragment = -1; +static int hf_msg_fragment_overlap = -1; +static int hf_msg_fragment_overlap_conflicts = -1; +static int hf_msg_fragment_multiple_tails = -1; +static int hf_msg_fragment_too_long_fragment = -1; +static int hf_msg_fragment_error = -1; +static int hf_msg_fragment_count = -1; +static int hf_msg_reassembled_in = -1; +static int hf_msg_reassembled_length = -1; + +static gint ett_msg_fragment = -1; +static gint ett_msg_fragments = -1; + +static gint ett_bacapp = -1; +static gint ett_bacapp_control = -1; +static gint ett_bacapp_tag = -1; +static gint ett_bacapp_list = -1; +static gint ett_bacapp_value = -1; + +static expert_field ei_bacapp_bad_length = EI_INIT; +static expert_field ei_bacapp_bad_tag = EI_INIT; +static expert_field ei_bacapp_opening_tag = EI_INIT; +static expert_field ei_bacapp_max_recursion_depth_reached = EI_INIT; + +static gint32 propertyIdentifier = -1; +static gint32 propertyArrayIndex = -1; +static guint32 object_type = 4096; + +static guint8 bacapp_flags = 0; +static guint8 bacapp_seq = 0; + +/* Defined to allow vendor identifier registration of private transfer dissectors */ +static dissector_table_t bacapp_dissector_table; + + +/* Stat: BACnet Packets sorted by IP */ +bacapp_info_value_t bacinfo; + +static const gchar* st_str_packets_by_ip = "BACnet Packets by IP"; +static const gchar* st_str_packets_by_ip_dst = "By Destination"; +static const gchar* st_str_packets_by_ip_src = "By Source"; +static int st_node_packets_by_ip = -1; +static int st_node_packets_by_ip_dst = -1; +static int st_node_packets_by_ip_src = -1; + +static void +bacapp_packet_stats_tree_init(stats_tree* st) +{ + st_node_packets_by_ip = stats_tree_create_pivot(st, st_str_packets_by_ip, 0); + st_node_packets_by_ip_src = stats_tree_create_node(st, st_str_packets_by_ip_src, st_node_packets_by_ip, STAT_DT_INT, TRUE); + st_node_packets_by_ip_dst = stats_tree_create_node(st, st_str_packets_by_ip_dst, st_node_packets_by_ip, STAT_DT_INT, TRUE); +} + +static gchar * +bacapp_get_address_label(const char *tag, address *addr) +{ + gchar *addr_str, *label_str; + + addr_str = address_to_str(NULL, addr); + label_str = wmem_strconcat(NULL, tag, addr_str, NULL); + wmem_free(NULL, addr_str); + return label_str; +} + +static tap_packet_status +bacapp_stats_tree_packet(stats_tree* st, packet_info* pinfo, epan_dissect_t* edt _U_, const void* p, tap_flags_t flags _U_) +{ + int packets_for_this_dst; + int packets_for_this_src; + int service_for_this_dst; + int service_for_this_src; + int src_for_this_dst; + int dst_for_this_src; + int objectid_for_this_dst; + int objectid_for_this_src; + int instanceid_for_this_dst; + int instanceid_for_this_src; + gchar *dststr; + gchar *srcstr; + const bacapp_info_value_t *binfo = (const bacapp_info_value_t *)p; + + srcstr = bacapp_get_address_label("Src: ", &pinfo->src); + dststr = bacapp_get_address_label("Dst: ", &pinfo->dst); + + tick_stat_node(st, st_str_packets_by_ip, 0, TRUE); + packets_for_this_dst = tick_stat_node(st, st_str_packets_by_ip_dst, st_node_packets_by_ip, TRUE); + packets_for_this_src = tick_stat_node(st, st_str_packets_by_ip_src, st_node_packets_by_ip, TRUE); + src_for_this_dst = tick_stat_node(st, dststr, packets_for_this_dst, TRUE); + dst_for_this_src = tick_stat_node(st, srcstr, packets_for_this_src, TRUE); + service_for_this_src = tick_stat_node(st, dststr, dst_for_this_src, TRUE); + service_for_this_dst = tick_stat_node(st, srcstr, src_for_this_dst, TRUE); + if (binfo->service_type) { + objectid_for_this_dst = tick_stat_node(st, binfo->service_type, service_for_this_dst, TRUE); + objectid_for_this_src = tick_stat_node(st, binfo->service_type, service_for_this_src, TRUE); + if (binfo->object_ident) { + instanceid_for_this_dst = tick_stat_node(st, binfo->object_ident, objectid_for_this_dst, TRUE); + tick_stat_node(st, binfo->instance_ident, instanceid_for_this_dst, FALSE); + instanceid_for_this_src = tick_stat_node(st, binfo->object_ident, objectid_for_this_src, TRUE); + tick_stat_node(st, binfo->instance_ident, instanceid_for_this_src, FALSE); + } + } + + wmem_free(NULL, srcstr); + wmem_free(NULL, dststr); + + return TAP_PACKET_REDRAW; +} + +/* Stat: BACnet Packets sorted by Service */ +static const gchar* st_str_packets_by_service = "BACnet Packets by Service"; +static int st_node_packets_by_service = -1; + +static void +bacapp_service_stats_tree_init(stats_tree* st) +{ + st_node_packets_by_service = stats_tree_create_pivot(st, st_str_packets_by_service, 0); +} + +static tap_packet_status +bacapp_stats_tree_service(stats_tree* st, packet_info* pinfo, epan_dissect_t* edt _U_, const void* p, tap_flags_t flags _U_) +{ + int servicetype; + int src, dst; + int objectid; + + gchar *dststr; + gchar *srcstr; + + const bacapp_info_value_t *binfo = (const bacapp_info_value_t *)p; + + srcstr = bacapp_get_address_label("Src: ", &pinfo->src); + dststr = bacapp_get_address_label("Dst: ", &pinfo->dst); + + tick_stat_node(st, st_str_packets_by_service, 0, TRUE); + if (binfo->service_type) { + servicetype = tick_stat_node(st, binfo->service_type, st_node_packets_by_service, TRUE); + src = tick_stat_node(st, srcstr, servicetype, TRUE); + dst = tick_stat_node(st, dststr, src, TRUE); + if (binfo->object_ident) { + objectid = tick_stat_node(st, binfo->object_ident, dst, TRUE); + tick_stat_node(st, binfo->instance_ident, objectid, FALSE); + } + } + + wmem_free(NULL, srcstr); + wmem_free(NULL, dststr); + + return TAP_PACKET_REDRAW; +} + +/* Stat: BACnet Packets sorted by Object Type */ +static const gchar* st_str_packets_by_objectid = "BACnet Packets by Object Type"; +static int st_node_packets_by_objectid = -1; + +static void +bacapp_objectid_stats_tree_init(stats_tree* st) +{ + st_node_packets_by_objectid = stats_tree_create_pivot(st, st_str_packets_by_objectid, 0); +} + +static tap_packet_status +bacapp_stats_tree_objectid(stats_tree* st, packet_info* pinfo, epan_dissect_t* edt _U_, const void* p, tap_flags_t flags _U_) +{ + int servicetype; + int src, dst; + int objectid; + + gchar *dststr; + gchar *srcstr; + const bacapp_info_value_t *binfo = (const bacapp_info_value_t *)p; + + srcstr = bacapp_get_address_label("Src: ", &pinfo->src); + dststr = bacapp_get_address_label("Dst: ", &pinfo->dst); + + tick_stat_node(st, st_str_packets_by_objectid, 0, TRUE); + if (binfo->object_ident) { + objectid = tick_stat_node(st, binfo->object_ident, st_node_packets_by_objectid, TRUE); + src = tick_stat_node(st, srcstr, objectid, TRUE); + dst = tick_stat_node(st, dststr, src, TRUE); + if (binfo->service_type) { + servicetype = tick_stat_node(st, binfo->service_type, dst, TRUE); + tick_stat_node(st, binfo->instance_ident, servicetype, FALSE); + } + } + + wmem_free(NULL, srcstr); + wmem_free(NULL, dststr); + + return TAP_PACKET_REDRAW; +} + +/* Stat: BACnet Packets sorted by Instance No */ +static const gchar* st_str_packets_by_instanceid = "BACnet Packets by Instance ID"; +static int st_node_packets_by_instanceid = -1; + +static void +bacapp_instanceid_stats_tree_init(stats_tree* st) +{ + st_node_packets_by_instanceid = stats_tree_create_pivot(st, st_str_packets_by_instanceid, 0); +} + +static tap_packet_status +bacapp_stats_tree_instanceid(stats_tree* st, packet_info* pinfo, epan_dissect_t* edt _U_, const void* p, tap_flags_t flags _U_) +{ + int servicetype; + int src, dst; + int instanceid; + + gchar *dststr; + gchar *srcstr; + const bacapp_info_value_t *binfo = (const bacapp_info_value_t *)p; + + srcstr = bacapp_get_address_label("Src: ", &pinfo->src); + dststr = bacapp_get_address_label("Dst: ", &pinfo->dst); + + tick_stat_node(st, st_str_packets_by_instanceid, 0, TRUE); + if (binfo->object_ident) { + instanceid = tick_stat_node(st, binfo->instance_ident, st_node_packets_by_instanceid, TRUE); + src = tick_stat_node(st, srcstr, instanceid, TRUE); + dst = tick_stat_node(st, dststr, src, TRUE); + if (binfo->service_type) { + servicetype = tick_stat_node(st, binfo->service_type, dst, TRUE); + tick_stat_node(st, binfo->object_ident, servicetype, FALSE); + } + } + + wmem_free(NULL, srcstr); + wmem_free(NULL, dststr); + + return TAP_PACKET_REDRAW; +} + + +/* register all BACnet Ststistic trees */ +static void +register_bacapp_stat_trees(void) +{ + stats_tree_register("bacapp", "bacapp_ip", "BACnet/Packets sorted by IP", 0, + bacapp_stats_tree_packet, bacapp_packet_stats_tree_init, NULL); + stats_tree_register("bacapp", "bacapp_service", "BACnet/Packets sorted by Service", 0, + bacapp_stats_tree_service, bacapp_service_stats_tree_init, NULL); + stats_tree_register("bacapp", "bacapp_objectid", "BACnet/Packets sorted by Object Type", 0, + bacapp_stats_tree_objectid, bacapp_objectid_stats_tree_init, NULL); + stats_tree_register("bacapp", "bacapp_instanceid", "BACnet/Packets sorted by Instance ID", 0, + bacapp_stats_tree_instanceid, bacapp_instanceid_stats_tree_init, NULL); +} + +/* 'data' must be allocated with wmem packet scope */ +static gint +updateBacnetInfoValue(gint whichval, const gchar *data) +{ + if (whichval == BACINFO_SERVICE) { + bacinfo.service_type = data; + return 0; + } + if (whichval == BACINFO_INVOKEID) { + bacinfo.invoke_id = data; + return 0; + } + if (whichval == BACINFO_OBJECTID) { + bacinfo.object_ident = data; + return 0; + } + if (whichval == BACINFO_INSTANCEID) { + bacinfo.instance_ident = data; + return 0; + } + return -1; +} + +static const fragment_items msg_frag_items = { + /* Fragment subtrees */ + &ett_msg_fragment, + &ett_msg_fragments, + /* Fragment fields */ + &hf_msg_fragments, + &hf_msg_fragment, + &hf_msg_fragment_overlap, + &hf_msg_fragment_overlap_conflicts, + &hf_msg_fragment_multiple_tails, + &hf_msg_fragment_too_long_fragment, + &hf_msg_fragment_error, + &hf_msg_fragment_count, + /* Reassembled in field */ + &hf_msg_reassembled_in, + /* Reassembled length field */ + &hf_msg_reassembled_length, + /* Reassembled data field */ + NULL, + /* Tag */ + "Message fragments" +}; + +#if 0 +/* if BACnet uses the reserved values, then patch the corresponding values here, maximum 16 values are defined */ +/* FIXME: fGetMaxAPDUSize is commented out, as it is not used. It was used to set variables which were not later used. */ +static const guint MaxAPDUSize [] = { 50, 128, 206, 480, 1024, 1476 }; + +static guint +fGetMaxAPDUSize(guint8 idx) +{ + /* only 16 values are defined, so use & 0x0f */ + /* check the size of the Array, deliver either the entry + or the first entry if idx is outside of the array (bug 3736 comment#7) */ + + if ((idx & 0x0f) >= (gint)(sizeof(MaxAPDUSize)/sizeof(guint))) + return MaxAPDUSize[0]; + else + return MaxAPDUSize[idx & 0x0f]; +} +#endif + +static const char* +val_to_split_str(guint32 val, guint32 split_val, const value_string *vs, + const char *fmt, const char *split_fmt) + G_GNUC_PRINTF(4, 0) + G_GNUC_PRINTF(5, 0); + +/* Used when there are ranges of reserved and proprietary enumerations */ +static const char* +val_to_split_str(guint32 val, guint32 split_val, const value_string *vs, + const char *fmt, const char *split_fmt) +{ + if (val < split_val) + return val_to_str(val, vs, fmt); + else + return val_to_str(val, vs, split_fmt); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns true if the extended value is used */ +static gboolean +tag_is_extended_value(guint8 tag) +{ + return (tag & 0x07) == 5; +} + +static gboolean +tag_is_opening(guint8 tag) +{ + return (tag & 0x07) == 6; +} + +static gboolean +tag_is_closing(guint8 tag) +{ + return (tag & 0x07) == 7; +} + +/* from clause 20.2.1.1 Class + class bit shall be one for context specific tags */ +/* returns true if the tag is context specific */ +static gboolean +tag_is_context_specific(guint8 tag) +{ + return (tag & 0x08) != 0; +} + +static gboolean +tag_is_extended_tag_number(guint8 tag) +{ + return ((tag & 0xF0) == 0xF0); +} + +static guint32 +object_id_type(guint32 object_identifier) +{ + return ((object_identifier >> 22) & 0x3FF); +} + +static guint32 +object_id_instance(guint32 object_identifier) +{ + return (object_identifier & 0x3FFFFF); +} + +static guint +fTagNo(tvbuff_t *tvb, guint offset) +{ + return (guint)(tvb_get_guint8(tvb, offset) >> 4); +} + +static gboolean +fUnsigned32(tvbuff_t *tvb, guint offset, guint32 lvt, guint32 *val) +{ + gboolean valid = TRUE; + + switch (lvt) { + case 1: + *val = tvb_get_guint8(tvb, offset); + break; + case 2: + *val = tvb_get_ntohs(tvb, offset); + break; + case 3: + *val = tvb_get_ntoh24(tvb, offset); + break; + case 4: + *val = tvb_get_ntohl(tvb, offset); + break; + default: + valid = FALSE; + break; + } + + return valid; +} + +static gboolean +fUnsigned64(tvbuff_t *tvb, guint offset, guint32 lvt, guint64 *val) +{ + gboolean valid = FALSE; + gint64 value = 0; + guint8 data, i; + + if (lvt && (lvt <= 8)) { + valid = TRUE; + for (i = 0; i < lvt; i++) { + data = tvb_get_guint8(tvb, offset+i); + value = (value << 8) + data; + } + *val = value; + } + + return valid; +} + +/* BACnet Signed Value uses 2's complement notation, but with a twist: + All signed integers shall be encoded in the smallest number of octets + possible. That is, the first octet of any multi-octet encoded value + shall not be X'00' if the most significant bit (bit 7) of the second + octet is 0, and the first octet shall not be X'FF' if the most + significant bit of the second octet is 1. ASHRAE-135-2004-20.2.5 */ +static gboolean +fSigned64(tvbuff_t *tvb, guint offset, guint32 lvt, gint64 *val) +{ + gboolean valid = FALSE; + gint64 value = 0; + guint8 data; + guint32 i; + + /* we can only handle 7 bytes for a 64-bit value due to signed-ness */ + if (lvt && (lvt <= 7)) { + valid = TRUE; + data = tvb_get_guint8(tvb, offset); + if ((data & 0x80) != 0) + value = (~G_GUINT64_CONSTANT(0) << 8) | data; + else + value = data; + for (i = 1; i < lvt; i++) { + data = tvb_get_guint8(tvb, offset+i); + value = ((guint64)value << 8) | data; + } + *val = value; + } + + return valid; +} + +static guint +fTagHeaderTree(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint offset, guint8 *tag_no, guint8* tag_info, guint32 *lvt) +{ + proto_item *ti = NULL; + guint8 tag; + guint8 value; + guint tag_len = 1; + guint lvt_len = 1; /* used for tree display of lvt */ + guint lvt_offset; /* used for tree display of lvt */ + + lvt_offset = offset; + tag = tvb_get_guint8(tvb, offset); + *tag_info = 0; + *lvt = tag & 0x07; + + /* To solve the problem of lvt values of 6/7 being indeterminate - it */ + /* can mean open/close tag or length of 6/7 after the length is */ + /* computed below - store whole tag info, not just context bit. */ + if (tag_is_context_specific(tag)) *tag_info = tag & 0x0F; + *tag_no = tag >> 4; + if (tag_is_extended_tag_number(tag)) { + *tag_no = tvb_get_guint8(tvb, offset + tag_len++); + } + if (tag_is_extended_value(tag)) { /* length is more than 4 Bytes */ + lvt_offset += tag_len; + value = tvb_get_guint8(tvb, lvt_offset); + tag_len++; + if (value == 254) { /* length is encoded with 16 Bits */ + *lvt = tvb_get_ntohs(tvb, lvt_offset+1); + tag_len += 2; + lvt_len += 2; + } else if (value == 255) { /* length is encoded with 32 Bits */ + *lvt = tvb_get_ntohl(tvb, lvt_offset+1); + tag_len += 4; + lvt_len += 4; + } else + *lvt = value; + } + + if (tree) { + proto_tree *subtree; + if (tag_is_opening(tag)) { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, tag_len, + ett_bacapp_tag, &ti, "{[%u]", *tag_no ); + } else if (tag_is_closing(tag)) { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, tag_len, + ett_bacapp_tag, &ti, "}[%u]", *tag_no ); + } else if (tag_is_context_specific(tag)) { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, tag_len, + ett_bacapp_tag, &ti, + "Context Tag: %u, Length/Value/Type: %u", *tag_no, *lvt); + } else { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, tag_len, + ett_bacapp_tag, &ti, + "Application Tag: %s, Length/Value/Type: %u", + val_to_str(*tag_no, BACnetApplicationTagNumber, + ASHRAE_Reserved_Fmt), + *lvt); + } + + /* details if needed */ + proto_tree_add_item(subtree, hf_BACnetTagClass, tvb, offset, 1, ENC_BIG_ENDIAN); + if (tag_is_extended_tag_number(tag)) { + proto_tree_add_uint_format(subtree, + hf_BACnetContextTagNumber, + tvb, offset, 1, tag, + "Extended Tag Number"); + proto_tree_add_item(subtree, + hf_BACnetExtendedTagNumber, + tvb, offset + 1, 1, ENC_BIG_ENDIAN); + } else { + if (tag_is_context_specific(tag)) + proto_tree_add_item(subtree, + hf_BACnetContextTagNumber, + tvb, offset, 1, ENC_BIG_ENDIAN); + else + proto_tree_add_item(subtree, + hf_BACnetApplicationTagNumber, + tvb, offset, 1, ENC_BIG_ENDIAN); + } + if (tag_is_closing(tag) || tag_is_opening(tag)) + proto_tree_add_item(subtree, + hf_BACnetNamedTag, + tvb, offset, 1, ENC_BIG_ENDIAN); + else if (tag_is_extended_value(tag)) { + proto_tree_add_item(subtree, + hf_BACnetNamedTag, + tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_uint(subtree, hf_bacapp_tag_lvt, + tvb, lvt_offset, lvt_len, *lvt); + } else + proto_tree_add_uint(subtree, hf_bacapp_tag_lvt, + tvb, lvt_offset, lvt_len, *lvt); + } /* if (tree) */ + + if (*lvt > tvb_reported_length(tvb)) { + expert_add_info_format(pinfo, ti, &ei_bacapp_bad_length, + "LVT length too long: %d > %d", *lvt, + tvb_reported_length(tvb)); + *lvt = 1; + } + + return tag_len; +} + +static guint +fTagHeader(tvbuff_t *tvb, packet_info *pinfo, guint offset, guint8 *tag_no, guint8* tag_info, + guint32 *lvt) +{ + return fTagHeaderTree(tvb, pinfo, NULL, offset, tag_no, tag_info, lvt); +} + +static guint +fNullTag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree; + + subtree = proto_tree_add_subtree_format(tree, tvb, offset, 1, ett_bacapp_tag, NULL, "%sNULL", label); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset + 1; +} + +static guint +fBooleanTag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt = 0; + proto_tree *subtree; + guint bool_len = 1; + + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_info && lvt == 1) { + lvt = tvb_get_guint8(tvb, offset+1); + ++bool_len; + } + + subtree = proto_tree_add_subtree_format(tree, tvb, offset, bool_len, + ett_bacapp_tag, NULL, "%s%s", label, lvt == 0 ? "FALSE" : "TRUE"); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset + bool_len; +} + +static guint +fUnsignedTag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint64 val = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* only support up to an 8 byte (64-bit) integer */ + if (fUnsigned64(tvb, offset + tag_len, lvt, &val)) + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "%s(Unsigned) %" PRIu64, label, val); + else + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "%s - %u octets (Unsigned)", label, lvt); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fDevice_Instance(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, int hf) +{ + guint8 tag_no, tag_info; + guint32 lvt, safe_lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + + if (lvt > 4) + safe_lvt = 4; + else + safe_lvt = lvt; + + ti = proto_tree_add_item(tree, hf, tvb, offset+tag_len, safe_lvt, ENC_BIG_ENDIAN); + + if (lvt != safe_lvt) + expert_add_info_format(pinfo, ti, &ei_bacapp_bad_length, + "This field claims to be an impossible %u bytes, while the max is %u", lvt, safe_lvt); + + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +/* set split_val to zero when not needed */ +static guint +fEnumeratedTagSplit(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint offset, const gchar *label, const value_string *vs, guint32 split_val) +{ + guint32 val = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* only support up to a 4 byte (32-bit) enumeration */ + if (fUnsigned32(tvb, offset+tag_len, lvt, &val)) { + if (vs) + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "%s %s (%u)", label, val_to_split_str(val, split_val, vs, + ASHRAE_Reserved_Fmt, Vendor_Proprietary_Fmt), val); + else + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "%s %u", label, val); + } else { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "%s - %u octets (enumeration)", label, lvt); + } + + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fEnumeratedTag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint offset, const gchar *label, const value_string *vs) +{ + return fEnumeratedTagSplit(tvb, pinfo, tree, offset, label, vs, 0); +} + +static guint +fSignedTag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + gint64 val = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (fSigned64(tvb, offset + tag_len, lvt, &val)) + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "%s(Signed) %" PRId64, label, val); + else + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "%s - %u octets (Signed)", label, lvt); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fRealTag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + gfloat f_val; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + f_val = tvb_get_ntohieee_float(tvb, offset+tag_len); + subtree = proto_tree_add_subtree_format(tree, tvb, offset, 4+tag_len, + ett_bacapp_tag, NULL, "%s%f (Real)", label, f_val); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+4; +} + +static guint +fDoubleTag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + gdouble d_val; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + d_val = tvb_get_ntohieee_double(tvb, offset+tag_len); + subtree = proto_tree_add_subtree_format(tree, tvb, offset, 8+tag_len, + ett_bacapp_tag, NULL, "%s%f (Double)", label, d_val); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+8; +} + +static guint +fProcessId(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint32 val = 0, lvt; + guint8 tag_no, tag_info; + proto_item *ti; + proto_tree *subtree; + guint tag_len; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32(tvb, offset+tag_len, lvt, &val)) + { + ti = proto_tree_add_uint(tree, hf_bacapp_tag_ProcessId, + tvb, offset, lvt+tag_len, val); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + } + else + { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "Process Identifier - %u octets (Signed)", lvt); + } + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset += tag_len + lvt; + + return offset; +} + +static guint +fPresentValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const value_string *vs, guint32 split_val, BacappPresentValueType type) +{ + // tag vars + guint32 lvt; + guint8 tag_no, tag_info; + guint tag_len; + guint curr_offset = offset; + // tree vars + proto_item *tree_item = NULL; + proto_tree *subtree = NULL; + // dissection vars + guint bool_len = 1; + guint64 unsigned_val = 0; + gint64 signed_val = 0; + gfloat float_val; + gdouble double_val; + guint32 enum_index = 0; + guint32 object_id; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + switch(type) { + case BACAPP_PRESENT_VALUE_NULL: + tree_item = proto_tree_add_string(tree, hf_bacapp_present_value_null, tvb, offset, lvt+tag_len, "NULL"); + curr_offset += 1; + break; + case BACAPP_PRESENT_VALUE_BOOL: + if (tag_info && lvt == 1) { + lvt = tvb_get_guint8(tvb, offset+1); + bool_len++; + } + tree_item = proto_tree_add_boolean(tree, hf_bacapp_present_value_bool, tvb, offset, bool_len, lvt); + curr_offset += bool_len; + break; + case BACAPP_PRESENT_VALUE_UNSIGNED: + if (fUnsigned64(tvb, offset + tag_len, lvt, &unsigned_val)) + tree_item = proto_tree_add_uint64(tree, hf_bacapp_present_value_unsigned, tvb, offset, lvt+tag_len, unsigned_val); + curr_offset += tag_len + lvt; + break; + case BACAPP_PRESENT_VALUE_SIGNED: + if (fSigned64(tvb, offset + tag_len, lvt, &signed_val)) + tree_item = proto_tree_add_int64(tree, hf_bacapp_present_value_signed, tvb, offset, lvt+tag_len, signed_val); + curr_offset += tag_len + lvt; + break; + case BACAPP_PRESENT_VALUE_REAL: + float_val = tvb_get_ntohieee_float(tvb, offset+tag_len); + double_val = (gdouble) float_val; + tree_item = proto_tree_add_double(tree, hf_bacapp_present_value_real, tvb, offset, lvt+tag_len, double_val); + curr_offset += tag_len + lvt; + break; + case BACAPP_PRESENT_VALUE_DOUBLE: + double_val = tvb_get_ntohieee_double(tvb, offset+tag_len); + tree_item = proto_tree_add_double(tree, hf_bacapp_present_value_double, tvb, offset, lvt+tag_len, double_val); + curr_offset += tag_len + lvt; + break; + case BACAPP_PRESENT_VALUE_OCTET_STRING: + if (lvt > 0) + tree_item = proto_tree_add_item(tree, hf_bacapp_present_value_octet_string, tvb, offset, lvt+tag_len, ENC_NA); + curr_offset += tag_len + lvt; + break; + case BACAPP_PRESENT_VALUE_CHARACTER_STRING: + curr_offset = fCharacterStringBase(tvb, pinfo, tree, offset, NULL, TRUE, FALSE); + break; + case BACAPP_PRESENT_VALUE_BIT_STRING: + curr_offset = fBitStringTagVSBase(tvb, pinfo, tree, offset, NULL, NULL, TRUE); + break; + case BACAPP_PRESENT_VALUE_ENUM: + if (fUnsigned32(tvb, offset+tag_len, lvt, &enum_index)) { + if (vs) { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, ett_bacapp_tag, NULL, + "Present Value (enum value): %s", + val_to_split_str(enum_index, + split_val, + vs, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt)); + proto_tree_add_uint(subtree, hf_bacapp_present_value_enum_index, tvb, offset, lvt+tag_len, enum_index); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } else { + tree_item = proto_tree_add_uint(tree, hf_bacapp_present_value_enum_index, tvb, offset, lvt+tag_len, enum_index); + } + } + curr_offset += tag_len + lvt; + break; + case BACAPP_PRESENT_VALUE_DATE: + curr_offset = fDate(tvb, pinfo, tree, offset, "Date: "); + break; + case BACAPP_PRESENT_VALUE_TIME: + curr_offset = fTime(tvb, pinfo, tree, offset, "Time: "); + break; + case BACAPP_PRESENT_VALUE_OBJECT_IDENTIFIER: + object_id = tvb_get_ntohl(tvb, offset+tag_len); + object_type = object_id_type(object_id); + subtree = proto_tree_add_subtree_format(tree, tvb, offset, tag_len + 4, ett_bacapp_tag, NULL, + "Present Value (enum value): %s", + val_to_split_str(object_type, + 128, + BACnetObjectType, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt)); + proto_tree_add_uint(subtree, hf_bacapp_present_value_enum_index, tvb, offset, lvt+tag_len, object_type); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + curr_offset += tag_len + lvt; + break; + default: + curr_offset += tag_len + lvt; + break; + } + + if (tree_item != NULL && subtree == NULL) { + subtree = proto_item_add_subtree(tree_item, ett_bacapp_value); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } + + return curr_offset; +} + +static guint +fEventType(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint32 val = 0, lvt; + guint8 tag_no, tag_info; + proto_item *ti; + proto_tree *subtree; + guint tag_len; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32(tvb, offset+tag_len, lvt, &val)) + { + ti = proto_tree_add_uint(tree, hf_bacapp_event_type, + tvb, offset, lvt+tag_len, val); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + } + else + { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "Event Type - %u octets (Signed)", lvt); + } + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset += tag_len + lvt; + + return offset; +} + +static guint +fNotifyType(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint32 val = 0, lvt; + guint8 tag_no, tag_info; + proto_item *ti; + proto_tree *subtree; + guint tag_len; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32(tvb, offset+tag_len, lvt, &val)) + { + ti = proto_tree_add_uint(tree, hf_bacapp_notify_type, + tvb, offset, lvt+tag_len, val); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + } + else + { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "Notify Type - %u octets (Signed)", lvt); + } + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset += tag_len + lvt; + + return offset; +} + +static guint +fToState(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint32 val = 0, lvt; + guint8 tag_no, tag_info; + proto_item *ti; + proto_tree *subtree; + guint tag_len; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32(tvb, offset+tag_len, lvt, &val)) + { + ti = proto_tree_add_uint(tree, hf_bacapp_tag_to_state, + tvb, offset, lvt+tag_len, val); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + } + else + { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "To State - %u octets (Signed)", lvt); + } + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset += tag_len + lvt; + + return offset; +} + +static guint +fFromState(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint32 val = 0, lvt; + guint8 tag_no, tag_info; + proto_item *ti; + proto_tree *subtree; + guint tag_len; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32(tvb, offset+tag_len, lvt, &val)) + { + ti = proto_tree_add_uint(tree, hf_bacapp_tag_from_state, + tvb, offset, lvt+tag_len, val); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + } + else + { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "From State - %u octets (Signed)", lvt); + } + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset += tag_len + lvt; + + return offset; +} + +static guint +fTimeSpan(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint32 val = 0, lvt; + guint8 tag_no, tag_info; + proto_tree *subtree; + guint tag_len; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32(tvb, offset+tag_len, lvt, &val)) + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, + "%s (hh.mm.ss): %d.%02d.%02d%s", + label, + (val / 3600), ((val % 3600) / 60), (val % 60), + val == 0 ? " (indefinite)" : ""); + else + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, + "%s - %u octets (Signed)", label, lvt); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fWeekNDay(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint32 month, weekOfMonth, dayOfWeek; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + month = tvb_get_guint8(tvb, offset+tag_len); + weekOfMonth = tvb_get_guint8(tvb, offset+tag_len+1); + dayOfWeek = tvb_get_guint8(tvb, offset+tag_len+2); + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "%s %s, %s", + val_to_str(month, months, "month (%d) not found"), + val_to_str(weekOfMonth, weekofmonth, "week of month (%d) not found"), + val_to_str(dayOfWeek, day_of_week, "day of week (%d) not found")); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fDate(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint32 year, month, day, weekday; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + year = tvb_get_guint8(tvb, offset+tag_len); + month = tvb_get_guint8(tvb, offset+tag_len+1); + day = tvb_get_guint8(tvb, offset+tag_len+2); + weekday = tvb_get_guint8(tvb, offset+tag_len+3); + if ((year == 255) && (day == 255) && (month == 255) && (weekday == 255)) { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, + "%sany", label); + } + else if (year != 255) { + year += 1900; + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, + "%s%s %d, %d, (Day of Week = %s)", + label, val_to_str(month, + months, + "month (%d) not found"), + day, year, val_to_str(weekday, + day_of_week, + "(%d) not found")); + } else { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, + "%s%s %d, any year, (Day of Week = %s)", + label, val_to_str(month, months, "month (%d) not found"), + day, val_to_str(weekday, day_of_week, "(%d) not found")); + } + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fTime(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint32 hour, minute, second, msec, lvt; + guint8 tag_no, tag_info; + guint tag_len; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + hour = tvb_get_guint8(tvb, offset+tag_len); + minute = tvb_get_guint8(tvb, offset+tag_len+1); + second = tvb_get_guint8(tvb, offset+tag_len+2); + msec = tvb_get_guint8(tvb, offset+tag_len+3); + if ((hour == 255) && (minute == 255) && (second == 255) && (msec == 255)) + subtree = proto_tree_add_subtree_format(tree, tvb, offset, + lvt+tag_len, ett_bacapp_tag, NULL, + "%sany", label); + else + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, + "%s%d:%02d:%02d.%d %s = %02d:%02d:%02d.%d", + label, + hour > 12 ? hour - 12 : hour, + minute, second, msec, + hour >= 12 ? "P.M." : "A.M.", + hour, minute, second, msec); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fDateTime(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + proto_tree *subtree = tree; + + if (label != NULL) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 10, ett_bacapp_value, NULL, label); + } + offset = fDate(tvb, pinfo, subtree, offset, "Date: "); + return fTime(tvb, pinfo, subtree, offset, "Time: "); +} + +static guint +fTimeValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* closing Tag, but not for me */ + return offset; + } + offset = fTime(tvb, pinfo, tree, offset, "Time: "); + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Value: "); + + if (offset <= lastoffset) break; /* exit loop if nothing happens inside */ + } + return offset; +} + +static guint +fCalendarEntry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + + switch (fTagNo(tvb, offset)) { + case 0: /* Date */ + offset = fDate(tvb, pinfo, tree, offset, "Date: "); + break; + case 1: /* dateRange */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateRange(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* BACnetWeekNDay */ + offset = fWeekNDay(tvb, pinfo, tree, offset); + break; + default: + return offset; + } + + return offset; +} + +static guint +fEventTimeStamps( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint32 lvt = 0; + proto_tree* subtree = tree; + + if (tvb_reported_length_remaining(tvb, offset) > 0) { + subtree = proto_tree_add_subtree(tree, tvb, offset, lvt, ett_bacapp_tag, NULL, "eventTimeStamps"); + + offset = fTimeStamp(tvb, pinfo, subtree, offset, "TO-OFFNORMAL timestamp: "); + offset = fTimeStamp(tvb, pinfo, subtree, offset, "TO-FAULT timestamp: "); + offset = fTimeStamp(tvb, pinfo, subtree, offset, "TO-NORMAL timestamp: "); + } + return offset; +} + +static guint +fTimeStamp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + + if (tvb_reported_length_remaining(tvb, offset) > 0) { /* don't loop, it's a CHOICE */ + switch (fTagNo(tvb, offset)) { + case 0: /* time */ + offset = fTime(tvb, pinfo, tree, offset, label?label:"time: "); + break; + case 1: /* sequenceNumber */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, + label?label:"sequence number: "); + break; + case 2: /* dateTime */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, tree, offset, label?label:"date time: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + } + + return offset; +} + + +static guint +fClientCOV(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + if (tvb_reported_length_remaining(tvb, offset) > 0) { + offset = fApplicationTypes(tvb, pinfo, tree, offset, "increment: "); + } + return offset; +} + +static const value_string +BACnetDaysOfWeek [] = { + { 0, "Monday" }, + { 1, "Tuesday" }, + { 2, "Wednesday" }, + { 3, "Thursday" }, + { 4, "Friday" }, + { 5, "Saturday" }, + { 6, "Sunday" }, + { 0, NULL } +}; + +static guint +fDestination(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + if (tvb_reported_length_remaining(tvb, offset) > 0) { + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, + "valid Days: ", BACnetDaysOfWeek); + offset = fTime(tvb, pinfo, tree, offset, "from time: "); + offset = fTime(tvb, pinfo, tree, offset, "to time: "); + offset = fRecipient(tvb, pinfo, tree, offset); + offset = fProcessId(tvb, pinfo, tree, offset); + offset = fApplicationTypes(tvb, pinfo, tree, offset, + "issue confirmed notifications: "); + offset = fBitStringTagVS(tvb, pinfo, tree, offset, + "transitions: ", BACnetEventTransitionBits); + } + return offset; +} + + +static guint +fOctetString(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label, guint32 lvt) +{ + gchar *tmp; + guint start = offset; + guint8 tag_no, tag_info; + proto_tree *subtree = tree; + + offset += fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + + if (lvt > 0) { + tmp = tvb_bytes_to_str(pinfo->pool, tvb, offset, lvt); + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt, + ett_bacapp_tag, NULL, "%s %s", label, tmp); + offset += lvt; + } + + fTagHeaderTree(tvb, pinfo, subtree, start, &tag_no, &tag_info, &lvt); + + return offset; +} + +static guint +fMacAddress(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label, guint32 lvt) +{ + guint start = offset; + guint8 tag_no, tag_info; + proto_tree* subtree = tree; + + offset += fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + + /* just add the label, with the tagHeader information in its subtree */ + subtree = proto_tree_add_subtree(tree, tvb, offset, lvt, ett_bacapp_tag, NULL, label); + + if (lvt == 6) { /* we have 6 Byte IP Address with 4 Octets IPv4 and 2 Octets Port Information */ + proto_tree_add_item(tree, hf_bacapp_tag_IPV4, tvb, offset, 4, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_bacapp_tag_PORT, tvb, offset+4, 2, ENC_BIG_ENDIAN); + } else if (lvt == 18) { /* we have 18 Byte IP Address with 16 Octets IPv6 and 2 Octets Port Information */ + proto_tree_add_item(tree, hf_bacapp_tag_IPV6, tvb, offset, 16, ENC_NA); + proto_tree_add_item(tree, hf_bacapp_tag_PORT, tvb, offset+16, 2, ENC_BIG_ENDIAN); + } else { /* we have 1 Byte MS/TP Address or anything else interpreted as an address */ + subtree = proto_tree_add_subtree(tree, tvb, offset, lvt, + ett_bacapp_tag, NULL, tvb_bytes_to_str(pinfo->pool, tvb, offset, lvt)); + } + offset += lvt; + + fTagHeaderTree(tvb, pinfo, subtree, start, &tag_no, &tag_info, &lvt); + + return offset; +} + +static guint +fAddress(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint offs; + + offset = fUnsignedTag(tvb, pinfo, tree, offset, "network-number"); + offs = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (lvt == 0) { + proto_tree_add_item(tree, hf_bacapp_tag_mac_address_broadcast, tvb, offset, offs, ENC_NA); + offset += offs; + } else + offset = fMacAddress(tvb, pinfo, tree, offset, "MAC-address: ", lvt); + + return offset; +} + +static guint +fSessionKey(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + offset = fOctetString(tvb, pinfo, tree, offset, "session key: ", 8); + return fAddress(tvb, pinfo, tree, offset); +} + +static guint +fObjectIdentifier(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_length; + proto_tree *subtree; + guint32 object_id; + + tag_length = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + object_id = tvb_get_ntohl(tvb, offset+tag_length); + object_type = object_id_type(object_id); + subtree = proto_tree_add_subtree_format(tree, tvb, offset, tag_length + 4, + ett_bacapp_tag, NULL, "%s%s, %u", label, + val_to_split_str(object_type, + 128, + BACnetObjectType, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt), + object_id_instance(object_id)); + + col_append_fstr(pinfo->cinfo, COL_INFO, "%s,%u ", + val_to_split_str(object_type, + 128, + BACnetObjectType, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt), + object_id_instance(object_id)); + + /* update BACnet Statistics */ + updateBacnetInfoValue(BACINFO_OBJECTID, + wmem_strdup(pinfo->pool, + val_to_split_str(object_type, 128, + BACnetObjectType, ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt))); + updateBacnetInfoValue(BACINFO_INSTANCEID, + wmem_strdup_printf(pinfo->pool, + "Instance ID: %u", + object_id_instance(object_id))); + + /* here are the details of how we arrived at the above text */ + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset += tag_length; + proto_tree_add_item(subtree, hf_bacapp_objectType, tvb, offset, 4, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_bacapp_instanceNumber, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + + return offset; +} + +static guint +fObjectName(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fCharacterStringBase(tvb, pinfo, tree, offset, "Object Name", FALSE, TRUE); +} + +static guint +fRecipient(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_no < 2) { + if (tag_no == 0) { /* device */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "DeviceIdentifier: "); + } + else { /* address */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAddress(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } + } + return offset; +} + +static guint +fRecipientProcess(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *orgtree = tree; + proto_tree *subtree; + + /* beginning of new item - indent and label */ + tree = proto_tree_add_subtree(orgtree, tvb, offset, 1, ett_bacapp_value, NULL, "Recipient Process" ); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* recipient */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); /* show context open */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "Recipient"); /* add tree label and indent */ + offset = fRecipient(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); /* show context close */ + break; + case 1: /* processId */ + offset = fProcessId(tvb, pinfo, tree, offset); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fCOVSubscription(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree; + proto_tree *orgtree = tree; + guint itemno = 1; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info) ) { + return offset; + } + switch (tag_no) { + + case 0: /* recipient */ + /* beginning of new item in list */ + tree = proto_tree_add_subtree_format(orgtree, tvb, offset, 1, + ett_bacapp_value, NULL, "Subscription %d",itemno); /* add tree label and indent */ + itemno = itemno + 1; + + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, + ett_bacapp_value, NULL, "Recipient"); /* add tree label and indent */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context open */ + offset = fRecipientProcess(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context close */ + break; + case 1: /* MonitoredPropertyReference */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, + ett_bacapp_value, NULL, "Monitored Property Reference"); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetObjectPropertyReference(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* IssueConfirmedNotifications - boolean */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "Issue Confirmed Notifications: "); + break; + case 3: /* TimeRemaining */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "Time Remaining: "); + break; + case 4: /* COVIncrement */ + offset = fRealTag(tvb, pinfo, tree, offset, "COV Increment: "); + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fAddressBinding(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "DeviceIdentifier: "); + return fAddress(tvb, pinfo, tree, offset); +} + +static guint +fActionCommand(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint8 tag_match) +{ + guint lastoffset = 0, len; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + /* set the optional global properties to indicate not-used */ + propertyArrayIndex = -1; + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info) ) { + if (tag_no == tag_match) { + return offset; + } + offset += len; + subtree = tree; + continue; + } + switch (tag_no) { + + case 0: /* deviceIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "DeviceIdentifier: "); + break; + case 1: /* objectIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + case 2: /* propertyIdentifier */ + offset = fPropertyIdentifier(tvb, pinfo, subtree, offset); + break; + case 3: /* propertyArrayIndex */ + offset = fPropertyArrayIndex(tvb, pinfo, subtree, offset); + break; + case 4: /* propertyValue */ + offset = fPropertyValue(tvb, pinfo, subtree, offset, tag_info); + break; + case 5: /* priority */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "Priority: "); + break; + case 6: /* postDelay */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "Post Delay: "); + break; + case 7: /* quitOnFailure */ + offset = fBooleanTag(tvb, pinfo, subtree, offset, + "Quit On Failure: "); + break; + case 8: /* writeSuccessful */ + offset = fBooleanTag(tvb, pinfo, subtree, offset, + "Write Successful: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +/* BACnetActionList ::= SEQUENCE{ + action [0] SEQUENCE OF BACnetActionCommand + } +*/ +static guint +fActionList(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0, len; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + if ( tag_no != 0 ) /* don't eat the closing property tag, just return */ + return offset; + /* print closing tag of action list too */ + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + subtree = tree; + offset += len; + continue; + } + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_tag, NULL, "Action List"); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, + &tag_no, &tag_info, &lvt); + } + switch (tag_no) { + case 0: /* BACnetActionCommand */ + offset = fActionCommand(tvb, pinfo, subtree, offset, tag_no); + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fPropertyAccessResult(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint32 save_object_type; + guint32 save_inner_object_type; + gint32 save_propertyIdentifier; + + /* save the external entry data because it might get overwritten here */ + save_object_type = object_type; + save_propertyIdentifier = propertyIdentifier; + + /* inner object type might get overwritten by device id */ + save_inner_object_type = object_type; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + /* save the local object type because device id might overwrite it */ + save_inner_object_type = object_type; + break; + case 1: /* propertyIdentifier */ + offset = fPropertyIdentifier(tvb, pinfo, tree, offset); + break; + case 2: /* propertyArrayIndex */ + offset = fPropertyArrayIndex(tvb, pinfo, tree, offset); + break; + case 3: /* deviceIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "DeviceIdentifier: "); + /* restore the inner object type to decode the right property value */ + object_type = save_inner_object_type; + break; + case 4: /* propertyValue */ + offset = fPropertyValue(tvb, pinfo, tree, offset, tag_info); + /* restore the external values for next loop */ + object_type = save_object_type; + propertyIdentifier = save_propertyIdentifier; + break; + case 5: /* propertyAccessError */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fError(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } + else { + expert_add_info(pinfo, tree, &ei_bacapp_bad_tag); + } + /* restore the external values for next loop */ + object_type = save_object_type; + propertyIdentifier = save_propertyIdentifier; + break; + default: + break; + } + + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + /* restore the external values for next decoding */ + object_type = save_object_type; + propertyIdentifier = save_propertyIdentifier; + return offset; +} + +static guint +fPropertyIdentifier(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_tree *subtree; + const gchar *label = "Property Identifier"; + + propertyIdentifier = 0; /* global Variable */ + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* can we decode this value? */ + if (fUnsigned32(tvb, offset+tag_len, lvt, (guint32 *)&propertyIdentifier)) { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, + "%s: %s (%u)", label, + val_to_split_str(propertyIdentifier, 512, + BACnetPropertyIdentifier, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt), propertyIdentifier); + col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", + val_to_split_str(propertyIdentifier, 512, + BACnetPropertyIdentifier, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt)); + } else { + /* property identifiers cannot be larger than 22-bits */ + return offset; + } + + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + proto_tree_add_item(subtree, hf_BACnetPropertyIdentifier, tvb, + offset+tag_len, lvt, ENC_BIG_ENDIAN); + + return offset+tag_len+lvt; +} + +static guint +fPropertyArrayIndex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_tree *subtree; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32(tvb, offset + tag_len, lvt, (guint32 *)&propertyArrayIndex)) + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "property Array Index (Unsigned) %u", propertyArrayIndex); + else + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "property Array Index - %u octets (Unsigned)", lvt); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset+tag_len+lvt; +} + +static guint +fChannelValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + + if (tvb_reported_length_remaining(tvb, offset) > 0) { + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_opening(tag_info) && tag_no == 0) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fLightingCommand(tvb, pinfo, tree, offset, "lighting-command: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } else if (tag_is_opening(tag_info) && tag_no == 1) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fXyColor(tvb, pinfo, tree, offset, "xy-color: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } else if (tag_is_opening(tag_info) && tag_no == 2) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fColorCommand(tvb, pinfo, tree, offset, "color-command: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } else { + if (tag_info) { + offset = fContextTaggedValue(tvb, pinfo, tree, offset, label); + } else { + offset = fApplicationTypes(tvb, pinfo, tree, offset, label); + } + } + } + + return offset; +} + +static guint +fCharacterString(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + return fCharacterStringBase(tvb, pinfo, tree, offset, label, FALSE, FALSE); +} + +static guint +fCharacterStringBase(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label, gboolean present_val_dissect, gboolean object_name_dissect) +{ + guint8 tag_no, tag_info, character_set; + guint32 lvt, l; + guint offs; + const char *coding; + guint8 *out; + proto_tree *subtree; + guint start = offset; + + if (tvb_reported_length_remaining(tvb, offset) > 0) { + + offs = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + offset += offs; + + character_set = tvb_get_guint8(tvb, offset); + offset++; + lvt--; + + /* Account for code page if DBCS */ + if (character_set == IBM_MS_DBCS) { + offset += 2; + lvt -= 2; + } + + do { + l = MIN(lvt, 256); + /* + * XXX - are we guaranteed that these encoding + * names correspond, on *all* platforms with + * iconv(), to the encodings we want? + * + * Not necessarily. These specify "character sets" but + * not the encodings. IBM/MS DBCS specifies that it uses + * some IBM or MS double byte character set, but does not + * specify the code page - there was a proposal to explicitly + * add the code page, but that was apparently withdrawn in favor + * of just deprecating using DBCS, as it never got past a draft + * (One problem could be that IBM and MS code pages with the + * same number are slightly different, and then there's non + * IBM/MS unofficial ones that got used, sometimes conflicting + * numbers.) Even if we assume that they certainly mean one + * of the DBCS and not just any non ISO-8859-1 code page, there's + * all four types of CJK to choose from. - + * http://www.bacnet.org/Addenda/Add-135-2004k-PPR1-chair-approved.pdf + * JIS C 6226 (now JIS X 0208) + * http://www.bacnet.org/Addenda/Add-135-2008k.pdf + * is a character set, which are supported by several different + * encodings, the main types being ISO-2022-JP (JIS X 0202, + * a 7 bit encoding), Shift-JIS (most common), and EUC-JP (UNIX). + * It is unclear which encoding this refers to. + * + * If not (and perhaps even if so), we should + * perhaps have our own iconv() implementation, + * with a different name, so that we control the + * encodings it supports and the names of those + * encodings. + * + * We should also handle that in the general + * string handling code, rather than making it + * specific to the BACAPP dissector, as many + * other dissectors need to handle various + * character encodings. + */ + /** this decoding may be not correct for multi-byte characters, Lka */ + switch (character_set) { + case ANSI_X3_4: + out = tvb_get_string_enc(pinfo->pool, tvb, offset, l, ENC_UTF_8); + coding = "UTF-8"; + break; + case IBM_MS_DBCS: + out = tvb_get_string_enc(pinfo->pool, tvb, offset, l, ENC_ASCII); + coding = "IBM MS DBCS"; + break; + case JIS_C_6226: + out = tvb_get_string_enc(pinfo->pool, tvb, offset, l, ENC_ASCII); + coding = "JIS C 6226"; + break; + case ISO_10646_UCS4: + out = tvb_get_string_enc(pinfo->pool, tvb, offset, l, ENC_UCS_4|ENC_BIG_ENDIAN); + coding = "ISO 10646 UCS-4"; + break; + case ISO_10646_UCS2: + out = tvb_get_string_enc(pinfo->pool, tvb, offset, l, ENC_UCS_2|ENC_BIG_ENDIAN); + coding = "ISO 10646 UCS-2"; + break; + case ISO_8859_1: + out = tvb_get_string_enc(pinfo->pool, tvb, offset, l, ENC_ISO_8859_1); + coding = "ISO 8859-1"; + break; + default: + /* Assume this is some form of extended ASCII, with one-byte code points for ASCII characters */ + out = tvb_get_string_enc(pinfo->pool, tvb, offset, l, ENC_ASCII); + coding = "unknown"; + break; + } + + if (present_val_dissect) { + subtree = proto_tree_add_subtree(tree, tvb, offset, l, ett_bacapp_tag, NULL, "present-value"); + proto_tree_add_string(subtree, hf_bacapp_present_value_char_string, tvb, offset, l, (const gchar*) out); + } else if (object_name_dissect) { + subtree = proto_tree_add_subtree(tree, tvb, offset, l, ett_bacapp_tag, NULL, label); + proto_tree_add_string(subtree, hf_bacapp_object_name, tvb, offset, l, (const gchar*) out); + } else { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, l, ett_bacapp_tag, NULL, + "%s%s '%s'", label, coding, out); + } + + lvt -= l; + offset += l; + } while (lvt > 0); + + fTagHeaderTree(tvb, pinfo, subtree, start, &tag_no, &tag_info, &lvt); + proto_tree_add_item(subtree, hf_BACnetCharacterSet, tvb, start+offs, 1, ENC_BIG_ENDIAN); + + if (character_set == IBM_MS_DBCS) { + proto_tree_add_item(subtree, hf_BACnetCodePage, tvb, start+offs+1, 2, ENC_BIG_ENDIAN); + } + /* XXX - put the string value here */ + } + return offset; +} + +static guint +fBitStringTagVS(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label, + const value_string *src) +{ + return fBitStringTagVSBase(tvb, pinfo, tree, offset, label, src, FALSE); +} + +static guint +fBitStringTagVSBase(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label, + const value_string *src, gboolean present_val_dissect) +{ + guint8 tag_no, tag_info, tmp; + gint j, unused, skip; + guint start = offset; + guint offs; + guint32 lvt, i, numberOfBytes; + char bf_arr[256 + 1]; + proto_tree *subtree = tree; + + offs = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + numberOfBytes = lvt-1; /* Ignore byte for unused bit count */ + offset += offs; + unused = tvb_get_guint8(tvb, offset); /* get the unused Bits */ + + memset(bf_arr, 0, sizeof(bf_arr)); + skip = 0; + for (i = 0; i < numberOfBytes; i++) { + tmp = tvb_get_guint8(tvb, (offset)+i + 1); + if (i == numberOfBytes - 1) { skip = unused; } + for (j = 0; j < 8 - skip; j++) { + bf_arr[MIN(sizeof(bf_arr) - 2, (i * 8) + j)] = tmp & (1 << (7 - j)) ? 'T' : 'F'; + } + } + + if (!present_val_dissect) { + subtree = proto_tree_add_subtree_format(tree, tvb, start, offs+lvt, + ett_bacapp_tag, NULL, + "%s(Bit String) (%s)", label, bf_arr); + } else { + subtree = proto_tree_add_subtree_format(tree, tvb, start, offs+lvt, + ett_bacapp_tag, NULL, + "present-value (%s)", bf_arr); + } + + fTagHeaderTree(tvb, pinfo, subtree, start, &tag_no, &tag_info, &lvt); + proto_tree_add_item(subtree, hf_bacapp_unused_bits, tvb, offset, 1, ENC_NA); + memset(bf_arr, 0, sizeof(bf_arr)); + skip = 0; + for (i = 0; i < numberOfBytes; i++) { + tmp = tvb_get_guint8(tvb, (offset)+i+1); + if (i == numberOfBytes-1) { skip = unused; } + for (j = 0; j < 8-skip; j++) { + if (src != NULL) { + proto_tree_add_boolean_format(subtree, hf_bacapp_bit, tvb, offset+i+1, 1, + (tmp & (1 << (7 - j))), "%s = %s", + val_to_str((guint) (i*8 +j), src, ASHRAE_Reserved_Fmt), + (tmp & (1 << (7 - j))) ? "TRUE" : "FALSE"); + } else { + bf_arr[MIN(255, (i*8)+j)] = tmp & (1 << (7 - j)) ? '1' : '0'; + } + } + } + + if (src == NULL) { + bf_arr[MIN(255, numberOfBytes*8-unused)] = 0; + proto_tree_add_bytes_format(subtree, hf_bacapp_complete_bitstring, tvb, offset, lvt, NULL, "B'%s'", bf_arr); + } + + offset += lvt; + + return offset; +} + +static guint +fBitStringTag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + return fBitStringTagVS(tvb, pinfo, tree, offset, label, NULL); +} + +/* handles generic application types, as well as enumerated and enumerations + with reserved and proprietarty ranges (split) */ +static guint +fApplicationTypesEnumeratedSplit(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, + const gchar *label, const value_string *src, guint32 split_val) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + + if (tvb_reported_length_remaining(tvb, offset) > 0) { + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (!tag_is_context_specific(tag_info)) { + switch (tag_no) { + case 0: /** NULL 20.2.2 */ + offset = fNullTag(tvb, pinfo, tree, offset, label); + break; + case 1: /** BOOLEAN 20.2.3 */ + offset = fBooleanTag(tvb, pinfo, tree, offset, label); + break; + case 2: /** Unsigned Integer 20.2.4 */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, label); + break; + case 3: /** Signed Integer 20.2.5 */ + offset = fSignedTag(tvb, pinfo, tree, offset, label); + break; + case 4: /** Real 20.2.6 */ + offset = fRealTag(tvb, pinfo, tree, offset, label); + break; + case 5: /** Double 20.2.7 */ + offset = fDoubleTag(tvb, pinfo, tree, offset, label); + break; + case 6: /** Octet String 20.2.8 */ + offset = fOctetString(tvb, pinfo, tree, offset, label, lvt); + break; + case 7: /** Character String 20.2.9 */ + offset = fCharacterString(tvb, pinfo, tree, offset, label); + break; + case 8: /** Bit String 20.2.10 */ + offset = fBitStringTagVS(tvb, pinfo, tree, offset, label, src); + break; + case 9: /** Enumerated 20.2.11 */ + offset = fEnumeratedTagSplit(tvb, pinfo, tree, offset, label, src, split_val); + break; + case 10: /** Date 20.2.12 */ + offset = fDate(tvb, pinfo, tree, offset, label); + break; + case 11: /** Time 20.2.13 */ + offset = fTime(tvb, pinfo, tree, offset, label); + break; + case 12: /** BACnetObjectIdentifier 20.2.14 */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 13: /* reserved for ASHRAE */ + case 14: + case 15: + proto_tree_add_bytes_format(tree, hf_bacapp_reserved_ashrea, tvb, offset, lvt+tag_len, NULL, "%s'reserved for ASHRAE'", label); + offset += lvt + tag_len; + break; + default: + break; + } + } + } + return offset; +} + +static guint +fShedLevel(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* percent */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "shed percent: "); + break; + case 1: /* level */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "shed level: "); + break; + case 2: /* amount */ + offset = fRealTag(tvb, pinfo, tree, offset, "shed amount: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fApplicationTypesEnumerated(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, + const gchar *label, const value_string *vs) +{ + return fApplicationTypesEnumeratedSplit(tvb, pinfo, tree, offset, label, vs, 0); +} + +static guint +fApplicationTypes(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, + const gchar *label) +{ + return fApplicationTypesEnumeratedSplit(tvb, pinfo, tree, offset, label, NULL, 0); +} + +static guint +fContextTaggedValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_tree *subtree; + gint tvb_len; + + (void)label; + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* cap the the suggested length in case of bad data */ + tvb_len = tvb_reported_length_remaining(tvb, offset+tag_len); + if ((tvb_len >= 0) && ((guint32)tvb_len < lvt)) { + lvt = tvb_len; + } + subtree = proto_tree_add_subtree_format(tree, tvb, offset+tag_len, lvt, + ett_bacapp_tag, NULL, "Context Value (as %u DATA octets)", lvt); + + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset + tag_len + lvt; +} +/* +BACnetPrescale ::= SEQUENCE { + multiplier [0] Unsigned, +moduloDivide [1] Unsigned +} +*/ +static guint +fPrescale(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info) ) { + return offset; + } + switch (tag_no) { + case 0: /* multiplier */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "Multiplier: "); + break; + case 1: /* moduloDivide */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "Modulo Divide: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; + +} +/* +BACnetScale ::= CHOICE { + floatScale [0] REAL, +integerScale [1] INTEGER +} +*/ +static guint +fScale(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info) ) { + return offset; + } + switch (tag_no) { + case 0: /* floatScale */ + offset = fRealTag(tvb, pinfo, tree, offset, "Float Scale: "); + break; + case 1: /* integerScale */ + offset = fSignedTag(tvb, pinfo, tree, offset, "Integer Scale: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} +/* +BACnetAccumulatorRecord ::= SEQUENCE { + timestamp [0] BACnetDateTime, + presentValue [1] Unsigned, + accumulatedValue [2] Unsigned, + accumulatortStatus [3] ENUMERATED { + normal (0), + starting (1), + recovered (2), + abnormal (3), + failed (4) + } +} +*/ +static guint +fLoggingRecord(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info) ) { + return offset; + } + switch (tag_no) { + case 0: /* timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, tree, offset, "Timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* presentValue */ + offset = fPresentValue(tvb, pinfo, tree, offset, NULL, 0, BACAPP_PRESENT_VALUE_UNSIGNED); + break; + case 2: /* accumulatedValue */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "Accumulated Value: "); + break; + case 3: /* accumulatorStatus */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "Accumulator Status: ", BACnetAccumulatorStatus); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +/* + SEQ OF Any enumeration (current usage is SEQ OF BACnetDoorAlarmState +*/ +static guint +fSequenceOfEnums(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label, const value_string *vs) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info) ) { + return offset; + } + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, label, vs); + if ( offset <= lastoffset ) break; + } + return offset; +} + +/* +SEQ OF BACnetDeviceObjectReference (accessed as an array) +} +*/ +static guint +fDoorMembers(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info) ) { + return offset; + } + offset = fDeviceObjectReference(tvb, pinfo, tree, offset); + if (offset <= lastoffset) break; + } + return offset; +} + +/* +SEQ OF ReadAccessSpecification +*/ +static guint +fListOfGroupMembers(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info) ) { + return offset; + } + offset = fReadAccessSpecification(tvb, pinfo, tree, offset); + if ( offset <= lastoffset ) break; + } + return offset; +} + +static guint +fAbstractSyntaxNType(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0, depth = 0; + char ar[256]; + guint32 save_object_type; + gboolean do_default_handling; + + if (propertyIdentifier >= 0) { + snprintf(ar, sizeof(ar), "%s: ", + val_to_split_str(propertyIdentifier, 512, + BACnetPropertyIdentifier, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt)); + } else { + snprintf(ar, sizeof(ar), "Abstract Type: "); + } + + unsigned recursion_depth = p_get_proto_depth(pinfo, proto_bacapp); + if (++recursion_depth >= BACAPP_MAX_RECURSION_DEPTH) { + proto_tree_add_expert(tree, pinfo, &ei_bacapp_max_recursion_depth_reached, tvb, 0, 0); + return offset; + } + p_set_proto_depth(pinfo, proto_bacapp, recursion_depth); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* closing tag, but not for me */ + if (depth <= 0) { + goto cleanup; + } + } + + do_default_handling = FALSE; + + /* Application Tags */ + switch (propertyIdentifier) { + case 0: /* acked-transitions */ + case 35: /* event-enable */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, + BACnetAcknowledgedTransitions); + break; + case 2: /* action */ + /* loop object is application tagged, + command object is context tagged */ + if (tag_is_context_specific(tag_info)) { + /* BACnetActionList */ + offset = fActionList(tvb, pinfo, tree, offset); + } else { + /* BACnetAction */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, + BACnetAction); + } + break; + case 7: /* alarm-values*/ + switch (object_type) { + case 21: /* life-point */ + case 22: /* life-zone */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLifeSafetyState); + break; + case 30: /* access-door */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetDoorAlarmState); + break; + case 31: /* timer */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetTimerState); + break; + case 36: /* access-zone */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetAccessZoneOccupancyState); + break; + case 39: /* bitstring-value */ + default: + if (tag_info) { + if (tag_is_opening(tag_info)) { + ++depth; + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } + else if (tag_is_closing(tag_info)) { + --depth; + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } + else { + offset = fContextTaggedValue(tvb, pinfo, tree, offset, ar); + } + } + else { + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } + break; + } + break; + case 37: /* event-type */ + offset = fEventType(tvb, pinfo, tree, offset); + break; + case 39: /* fault-values */ + switch (object_type) { + case 21: /* life-point */ + case 22: /* life-zone */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLifeSafetyState); + break; + case 30: /* access-door */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetDoorAlarmState); + break; + case 31: /* timer */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetTimerState); + break; + case 36: /* access-zone */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetAccessZoneOccupancyState); + break; + case 39: /* bitstring-value */ + default: + if (tag_info) { + if (tag_is_opening(tag_info)) { + ++depth; + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } + else if (tag_is_closing(tag_info)) { + --depth; + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } + else { + offset = fContextTaggedValue(tvb, pinfo, tree, offset, ar); + } + } + else { + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } + break; + } + break; + case 30: /* BACnetAddressBinding */ + case 331: /* last-key-server */ + offset = fAddressBinding(tvb, pinfo, tree, offset); + break; + case 52: /* limit-enable */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLimitEnable); + break; + case 54: /* list of object property reference */ + offset = fLOPR(tvb, pinfo, tree, offset); + break; + case 55: /* list-of-session-keys */ + fSessionKey(tvb, pinfo, tree, offset); + break; + case 77: /* object-name */ + offset = fObjectName(tvb, pinfo, tree, offset); + break; + case 79: /* object-type */ + case 96: /* protocol-object-types-supported */ + offset = fApplicationTypesEnumeratedSplit(tvb, pinfo, tree, offset, ar, + BACnetObjectType, 128); + break; + case 97: /* Protocol-Services-Supported */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, + BACnetServicesSupported); + break; + case 102: /* recipient-list */ + offset = fDestination(tvb, pinfo, tree, offset); + break; + case 107: /* segmentation-supported */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, + BACnetSegmentation); + break; + case 111: /* Status-Flags */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, + BACnetStatusFlags); + break; + case 112: /* System-Status */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, + BACnetDeviceStatus); + break; + case 117: /* units */ + case 455: /* car-load-units */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, + BACnetEngineeringUnits); + break; + case 87: /* priority-array -- accessed as a BACnetARRAY */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fPriorityArray(tvb, pinfo, tree, offset); + } + break; + case 38: /* exception-schedule */ + if (object_type < 128) { + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fSpecialEvent(tvb, pinfo, tree, offset); + } + } + break; + case 19: /* controlled-variable-reference */ + case 60: /* manipulated-variable-reference */ + case 78: /* object-property-reference */ + case 181: /* input-reference */ + case 355: /* event-algorithm-inhibit-reference */ + offset = fObjectPropertyReference(tvb, pinfo, tree, offset); + break; + case 132: /* log-device-object-property */ + offset = fDeviceObjectPropertyReference(tvb, pinfo, tree, offset); + break; + case 109: /* Setpoint-Reference */ + /* setpoint-Reference is actually BACnetSetpointReference which is a SEQ of [0] */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetObjectPropertyReference(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 123: /* weekly-schedule -- accessed as a BACnetARRAY */ + if (object_type < 128) { + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fWeeklySchedule(tvb, pinfo, tree, offset); + } + } + break; + case 127: /* client COV increment */ + offset = fClientCOV(tvb, pinfo, tree, offset); + break; + case 131: /* log-buffer */ + if ( object_type == 61 ) + offset = fAuditLogRecord(tvb, pinfo, tree, offset); + else if ( object_type == 25 ) + offset = fEventLogRecord(tvb, pinfo, tree, offset); + else if ( object_type == 27 ) + offset = fLogMultipleRecord(tvb, pinfo, tree, offset); + else + offset = fLogRecord(tvb, pinfo, tree, offset); + break; + case 159: /* member-of */ + case 165: /* zone-members */ + case 211: /* subordinate-list */ + case 246: /* access-doors */ + case 249: /* access-event-credential */ + case 252: /* accompaniment */ + case 265: /* credentials */ + case 266: /* credentials-in-zone */ + case 277: /* last-credential-added */ + case 279: /* last-credential-removed */ + case 286: /* members */ + case 320: /* zone-from */ + case 321: /* zone-to */ + case 461: /* energy-meter-ref */ + case 491: /* represents */ + offset = fDeviceObjectReference(tvb, pinfo, tree, offset); + break; + case 196: /* last-restart-reason */ + offset = fRestartReason(tvb, pinfo, tree, offset); + break; + case 212: /* actual-shed-level */ + case 214: /* expected-shed-level */ + case 218: /* requested-shed-level */ + offset = fShedLevel(tvb, pinfo, tree, offset); + break; + case 152: /* active-cov-subscriptions */ + offset = fCOVSubscription(tvb, pinfo, tree, offset); + break; + case 23: /* date-list */ + offset = fCalendarEntry(tvb, pinfo, tree, offset); + break; + case 116: /* time-sychronization-recipients */ + case 206: /* utc-time-synchronization-recipients */ + case 202: /* restart-notification-recipients */ + offset = fRecipient(tvb, pinfo, tree, offset); + break; + case 83: /* event-parameters */ + offset = fEventParameter(tvb, pinfo, tree, offset); + break; + case 130: /* event-time-stamp */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fEventTimeStamps(tvb, pinfo, tree, offset); + } + break; + case 197: /* logging-type */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLoggingType); + break; + case 36: /* event-state */ + offset = fApplicationTypesEnumeratedSplit(tvb, pinfo, tree, offset, ar, BACnetEventState, 64); + break; + case 103: /* reliability */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetReliability); + break; + case 72: /* notify-type */ + offset = fNotifyType(tvb, pinfo, tree, offset); + break; + case 208: /* node-type */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetNodeType); + break; + case 231: /* door-status */ + case 450: /* car-door-status */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetDoorStatus); + break; + case 233: /* lock-status */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLockStatus); + break; + case 235: /* secured-status */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetDoorSecuredStatus); + break; + case 158: /* maintenance-required */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetMaintenance); + break; + case 92: /* program-state */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetProgramState); + break; + case 90: /* program-change */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetProgramRequest); + break; + case 100: /* reason-for-halt */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetProgramError); + break; + case 157: /* last-restore-time */ + offset = fTimeStamp(tvb, pinfo, tree, offset, ar); + break; + case 160: /* mode */ + case 175: /* accepted-modes */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLifeSafetyMode); + break; + case 163: /* silenced */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetSilencedState); + break; + case 161: /* operation-expected */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLifeSafetyOperation); + break; + case 164: /* tracking-value */ + if (object_type == 21 || object_type == 22) /* life-safety-point/zone */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLifeSafetyState); + else if (object_type == 63) /* color */ + offset = fXyColor(tvb, pinfo, tree, offset, ar); + else if (object_type == 64) /* color-temperature */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, ar); + break; + case 166: /* life-safety-alarm-values */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLifeSafetyState); + break; + case 41: /* file-access-method */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetFileAccessMethod); + break; + case 185: /* prescale */ + offset = fPrescale(tvb, pinfo, tree, offset); + break; + case 187: /* scale */ + offset = fScale(tvb, pinfo, tree, offset); + break; + case 189: /* update-time */ + if (object_type == 37) + offset = fTimeStamp(tvb, pinfo, tree, offset, ar); + else + offset = fDateTime(tvb, pinfo, tree, offset, ar); + break; + case 184: /* logging-record */ + offset = fLoggingRecord(tvb, pinfo, tree, offset); + break; + case 203: /* time-of-device-restart */ + offset = fTimeStamp(tvb, pinfo, tree, offset, ar); + break; + case 226: /* door-alarm-state */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetDoorAlarmState); + break; + case 228: /* door-members */ + offset = fDoorMembers(tvb, pinfo, tree, offset); + break; + case 234: /* masked-alarm-values */ + offset = fSequenceOfEnums(tvb, pinfo, tree, offset, ar, BACnetDoorAlarmState); + break; + case 248: /* access-event-authentication-factor */ + offset = fAuthenticationFactor(tvb, pinfo, tree, offset); + break; + case 261: /* authorization-mode */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetAuthorizationMode); + break; + case 53: /* list-of-group-members */ + save_object_type = object_type; + offset = fListOfGroupMembers(tvb, pinfo, tree, offset); + object_type = save_object_type; + break; + case 296: /* occupancy-state */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetAccessZoneOccupancyState); + break; + case 300: /* passback-mode */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetAccessPassbackMode); + break; + case 303: /* reason-for-disable */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetAccessCredentialDisableReason); + break; + case 318: /* user-type */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetAccessUserType); + break; + case 330: /* key-sets */ + offset = fSecurityKeySet(tvb, pinfo, tree, offset); + break; + case 332: /* network-access-security-policies */ + offset = fNetworkSecurityPolicy(tvb, pinfo, tree, offset); + break; + case 338: /* backup-and-restore-state */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetBackupState); + break; + case 370: /* write-status */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetWriteStatus); + break; + case 385: /* transition */ + if (object_type == 54) /* lighting-output */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLightingTransition); + else if (object_type == 63) /* color */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetColorTransition); + else if (object_type == 64) /* color-temperature */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetColorTransition); + break; + case 288: /* negative-access-rules */ + case 302: /* positive-access-rules */ + offset = fAccessRule(tvb, pinfo, tree, offset); + break; + case 304: /* suppoprted-formats */ + offset = fAuthenticationFactorFormat(tvb, pinfo, tree, offset); + break; + case 327: /* base-device-security-policy */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetSecurityLevel); + break; + case 371: /* property-list */ + offset = fSequenceOfEnums(tvb, pinfo, tree, offset, ar, BACnetPropertyIdentifier); + break; + case 358: /* fault-parameters */ + offset = fFaultParameter(tvb, pinfo, tree, offset); + break; + case 359: /* fault type */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetFaultType); + break; + case 362: /* subscribed-recipients */ + offset = fEventNotificationSubscription(tvb, pinfo, tree, offset); + break; + case 364: /* authorization-exemptions */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetAuthorizationExemption); + break; + case 378: /* in-progress */ + if (object_type == 54) /* lighting-output */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLightingInProgress); + else if (object_type == 63) /* color */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetColorOperationInProgress); + else if (object_type == 64) /* color-temperature */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetColorOperationInProgress); + break; + case 380: /* lighting-command */ + offset = fLightingCommand(tvb, pinfo, tree, offset, ar); + break; + case 16: /* change-of-state-time */ + case 71: /* modification-date */ + case 114: /* time-of-active-time-reset */ + case 115: /* time-of-state-count-reset */ + case 142: /* start-time */ + case 143: /* stop-time */ + case 149: /* maximum-value-time-stamp */ + case 150: /* minimum-value-time-stamp */ + case 179: /* count-change-time */ + case 192: /* value-change-time */ + case 254: /* activation-time */ + case 270: /* expiration-time */ + case 278: /* last-credential-added-time */ + case 280: /* last-credential-removed-time */ + case 281: /* last-use-time */ + case 392: /* time-of-strike-count-reset */ + offset = fDateTime(tvb, pinfo, tree, offset, ar); + break; + case 258: /* authentication-policy-list */ + offset = fAuthenticationPolicy(tvb, pinfo, tree, offset); + break; + case 395: /* last-state-change */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetTimerTransition); + break; + case 396: /* state-change-values */ + offset = fTimerStateChangeValue(tvb, pinfo, tree, offset); + break; + case 398: /* timer-state */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetTimerState); + break; + case 407: /* bacnet-ip-global-address */ + case 418: /* fd-bbmd-address */ + offset = fHostNPort(tvb, pinfo, tree, offset, ar); + break; + case 408: /* bacnet-ip-mode */ + case 435: /* bacnet-ipv6-mode */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetIpMode); + break; + case 414: /* bmd-broadcast-distribution-table */ + offset = fBDTEntry(tvb, pinfo, tree, offset, ar); + break; + case 415: /* bbmd-foreign-device-table */ + offset = fFDTEntry(tvb, pinfo, tree, offset, ar); + break; + case 417: /* command */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetNetworkPortCommand); + break; + case 426: /* network-number-quality */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetNetworkNumberQuality); + break; + case 427: /* network-type */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetNetworkType); + break; + case 428: /* routing-table */ + offset = fRouterEntry(tvb, pinfo, tree, offset); + break; + case 429: /* virtual-mac-address-table */ + offset = fVMACEntry(tvb, pinfo, tree, offset); + break; + case 430: /* command-time-array */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fTimeStamp(tvb, pinfo, tree, offset, ar); + } + break; + case 432: /* last-command-time */ + offset = fTimeStamp(tvb, pinfo, tree, offset, ar); + break; + case 433: /* value-source */ + offset = fValueSource(tvb, pinfo, tree, offset); + break; + case 434: /* value-source-array */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fValueSource(tvb, pinfo, tree, offset); + } + break; + case 447: /* assigned-landing-calls */ + offset = fAssignedLandingCalls(tvb, pinfo, tree, offset); + break; + case 448: /* car-assigned-direction */ + case 457: /* car-moving-direction */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLiftCarDirection); + break; + case 449: /* car-door-command */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLiftCarDoorCommand); + break; + case 453: /* car-drive-status */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLiftCarDriveStatus); + break; + case 456: /* car-mode */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLiftCarMode); + break; + case 462: /* escalator-mode */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetEscalatorMode); + break; + case 463: /* fault-signals */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + if (object_type == 59) /* lift object */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLiftFault); + else + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetEscalatorFault); + } + break; + case 467: /* group-mode */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetLiftGroupMode); + break; + case 470: /* landing-calls */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fLandingCallStatus(tvb, pinfo, tree, offset); + } + break; + case 471: /* landing-call-control */ + offset = fLandingCallStatus(tvb, pinfo, tree, offset); + break; + case 472: /* landing-door-status */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fLandingDoorStatus(tvb, pinfo, tree, offset); + } + break; + case 477: /* "operation-direction */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetEscalatorOperationDirection); + break; + case 481: /* active-cov-multiple-subscriptions */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fCOVMultipleSubscription(tvb, pinfo, tree, offset); + } + break; + case 482: /* protocol-level */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetProtocolLevel); + break; + case 486: /* tags */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fNameValue(tvb, pinfo, tree, offset); + } + break; + case 487: /* subordinate-node-types */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetNodeType); + } + break; + case 488: /* subordinate-tags */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fNameValueCollection(tvb, pinfo, tree, offset); + } + break; + case 489: /* subordinate-relationship */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetRelationship); + } + break; + case 490: /* default-subordinate-relationship */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetRelationship); + break; + case 494: /* stages */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fStageLimitValue(tvb, pinfo, tree, offset); + } + break; + case 498: /* audit-level */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetAuditLevel); + break; + case 500: /* audit-priority-filter */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetAuditPriorityFilter); + break; + case 501: /* auditable-operations */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetAuditOperation); + break; + case 504: /* monitored-objects */ + if (propertyArrayIndex == 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + offset = fObjectSelector(tvb, pinfo, tree, offset); + } + break; + case 510: /* command-validation-result */ + case 4194307: /* current-health */ + offset = fHealth(tvb, pinfo, tree, offset); + break; + case 4194312: /* sc-direct-connect-connection-status */ + offset = fSCDirectConnection(tvb, pinfo, tree, offset); + break; + case 4194315: /* sc-failed-connection-requests */ + offset = fSCFailedConnectionRequest(tvb, pinfo, tree, offset); + break; + case 4194316: /* sc-failover-hub-connection-status */ + case 4194324: /* sc-primary-hub-connection-status */ + offset = fSCHubConnection(tvb, pinfo, tree, offset); + break; + case 4194318: /* sc_hub_connector_state */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, ar, BACnetSCHubConnectorState); + break; + case 4194321: /* sc-hub-function-connection-status */ + offset = fSCHubFunctionConnection(tvb, pinfo, tree, offset); + break; + case 4194330: /* default-color */ + offset = fXyColor(tvb, pinfo, tree, offset, ar); + break; + case 4194334: /* color-command */ + offset = fColorCommand(tvb, pinfo, tree, offset, ar); + break; + + case 85: /* present-value */ + if ( object_type == 11 ) /* group object handling of present-value */ + { + offset = fReadAccessResult(tvb, pinfo, tree, offset); + } + else if (object_type == 30) /* access-door object */ + { + offset = fPresentValue(tvb, pinfo, tree, offset, BACnetDoorValue, 0, BACAPP_PRESENT_VALUE_ENUM); + } + else if (object_type == 21) /* life-point */ + { + offset = fPresentValue(tvb, pinfo, tree, offset, BACnetLifeSafetyState, 0, BACAPP_PRESENT_VALUE_ENUM); + } + else if (object_type == 22) /* life-zone */ + { + offset = fPresentValue(tvb, pinfo, tree, offset, BACnetLifeSafetyState, 0, BACAPP_PRESENT_VALUE_ENUM); + } + else if (object_type == 53) /* channel object */ + { + offset = fChannelValue(tvb, pinfo, tree, offset, ar); + } + else if (object_type == 37) /* credential-data-input */ + { + offset = fAuthenticationFactor(tvb, pinfo, tree, offset); + } + else if (object_type == 26) /* global-group */ + { + offset = fPropertyAccessResult(tvb, pinfo, tree, offset); + } + else if (object_type == 28) /* loac-control */ + { + offset = fPresentValue(tvb, pinfo, tree, offset, BACnetShedState, 0, BACAPP_PRESENT_VALUE_ENUM); + } + else if (object_type == 43) /* date-time-pattern-value */ + { + offset = fDateTime(tvb, pinfo, tree, offset, ar); + } + else if (object_type == 44) /* date-time-value */ + { + offset = fDateTime(tvb, pinfo, tree, offset, ar); + } + else if (object_type == 63) /* color */ + { + offset = fXyColor(tvb, pinfo, tree, offset, ar); + } + else + { + if (!tag_info) { + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + // application tag number above 12 reserved for ASHRAE + if (!tag_is_context_specific(tag_info) && tag_no <= 12) { + offset = fPresentValue(tvb, pinfo, tree, offset, NULL, 0, (BacappPresentValueType) tag_no); + } + } else { + do_default_handling = TRUE; + } + } + break; + default: + do_default_handling = TRUE; + break; + } + if (do_default_handling) { + if (tag_info) { + if (tag_is_opening(tag_info)) { + ++depth; + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } else if (tag_is_closing(tag_info)) { + --depth; + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } else { + offset = fContextTaggedValue(tvb, pinfo, tree, offset, ar); + } + } else { + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + +cleanup: + recursion_depth = p_get_proto_depth(pinfo, proto_bacapp); + p_set_proto_depth(pinfo, proto_bacapp, recursion_depth - 1); + return offset; +} + +static guint +fPropertyValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint8 tag_info) +{ + guint8 tag_no; + guint32 lvt; + + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, + &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType(tvb, pinfo, tree, offset); + if (tvb_reported_length_remaining(tvb, offset) > 0) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, + &tag_no, &tag_info, &lvt); + } + } else { + proto_tree_add_expert(tree, pinfo, &ei_bacapp_opening_tag, tvb, offset, -1); + offset = tvb_reported_length(tvb); + } + + return offset; +} + + +static guint +fPropertyIdentifierValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint8 tagoffset) +{ + guint lastoffset = offset; + guint8 tag_no, tag_info; + guint32 lvt; + + offset = fPropertyReference(tvb, pinfo, tree, offset, tagoffset, 0); + if (offset > lastoffset) { + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_no == tagoffset+2) { /* Value - might not be present in ReadAccessResult */ + offset = fPropertyValue(tvb, pinfo, tree, offset, tag_info); + } + } + return offset; +} + +static guint +fBACnetPropertyValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fPropertyIdentifierValue(tvb, pinfo, tree, offset, 0); + if (offset > lastoffset) { + /* detect optional priority + by looking to see if the next tag is context tag number 3 */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_context_specific(tag_info) && (tag_no == 3)) + offset = fUnsignedTag(tvb, pinfo, tree, offset, "Priority: "); + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fSubscribeCOVPropertyRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0, len; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += len; + subtree = tree; + continue; + } + + switch (tag_no) { + case 0: /* ProcessId */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "subscriber Process Id: "); + break; + case 1: /* monitored ObjectId */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 2: /* issueConfirmedNotifications */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "issue Confirmed Notifications: "); + break; + case 3: /* life time */ + offset = fTimeSpan(tvb, pinfo, tree, offset, "life time"); + break; + case 4: /* monitoredPropertyIdentifier */ + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "monitoredPropertyIdentifier"); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyReference(tvb, pinfo, subtree, offset, 1); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + case 5: /* covIncrement */ + offset = fRealTag(tvb, pinfo, tree, offset, "COV Increment: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fSubscribeCOVPropertyMultipleRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0, len; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_tree *subsubtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += len; + subtree = tree; + continue; + } + + switch (tag_no) { + case 0: /* ProcessId */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "subscriber Process Id: "); + break; + case 1: /* issueConfirmedNotifications */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "issue Confirmed Notifications: "); + break; + case 2: /* life time */ + offset = fTimeSpan(tvb, pinfo, tree, offset, "life time"); + break; + case 3: /* notification delay */ + offset = fTimeSpan(tvb, pinfo, tree, offset, "notification delay"); + break; + case 4: /* list-of-cov-subscription-specifications */ + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "list-of-cov-subscription-specifications: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset += len; + subtree = tree; + break; + } + + switch (tag_no) { + case 0: /* monitored-object-identifier */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + case 1: /* list-of-cov-references */ + if (tag_is_opening(tag_info)) { + subsubtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "list-of-cov-references: "); + offset += fTagHeaderTree(tvb, pinfo, subsubtree, offset, &tag_no, &tag_info, &lvt); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + fTagHeaderTree(tvb, pinfo, subsubtree, offset, &tag_no, &tag_info, &lvt); + offset += len; + break; + } + + switch (tag_no) { + case 0: /* monitored-property */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, subsubtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyReference(tvb, pinfo, subsubtree, offset, 1); + offset += fTagHeaderTree(tvb, pinfo, subsubtree, offset, &tag_no, &tag_info, &lvt); + } + else { + expert_add_info(pinfo, subsubtree, &ei_bacapp_bad_tag); + } + break; + case 1: /* cov-increment */ + offset = fRealTag(tvb, pinfo, subsubtree, offset, "COV Increment: "); + break; + case 2: /* timestamped */ + offset = fBooleanTag(tvb, pinfo, subsubtree, offset, "timestamped: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + } + else { + expert_add_info(pinfo, subsubtree, &ei_bacapp_bad_tag); + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + } + else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fSubscribeCOVPropertyMultipleError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0, len; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + offset += len; + break; + } + + switch (tag_no) { + case 0: /* normal error */ + if (tag_is_opening(tag_info)) { + offset = fContextTaggedError(tvb, pinfo, tree, offset); + } + else { + offset = fError(tvb, pinfo, tree, offset); + } + break; + case 1: /* first-failed-subscription */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset += len; + break; + } + + switch (tag_no) { + case 0: /* monitored-object-identifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 1: /* monitored-property-reference */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyReference(tvb, pinfo, tree, offset, 1); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } + else { + expert_add_info(pinfo, tree, &ei_bacapp_bad_tag); + } + break; + case 2: /* error-type */ + offset = fContextTaggedError(tvb, pinfo, tree, offset); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fSubscribeCOVRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fSubscribeCOVPropertyRequest(tvb, pinfo, tree, offset); +} + +static guint +fWhoHas(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* deviceInstanceLowLimit */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "device Instance Low Limit: "); + break; + case 1: /* deviceInstanceHighLimit */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "device Instance High Limit: "); + break; + case 2: /* BACnetObjectId */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 3: /* ObjectName */ + offset = fObjectName(tvb, pinfo, tree, offset); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + + +static guint +fDailySchedule(tvbuff_t *tvb, packet_info *pinfo, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_opening(tag_info) && tag_no == 0) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* opening context tag 0 */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + /* should be closing context tag 0 */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + return offset; + } + + offset = fTimeValue(tvb, pinfo, subtree, offset); + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + } else if ((tag_no == 0) && (lvt == 0)) { + /* not sure null (empty array element) is legal */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } + return offset; +} + +/** + * BACnetHealth ::= SEQUENCE { + * timestamp [0] BACnetDateTime, + * result [1] Error, + * property [2] BACnetPropertiyIdentifier OPTIONAL, + * details [3] CharacterString OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fHealth(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, tree, offset, "timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* result */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fError(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* property - OPTIONAL*/ + offset = fPropertyIdentifier(tvb, pinfo, tree, offset); + break; + case 3: /* details - OPTIONAL */ + offset = fCharacterString(tvb, pinfo, tree, offset, "details: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +/** + * BACnetSCDirectConnection ::= SEQUENCE { + * uri [0] CharacterString + * connection-state [1] BACnetSCConnectionState, + * connect-timestamp [2] BACnetDateTime, + * disconnect-timestamp [3] BACnetDateTime, + * peer-address [4] BACnetHostNPort, + * peer-vmac [5] OCTET STRING (SIZE(6)) + * peer-uuid [6] OCTET STRING (SIZE(16)) + * error [7] Error OPTIONAL + * error-details [8] CharacterString OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSCDirectConnection(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* uri */ + offset = fCharacterString(tvb, pinfo, tree, offset, "uri: "); + break; + case 1: /* connection-state */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "connection-state: ", BACnetSCConnectionState); + break; + case 2: /* connect-timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, tree, offset, "connet-timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 3: /* disconnect-timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, tree, offset, "disconnect-timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* peer-address */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fHostNPort(tvb, pinfo, tree, offset,"peer-address: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 5: /* peer-vmac */ + offset = fOctetString(tvb, pinfo, tree, offset, "peer-vmac: ", lvt); + break; + case 6: /* peer-uuid */ + offset = fOctetString(tvb, pinfo, tree, offset, "peer-uuid: ", lvt); + break; + case 7: /* error */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fError(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 8: /* details - OPTIONAL */ + offset = fCharacterString(tvb, pinfo, tree, offset, "details: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +/** + * BACnetSCFailedConnectionRequest ::= SEQUENCE { + * timestamp [0] BACnetDateTime, + * peer-address [1] BACnetHostNPort, + * peer-vmac [2] OCTET STRING (SIZE(6)) + * peer-uuid [3] OCTET STRING (SIZE(16)) + * error [4] Error OPTIONAL + * error-details [5] CharacterString OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSCFailedConnectionRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, tree, offset, "connet-timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* peer-address */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fHostNPort(tvb, pinfo, tree, offset,"peer-address: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* peer-vmac */ + offset = fOctetString(tvb, pinfo, tree, offset, "peer-vmac: ", lvt); + break; + case 3: /* peer-uuid */ + offset = fOctetString(tvb, pinfo, tree, offset, "peer-uuid: ", lvt); + break; + case 4: /* error */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fError(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 5: /* details - OPTIONAL */ + offset = fCharacterString(tvb, pinfo, tree, offset, "details: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +/** + * BACnetSCHubConnection ::= SEQUENCE { + * connection-state [0] BACnetSCConnectionState, + * connect-timestamp [1] BACnetDateTime, + * disconnect-timestamp [2] BACnetDateTime, + * error [3] Error OPTIONAL + * error-details [4] CharacterString OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSCHubConnection(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* connection-state */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "connection-state: ", BACnetSCConnectionState); + break; + case 1: /* connect-timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, tree, offset, "connet-timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* disconnect-timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, tree, offset, "disconnect-timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 3: /* error */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fError(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* details - OPTIONAL */ + offset = fCharacterString(tvb, pinfo, tree, offset, "details: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +/** + * BACnetSCHubFunctionConnection ::= SEQUENCE { + * connection-state [0] BACnetSCConnectionState, + * connect-timestamp [1] BACnetDateTime, + * disconnect-timestamp [2] BACnetDateTime, + * peer-address [3] BACnetHostNPort, + * peer-vmac [4] OCTET STRING (SIZE(6)) + * peer-uuid [5] OCTET STRING (SIZE(16)) + * error [6] Error OPTIONAL + * error-details [7] CharacterString OPTIONAL + * } + * @param tvb the tv buffer of the current data + * @param pinfo the packet info of the current data + * @param tree the tree to append this item to + * @param offset the offset in the tvb + * @return modified offset + */ +static guint +fSCHubFunctionConnection(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* connection-state */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "connection-state: ", BACnetSCConnectionState); + break; + case 1: /* connect-timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, tree, offset, "connet-timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* disconnect-timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, tree, offset, "disconnect-timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 3: /* peer-address */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fHostNPort(tvb, pinfo, tree, offset,"peer-address: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* peer-vmac */ + offset = fOctetString(tvb, pinfo, tree, offset, "peer-vmac: ", lvt); + break; + case 5: /* peer-uuid */ + offset = fOctetString(tvb, pinfo, tree, offset, "peer-uuid: ", lvt); + break; + case 6: /* error */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fError(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 7: /* details - OPTIONAL */ + offset = fCharacterString(tvb, pinfo, tree, offset, "details: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fWeeklySchedule(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint i = 1; /* day of week array index */ + proto_tree *subtree = tree; + + if (propertyArrayIndex > 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array. + BACnetARRAY index -1 is our internal flag that + the optional index was not used. + BACnetARRAY refers to this as all elements of the array. + If the optional index is specified for a BACnetARRAY, + then that specific array element is referenced. */ + i = propertyArrayIndex; + } + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; /* outer encoding will print out closing tag */ + } + subtree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_bacapp_value, NULL, + val_to_str(i++, day_of_week, "day of week (%d) not found")); + offset = fDailySchedule(tvb, pinfo, subtree, offset); + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + + +static guint +fUTCTimeSynchronizationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + if (tvb_reported_length_remaining(tvb, offset) <= 0) + return offset; + + return fDateTime(tvb, pinfo, tree, offset, "UTC-Time: "); +} + +static guint +fTimeSynchronizationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + if (tvb_reported_length_remaining(tvb, offset) <= 0) + return offset; + + return fDateTime(tvb, pinfo, tree, offset, NULL); +} + +static guint +fWriteGroupRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0, len; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += len; + subtree = tree; + continue; + } + + switch (tag_no) { + case 0: /* group-number */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "Group number: "); + break; + case 1: /* write-priority */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "Priority: "); + break; + case 2: /* change-list */ + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "change-list: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset += len; + subtree = tree; + break; + } + + switch (tag_no) { + case 0: /* channel */ + if (tag_info && ! tag_is_opening(tag_info)) { + /* context tagged */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "Channel: "); + } else { + /* application tagged */ + offset = fChannelValue(tvb, pinfo, subtree, offset, "Value: "); + } + break; + case 1: /* overriding-priority */ + if (tag_info && ! tag_is_opening(tag_info)) { + /* context tagged */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "Overriding priority: "); + } else { + /* application tagged */ + offset = fChannelValue(tvb, pinfo, subtree, offset, "Value: "); + } + break; + default: /* channel-value (application tagged, or opening/closing context-0 tagged) */ + offset = fChannelValue(tvb, pinfo, subtree, offset, "Value: "); + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + } + else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + case 3: /* inhibit-delay */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "Inhibit delay: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fDateRange(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + if (tvb_reported_length_remaining(tvb, offset) <= 0) + return offset; + offset = fDate(tvb, pinfo, tree, offset, "Start Date: "); + return fDate(tvb, pinfo, tree, offset, "End Date: "); +} + +static guint +fVendorIdentifier(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint32 val = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + const gchar *label = "Vendor ID"; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32(tvb, offset + tag_len, lvt, &val)) + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, &ti, "%s: %s (%u)", + label, + val_to_str_ext_const(val, &BACnetVendorIdentifiers_ext, "Unknown Vendor"), + val); + else + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, &ti, "%s - %u octets (Unsigned)", label, lvt); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + if ((lvt < 1) || (lvt > 2)) { /* vendorIDs >= 1 and <= 2 are supported */ + expert_add_info_format(pinfo, ti, &ei_bacapp_bad_length, + "Wrong length indicated. Expected 1 or 2, got %u", lvt); + return offset+tag_len+lvt; + } + + proto_tree_add_item(subtree, hf_BACnetVendorIdentifier, tvb, + offset+tag_len, lvt, ENC_BIG_ENDIAN); + + return offset+tag_len+lvt; +} + +static guint +fRestartReason(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint32 val = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint tag_len; + proto_item *ti; + proto_tree *subtree; + const gchar *label = "Restart Reason"; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32(tvb, offset + tag_len, lvt, &val)) + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, &ti, "%s: %s (%u)", label, + val_to_str_const(val, BACnetRestartReason, "Unknown reason"), val); + else + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, &ti, "%s - %u octets (Unsigned)", label, lvt); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + if (lvt != 1) { + expert_add_info_format(pinfo, ti, &ei_bacapp_bad_length, + "Wrong length indicated. Expected 1, got %u", lvt); + return offset+tag_len+lvt; + } + + proto_tree_add_item(subtree, hf_BACnetRestartReason, tvb, + offset+tag_len, lvt, ENC_BIG_ENDIAN); + + return offset+tag_len+lvt; +} + +static guint +fConfirmedTextMessageRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* textMessageSourceDevice */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "DeviceIdentifier: "); + break; + case 1: /* messageClass */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + switch (fTagNo(tvb, offset)) { + case 0: /* numeric */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "message Class: "); + break; + case 1: /* character */ + offset = fCharacterString(tvb, pinfo, tree, offset, "message Class: "); + break; + } + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* messagePriority */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "message Priority: ", + BACnetMessagePriority); + break; + case 3: /* message */ + offset = fCharacterString(tvb, pinfo, tree, offset, "message: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fUnconfirmedTextMessageRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fConfirmedTextMessageRequest(tvb, pinfo, tree, offset); +} + +static guint +fConfirmedPrivateTransferRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset, len; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + tvbuff_t *next_tvb; + guint vendor_identifier = 0; + guint service_number = 0; + + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + fUnsigned32(tvb, offset+len, lvt, &vendor_identifier); + col_append_fstr(pinfo->cinfo, COL_INFO, "V=%u ", vendor_identifier); + offset = fVendorIdentifier(tvb, pinfo, subtree, offset); + + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (dissector_try_uint(bacapp_dissector_table, + vendor_identifier, next_tvb, pinfo, tree)) { + /* we parsed it so skip over length and we are done */ + offset += tvb_reported_length(next_tvb); + return offset; + } + + /* Not handled by vendor dissector */ + + /* exit loop if nothing happens inside */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + if (tag_no == 2) { /* Make sure it's the expected tag */ + offset += len; + subtree = tree; + continue; + } else { + break; /* End loop if incorrect closing tag */ + } + } + switch (tag_no) { + + /* vendorID is now parsed above */ + case 1: /* serviceNumber */ + fUnsigned32(tvb, offset+len, lvt, &service_number); + col_append_fstr(pinfo->cinfo, COL_INFO, "SN=%u ", service_number); + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "service Number: "); + break; + case 2: /*serviceParameters */ + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, + ett_bacapp_value, NULL, "service Parameters"); + propertyIdentifier = -1; + offset = fAbstractSyntaxNType(tvb, pinfo, subtree, offset); + } else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + return offset; +} + +static guint +fUnconfirmedPrivateTransferRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fConfirmedPrivateTransferRequest(tvb, pinfo, tree, offset); +} + +static guint +fConfirmedPrivateTransferAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fConfirmedPrivateTransferRequest(tvb, pinfo, tree, offset); +} + +static guint +fLifeSafetyOperationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *label) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + if (label != NULL) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, label); + } + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + + switch (tag_no) { + case 0: /* subscriberProcessId */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "requesting Process Id: "); + break; + case 1: /* requestingSource */ + offset = fCharacterString(tvb, pinfo, tree, offset, "requesting Source: "); + break; + case 2: /* request */ + offset = fEnumeratedTagSplit(tvb, pinfo, tree, offset, + "request: ", BACnetLifeSafetyOperation, 64); + break; + case 3: /* objectId */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +typedef struct _value_string_enum { + guint8 tag_no; + const value_string *valstr; +} value_string_enum; + +static const value_string_enum +BACnetPropertyStatesEnums[] = { + { 1, BACnetBinaryPV }, + { 2, BACnetEventType }, + { 3, BACnetPolarity }, + { 4, BACnetProgramRequest }, + { 5, BACnetProgramState }, + { 6, BACnetProgramError }, + { 7, BACnetReliability }, + { 8, BACnetEventState }, + { 9, BACnetDeviceStatus }, + { 10, BACnetEngineeringUnits }, + { 12, BACnetLifeSafetyMode }, + { 13, BACnetLifeSafetyState }, + { 14, BACnetRestartReason }, + { 15, BACnetDoorAlarmState }, + { 16, BACnetAction }, + { 17, BACnetDoorSecuredStatus }, + { 18, BACnetDoorStatus }, + { 19, BACnetDoorValue }, + { 20, BACnetFileAccessMethod }, + { 21, BACnetLockStatus }, + { 22, BACnetLifeSafetyOperation }, + { 23, BACnetMaintenance }, + { 24, BACnetNodeType }, + { 25, BACnetNotifyType }, + { 26, BACnetSecurityLevel }, + { 27, BACnetShedState }, + { 28, BACnetSilencedState }, + { 30, BACnetAccessEvent }, + { 31, BACnetAccessZoneOccupancyState }, + { 32, BACnetAccessCredentialDisableReason }, + { 33, BACnetAccessCredentialDisable }, + { 34, BACnetAuthenticationStatus }, + { 36, BACnetBackupState }, + { 37, BACnetWriteStatus }, + { 38, BACnetLightingInProgress }, + { 39, BACnetLightingOperation }, + { 40, BACnetLightingTransition }, + { 42, BACnetBinaryLightingPV }, + { 43, BACnetTimerState }, + { 44, BACnetTimerTransition }, + { 45, BACnetIpMode }, + { 46, BACnetNetworkPortCommand }, + { 47, BACnetNetworkType }, + { 48, BACnetNetworkNumberQuality }, + { 49, BACnetEscalatorOperationDirection }, + { 50, BACnetEscalatorFault }, + { 51, BACnetEscalatorMode }, + { 52, BACnetLiftCarDirection }, + { 53, BACnetLiftCarDoorCommand }, + { 54, BACnetLiftCarDriveStatus }, + { 55, BACnetLiftCarMode }, + { 56, BACnetLiftGroupMode }, + { 57, BACnetLiftFault }, + { 58, BACnetProtocolLevel }, + { 59, BACnetAuditLevel }, + { 60, BACnetAuditOperation } +}; +#define BACnetPropertyStatesEnums_Size \ + (sizeof(BACnetPropertyStatesEnums) / sizeof(BACnetPropertyStatesEnums[0])) + +static guint +fBACnetPropertyStates(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint32 idx; + const gchar* label; + const value_string_enum* valstrenum; + + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + label = wmem_strdup_printf(pinfo->pool, "%s: ", + val_to_str_const( tag_no, VALS(BACnetPropertyStates), "Unknown State" )); + + switch (tag_no) { + case 0: + offset = fBooleanTag(tvb, pinfo, tree, offset, label); + break; + case 11: + offset = fUnsignedTag(tvb, pinfo, tree, offset, label); + break; + case 41: + offset = fSignedTag(tvb, pinfo, tree, offset, label); + break; + case 63: + offset = fUnsignedTag(tvb, pinfo, tree, offset, label); + break; + default: + valstrenum = NULL; + + for (idx = 0; idx < BACnetPropertyStatesEnums_Size; idx++) { + valstrenum = &BACnetPropertyStatesEnums[idx]; + if (valstrenum->tag_no == tag_no && + valstrenum->valstr != NULL) { + break; + } + valstrenum = NULL; + } + + if (valstrenum == NULL) + { + offset = fEnumeratedTag(tvb, pinfo, tree, offset, label, NULL); + /* don't use Abstract type here because it is context tagged and therefore we don't know app type */ + } + else + { + offset = fEnumeratedTagSplit(tvb, pinfo, tree, offset, label, + VALS(valstrenum->valstr), 64); + } + break; + } + return offset; +} + + +/* +BACnetDeviceObjectPropertyValue ::= SEQUENCE { + deviceIdentifier [0] BACnetObjectIdentifier, + objectIdentifier [1] BACnetObjectIdentifier, + propertyIdentifier [2] BACnetPropertyIdentifier, + arrayIndex [3] Unsigned OPTIONAL, + value [4] ABSTRACT-SYNTAX.&Type + } +*/ +static guint +fDeviceObjectPropertyValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* deviceIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "DeviceIdentifier: "); + break; + case 1: /* objectIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 2: /* propertyIdentifier */ + offset = fPropertyIdentifier(tvb, pinfo, tree, offset); + break; + case 3: /* arrayIndex - OPTIONAL */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, + "arrayIndex: "); + break; + case 4: /* value */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +/* +BACnetDeviceObjectPropertyReference ::= SEQUENCE { + objectIdentifier [0] BACnetObjectIdentifier, + propertyIdentifier [1] BACnetPropertyIdentifier, + propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + -- if omitted with an array then + -- the entire array is referenced + deviceIdentifier [3] BACnetObjectIdentifier OPTIONAL + } +*/ +static guint +fObjectPropertyReference(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fDeviceObjectPropertyReference(tvb, pinfo, tree, offset); +} + +/* +BACnetDeviceObjectPropertyReference ::= SEQUENCE { + objectIdentifier [0] BACnetObjectIdentifier, + propertyIdentifier [1] BACnetPropertyIdentifier, + propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype + -- if omitted with an array then + -- the entire array is referenced + deviceIdentifier [3] BACnetObjectIdentifier OPTIONAL + } +*/ +static guint +fDeviceObjectPropertyReference(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 1: /* propertyIdentifier */ + offset = fPropertyIdentifier(tvb, pinfo, tree, offset); + break; + case 2: /* arrayIndex - OPTIONAL */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, + "arrayIndex: "); + break; + case 3: /* deviceIdentifier - OPTIONAL */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "DeviceIdentifier: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fNotificationParameters(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = offset; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_tree *pvtree; + + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + subtree = proto_tree_add_subtree_format(subtree, tvb, offset, 0, + ett_bacapp_value, NULL, "notification parameters (%d) %s", + tag_no, val_to_str_const(tag_no, BACnetEventType, "invalid type")); + /* Opening tag for parameter choice */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + switch (tag_no) { + case 0: /* change-of-bitstring */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fBitStringTag(tvb, pinfo, subtree, offset, + "referenced-bitstring: "); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 1: /* change-of-state */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyStates(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 2: /* change-of-value */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + switch (fTagNo(tvb, offset)) { + case 0: + offset = fBitStringTag(tvb, pinfo, subtree, offset, + "changed-bits: "); + break; + case 1: + offset = fRealTag(tvb, pinfo, subtree, offset, + "changed-value: "); + break; + default: + break; + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 3: /* command-failure */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* "command-value: " */ + /* from BACnet Table 13-3, + Standard Object Property Values Returned in Notifications */ + propertyIdentifier = 85; /* PRESENT_VALUE */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: /* "feedback-value: " */ + propertyIdentifier = 40; /* FEEDBACK_VALUE */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 4: /* floating-limit */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fRealTag(tvb, pinfo, subtree, offset, "reference-value: "); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: + offset = fRealTag(tvb, pinfo, subtree, offset, "setpoint-value: "); + break; + case 3: + offset = fRealTag(tvb, pinfo, subtree, offset, "error-limit: "); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 5: /* out-of-range */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fRealTag(tvb, pinfo, subtree, offset, "exceeding-value: "); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: + offset = fRealTag(tvb, pinfo, subtree, offset, "deadband: "); + break; + case 3: + offset = fRealTag(tvb, pinfo, subtree, offset, "exceeded-limit: "); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 6: + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset =fBACnetPropertyValue(tvb, pinfo, subtree, offset); + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 7: /* deprecated (was 'buffer-ready', changed and moved to [10]) */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "DeviceIdentifier: "); /* buffer-device */ + break; + case 1: + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); /* buffer-object */ + break; + case 2: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, subtree, offset, "previous-notification: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 3: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, subtree, offset, "current-notification: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 8: /* change-of-life-safety */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fEnumeratedTagSplit(tvb, pinfo, subtree, offset, + "new-state: ", BACnetLifeSafetyState, 256); + break; + case 1: + offset = fEnumeratedTagSplit(tvb, pinfo, subtree, offset, + "new-mode: ", BACnetLifeSafetyMode, 256); + break; + case 2: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 3: + offset = fEnumeratedTagSplit(tvb, pinfo, subtree, offset, + "operation-expected: ", BACnetLifeSafetyOperation, 64); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 9: /* extended */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fVendorIdentifier(tvb, pinfo, subtree, offset); + break; + case 1: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "extended-event-type: "); + break; + case 2: /* parameters */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + const guint param_lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) + { + break; + } + + if (tag_is_opening(tag_info)) + { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectPropertyValue(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } + else + { + offset = fApplicationTypes(tvb, pinfo, subtree, offset, "parameters: "); + } + if (offset <= param_lastoffset) + break; /* nothing happened, exit loop */ + } + + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 10: /* buffer ready */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* buffer-property */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectPropertyReference(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, + "previous-notification: "); + break; + case 2: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, + "current-notification: "); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 11: /* unsigned range */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, + "exceeding-value: "); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, + "exceeded-limit: "); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + /* 12 reserved */ + case 13: /* access-event */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fEnumeratedTagSplit(tvb, pinfo, subtree, offset, + "access event: ", BACnetAccessEvent, 512); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, + "access-event-tag: "); + break; + case 3: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp(tvb, pinfo, subtree, offset, "access-event-time: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectReference(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 5: /* optional authentication-factor */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAuthenticationFactor(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 14: /* double-out-of-range */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fDoubleTag(tvb, pinfo, subtree, offset, "exceeding-value: "); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: + offset = fDoubleTag(tvb, pinfo, subtree, offset, "deadband: "); + break; + case 3: + offset = fDoubleTag(tvb, pinfo, subtree, offset, "exceeded-limit: "); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 15: /* signed-out-of-range */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fSignedTag(tvb, pinfo, subtree, offset, "exceeding-value: "); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "deadband: "); + break; + case 3: + offset = fSignedTag(tvb, pinfo, subtree, offset, "exceeded-limit: "); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 16: /* unsigned-out-of-range */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "exceeding-value: "); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "deadband: "); + break; + case 3: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "exceeded-limit: "); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 17: /* change-of-characterstring */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + /* changed-value (CharacterString) */ + offset = fCharacterString(tvb, pinfo, subtree, offset, "changed-value: "); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: + /* alarm-value (CharacterString) */ + offset = fCharacterString(tvb, pinfo, subtree, offset, "alarm-value: "); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 18: /* change-of-status-flags */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { + /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_context_specific(tag_info)) { + propertyIdentifier = 85; /* suppose present-value here */ + offset = fAbstractSyntaxNType(tvb, pinfo, subtree, offset); + } else { + offset = fPresentValue(tvb, pinfo, tree, offset, BACnetStatusFlags, 0, BACAPP_PRESENT_VALUE_ENUM); + } + + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "referenced-flags: ", BACnetStatusFlags); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 19: /* change-of-reliability */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { + /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: + offset = fEnumeratedTag(tvb, pinfo, subtree, offset, "reliability:", BACnetReliability); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, "status-flags: ", BACnetStatusFlags); + break; + case 2: /* property-values */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + return offset; + } + pvtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "property-values"); + offset += fTagHeaderTree(tvb, pinfo, pvtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyValue(tvb, pinfo, pvtree, offset); + offset += fTagHeaderTree(tvb, pinfo, pvtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + if (offset <= lastoffset) + break; /* nothing happened, exit loop */ + } + break; + case 20: /* context tag [20] is not used */ + break; + case 21: /* change-of-discrete-value */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { + /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* new-value */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_opening(tag_info)) + { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, subtree, offset, "new-value: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } + else + { + offset = fApplicationTypes(tvb, pinfo, subtree, offset, "new-value: "); + } + + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* status-flags */ + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 22: /* change-of-timer */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { + /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* new-state */ + offset = fEnumeratedTagSplit(tvb, pinfo, subtree, offset, + "new-state: ", BACnetTimerState, 256); + break; + case 1: /* status-flags */ + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "status-flags: ", BACnetStatusFlags); + break; + case 2: /* update-time */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, subtree, offset, "update-time: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 3: /* last-state-change (OPTIONAL) */ + offset = fEnumeratedTagSplit(tvb, pinfo, subtree, offset, + "new-state: ", BACnetTimerTransition, 256); + break; + case 4: /* initial-timeout (OPTIONAL) */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "initial-timeout: "); + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + + /* todo: add new parameters here ... */ + default: + offset = fAbstractSyntaxNType(tvb, pinfo, subtree, offset); + break; + } + + /* Closing tag for parameter choice */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + return offset; +} + +static guint +fEventParameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = offset; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + subtree = proto_tree_add_subtree_format(subtree, tvb, offset, 0, + ett_bacapp_value, NULL, "event parameters (%d) %s", + tag_no, val_to_str_const(tag_no, BACnetEventType, "invalid type")); + + /* Opening tag for parameter choice */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + switch (tag_no) { + case 0: /* change-of-bitstring */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: + offset = fBitStringTag(tvb, pinfo, subtree, offset, "bitmask: "); + break; + case 2: /* SEQUENCE OF BIT STRING */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + offset = fBitStringTag(tvb, pinfo, subtree, offset, + "bitstring value: "); + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + case 1: /* change-of-state */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: /* SEQUENCE OF BACnetPropertyStates */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + offset = fBACnetPropertyStates(tvb, pinfo, subtree, offset); + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + case 2: /* change-of-value */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: /* don't loop it, it's a CHOICE */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + switch (fTagNo(tvb, offset)) { + case 0: + offset = fBitStringTag(tvb, pinfo, subtree, offset, "bitmask: "); + break; + case 1: + offset = fRealTag(tvb, pinfo, subtree, offset, + "referenced Property Increment: "); + break; + default: + break; + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + case 3: /* command-failure */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + tag_no = fTagNo(tvb, offset); + switch (tag_no) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectPropertyReference(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + case 4: /* floating-limit */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectPropertyReference(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: + offset = fRealTag(tvb, pinfo, subtree, offset, "low diff limit: "); + break; + case 3: + offset = fRealTag(tvb, pinfo, subtree, offset, "high diff limit: "); + break; + case 4: + offset = fRealTag(tvb, pinfo, subtree, offset, "deadband: "); + break; + default: + break; + } + } + break; + case 5: /* out-of-range */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: + offset = fRealTag(tvb, pinfo, subtree, offset, "low limit: "); + break; + case 2: + offset = fRealTag(tvb, pinfo, subtree, offset, "high limit: "); + break; + case 3: + offset = fRealTag(tvb, pinfo, subtree, offset, "deadband: "); + break; + default: + break; + } + } + break; + case 6: /* complex-event-type */ + /* deprecated */ + offset = fBACnetPropertyValue (tvb, pinfo, tree, offset); + break; + case 7: /* buffer-ready */ + /* deprecated */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fUnsignedTag(tvb, pinfo, tree, offset, "notification threshold"); + break; + case 1: + offset = fUnsignedTag(tvb, pinfo, tree, offset, + "previous notification count: "); + break; + default: + return offset; + } + } + break; + case 8: /* change-of-life-safety */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + offset = fEnumeratedTagSplit(tvb, pinfo, subtree, offset, + "life safety alarm value: ", BACnetLifeSafetyState, 256); + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + offset = fEnumeratedTagSplit(tvb, pinfo, subtree, offset, + "alarm value: ", BACnetLifeSafetyState, 256); + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 3: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectPropertyReference(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + case 9: /* extended */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fVendorIdentifier(tvb, pinfo, tree, offset); + break; + case 1: + offset = fUnsignedTag(tvb, pinfo, tree, offset, "extended-event-type: "); + break; + case 2: /* parameters */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info) && tag_no == 2) { + break; + } + + if ( ! tag_is_context_specific(tag_info)) { + offset = fApplicationTypes(tvb, pinfo, tree, offset, "parameters: "); + } else { + if (tag_no == 0) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectPropertyReference(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } else { + offset = fAbstractSyntaxNType(tvb, pinfo, tree, offset); + } + } + } + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + lastoffset = offset; + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 10: /* buffer-ready */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, + "notification-threshold: "); + break; + case 1: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, + "previous-notification-count: "); + break; + default: + break; + } + } + break; + case 11: /* unsigned-range */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan(tvb, pinfo, tree, offset, "Time Delay"); + break; + case 1: + offset = fUnsignedTag(tvb, pinfo, tree, offset, + "low-limit: "); + break; + case 2: + offset = fUnsignedTag(tvb, pinfo, tree, offset, + "high-limit: "); + break; + default: + break; + } + } + break; + case 13: /* access-event */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + /* TODO: [0] SEQUENCE OF BACnetAccessEvent */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + offset = fEnumeratedTagSplit(tvb, pinfo, subtree, offset, + "access event: ", BACnetAccessEvent, 512); + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectPropertyReference(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + case 14: /* double-out-of-range */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: + offset = fDoubleTag(tvb, pinfo, subtree, offset, "low limit: "); + break; + case 2: + offset = fDoubleTag(tvb, pinfo, subtree, offset, "high limit: "); + break; + case 3: + offset = fDoubleTag(tvb, pinfo, subtree, offset, "deadband: "); + break; + default: + break; + } + } + break; + case 15: /* signed-out-of-range */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: + offset = fSignedTag(tvb, pinfo, subtree, offset, "low limit: "); + break; + case 2: + offset = fSignedTag(tvb, pinfo, subtree, offset, "high limit: "); + break; + case 3: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "deadband: "); + break; + default: + break; + } + } + break; + case 16: /* unsigned-out-of-range */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "low limit: "); + break; + case 2: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "high limit: "); + break; + case 3: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "deadband: "); + break; + default: + break; + } + } + break; + case 17: /* change-of-characterstring */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: /* SEQUENCE OF CharacterString */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + offset = fCharacterString(tvb, pinfo, tree, offset, "alarm value: "); + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + case 18: /* change-of-status-flags */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: + offset = fBitStringTagVS(tvb, pinfo, subtree, offset, + "selected flags: ", BACnetStatusFlags); + break; + default: + break; + } + } + break; + case 19: /* has been intentionally omitted. It parallels the change-of-reliability event type */ + break; + case 20: /* none */ + /* no closing tag expected only context tag here */ + return offset; + case 21: /* change-of-discrete-value */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + default: + break; + } + } + break; + case 22: /* change-of-timer */ + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* time-delay */ + offset = fTimeSpan(tvb, pinfo, subtree, offset, "Time Delay"); + break; + case 1: /* alarm-values */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + offset = fEnumeratedTag(tvb, pinfo, subtree, offset, + "alarm value: ", BACnetTimerState); + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* update-time-reference */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectPropertyReference(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + /* todo: add new event-parameter cases here */ + default: + break; + } + + /* Closing tag for parameter choice */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + return offset; +} + +static guint +fFaultParameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = offset; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + subtree = proto_tree_add_subtree_format(subtree, tvb, offset, 0, + ett_bacapp_value, NULL, "fault parameters (%d) %s", + tag_no, val_to_str_const(tag_no, BACnetFaultType, "invalid type")); + + /* Opening tag for parameter choice */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + switch (tag_no) { + case 0: /* none */ + /* no closing tag expected only context tag here */ + return offset; + case 1: /* fault-characterstring */ + while ((tvb_reported_length_remaining(tvb, offset) > 0) && (offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* SEQUENCE OF CharacterString */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + while ((tvb_reported_length_remaining(tvb, offset) > 0) && (offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + offset = fCharacterString(tvb, pinfo, subtree, offset, "fault value: "); + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + case 2: /* fault-extended */ + while ((tvb_reported_length_remaining(tvb, offset) > 0) && (offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset = fVendorIdentifier(tvb, pinfo, subtree, offset); + break; + case 1: + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "extended-fault-type: "); + break; + case 2: /* parameters */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fApplicationTypes(tvb, pinfo, subtree, offset, "parameters: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + lastoffset = offset; + break; + default: + break; + } + } + break; + case 3: /* fault-life-safety */ + while ((tvb_reported_length_remaining(tvb, offset) > 0) && (offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + while ((tvb_reported_length_remaining(tvb, offset) > 0) && (offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + offset = fEnumeratedTag(tvb, pinfo, subtree, offset, + "fault value: ", BACnetLifeSafetyState); + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectPropertyReference(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + case 4: /* fault-state */ + while ((tvb_reported_length_remaining(tvb, offset) > 0) && (offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: /* SEQUENCE OF BACnetPropertyStates */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + while ((tvb_reported_length_remaining(tvb, offset) > 0) && (offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + offset = fBACnetPropertyStates(tvb, pinfo, subtree, offset); + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + case 5: /* fault-status-flags */ + while ((tvb_reported_length_remaining(tvb, offset) > 0) && (offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectPropertyReference(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + case 6: /* fault-out-of-range */ + while ((tvb_reported_length_remaining(tvb, offset) > 0) && (offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (fTagNo(tvb, offset)) { + case 0: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fApplicationTypes(tvb, pinfo, subtree, offset, "min-normal-value: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fApplicationTypes(tvb, pinfo, subtree, offset, "max-normal-value: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + case 7: /* fault-listed */ + while ((tvb_reported_length_remaining(tvb, offset) > 0) && (offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectPropertyReference(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + } + break; + default: + break; + } + + /* Closing tag for parameter choice */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + return offset; +} + +static guint +fEventNotificationSubscription(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree; + guint itemno = 1; + + while (tvb_reported_length_remaining(tvb, offset) > 0 && offset > lastoffset) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* recipient */ + tree = proto_tree_add_subtree_format(tree, tvb, offset, 1, + ett_bacapp_value, NULL, "Subscription %d", itemno); /* add tree label and indent */ + itemno = itemno + 1; + + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, + ett_bacapp_value, NULL, "Recipient: "); /* add tree label and indent */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context open */ + offset = fRecipient(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context close */ + break; + case 1: /* process-identifier */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "Process Identifier: "); + break; + case 2: /* issue-confirmed-notifications */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "Issue Confirmed Notifications: "); + break; + case 3: /* time-remaining */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "Time Remaining: "); + break; + default: + return offset; + } + } + + return offset; +} + +static guint +fLightingCommand(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *lable) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + subtree = proto_tree_add_subtree_format(subtree, tvb, offset, 0, + ett_bacapp_value, NULL, "%s", lable); + + while (tvb_reported_length_remaining(tvb, offset) > 0 && offset > lastoffset) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* operation */ + offset = fEnumeratedTag(tvb, pinfo, subtree, offset, "operation: ", BACnetLightingOperation); + break; + case 1: /* target-level */ + offset = fRealTag(tvb, pinfo, subtree, offset, "target-level: "); + break; + case 2: /* ramp-rate */ + offset = fRealTag(tvb, pinfo, subtree, offset, "ramp-rate: "); + break; + case 3: /* step-increment */ + offset = fRealTag(tvb, pinfo, subtree, offset, "step-increment: "); + break; + case 4: /* fade-time */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "fade-time: "); + break; + case 5: /* priority */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "priority: "); + break; + default: + return offset; + } + } + + return offset; +} + +static guint +fColorCommand(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, guint offset, const gchar* lable) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree* subtree = tree; + + subtree = proto_tree_add_subtree_format(subtree, tvb, offset, 0, + ett_bacapp_value, NULL, "%s", lable); + + while (tvb_reported_length_remaining(tvb, offset) > 0 && offset > lastoffset) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* operation */ + offset = fEnumeratedTag(tvb, pinfo, subtree, offset, "operation: ", BACnetColorOperation); + break; + case 1: /* target-color */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fXyColor(tvb, pinfo, subtree, offset, "xy-color: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* target-color-temperature */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "target-color-temperature: "); + break; + case 3: /* fade-time */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "fade-time: "); + break; + case 4: /* ramp-rate */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "ramp-rate: "); + break; + case 5: /* step-increment */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "step-increment: "); + break; + default: + return offset; + } + } + + return offset; +} + +static guint +fXyColor(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, guint offset, const gchar* label) +{ + proto_tree* subtree = tree; + + if (label != NULL) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 10, ett_bacapp_value, NULL, label); + } + offset = fRealTag(tvb, pinfo, subtree, offset, "x-coordinate: "); + return fRealTag(tvb, pinfo, subtree, offset, "y-coordinate: "); +} + +static guint +fTimerStateChangeValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint ftag_offset; + + while (tvb_reported_length_remaining(tvb, offset) > 0 && offset > lastoffset) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + ftag_offset = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + if (tag_is_context_specific(tag_info)){ + switch (tag_no) { + case 0: /* no-value */ + offset = fNullTag(tvb, pinfo, tree, offset, "no-value: "); + break; + case 1: /* constructed-value */ + offset += ftag_offset; + offset = fAbstractSyntaxNType(tvb, pinfo, tree, offset); + offset += fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* date-time */ + offset += ftag_offset; + offset = fDateTime(tvb, pinfo, tree, offset, "date-time: "); + offset += fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + break; + case 3: /* lighting-command */ + offset += ftag_offset; + offset = fLightingCommand(tvb, pinfo, tree, offset, "lighting-command: "); + offset += fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + } + else { + offset = fApplicationTypes(tvb, pinfo, tree, offset, NULL); + } + } + return offset; +} + +static guint +fHostAddress(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + + if (tvb_reported_length_remaining(tvb, offset) > 0) { + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 0: /* none */ + offset = fNullTag(tvb, pinfo, tree, offset, "no-value: "); + break; + case 1: /* ip-address */ + offset = fOctetString(tvb, pinfo, tree, offset, "ip-address: ", lvt); + break; + case 2: /* internet name (see RFC 1123) */ + offset = fCharacterString(tvb, pinfo, tree, offset, "name: "); + break; + default: + return offset; + } + } + + return offset; +} + +static guint +fHostNPort(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *lable) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + subtree = proto_tree_add_subtree_format(subtree, tvb, offset, 0, + ett_bacapp_value, NULL, "%s", lable); + + while (tvb_reported_length_remaining(tvb, offset) > 0 && offset > lastoffset) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* host */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fHostAddress(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* port */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "port: "); + break; + default: + return offset; + } + } + + return offset; +} + +static guint +fBDTEntry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *lable) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + subtree = proto_tree_add_subtree_format(subtree, tvb, offset, 0, + ett_bacapp_value, NULL, "%s", lable); + + while (tvb_reported_length_remaining(tvb, offset) > 0 && offset > lastoffset) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + + switch (tag_no) { + case 0: /* bbmd-address */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fHostNPort(tvb, pinfo, subtree, offset, "bbmd-address: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* bbmd-mask */ + offset = fOctetString(tvb, pinfo, subtree, offset, "bbmd-mask: ", lvt); + break; + default: + return offset; + } + } + + return offset; +} + +static guint +fFDTEntry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, const gchar *lable) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + subtree = proto_tree_add_subtree_format(subtree, tvb, offset, 0, + ett_bacapp_value, NULL, "%s", lable); + + while (tvb_reported_length_remaining(tvb, offset) > 0 && offset > lastoffset) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + + switch (tag_no) { + case 0: /* bacnetip-address */ + offset = fOctetString(tvb, pinfo, subtree, offset, "bacnetip-address: ", lvt); + break; + case 1: /* time-to-live */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "time-to-live: "); + break; + case 2: /* remaining-time-to-live */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "remaining-time-to-live: "); + break; + default: + return offset; + } + } + + return offset; +} + +static guint +fRouterEntry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: /* network number */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "network number: "); + break; + case 1: /* MAC address */ + offset = fOctetString(tvb, pinfo, tree, offset, "MAC address: ", lvt); + break; + case 2: /* status */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "status: ", BACnetRouterStatus); + break; + case 3: /* performance index */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "performance index: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + return offset; +} + +static guint +fVMACEntry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: /* virtual mac address */ + offset = fOctetString(tvb, pinfo, tree, offset, "virtual MAC address: ", lvt); + break; + case 1: /* native mac address */ + offset = fOctetString(tvb, pinfo, tree, offset, "native MAC address: ", lvt); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + return offset; +} + +static guint +fValueSource(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + + if (tvb_reported_length_remaining(tvb, offset) > 0) { + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 0: /* null */ + offset = fNullTag(tvb, pinfo, tree, offset, "no-value: "); + break; + case 1: /* object reference */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDeviceObjectReference(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* address */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAddress(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + } + + return offset; +} + +static guint +fAssignedLandingCalls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: /* floor number */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "floor number: "); + break; + case 1: /* direction */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "direction: ", BACnetLiftCarDirection); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + return offset; +} + +static guint +fLandingCallStatus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: /* floor number */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "floor number: "); + break; + case 1: /* direction */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "direction: ", BACnetLiftCarDirection); + break; + case 2: /* destination */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "destination: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + return offset; +} + +static guint +fLandingDoorStatus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: /* floor number */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "floor number: "); + break; + case 1: /* door status */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "door status: ", BACnetDoorStatus); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + return offset; +} + +static guint +fCOVMultipleSubscription(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: /* recipient */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fRecipientProcess(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* issue-confirmed-notifications */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "issue confirmed notifications: "); + break; + case 2: /* time-remaining */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "time remaining: "); + break; + case 3: /* max-notification-delay */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "max notification delay: "); + break; + case 4: /* list-of-cov-subscription-specifications */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: /* monitored-object-identifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 1: /* list-of-cov-references */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: /* monitored-property */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyReference(tvb, pinfo, tree, offset, 0); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* cov-increment */ + offset = fRealTag(tvb, pinfo, tree, offset, "cov-increment: "); + break; + case 2: /* timestamped */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "timestamped: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + return offset; +} + +static guint +fNameValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + if (tag_is_context_specific(tag_info)) { + switch (tag_no) { + case 0: /* name */ + offset = fCharacterString(tvb, pinfo, tree, offset, "name: "); + break; + case 1: /* date+time value */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, tree, offset, "value: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: /* abstract syntax and type */ + /* DMR Should be fAbstractNSyntax, but that's where we came from! */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, "value: "); + break; + } + } + else { + /* DMR Should be fAbstractNSyntax, but that's where we came from! */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, "value: "); + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fNameValueCollection(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + subtree = proto_tree_add_subtree_format(subtree, tvb, offset, 0, + ett_bacapp_value, NULL, "%s", "name-value-collection: "); + + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + while (tvb_reported_length_remaining(tvb, offset) > 0 && offset > lastoffset) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + + offset = fNameValue(tvb, pinfo, subtree, offset); + } + + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + return offset; +} + +static guint +fObjectSelector(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* NULL */ + offset = fNullTag(tvb, pinfo, tree, offset, "NULL: "); + break; + case 9: /* object-type */ + offset = fEnumeratedTagSplit(tvb, pinfo, tree, offset, "object-type: ", BACnetObjectType, 256); + break; + case 12: /* object */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fStageLimitValue(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + if (tvb_reported_length_remaining(tvb, offset) <= 0) + return offset; + offset = fRealTag(tvb, pinfo, tree, offset, "limit: "); + if (tvb_reported_length_remaining(tvb, offset) <= 0) + return offset; + offset = fBitStringTag(tvb, pinfo, tree, offset, "values: "); + if (tvb_reported_length_remaining(tvb, offset) <= 0) + return offset; + offset = fRealTag(tvb, pinfo, tree, offset, "deadband: "); + return offset; +} + +static guint +fLifeSafetyInfo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* requesting-process-identifier */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "requesting-process-identifier: "); + break; + case 1: /* request */ + offset = fEnumeratedTagSplit(tvb, pinfo, tree, offset, + "requested-operation: ", BACnetLifeSafetyOperation, 64); + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fAcknowledgeAlarmInfo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* event-state-acknowledged */ + offset = fEnumeratedTagSplit(tvb, pinfo, tree, offset, + "event-state-acknowledged: ", BACnetEventState, 64); + break; + case 1: /* timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); /* show context open */ + offset = fTimeStamp(tvb, pinfo, tree, offset, "source-timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); /* show context close */ + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fAuditNotificationInfo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint len, lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + guint32 operation = 0; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* source-timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); /* show context open */ + offset = fTimeStamp(tvb, pinfo, tree, offset, "source-timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); /* show context close */ + break; + case 1: /* target-timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); /* show context open */ + offset = fTimeStamp(tvb, pinfo, tree, offset, "target-timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); /* show context close */ + break; + case 2: /* source-device */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "source-device: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context open */ + offset = fRecipient(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context close */ + break; + case 3: /* source-object */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "source-object: "); + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + case 4: /* operation */ + fUnsigned32(tvb, offset, lvt, &operation); + offset = fEnumeratedTagSplit(tvb, pinfo, tree, offset, + "operation: ", BACnetAuditOperation, 64); + break; + case 5: /* source-comment */ + offset = fCharacterString(tvb, pinfo, tree, offset, "source-comment: "); + break; + case 6: /* target-comment */ + offset = fCharacterString(tvb, pinfo, tree, offset, "target-comment: "); + break; + case 7: /* invoke-id */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "invoke-id: "); + break; + case 8: /* source-user-id */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "source-user-id: "); + break; + case 9: /* source-user-role */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "source-user-role: "); + break; + case 10: /* target-device */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "target-device: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context open */ + offset = fRecipient(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context close */ + break; + case 11: /* target-object */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "target-object: "); + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + case 12: /* target-property */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "target-property: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context open */ + offset = fPropertyReference(tvb, pinfo, subtree, offset, 0, 0); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context close */ + break; + case 13: /* target-priority */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "target-priority: "); + break; + case 14: /* target-value */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "target-value: "); + if (operation == 4) { + /* operation life safety */ + /* inspect next tag */ + fTagHeader(tvb, pinfo, offset + len, &tag_no, &tag_info, &lvt); + if ( tag_no == 0 && + ! tag_is_opening(tag_info) && + tag_is_context_specific(tag_info) ) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context open */ + offset = fLifeSafetyInfo(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context close */ + } else { + /* abstract syntax and type */ + offset = fPropertyValue(tvb, pinfo, subtree, offset, tag_info); + } + } else if ( operation == 5 ) { + /* operation acknowledge alarm */ + /* inspect next tag */ + fTagHeader(tvb, pinfo, offset + len, &tag_no, &tag_info, &lvt); + if ( tag_no == 0 && + ! tag_is_opening(tag_info) && + tag_is_context_specific(tag_info) ) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context open */ + offset = fAcknowledgeAlarmInfo(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context close */ + } else { + /* abstract syntax and type */ + offset = fPropertyValue(tvb, pinfo, subtree, offset, tag_info); + } + } else { + /* abstract syntax and type */ + offset = fPropertyValue(tvb, pinfo, subtree, offset, tag_info); + } + break; + case 15: /* current-value */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "current-value: "); + /* always abstract syntax and type */ + offset = fPropertyValue(tvb, pinfo, subtree, offset, tag_info); + break; + case 16: /* error-result */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "error-result: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context open */ + offset = fError(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); /* show context close */ + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fAuditLogRecord(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDate(tvb, pinfo, tree, offset, "Date: "); + offset = fTime(tvb, pinfo, tree, offset, "Time: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* logDatum: don't loop, it's a CHOICE */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + switch (fTagNo(tvb, offset)) { + case 0: /* logStatus */ /* Changed this to BitString per BACnet Spec. */ + offset = fBitStringTagVS(tvb, pinfo, tree, offset, "log status:", BACnetLogStatus); + break; + case 1: /* notification */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "notification: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAuditNotificationInfo(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* time-change */ + offset = fRealTag(tvb, pinfo, tree, offset, "time-change: "); + break; + default: + return offset; + } + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fEventLogRecord(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDate(tvb, pinfo, tree, offset, "Date: "); + offset = fTime(tvb, pinfo, tree, offset, "Time: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* logDatum: don't loop, it's a CHOICE */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + switch (fTagNo(tvb, offset)) { + case 0: /* logStatus */ /* Changed this to BitString per BACnet Spec. */ + offset = fBitStringTagVS(tvb, pinfo, tree, offset, "log status:", BACnetLogStatus); + break; + case 1: /* notification */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "notification: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fConfirmedEventNotificationRequest(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* time-change */ + offset = fRealTag(tvb, pinfo, tree, offset, "time-change: "); + break; + default: + return offset; + } + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fLogRecord(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + gint32 save_propertyIdentifier; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDate(tvb, pinfo, tree, offset, "Date: "); + offset = fTime(tvb, pinfo, tree, offset, "Time: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* logDatum: don't loop, it's a CHOICE */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + switch (fTagNo(tvb, offset)) { + case 0: /* logStatus */ /* Changed this to BitString per BACnet Spec. */ + offset = fBitStringTagVS(tvb, pinfo, tree, offset, "log status: ", BACnetLogStatus); + break; + case 1: + offset = fBooleanTag(tvb, pinfo, tree, offset, "boolean-value: "); + break; + case 2: + offset = fRealTag(tvb, pinfo, tree, offset, "real value: "); + break; + case 3: + offset = fUnsignedTag(tvb, pinfo, tree, offset, "enum value: "); + break; + case 4: + offset = fUnsignedTag(tvb, pinfo, tree, offset, "unsigned value: "); + break; + case 5: + offset = fSignedTag(tvb, pinfo, tree, offset, "signed value: "); + break; + case 6: + offset = fBitStringTag(tvb, pinfo, tree, offset, "bitstring value: "); + break; + case 7: + offset = fNullTag(tvb, pinfo, tree, offset, "null value: "); + break; + case 8: + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fError(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 9: + offset = fRealTag(tvb, pinfo, tree, offset, "time change: "); + break; + case 10: /* any Value */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + /* this ASN-1 construction may contain also an property identifier, so + save the one we have got and restore it later and invalidate current + one to avoid misinterpretations */ + save_propertyIdentifier = propertyIdentifier; + propertyIdentifier = -1; + offset = fAbstractSyntaxNType(tvb, pinfo, tree, offset); + propertyIdentifier = save_propertyIdentifier; + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: + /* Changed this to BitString per BACnet Spec. */ + offset = fBitStringTagVS(tvb, pinfo, tree, offset, "Status Flags: ", BACnetStatusFlags); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fLogMultipleRecord(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + gint32 save_propertyIdentifier; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDate(tvb, pinfo, tree, offset, "Date: "); + offset = fTime(tvb, pinfo, tree, offset, "Time: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* logData: don't loop, it's a CHOICE */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + switch (fTagNo(tvb, offset)) { + case 0: /* logStatus */ /* Changed this to BitString per BACnet Spec. */ + offset = fBitStringTagVS(tvb, pinfo, tree, offset, "log status: ", BACnetLogStatus); + break; + case 1: /* log-data: SEQUENCE OF CHOICE */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + while ((tvb_reported_length_remaining(tvb, offset) > 0) && (offset != lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + lastoffset = offset; + break; + } + switch (tag_no) { + case 0: + offset = fBooleanTag(tvb, pinfo, tree, offset, "boolean-value: "); + break; + case 1: + offset = fRealTag(tvb, pinfo, tree, offset, "real value: "); + break; + case 2: + offset = fUnsignedTag(tvb, pinfo, tree, offset, "enum value: "); + break; + case 3: + offset = fUnsignedTag(tvb, pinfo, tree, offset, "unsigned value: "); + break; + case 4: + offset = fSignedTag(tvb, pinfo, tree, offset, "signed value: "); + break; + case 5: + offset = fBitStringTag(tvb, pinfo, tree, offset, "bitstring value: "); + break; + case 6: + offset = fNullTag(tvb, pinfo, tree, offset, "null value: "); + break; + case 7: + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fError(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 8: /* any Value */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + /* this ASN-1 construction may contain also an property identifier, so + save the one we have got and restore it later and invalidate current + one to avoid misinterpretations */ + save_propertyIdentifier = propertyIdentifier; + propertyIdentifier = -1; + offset = fAbstractSyntaxNType(tvb, pinfo, tree, offset); + propertyIdentifier = save_propertyIdentifier; + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + } + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: + offset = fRealTag(tvb, pinfo, tree, offset, "time-change: "); + break; + default: + return offset; + } + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + + +static guint +fConfirmedEventNotificationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* ProcessId */ + offset = fProcessId(tvb, pinfo, tree, offset); + break; + case 1: /* initiating ObjectId */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 2: /* event ObjectId */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 3: /* time stamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp(tvb, pinfo, tree, offset, NULL); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* notificationClass */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "Notification Class: "); + break; + case 5: /* Priority */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "Priority: "); + break; + case 6: /* EventType */ + offset = fEventType(tvb, pinfo, tree, offset); + break; + case 7: /* messageText */ + offset = fCharacterString(tvb, pinfo, tree, offset, "message Text: "); + break; + case 8: /* NotifyType */ + offset = fNotifyType(tvb, pinfo, tree, offset); + break; + case 9: /* ackRequired */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "ack Required: "); + break; + case 10: /* fromState */ + offset = fFromState(tvb, pinfo, tree, offset); + break; + case 11: /* toState */ + offset = fToState(tvb, pinfo, tree, offset); + break; + case 12: /* NotificationParameters */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fNotificationParameters(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fUnconfirmedEventNotificationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fConfirmedEventNotificationRequest(tvb, pinfo, tree, offset); +} + +static guint +fConfirmedCOVNotificationMultipleRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0, len; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + proto_tree *subsubtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += len; + subtree = tree; + continue; + } + + switch (tag_no) { + case 0: /* ProcessId */ + offset = fProcessId(tvb, pinfo, tree, offset); + break; + case 1: /* initiating DeviceId */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "DeviceIdentifier: "); + break; + case 2: /* time remaining */ + offset = fTimeSpan(tvb, pinfo, tree, offset, "Time remaining: "); + break; + case 3: /* timestamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, tree, offset, "Timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* list-of-cov-notifications */ + if (tag_is_opening(tag_info)) { + /* new subtree for list-of-cov-notifications */ + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "list-of-cov-notifications: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + /* end for list-of-cov-notifications */ + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset += len; + subtree = tree; + break; + } + + switch (tag_no) { + case 0: /* monitored-object-identifier */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + case 1: /* list-of-values */ + if (tag_is_opening(tag_info)) { + /* new subtree for list-of-values */ + subsubtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "list-of-values: "); + offset += fTagHeaderTree(tvb, pinfo, subsubtree, offset, &tag_no, &tag_info, &lvt); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + /* end of list-of-values */ + fTagHeaderTree(tvb, pinfo, subsubtree, offset, &tag_no, &tag_info, &lvt); + offset += len; + break; + } + + switch (tag_no) { + case 0: /* PropertyIdentifier */ + offset = fPropertyIdentifier(tvb, pinfo, subsubtree, offset); + break; + case 1: /* propertyArrayIndex */ + offset = fPropertyArrayIndex(tvb, pinfo, subsubtree, offset); + break; + case 2: /* property-value */ + offset = fPropertyValue(tvb, pinfo, subsubtree, offset, tag_info); + break; + case 3: /* time-of-change */ + offset = fTime(tvb, pinfo, subsubtree, offset, "time of change: "); + break; + default: + /* wrong tag encoding */ + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + } + else { + /* wrong tag encoding */ + expert_add_info(pinfo, subsubtree, &ei_bacapp_bad_tag); + } + break; + default: + /* wrong tag encoding */ + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + } + else { + /* wrong tag encoding */ + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + default: + /* wrong tag encoding */ + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fUnconfirmedCOVNotificationMultipleRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fConfirmedCOVNotificationMultipleRequest(tvb, pinfo, tree, offset); +} + +static guint +fConfirmedCOVNotificationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0, len; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset += len; + subtree = tree; + continue; + } + + switch (tag_no) { + case 0: /* ProcessId */ + offset = fProcessId(tvb, pinfo, tree, offset); + break; + case 1: /* initiating DeviceId */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "DeviceIdentifier: "); + break; + case 2: /* monitored ObjectId */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + case 3: /* time remaining */ + offset = fTimeSpan(tvb, pinfo, tree, offset, "Time remaining: "); + break; + case 4: /* List of Values */ + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "list of Values: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyValue(tvb, pinfo, subtree, offset); + } + else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fUnconfirmedCOVNotificationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fConfirmedCOVNotificationRequest(tvb, pinfo, tree, offset); +} + +static guint +fAcknowledgeAlarmRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* acknowledgingProcessId */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "acknowledging Process Id: "); + break; + case 1: /* eventObjectId */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 2: /* eventStateAcknowledged */ + offset = fEnumeratedTagSplit(tvb, pinfo, tree, offset, + "event State Acknowledged: ", BACnetEventState, 64); + break; + case 3: /* timeStamp */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp(tvb, pinfo, tree, offset, NULL); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* acknowledgementSource */ + offset = fCharacterString(tvb, pinfo, tree, offset, "acknowledgement Source: "); + break; + case 5: /* timeOfAcknowledgement */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp(tvb, pinfo, tree, offset, "acknowledgement timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fGetAlarmSummaryAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Object Identifier: "); + offset = fApplicationTypesEnumeratedSplit(tvb, pinfo, tree, offset, + "alarm State: ", BACnetEventState, 64); + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, + "acknowledged Transitions: ", BACnetEventTransitionBits); + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fGetEnrollmentSummaryRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* acknowledgmentFilter */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, + "acknowledgment Filter: ", BACnetAcknowledgementFilter); + break; + case 1: /* eventObjectId - OPTIONAL */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fRecipientProcess(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* eventStateFilter */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, + "event State Filter: ", BACnetEventStateFilter); + break; + case 3: /* eventTypeFilter - OPTIONAL */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, + "event Type Filter: ", BACnetEventType); + break; + case 4: /* priorityFilter */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fUnsignedTag(tvb, pinfo, tree, offset, "min Priority: "); + offset = fUnsignedTag(tvb, pinfo, tree, offset, "max Priority: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 5: /* notificationClassFilter - OPTIONAL */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "notification Class Filter: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fGetEnrollmentSummaryAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Object Identifier: "); + offset = fApplicationTypesEnumeratedSplit(tvb, pinfo, tree, offset, + "event Type: ", BACnetEventType, 64); + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, + "event State: ", BACnetEventState); + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Priority: "); + if (tvb_reported_length_remaining(tvb, offset) > 0 && fTagNo(tvb, offset) == 2) /* Notification Class - OPTIONAL */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "Notification Class: "); + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + return offset; +} + +static guint +fGetEventInformationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + if (tvb_reported_length_remaining(tvb, offset) > 0) { + if (fTagNo(tvb, offset) == 0) { + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + } + } + return offset; +} + +static guint +flistOfEventSummaries(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree* subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* we are finished here if we spot a closing tag */ + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: /* ObjectId */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 1: /* eventState */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, + "event State: ", BACnetEventState); + break; + case 2: /* acknowledgedTransitions */ + offset = fBitStringTagVS(tvb, pinfo, tree, offset, + "acknowledged Transitions: ", BACnetEventTransitionBits); + break; + case 3: /* eventTimeStamps */ + subtree = proto_tree_add_subtree(tree, tvb, offset, lvt, ett_bacapp_tag, NULL, "eventTimeStamps"); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeStamp(tvb, pinfo, subtree, offset, "TO-OFFNORMAL timestamp: "); + offset = fTimeStamp(tvb, pinfo, subtree, offset, "TO-FAULT timestamp: "); + offset = fTimeStamp(tvb, pinfo, subtree, offset, "TO-NORMAL timestamp: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 4: /* notifyType */ + offset = fNotifyType(tvb, pinfo, tree, offset); + break; + case 5: /* eventEnable */ + offset = fBitStringTagVS(tvb, pinfo, tree, offset, + "event Enable: ", BACnetEventTransitionBits); + break; + case 6: /* eventPriorities */ + subtree = proto_tree_add_subtree(tree, tvb, offset, lvt, ett_bacapp_tag, NULL, "eventPriorities"); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "TO-OFFNORMAL Priority: "); + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "TO-FAULT Priority: "); + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "TO-NORMAL Priority: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fLOPR(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + col_set_writable(pinfo->cinfo, COL_INFO, FALSE); /* don't set all infos into INFO column */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* we are finished here if we spot a closing tag */ + if (tag_is_closing(tag_info)) { + break; + } + offset = fDeviceObjectPropertyReference(tvb, pinfo, tree, offset); + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fGetEventInformationACK(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* listOfEventSummaries */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = flistOfEventSummaries(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* moreEvents */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "more Events: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fAddListElementRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0, len; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + col_set_writable(pinfo->cinfo, COL_INFO, FALSE); /* don't set all infos into INFO column */ + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += len; + subtree = tree; + continue; + } + + switch (tag_no) { + case 0: /* ObjectId */ + offset = fBACnetObjectPropertyReference(tvb, pinfo, subtree, offset); + break; + case 3: /* listOfElements */ + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "listOfElements"); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType(tvb, pinfo, subtree, offset); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fDeleteObjectRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); +} + +static guint +fDeviceCommunicationControlRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* timeDuration */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "time Duration: "); + break; + case 1: /* enable-disable */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "enable-disable: ", + BACnetEnableDisable); + break; + case 2: /* password - OPTIONAL */ + offset = fCharacterString(tvb, pinfo, tree, offset, "Password: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fReinitializeDeviceRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* reinitializedStateOfDevice */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, + "reinitialized State Of Device: ", + BACnetReinitializedStateOfDevice); + break; + case 1: /* password - OPTIONAL */ + offset = fCharacterString(tvb, pinfo, tree, offset, "Password: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fVtOpenRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, + "vtClass: ", BACnetVTClass); + return fApplicationTypes(tvb, pinfo, tree, offset, "local VT Session ID: "); +} + +static guint +fVtOpenAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fApplicationTypes(tvb, pinfo, tree, offset, "remote VT Session ID: "); +} + +static guint +fVtCloseRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset= fApplicationTypes(tvb, pinfo, tree, offset, "remote VT Session ID: "); + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fVtDataRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + offset= fApplicationTypes(tvb, pinfo, tree, offset, "VT Session ID: "); + offset = fApplicationTypes(tvb, pinfo, tree, offset, "VT New Data: "); + return fApplicationTypes(tvb, pinfo, tree, offset, "VT Data Flag: "); +} + +static guint +fVtDataAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* BOOLEAN */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "all New Data Accepted: "); + break; + case 1: /* Unsigned OPTIONAL */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "accepted Octet Count: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fConfirmedAuditNotificationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint firstloop = 1; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + } + + if (tag_is_opening(tag_info) && firstloop) { + firstloop = 0; + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } + + offset = fAuditNotificationInfo(tvb, pinfo, tree, offset); + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fUnconfirmedAuditNotificationRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fConfirmedAuditNotificationRequest(tvb, pinfo, tree, offset); +} + +static guint +fAuditLogQueryByTargetParameters(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* target-device-identifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "DeviceIdentifier: "); + break; + case 1: /* target-device-address */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAddress(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* target-object-identifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 3: /* target-property-identifier */ + offset = fPropertyIdentifier(tvb, pinfo, tree, offset); + break; + case 4: /* target-property-array-index */ + offset = fPropertyArrayIndex(tvb, pinfo, tree, offset); + break; + case 5: /* target-priority */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "target-priority: "); + break; + case 6: /* target-operation */ + offset = fBitStringTagVS(tvb, pinfo, tree, offset, + "target-operation: ", BACnetAuditOperation); + break; + case 7: /* successful-action */ + offset = fEnumeratedTagSplit(tvb, pinfo, tree, offset, + "target-successful-action: ", BACnetSuccessFilter, 64); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fAuditLogQueryBySourceParameters(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* source-device-identifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "DeviceIdentifier: "); + break; + case 1: /* source-device-address */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAddress(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* source-object-identifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 3: /* source-operation */ + offset = fBitStringTagVS(tvb, pinfo, tree, offset, + "source-operation: ", BACnetAuditOperation); + break; + case 4: /* successful-action */ + offset = fEnumeratedTagSplit(tvb, pinfo, tree, offset, + "source-successful-action: ", BACnetSuccessFilter, 64); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fAuditLogQueryParameters(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* query-by-target-parameters */ + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "target-parameters: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAuditLogQueryByTargetParameters(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 1: /* query-by-source-parameters */ + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "source-parameters: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAuditLogQueryBySourceParameters(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fAuditLogQueryRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + + switch (tag_no) { + case 0: /* audit-log */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 1: /* query-parameters */ + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "query-parameters: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAuditLogQueryParameters(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* start-at-sequence-number */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "start-at-sequence-number: "); + break; + case 3: /* requested-count */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "requested-count: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fAuditLogRecordResult(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0 : /* sequence-number */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "sequence-number: "); + break; + case 1: /* log-record */ + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "log-record: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAuditLogRecord(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fAuditLogQueryAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + + switch (tag_no) { + case 0: /* audit-log */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 1: /* records */ + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "records: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAuditLogRecordResult(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* no-more-items */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "no-more-items: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fWhoAmIRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Vendor ID: "); + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Model name: "); + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Serial number: "); + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fYouAreRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Vendor ID: "); + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Model name: "); + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Serial number: "); + if(tvb_reported_length_remaining(tvb, offset) > 0) { + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Device Identifier: "); + } + if(tvb_reported_length_remaining(tvb, offset) > 0) { + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Device MAC address: "); + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fAuthenticateRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* Unsigned32 */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "pseudo Random Number: "); + break; + case 1: /* expected Invoke ID Unsigned8 OPTIONAL */ + proto_tree_add_item(tree, hf_bacapp_invoke_id, tvb, offset++, 1, ENC_BIG_ENDIAN); + break; + case 2: /* Chararacter String OPTIONAL */ + offset = fCharacterString(tvb, pinfo, tree, offset, "operator Name: "); + break; + case 3: /* Chararacter String OPTIONAL */ + offset = fCharacterString(tvb, pinfo, tree, offset, "operator Password: "); + break; + case 4: /* Boolean OPTIONAL */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "start Encyphered Session: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fAuthenticateAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fApplicationTypes(tvb, pinfo, tree, offset, "modified Random Number: "); +} + +static guint +fAuthenticationFactor(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* quit loop if we spot a closing tag */ + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* format-type */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "format-type: ", NULL); + break; + case 1: /* format-class */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "format-class: "); + break; + case 2: /* value */ + offset = fOctetString(tvb, pinfo, tree, offset, "value: ", lvt); + break; + default: + break; + } + + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + return offset; +} + +static guint +fAuthenticationFactorFormat(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* quit loop if we spot a closing tag */ + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* format-type */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "format-type: ", NULL); + break; + case 1: /* vendor-id */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "vendor-id: "); + break; + case 2: /* vendor-format */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "vendor-format: "); + break; + default: + break; + } + + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + return offset; +} + +static guint +fAuthenticationPolicy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* quit loop if we spot a closing tag */ + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* policy */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* quit loop if we spot a closing tag */ + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + } + + switch (tag_no) { + case 0: /* credential-data-input */ + offset = fDeviceObjectReference(tvb, pinfo, tree, offset); + break; + case 1: /* index */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "index: "); + break; + default: + break; + } + + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + break; + case 1: /* order-enforced */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "order-enforced: "); + break; + case 2: /* timeout */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "timeout: "); + break; + default: + break; + } + + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + return offset; +} + +static guint +fRequestKeyRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "DeviceIdentifier: "); /* Requesting Device Identifier */ + offset = fAddress(tvb, pinfo, tree, offset); + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "DeviceIdentifier: "); /* Remote Device Identifier */ + return fAddress(tvb, pinfo, tree, offset); +} + +static guint +fRemoveListElementRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + /* Same as AddListElement request after service choice */ + return fAddListElementRequest(tvb, pinfo, tree, offset); +} + +static guint +fReadPropertyRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fBACnetObjectPropertyReference(tvb, pinfo, tree, offset); +} + +static guint +fReadPropertyAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0, len; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + /* set the optional global properties to indicate not-used */ + propertyArrayIndex = -1; + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += len; + subtree = tree; + continue; + } + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + case 1: /* propertyIdentifier */ + offset = fPropertyIdentifier(tvb, pinfo, subtree, offset); + break; + case 2: /* propertyArrayIndex */ + offset = fPropertyArrayIndex(tvb, pinfo, subtree, offset); + break; + case 3: /* propertyValue */ + offset = fPropertyValue(tvb, pinfo, subtree, offset, tag_info); + break; + default: + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fWritePropertyRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + /* set the optional global properties to indicate not-used */ + propertyArrayIndex = -1; + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* quit loop if we spot a closing tag */ + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + case 1: /* propertyIdentifier */ + offset = fPropertyIdentifier(tvb, pinfo, subtree, offset); + break; + case 2: /* propertyArrayIndex */ + offset = fPropertyArrayIndex(tvb, pinfo, subtree, offset); + break; + case 3: /* propertyValue */ + offset = fPropertyValue(tvb, pinfo, subtree, offset, tag_info); + break; + case 4: /* Priority (only used for write) */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "Priority: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fWriteAccessSpecification(tvbuff_t *tvb, packet_info *pinfo, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0, len; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* maybe a listOfwriteAccessSpecifications if we spot a closing tag */ + if (tag_is_closing(tag_info)) { + offset += len; + continue; + } + + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + case 1: /* listOfPropertyValues */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyValue(tvb, pinfo, subtree, offset); + } else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fWritePropertyMultipleRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + if (offset >= tvb_reported_length(tvb)) + return offset; + + col_set_writable(pinfo->cinfo, COL_INFO, FALSE); /* don't set all infos into INFO column */ + return fWriteAccessSpecification(tvb, pinfo, tree, offset); +} + +static guint +fPropertyReference(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint8 tagoffset, guint8 list) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + /* set the optional global properties to indicate not-used */ + propertyArrayIndex = -1; + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { /* closing Tag, but not for me */ + return offset; + } else if (tag_is_opening(tag_info)) { /* opening Tag, but not for me */ + return offset; + } + switch (tag_no-tagoffset) { + case 0: /* PropertyIdentifier */ + offset = fPropertyIdentifier(tvb, pinfo, tree, offset); + break; + case 1: /* propertyArrayIndex */ + offset = fPropertyArrayIndex(tvb, pinfo, tree, offset); + if (list != 0) + break; /* Continue decoding if this may be a list */ + /* FALLTHROUGH */ + default: + lastoffset = offset; /* Set loop end condition */ + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fBACnetPropertyReference(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint8 list) +{ + col_set_writable(pinfo->cinfo, COL_INFO, FALSE); /* don't set all infos into INFO column */ + return fPropertyReference(tvb, pinfo, tree, offset, 0, list); +} + +static guint +fBACnetObjectPropertyReference(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + switch (fTagNo(tvb, offset)) { + case 0: /* ObjectIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 1: /* PropertyIdentifier and propertyArrayIndex */ + offset = fPropertyReference(tvb, pinfo, tree, offset, 1, 0); + col_set_writable(pinfo->cinfo, COL_INFO, FALSE); /* don't set all infos into INFO column */ + /* FALLTHROUGH */ + default: + lastoffset = offset; /* Set loop end condition */ + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +#if 0 +static guint +fObjectPropertyValue(tvbuff_t *tvb, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree* subtree = tree; + proto_item* tt; + + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, + &tag_no, &tag_info, &lvt); + continue; + } + switch (tag_no) { + case 0: /* ObjectIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + case 1: /* PropertyIdentifier */ + offset = fPropertyIdentifier(tvb, pinfo, subtree, offset); + break; + case 2: /* propertyArrayIndex */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "property Array Index: "); + break; + case 3: /* Value */ + offset = fPropertyValue(tvb, pinfo, subtree, offset, tag_info); + break; + case 4: /* Priority */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "Priority: "); + break; + default: + break; + } + } + return offset; +} +#endif + +static guint +fPriorityArray(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + char i = 1, ar[256]; + guint lastoffset = 0; + guint8 tag_no; + guint8 tag_info; + guint32 lvt; + + if (propertyArrayIndex > 0) { + /* BACnetARRAY index 0 refers to the length + of the array, not the elements of the array. + BACnetARRAY index -1 is our internal flag that + the optional index was not used. + BACnetARRAY refers to this as all elements of the array. + If the optional index is specified for a BACnetARRAY, + then that specific array element is referenced. */ + i = propertyArrayIndex; + } + while (tvb_reported_length_remaining(tvb, offset) > 0) { + /* exit loop if nothing happens inside */ + lastoffset = offset; + snprintf(ar, sizeof(ar), "%s[%d]: ", + val_to_split_str(87 , 512, + BACnetPropertyIdentifier, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt), + i++); + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if ( ! tag_is_context_specific(tag_info)) { + /* DMR Should be fAbstractNSyntax, but that's where we came from! */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } else { + if (tag_is_opening(tag_info) && tag_no == 0) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } else if (tag_is_opening(tag_info) && tag_no == 1) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fDate(tvb, pinfo, tree, offset, "Date: "); + offset = fTime(tvb, pinfo, tree, offset, "Time: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } else if (tag_is_opening(tag_info) && tag_no == 2) { + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fXyColor(tvb, pinfo, tree, offset, "xy-color: "); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } else { + /* DMR Should be fAbstractNSyntax, but that's where we came from! */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, ar); + } + } + /* there are only 16 priority array elements */ + if (i > 16) { + break; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + return offset; +} + +static guint +fDeviceObjectReference(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* quit loop if we spot an un-matched closing tag */ + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: /* deviceIdentifier - OPTIONAL */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "DeviceIdentifier: "); + break; + case 1: /* ObjectIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fSpecialEvent(tvbuff_t *tvb, packet_info *pinfo, proto_tree *subtree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* quit loop if we spot an un-matched closing tag */ + if (tag_is_closing(tag_info)) { + break; + } + switch (tag_no) { + case 0: /* calendarEntry */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fCalendarEntry(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } + break; + case 1: /* calendarReference */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + case 2: /* list of BACnetTimeValue */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fTimeValue(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + case 3: /* eventPriority */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "event priority: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fNetworkSecurityPolicy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree; + + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_tag, NULL, "network security policy"); + + while (tvb_reported_length_remaining(tvb, offset) > 0 && offset > lastoffset) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* port-id */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "port-id: "); + break; + case 1: /* security-level */ + offset = fEnumeratedTag(tvb, pinfo, subtree, offset, + "security-level: ", BACnetSecurityPolicy); + break; + default: + return offset; + } + } + + return offset; +} + +static guint +fKeyIdentifier(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0 && offset > lastoffset) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* algorithm */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "algorithm: "); + break; + case 1: /* key-id */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "key-id: "); + break; + default: + return offset; + } + } + + return offset; +} + +static guint +fSecurityKeySet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree; + + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_tag, NULL, "security keyset"); + + while (tvb_reported_length_remaining(tvb, offset) > 0 && offset > lastoffset) { + lastoffset = offset; + /* check the tag. A closing tag means we are done */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + return offset; + } + switch (tag_no) { + case 0: /* key-revision */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "key-revision: "); + break; + case 1: /* activation-time */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, subtree, offset, "activation-time: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 2: /* expiration-time */ + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fDateTime(tvb, pinfo, subtree, offset, "expiration-time: "); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + break; + case 3: /* key-ids */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fKeyIdentifier(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + default: + return offset; + } + } + + return offset; +} + +static guint +fSelectionCriteria(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* quit loop if we spot a closing tag */ + if (tag_is_closing(tag_info)) { + break; + } + + switch (fTagNo(tvb, offset)) { + case 0: /* propertyIdentifier */ + offset = fPropertyIdentifier(tvb, pinfo, tree, offset); + break; + case 1: /* propertyArrayIndex */ + offset = fPropertyArrayIndex(tvb, pinfo, tree, offset); + break; + case 2: /* relationSpecifier */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, + "relation Specifier: ", BACnetRelationSpecifier); + break; + case 3: /* comparisonValue */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fObjectSelectionCriteria(tvbuff_t *tvb, packet_info *pinfo, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* quit loop if we spot a closing tag */ + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* selectionLogic */ + offset = fEnumeratedTag(tvb, pinfo, subtree, offset, + "selection Logic: ", BACnetSelectionLogic); + break; + case 1: /* listOfSelectionCriteria */ + if (tag_is_opening(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fSelectionCriteria(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + + +static guint +fReadPropertyConditionalRequest(tvbuff_t *tvb, packet_info* pinfo, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader (tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + + if (tag_is_opening(tag_info) && tag_no < 2) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 0: /* objectSelectionCriteria */ + offset = fObjectSelectionCriteria(tvb, pinfo, subtree, offset); + break; + case 1: /* listOfPropertyReferences */ + offset = fBACnetPropertyReference(tvb, pinfo, subtree, offset, 1); + break; + default: + return offset; + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fReadAccessSpecification(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 0: /* objectIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + case 1: /* listOfPropertyReferences */ + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "listOfPropertyReferences"); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetPropertyReference(tvb, pinfo, subtree, offset, 1); + } else if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, + &tag_no, &tag_info, &lvt); + subtree = tree; + } else { + /* error condition: let caller handle */ + return offset; + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fReadAccessResult(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0, len; + guint8 tag_no; + guint8 tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* maybe a listOfReadAccessResults if we spot a closing tag here */ + if (tag_is_closing(tag_info)) { + offset += len; + if ((tag_no == 4 || tag_no == 5) && (subtree != tree)) + subtree = subtree->parent; /* Value and error have extra subtree */ + + if (tag_no == 1) { + /* closing list of results for this objectSpecifier */ + fTagHeaderTree(tvb, pinfo, subtree, offset - len, &tag_no, &tag_info, &lvt); + /* look if another objectSpecifier follows here */ + if (tvb_reported_length_remaining(tvb, offset) <= 0) + return offset; /* nothing more to decode left */ + + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_no != 0 || tag_info != 12) + return offset; /* no objectSpecifier */ + } + + continue; + } + + switch (tag_no) { + case 0: /* objectSpecifier */ + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + break; + case 1: /* list of Results */ + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, "listOfResults"); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + case 2: /* propertyIdentifier */ + offset = fPropertyIdentifierValue(tvb, pinfo, subtree, offset, 2); + break; + case 5: /* propertyAccessError */ + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "propertyAccessError"); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + /* Error Code follows */ + offset = fError(tvb, pinfo, subtree, offset); + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } + else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + + +static guint +fReadPropertyConditionalAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + /* listOfReadAccessResults */ + return fReadAccessResult(tvb, pinfo, tree, offset); +} + + +static guint +fCreateObjectRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *subtree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + + if (tag_no < 2) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 0: /* objectSpecifier */ + switch (fTagNo(tvb, offset)) { /* choice of objectType or objectIdentifier */ + case 0: /* objectType */ + offset = fEnumeratedTagSplit(tvb, pinfo, subtree, offset, "Object Type: ", BACnetObjectType, 128); + break; + case 1: /* objectIdentifier */ + offset = fObjectIdentifier(tvb, pinfo, subtree, offset, "ObjectIdentifier: "); + break; + default: + break; + } + break; + case 1: /* propertyValue */ + if (tag_is_opening(tag_info)) { + offset = fBACnetPropertyValue(tvb, pinfo, subtree, offset); + } else { + expert_add_info(pinfo, subtree, &ei_bacapp_bad_tag); + } + break; + default: + break; + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fCreateObjectAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + return fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); +} + +static guint +fReadRangeRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + offset = fBACnetObjectPropertyReference(tvb, pinfo, subtree, offset); + + if (tvb_reported_length_remaining(tvb, offset) > 0) { + /* optional range choice */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, + val_to_str_const(tag_no, BACnetReadRangeOptions, "unknown range option")); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 3: /* range byPosition */ + case 6: /* range bySequenceNumber, 2004 spec */ + offset = fApplicationTypes(tvb, pinfo, subtree, offset, "reference Index: "); + offset = fApplicationTypes(tvb, pinfo, subtree, offset, "reference Count: "); + break; + case 4: /* range byTime - deprecated in 2004 */ + case 7: /* 2004 spec */ + offset = fDateTime(tvb, pinfo, subtree, offset, "reference Date/Time: "); + offset = fApplicationTypes(tvb, pinfo, subtree, offset, "reference Count: "); + break; + case 5: /* range timeRange - deprecated in 2004 */ + offset = fDateTime(tvb, pinfo, subtree, offset, "beginning Time: "); + offset = fDateTime(tvb, pinfo, subtree, offset, "ending Time: "); + break; + default: + break; + } + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } + } + return offset; +} + +static guint +fReadRangeAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + /* set the optional global properties to indicate not-used */ + propertyArrayIndex = -1; + /* objectIdentifier, propertyIdentifier, and + OPTIONAL propertyArrayIndex */ + offset = fBACnetObjectPropertyReference(tvb, pinfo, subtree, offset); + /* resultFlags => BACnetResultFlags ::= BIT STRING */ + offset = fBitStringTagVS(tvb, pinfo, tree, offset, + "resultFlags: ", + BACnetResultFlags); + /* itemCount */ + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "item Count: "); + /* itemData */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_opening(tag_info)) { + col_set_writable(pinfo->cinfo, COL_INFO, FALSE); /* don't set all infos into INFO column */ + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, "itemData"); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType(tvb, pinfo, subtree, offset); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } + /* firstSequenceNumber - OPTIONAL */ + if (tvb_reported_length_remaining(tvb, offset) > 0) { + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "first Sequence Number: "); + } + + return offset; +} + +static guint +fAccessMethod(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint32 lvt; + guint8 tag_no, tag_info; + proto_tree* subtree = NULL; + + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(tree, tvb, offset, 1, ett_bacapp_value, NULL, + val_to_str_const(tag_no, BACnetFileAccessOption, "invalid access method")); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fApplicationTypes(tvb, pinfo, subtree, offset, val_to_str_const(tag_no, BACnetFileStartOption, "invalid option")); + offset = fApplicationTypes(tvb, pinfo, subtree, offset, val_to_str_const(tag_no, BACnetFileWriteInfo, "unknown option")); + + if (tag_no == 1) { + while ((tvb_reported_length_remaining(tvb, offset) > 0)&&(offset>lastoffset)) { + /* exit loop if nothing happens inside */ + lastoffset = offset; + offset = fApplicationTypes(tvb, pinfo, subtree, offset, "Record Data: "); + } + } + + if ((bacapp_flags & BACAPP_MORE_SEGMENTS) == 0) { + /* More Flag is not set, so we can look for closing tag in this segment */ + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } + } + } + return offset; +} + +static guint +fAccessRule(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + /* quit loop if we spot a closing tag */ + if (tag_is_closing(tag_info)) { + break; + } + + switch (tag_no) { + case 0: /* time-range-specifier */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "time-range-specifier: ", NULL); + break; + case 1: /* time-range */ + offset = fDeviceObjectPropertyReference(tvb, pinfo, tree, offset); + break; + case 2: /* location-specifier */ + offset = fEnumeratedTag(tvb, pinfo, tree, offset, "location-specifier: ", NULL); + break; + case 3: /* location */ + offset = fDeviceObjectReference(tvb, pinfo, tree, offset); + break; + case 4: /* enable */ + offset = fBooleanTag(tvb, pinfo, tree, offset, "enable: "); + break; + default: + break; + } + + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + + return offset; +} + +static guint +fAtomicReadFileRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no, tag_info; + guint32 lvt; + proto_tree *subtree = tree; + + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); + + fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_bacapp_value, NULL, + val_to_str_const(tag_no, BACnetFileAccessOption, "unknown access method")); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fSignedTag(tvb, pinfo, subtree, offset, val_to_str_const(tag_no, BACnetFileStartOption, "unknown option")); + offset = fUnsignedTag(tvb, pinfo, subtree, offset, val_to_str_const(tag_no, BACnetFileRequestCount, "unknown option")); + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + } + return offset; +} + +static guint +fAtomicWriteFileRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + + offset = fObjectIdentifier(tvb, pinfo, tree, offset, "ObjectIdentifier: "); /* file Identifier */ + offset = fAccessMethod(tvb, pinfo, tree, offset); + + return offset; +} + +static guint +fAtomicWriteFileAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint tag_no = fTagNo(tvb, offset); + return fSignedTag(tvb, pinfo, tree, offset, val_to_str_const(tag_no, BACnetFileStartOption, "unknown option")); +} + +static guint +fAtomicReadFileAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + offset = fApplicationTypes(tvb, pinfo, tree, offset, "End Of File: "); + offset = fAccessMethod(tvb, pinfo, tree, offset); + + return offset; +} + +static guint +fReadPropertyMultipleRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *subtree, guint offset) +{ + col_set_writable(pinfo->cinfo, COL_INFO, FALSE); /* don't set all infos into INFO column */ + return fReadAccessSpecification(tvb, pinfo, subtree, offset); +} + +static guint +fReadPropertyMultipleAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + col_set_writable(pinfo->cinfo, COL_INFO, FALSE); /* don't set all infos into INFO column */ + return fReadAccessResult(tvb, pinfo, tree, offset); +} + +static guint +fConfirmedServiceRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, gint service_choice) +{ + if (tvb_reported_length_remaining(tvb, offset) <= 0) + return offset; + + switch (service_choice) { + case 0: /* acknowledgeAlarm */ + offset = fAcknowledgeAlarmRequest(tvb, pinfo, tree, offset); + break; + case 1: /* confirmedCOVNotification */ + offset = fConfirmedCOVNotificationRequest(tvb, pinfo, tree, offset); + break; + case 2: /* confirmedEventNotification */ + offset = fConfirmedEventNotificationRequest(tvb, pinfo, tree, offset); + break; + case 3: /* confirmedGetAlarmSummary conveys no parameters */ + break; + case 4: /* getEnrollmentSummaryRequest */ + offset = fGetEnrollmentSummaryRequest(tvb, pinfo, tree, offset); + break; + case 5: /* subscribeCOVRequest */ + offset = fSubscribeCOVRequest(tvb, pinfo, tree, offset); + break; + case 6: /* atomicReadFile-Request */ + offset = fAtomicReadFileRequest(tvb, pinfo, tree, offset); + break; + case 7: /* atomicWriteFile-Request */ + offset = fAtomicWriteFileRequest(tvb, pinfo, tree, offset); + break; + case 8: /* AddListElement-Request */ + offset = fAddListElementRequest(tvb, pinfo, tree, offset); + break; + case 9: /* removeListElement-Request */ + offset = fRemoveListElementRequest(tvb, pinfo, tree, offset); + break; + case 10: /* createObjectRequest */ + offset = fCreateObjectRequest(tvb, pinfo, tree, offset); + break; + case 11: /* deleteObject */ + offset = fDeleteObjectRequest(tvb, pinfo, tree, offset); + break; + case 12: + offset = fReadPropertyRequest(tvb, pinfo, tree, offset); + break; + case 13: + offset = fReadPropertyConditionalRequest(tvb, pinfo, tree, offset); + break; + case 14: + offset = fReadPropertyMultipleRequest(tvb, pinfo, tree, offset); + break; + case 15: + offset = fWritePropertyRequest(tvb, pinfo, tree, offset); + break; + case 16: + offset = fWritePropertyMultipleRequest(tvb, pinfo, tree, offset); + break; + case 17: + offset = fDeviceCommunicationControlRequest(tvb, pinfo, tree, offset); + break; + case 18: + offset = fConfirmedPrivateTransferRequest(tvb, pinfo, tree, offset); + break; + case 19: + offset = fConfirmedTextMessageRequest(tvb, pinfo, tree, offset); + break; + case 20: + offset = fReinitializeDeviceRequest(tvb, pinfo, tree, offset); + break; + case 21: + offset = fVtOpenRequest(tvb, pinfo, tree, offset); + break; + case 22: + offset = fVtCloseRequest(tvb, pinfo, tree, offset); + break; + case 23: + offset = fVtDataRequest(tvb, pinfo, tree, offset); + break; + case 24: + offset = fAuthenticateRequest(tvb, pinfo, tree, offset); + break; + case 25: + offset = fRequestKeyRequest(tvb, pinfo, tree, offset); + break; + case 26: + offset = fReadRangeRequest(tvb, pinfo, tree, offset); + break; + case 27: + offset = fLifeSafetyOperationRequest(tvb, pinfo, tree, offset, NULL); + break; + case 28: + offset = fSubscribeCOVPropertyRequest(tvb, pinfo, tree, offset); + break; + case 29: + offset = fGetEventInformationRequest(tvb, pinfo, tree, offset); + break; + case 30: + offset = fSubscribeCOVPropertyMultipleRequest(tvb, pinfo, tree, offset); + break; + case 31: + offset = fConfirmedCOVNotificationMultipleRequest(tvb, pinfo, tree, offset); + break; + case 32: + offset = fConfirmedAuditNotificationRequest(tvb, pinfo, tree, offset); + break; + case 33: + offset = fAuditLogQueryRequest(tvb, pinfo, tree, offset); + break; + default: + return offset; + } + return offset; +} + +static guint +fConfirmedServiceAck(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, gint service_choice) +{ + if (tvb_reported_length_remaining(tvb, offset) <= 0) + return offset; + + switch (service_choice) { + case 3: /* confirmedEventNotificationAck */ + offset = fGetAlarmSummaryAck(tvb, pinfo, tree, offset); + break; + case 4: /* getEnrollmentSummaryAck */ + offset = fGetEnrollmentSummaryAck(tvb, pinfo, tree, offset); + break; + case 6: /* atomicReadFile */ + offset = fAtomicReadFileAck(tvb, pinfo, tree, offset); + break; + case 7: /* atomicReadFileAck */ + offset = fAtomicWriteFileAck(tvb, pinfo, tree, offset); + break; + case 10: /* createObject */ + offset = fCreateObjectAck(tvb, pinfo, tree, offset); + break; + case 12: + offset = fReadPropertyAck(tvb, pinfo, tree, offset); + break; + case 13: + offset = fReadPropertyConditionalAck(tvb, pinfo, tree, offset); + break; + case 14: + offset = fReadPropertyMultipleAck(tvb, pinfo, tree, offset); + break; + case 18: + offset = fConfirmedPrivateTransferAck(tvb, pinfo, tree, offset); + break; + case 21: + offset = fVtOpenAck(tvb, pinfo, tree, offset); + break; + case 23: + offset = fVtDataAck(tvb, pinfo, tree, offset); + break; + case 24: + offset = fAuthenticateAck(tvb, pinfo, tree, offset); + break; + case 26: + offset = fReadRangeAck(tvb, pinfo, tree, offset); + break; + case 29: + offset = fGetEventInformationACK(tvb, pinfo, tree, offset); + break; + case 33: + offset = fAuditLogQueryAck(tvb, pinfo, tree, offset); + break; + default: + return offset; + } + return offset; +} + +static guint +fIAmRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + /* BACnetObjectIdentifier */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, "BACnet Object Identifier: "); + + /* MaxAPDULengthAccepted */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Maximum ADPU Length Accepted: "); + + /* segmentationSupported */ + offset = fApplicationTypesEnumerated(tvb, pinfo, tree, offset, + "Segmentation Supported: ", BACnetSegmentation); + + /* vendor ID */ + return fVendorIdentifier(tvb, pinfo, tree, offset); +} + +static guint +fIHaveRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + /* BACnetDeviceIdentifier */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Device Identifier: "); + + /* BACnetObjectIdentifier */ + offset = fApplicationTypes(tvb, pinfo, tree, offset, "Object Identifier: "); + + /* ObjectName */ + return fObjectName(tvb, pinfo, tree, offset); +} + +static guint +fWhoIsRequest(tvbuff_t *tvb, packet_info* pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint val; + guint8 tag_len; + + guint8 tag_no, tag_info; + guint32 lvt; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + + switch (tag_no) { + case 0: + /* DeviceInstanceRangeLowLimit Optional */ + if (fUnsigned32(tvb, offset+tag_len, lvt, &val)) + col_append_fstr(pinfo->cinfo, COL_INFO, "%d ", val); + offset = fDevice_Instance(tvb, pinfo, tree, offset, + hf_Device_Instance_Range_Low_Limit); + break; + case 1: + /* DeviceInstanceRangeHighLimit Optional but + required if DeviceInstanceRangeLowLimit is there */ + if (fUnsigned32(tvb, offset+tag_len, lvt, &val)) + col_append_fstr(pinfo->cinfo, COL_INFO, "%d ", val); + offset = fDevice_Instance(tvb, pinfo, tree, offset, + hf_Device_Instance_Range_High_Limit); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fUnconfirmedServiceRequest(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, gint service_choice) +{ + if (tvb_reported_length_remaining(tvb, offset) <= 0) + return offset; + + switch (service_choice) { + case 0: /* I-Am-Request */ + offset = fIAmRequest(tvb, pinfo, tree, offset); + break; + case 1: /* i-Have Request */ + offset = fIHaveRequest(tvb, pinfo, tree, offset); + break; + case 2: /* unconfirmedCOVNotification */ + offset = fUnconfirmedCOVNotificationRequest(tvb, pinfo, tree, offset); + break; + case 3: /* unconfirmedEventNotification */ + offset = fUnconfirmedEventNotificationRequest(tvb, pinfo, tree, offset); + break; + case 4: /* unconfirmedPrivateTransfer */ + offset = fUnconfirmedPrivateTransferRequest(tvb, pinfo, tree, offset); + break; + case 5: /* unconfirmedTextMessage */ + offset = fUnconfirmedTextMessageRequest(tvb, pinfo, tree, offset); + break; + case 6: /* timeSynchronization */ + offset = fTimeSynchronizationRequest(tvb, pinfo, tree, offset); + break; + case 7: /* who-Has */ + offset = fWhoHas(tvb, pinfo, tree, offset); + break; + case 8: /* who-Is */ + offset = fWhoIsRequest(tvb, pinfo, tree, offset); + break; + case 9: /* utcTimeSynchronization */ + offset = fUTCTimeSynchronizationRequest(tvb, pinfo, tree, offset); + break; + case 10: + offset = fWriteGroupRequest(tvb, pinfo, tree, offset); + break; + case 11: + offset = fUnconfirmedCOVNotificationMultipleRequest(tvb, pinfo, tree, offset); + break; + case 12: + offset = fUnconfirmedAuditNotificationRequest(tvb, pinfo, tree, offset); + break; + case 13: + offset = fWhoAmIRequest(tvb, pinfo, tree, offset); + break; + case 14: + offset = fYouAreRequest(tvb, pinfo, tree, offset); + break; + default: + break; + } + return offset; +} + +static guint +fStartConfirmed(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *bacapp_tree, guint offset, guint8 ack, + gint *svc, proto_item **tt) +{ + proto_item *tc; + proto_tree *bacapp_tree_control; + gint tmp; + guint extra = 2; + + bacapp_seq = 0; + tmp = tvb_get_gint8(tvb, offset); + bacapp_flags = tmp & 0x0f; + + if (ack == 0) { + extra = 3; + } + *svc = tvb_get_gint8(tvb, offset+extra); + if (bacapp_flags & 0x08) + *svc = tvb_get_gint8(tvb, offset+extra+2); + + proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset, 1, ENC_BIG_ENDIAN); + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_pduflags, tvb, offset, 1, ENC_BIG_ENDIAN); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp_control); + + proto_tree_add_item(bacapp_tree_control, hf_bacapp_SEG, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_MOR, tvb, offset, 1, ENC_BIG_ENDIAN); + if (ack == 0) { /* The following are for ConfirmedRequest, not Complex ack */ + proto_tree_add_item(bacapp_tree_control, hf_bacapp_SA, tvb, offset++, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(bacapp_tree, hf_bacapp_response_segments, tvb, + offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(bacapp_tree, hf_bacapp_max_adpu_size, tvb, + offset, 1, ENC_BIG_ENDIAN); + } + offset++; + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, offset++, 1, ENC_BIG_ENDIAN); + if (bacapp_flags & 0x08) { + bacapp_seq = tvb_get_guint8(tvb, offset); + proto_tree_add_item(bacapp_tree, hf_bacapp_sequence_number, tvb, + offset++, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(bacapp_tree, hf_bacapp_window_size, tvb, + offset++, 1, ENC_BIG_ENDIAN); + } + *tt = proto_tree_add_item(bacapp_tree, hf_bacapp_service, tvb, + offset++, 1, ENC_BIG_ENDIAN); + return offset; +} + +static guint +fContinueConfirmedRequestPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bacapp_tree, guint offset, gint svc) +{ /* BACnet-Confirmed-Request */ + /* ASHRAE 135-2001 20.1.2 */ + + return fConfirmedServiceRequest(tvb, pinfo, bacapp_tree, offset, svc); +} + +static guint +fConfirmedRequestPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Confirmed-Request */ + /* ASHRAE 135-2001 20.1.2 */ + gint svc; + proto_item *tt = 0; + + offset = fStartConfirmed(tvb, pinfo, bacapp_tree, offset, 0, &svc, &tt); + return fContinueConfirmedRequestPDU(tvb, pinfo, bacapp_tree, offset, svc); +} + +static guint +fUnconfirmedRequestPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Unconfirmed-Request-PDU */ + /* ASHRAE 135-2001 20.1.3 */ + + gint tmp; + + proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset++, 1, ENC_BIG_ENDIAN); + + tmp = tvb_get_guint8(tvb, offset); + proto_tree_add_item(bacapp_tree, hf_bacapp_uservice, tvb, + offset++, 1, ENC_BIG_ENDIAN); + /* Service Request follows... Variable Encoding 20.2ff */ + return fUnconfirmedServiceRequest(tvb, pinfo, bacapp_tree, offset, tmp); +} + +static guint +fSimpleAckPDU(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Simple-Ack-PDU */ + /* ASHRAE 135-2001 20.1.4 */ + + proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset++, 1, ENC_BIG_ENDIAN); + + proto_tree_add_item(bacapp_tree, hf_bacapp_invoke_id, tvb, + offset++, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(bacapp_tree, hf_bacapp_service, tvb, + offset++, 1, ENC_BIG_ENDIAN); + + return offset; +} + +static guint +fContinueComplexAckPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bacapp_tree, guint offset, gint svc) +{ /* BACnet-Complex-Ack-PDU */ + /* ASHRAE 135-2001 20.1.5 */ + + /* Service ACK follows... */ + return fConfirmedServiceAck(tvb, pinfo, bacapp_tree, offset, svc); +} + +static guint +fComplexAckPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Complex-Ack-PDU */ + /* ASHRAE 135-2001 20.1.5 */ + gint svc; + proto_item *tt = 0; + + offset = fStartConfirmed(tvb, pinfo, bacapp_tree, offset, 1, &svc, &tt); + return fContinueComplexAckPDU(tvb, pinfo, bacapp_tree, offset, svc); +} + +static guint +fSegmentAckPDU(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-SegmentAck-PDU */ + /* ASHRAE 135-2001 20.1.6 */ + + proto_item *tc; + proto_tree *bacapp_tree_control; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset, 1, ENC_BIG_ENDIAN); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp); + + proto_tree_add_item(bacapp_tree_control, hf_bacapp_NAK, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_SRV, tvb, offset++, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_invoke_id, tvb, + offset++, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_sequence_number, tvb, + offset++, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_window_size, tvb, + offset++, 1, ENC_BIG_ENDIAN); + return offset; +} + +static guint +fContextTaggedError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_info = 0; + guint8 parsed_tag = 0; + guint32 lvt = 0; + + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &parsed_tag, &tag_info, &lvt); + offset = fError(tvb, pinfo, tree, offset); + return offset + fTagHeaderTree(tvb, pinfo, tree, offset, &parsed_tag, &tag_info, &lvt); +} + +static guint +fConfirmedPrivateTransferError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + proto_tree *subtree = tree; + + guint vendor_identifier = 0; + guint service_number = 0; + guint8 tag_len = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + /* exit loop if nothing happens inside */ + lastoffset = offset; + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + switch (tag_no) { + case 0: /* errorType */ + offset = fContextTaggedError(tvb, pinfo, subtree, offset); + break; + case 1: /* vendorID */ + fUnsigned32(tvb, offset+tag_len, lvt, &vendor_identifier); + col_append_fstr(pinfo->cinfo, COL_INFO, "V=%u ", vendor_identifier); + offset = fVendorIdentifier(tvb, pinfo, subtree, offset); + break; + case 2: /* serviceNumber */ + fUnsigned32(tvb, offset+tag_len, lvt, &service_number); + col_append_fstr(pinfo->cinfo, COL_INFO, "SN=%u ", service_number); + offset = fUnsignedTag(tvb, pinfo, subtree, offset, "service Number: "); + break; + case 3: /* errorParameters */ + if (tag_is_opening(tag_info)) { + subtree = proto_tree_add_subtree(subtree, tvb, offset, 1, + ett_bacapp_value, NULL, "error Parameters"); + propertyIdentifier = -1; + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset = fAbstractSyntaxNType(tvb, pinfo, subtree, offset); + } else if (tag_is_closing(tag_info)) { + offset += fTagHeaderTree(tvb, pinfo, subtree, offset, + &tag_no, &tag_info, &lvt); + subtree = tree; + } else { + /* error condition: let caller handle */ + return offset; + } + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fCreateObjectError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* errorType */ + offset = fContextTaggedError(tvb, pinfo, tree, offset); + break; + case 1: /* firstFailedElementNumber */ + offset = fUnsignedTag(tvb, pinfo, tree, offset, "first failed element number: "); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fChangeListError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + /* Identical to CreateObjectError */ + return fCreateObjectError(tvb, pinfo, tree, offset); +} + +static guint +fVTCloseError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + + if (fTagNo(tvb, offset) == 0) { + /* errorType */ + offset = fContextTaggedError(tvb, pinfo, tree, offset); + if (fTagNo(tvb, offset) == 1) { + /* listOfVTSessionIdentifiers [OPTIONAL] */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fVtCloseRequest(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + } + } + /* should report bad packet if initial tag wasn't 0 */ + return offset; +} + +static guint +fWritePropertyMultipleError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint lastoffset = 0; + guint8 tag_no = 0, tag_info = 0; + guint32 lvt = 0; + + col_set_writable(pinfo->cinfo, COL_INFO, FALSE); /* don't set all infos into INFO column */ + while (tvb_reported_length_remaining(tvb, offset) > 0) { /* exit loop if nothing happens inside */ + lastoffset = offset; + switch (fTagNo(tvb, offset)) { + case 0: /* errorType */ + offset = fContextTaggedError(tvb, pinfo, tree, offset); + break; + case 1: /* firstFailedWriteAttempt */ + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + offset = fBACnetObjectPropertyReference(tvb, pinfo, tree, offset); + offset += fTagHeaderTree(tvb, pinfo, tree, offset, &tag_no, &tag_info, &lvt); + break; + default: + return offset; + } + if (offset <= lastoffset) break; /* nothing happened, exit loop */ + } + return offset; +} + +static guint +fErrorClass(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint32 val = 0, lvt; + guint8 tag_no, tag_info; + proto_item *ti; + proto_tree *subtree; + guint tag_len; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32(tvb, offset+tag_len, lvt, &val)) + { + ti = proto_tree_add_uint(tree, hf_bacapp_error_class, + tvb, offset, lvt+tag_len, val); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + } + else + { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "Error Class - %u octets (Signed)", lvt); + } + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset += tag_len + lvt; + + return offset; +} + +static guint +fErrorCode(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + guint32 val = 0, lvt; + guint8 tag_no, tag_info; + proto_item *ti; + proto_tree *subtree; + guint tag_len; + + tag_len = fTagHeader(tvb, pinfo, offset, &tag_no, &tag_info, &lvt); + if (fUnsigned32(tvb, offset+tag_len, lvt, &val)) + { + ti = proto_tree_add_uint(tree, hf_bacapp_error_code, + tvb, offset, lvt+tag_len, val); + subtree = proto_item_add_subtree(ti, ett_bacapp_tag); + } + else + { + subtree = proto_tree_add_subtree_format(tree, tvb, offset, lvt+tag_len, + ett_bacapp_tag, NULL, "Error Code - %u octets (Signed)", lvt); + } + fTagHeaderTree(tvb, pinfo, subtree, offset, &tag_no, &tag_info, &lvt); + offset += tag_len + lvt; + + return offset; +} + +static guint +fError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset) +{ + offset = fErrorClass(tvb, pinfo, tree, offset); + + return fErrorCode(tvb, pinfo, tree, offset); +} + +static guint +fBACnetError(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset, guint service) +{ + switch (service) { + case 8: + offset = fChangeListError(tvb, pinfo, tree, offset); + break; + case 9: + offset = fChangeListError(tvb, pinfo, tree, offset); + break; + case 10: + offset = fCreateObjectError(tvb, pinfo, tree, offset); + break; + case 16: + offset = fWritePropertyMultipleError(tvb, pinfo, tree, offset); + break; + case 18: + offset = fConfirmedPrivateTransferError(tvb, pinfo, tree, offset); + break; + case 22: + offset = fVTCloseError(tvb, pinfo, tree, offset); + break; + case 30: + offset = fSubscribeCOVPropertyMultipleError(tvb, pinfo, tree, offset); + break; + default: + offset = fError(tvb, pinfo, tree, offset); + break; + } + return offset; +} + +static guint +fErrorPDU(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Error-PDU */ + /* ASHRAE 135-2001 20.1.7 */ + + proto_item *tc; + proto_tree *bacapp_tree_control; + guint8 tmp; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset++, 1, ENC_BIG_ENDIAN); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp); + + proto_tree_add_item(bacapp_tree_control, hf_bacapp_invoke_id, tvb, + offset++, 1, ENC_BIG_ENDIAN); + tmp = tvb_get_guint8(tvb, offset); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_service, tvb, + offset++, 1, ENC_BIG_ENDIAN); + /* Error Handling follows... */ + return fBACnetError(tvb, pinfo, bacapp_tree, offset, tmp); +} + +static guint +fRejectPDU(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Reject-PDU */ + /* ASHRAE 135-2001 20.1.8 */ + + proto_item *tc; + proto_tree *bacapp_tree_control; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset++, 1, ENC_BIG_ENDIAN); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp); + + proto_tree_add_item(bacapp_tree_control, hf_bacapp_invoke_id, tvb, + offset++, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(bacapp_tree_control, hf_BACnetRejectReason, tvb, + offset++, 1, ENC_BIG_ENDIAN); + return offset; +} + +static guint +fAbortPDU(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *bacapp_tree, guint offset) +{ /* BACnet-Abort-PDU */ + /* ASHRAE 135-2001 20.1.9 */ + + proto_item *tc; + proto_tree *bacapp_tree_control; + + tc = proto_tree_add_item(bacapp_tree, hf_bacapp_type, tvb, offset, 1, ENC_BIG_ENDIAN); + bacapp_tree_control = proto_item_add_subtree(tc, ett_bacapp); + + proto_tree_add_item(bacapp_tree_control, hf_bacapp_SRV, tvb, offset++, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(bacapp_tree_control, hf_bacapp_invoke_id, tvb, + offset++, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(bacapp_tree_control, hf_BACnetAbortReason, tvb, + offset++, 1, ENC_BIG_ENDIAN); + return offset; +} + +static guint +do_the_dissection(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + guint8 flag, bacapp_type; + guint offset = 0; + + flag = tvb_get_gint8(tvb, 0); + bacapp_type = (flag >> 4) & 0x0f; + + if (tvb == NULL) { + return 0; + } + + /* ASHRAE 135-2001 20.1.1 */ + switch (bacapp_type) { + case BACAPP_TYPE_CONFIRMED_SERVICE_REQUEST: /* BACnet-Confirmed-Service-Request */ + offset = fConfirmedRequestPDU(tvb, pinfo, tree, offset); + break; + case BACAPP_TYPE_UNCONFIRMED_SERVICE_REQUEST: /* BACnet-Unconfirmed-Request-PDU */ + offset = fUnconfirmedRequestPDU(tvb, pinfo, tree, offset); + break; + case BACAPP_TYPE_SIMPLE_ACK: /* BACnet-Simple-Ack-PDU */ + offset = fSimpleAckPDU(tvb, pinfo, tree, offset); + break; + case BACAPP_TYPE_COMPLEX_ACK: /* BACnet-Complex-Ack-PDU */ + offset = fComplexAckPDU(tvb, pinfo, tree, offset); + break; + case BACAPP_TYPE_SEGMENT_ACK: /* BACnet-SegmentAck-PDU */ + offset = fSegmentAckPDU(tvb, pinfo, tree, offset); + break; + case BACAPP_TYPE_ERROR: /* BACnet-Error-PDU */ + offset = fErrorPDU(tvb, pinfo, tree, offset); + break; + case BACAPP_TYPE_REJECT: /* BACnet-Reject-PDU */ + offset = fRejectPDU(tvb, pinfo, tree, offset); + break; + case BACAPP_TYPE_ABORT: /* BACnet-Abort-PDU */ + offset = fAbortPDU(tvb, pinfo, tree, offset); + break; + } + return offset; +} + +static int +dissect_bacapp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + guint8 flag, bacapp_type; + guint save_fragmented = FALSE, data_offset = 0, /*bacapp_apdu_size,*/ fragment = FALSE; + tvbuff_t *new_tvb = NULL; + guint offset = 0; + guint8 bacapp_seqno = 0; + guint8 bacapp_service, bacapp_reason/*, bacapp_prop_win_size*/; + guint8 bacapp_invoke_id = 0; + proto_item *ti; + proto_tree *bacapp_tree = NULL; + + gint svc = 0; + proto_item *tt = 0; + gint8 ack = 0; + + /* Strings for BACnet Statistics */ + const gchar errstr[] = "ERROR: "; + const gchar rejstr[] = "REJECTED: "; + const gchar abortstr[] = "ABORTED: "; + const gchar sackstr[] = " (SimpleAck)"; + const gchar cackstr[] = " (ComplexAck)"; + const gchar uconfsreqstr[] = " (Unconfirmed Service Request)"; + const gchar confsreqstr[] = " (Confirmed Service Request)"; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet-APDU"); + col_clear(pinfo->cinfo, COL_INFO); + + flag = tvb_get_guint8(tvb, 0); + bacapp_type = (flag >> 4) & 0x0f; + + /* show some descriptive text in the INFO column */ + col_add_fstr(pinfo->cinfo, COL_INFO, "%-16s", + val_to_str_const(bacapp_type, BACnetTypeName, "# unknown APDU #")); + + bacinfo.service_type = NULL; + bacinfo.invoke_id = NULL; + bacinfo.instance_ident = NULL; + bacinfo.object_ident = NULL; + + switch (bacapp_type) { + case BACAPP_TYPE_CONFIRMED_SERVICE_REQUEST: + /* segmented messages have 2 additional bytes */ + if (flag & BACAPP_SEGMENTED_REQUEST) { + fragment = TRUE; + ack = 0; + /* bacapp_apdu_size = fGetMaxAPDUSize(tvb_get_guint8(tvb, offset + 1)); */ /* has 16 values, reserved are 50 Bytes */ + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 2); + bacapp_seqno = tvb_get_guint8(tvb, offset + 3); + /* bacapp_prop_win_size = tvb_get_guint8(tvb, offset + 4); */ + bacapp_service = tvb_get_guint8(tvb, offset + 5); + data_offset = 6; + } else { + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 2); + bacapp_service = tvb_get_guint8(tvb, offset + 3); + } + col_append_fstr(pinfo->cinfo, COL_INFO, "%s[%3u] ", + val_to_str_const(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str), + bacapp_invoke_id); + + updateBacnetInfoValue(BACINFO_INVOKEID, + wmem_strdup_printf(pinfo->pool, "Invoke ID: %d", bacapp_invoke_id)); + + updateBacnetInfoValue(BACINFO_SERVICE, + wmem_strconcat(pinfo->pool, + val_to_str_const(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str), + confsreqstr, NULL)); + break; + case BACAPP_TYPE_UNCONFIRMED_SERVICE_REQUEST: + bacapp_service = tvb_get_guint8(tvb, offset + 1); + col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", + val_to_str_const(bacapp_service, + BACnetUnconfirmedServiceChoice, + bacapp_unknown_service_str)); + + updateBacnetInfoValue(BACINFO_SERVICE, + wmem_strconcat(pinfo->pool, + val_to_str_const(bacapp_service, + BACnetUnconfirmedServiceChoice, + bacapp_unknown_service_str), + uconfsreqstr, NULL)); + break; + case BACAPP_TYPE_SIMPLE_ACK: + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_service = tvb_get_guint8(tvb, offset + 2); + col_append_fstr(pinfo->cinfo, COL_INFO, "%s[%3u] ", /* "original-invokeID" replaced */ + val_to_str_const(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str), + bacapp_invoke_id); + + updateBacnetInfoValue(BACINFO_INVOKEID, + wmem_strdup_printf(pinfo->pool, + "Invoke ID: %d", bacapp_invoke_id)); + + updateBacnetInfoValue(BACINFO_SERVICE, + wmem_strconcat(pinfo->pool, + val_to_str_const(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str), + sackstr, NULL)); + break; + case BACAPP_TYPE_COMPLEX_ACK: + /* segmented messages have 2 additional bytes */ + if (flag & BACAPP_SEGMENTED_REQUEST) { + fragment = TRUE; + ack = 1; + /* bacapp_apdu_size = fGetMaxAPDUSize(0); */ /* has minimum of 50 Bytes */ + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_seqno = tvb_get_guint8(tvb, offset + 2); + /* bacapp_prop_win_size = tvb_get_guint8(tvb, offset + 3); */ + bacapp_service = tvb_get_guint8(tvb, offset + 4); + data_offset = 5; + } else { + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_service = tvb_get_guint8(tvb, offset + 2); + } + col_append_fstr(pinfo->cinfo, COL_INFO, "%s[%3u] ", /* "original-invokeID" replaced */ + val_to_str_const(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str), + bacapp_invoke_id); + + updateBacnetInfoValue(BACINFO_INVOKEID, + wmem_strdup_printf(pinfo->pool, "Invoke ID: %d", bacapp_invoke_id)); + + updateBacnetInfoValue(BACINFO_SERVICE, + wmem_strconcat(pinfo->pool, + val_to_str_const(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str), + cackstr, NULL)); + break; + case BACAPP_TYPE_SEGMENT_ACK: + /* nothing more to add */ + break; + case BACAPP_TYPE_ERROR: + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_service = tvb_get_guint8(tvb, offset + 2); + col_append_fstr(pinfo->cinfo, COL_INFO, "%s[%3u] ", /* "original-invokeID" replaced */ + val_to_str_const(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str), + bacapp_invoke_id); + + updateBacnetInfoValue(BACINFO_INVOKEID, + wmem_strdup_printf(pinfo->pool, "Invoke ID: %d", bacapp_invoke_id)); + + updateBacnetInfoValue(BACINFO_SERVICE, + wmem_strconcat(pinfo->pool, + errstr, + val_to_str_const(bacapp_service, + BACnetConfirmedServiceChoice, + bacapp_unknown_service_str), + NULL)); + break; + case BACAPP_TYPE_REJECT: + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_reason = tvb_get_guint8(tvb, offset + 2); + col_append_fstr(pinfo->cinfo, COL_INFO, "%s[%3u] ", /* "original-invokeID" replaced */ + val_to_split_str(bacapp_reason, + 64, + BACnetRejectReason, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt), bacapp_invoke_id); + + updateBacnetInfoValue(BACINFO_INVOKEID, + wmem_strdup_printf(pinfo->pool, "Invoke ID: %d", bacapp_invoke_id)); + + updateBacnetInfoValue(BACINFO_SERVICE, + wmem_strconcat(pinfo->pool, rejstr, + val_to_split_str(bacapp_reason, 64, + BACnetRejectReason, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt), + NULL)); + break; + case BACAPP_TYPE_ABORT: + bacapp_invoke_id = tvb_get_guint8(tvb, offset + 1); + bacapp_reason = tvb_get_guint8(tvb, offset + 2); + col_append_fstr(pinfo->cinfo, COL_INFO, "%s[%3u] ", /* "original-invokeID" replaced */ + val_to_split_str(bacapp_reason, + 64, + BACnetAbortReason, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt), bacapp_invoke_id); + + updateBacnetInfoValue(BACINFO_INVOKEID, + wmem_strdup_printf(pinfo->pool, "Invoke ID: %d", bacapp_invoke_id)); + + updateBacnetInfoValue(BACINFO_SERVICE, + wmem_strconcat(pinfo->pool, abortstr, + val_to_split_str(bacapp_reason, + 64, + BACnetAbortReason, + ASHRAE_Reserved_Fmt, + Vendor_Proprietary_Fmt), + NULL)); + break; + /* UNKNOWN */ + default: + /* nothing more to add */ + break; + } + + save_fragmented = pinfo->fragmented; + + ti = proto_tree_add_item(tree, proto_bacapp, tvb, offset, -1, ENC_NA); + bacapp_tree = proto_item_add_subtree(ti, ett_bacapp); + + if (!fragment) + do_the_dissection(tvb, pinfo, bacapp_tree); + else + fStartConfirmed(tvb, pinfo, bacapp_tree, offset, ack, &svc, &tt); + /* not resetting the offset so the remaining can be done */ + + if (fragment) { /* fragmented */ + fragment_head *frag_msg; + + pinfo->fragmented = TRUE; + + frag_msg = fragment_add_seq_check(&msg_reassembly_table, + tvb, data_offset, + pinfo, + bacapp_invoke_id, /* ID for fragments belonging together */ + NULL, + bacapp_seqno, /* fragment sequence number */ + tvb_reported_length_remaining(tvb, data_offset), /* fragment length - to the end */ + flag & BACAPP_MORE_SEGMENTS); /* Last fragment reached? */ + new_tvb = process_reassembled_data(tvb, data_offset, pinfo, + "Reassembled BACapp", frag_msg, &msg_frag_items, + NULL, tree); + + if (new_tvb) { /* Reassembled */ + col_append_str(pinfo->cinfo, COL_INFO, + " (Message Reassembled)"); + } else { /* Not last packet of reassembled Short Message */ + col_append_fstr(pinfo->cinfo, COL_INFO, + " (Message fragment %u)", bacapp_seqno); + } + if (new_tvb) { /* take it all */ + switch (bacapp_type) { + case BACAPP_TYPE_CONFIRMED_SERVICE_REQUEST: + fContinueConfirmedRequestPDU(new_tvb, pinfo, bacapp_tree, 0, svc); + break; + case BACAPP_TYPE_COMPLEX_ACK: + fContinueComplexAckPDU(new_tvb, pinfo, bacapp_tree, 0, svc); + break; + default: + /* do nothing */ + break; + } + } + } + + pinfo->fragmented = save_fragmented; + + /* tapping */ + tap_queue_packet(bacapp_tap, pinfo, &bacinfo); + return tvb_captured_length(tvb); +} + +void +proto_register_bacapp(void) +{ + static hf_register_info hf[] = { + { &hf_bacapp_type, + { "APDU Type", "bacapp.type", + FT_UINT8, BASE_DEC, VALS(BACnetTypeName), 0xf0, NULL, HFILL } + }, + { &hf_bacapp_pduflags, + { "PDU Flags", "bacapp.pduflags", + FT_UINT8, BASE_HEX, NULL, 0x0f, NULL, HFILL } + }, + { &hf_bacapp_SEG, + { "Segmented Request", "bacapp.segmented_request", + FT_BOOLEAN, 8, TFS(&segments_follow), 0x08, NULL, HFILL } + }, + { &hf_bacapp_MOR, + { "More Segments", "bacapp.more_segments", + FT_BOOLEAN, 8, TFS(&more_follow), 0x04, "More Segments Follow", HFILL } + }, + { &hf_bacapp_SA, + { "SA", "bacapp.SA", + FT_BOOLEAN, 8, TFS(&segmented_accept), 0x02, "Segmented Response accepted", HFILL } + }, + { &hf_bacapp_max_adpu_size, + { "Size of Maximum ADPU accepted", "bacapp.max_adpu_size", + FT_UINT8, BASE_DEC, VALS(BACnetMaxAPDULengthAccepted), 0x0f, NULL, HFILL } + }, + { &hf_bacapp_response_segments, + { "Max Response Segments accepted", "bacapp.response_segments", + FT_UINT8, BASE_DEC, VALS(BACnetMaxSegmentsAccepted), 0x70, NULL, HFILL } + }, + { &hf_bacapp_objectType, + { "Object Type", "bacapp.objectType", + FT_UINT32, BASE_DEC, VALS(BACnetObjectType), 0xffc00000, NULL, HFILL } + }, + { &hf_bacapp_object_name, + { "Object Name", "bacapp.object_name", + FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_instanceNumber, + { "Instance Number", "bacapp.instance_number", + FT_UINT32, BASE_DEC, NULL, 0x003fffff, NULL, HFILL } + }, + { &hf_BACnetPropertyIdentifier, + { "Property Identifier", "bacapp.property_identifier", + FT_UINT32, BASE_DEC, VALS(BACnetPropertyIdentifier), 0, NULL, HFILL } + }, + { &hf_BACnetVendorIdentifier, + { "Vendor Identifier", "bacapp.vendor_identifier", + FT_UINT16, BASE_DEC|BASE_EXT_STRING, &BACnetVendorIdentifiers_ext, 0, NULL, HFILL } + }, + { &hf_BACnetRestartReason, + { "Restart Reason", "bacapp.restart_reason", + FT_UINT8, BASE_DEC, VALS(BACnetRestartReason), 0, NULL, HFILL } + }, + { &hf_bacapp_invoke_id, + { "Invoke ID", "bacapp.invoke_id", + FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_sequence_number, + { "Sequence Number", "bacapp.sequence_number", + FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_window_size, + { "Proposed Window Size", "bacapp.window_size", + FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_service, + { "Service Choice", "bacapp.confirmed_service", + FT_UINT8, BASE_DEC, VALS(BACnetConfirmedServiceChoice), 0x00, NULL, HFILL } + }, + { &hf_bacapp_uservice, + { "Unconfirmed Service Choice", "bacapp.unconfirmed_service", + FT_UINT8, BASE_DEC, VALS(BACnetUnconfirmedServiceChoice), 0x00, NULL, HFILL } + }, + { &hf_bacapp_NAK, + { "NAK", "bacapp.NAK", + FT_BOOLEAN, 8, NULL, 0x02, "negative ACK", HFILL } + }, + { &hf_bacapp_SRV, + { "SRV", "bacapp.SRV", + FT_BOOLEAN, 8, NULL, 0x01, "Server", HFILL } + }, + { &hf_bacapp_event_type, + { "Event Type", "bacapp.event_type", + FT_UINT32, BASE_DEC, VALS(BACnetEventType), 0, NULL, HFILL } + }, + { &hf_bacapp_notify_type, + { "Notify Type", "bacapp.notify_type", + FT_UINT8, BASE_DEC, VALS(BACnetNotifyType), 0, NULL, HFILL } + }, + { &hf_bacapp_error_class, + { "Error Class", "bacapp.error_class", + FT_UINT32, BASE_DEC, VALS(BACnetErrorClass), 0, NULL, HFILL } + }, + { &hf_bacapp_error_code, + { "Error Code", "bacapp.error_code", + FT_UINT32, BASE_DEC, VALS(BACnetErrorCode), 0, NULL, HFILL } + }, + { &hf_bacapp_present_value_null, + { "Present Value (null)", "bacapp.present_value.null", + FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_present_value_bool, + { "Present Value (bool)", "bacapp.present_value.boolean", + FT_BOOLEAN, 8, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_present_value_unsigned, + { "Present Value (uint)", "bacapp.present_value.uint", + FT_UINT64, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_present_value_signed, + { "Present Value (int)", "bacapp.present_value.int", + FT_INT64, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_present_value_real, + { "Present Value (real)", "bacapp.present_value.real", + FT_DOUBLE, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_present_value_double, + { "Present Value (double)", "bacapp.present_value.double", + FT_DOUBLE, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_present_value_octet_string, + { "Present Value (octet string)", "bacapp.present_value.octet_string", + FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_present_value_char_string, + { "Present Value (char string)", "bacapp.present_value.char_string", + FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_present_value_bit_string, + { "Present Value (bit string)", "bacapp.present_value.bit_string", + FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_present_value_enum_index, + { "Present Value (enum index)", "bacapp.present_value.enum_index", + FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_Device_Instance_Range_Low_Limit, + { "Device Instance Range Low Limit", "bacapp.who_is.low_limit", + FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_Device_Instance_Range_High_Limit, + { "Device Instance Range High Limit", "bacapp.who_is.high_limit", + FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_BACnetRejectReason, + { "Reject Reason", "bacapp.reject_reason", + FT_UINT8, BASE_DEC, VALS(BACnetRejectReason), 0x00, NULL, HFILL } + }, + { &hf_BACnetAbortReason, + { "Abort Reason", "bacapp.abort_reason", + FT_UINT8, BASE_DEC, VALS(BACnetAbortReason), 0x00, NULL, HFILL } + }, + { &hf_BACnetApplicationTagNumber, + { "Application Tag Number", + "bacapp.application_tag_number", + FT_UINT8, BASE_DEC, VALS(BACnetApplicationTagNumber), 0xF0, + NULL, HFILL } + }, + { &hf_BACnetContextTagNumber, + { "Context Tag Number", + "bacapp.context_tag_number", + FT_UINT8, BASE_DEC, NULL, 0xF0, + NULL, HFILL } + }, + { &hf_BACnetExtendedTagNumber, + { "Extended Tag Number", + "bacapp.extended_tag_number", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_BACnetNamedTag, + { "Named Tag", + "bacapp.named_tag", + FT_UINT8, BASE_DEC, VALS(BACnetTagNames), 0x07, + NULL, HFILL } + }, + { &hf_BACnetCharacterSet, + { "String Character Set", + "bacapp.string_character_set", + FT_UINT8, BASE_DEC, VALS(BACnetCharacterSet), 0, + NULL, HFILL } + }, + { &hf_BACnetCodePage, + { "Code Page", + "bacapp.code_page", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_BACnetTagClass, + { "Tag Class", "bacapp.tag_class", + FT_BOOLEAN, 8, TFS(&BACnetTagClass), 0x08, NULL, HFILL } + }, + { &hf_bacapp_tag_lvt, + { "Length Value Type", + "bacapp.LVT", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_bacapp_tag_ProcessId, + { "ProcessIdentifier", "bacapp.processId", + FT_UINT32, BASE_DEC, NULL, 0, "Process Identifier", HFILL } + }, + { &hf_bacapp_tag_to_state, + { "To State", "bacapp.to_state", + FT_UINT32, BASE_DEC, VALS(BACnetEventState), 0, NULL, HFILL } + }, + { &hf_bacapp_tag_from_state, + { "From State", "bacapp.from_state", + FT_UINT32, BASE_DEC, VALS(BACnetEventState), 0, NULL, HFILL } + }, + { &hf_bacapp_tag_IPV4, + { "IPV4", "bacapp.IPV4", + FT_IPv4, BASE_NONE, NULL, 0, "IP-Address", HFILL } + }, + { &hf_bacapp_tag_IPV6, + { "IPV6", "bacapp.IPV6", + FT_IPv6, BASE_NONE, NULL, 0, "IP-Address", HFILL } + }, + { &hf_bacapp_tag_PORT, + { "Port", "bacapp.Port", + FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_tag_mac_address_broadcast, + { "MAC-address: broadcast", "bacapp.mac_address_broadcast", + FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_reserved_ashrea, + { "reserved for ASHRAE", "bacapp.reserved_ashrea", + FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_unused_bits, + { "Unused bits", "bacapp.unused_bits", + FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_bit, + { "bit", "bacapp.bit", + FT_BOOLEAN, 8, NULL, 0, NULL, HFILL } + }, + { &hf_bacapp_complete_bitstring, + { "Complete bitstring", "bacapp.complete_bitstring", + FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } + }, + {&hf_msg_fragments, + { "Message fragments", "bacapp.fragments", + FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_msg_fragment, + { "Message fragment", "bacapp.fragment", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_msg_fragment_overlap, + { "Message fragment overlap", "bacapp.fragment.overlap", + FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_msg_fragment_overlap_conflicts, + { "Message fragment overlapping with conflicting data", + "bacapp.fragment.overlap.conflicts", + FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_msg_fragment_multiple_tails, + { "Message has multiple tail fragments", + "bacapp.fragment.multiple_tails", + FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_msg_fragment_too_long_fragment, + { "Message fragment too long", "bacapp.fragment.too_long_fragment", + FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_msg_fragment_error, + { "Message defragmentation error", "bacapp.fragment.error", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_msg_fragment_count, + { "Message fragment count", "bacapp.fragment.count", + FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, + {&hf_msg_reassembled_in, + { "Reassembled in", "bacapp.reassembled.in", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_msg_reassembled_length, + { "Reassembled BACapp length", "bacapp.reassembled.length", + FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } } + }; + static gint *ett[] = { + &ett_bacapp, + &ett_bacapp_control, + &ett_bacapp_tag, + &ett_bacapp_list, + &ett_bacapp_value, + &ett_msg_fragment, + &ett_msg_fragments + + }; + + static ei_register_info ei[] = { + { &ei_bacapp_bad_length, { "bacapp.bad_length", PI_MALFORMED, PI_ERROR, "Wrong length indicated", EXPFILL }}, + { &ei_bacapp_bad_tag, { "bacapp.bad_tag", PI_MALFORMED, PI_ERROR, "Wrong tag found", EXPFILL }}, + { &ei_bacapp_opening_tag, { "bacapp.bad_opening_tag", PI_MALFORMED, PI_ERROR, "Expected Opening Tag!", EXPFILL }}, + { &ei_bacapp_max_recursion_depth_reached, { "bacapp.max_recursion_depth_reached", + PI_PROTOCOL, PI_WARN, "Maximum allowed recursion depth reached. Dissection stopped.", EXPFILL }} + }; + + expert_module_t* expert_bacapp; + + proto_bacapp = proto_register_protocol("Building Automation and Control Network APDU", + "BACapp", "bacapp"); + + proto_register_field_array(proto_bacapp, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_bacapp = expert_register_protocol(proto_bacapp); + expert_register_field_array(expert_bacapp, ei, array_length(ei)); + register_dissector("bacapp", dissect_bacapp, proto_bacapp); + + reassembly_table_register(&msg_reassembly_table, + &addresses_reassembly_table_functions); + + bacapp_dissector_table = register_dissector_table("bacapp.vendor_identifier", + "BACapp Vendor Identifier", proto_bacapp, + FT_UINT8, BASE_HEX); + + /* Register BACnet Statistic trees */ + register_bacapp_stat_trees(); + bacapp_tap = register_tap("bacapp"); /* BACnet statistics tap */ +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ |