wireshark/epan/dissectors/packet-dof.c
Michael Mann 2eb7b05b8c Convert most UDP dissectors to use "auto" preferences.
Similar to the "tcp.port" changes in I99604f95d426ad345f4b494598d94178b886eb67,
convert dissectors that use "udp.port".

More cleanup done on dissectors that use both TCP and UDP dissector
tables, so that less preference callbacks exist.

Change-Id: If07be9b9e850c244336a7069599cd554ce312dd3
Reviewed-on: https://code.wireshark.org/review/18120
Petri-Dish: Michael Mann <mmann78@netscape.net>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Michael Mann <mmann78@netscape.net>
2016-10-13 02:51:18 +00:00

12640 lines
435 KiB
C

/* packet-dof.c
* Routines for Distributed Object Framework (DOF) Wireshark Support
* Copyright 2015 Bryant Eastham <bryant.eastham[AT]us.panasonic.com>
* See https://opendof.org for more information.
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* INTRODUCTION
* This very large dissector implements packet decoding for the entire
* protocol suite of the OpenDOF Project. The OpenDOF Project
* (https://opendof.org) is an open-source IoT platform with
* implementations in Java, C#, and C. The protocols are documented
* on the web site, and the IP ports referenced are registered with IANA.
*
* "DOF" stands for Distributed Object Framework. The protocols define
* a complete protocol stack that can sit on top of a variety of transports.
* The stack itself is called the DPS, or "DOF Protocol Stack". It
* is a layered stack including a Network, Presentation, and Application
* layer. The underlying transport can be anything, these dissectors
* hook in to UDP and TCP. To the Wireshark user, however, this is
* referred to as "dof" and not "dps".
*
* The following protocols are defined in the stack and implemented
* here:
* DNP - DOF Network Protocol (versions: 0, 1)
* DPP - DOF Presentation Protocol (version: 0, 2) [1 is reserved and not supported]
* DAP - DOF Application Protocols:
* DSP - DOF Session Protocol (versions: 0)
* OAP - Object Access Protocol (versions: 1)
* TEP - Ticket Exchange Protocol (versions: 128)
* TRP - Ticket Request Protocol (versions: 129)
* SGMP - Secure Group Management Protocol (versions: 130)
* DOFSEC - DOF Security Protocols:
* CCM - Chained mode
* TUN - A tunneling protocol for embedding DOF in other protocols.
*/
/* VERSIONS AND NAMING
* There are several different ways in which "versions" are used
* throughout the dissector. First, each of the DNP and DPP layers
* has a defined 'version'. The DOF Application Protocols are also
* distinguished by versioning, but it is actually the registered
* application ID that is the version. This is complicated by
* the fact that many of the application IDs represent the same
* version of a protocol from a capability perspective (and
* a user perspective) with the difference being some attribute
* of the protocol - for example the security primitives used.
*
* Another means of versioning is by specification document.
* In this case the document is identified by a year and sequence,
* with specifications and PDUs using a name and sequence.
* Naming of fields and variables will use these identifiers
* as they are the easiest way to tie the code to the specifications.
*
* The specification documents are also the easiest way (although
* maybe not the clearest) to expose fields to the Wireshar user.
* A consistent naming is used, which is:
*
* (spec)-pdu-(seq)-(field)
* For example: dof-2009-1-pdu-1-value
*
* Variable naming includes a protocol name to provide clarity.
*
* This is not the clearest from a user perspective, but it
* has the benefit of tying directly to the specifications
* themselves and uniquely identifies each field.
*
* Routines that dissect are uniformly named by the PDU
* that they dissect using the PDU specification document
* and PDU name from that document as follows:
*
* dissect_(spec)_(name)
*/
/* DISSECTOR DESIGN
* The original work on these dissectors began over ten years ago, but
* shared only within Panasonic. During the opening of the protocols in
* March of 2015 the decision was made to contribute the code to the Wireshark
* community. During this process the plugin approach was rejected and the
* entire set made into standard dissectors, and further to that all of the
* dissectors were merged into a single file.
*
* There are several types of supported dissectors that are part of the DPS family.
* At the lowest level are the transport dissectors. The responsibility
* of these dissectors is to determine the transport session information, pass
* DPS packets to the DPS dissector, and properly maintain the dof_api_data
* structure.
*
* The DPS dissector API comprises:
* 1. The structure (dof_api_data) that is passed in the data field to the DPS
* dissector. Transport plugins must understand this.
* 2. The dof_transport_session structure, which contains all transport
* information that is passed to the DPS dissector.
* 3. The name of the DPS dissector.
*
* The DPS dissector API extends to dissectors that are called by the DPS dissectors.
*
* Finally, there is the DPS Security Mode dissectors. These dissectors are passed
* additional security context information and it is their job to decrypt packets
* and pass them to higher-level dissectors.
*
* The DOF Protocol Stack is strictly layered with minimal (and defined) state
* exchanged between layers. This allows a fairly structured design, using
* dissector tables at each layer. The main DPS dissector receives packets
* from the transport hooks, and then dissects the packet layer by layer using
* the different dissector tables. Dissectors and the DNP, DPP, and DAP layers.
*
* In addition to the main protocol stack with its associated protocols there are
* additional common data elements that include extensibility. If an extension
* is found then it will be used to dissect, otherwise the base dissector will be
* used.
*/
/* SESSIONS
* DOF defines sessions at many different levels, and state is always associated
* with a session. Much of the power (and complexity) of these dissectors relates
* to accurately tracking and maintaining context for each session, and displaying
* context-related decode information based on the session. This includes, for
* example, decoding encrypted data (including multicast group traffic) and
* showing full packet information even when the packet data uses aliases or
* other context specific data.
*
* Sessions are an extremely complex part of the dissectors because they occur at
* so many different levels, and that they are temporal in nature while wireshark
* is not. This means that all data structures that deal with sessions must deal
* with both the level and the time of the packet.
*
* The levels are:
* 1. Transport. These sessions are defined by the transport, and transport
* addresses. As in the transports, there is no transport information allowed
* at the dps level, but transport information is allowed to influence other
* decisions. Every dps packet must be part of a transport session. Transport
* sessions are usually managed as conversations in Wireshark. Each transport
* session is has an identifier that is defined by the DPS plugin the first
* time a packet in the transport session is passed to the plugin.
* 2. DPS. These sessions are defined by DPS, and are part of the DNP definition.
* These sessions are also assigned a unique DPS session identifier.
*
* 3. Security (Optional). Security sessions always exist inside of
* an DPS session. Security sessions are further divided into epochs, keys, etc.
*
* Temporal information is always associated with packet numbers, which always increase.
* This temporal information is used during the first pass to create sessions by
* determining that a new packet doesn't belong to a previous session.
*
* During the first pass the data structures are referenced from the transport
* session information up. The goal of the first pass is to create the most specific
* session information and associate each packet with the appropriate session. These
* sessions refer to more general session information.
*
* In order to make lookups easier, the most fine-grainded sessions are assigned
* unique identifiers. Secure sessions are always born unsecure (during security
* negotiation). These use the same session identifiers, but the state for the
* secure and unsecured times are separated. Once a session is secured it never
* transitions back to unsecured.
*
* MEMBERSHIP
* Each packet is sent by a member of the session. Session members have state related
* to the session. Packets are received by either a member of the session or the
* session itself (implying all members). This means that packet state can come
* from:
* 1. The sender.
* 2. The receiver (if directed to a receiver).
* 3. The session.
* The identity of a member is always a combination of transport and dps information.
* However, the state of the membership is in the context of the session, keyed by
* the sender.
*
* In order to make lookups easier, each unique sender in the system is
* assigned a unique identifier.
*/
#include <config.h>
#include <ctype.h>
#include <stdio.h>
#include <glib.h>
#ifdef HAVE_LIBGCRYPT
#include <wsutil/wsgcrypt.h>
#if (defined GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010600)
#define LIBGCRYPT_OK
#endif
#endif
#include <epan/packet.h>
#include <epan/proto.h>
#include <epan/proto_data.h>
#include <epan/prefs.h>
#include <epan/conversation.h>
#include <epan/expert.h>
#include <epan/uat.h>
#include <wsutil/aes.h>
#include <wsutil/str_util.h>
#include <epan/to_str.h>
#include "packet-tcp.h"
/* DEFINES, STRUCTURES, AND SUPPORT METHOD DECLARATIONS
* The following sections includes preprocessor definitions, structure definitions,
* and method declarations for all dissectors.
* The ordering is by DPS stack order, general first and then by protocols.
*/
/**
* GENERAL SUPPORT STRUCTURES
* The following structures represent state that must be maintained for
* the dissectors to operate. They are not directly related to protocol
* information.
*/
/**
* This structure represents a SID, or Sender ID, in the system.
* This is allocated as global memory, and must be freed. SIDs
* are Object IDs, and can be displayed in hex but preferrably
* using the OID output format. Even though the OID contains
* a length, we prefix this buffer with a length (which must
* be less than 255 by the definition of a SID.
* SIDs are not versioned, so they can be used universally in
* any protocol version.
*/
typedef guint8 *dof_2009_1_pdu_19_sid;
/**
* This structure encapsulates an OPID, which is the combination of
* a source identifier (SID, and OID) and a packet number. This is a separate
* structure because some operations actually contain multiple opids, but need
* to be placed in the appropriate data structures based on SID lookup. This
* structure can be used as a key in different hash tables.
*/
typedef struct _dpp_opid
{
guint op_sid_id;
dof_2009_1_pdu_19_sid op_sid;
guint op_cnt;
} dof_2009_1_pdu_20_opid;
/**
* This structure contains all of the transport session information
* related to a particular session, but not related to the packet
* within that session. That information is separated to allow
* reuse of the structure.
*/
typedef struct _dof_transport_session
{
/**
* TRANSPORT ID: This is a unique identifier for each transport,
* used to prevent aliasing of the SENDER ID value in the
* transport packet structure. It contains the protocol id
* assigned by Wireshark (unique per protocol).
*/
gint transport_id;
/**
* For new sessions, this is left zero. The DPS dissector will
* set this value.
*/
guint32 transport_session_id;
/**
* Timestamp of start of session.
*/
nstime_t session_start_ts;
/**
* Whether negotiation is required on this session.
*/
gboolean negotiation_required;
/**
* The frame number where negotiation was complete, or zero if not complete.
*/
guint32 negotiation_complete_at;
/**
* The time when negotiation was complete, or zero if not complete.
*/
nstime_t negotiation_complete_at_ts;
/**
* Type of transport session.
*/
gboolean is_streaming; /* Inverse is 'is_datagram'. */
/**
* Cardinality of transport session.
*/
gboolean is_2_node; /* Inverse is 'is_n_node'. */
} dof_transport_session;
typedef struct _dof_transport_packet
{
/**
* Source of packet (if known, default is server).
*/
gboolean is_sent_by_client; /* Inverse is 'is_sent_by_server'. */
/**
* SENDER ID/RECEIVER ID: A unique value that identifies the unique
* transport sender/receiver address. This number is based on only
* the transport, and not session, information.
*/
guint sender_id;
guint receiver_id;
} dof_transport_packet;
/**
* This structure maintains security state throughout an DPS session.
* It is managed by the key exchange protocol, and becomes effective
* at different dps packets in each communication direction. Decrypting
* a packet requires that this structure exists.
*/
typedef struct _dof_session_key_exchange_data
{
/**
* The frame at which this becomes valid for initiator packets.
*/
guint32 i_valid;
/**
* The frame at which this becomes valid for responder packets.
*/
guint32 r_valid;
/**
* SECURITY MODE: The security mode for a secure session. Set
* by the key exchange dissector.
*/
guint32 security_mode;
/**
* SECURITY MODE INITIALIZATION DATA: Determined by the key exchange
* protocol and passed here for the reference of the security mode.
*/
guint32 security_mode_data_length;
guint8 *security_mode_data;
/**
* SECURITY MODE DATA: Created and managed by the security mode
* dissector.
*/
void *security_mode_key_data;
/**
* SESSION KEY: Pointer to seasonal data that holds the encryption key.
*/
guint8 *session_key;
/**
* The next security data in this session.
*/
struct _dof_session_key_exchange_data *next;
} dof_session_key_exchange_data;
/**
* This structure contains security keys that should be tried with
* sessions that otherwise are not known.
*/
typedef struct _dof_session_key_data
{
guint8 *session_key;
} dof_session_key_data;
/**
* This structure contains security keys for groups.
*/
typedef struct _dof_group_data
{
guint8 *domain;
guint8 domain_length;
guint8 *identity;
guint8 identity_length;
guint8 *kek;
} dof_group_data;
/**
* This structure contains security keys for non-group identities.
*/
typedef struct _dof_identity_data
{
guint8 *domain;
guint8 domain_length;
guint8 *identity;
guint8 identity_length;
guint8 *secret;
} dof_identity_data;
/**
* This structure exists for global security state. It exposes the
* configuration data associated with DPS, and also is a common location
* that learned security information is stored. Each dof_packet_data will
* contain a pointer to this structure - there is only one for the entire
* DPS.
*/
typedef struct _dof_security_data
{
/* Array of session_keys. */
dof_session_key_data *session_key;
guint16 session_key_count;
/* Array of group data. */
dof_group_data *group_data;
guint16 group_data_count;
/* Array of identity data. */
dof_identity_data *identity_data;
guint16 identity_data_count;
/* Global sessions. */
/*TODO: Figure this out */
/* dof_session_list* sessions; */
} dof_security_data;
/**
* This structure represents a key that is learned for a group and epoch.
*/
struct _dof_learned_group_data;
typedef struct _dof_learned_group_auth_data
{
guint32 epoch;
guint8 *kek;
guint mode_length;
guint8 *mode;
guint16 security_mode;
struct _dof_learned_group_data *parent;
struct _dof_learned_group_auth_data *next;
} dof_learned_group_auth_data;
/**
* This structure represents a group that is learned about.
*/
typedef struct _dof_learned_group_data
{
guint8 domain_length;
guint8 *domain;
guint8 group_length;
guint8 *group;
guint32 ssid;
dof_learned_group_auth_data *keys;
struct _dof_learned_group_data *next;
} dof_learned_group_data;
/**
* This structure exists for each secure DPS session. This is kept in
* addition to the normal session
* Each packet that has state will contain a reference to one of these.
*
* Information in this structure is invariant for the duration of the
* session *or* is only used during the initial pass through the packets.
* Information that changes (for example, security parameters, keys, etc.)
* needs to be maintained separately, although this structure is the
* starting place for this information.
*
* This structure is initialized to zero.
*/
struct _dof_session_data;
typedef struct _dof_secure_session_data
{
/**
* SSID: Zero is typically used for streaming sessions.
*/
guint32 ssid;
/**
* DOMAIN LENGTH: The length of the security domain, greater than
* zero for secure sessions. Set by the key exchange dissector.
*/
guint8 domain_length;
/**
* DOMAIN: The security domain itself, seasonal storage, non-null
* for secure sessions. Set by the key exchange dissector.
*/
guint8 *domain;
/**
* SESSION SECURITY: This is a list of security data for this
* session, created by the key exchange protocol.
*/
dof_session_key_exchange_data *session_security_data;
dof_session_key_exchange_data *session_security_data_last;
/**
* NEXT: This is the next secure session related to the parent
* unsecure session. Protocols can define new secure sessions and
* add them to this list. DPP then finds the correct secure session
* for a secure packet and caches it.
*/
struct _dof_secure_session_data *next;
struct _dof_session_data *parent;
guint32 original_session_id;
gboolean is_2_node;
} dof_secure_session_data;
/**
* This structure exists for each DPS session. Secure sessions have an
* additional data structure that includes the secure session information.
* Each packet that has state will contain a reference to one of these.
*
* Information in this structure is invariant for the duration of the
* session *or* is only used during the initial pass through the packets.
* Information that changes (for example, security parameters, keys, etc.)
* needs to be maintained separately, although this structure is the
* starting place for this information.
*
* This structure is initialized to zero.
*/
typedef struct _dof_session_data
{
/**
* SESSION ID: Set when the session is created, required.
*/
guint32 session_id;
/**
* DPS ID: The type of DPS SENDER ID (in the packet data) to prevent
* aliasing. Since DPS senders identifiers relate to DNP, this is the
* DNP version number.
*/
guint8 dof_id;
/**
* SECURE SESSIONS: When secure sessions are created from this
* unsecure session then they are added to this list. Each member
* of the list must be distinguished.
*/
dof_secure_session_data *secure_sessions;
/**
* Protocol-specific data.
*/
GSList *data_list;
} dof_session_data;
/* DOF Security Structures. */
/* Return structures for different packets. */
typedef struct _dof_2008_16_security_3_1
{
tvbuff_t *identity;
} dof_2008_16_security_3_1;
typedef struct _dof_2008_16_security_4
{
tvbuff_t *identity;
tvbuff_t *nonce;
} dof_2008_16_security_4;
typedef struct _dof_2008_16_security_6_1
{
tvbuff_t *i_identity;
tvbuff_t *i_nonce;
guint16 security_mode;
guint32 security_mode_data_length;
guint8 *security_mode_data;
} dof_2008_16_security_6_1;
typedef struct _dof_2008_16_security_6_2
{
tvbuff_t *r_identity;
tvbuff_t *r_nonce;
} dof_2008_16_security_6_2;
/**
* This structure defines the address for Wireshark transports. There is no
* DPS information associated here.
*/
typedef struct _ws_node
{
address addr;
guint32 port;
} ws_node;
typedef struct _dof_session_list
{
dof_session_data *session;
struct _dof_session_list *next;
} dof_session_list;
/**
* DOF PACKET DATA
* This structure exists for each DOF packet. There is ABSOLUTELY NO
* transport-specific information here, although there is a session
* number which may relate to transport information indirectly through
* a transport session.
* There will be one of these for each DOF packet, even if the corresponding
* Wireshark frame has multiple DOF packets encapsulated in it. The key
* to this structure is the operation identifier, and there is a hash
* lookup to go from an operation identifier to this structure.
*/
typedef struct _dof_packet_data
{
/**
* NON-DPS FIELDS, USED FOR WIRESHARK COMMUNICATION/PROCESSING
* Protocol-specific data.
*/
GSList *data_list;
/**
* The Wireshark frame. Note that a single frame can have multiple DPS packets.
*/
guint32 frame;
/**
* The DPS frame/packet. This number is unique in the entire trace.
*/
guint32 dof_frame;
/**
* Packet linked list for all dps packets.
*/
struct _dof_packet_data *next;
/**
* DPS FIELDS
* Indicator that the packet has already been processed. Processed packets
* have all their fields set that can be determined. Further attempts to
* determine NULL fields are worthless.
*/
gboolean processed;
/**
* SUMMARY: An operation summary, displayed in the Operation History. This is seasonal
* data, managed by the DPP dissector.
*/
const gchar *summary;
/**
* SENDER ID/RECEIVER ID: An identifier for each unique sender/receiver according to DPS.
* This augments the transport SENDER ID/RECEIVER ID in determining each
* unique sender.
*/
gint sender_id;
gint receiver_id;
/**
* DPP INFORMATION - CACHED INFORMATION
*/
gboolean is_command; /* Inverse is 'is_response'. */
gboolean is_sent_by_initiator;
/**
* SENDER SID ID/RECEIVER SID ID: An identifier for the sid associated with this packet's sender.
* Zero indicates that it has not been assigned. Assigned by the DPP
* dissector.
*/
guint sender_sid_id;
guint receiver_sid_id;
/**
* SENDER SID/RECEIVER SID: The SID of the sender/receiver, or NULL if not known.
*/
dof_2009_1_pdu_19_sid sender_sid;
dof_2009_1_pdu_19_sid receiver_sid;
/**
* Operation references.
*/
gboolean has_opid;
dof_2009_1_pdu_20_opid op;
gboolean has_referenced_opid;
dof_2009_1_pdu_20_opid ref_op;
struct _dof_packet_data *opid_first;
struct _dof_packet_data *opid_next;
struct _dof_packet_data *opid_last;
struct _dof_packet_data *opid_first_response;
struct _dof_packet_data *opid_next_response;
struct _dof_packet_data *opid_last_response;
/**
* SECURITY INFORMATION - CACHED
*/
const gchar *security_session_error;
dof_session_key_exchange_data *security_session;
void *security_packet;
guint8 *decrypted_buffer;
tvbuff_t *decrypted_tvb;
guint16 decrypted_offset;
gchar *decrypted_buffer_error;
/**
* OPERATION DATA: Generic data, seasonal, owned by the application protocol dissector
* for this packet.
*/
void *opid_data;
} dof_packet_data;
/**
* This structure represents globals that are passed to all dissectors.
*/
typedef struct _dof_globals
{
guint32 next_transport_session;
guint32 next_session;
dof_packet_data *dof_packet_head;
dof_packet_data *dof_packet_tail;
dof_security_data *global_security;
dof_learned_group_data *learned_group_data;
gboolean decrypt_all_packets;
gboolean track_operations;
guint track_operations_window;
} dof_globals;
/**
* This structure contains all information that is passed between
* transport dissectors/plugins and the DPS dissector. It is allocated
* by the transport plugin, and its fields are set as described here.
*/
typedef struct _dof_api_data
{
/**
* TRANSPORT SESSION: Set by the transport dissector, required.
*/
dof_transport_session *transport_session;
/**
* TRANSPORT PACKET: Set by the transport dissector, required.
*/
dof_transport_packet *transport_packet;
/**
* DPS SESSION: Set by the DPS dissector.
*/
dof_session_data *session;
/**
* DPS DATA: Set by the DPS dissector.
*/
dof_packet_data *packet;
/**
* DPS SECURE SESSION: Set by the DPP dissector.
*/
dof_secure_session_data *secure_session;
} dof_api_data;
/**
* This set of types defines the Security Mode dissector API.
* This structure identifies the context of the dissection,
* allowing a single structure to know what part of the packet
* of sequence of packets it is working with.
*
* Structure for Security Mode of Operation dissectors.
*/
typedef enum _dof_secmode_context
{
INITIALIZE,
HEADER,
TRAILER
} dof_secmode_context;
/* Seasonal, initialized to zero. */
typedef struct _dof_secmode_api_data
{
/**
* API VERSION: Set by the DPS dissector, required.
* MUST BE THE FIRST FIELD.
*/
guint8 version;
/**
* CONTEXT: Set the DPS dissector, required.
*/
dof_secmode_context context;
/**
* SECURITY MODE OFFSET: The packet offset from the DPP header of the security mode.
*/
guint security_mode_offset;
/**
* API DATA: Set by the DPS dissector, required.
*/
dof_api_data *dof_api;
/**
* SECURE SESSION DATA: Controlled by the caller, either associated
* with the current packet (HEADER mode) or not (other modes).
* Used to access session information.
*/
dof_secure_session_data *secure_session;
/**
* KEY EXCHANGE: Controlled by the caller, represents the key exchange
* for INITIALIZE mode.
*/
dof_session_key_exchange_data *session_key_data;
} dof_secmode_api_data;
/* These should be the only non-static declarations in the file. */
void proto_register_dof(void);
void proto_reg_handoff_dof(void);
/* Dissector routines. */
static int dissect_2008_1_dsp_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
static int dissect_2008_16_security_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_3_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_3_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_5(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_6_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_6_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_6_3(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_7(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_8(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_9(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_10(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_11(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_12(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2008_16_security_13(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2009_11_type_4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data);
static int dissect_2009_11_type_5(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
static const gchar* dof_oid_create_standard_string(guint32 bufferSize, const guint8 *pOIDBuffer);
static const gchar* dof_iid_create_standard_string(guint32 bufferSize, const guint8 *pIIDBuffer);
static guint8 dof_oid_create_internal(const char *oid, guint32 *size, guint8 *buffer);
static void dof_oid_new_standard_string(const char *data, guint32 *rsize, guint8 **oid);
static gint read_c4(tvbuff_t *tvb, gint offset, guint32 *v, gint *len);
static void validate_c4(packet_info *pinfo, proto_item *pi, guint32, gint len);
static gint read_c3(tvbuff_t *tvb, gint offset, guint32 *v, gint *len);
static void validate_c3(packet_info *pinfo, proto_item *pi, guint32, gint len);
static gint read_c2(tvbuff_t *tvb, gint offset, guint16 *v, gint *len);
static void validate_c2(packet_info *pinfo, proto_item *pi, guint16, gint len);
static gint dof_dissect_pdu(dissector_t dissector, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *result);
static gint dof_dissect_pdu_as_field(dissector_t disector, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, int item, int ett, void *result);
#if 0 /* TODO not used yet */
static void dof_session_add_proto_data(dof_session_data *session, int proto, void *proto_data);
static void* dof_session_get_proto_data(dof_session_data *session, int proto);
static void dof_session_delete_proto_data(dof_session_data *session, int proto);
#endif
static void dof_packet_add_proto_data(dof_packet_data *packet, int proto, void *proto_data);
static void* dof_packet_get_proto_data(dof_packet_data *packet, int proto);
#if 0 /* TODO not used yet */
static void dof_packet_delete_proto_data(dof_packet_data *packet, int proto);
#endif
/* DOF PROTOCOL STACK */
#define DOF_PROTOCOL_STACK "DOF Protocol Stack"
/**
* PORTS
* The following ports are registered with IANA and used to hook transport
* dissectors into the lower-level Wireshark transport dissectors.
*
* Related to these ports is the usage of conversations for DOF. The goal of
* using Wireshark conversations is to guarantee that DPS data is available for
* any DPS packet. However, there is no assumption that Wireshark conversations
* map in any way to DOF sessions.
*
* One exception to this use is in discovery of DOF servers. The DOF_MCAST_NEG_SEC_UDP_PORT
* is watched for all traffic. A "wildcard" conversation is then created for the
* source address, and the DPS dissector is associated with that port. In this
* way, servers on non-standard ports will automatically be decoded using DPS.
*/
#define DOF_NEG_SEC_UDP_PORT_RANGE "3567,5567" /* P2P + Multicast */
#define DOF_P2P_NEG_SEC_TCP_PORT 3567
/* Reserved UDP port 3568*/
#define DOF_TUN_SEC_TCP_PORT 3568
#define DOF_P2P_SEC_TCP_PORT 5567
/* Reserved UDP port 8567*/
#define DOF_TUN_NON_SEC_TCP_PORT 8567
/* This is needed to register multicast sessions with the UDP handler. */
static dissector_handle_t dof_udp_handle;
static int proto_2008_1_dof = -1;
static int proto_2008_1_dof_tcp = -1;
static int proto_2008_1_dof_udp = -1;
static int hf_2008_1_dof_session = -1;
static int hf_2008_1_dof_is_2_node = -1;
static int hf_2008_1_dof_is_streaming = -1;
static int hf_2008_1_dof_is_from_client = -1;
static int hf_2008_1_dof_frame = -1;
static int hf_2008_1_dof_session_transport = -1;
static int ett_2008_1_dof = -1;
/* DOF Tunnel Protocol */
/* UDP Registrations */
#define TUNNEL_PROTOCOL_STACK "DOF Tunnel Protocol Stack"
#define TUNNEL_APPLICATION_PROTOCOL "DOF Tunnel Protocol"
static dissector_table_t dof_tun_app_dissectors;
/***** TUNNEL *****/
static int proto_2012_1_tunnel = -1;
static int ett_2012_1_tunnel = -1;
static int hf_2012_1_tunnel_1_version = -1;
static int hf_2012_1_tunnel_1_length = -1;
/* DOF NETWORK PROTOCOL */
#define DNP_MAX_VERSION 1
#define DOF_NETWORK_PROTOCOL "DOF Network Protocol"
static dissector_table_t dnp_dissectors;
static dissector_table_t dnp_framing_dissectors;
static int proto_2008_1_dnp = -1;
static int hf_2008_1_dnp_1_version = -1;
static int hf_2008_1_dnp_1_flag = -1;
static int ett_2008_1_dnp = -1;
static int ett_2008_1_dnp_header = -1;
/* DNP V0 */
static int proto_2008_1_dnp_0 = -1;
static int hf_2008_1_dnp_0_1_1_padding = -1;
static int hf_2008_1_dnp_0_1_1_version = -1;
/* DNP V1 */
#define DNP_V1_DEFAULT_FLAGS (0)
static int proto_2009_9_dnp_1 = -1;
static int hf_2009_9_dnp_1_flags = -1;
static int hf_2009_9_dnp_1_flag_length = -1;
static int hf_2009_9_dnp_1_length = -1;
static int hf_2009_9_dnp_1_flag_srcport = -1;
static int hf_2009_9_dnp_1_srcport = -1;
static int hf_2009_9_dnp_1_flag_dstport = -1;
static int hf_2009_9_dnp_1_dstport = -1;
static int ett_2009_9_dnp_1_flags = -1;
static const int *bitmask_2009_9_dnp_1_flags[] = {
&hf_2009_9_dnp_1_flag_length,
&hf_2009_9_dnp_1_flag_srcport,
&hf_2009_9_dnp_1_flag_dstport,
NULL
};
/* DOF PRESENTATION PROTOCOL */
#define DOF_PRESENTATION_PROTOCOL "DOF Presentation Protocol"
static dissector_table_t dof_dpp_dissectors;
static int proto_2008_1_dpp = -1;
static int hf_2008_1_dpp_sid_num = -1;
static int hf_2008_1_dpp_rid_num = -1;
static int hf_2008_1_dpp_sid_str = -1;
static int hf_2008_1_dpp_rid_str = -1;
static int hf_2008_1_dpp_first_command = -1;
static int hf_2008_1_dpp_last_command = -1;
static int hf_2008_1_dpp_first_response = -1;
static int hf_2008_1_dpp_last_response = -1;
static int hf_2008_1_dpp_related_frame = -1;
static int hf_2008_1_dpp_1_version = -1;
static int hf_2008_1_dpp_1_flag = -1;
static int ett_2008_1_dpp = -1;
static int ett_2008_1_dpp_1_header = -1;
/* DPP V0 */
static int proto_2008_1_dpp_0 = -1;
static int hf_2008_1_dpp_0_1_1_version = -1;
/* DPP V1 - RESERVED, NOT SUPPORTED */
/* DPP V2 */
#define DPP_V2_DEFAULT_FLAGS (0)
#define DPP_V2_SEC_FLAG_E (0x80)
#define DPP_V2_SEC_FLAG_D (0x08)
#define DPP_V2_SEC_FLAG_P (0x04)
#define DPP_V2_SEC_FLAG_A (0x02)
#define DPP_V2_SEC_FLAG_S (0x01)
static int proto_2009_12_dpp = -1;
static int proto_2009_12_dpp_common = -1;
/* TODO: The complete on final and final flags are not covered. */
static int hf_2009_12_dpp_2_1_flags = -1;
static int hf_2009_12_dpp_2_1_flag_security = -1;
static int hf_2009_12_dpp_2_1_flag_opid = -1;
static int hf_2009_12_dpp_2_1_flag_seq = -1;
static int hf_2009_12_dpp_2_1_flag_retry = -1;
static int hf_2009_12_dpp_2_1_flag_cmdrsp = -1;
static int hf_2009_12_dpp_2_3_sec_flags = -1;
static int hf_2009_12_dpp_2_3_sec_flag_secure = -1;
static int hf_2009_12_dpp_2_3_sec_flag_rdid = -1;
static int hf_2009_12_dpp_2_3_sec_flag_partition = -1;
static int hf_2009_12_dpp_2_3_sec_flag_ssid = -1;
static int hf_2009_12_dpp_2_3_sec_flag_as = -1;
static int hf_2009_12_dpp_2_3_sec_ssid = -1;
static int hf_2009_12_dpp_2_3_sec_rdid = -1;
static int hf_2009_12_dpp_2_3_sec_remote_partition = -1;
static int hf_2009_12_dpp_2_3_sec_partition = -1;
static int hf_2009_12_dpp_2_1_opcnt = -1;
static int hf_2009_12_dpp_2_1_seq = -1;
static int hf_2009_12_dpp_2_1_retry = -1;
static int hf_2009_12_dpp_2_1_delay = -1;
static int hf_2009_12_dpp_2_14_opcode = -1;
static int ett_2009_12_dpp_2_1_flags = -1;
static int ett_2009_12_dpp_2_3_security = -1;
static int ett_2009_12_dpp_2_3_sec_flags = -1;
static int ett_2009_12_dpp_2_3_sec_remote_partition = -1;
static int ett_2009_12_dpp_2_3_sec_partition = -1;
static int ett_2009_12_dpp_2_opid = -1;
static int ett_2009_12_dpp_2_opid_history = -1;
static int ett_2009_12_dpp_common = -1;
static const value_string strings_2009_12_dpp_opid_types[] = {
{ 0, "Not Present" },
{ 1, "SID [Sender]" },
{ 2, "SID [Receiver]" },
{ 3, "SID [Explicit]" },
{ 0, NULL }
};
#define OP_2009_12_RESPONSE_FLAG (0x80)
#define OP_2009_12_NODE_DOWN_CMD (0)
#define OP_2009_12_NODE_DOWN_RSP (OP_2009_12_RESPONSE_FLAG|OP_2009_12_NODE_DOWN_CMD)
#define OP_2009_12_SOURCE_LOST_CMD (1)
#define OP_2009_12_SOURCE_LOST_RSP (OP_2009_12_RESPONSE_FLAG|OP_2009_12_SOURCE_LOST_CMD)
#define OP_2009_12_RENAME_CMD (2)
#define OP_2009_12_RENAME_RSP (OP_2009_12_RESPONSE_FLAG|OP_2009_12_RENAME_CMD)
#define OP_2009_12_PING_CMD (3)
#define OP_2009_12_PING_RSP (OP_2009_12_RESPONSE_FLAG|OP_2009_12_PING_CMD)
#define OP_2009_12_CANCEL_ALL_CMD (4)
#define OP_2009_12_CANCEL_ALL_RSP (OP_2009_12_RESPONSE_FLAG|OP_2009_12_CANCEL_ALL_CMD)
#define OP_2009_12_HEARTBEAT_CMD (5)
#define OP_2009_12_HEARTBEAT_RSP (OP_2009_12_RESPONSE_FLAG|OP_2009_12_HEARTBEAT_CMD)
#define OP_2009_12_QUERY_CMD (6)
#define OP_2009_12_QUERY_RSP (OP_2009_12_RESPONSE_FLAG|OP_2009_12_QUERY_CMD)
#define OP_2009_12_SOURCE_FOUND_CMD (8)
#define OP_2009_12_SOURCE_FOUND_RSP (OP_2009_12_RESPONSE_FLAG|OP_2009_12_SOURCE_FOUND_CMD)
static const value_string strings_2009_12_dpp_common_opcodes[] = {
{ OP_2009_12_NODE_DOWN_CMD, "DPP Node Down" },
{ OP_2009_12_NODE_DOWN_RSP, "DPP Node Down Response (Illegal)" },
{ OP_2009_12_SOURCE_LOST_CMD, "DPP Source Lost" },
{ OP_2009_12_SOURCE_LOST_RSP, "DPP Source Lost Response (Illegal)" },
{ OP_2009_12_SOURCE_FOUND_CMD, "DPP Source Found" },
{ OP_2009_12_SOURCE_FOUND_RSP, "DPP Source Found Response (Illegal)" },
{ OP_2009_12_RENAME_CMD, "DPP Rename" },
{ OP_2009_12_RENAME_RSP, "DPP Rename Response (Illegal)" },
{ OP_2009_12_PING_CMD, "DPP Ping" },
{ OP_2009_12_PING_RSP, "DPP Ping Response" },
{ OP_2009_12_HEARTBEAT_CMD, "DPP Heartbeat" },
{ OP_2009_12_HEARTBEAT_RSP, "DPP Heartbeat Response (Illegal)" },
{ OP_2009_12_QUERY_CMD, "DPP Query" },
{ OP_2009_12_QUERY_RSP, "DPP Query Response" },
{ OP_2009_12_CANCEL_ALL_CMD, "DPP Cancel All" },
{ OP_2009_12_CANCEL_ALL_RSP, "DPP Cancel All Response (Illegal)" },
{ 0, NULL }
};
/* DOF APPLICATION PROTOCOL */
#define DOF_APPLICATION_PROTOCOL "DOF Application Protocol"
static dissector_table_t app_dissectors;
static int proto_2008_1_app = -1;
static int hf_2008_1_app_version = -1;
/* DAP V0 (DSP - DOF SESSION PROTOCOL) */
/* Note that DSP is *always* appid 0 and so it violates the standard naming rule. */
static dissector_table_t dsp_option_dissectors;
static int hf_2008_1_dsp_12_opcode = -1;
static int hf_2008_1_dsp_attribute_code = -1;
static int hf_2008_1_dsp_attribute_data = -1;
static int hf_2008_1_dsp_value_length = -1;
static int hf_2008_1_dsp_value_data = -1;
static const value_string strings_2008_1_dsp_attribute_codes[] = {
{ 0, "TEP Family" },
{ 1, "OAP Family" },
{ 2, "CCM Family" },
{ 3, "TRP Family" },
{ 255, "General" },
{ 0, NULL }
};
#define DOF_PROTOCOL_DSP 0
#define DSP_OAP_FAMILY 0x010000
static int proto_2008_1_dsp = -1;
#define OP_2008_1_RSP (0x80)
#define OP_2008_1_QUERY_CMD 0
#define OP_2008_1_QUERY_RSP (OP_2008_1_RSP|OP_2008_1_QUERY_CMD)
#define OP_2008_1_CONFIG_REQ 1
#define OP_2008_1_CONFIG_ACK (OP_2008_1_RSP|2)
#define OP_2008_1_CONFIG_NAK (OP_2008_1_RSP|3)
#define OP_2008_1_CONFIG_REJ (OP_2008_1_RSP|4)
#define OP_2008_1_TERMINATE_CMD 5
#define OP_2008_1_TERMINATE_RSP (OP_2008_1_RSP|OP_2008_1_TERMINATE_CMD)
#define OP_2008_1_OPEN_CMD 6
#define OP_2008_1_OPEN_RSP (OP_2008_1_RSP|OP_2008_1_OPEN_CMD)
#define OP_2008_1_OPEN_SECURE_RSP (OP_2008_1_RSP|7)
static const value_string strings_2008_1_dsp_opcodes[] = {
{ OP_2008_1_QUERY_CMD, "DSP Query" },
{ OP_2008_1_QUERY_RSP, "DSP Query Response" },
{ OP_2008_1_CONFIG_REQ, "DSP Request" },
{ OP_2008_1_CONFIG_ACK, "DSP ACK Response" },
{ OP_2008_1_CONFIG_NAK, "DSP NAK Response" },
{ OP_2008_1_CONFIG_REJ, "DSP REJ Response" },
{ OP_2008_1_TERMINATE_CMD, "DSP Terminate/Close Request" },
{ OP_2008_1_TERMINATE_RSP, "DSP Terminate/Close Response" },
{ OP_2008_1_OPEN_CMD, "DSP Open" },
{ OP_2008_1_OPEN_RSP, "DSP Open Response" },
{ OP_2008_1_OPEN_SECURE_RSP, "DSP Open Secure Response" },
{ 0, NULL }
};
#define DSP_AVP_AUTHENTICATION 0
#define DSP_AVP_APPLICATION 1
#if 0 /* not used yet */
static const value_string strings_2008_1_dsp_attributes[] = {
{ DSP_AVP_AUTHENTICATION, "Authentication Protocol" },
{ DSP_AVP_APPLICATION, "Application Protocol" },
{ 0, NULL }
};
static const value_string strings_2008_1_dsp_values[] = {
{ 1, "DOF Object Access Protocol (version 1)" },
{ 3, "DOF Ticket Exchange Protocol (version 1)" },
{ 0, NULL }
};
#endif
static int ett_2008_1_dsp_12 = -1;
static int ett_2008_1_dsp_12_options = -1;
static int ett_2008_1_dsp_12_option = -1;
/* DAP V1 (OAP - OBJECT ACCESS PROTOCOL V1) */
/* This is the defined protocol id for OAP. */
#define DOF_PROTOCOL_OAP_1 1
/* There are two "protocols", one hooks into DSP and the other to DOF. */
static int proto_oap_1 = -1;
static int proto_oap_1_dsp = -1;
/* OAP DSP protocol items. */
static int hf_oap_1_dsp_option = -1;
/* OAP protocol items. */
static int hf_oap_1_opcode = -1;
static int hf_oap_1_alias_size = -1;
static int hf_oap_1_flags = -1;
static int hf_oap_1_exception_internal_flag = -1;
static int hf_oap_1_exception_final_flag = -1;
static int hf_oap_1_exception_provider_flag = -1;
static int hf_oap_1_cmdcontrol = -1;
static int hf_oap_1_cmdcontrol_cache_flag = -1;
static int hf_oap_1_cmdcontrol_verbosity_flag = -1;
static int hf_oap_1_cmdcontrol_noexecute_flag = -1;
static int hf_oap_1_cmdcontrol_ack_flag = -1;
static int hf_oap_1_cmdcontrol_delay_flag = -1;
static int hf_oap_1_cmdcontrol_heuristic_flag = -1;
static int hf_oap_1_cmdcontrol_heuristic = -1;
static int hf_oap_1_cmdcontrol_cache = -1;
static int hf_oap_1_cmdcontrol_ackcnt = -1;
static int hf_oap_1_cmdcontrol_ack = -1;
#if 0 /* not used yet */
static int hf_oap_1_opinfo_start_frame = -1;
static int hf_oap_1_opinfo_end_frame = -1;
static int hf_oap_1_opinfo_timeout = -1;
#endif
static int hf_oap_1_providerid = -1;
static int ett_oap_1_1_providerid = -1;
static int hf_oap_1_objectid = -1;
static int ett_oap_1_objectid = -1;
static int hf_oap_1_interfaceid = -1;
static int hf_oap_1_itemid = -1;
#if 0 /* not used yet */
static int hf_oap_1_distance = -1;
#endif
static int hf_oap_1_alias = -1;
static int hf_oap_1_alias_frame = -1;
static int hf_oap_1_subscription_delta = -1;
static int hf_oap_1_update_sequence = -1;
static int hf_oap_1_value_list = -1;
static int ett_oap_1_dsp = -1;
static int ett_oap_1_dsp_options = -1;
static int ett_oap_1 = -1;
static int ett_oap_1_opinfo = -1;
static int ett_oap_1_cmdcontrol = -1;
static int ett_oap_1_cmdcontrol_flags = -1;
static int ett_oap_1_cmdcontrol_ack = -1;
static int ett_oap_1_alias = -1;
static const int *bitmask_oap_1_cmdcontrol_flags[] = {
&hf_oap_1_cmdcontrol_cache_flag,
&hf_oap_1_cmdcontrol_verbosity_flag,
&hf_oap_1_cmdcontrol_noexecute_flag,
&hf_oap_1_cmdcontrol_ack_flag,
&hf_oap_1_cmdcontrol_delay_flag,
&hf_oap_1_cmdcontrol_heuristic_flag,
NULL
};
static expert_field ei_oap_no_session = EI_INIT;
static GHashTable *oap_1_alias_to_binding = NULL;
#define OAP_1_RESPONSE (0x80)
#define OAP_1_CMD_ACTIVATE 28
#define OAP_1_RSP_ACTIVATE (OAP_1_CMD_ACTIVATE|OAP_1_RESPONSE)
#define OAP_1_CMD_ADVERTISE 5
#define OAP_1_RSP_ADVERTISE (OAP_1_CMD_ADVERTISE|OAP_1_RESPONSE)
#define OAP_1_CMD_CHANGE 2
#define OAP_1_RSP_CHANGE (OAP_1_CMD_CHANGE|OAP_1_RESPONSE)
#define OAP_1_CMD_CONNECT 4
#define OAP_1_RSP_CONNECT (OAP_1_CMD_CONNECT|OAP_1_RESPONSE)
#define OAP_1_CMD_DEFINE 6
#define OAP_1_RSP_DEFINE (OAP_1_CMD_DEFINE|OAP_1_RESPONSE)
#define OAP_1_CMD_EXCEPTION 9
#define OAP_1_RSP_EXCEPTION (OAP_1_CMD_EXCEPTION|OAP_1_RESPONSE)
#define OAP_1_CMD_FULL_CONNECT 3
#define OAP_1_RSP_FULL_CONNECT (OAP_1_CMD_FULL_CONNECT|OAP_1_RESPONSE)
#define OAP_1_CMD_GET 10
#define OAP_1_RSP_GET (OAP_1_CMD_GET|OAP_1_RESPONSE)
#define OAP_1_CMD_INVOKE 12
#define OAP_1_RSP_INVOKE (OAP_1_CMD_INVOKE|OAP_1_RESPONSE)
#define OAP_1_CMD_OPEN 14
#define OAP_1_RSP_OPEN (OAP_1_CMD_OPEN|OAP_1_RESPONSE)
#define OAP_1_CMD_PROVIDE 16
#define OAP_1_RSP_PROVIDE (OAP_1_CMD_PROVIDE|OAP_1_RESPONSE)
#define OAP_1_CMD_REGISTER 25
#define OAP_1_RSP_REGISTER (OAP_1_CMD_REGISTER|OAP_1_RESPONSE)
#define OAP_1_CMD_SET 20
#define OAP_1_RSP_SET (OAP_1_CMD_SET|OAP_1_RESPONSE)
#define OAP_1_CMD_SIGNAL 22
#define OAP_1_RSP_SIGNAL (OAP_1_CMD_SIGNAL|OAP_1_RESPONSE)
#define OAP_1_CMD_SUBSCRIBE 24
#define OAP_1_RSP_SUBSCRIBE (OAP_1_CMD_SUBSCRIBE|OAP_1_RESPONSE)
#define OAP_1_CMD_WATCH 30
#define OAP_1_RSP_WATCH (OAP_1_CMD_WATCH|OAP_1_RESPONSE)
static const value_string oap_opcode_strings[] = {
{ OAP_1_CMD_ACTIVATE, "OAP Activate" },
{ OAP_1_RSP_ACTIVATE, "OAP Activate Response (Illegal)" },
{ OAP_1_CMD_ADVERTISE, "OAP Advertise" },
{ OAP_1_RSP_ADVERTISE, "OAP Advertise Response (Illegal)" },
{ OAP_1_CMD_CHANGE, "OAP Change" },
{ OAP_1_RSP_CHANGE, "OAP Change Response (Illegal)" },
{ OAP_1_CMD_CONNECT, "OAP Connect" },
{ OAP_1_RSP_CONNECT, "OAP Connect Response (Illegal)" },
{ OAP_1_CMD_DEFINE, "OAP Define" },
{ OAP_1_RSP_DEFINE, "OAP Define Response" },
{ OAP_1_CMD_EXCEPTION, "OAP Exception (Illegal)" },
{ OAP_1_RSP_EXCEPTION, "OAP Exception Response" },
{ OAP_1_CMD_FULL_CONNECT, "OAP Full Connect" },
{ OAP_1_RSP_FULL_CONNECT, "OAP Full Connect Response (Illegal)" },
{ OAP_1_CMD_GET, "OAP Get" },
{ OAP_1_RSP_GET, "OAP Get Response" },
{ OAP_1_CMD_INVOKE, "OAP Invoke" },
{ OAP_1_RSP_INVOKE, "OAP Invoke Response" },
{ OAP_1_CMD_OPEN, "OAP Open" },
{ OAP_1_RSP_OPEN, "OAP Open Response" },
{ OAP_1_CMD_PROVIDE, "OAP Provide" },
{ OAP_1_RSP_PROVIDE, "OAP Provide Response (Illegal)" },
{ OAP_1_CMD_REGISTER, "OAP Register" },
{ OAP_1_RSP_REGISTER, "OAP Register Response" },
{ OAP_1_CMD_SET, "OAP Set" },
{ OAP_1_RSP_SET, "OAP Set Response" },
{ OAP_1_CMD_SIGNAL, "OAP Signal" },
{ OAP_1_RSP_SIGNAL, "OAP Signal Response (Illegal)" },
{ OAP_1_CMD_SUBSCRIBE, "OAP Subscribe" },
{ OAP_1_RSP_SUBSCRIBE, "OAP Subscribe Response" },
{ OAP_1_CMD_WATCH, "OAP Watch" },
{ OAP_1_RSP_WATCH, "OAP Watch Response (Illegal)" },
{ 0, NULL }
};
typedef struct _alias_key
{
guint32 session;
guint32 sender;
guint32 alias;
} oap_1_alias_key;
static guint oap_1_alias_hash_func(gconstpointer ptr)
{
const oap_1_alias_key *key = (const oap_1_alias_key *)ptr;
return g_int_hash(&key->session) + g_int_hash(&key->sender) + g_int_hash(&key->alias);
}
static int oap_1_alias_equal_func(gconstpointer ptr1, gconstpointer ptr2)
{
const oap_1_alias_key *key1 = (const oap_1_alias_key *)ptr1;
const oap_1_alias_key *key2 = (const oap_1_alias_key *)ptr2;
if (key1->session != key2->session)
return 0;
if (key1->sender != key2->sender)
return 0;
if (key1->alias != key2->alias)
return 0;
return 1;
}
typedef struct
{
guint8 *oid;
guint16 oid_length;
guint8 *iid;
guint16 iid_length;
guint32 frame;
} oap_1_binding;
typedef struct oap_1_binding_list
{
oap_1_binding *binding;
struct oap_1_binding_list *next;
} oap_1_binding_list;
typedef struct
{
oap_1_binding *resolved_alias;
} oap_1_packet_data;
static oap_1_binding* oap_1_resolve_alias(oap_1_alias_key *key);
static int oap_1_tree_add_alias(dof_api_data *api_data, oap_1_packet_data *oap_packet _U_, dof_packet_data *packet, proto_tree *tree, tvbuff_t *tvb, gint offset, guint8 alias_length, guint8 resolve)
{
dof_session_data *session = api_data->session;
proto_item *ti;
proto_tree *options_tree;
if (alias_length == 0)
/* TODO: Output error. */
return offset;
if (session == NULL)
/* TODO: Output error. */
return offset;
ti = proto_tree_add_item(tree, hf_oap_1_alias, tvb, offset, alias_length, ENC_BIG_ENDIAN);
if (resolve)
{
oap_1_binding *binding = NULL;
oap_1_alias_key key;
int i;
guint32 alias;
alias = 0;
for (i = 0; i < alias_length; i++)
alias = (alias << 8) | tvb_get_guint8(tvb, offset + i);
key.session = session->session_id;
key.sender = packet->sender_id;
key.alias = alias;
binding = oap_1_resolve_alias(&key);
if (binding)
{
options_tree = proto_item_add_subtree(ti, ett_oap_1_alias);
/* Decode the Interface */
ti = proto_tree_add_bytes_format_value(tree, hf_oap_1_interfaceid, tvb, offset, alias_length, binding->iid, "%s", dof_iid_create_standard_string(binding->iid_length, binding->iid));
PROTO_ITEM_SET_GENERATED(ti);
/* Decode the Object ID */
ti = proto_tree_add_bytes_format_value(tree, hf_oap_1_objectid, tvb, offset, alias_length, binding->oid, "%s", dof_oid_create_standard_string(binding->oid_length, binding->oid));
PROTO_ITEM_SET_GENERATED(ti);
proto_tree_add_uint_format(options_tree, hf_oap_1_alias_frame,
tvb, 0, 0, binding->frame,
"This alias is defined in frame %u",
binding->frame);
}
}
return offset + alias_length;
}
static int oap_1_tree_add_interface(proto_tree *tree, tvbuff_t *tvb, int offset)
{
guint8 registry;
guint8 len;
registry = tvb_get_guint8(tvb, offset);
len = registry & 0x03;
if (len == 0)
len = 16;
else
len = 1 << (len - 1);
proto_tree_add_item(tree, hf_oap_1_interfaceid, tvb, offset, 1 + len, ENC_NA);
return offset + 1 + len;
}
static int oap_1_tree_add_binding(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset)
{
guint8 len;
/* guint8 cl; */
len = tvb_get_guint8(tvb, offset);
len = len & 0x03;
if (len == 0)
len = 16;
else
len = 1 << (len - 1);
proto_tree_add_item(tree, hf_oap_1_interfaceid, tvb, offset, 1 + len, ENC_NA);
offset += 1 + len;
#if 0 /* this seems to be dead code - check! */
cl = tvb_get_guint8(tvb, offset);
if (cl & 0x80)
len = tvb_get_guint8(tvb, offset + 2);
else
len = tvb_get_guint8(tvb, offset + 1);
#endif
offset = dof_dissect_pdu_as_field(dissect_2009_11_type_4, tvb, pinfo, tree,
offset, hf_oap_1_objectid, ett_oap_1_objectid, NULL);
return offset;
}
static int oap_1_tree_add_cmdcontrol(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset)
{
proto_item *ti;
proto_tree *opinfo_tree;
guint8 flags;
flags = tvb_get_guint8(tvb, offset);
ti = proto_tree_add_bitmask(tree, tvb, offset, hf_oap_1_cmdcontrol, ett_oap_1_cmdcontrol_flags, bitmask_oap_1_cmdcontrol_flags, ENC_NA);
opinfo_tree = proto_item_add_subtree(ti, ett_oap_1_cmdcontrol);
proto_tree_add_item(opinfo_tree, hf_oap_1_cmdcontrol_cache_flag, tvb, offset, 1, ENC_NA);
proto_tree_add_item(opinfo_tree, hf_oap_1_cmdcontrol_verbosity_flag, tvb, offset, 1, ENC_NA);
proto_tree_add_item(opinfo_tree, hf_oap_1_cmdcontrol_noexecute_flag, tvb, offset, 1, ENC_NA);
proto_tree_add_item(opinfo_tree, hf_oap_1_cmdcontrol_ack_flag, tvb, offset, 1, ENC_NA);
proto_tree_add_item(opinfo_tree, hf_oap_1_cmdcontrol_delay_flag, tvb, offset, 1, ENC_NA);
proto_tree_add_item(opinfo_tree, hf_oap_1_cmdcontrol_heuristic_flag, tvb, offset, 1, ENC_NA);
offset += 1;
if (flags & 0x01)
{
/* Heuristic */
gint heur_len;
guint16 heur;
proto_item *pi;
read_c2(tvb, offset, &heur, &heur_len);
pi = proto_tree_add_uint_format(opinfo_tree, hf_oap_1_cmdcontrol_heuristic, tvb, offset, heur_len, heur, "Heuristic Value: %hu", heur);
validate_c2(pinfo, pi, heur, heur_len);
offset += heur_len;
}
if (flags & 0x04)
{
/* Ack List */
guint8 ackcnt;
guint8 i;
ackcnt = tvb_get_guint8(tvb, offset);
proto_tree_add_item(opinfo_tree, hf_oap_1_cmdcontrol_ackcnt, tvb, offset, 1, ENC_NA);
offset += 1;
for (i = 0; i < ackcnt; i++)
{
offset = dof_dissect_pdu_as_field(dissect_2009_11_type_4, tvb, pinfo, opinfo_tree,
offset, hf_oap_1_cmdcontrol_ack, ett_oap_1_cmdcontrol_ack, NULL);
}
}
if (flags & 0x40)
{
/* Cache Delay */
gint cache_len;
guint16 cache;
proto_item *pi;
read_c2(tvb, offset, &cache, &cache_len);
pi = proto_tree_add_uint_format(opinfo_tree, hf_oap_1_cmdcontrol_cache, tvb, offset, cache_len, cache, "Cache Delay: %hu", cache);
validate_c2(pinfo, pi, cache, cache_len);
offset += cache_len;
}
return offset;
}
/**
* Define an alias. This routine is called for each Provide operation that includes an alias assignment.
* It is also called for retries of Provide operations.
* The alias is defined for the duration of the Provide. This means that if the operation is cancelled
* then the alias should no longer be valid.
* The alias is associated with an oap_session, an dof_node, and the alias itself. Aliases
* may be reused as long as the previous use has expired, and so the list is stored in reverse
* order.
*
* NOTE: The alias is passed as a structure pointer, and must be reallocated if it is stored in
* the hash.
*/
static void oap_1_define_alias(dof_api_data *api_data, guint32 alias, oap_1_binding *binding)
{
/* The definer of an alias is the sender, in the session. */
dof_session_data *session = api_data->session;
dof_packet_data *packet = (dof_packet_data *)api_data->packet;
guint32 session_id;
guint32 sender_id;
oap_1_alias_key key;
if (!session)
return;
session_id = session->session_id;
sender_id = packet->sender_id;
if (!binding)
return;
key.session = session_id;
key.sender = sender_id;
key.alias = alias;
/* If there isn't an entry for the alias, then we need to create one.
* The first entry will be the binding we are defining.
*/
if (!g_hash_table_lookup(oap_1_alias_to_binding, &key))
{
oap_1_alias_key *alias_ptr = (oap_1_alias_key *)wmem_alloc0(wmem_file_scope(), sizeof(oap_1_alias_key));
memcpy(alias_ptr, &key, sizeof(oap_1_alias_key));
g_hash_table_insert(oap_1_alias_to_binding, alias_ptr, binding);
}
}
/**
* Given an oap_alias, resolve it to an oap_1_binding. This assumes that the destination of the
* packet is the one that defined the alias.
*/
static oap_1_binding* oap_1_resolve_alias(oap_1_alias_key *key)
{
/* The first lookup is inside the session based on defining node. */
return (oap_1_binding *)g_hash_table_lookup(oap_1_alias_to_binding, key);
}
/* DAP V128 (TEP - TICKET EXCHANGE PROTOCOL V1) */
#define DOF_PROTOCOL_TEP 128
#define DSP_TEP_FAMILY 0x000000
static int proto_tep = -1;
static int proto_tep_dsp = -1;
static int hf_dsp_option = -1;
static int ett_tep_operation = -1;
static int hf_tep_operation = -1;
static int hf_tep_operation_type = -1;
static int hf_tep_opcode = -1;
static int hf_tep_k = -1;
static int hf_tep_c = -1;
static int hf_tep_reject_code = -1;
static int hf_tep_reject_data = -1;
static const true_false_string tep_optype_vals = { "DPP Response", "DPP Command" };
/* TEP.2.1 */
static int ett_tep_2_1_domain = -1;
static int hf_tep_2_1_domain = -1;
static int ett_tep_2_1_initiator_block = -1;
static int hf_tep_2_1_initiator_block = -1;
static int hf_tep_2_1_ticket_confirmation = -1;
/* TEP.2.2 */
static int ett_tep_2_2_initiator_ticket = -1;
static int hf_tep_2_2_initiator_ticket = -1;
static int hf_tep_2_2_ticket_confirmation = -1;
static int ett_tep_2_2_responder_initialization = -1;
static int hf_tep_2_2_responder_initialization = -1;
static int ett_tep_2_2_responder_block = -1;
static int hf_tep_2_2_responder_block = -1;
static int ett_tep_2_2_authenticator_initialization = -1;
static int hf_tep_2_2_authenticator_initialization = -1;
/* TEP.2.2.1 */
static int hf_tep_2_2_1_state_identifier = -1;
static int ett_tep_2_2_1_initial_state = -1;
static int hf_tep_2_2_1_initial_state = -1;
static int hf_tep_session_key = -1;
static int ett_tep_dsp = -1;
static int ett_tep_dsp_options = -1;
static int ett_tep = -1;
#if 0 /* not used yet */
static const value_string tep_filter_existing[] = {
{ 1, "Include Existing Matches" },
{ 0, "Exclude Existing Matches" },
{ 0, NULL }
};
#endif
#define TEP_OPCODE_RSP (0x80)
#define TEP_OPCODE_C (0x20)
#define TEP_OPCODE_K (0x10)
#define TEP_PDU_REJECT (TEP_OPCODE_RSP|0)
#define TEP_PDU_REQUEST (1)
#define TEP_PDU_END_SESSION (5)
#define TEP_PDU_SESSION_ENDING (6)
#define TEP_PDU_REQUEST_KEY (TEP_OPCODE_K|TEP_PDU_REQUEST)
#define TEP_PDU_CONFIRM (TEP_OPCODE_C|TEP_PDU_REQUEST)
#define TEP_PDU_ACCEPT (TEP_OPCODE_RSP|TEP_PDU_REQUEST)
#define TEP_PDU_CONFIRM_ACK (TEP_OPCODE_RSP|TEP_OPCODE_C|TEP_PDU_REQUEST)
static const value_string tep_opcode_strings[] = {
{ TEP_PDU_REJECT, "TEP Reject" },
{ TEP_PDU_REQUEST, "TEP Request" },
{ TEP_PDU_END_SESSION, "TEP End Session" },
{ TEP_PDU_SESSION_ENDING, "TEP Session Ending" },
{ TEP_PDU_REQUEST_KEY, "TEP Rekey" },
{ TEP_PDU_CONFIRM, "TEP Confirm" },
{ TEP_PDU_ACCEPT, "TEP Accept" },
{ TEP_PDU_CONFIRM_ACK, "TEP Confirm Ack" },
{ 0, NULL }
};
#if 0 /* not use yet */
static const value_string tep_error_strings[] = {
{ 1, "Parse Error" },
{ 2, "Access Denied" },
{ 3, "Duration Not Supported" },
{ 4, "Authentication Failed" },
{ 0, NULL }
};
#endif
/* Initialized to zero. */
typedef struct tep_rekey_data
{
/* Stored from the K bit of the Request PDU. */
gboolean is_rekey;
/* Stored from the key request for non-secure rekeys. Otherwise 0 and NULL. */
guint8 domain_length;
guint8 *domain;
/* Stored from the identity of the Request PDU. Seasonal. */
guint8 *i_identity;
guint8 i_identity_length;
/* Stored from the nonce of the Request PDU. Seasonal. */
guint8 *i_nonce;
guint8 i_nonce_length;
/* Stored from the identity of the Request response PDU. Seasonal. */
guint8 *r_identity;
guint8 r_identity_length;
/* Stored from the nonce of the Request response PDU. Seasonal. */
guint8 *r_nonce;
guint8 r_nonce_length;
guint16 security_mode;
guint32 security_mode_data_length;
guint8 *security_mode_data;
/* Security session data for this rekey, if is_rekey is TRUE. */
dof_session_key_exchange_data *key_data;
} tep_rekey_data;
/* DAP V129 (TRP - TICKET REQUEST PROTOCOL V2) */
#define DOF_PROTOCOL_TRP 129
#define DSP_TRP_FAMILY 0x030000
typedef struct _trp_packet_data
{
guint8 *domain;
guint8 domain_length;
guint8 *identity;
guint8 identity_length;
guint8 *group;
guint8 group_length;
guint8 *block_I;
guint16 block_I_length;
guint8 *secret;
gboolean kek_known;
} trp_packet_data;
static int proto_trp = -1;
static int proto_trp_dsp = -1;
static int hf_trp_dsp_option = -1;
static int hf_trp_opcode = -1;
static int hf_domain = -1;
static int hf_identity_resolution = -1;
static int hf_initiator_request = -1;
static int hf_responder_request = -1;
static int hf_initiator_ticket = -1;
static int hf_responder_ticket = -1;
static int hf_authentication_block = -1;
static int hf_group_identifier = -1;
static int hf_node_identifier = -1;
static int hf_thb = -1;
static int hf_tmin = -1;
static int hf_tmax = -1;
static int hf_trp_epoch = -1;
static int hf_sidg = -1;
static int hf_security_scope = -1;
static int hf_security_mode = -1;
static int hf_ssid = -1;
#if 0 /* not used yet */
static int hf_initiator_pg = -1;
#endif
static int hf_initiator_validation = -1;
static int hf_responder_pg = -1;
static int hf_responder_validation = -1;
static int hf_trp_errorcode = -1;
static int hf_trp_duration = -1;
#if 0 /* not used yet */
static int hf_trp_rnonce = -1;
static int hf_trp_pnonce = -1;
static int hf_trp_reqid = -1;
static int hf_trp_provid = -1;
static int hf_trp_perm_count = -1;
static int hf_trp_perm_type = -1;
static int hf_trp_perm_rcache = -1;
static int hf_trp_perm_rsrp = -1;
static int hf_trp_perm_rsrp_a = -1;
static int hf_trp_perm_rsrp_u = -1;
static int hf_trp_perm_rflags = -1;
static int hf_trp_perm_pcache = -1;
static int hf_trp_perm_psrp = -1;
static int hf_trp_perm_psrp_a = -1;
static int hf_trp_perm_psrp_u = -1;
static int hf_trp_perm_psrp_b = -1;
static int hf_trp_perm_psrp_s = -1;
static int hf_trp_perm_pflags = -1;
static int hf_trp_confirmation = -1;
static int hf_trp_perm_pke = -1;
static int hf_trp_perm_pka = -1;
#endif
static int ett_trp_dsp = -1;
static int ett_trp = -1;
static int ett_domain = -1;
static int ett_identity_resolution = -1;
static int ett_initiator_request = -1;
static int ett_initiator_ticket = -1;
static int ett_responder_request = -1;
static int ett_responder_ticket = -1;
static int ett_authentication_block = -1;
static int ett_group_identifier = -1;
static int ett_node_identifier = -1;
static int ett_sidg = -1;
static int ett_security_scope = -1;
static int ett_security_mode = -1;
static int ett_initiator_pg = -1;
static int ett_initiator_validation = -1;
static int ett_responder_pg = -1;
static int ett_responder_validation = -1;
static int ett_trp_permset = -1;
static int ett_srp_flags = -1;
static int ett_trp_ticket = -1;
static expert_field ei_trp_initiator_id_known = EI_INIT;
static expert_field ei_trp_kek_discovered = EI_INIT;
#define TRP_RESPONSE (0x80)
#define TRP_RSP_REJECT (TRP_RESPONSE|0)
#define TRP_CMD_REQUEST_KEK (1)
#define TRP_RSP_REQUEST_KEK (TRP_RESPONSE|TRP_CMD_REQUEST_KEK)
#define TRP_CMD_REQUEST_RANDOM (2)
#define TRP_RSP_REQUEST_RANDOM (TRP_RESPONSE|TRP_CMD_REQUEST_RANDOM)
#define TRP_CMD_REQUEST_SESSION (3)
#define TRP_RSP_REQUEST_SESSION (TRP_RESPONSE|TRP_CMD_REQUEST_SESSION)
#define TRP_CMD_REQUEST_SECURITY_SCOPES (4)
#define TRP_RSP_REQUEST_SECURITY_SCOPES (TRP_RESPONSE|TRP_CMD_REQUEST_SECURITY_SCOPES)
#define TRP_CMD_RESOLVE_CREDENTIAL (6)
#define TRP_RSP_RESOLVE_CREDENTIAL (TRP_RESPONSE|TRP_CMD_RESOLVE_CREDENTIAL)
#define TRP_CMD_REQUEST_LOCAL_DOMAIN (7)
#define TRP_RSP_REQUEST_LOCAL_DOMAIN (TRP_RESPONSE|TRP_CMD_REQUEST_LOCAL_DOMAIN)
#define TRP_CMD_REQUEST_REMOTE_DOMAIN (8)
#define TRP_RSP_REQUEST_REMOTE_DOMAIN (TRP_RESPONSE|TRP_CMD_REQUEST_REMOTE_DOMAIN)
#define TRP_RSP_REQUEST_DISCOVERED_REMOTE_DOMAIN (TRP_RESPONSE|0x0A)
#define TRP_CMD_VALIDATE_CREDENTIAL (9)
#define TRP_RSP_VALIDATE_CREDENTIAL (TRP_RESPONSE|TRP_CMD_VALIDATE_CREDENTIAL)
static const value_string trp_opcode_strings[] = {
{ TRP_RSP_REJECT, "Reject" },
{ TRP_CMD_REQUEST_KEK, "TRP Request KEK" },
{ TRP_RSP_REQUEST_KEK, "TRP Request KEK Response" },
{ TRP_CMD_REQUEST_RANDOM, "TRP Request Random" },
{ TRP_RSP_REQUEST_RANDOM, "TRP Request Random Response" },
{ TRP_CMD_REQUEST_SESSION, "TRP Request Session" },
{ TRP_RSP_REQUEST_SESSION, "TRP Request Session Response" },
{ TRP_CMD_REQUEST_SECURITY_SCOPES, "TRP Request Security Scopes" },
{ TRP_RSP_REQUEST_SECURITY_SCOPES, "TRP Request Security Scopes Response" },
{ TRP_CMD_RESOLVE_CREDENTIAL, "TRP Resolve Credential" },
{ TRP_RSP_RESOLVE_CREDENTIAL, "TRP Resolve Credential Response" },
{ TRP_CMD_REQUEST_LOCAL_DOMAIN, "TRP Request Local Domain" },
{ TRP_RSP_REQUEST_LOCAL_DOMAIN, "TRP Request Local Domain Response" },
{ TRP_CMD_REQUEST_REMOTE_DOMAIN, "TRP Request Remote Domain" },
{ TRP_RSP_REQUEST_REMOTE_DOMAIN, "TRP Request Remote Domain Response" },
{ TRP_RSP_REQUEST_DISCOVERED_REMOTE_DOMAIN, "TRP Request Discovered Remote Domain Response" },
{ TRP_CMD_VALIDATE_CREDENTIAL, "TRP Validate Credential" },
{ TRP_RSP_VALIDATE_CREDENTIAL, "TRP Validate Credential Response" },
{ 0, NULL }
};
static const value_string trp_error_strings[] = {
{ 1, "Parse Error" },
{ 2, "Access Denied" },
{ 3, "Unknown Initiator" },
{ 4, "Unknown Responder" },
{ 5, "Unknown Domain" },
{ 6, "High Load" },
{ 7, "Bad Mode" },
{ 8, "Incompatible Security Identifiers" },
{ 127, "Internal Error" },
{ 0, NULL }
};
/* DAP V130 (SGMP - SECURE GROUP MANAGEMENT PROTOCOL V1) */
#define DOF_PROTOCOL_SGMP 130
typedef struct _sgmp_packet_data
{
guint8 domain_length;
guint8 *domain;
guint8 group_length;
guint8 *group;
guint16 epoch;
guint8 *kek;
guint I_length;
guint8 *I;
guint A_length;
guint8 *A;
dof_session_data *request_session;
} sgmp_packet_data;
static int proto_sgmp = -1;
static int hf_opcode = -1;
static int hf_sgmp_domain = -1;
static int hf_sgmp_epoch = -1;
static int hf_initiator_block = -1;
static int hf_sgmp_security_scope = -1;
static int hf_initial_state = -1;
static int hf_latest_version = -1;
static int hf_desire = -1;
static int hf_ticket = -1;
static int hf_sgmp_tmin = -1;
static int hf_tie_breaker = -1;
static int hf_delay = -1;
static int hf_key = -1;
static int ett_sgmp = -1;
static int ett_sgmp_domain = -1;
static int ett_initiator_block = -1;
static int ett_sgmp_security_scope = -1;
static int ett_initial_state = -1;
static int ett_ticket = -1;
#define SGMP_RESPONSE (0x80)
#define SGMP_CMD_HEARTBEAT (0)
#define SGMP_RSP_HEARTBEAT (SGMP_CMD_HEARTBEAT|SGMP_RESPONSE)
#define SGMP_CMD_EPOCH_CHANGED (1)
#define SGMP_RSP_EPOCH_CHANGED (SGMP_CMD_EPOCH_CHANGED|SGMP_RESPONSE)
#define SGMP_CMD_REKEY (2)
#define SGMP_RSP_REKEY (SGMP_CMD_REKEY|SGMP_RESPONSE)
#define SGMP_CMD_REQUEST_GROUP (3)
#define SGMP_RSP_REQUEST_GROUP (SGMP_CMD_REQUEST_GROUP|SGMP_RESPONSE)
#define SGMP_CMD_REKEY_EPOCH (5)
#define SGMP_RSP_REKEY_EPOCH (SGMP_CMD_REKEY_EPOCH|SGMP_RESPONSE)
#define SGMP_CMD_REKEY_MERGE (7)
#define SGMP_RSP_REKEY_MERGE (SGMP_CMD_REKEY_MERGE|SGMP_RESPONSE)
static const value_string sgmp_opcode_strings[] = {
{ SGMP_CMD_HEARTBEAT, "SGMP Heartbeat" },
{ SGMP_RSP_HEARTBEAT, "SGMP Heartbeat Response (Illegal)" },
{ SGMP_CMD_EPOCH_CHANGED, "SGMP Epoch Changed" },
{ SGMP_RSP_EPOCH_CHANGED, "SGMP Epoch Changed Response (Illegal)" },
{ SGMP_CMD_REKEY, "SGMP Rekey" },
{ SGMP_RSP_REKEY, "SGMP Rekey Response (Illegal)" },
{ SGMP_CMD_REQUEST_GROUP, "SGMP Request Group" },
{ SGMP_RSP_REQUEST_GROUP, "SGMP Request Group Response" },
{ SGMP_CMD_REKEY_EPOCH, "SGMP Rekey Epoch" },
{ SGMP_RSP_REKEY_EPOCH, "SGMP Rekey Epoch Response (Illegal)" },
{ SGMP_CMD_REKEY_MERGE, "SGMP Rekey Merge" },
{ SGMP_RSP_REKEY_MERGE, "SGMP Rekey Merge Response (Illegal)" },
{ 0, NULL }
};
#if 0 /* TODO not used yet */
static gboolean sgmp_validate_session_key(sgmp_packet_data *cmd_data, guint8 *confirmation, guint8 *kek, guint8 *key)
{
#ifdef LIBGCRYPT_OK
gcry_mac_hd_t hmac;
gcry_error_t result;
result = gcry_mac_open(&hmac, GCRY_MAC_HMAC_SHA256, 0, NULL);
if (result != 0)
return FALSE;
gcry_mac_setkey(hmac, kek, 32);
gcry_mac_write(hmac, cmd_data->I, cmd_data->I_length);
gcry_mac_write(hmac, cmd_data->A, cmd_data->A_length);
gcry_mac_write(hmac, key, 32);
result = gcry_mac_verify(hmac, confirmation, sizeof(confirmation));
return result == 0;
#else
return FALSE;
#endif
}
#endif
/* DOF SECURITY PROTOCOL */
#define DOF_SECURITY_PROTOCOL "DOF Security Protocol"
static dissector_table_t dof_sec_dissectors;
#define AS_ASSIGNED_SSID 0x40000000
/* DOFSEC Vxxxx (CCM - COUNTER WITH CBC-MAC PROTOCOL V1) */
#define DOF_PROTOCOL_CCM 24577
#define DSP_CCM_FAMILY 0x020000
static int proto_ccm_app = -1;
static int proto_ccm = -1;
static int proto_ccm_dsp = -1;
static int hf_ccm_dsp_option = -1;
static int hf_ccm_dsp_strength_count = -1;
static int hf_ccm_dsp_strength = -1;
static int hf_ccm_dsp_e_flag = -1;
static int hf_ccm_dsp_m_flag = -1;
static int hf_ccm_dsp_tmax = -1;
static int hf_ccm_dsp_tmin = -1;
static const value_string ccm_strengths[] = {
{ 1, "256-bit" },
{ 2, "192-bit" },
{ 3, "128-bit" },
{ 0, NULL }
};
static int hf_ccm_opcode = -1;
static int hf_epp_v1_ccm_flags = -1;
static int hf_epp_v1_ccm_flags_manager = -1;
static int hf_epp_v1_ccm_flags_period = -1;
static int hf_epp_v1_ccm_flags_target = -1;
static int hf_epp_v1_ccm_flags_next_nid = -1;
static int hf_epp_v1_ccm_flags_packet = -1;
static int hf_epp_v1_ccm_tnid = -1;
static int hf_epp_v1_ccm_nnid = -1;
static int hf_epp_v1_ccm_nid = -1;
static int hf_epp_v1_ccm_slot = -1;
static int hf_epp_v1_ccm_pn = -1;
static int ett_header = -1;
static int ett_epp_v1_ccm_flags = -1;
static int ett_ccm_dsp_option = -1;
static int ett_ccm_dsp = -1;
static int ett_ccm = -1;
static expert_field ei_decode_failure = EI_INIT;
typedef struct _ccm_session_data
{
guint protocol_id;
void *cipher_data;
GHashTable *cipher_data_table;
/* Starts at 1, incrementing for each new key. */
guint32 period;
/* Mapping from wire period to absolute periods. */
guint8 periods[8];
guint8 cipher;
gboolean encrypted;
guint8 mac_len;
guint32 client_datagram_number;
guint32 server_datagram_number;
} ccm_session_data;
typedef struct _ccm_packet_data
{
guint32 nid;
guint32 dn;
guint32 period;
} ccm_packet_data;
#define CCM_PDU_PROBE (0)
static const value_string ccm_opcode_strings[] = {
{ CCM_PDU_PROBE, "Probe" },
{ 0, NULL }
};
/* DOF OBJECT IDENTIFIER (OID) */
#define DOF_OBJECT_IDENTIFIER "DOF Object Identifier"
static dissector_handle_t dof_oid_handle;
static dissector_handle_t undissected_data_handle;
static int oid_proto = -1;
static int hf_oid_class = -1;
static int hf_oid_header = -1;
static int hf_oid_attribute = -1;
static int hf_oid_length = -1;
static int hf_oid_data = -1;
static int hf_oid_all_attribute_data = -1;
static int hf_oid_attribute_header = -1;
static int hf_oid_attribute_attribute = -1;
static int hf_oid_attribute_id = -1;
static int hf_oid_attribute_length = -1;
static int hf_oid_attribute_data = -1;
static int hf_oid_attribute_oid = -1;
static int ett_oid = -1;
static int ett_oid_header = -1;
static int ett_oid_attribute = -1;
static int ett_oid_attribute_header = -1;
static int ett_oid_attribute_oid = -1;
/**
* EXPERT INFOS
* Expert infos are related to either a PDU type or a specification, and so
* they are listed separately.
*/
#if 0
static expert_field ei_undecoded = EI_INIT;
#endif
static expert_field ei_malformed = EI_INIT;
static expert_field ei_implicit_no_op = EI_INIT;
static expert_field ei_c2_c3_c4_format = EI_INIT;
static expert_field ei_type_4_header_zero = EI_INIT;
static expert_field ei_dof_10_flags_zero = EI_INIT;
#if 0
static expert_field ei_dof_13_length_specified = EI_INIT;
#endif
static expert_field ei_dpp2_dof_10_flags_zero = EI_INIT;
static expert_field ei_dpp_default_flags = EI_INIT;
static expert_field ei_dpp_explicit_sender_sid_included = EI_INIT;
static expert_field ei_dpp_explicit_receiver_sid_included = EI_INIT;
static expert_field ei_dpp_no_security_context = EI_INIT;
static expert_field ei_dof_6_timeout = EI_INIT;
static expert_field ei_security_3_1_invalid_stage = EI_INIT;
static expert_field ei_security_4_invalid_bit = EI_INIT;
static expert_field ei_security_13_out_of_range = EI_INIT;
/**
* SOURCE IDENTIFIER (SID) SUPPORT
* Source identifiers are used as part of operation tracking in the
* DOF Protocol Stack. They are version independent, and associated with
* a node in the DOF mesh network. Each session is associated with a SID.
*
* DPP Manages the SID information, since it is DPP that learns about SIDs.
* SIDs are complicated because the can be 'unknown' for periods, and then
* learned later. The requirement here is that all SIDs that can be known
* are known by the second pass of the dissector (pinfo->visited != 0).
*
* There are two hash tables to map to an actual SID. The first goes
* from sender information to SID ID. During the first pass multiple SID ID
* may actually refer to the same SID, and so the system must be able to "patch"
* these values as actual SIDs are learned. The second hash table goes from SID ID
* to actual SID. This lookup is only known after a real SID has been learned.
*
* The hash tables are used in order to look up full SID information when only
* partial information is known, and must support looking up in both directions
* based on what is known from a particular PDU.
*/
static GHashTable *node_key_to_sid_id = NULL;
static GHashTable *sid_buffer_to_sid_id = NULL;
static GHashTable *sid_id_to_sid_buffer = NULL;
typedef struct _node_key_to_sid_id_key
{
gint transport_id;
gint transport_node_id;
gint dof_id;
gint dof_node_id;
gint dof_session_id;
} node_key_to_sid_id_key;
static guint sender_key_hash_fn(gconstpointer key)
{
const node_key_to_sid_id_key *sid_key_ptr = (const node_key_to_sid_id_key *)key;
guint result = 0;
result += g_int_hash(&(sid_key_ptr->transport_id));
result += g_int_hash(&(sid_key_ptr->transport_node_id));
result += g_int_hash(&(sid_key_ptr->dof_id));
result += g_int_hash(&(sid_key_ptr->dof_node_id));
result += g_int_hash(&(sid_key_ptr->dof_session_id));
return result;
}
static guint sid_buffer_hash_fn(gconstpointer key)
{
/* The sid buffer is a length byte followed by data. */
guint hash = 5381;
const guint8 *str = (const guint8 *)key;
guint8 i;
for (i = 0; i <= str[0]; i++)
hash = ((hash << 5) + hash) + str[i]; /* hash * 33 + c */
return hash;
}
static gboolean sender_key_equal_fn(gconstpointer key1, gconstpointer key2)
{
const node_key_to_sid_id_key *sid_key_ptr1 = (const node_key_to_sid_id_key *)key1;
const node_key_to_sid_id_key *sid_key_ptr2 = (const node_key_to_sid_id_key *)key2;
if (sid_key_ptr1->transport_id != sid_key_ptr2->transport_id)
return FALSE;
if (sid_key_ptr1->transport_node_id != sid_key_ptr2->transport_node_id)
return FALSE;
if (sid_key_ptr1->dof_id != sid_key_ptr2->dof_id)
return FALSE;
if (sid_key_ptr1->dof_node_id != sid_key_ptr2->dof_node_id)
return FALSE;
if (sid_key_ptr1->dof_session_id != sid_key_ptr2->dof_session_id)
return FALSE;
return TRUE;
}
static gboolean sid_buffer_equal_fn(gconstpointer key1, gconstpointer key2)
{
const guint8 *sb1 = (const guint8 *)key1;
const guint8 *sb2 = (const guint8 *)key2;
if (sb1[0] != sb2[0])
return FALSE;
return memcmp(sb1 + 1, sb2 + 1, sb1[0]) == 0;
}
static guint dpp_next_sid_id = 1;
/**
* This routine is called for each reset (file load, capture) and is responsible
* for allocating the SID support hash tables. Previous information is freed
* if needed.
*/
static void dpp_reset_sid_support(void)
{
dpp_next_sid_id = 1;
if (node_key_to_sid_id != NULL)
{
g_hash_table_destroy(node_key_to_sid_id);
node_key_to_sid_id = NULL;
}
if (sid_buffer_to_sid_id != NULL)
{
g_hash_table_destroy(sid_buffer_to_sid_id);
sid_buffer_to_sid_id = NULL;
}
if (sid_id_to_sid_buffer != NULL)
{
g_hash_table_destroy(sid_id_to_sid_buffer);
sid_id_to_sid_buffer = NULL;
}
/* The value is not allocated, so does not need to be freed. */
node_key_to_sid_id = g_hash_table_new_full(sender_key_hash_fn, sender_key_equal_fn, g_free, NULL);
sid_buffer_to_sid_id = g_hash_table_new_full(sid_buffer_hash_fn, sid_buffer_equal_fn, g_free, NULL);
sid_id_to_sid_buffer = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
}
/**
* OPERATION IDENTIFIER SUPPORT
* Operation identifiers are an extension of a SID, and represent each separate
* operation in the DOF. They are identified by a SID and an operation count.
* Like SIDs, they are indepenent of version (at least in meaning, the formatting
* may change).
*
* The hash is used to look up common operation information each time an operation
* is seen in any packet.
*/
static GHashTable *dpp_opid_to_packet_data = NULL;
static guint dpp_opid_hash_fn(gconstpointer opid)
{
const dof_2009_1_pdu_20_opid *ptr = (const dof_2009_1_pdu_20_opid *)opid;
return g_int_hash(&ptr->op_sid_id) + g_int_hash(&ptr->op_cnt);
}
static gboolean dpp_opid_equal_fn(gconstpointer opid1, gconstpointer opid2)
{
const dof_2009_1_pdu_20_opid *ptr1 = (const dof_2009_1_pdu_20_opid *)opid1;
const dof_2009_1_pdu_20_opid *ptr2 = (const dof_2009_1_pdu_20_opid *)opid2;
if (ptr1->op_cnt != ptr2->op_cnt)
return FALSE;
if (ptr1->op_sid_id != ptr2->op_sid_id)
return FALSE;
return TRUE;
}
static void dpp_reset_opid_support(void)
{
if (dpp_opid_to_packet_data != NULL)
{
/* Clear it out. Note that this calls the destroy functions for each element. */
g_hash_table_destroy(dpp_opid_to_packet_data);
dpp_opid_to_packet_data = NULL;
}
dpp_opid_to_packet_data = g_hash_table_new_full(dpp_opid_hash_fn, dpp_opid_equal_fn, NULL, NULL);
}
/**
* NON-SECURE SESSION LOOKUP SUPPORT
*/
static GHashTable *dof_ns_session_lookup = NULL;
/**
* NON-SECURE DPS SESSION
* This is defined by the transport session and the DNP port information.
*/
typedef struct _dof_ns_session_key
{
guint transport_session_id;
guint client;
guint server;
gboolean is_secure;
} dof_ns_session_key;
static dof_session_data* dof_ns_session_retrieve(guint transport_session_id, guint client, guint server)
{
dof_ns_session_key lookup_key;
dof_session_data *value;
/* Build a (non-allocated) key to do the lookup. */
lookup_key.transport_session_id = transport_session_id;
lookup_key.client = client;
lookup_key.server = server;
value = (dof_session_data *)g_hash_table_lookup(dof_ns_session_lookup, &lookup_key);
if (value)
{
/* We found a match. */
return value;
}
return NULL;
}
static void dof_ns_session_define(guint transport_session_id, guint client, guint server, dof_session_data *session_data)
{
dof_ns_session_key *key;
/* No match, need to add a key. */
key = g_new0(dof_ns_session_key, 1);
key->transport_session_id = transport_session_id;
key->client = client;
key->server = server;
/* Note, this is not multithread safe, but Wireshark isn't multithreaded. */
g_hash_table_insert(dof_ns_session_lookup, key, session_data);
}
/* COMMON PDU DISSECTORS */
/* Security.1 */
static int hf_security_1_permission_type = -1;
static int hf_security_1_length = -1;
static int hf_security_1_data = -1;
static const value_string dof_2008_16_permission_type[] = {
{ 1, "Binding" },
{ 3, "IAM" },
{ 5, "ACTAS" },
{ 128, "Requestor" },
{ 130, "Provider" },
{ 131, "Define" },
{ 133, "Tunnel Domain" },
{ 0, NULL }
};
/* Security.2 */
static int hf_security_2_count = -1;
static int ett_security_2_permission = -1;
static int hf_security_2_permission = -1;
/* Security.3.1 */
static int hf_security_3_1_credential_type = -1;
static int hf_security_3_1_stage = -1;
static int ett_security_3_1_security_node_identifier = -1;
static int hf_security_3_1_security_node_identifier = -1;
/* Security.3.2 */
static int hf_security_3_2_credential_type = -1;
static int hf_security_3_2_stage = -1;
static int hf_security_3_2_length = -1;
static int hf_security_3_2_public_data = -1;
/* Security.4 */
static int hf_security_4_l = -1;
static int hf_security_4_f = -1;
static int hf_security_4_ln = -1;
static int ett_security_4_identity = -1;
static int hf_security_4_identity = -1;
static int hf_security_4_nonce = -1;
static int ett_security_4_permission_set = -1;
static int hf_security_4_permission_set = -1;
/* Security.5 */
static int hf_security_5_mac = -1;
static int hf_security_5_key = -1;
/* Security.6.1 */
static int hf_security_6_1_desired_duration = -1;
static int ett_security_6_1_desired_security_mode = -1;
static int hf_security_6_1_desired_security_mode = -1;
static int ett_security_6_1_initiator_request = -1;
static int hf_security_6_1_initiator_request = -1;
/* Security.6.2 */
static int ett_security_6_2_responder_request = -1;
static int hf_security_6_2_responder_request = -1;
/* Security.6.3 */
static int hf_security_6_3_granted_duration = -1;
static int ett_security_6_3_session_security_scope = -1;
static int hf_security_6_3_session_security_scope = -1;
static int ett_security_6_3_initiator_validation = -1;
static int hf_security_6_3_initiator_validation = -1;
static int ett_security_6_3_responder_validation = -1;
static int hf_security_6_3_responder_validation = -1;
/* Security.9 */
static int hf_security_9_length = -1;
static int hf_security_9_initial_state = -1;
/* Security.10 */
static int hf_security_10_count = -1;
static int hf_security_10_permission_group_identifier = -1;
/* Security.11 */
static int hf_security_11_count = -1;
static int ett_security_11_permission_security_scope = -1;
static int hf_security_11_permission_security_scope = -1;
/* Security.12 */
static int hf_security_12_m = -1;
static const value_string dof_2008_16_security_12_m[] = {
{ 0, "Reference" },
{ 1, "Relative" },
{ 2, "Absolute" },
{ 3, "Continued" },
{ 0, NULL }
};
static int hf_security_12_count = -1;
static int hf_security_12_permission_group_identifier = -1;
static int dissect_2008_1_dsp_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
proto_item *parent = proto_tree_get_parent(tree);
guint8 attribute_code = tvb_get_guint8(tvb, 0);
guint16 attribute_data = tvb_get_ntohs(tvb, 1);
guint8 option_length = tvb_get_guint8(tvb, 3);
/* Add the generic representation of the fields. */
proto_tree_add_item(tree, hf_2008_1_dsp_attribute_code, tvb, 0, 1, ENC_NA);
proto_tree_add_item(tree, hf_2008_1_dsp_attribute_data, tvb, 1, 2, ENC_BIG_ENDIAN);
proto_tree_add_item(tree, hf_2008_1_dsp_value_length, tvb, 3, 1, ENC_NA);
/* Append description to the parent. */
proto_item_append_text(parent, " (Code=%s/Data=0x%04x)", val_to_str(attribute_code, strings_2008_1_dsp_attribute_codes, "%u"), attribute_data);
if (option_length)
{
proto_tree_add_item(tree, hf_2008_1_dsp_value_data, tvb, 4, option_length, ENC_NA);
/* call the next dissector */
tvb_set_reported_length(tvb, option_length + 4);
dissector_try_uint(dsp_option_dissectors, (attribute_code << 16) | attribute_data, tvb, pinfo, tree);
}
return option_length + 4;
}
/**
* Security.1: Permission. This is the base type for
* permissions, and supports extension.
*/
static int dissect_2008_16_security_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint offset = 0;
gboolean has_length;
guint16 length;
/* Permission Type */
{
gint start = offset;
guint16 value;
gint val_len;
proto_item *pi;
offset = read_c2(tvb, offset, &value, &val_len);
has_length = (gboolean)(value % 2);
pi = proto_tree_add_uint(tree, hf_security_1_permission_type, tvb, start, offset - start, value);
validate_c2(pinfo, pi, value, val_len);
}
if (!has_length)
return offset;
/* Length */
{
gint start = offset;
guint16 value;
gint value_len;
proto_item *pi;
offset = read_c2(tvb, offset, &value, &value_len);
length = value;
pi = proto_tree_add_uint(tree, hf_security_1_length, tvb, start, offset - start, value);
validate_c2(pinfo, pi, value, value_len);
}
/* Data */
proto_tree_add_item(tree, hf_security_1_data, tvb, offset, length, ENC_NA);
offset += length;
return offset;
}
/**
* Security.2: Permission Request.
*/
static int dissect_2008_16_security_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint offset = 0;
guint16 count;
/* Count */
{
gint start = offset;
guint16 value;
gint length;
proto_item *pi;
offset = read_c2(tvb, offset, &value, &length);
count = value;
pi = proto_tree_add_uint(tree, hf_security_2_count, tvb, start, offset - start, value);
validate_c2(pinfo, pi, value, length);
}
while (count--)
{
proto_item *ti = proto_tree_add_item(tree, hf_security_2_permission, tvb, offset, -1, ENC_NA);
proto_tree *subtree = proto_item_add_subtree(ti, ett_security_2_permission);
tvbuff_t *next_tvb = tvb_new_subset(tvb, offset, -1, -1);
gint len = dissect_2008_16_security_1(next_tvb, pinfo, subtree, NULL);
proto_item_set_len(ti, len);
offset += len;
}
return offset;
}
/**
* Security.3.1: Base Credential Format.
* Returns: dof_2008_16_security_3_1
*/
static int dissect_2008_16_security_3_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
gint offset = 0;
guint8 stage;
proto_item *ti;
dof_2008_16_security_3_1 *return_data = (dof_2008_16_security_3_1 *)data;
/* Credential Type */
{
gint start = offset;
guint16 value;
gint length;
proto_item *pi;
offset = read_c2(tvb, offset, &value, &length);
pi = proto_tree_add_uint(tree, hf_security_3_1_credential_type, tvb, start, offset - start, value);
validate_c2(pinfo, pi, value, length);
}
/* Stage */
stage = tvb_get_guint8(tvb, offset);
ti = proto_tree_add_item(tree, hf_security_3_1_stage, tvb, offset, 1, ENC_NA);
offset += 1;
if (stage != 0)
expert_add_info(pinfo, ti, &ei_security_3_1_invalid_stage);
/* Security Node Identifier */
{
int block_length;
tvbuff_t *start = tvb_new_subset(tvb, offset, -1, -1);
proto_tree *subtree;
ti = proto_tree_add_item(tree, hf_security_3_1_security_node_identifier, tvb, offset, 0, ENC_NA);
subtree = proto_item_add_subtree(ti, ett_security_3_1_security_node_identifier);
block_length = dissect_2008_16_security_8(start, pinfo, subtree, NULL);
proto_item_set_len(ti, block_length);
offset += block_length;
tvb_set_reported_length(start, block_length);
if (return_data)
return_data->identity = start;
}
return offset;
}
/**
* Security.3.2: Identity Resolution.
*/
int dissect_2008_16_security_3_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint offset = 0;
guint16 length;
/* Credential Type */
{
gint start = offset;
guint16 value;
gint val_len;
proto_item *pi;
offset = read_c2(tvb, offset, &value, &val_len);
pi = proto_tree_add_uint(tree, hf_security_3_2_credential_type, tvb, start, offset - start, value);
validate_c2(pinfo, pi, value, val_len);
}
/* Stage */
proto_tree_add_item(tree, hf_security_3_2_stage, tvb, offset, 1, ENC_NA);
offset += 1;
/* Length */
{
gint start = offset;
guint16 value;
gint value_len;
proto_item *pi;
offset = read_c2(tvb, offset, &value, &value_len);
length = value;
pi = proto_tree_add_uint(tree, hf_security_3_2_length, tvb, start, offset - start, value);
validate_c2(pinfo, pi, value, value_len);
}
/* Public Data */
proto_tree_add_item(tree, hf_security_3_2_public_data, tvb, offset, length, ENC_NA);
offset += length;
return offset;
}
/**
* Security.4: Key Request. Returns: dof_2008_16_security_4
*/
static int dissect_2008_16_security_4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
gint offset = 0;
guint8 flag;
dof_2008_16_security_4 *return_data = (dof_2008_16_security_4 *)data;
flag = tvb_get_guint8(tvb, offset);
if (flag & 0x30)
expert_add_info(pinfo, tree, &ei_security_4_invalid_bit);
proto_tree_add_item(tree, hf_security_4_l, tvb, offset, 1, ENC_NA);
proto_tree_add_item(tree, hf_security_4_f, tvb, offset, 1, ENC_NA);
proto_tree_add_item(tree, hf_security_4_ln, tvb, offset, 1, ENC_NA);
offset += 1;
{
int block_length;
tvbuff_t *start = tvb_new_subset(tvb, offset, -1, -1);
proto_item *ti;
proto_tree *subtree;
dof_2008_16_security_3_1 return_3_1;
ti = proto_tree_add_item(tree, hf_security_4_identity, tvb, offset, 0, ENC_NA);
subtree = proto_item_add_subtree(ti, ett_security_4_identity);
block_length = dissect_2008_16_security_3_1(start, pinfo, subtree, &return_3_1);
proto_item_set_len(ti, block_length);
offset += block_length;
if (return_data)
{
return_data->identity = return_3_1.identity;
}
}
{
tvbuff_t *start = tvb_new_subset(tvb, offset, (flag & 0x0F) + 1, (flag & 0x0F) + 1);
if (return_data)
return_data->nonce = start;
proto_tree_add_item(tree, hf_security_4_nonce, start, 0, (flag & 0x0F) + 1, ENC_NA);
offset += (flag & 0x0F) + 1;
}
{
int block_length;
tvbuff_t *start = tvb_new_subset(tvb, offset, -1, -1);
proto_item *ti;
proto_tree *subtree;
ti = proto_tree_add_item(tree, hf_security_4_permission_set, tvb, offset, 0, ENC_NA);
subtree = proto_item_add_subtree(ti, ett_security_4_permission_set);
block_length = dissect_2008_16_security_2(start, pinfo, subtree, NULL);
proto_item_set_len(ti, block_length);
offset += block_length;
}
return offset;
}
/**
* Security.5: Key Grant.
*/
static int dissect_2008_16_security_5(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
{
gint offset = 0;
proto_tree_add_item(tree, hf_security_5_mac, tvb, offset, 32, ENC_NA);
offset += 32;
proto_tree_add_item(tree, hf_security_5_key, tvb, offset, 32, ENC_NA);
offset += 32;
return offset;
}
/**
* Security.6.1: Session Initator Block.
* Returns dof_2008_16_security_6_1
*/
static int dissect_2008_16_security_6_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
gint offset = 0;
/* Allocate the return structure. */
dof_2008_16_security_6_1 *return_data = (dof_2008_16_security_6_1 *)data;
/* Desired Duration */
proto_tree_add_item(tree, hf_security_6_1_desired_duration, tvb, offset, 1, ENC_NA);
offset += 1;
/* Desired Security Mode */
{
int block_length;
tvbuff_t *start = tvb_new_subset(tvb, offset, -1, -1);
proto_item *ti;
proto_tree *subtree;
ti = proto_tree_add_item(tree, hf_security_6_1_desired_security_mode, tvb, offset, 0, ENC_NA);
subtree = proto_item_add_subtree(ti, ett_security_6_1_desired_security_mode);
block_length = dissect_2008_16_security_13(start, pinfo, subtree, NULL);
offset += block_length;
tvb_set_reported_length(start, block_length);
proto_item_set_len(ti, block_length);
if (return_data)
{
return_data->security_mode = tvb_get_ntohs(start, 1);
return_data->security_mode_data_length = block_length - 4;
return_data->security_mode_data = (guint8 *)tvb_memdup(wmem_file_scope(), start, 4, block_length - 4);
}
}
/* Initiator Request */
{
int block_length;
dof_2008_16_security_4 output;
tvbuff_t *start = tvb_new_subset(tvb, offset, -1, -1);
proto_item *ti;
proto_tree *subtree;
ti = proto_tree_add_item(tree, hf_security_6_1_initiator_request, tvb, offset, 0, ENC_NA);
subtree = proto_item_add_subtree(ti, ett_security_6_1_initiator_request);
block_length = dissect_2008_16_security_4(start, pinfo, subtree, &output);
proto_item_set_len(ti, block_length);
offset += block_length;
if (return_data)
{
return_data->i_identity = output.identity;
return_data->i_nonce = output.nonce;
}
}
return offset;
}
/**
* Security.6.2: Session Responder Block.
* Returns dof_2008_16_security_6_2
*/
static int dissect_2008_16_security_6_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
gint offset = 0;
dof_2008_16_security_6_2 *return_data = (dof_2008_16_security_6_2 *)data;
/* Responder Request */
{
int block_length;
dof_2008_16_security_4 output;
tvbuff_t *start = tvb_new_subset(tvb, offset, -1, -1);
proto_item *ti;
proto_tree *subtree;
ti = proto_tree_add_item(tree, hf_security_6_2_responder_request, tvb, offset, 0, ENC_NA);
subtree = proto_item_add_subtree(ti, ett_security_6_2_responder_request);
block_length = dissect_2008_16_security_4(start, pinfo, subtree, &output);
proto_item_set_len(ti, block_length);
offset += block_length;
if (return_data)
{
return_data->r_identity = output.identity;
return_data->r_nonce = output.nonce;
}
}
return offset;
}
/**
* Security.6.3: Authentication Response Block.
*/
static int dissect_2008_16_security_6_3(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint offset = 0;
/* Granted Duration */
proto_tree_add_item(tree, hf_security_6_3_granted_duration, tvb, offset, 1, ENC_NA);
offset += 1;
/* Session Security Scope */
{
int block_length;
tvbuff_t *start = tvb_new_subset(tvb, offset, -1, -1);
proto_item *ti;
proto_tree *subtree;
ti = proto_tree_add_item(tree, hf_security_6_3_session_security_scope, tvb, offset, 0, ENC_NA);
subtree = proto_item_add_subtree(ti, ett_security_6_3_session_security_scope);
block_length = dissect_2008_16_security_10(start, pinfo, subtree, NULL);
proto_item_set_len(ti, block_length);
offset += block_length;
}
/* Initiator Validation */
{
int block_length;
tvbuff_t *start = tvb_new_subset(tvb, offset, -1, -1);
proto_item *ti;
proto_tree *subtree;
ti = proto_tree_add_item(tree, hf_security_6_3_initiator_validation, tvb, offset, 0, ENC_NA);
subtree = proto_item_add_subtree(ti, ett_security_6_3_initiator_validation);
block_length = dissect_2008_16_security_11(start, pinfo, subtree, NULL);
proto_item_set_len(ti, block_length);
offset += block_length;
}
/* Responder Validation */
{
int block_length;
tvbuff_t *start = tvb_new_subset(tvb, offset, -1, -1);
proto_item *ti;
proto_tree *subtree;
ti = proto_tree_add_item(tree, hf_security_6_3_responder_validation, tvb, offset, 0, ENC_NA);
subtree = proto_item_add_subtree(ti, ett_security_6_3_responder_validation);
block_length = dissect_2008_16_security_11(start, pinfo, subtree, NULL);
proto_item_set_len(ti, block_length);
offset += block_length;
}
return offset;
}
/**
* Security.7: Security Domain.
*/
static int dissect_2008_16_security_7(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
/* Parse the base type. */
gint block_length;
block_length = dissect_2009_11_type_4(tvb, pinfo, tree, NULL);
return block_length;
}
/**
* Security.8: Security Node Identifier.
*/
static int dissect_2008_16_security_8(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
/* Parse the base type. */
gint block_length;
block_length = dissect_2009_11_type_4(tvb, pinfo, tree, NULL);
return block_length;
}
/**
* Security.9: Security Mode of Operation Initialization.
* If the packet info has knowledge of the active security mode
* of operation then this datagram can be further decoded.
*/
static int dissect_2008_16_security_9(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint offset = 0;
guint16 length;
/* Length */
{
gint start = offset;
guint16 value;
gint value_len;
proto_item *pi;
offset = read_c2(tvb, offset, &value, &value_len);
length = value;
pi = proto_tree_add_uint(tree, hf_security_9_length, tvb, start, offset - start, value);
validate_c2(pinfo, pi, value, value_len);
}
if (length > 0)
{
proto_tree_add_item(tree, hf_security_9_initial_state, tvb, offset, length, ENC_NA);
offset += length;
}
return offset;
}
/**
* Security.10: Security Scope.
*/
static int dissect_2008_16_security_10(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint offset = 0;
guint16 count;
/* Count */
{
gint start = offset;
guint16 value;
gint length;
proto_item *pi;
offset = read_c2(tvb, offset, &value, &length);
count = value;
pi = proto_tree_add_uint(tree, hf_security_10_count, tvb, start, offset - start, value);
validate_c2(pinfo, pi, value, length);
}
while (count--)
{
const char *def = "";
gint start = offset;
guint32 value;
gint length;
proto_item *pi;
offset = read_c4(tvb, offset, &value, &length);
switch (value)
{
case 0x3FFFFFFF:
def = " (all scopes)";
break;
case 0x3FFFFFFE:
def = " (doesn't mask)";
break;
case 0x3FFFFFFD:
def = " (session scope)";
break;
}
pi = proto_tree_add_uint_format_value(tree, hf_security_10_permission_group_identifier, tvb, start, offset - start, value, "%u%s", value, def);
validate_c4(pinfo, pi, value, length);
}
return offset;
}
/**
* Security.11: Permission Validation.
*/
static int dissect_2008_16_security_11(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint offset = 0;
guint16 count;
/* Count */
{
gint start = offset;
guint16 value;
gint length;
proto_item *pi;
offset = read_c2(tvb, offset, &value, &length);
count = value;
pi = proto_tree_add_uint(tree, hf_security_11_count, tvb, start, offset - start, value);
validate_c2(pinfo, pi, value, length);
}
while (count--)
{
proto_item *ti = proto_tree_add_item(tree, hf_security_11_permission_security_scope, tvb, offset, -1, ENC_NA);
proto_tree *subtree = proto_item_add_subtree(ti, ett_security_11_permission_security_scope);
tvbuff_t *next_tvb = tvb_new_subset(tvb, offset, -1, -1);
gint len;
len = dissect_2008_16_security_12(next_tvb, pinfo, subtree, NULL);
proto_item_set_len(ti, len);
offset += len;
}
return offset;
}
/**
* Security.12: Permission Security Scope.
*/
static int dissect_2008_16_security_12(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint offset = 0;
guint8 m = tvb_get_guint8(tvb, offset) >> 6;
guint16 count = tvb_get_guint8(tvb, offset) & 0x3F;
proto_item *pi;
proto_tree_add_item(tree, hf_security_12_m, tvb, offset, 1, ENC_NA);
proto_tree_add_item(tree, hf_security_12_count, tvb, offset, 1, ENC_NA);
offset += 1;
if (m == 0)
return offset;
while (count--)
{
const char *def = "";
gint start = offset;
guint32 value;
gint length;
offset = read_c4(tvb, offset, &value, &length);
switch (value)
{
case 0x3FFFFFFF:
def = " (all scopes)";
break;
case 0x3FFFFFFE:
def = " (doesn't mask)";
break;
case 0x3FFFFFFD:
def = " (session scope)";
break;
}
pi = proto_tree_add_uint_format_value(tree, hf_security_12_permission_group_identifier, tvb, start, offset - start, value, "%u%s", value, def);
validate_c4(pinfo, pi, value, length);
}
return offset;
}
/**
* Security.13: Security Mode of Operation Negotiation.
*/
static int dissect_2008_16_security_13(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
/* Parse the base type. */
gint block_length;
guint16 attribute_data;
/* TODO: Skipping this first byte means that no other encryption modes can be supported. */
attribute_data = tvb_get_ntohs(tvb, 1);
if (attribute_data < 0x6000 || attribute_data >= 0x7000)
expert_add_info(pinfo, tree, &ei_security_13_out_of_range);
block_length = dissect_2008_1_dsp_1(tvb, pinfo, tree);
return block_length;
}
/**
* Dissects a buffer that is pointing at an OID.
* Adds a subtree with detailed information about the fields of
* the OID,
* returns the length of the OID,
* and appends text to the tree (really a tree item) that is
* passed in that gives a more accurate description of the OID.
* Note that the tree already shows the bytes of the OID, so if
* no additional information can be displayed then it should not
* be.
*
* If 'tree' is NULL then just return the length.
*/
static gint dissect_2009_11_type_4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
proto_item *ti;
gint start_offset = 0;
gint offset = 0;
guint32 oid_class;
gint oid_class_len;
guint8 oid_len_byte;
proto_tree *oid_tree = tree;
proto_tree *header_tree;
if (tree)
{
ti = proto_tree_get_parent(tree);
proto_item_set_text(ti, "Object ID: %s", dof_oid_create_standard_string(tvb_reported_length(tvb), tvb_get_ptr(tvb, 0, tvb_reported_length(tvb))));
}
offset = read_c4(tvb, offset, &oid_class, &oid_class_len);
ti = proto_tree_add_uint_format(oid_tree, hf_oid_class, tvb, start_offset, offset - start_offset, oid_class, "Class: %u", oid_class);
validate_c4(pinfo, ti, oid_class, oid_class_len);
oid_len_byte = tvb_get_guint8(tvb, offset);
ti = proto_tree_add_uint_format(oid_tree, hf_oid_header, tvb,
offset, 1, oid_len_byte, "Header: 0x%02x (%sLength=%d)", oid_len_byte, oid_len_byte & 0x80 ? "Attribute, " : "", oid_len_byte & 0x3F);
header_tree = proto_item_add_subtree(ti, ett_oid_header);
proto_tree_add_item(header_tree, hf_oid_attribute, tvb, offset, 1, ENC_NA);
proto_tree_add_item(header_tree, hf_oid_length, tvb, offset, 1, ENC_NA);
offset += 1;
/* Validate the flag byte */
if (oid_len_byte & 0x40)
{
/* Type.4 Malformed (bit mandated zero). */
expert_add_info(pinfo, ti, &ei_type_4_header_zero);
}
if ((oid_len_byte & 0x3F) > 0)
{
/* Add the raw data. */
proto_tree_add_item(oid_tree, hf_oid_data, tvb, offset, oid_len_byte & 0x3F, ENC_NA);
offset += oid_len_byte & 0x3F;
}
/* Check for attributes */
if (oid_len_byte & 0x80)
{
/* Read attributes, adding them to oid_tree. */
guint8 flag;
do
{
tvbuff_t *packet = tvb_new_subset(tvb, offset, -1, -1);
proto_tree *attribute_tree;
gint attribute_length;
ti = proto_tree_add_item(tree, hf_oid_all_attribute_data, tvb, offset, -1, ENC_NA);
attribute_tree = proto_item_add_subtree(ti, ett_oid_attribute);
flag = tvb_get_guint8(tvb, offset);
attribute_length = dissect_2009_11_type_5(packet, pinfo, attribute_tree);
proto_item_set_len(ti, (const gint)attribute_length);
offset += attribute_length;
}
while (flag & 0x80);
}
if (tree)
{
ti = proto_tree_get_parent(tree);
proto_item_set_len(ti, offset - start_offset);
}
/* TODO: Add the description. */
/* proto_item_append_text( oid_tree, ": %s", "TODO" ); */
return offset;
}
/**
* Dissects a buffer that is pointing at an attribute.
* Adds a subtree with detailed information about the fields of
* the attribute,
* returns the new offset,
* and appends text to the tree (really a tree item) that is
* passed in that gives a more accurate description of the
* attribute.
* Note that the tree already shows the bytes of the OID, so if
* no additional information can be displayed then it should not
* be.
*
* If 'tree' is NULL then just return the length.
*/
static int dissect_2009_11_type_5(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
proto_item *ti;
gint offset = 0;
guint8 attribute_id_byte;
guint8 attribute_length_byte;
proto_tree *oid_tree = tree;
proto_tree *header_tree;
attribute_id_byte = tvb_get_guint8(tvb, offset);
ti = proto_tree_add_uint_format(oid_tree, hf_oid_attribute_header, tvb,
offset, 1, attribute_id_byte, "Header: 0x%02x (%sLength=%d)", attribute_id_byte, attribute_id_byte & 0x80 ? "Attribute, " : "", attribute_id_byte & 0x3F);
header_tree = proto_item_add_subtree(ti, ett_oid_attribute_header);
proto_tree_add_item(header_tree, hf_oid_attribute_attribute, tvb, offset, 1, ENC_NA);
proto_tree_add_item(header_tree, hf_oid_attribute_id, tvb, offset, 1, ENC_NA);
offset += 1;
attribute_length_byte = tvb_get_guint8(tvb, offset);
proto_tree_add_item(oid_tree, hf_oid_attribute_length, tvb, offset, 1, ENC_NA);
offset += 1;
switch (attribute_id_byte & 0x7F)
{
case 1:
/* TODO: Check length */
proto_tree_add_item(oid_tree, hf_oid_attribute_data, tvb, offset, attribute_length_byte, ENC_NA);
offset += attribute_length_byte;
break;
case 0:
case 2:
{
tvbuff_t *packet = tvb_new_subset(tvb, offset, attribute_length_byte, attribute_length_byte);
proto_tree *attribute_tree;
ti = proto_tree_add_item(tree, hf_oid_attribute_oid, tvb, offset, -1, ENC_NA);
attribute_tree = proto_item_add_subtree(ti, ett_oid_attribute_oid);
offset += dissect_2009_11_type_4(packet, pinfo, attribute_tree, NULL);
}
break;
default:
proto_tree_add_item(oid_tree, hf_oid_attribute_data, tvb, offset, attribute_length_byte, ENC_NA);
offset += attribute_length_byte;
}
return offset;
}
/* Transport Session ID */
static dof_globals globals;
/* Static Methods. */
static dof_packet_data* create_packet_data(packet_info *pinfo);
static int dof_dissect_dnp_length(tvbuff_t *tvb, packet_info *pinfo, guint8 version, gint *offset);
static void encryptInPlace(guint protocol_id, void *cipher_state, guint8 *ptct, guint8 ptct_len);
#define VALIDHEX(c) ( ((c) >= '0' && (c) <= '9') || ((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f') )
/* Configuration structures. These tables allow for security
* mode templates, security keys, and secrets to be configured.
*/
static gboolean decrypt_all_packets = FALSE;
static gboolean track_operations = FALSE;
static guint track_operations_window = 5;
static guint32 next_dof_frame = 1;
/* Structure for security mode of operation templates. */
typedef struct _secmode_field_t {
gchar *domain;
gchar *identity;
gchar *kek;
} secmode_field_t;
static secmode_field_t *secmode_list = NULL;
static guint num_secmode_list = 0;
/* Structure for security keys. */
typedef struct _seckey_field_t {
gchar *key;
} seckey_field_t;
/* Structure for secrets (for identities) */
typedef struct _identsecret_field_t {
gchar *domain;
gchar *identity;
gchar *secret;
} identsecret_field_t;
typedef struct _tcp_ignore_data
{
guint32 sequence;
gboolean ignore;
struct _tcp_ignore_data *next;
} tcp_ignore_data;
typedef struct _tcp_dof_packet_ref
{
/* A single TCP frame can contain multiple packets. We must
* be able to keep track of them all.
*/
dof_api_data api_data;
guint16 start_offset;
dof_transport_packet transport_packet;
struct _tcp_dof_packet_ref *next;
} tcp_dof_packet_ref;
/**
* This structure exists for TCP packets and allows matching Wireshark frames to
* DPS packets.
*/
typedef struct _tcp_packet_data
{
/* Packets are ignored based on the starting TCP SEQ (sequence of first byte). */
tcp_ignore_data *from_client_ignore_list;
tcp_ignore_data *from_server_ignore_list;
/* DPS packet structures contained within a TCP frame. */
tcp_dof_packet_ref *dof_packets;
} tcp_packet_data;
/**
* This structure exists for UDP sessions and allows for advanced stream handling
* and matching Wireshark frames to DPS packets.
*/
typedef struct _udp_session_data
{
/* This must be the first structure, as a pointer to this type is stored in each DPS packet. */
dof_transport_session common;
/* For the associated TCP conversation, this tracks the client and server
* addresses.
*/
ws_node server;
} udp_session_data;
/* This structure exists for TCP sessions and allows for advanced stream handling
* and matching Wireshark frames to DPS packets.
*/
typedef struct _tcp_session_data
{
/* This must be the first structure, as a pointer to this type is stored in each DPS packet. */
dof_transport_session common;
/* This flag is used to determine that an entire TCP session is NOT OpenDOF.
* Because of TCP/IP negotation in the DPS it is easy to confuse arbitrary
* protocols as OpenDOF. Once it is determined that it is not then this
* flag can be set, which will turn off all the OpenDOF dissectors.
*/
gboolean not_dps;
/* For the associated TCP conversation, this tracks the client and server
* addresses.
*/
ws_node client, server;
/* TCP sequence numbers, used to detect retransmissions. These are only valid
* during the first pass through the packets.
*/
guint32 from_client_seq;
guint32 from_server_seq;
} tcp_session_data;
static dof_security_data global_security;
static guint8 count_hex_bytes(gchar *str);
/* Global DPS data structures for security keys. */
static seckey_field_t *seckey_list = NULL;
static guint num_seckey_list = 0;
/* Global DPS data structures for identity secrets. */
static identsecret_field_t *identsecret_list = NULL;
static guint num_identsecret_list = 0;
/* Callbacks for Configuration security templates. */
UAT_CSTRING_CB_DEF(secmode_list, domain, secmode_field_t)
UAT_CSTRING_CB_DEF(secmode_list, identity, secmode_field_t)
UAT_CSTRING_CB_DEF(secmode_list, kek, secmode_field_t)
static void secmode_list_post_update_cb(void)
{
}
static void secmode_list_update_cb(void *r, const char **err)
{
secmode_field_t *rec = (secmode_field_t *)r;
guint32 size;
*err = NULL;
size = (guint32)strlen(rec->domain);
if (!VALIDHEX(rec->domain[0]) && !dof_oid_create_internal(rec->domain, &size, NULL))
{
*err = wmem_strdup(wmem_packet_scope(), "Invalid domain [must be valid OID].");
return;
}
else if (!count_hex_bytes(rec->domain))
{
*err = wmem_strdup(wmem_packet_scope(), "Invalid domain [must be valid OID].");
return;
}
size = (guint32)strlen(rec->identity);
if (!VALIDHEX(rec->identity[0]) && !dof_oid_create_internal(rec->identity, &size, NULL))
{
*err = wmem_strdup(wmem_packet_scope(), "Invalid identity [must be valid OID].");
return;
}
else if (!count_hex_bytes(rec->identity))
{
*err = wmem_strdup(wmem_packet_scope(), "Invalid identity [must be valid OID].");
return;
}
if (count_hex_bytes(rec->kek) != 32)
{
*err = wmem_strdup(wmem_packet_scope(), "Invalid KEK [must be 32 byte key].");
return;
}
}
static void* secmode_list_copy_cb(void *n, const void *o, unsigned siz _U_)
{
secmode_field_t *new_rec = (secmode_field_t *)n;
const secmode_field_t *old_rec = (const secmode_field_t *)o;
if (old_rec->domain)
{
new_rec->domain = g_strdup(old_rec->domain);
}
else
{
new_rec->domain = NULL;
}
if (old_rec->identity)
{
new_rec->identity = g_strdup(old_rec->identity);
}
else
{
new_rec->identity = NULL;
}
if (old_rec->kek)
{
new_rec->kek = g_strdup(old_rec->kek);
}
else
{
new_rec->kek = NULL;
}
return new_rec;
}
static void secmode_list_free_cb(void *r)
{
secmode_field_t *rec = (secmode_field_t *)r;
if (rec->domain)
g_free(rec->domain);
if (rec->identity)
g_free(rec->identity);
if (rec->kek)
g_free(rec->kek);
}
/* Callbacks for security keys. */
UAT_CSTRING_CB_DEF(seckey_list, key, seckey_field_t)
static void seckey_list_post_update_cb(void)
{
}
static void seckey_list_update_cb(void *r, const char **err)
{
seckey_field_t *rec = (seckey_field_t *)r;
*err = NULL;
if (count_hex_bytes(rec->key) != 32)
{
*err = wmem_strdup(wmem_packet_scope(), "Invalid secret [must be 32 bytes].");
return;
}
}
static void* seckey_list_copy_cb(void *n, const void *o, unsigned siz _U_)
{
seckey_field_t *new_rec = (seckey_field_t *)n;
const seckey_field_t *old_rec = (const seckey_field_t *)o;
if (old_rec->key)
{
new_rec->key = g_strdup(old_rec->key);
}
else
{
new_rec->key = NULL;
}
return new_rec;
}
static void seckey_list_free_cb(void *r)
{
seckey_field_t *rec = (seckey_field_t *)r;
if (rec->key)
g_free(rec->key);
}
/* Callbacks for identity secrets. */
UAT_CSTRING_CB_DEF(identsecret_list, domain, identsecret_field_t)
UAT_CSTRING_CB_DEF(identsecret_list, identity, identsecret_field_t)
UAT_CSTRING_CB_DEF(identsecret_list, secret, identsecret_field_t)
static void identsecret_list_post_update_cb(void)
{
}
static void identsecret_list_update_cb(void *r, const char **err)
{
identsecret_field_t *rec = (identsecret_field_t *)r;
guint32 size;
*err = NULL;
size = (guint32)strlen(rec->domain);
if (!VALIDHEX(rec->domain[0]))
{
if (dof_oid_create_internal(rec->domain, &size, NULL))
{
*err = wmem_strdup(wmem_packet_scope(), "Invalid domain [must be valid OID].");
return;
}
}
else if (!count_hex_bytes(rec->domain))
{
*err = wmem_strdup(wmem_packet_scope(), "Invalid domain [must be valid OID].");
return;
}
size = (guint32)strlen(rec->identity);
if (!VALIDHEX(rec->identity[0]))
{
if (dof_oid_create_internal(rec->identity, &size, NULL))
{
*err = wmem_strdup(wmem_packet_scope(), "Invalid identity [must be valid OID].");
return;
}
}
else if (!count_hex_bytes(rec->identity))
{
*err = wmem_strdup(wmem_packet_scope(), "Invalid identity [must be valid OID].");
return;
}
if (count_hex_bytes(rec->secret) != 32)
{
*err = wmem_strdup(wmem_packet_scope(), "Invalid secret [must be 32 byte key].");
return;
}
}
static void* identsecret_list_copy_cb(void *n, const void *o, unsigned siz _U_)
{
identsecret_field_t *new_rec = (identsecret_field_t *)n;
const identsecret_field_t *old_rec = (const identsecret_field_t *)o;
if (old_rec->domain)
{
new_rec->domain = g_strdup(old_rec->domain);
}
else
{
new_rec->domain = NULL;
}
if (old_rec->identity)
{
new_rec->identity = g_strdup(old_rec->identity);
}
else
{
new_rec->identity = NULL;
}
if (old_rec->secret)
{
new_rec->secret = g_strdup(old_rec->secret);
}
else
{
new_rec->secret = NULL;
}
return new_rec;
}
static void identsecret_list_free_cb(void *r)
{
identsecret_field_t *rec = (identsecret_field_t *)r;
if (rec->domain)
g_free(rec->domain);
if (rec->identity)
g_free(rec->identity);
if (rec->secret)
g_free(rec->secret);
}
static void init_addr_port_tables(void);
/* The IP transport protocols need to assign SENDER ID based on the
* transport address. This requires a hash lookup from address/port to ID.
*/
static GHashTable *addr_port_to_id = NULL;
typedef struct _addr_port_key
{
address addr;
guint16 port;
} addr_port_key;
static guint addr_port_key_hash_fn(gconstpointer key)
{
const addr_port_key *addr_key = (const addr_port_key *)key;
guint result = 0;
guint port_as_int = addr_key->port;
guint type_as_int = addr_key->addr.type;
result += g_int_hash(&port_as_int);
result += g_int_hash(&type_as_int);
{
guint hash = 5381;
const guint8 *str = (const guint8 *)addr_key->addr.data;
guint8 i;
for (i = 0; i < addr_key->addr.len; i++)
hash = ((hash << 5) + hash) + str[i]; /* hash * 33 + c */
result += hash;
}
return result;
}
static gboolean addr_port_key_equal_fn(gconstpointer key1, gconstpointer key2)
{
const addr_port_key *addr_key_ptr1 = (const addr_port_key *)key1;
const addr_port_key *addr_key_ptr2 = (const addr_port_key *)key2;
if (addr_key_ptr1->port != addr_key_ptr2->port)
return FALSE;
return addresses_equal(&addr_key_ptr1->addr, &addr_key_ptr2->addr);
}
static void addr_port_key_free_fn(gpointer key)
{
addr_port_key *addr_port = (addr_port_key *)key;
g_free(addr_port->addr.priv);
g_free(addr_port);
}
static void init_addr_port_tables(void)
{
/* This routine is called each time the system is reset (file load, capture)
* and so it should take care of freeing any of our persistent stuff.
*/
if (addr_port_to_id != NULL)
{
/* Clear it out. Note that this calls the destroy functions for each element. */
g_hash_table_destroy(addr_port_to_id);
addr_port_to_id = NULL;
}
/* The value is not allocated, so does not need to be freed. */
addr_port_to_id = g_hash_table_new_full(addr_port_key_hash_fn, addr_port_key_equal_fn, addr_port_key_free_fn, NULL);
}
static guint next_addr_port_id = 1;
#define EP_COPY_ADDRESS(to, from) { \
guint8 *EP_COPY_ADDRESS_data; \
(to)->type = (from)->type; \
(to)->len = (from)->len; \
EP_COPY_ADDRESS_data = (guint8*) wmem_alloc(wmem_packet_scope(),(from)->len); \
memcpy(EP_COPY_ADDRESS_data, (from)->data, (from)->len); \
(to)->priv = EP_COPY_ADDRESS_data; \
(to)->data = (to)->priv; \
}
/* Return the transport ID, a unique number for each transport sender.
*/
static guint assign_addr_port_id(address *addr, guint16 port)
{
addr_port_key lookup_key;
addr_port_key *key;
guint value;
/* Build a (non-allocated) key to do the lookup. */
EP_COPY_ADDRESS(&lookup_key.addr, addr);
lookup_key.port = port;
value = GPOINTER_TO_UINT(g_hash_table_lookup(addr_port_to_id, &lookup_key));
if (value)
{
/* We found a match. */
return value;
}
/* No match, need to add a key. */
key = g_new0(addr_port_key, 1);
copy_address(&key->addr, addr);
key->port = port;
/* Note, this is not multithread safe, but Wireshark isn't multithreaded. */
g_hash_table_insert(addr_port_to_id, key, GUINT_TO_POINTER(next_addr_port_id));
return next_addr_port_id++;
}
/* Wireshark Configuration Dialog Routines*/
static gboolean identsecret_chk_cb(void *r _U_, const char *p _U_, unsigned len _U_, const void *u1 _U_, const void *u2 _U_, char **err _U_)
{
#if 0
gchar** protos;
gchar* line = ep_strndup(p, len);
guint num_protos, i;
g_strstrip(line);
ascii_strdown_inplace(line);
protos = ep_strsplit(line, ":", 0);
for (num_protos = 0; protos[num_protos]; num_protos++)
g_strstrip(protos[num_protos]);
if (!num_protos)
{
*err = wmem_strdup(wmem_packet_scope(), "No protocols given");
return FALSE;
}
for (i = 0; i < num_protos; i++)
{
if (!find_dissector(protos[i]))
{
*err = wmem_strdup_printf(wmem_packet_scope(), "Could not find dissector for: '%s'", protos[i]);
return FALSE;
}
}
#endif
return TRUE;
}
/* Utility Methods */
static guint8 count_hex_bytes(gchar *str)
{
guint8 total = 0;
while (str != NULL && *str != '\0' && *str != '#')
{
if (!g_ascii_isxdigit(*str))
{
str += 1;
continue;
}
if (!g_ascii_isxdigit(str[1]))
return 0;
total += 1;
str += 2;
}
return total;
}
static void parse_hex_string(gchar *str, guint8 **ptr, guint8 *len)
{
guint8 j = 0;
*len = count_hex_bytes(str);
*ptr = (guint8 *)g_malloc0(*len);
while (j < *len)
{
int high, low;
if (!g_ascii_isxdigit(*str))
{
str += 1;
continue;
}
high = ws_xton(str[0]);
low = ws_xton(str[1]);
(*ptr)[j++] = (high << 4) | low;
str += 2;
}
}
/* OID and IID Parsing */
static const guint8 OALString_HexChar[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
#define IS_PRINTABLE(c) ( ((guint8)c) >= 32U && ((guint8)c) < 127U )
#define IS_ESCAPED(c) ( (c) == '(' || (c) == ')' || (c) == '[' || (c) == ']' || (c) == '{' || (c) == '}' || (c) == '\\' || (c) == '|' )
#define DOFOBJECTID_MAX_CLASS_SIZE (4)
#define MAX_OID_DATA_SIZE (63)
#define OID_DATA_LEN_MASK (MAX_OID_DATA_SIZE)
#define ObjectID_DataToStringLength( data, dataSize ) ObjectID_DataToString( (data), (dataSize), NULL )
#define OALString_HexDigitToChar(c) (OALString_HexChar[(c)])
#define DOFObjectIDAttribute_IsValid( attribute ) ((attribute).id < DOFOBJECTIDATTRIBUTE_INVALID)
#define DOFOBJECTID_HEADER_SIZE (offsetof( DOFObjectID_t, oid ))
#define DOFObjectIDAttribute_GetValueSize( attribute ) ((attribute).dataSize)
#define DOFObjectIDAttribute_GetValue( attribute ) ((attribute).data)
#define DOFObjectIDAttribute_GetType( attribute ) ((DOFObjectIDAttributeType)(attribute).id)
typedef enum DOFObjectIDAttributeID_t
{
/**
* Provider attribute. This attribute identifies an object as being
* provided by a specific service provider. The associated data must
* be an object identifier.
*/
DOFOBJECTIDATTRIBUTE_PROVIDER = 0,
/**
* Session attribute. This attribute associates the object with the
* specified session. The associated data must be exactly 16 bytes long.
*/
DOFOBJECTIDATTRIBUTE_SESSION = 1,
/**
* Group attribute. This attribute is normally used in association
* with the BROADCAST object identifier. It defines a target that is
* a multicast group in the DOF network (as opposed to the transport).
* The associated data must be an object identifier.
*/
DOFOBJECTIDATTRIBUTE_GROUP = 2,
/**
* Invalid, used to signal that an error has occurred.
*/
DOFOBJECTIDATTRIBUTE_INVALID = 128
} DOFObjectIDAttributeType;
typedef guint32 DOFObjectIDClass;
typedef struct DOFObjectID_t
{
guint32 refCount;
guint16 len; /* Actual length of oid's wire representation. Max is 32707: 4 + 1 + 63 + (127 * 257). */
guint8 oid[1]; /* Extends beyond end of this defined structure, so oid MUST be last structure member! */
} DOFObjectID_t;
typedef DOFObjectID_t *DOFObjectID;
typedef guint8 DOFObjectIDAttributeDataSize;
typedef struct DOFObjectIDAttribute_t
{
guint8 id; /**< Attribute Identifier. Intentionally defined as @ref uint8 for size, but holds all valid values for @ref DOFObjectIDAttributeType. **/
DOFObjectIDAttributeDataSize dataSize; /**< Size of the attribute data. **/
const guint8 *data; /**< Attribute data. **/
} DOFObjectIDAttribute;
static guint32 OALMarshal_UncompressValue(guint8 maxSize, guint32 *bufLength, const guint8 *buffer)
{
guint32 value = 0;
guint8 used = 0;
guint8 size = maxSize;
guint8 mask;
switch (buffer[0] >> 6)
{
case 0x02:
/* Two Bytes */
if (maxSize > 2)
mask = 0x3F;
else
mask = 0x7F;
size = 2;
break;
case 0x03:
/* Three/Four Bytes */
if (maxSize > 2)
mask = 0x3F;
else
mask = 0x7F;
break;
default:
/* One Byte */
size = 1;
mask = 0x7F;
break;
}
value = buffer[used++] & mask;
while (used < size)
value = (value << 8) | buffer[used++];
*bufLength = used;
return (value);
}
static guint32 DOFObjectID_GetClassSize_Bytes(const guint8 *pBytes)
{
guint32 size = 4;
(void)OALMarshal_UncompressValue(DOFOBJECTID_MAX_CLASS_SIZE, &size, pBytes);
return size;
}
static guint32 DOFObjectID_GetClassSize(DOFObjectID self)
{
return DOFObjectID_GetClassSize_Bytes(self->oid);
}
static guint32 DOFObjectID_GetDataSize(const DOFObjectID self)
{
return ((*((const guint8 *)self->oid + DOFObjectID_GetClassSize(self))) & OID_DATA_LEN_MASK);
}
static guint32 ObjectID_DataToString(const guint8 *data, guint32 dataSize, char *pBuf)
{
guint32 len = 0, i, nonprintable, escaped;
/* Determine if the data is printable... */
for (i = 0, nonprintable = 0, escaped = 0; i < dataSize; i++)
{
if (!IS_PRINTABLE(data[i]))
nonprintable++;
else if (IS_ESCAPED(data[i]))
escaped++;
}
if (nonprintable == 0)
{
/* Printable, so copy as a string, escaping where necessary. */
if (pBuf)
{
for (i = 0; i < dataSize; i++)
{
if (IS_ESCAPED(data[i]))
{
pBuf[len++] = '\\';
pBuf[len++] = data[i];
}
else
pBuf[len++] = data[i];
}
}
else
{
len = dataSize + escaped; /* Count escaped characters twice. */
}
}
else
{
/* Non-printable, so format as hex string. */
if (pBuf)
{
pBuf[len++] = '{';
for (i = 0; i < dataSize; i++)
{
pBuf[len++] = OALString_HexDigitToChar((data[i] >> 4) & 0x0F);
pBuf[len++] = OALString_HexDigitToChar((data[i]) & 0x0F);
}
pBuf[len++] = '}';
}
else
{
len = dataSize * 2 + 2;
}
}
return len;
}
static const guint8* DOFObjectID_GetData(const DOFObjectID self)
{
if (DOFObjectID_GetDataSize(self) > 0)
return (const guint8 *)self->oid + DOFObjectID_GetClassSize(self) + 1; /* 1: length of length byte. */
return NULL;
}
static guint32 DOFObjectID_GetIDClass(const DOFObjectID self)
{
guint32 size = 4;
return OALMarshal_UncompressValue(DOFOBJECTID_MAX_CLASS_SIZE, &size, self->oid);
}
static gboolean DOFObjectID_HasAttributes(const DOFObjectID self)
{
if (!self)
return FALSE;
/* bit 7: next attribute flag. */
return (gboolean)(((*(const guint8 *)((const guint8 *)(self->oid) + DOFObjectID_GetClassSize(self))) & 0x80) != 0);
}
static guint8 DOFObjectID_GetBaseSize(const DOFObjectID oid)
{
return DOFObjectID_GetClassSize(oid) + 1 + DOFObjectID_GetDataSize(oid);
}
static guint8 DOFObjectID_GetAttributeCount(const DOFObjectID self)
{
guint8 retVal = 0;
/* Note: No OID can duplicate an attribute ID. Legal attribute IDs can be from 0-126. So max count fits in uint8. */
if (self && DOFObjectID_HasAttributes(self))
{
const guint8 *pNextAttribute = (const guint8 *)self->oid + DOFObjectID_GetBaseSize(self);
++retVal;
while (*pNextAttribute & 0x80) /* bit 7: next attribute present flag. */
{
++retVal;
pNextAttribute += (2 + *((const guint8 *)pNextAttribute + 1)); /* 2: attribute marshalling overhead. */
}
}
return retVal;
}
static DOFObjectIDAttribute DOFObjectID_GetAttributeAtIndex(const DOFObjectID self, guint8 attribute_index)
{
DOFObjectIDAttribute retAttributeDescriptor = { DOFOBJECTIDATTRIBUTE_INVALID, 0, NULL };
/* Note: No OID can duplicate an attribute ID. Legal attribute IDs can be from 0-127. So max index fits in uint8. */
if (self && attribute_index < DOFOBJECTIDATTRIBUTE_INVALID)
{
if (DOFObjectID_HasAttributes(self))
{
guint8 count = 0;
const guint8 *pNextAttribute = (const guint8 *)self->oid + DOFObjectID_GetBaseSize(self);
while (1) /* Parse through the N Attributes. */
{
if (attribute_index == count++)
{
retAttributeDescriptor.id = *pNextAttribute & 0x7F;
retAttributeDescriptor.dataSize = (DOFObjectIDAttributeDataSize) * ((const guint8 *)pNextAttribute + 1);
retAttributeDescriptor.data = (const guint8 *)pNextAttribute + 2; /* 2: attr marshalling overhead. */
break; /* Success. */
}
if (!(*pNextAttribute & 0x80))
break; /* Fail: no more Attributes */
pNextAttribute += (2 + *((const guint8 *)pNextAttribute + 1));
}
}
}
return retAttributeDescriptor;
}
static void DOFObjectID_Destroy(DOFObjectID self _U_)
{
/* Ephemeral memory doesn't need to be freed. */
}
static void DOFObjectID_InitStruct(DOFObjectID newObjID, guint32 dataLen)
{
newObjID->refCount = 1;
newObjID->len = dataLen;
}
static DOFObjectID DOFObjectID_Create_Unmarshal(guint32 *length, const guint8 *buffer)
{
guint32 len = *length;
/* Legal OID described at buffer must have at least 2 bytes. */
if (buffer && len >= 2)
{
guint32 classSize = len;
guint32 classv = OALMarshal_UncompressValue(DOFOBJECTID_MAX_CLASS_SIZE, &classSize, buffer);
/* Legal OID described at buffer must have its class representation be correctly compressed. */
if (1)
{
guint32 computedSize;
/* Above call won't return 3 because DOFOBJECTID_MAX_CLASS_SIZE (4) was passed in. */
computedSize = classSize + 1; /* 1: length of length byte. */
/* Legal OID described at buffer must have enough bytes to describe its OID class. */
if (len >= computedSize)
{
guint8 lenByte = buffer[classSize];
/* Legal OID described at buffer must have its length byte bit 6 be 0. */
if (!(lenByte & 0x40))
{
gboolean hasAttr;
guint8 dataLen = lenByte & OID_DATA_LEN_MASK;
/* Legal broadcast OID described at buffer must have no base data, though it can have attribute(s)*/
if ((classv == 0) && (dataLen > 0))
goto notvalid;
computedSize += dataLen;
hasAttr = lenByte & 0x80; /* Valid OID base; check attributes. */
while (hasAttr)
{
/* Legal OID described at buffer must have enough bytes to hold each new found attribute. */
if (len >= computedSize + 2) /* 2: attribute marshalling overhead. */
{
hasAttr = buffer[computedSize] & 0x80; /* bit 7: next attribute present flag. */
computedSize += (2 + buffer[computedSize + 1]);
}
else
goto notvalid;
}
/* Legal OID described at buffer must have enough buffer bytes, final check. */
if (len >= computedSize)
{
DOFObjectID newObjID = (DOFObjectID)wmem_alloc0(wmem_packet_scope(), DOFOBJECTID_HEADER_SIZE + computedSize + 1);
/* Adds space for null-terminator, just in case. */
*length = computedSize;
if (newObjID)
{
DOFObjectID_InitStruct(newObjID, computedSize);
memcpy(newObjID->oid, buffer, computedSize);
newObjID->oid[computedSize] = 0;
return newObjID; /* Success. */
}
/* buffer describes valid OID, but due to alloc failure we cannot return the newly created OID*/
goto allocErrorOut;
}
}
}
}
}
notvalid:
/* buffer does not describe a valid OID, but do not log a message. The caller may have called us to find out if the
buffer does or does not obey the rules of a valid OID. He learns that by our NULL return. */
allocErrorOut :
*length = 0;
return NULL;
}
static DOFObjectID DOFObjectID_Create_Bytes(guint32 bufferSize, const guint8 *pOIDBuffer)
{
guint32 len = bufferSize;
DOFObjectID rval = DOFObjectID_Create_Unmarshal(&len, pOIDBuffer);
if (rval)
{
if (len != bufferSize)
{
DOFObjectID_Destroy(rval);
rval = NULL;
}
}
return rval;
}
static guint32 ObjectID_ToStringLength(const DOFObjectID oid)
{
guint32 len = 0;
/* Note: All these string functions can be exercised with objectid_test.c, which outputs the string to console. */
len = 7 /* [{xx}: and trailing ] */ + ObjectID_DataToStringLength(DOFObjectID_GetData(oid),
DOFObjectID_GetDataSize(oid));
if (DOFObjectID_GetIDClass(oid) & 0xFF000000)
len += 6; /* Six more hex digits. */
else if (DOFObjectID_GetIDClass(oid) & 0xFF0000)
len += 4; /* Four more hex digits. */
else if (DOFObjectID_GetIDClass(oid) & 0xFF00)
len += 2; /* Two more hex digits. */
/* Handle Attributes, if any. */
if (DOFObjectID_HasAttributes(oid))
{
guint8 i; /* Max attribute count is under uint8. */
guint8 attributeCount = DOFObjectID_GetAttributeCount(oid);
len += 2; /* surrounding ( ) */
for (i = 0; i < attributeCount; i++)
{
DOFObjectID embedOID;
DOFObjectIDAttribute avpDescriptor = DOFObjectID_GetAttributeAtIndex(oid, i);
if (!DOFObjectIDAttribute_IsValid(avpDescriptor))
break; /* Done with Attributes. If here, some error took place. */
if (i)
len++;
len += 5; /* {xx}: */
/* Handle embedded Object IDs. */
embedOID = DOFObjectID_Create_Bytes(DOFObjectIDAttribute_GetValueSize(avpDescriptor),
DOFObjectIDAttribute_GetValue(avpDescriptor));
if (embedOID)
{
len += ObjectID_ToStringLength(embedOID); /* Recurse to compute string rep length of found OID. */
DOFObjectID_Destroy(embedOID);
}
else
{
/* Hex Data. */
len += ObjectID_DataToStringLength(DOFObjectIDAttribute_GetValue(avpDescriptor),
DOFObjectIDAttribute_GetValueSize(avpDescriptor));
}
} /* end for(). */
}
return len;
}
static guint32 InterfaceID_ToString(const guint8 *iid, char *pBuf)
{
guint32 len = 0;
guint iid_len = iid[0] & 0x03;
guint i;
if (iid_len == 3)
iid_len = 4;
pBuf[len++] = '[';
pBuf[len++] = '{';
pBuf[len++] = OALString_HexDigitToChar((iid[0] >> 6) & 0x0F);
pBuf[len++] = OALString_HexDigitToChar((iid[0] >> 2) & 0x0F);
pBuf[len++] = '}';
pBuf[len++] = ':';
pBuf[len++] = '{';
/* Data */
for (i = 0; i < iid_len; i++)
{
pBuf[len++] = OALString_HexDigitToChar((iid[i + 1] >> 4) & 0x0F);
pBuf[len++] = OALString_HexDigitToChar(iid[i + 1] & 0x0F);
}
pBuf[len++] = '}';
pBuf[len++] = ']';
return len;
}
static guint32 ObjectID_ToString(const DOFObjectID oid, char *pBuf)
{
DOFObjectIDClass oidClass;
guint32 len = 0;
pBuf[len++] = '[';
pBuf[len++] = '{';
/* Class */
oidClass = DOFObjectID_GetIDClass(oid);
if (oidClass & 0xFF000000)
{
pBuf[len++] = OALString_HexDigitToChar((oidClass >> 28) & 0x0F);
pBuf[len++] = OALString_HexDigitToChar((oidClass >> 24) & 0x0F);
}
if (oidClass & 0xFFFF0000)
{
pBuf[len++] = OALString_HexDigitToChar((oidClass >> 20) & 0x0F);
pBuf[len++] = OALString_HexDigitToChar((oidClass >> 16) & 0x0F);
}
if (oidClass & 0xFFFFFF00)
{
pBuf[len++] = OALString_HexDigitToChar((oidClass >> 12) & 0x0F);
pBuf[len++] = OALString_HexDigitToChar((oidClass >> 8) & 0x0F);
}
pBuf[len++] = OALString_HexDigitToChar((oidClass >> 4) & 0x0F);
pBuf[len++] = OALString_HexDigitToChar((oidClass) & 0x0F);
pBuf[len++] = '}';
pBuf[len++] = ':';
/* Data */
len += ObjectID_DataToString(DOFObjectID_GetData(oid), DOFObjectID_GetDataSize(oid), &pBuf[len]);
/* Handle Attributes, if any. */
if (DOFObjectID_HasAttributes(oid))
{
guint8 i;
guint8 attributeCount = DOFObjectID_GetAttributeCount(oid);
pBuf[len++] = '(';
for (i = 0; i < attributeCount; i++)
{
DOFObjectID embedOID;
DOFObjectIDAttribute avpDescriptor = DOFObjectID_GetAttributeAtIndex(oid, i);
if (!DOFObjectIDAttribute_IsValid(avpDescriptor))
break; /* Done with Attributes. If here, some error took place. */
if (i)
pBuf[len++] = '|';
pBuf[len++] = '{';
pBuf[len++] = OALString_HexDigitToChar((DOFObjectIDAttribute_GetType(avpDescriptor) >> 4) & 0x0F);
pBuf[len++] = OALString_HexDigitToChar((DOFObjectIDAttribute_GetType(avpDescriptor)) & 0x0F);
pBuf[len++] = '}';
pBuf[len++] = ':';
/* Handle embedded Object IDs. */
embedOID = DOFObjectID_Create_Bytes(DOFObjectIDAttribute_GetValueSize(avpDescriptor),
DOFObjectIDAttribute_GetValue(avpDescriptor));
if (embedOID)
{
len += ObjectID_ToString(embedOID, &pBuf[len]); /* Recurse to output string rep of found OID. */
DOFObjectID_Destroy(embedOID);
}
else
{
/* Hex Data. */
len += ObjectID_DataToString(DOFObjectIDAttribute_GetValue(avpDescriptor),
DOFObjectIDAttribute_GetValueSize(avpDescriptor), &pBuf[len]);
}
} /* end for(). */
pBuf[len++] = ')';
}
pBuf[len++] = ']';
return len;
}
static const gchar* dof_iid_create_standard_string(guint32 bufferSize, const guint8 *pIIDBuffer)
{
gchar *pRetval;
guint len = 9 + (bufferSize - 1) * 2; /* Alias is always [{AA}:{01234567}] */
pRetval = (gchar *)wmem_alloc(wmem_packet_scope(), len + 1);
if (pRetval)
{
InterfaceID_ToString(pIIDBuffer, pRetval);
pRetval[len] = 0;
}
return pRetval;
}
static const gchar* dof_oid_create_standard_string(guint32 bufferSize, const guint8 *pOIDBuffer)
{
DOFObjectID oid;
gchar *pRetval;
guint32 len = bufferSize;
oid = DOFObjectID_Create_Unmarshal(&len, pOIDBuffer);
if (!oid)
return "Illegal OID";
len = ObjectID_ToStringLength(oid);
/* Use PCRMem_Alloc() and not DOFMem_Alloc() because app caller will be freeing memory with PCRMem_Destroy(). */
pRetval = (gchar *)wmem_alloc(wmem_packet_scope(), len + 1);
if (pRetval)
{
ObjectID_ToString(oid, pRetval);
pRetval[len] = 0;
}
return pRetval;
}
struct parseCtx
{
const char *oid;
guint8 *buffer;
guint32 buffLen;
guint32 oidLen;
guint32 currOidPos;
guint32 currBufferPos;
}parseCtx;
/* Operations on OID string */
#define PARSECTX_PEEK_CHAR_OID(ctx) ( (ctx)->oid[(ctx)->currOidPos] )
#define PARSECTX_PEEK_NEXT_CHAR_OID(ctx) ( (ctx)->oid[(ctx)->currOidPos+1] )
#define PARSECTX_READ_CHAR_OID(ctx) ( (ctx)->oid[(ctx)->currOidPos++] )
#define PARSECTX_GET_CURRENT_POS_OID(ctx) ( (ctx)->oid+(ctx)->currOidPos )
#define PARSECTX_STEP_OID(ctx, count)((ctx)->currOidPos+=(count))
/* Operations on DOFObjectID buffer */
#define PARSECTX_GET_CURRENT_POS_BUF(ctx)( ((ctx)->buffer)? (ctx)->buffer+(ctx)->currBufferPos: NULL )
#define PARSECTX_STEP_BUF(ctx, count)( (ctx)->currBufferPos+=(count))
#define PARSECTX_WRITE_AT_POS_BUF(ctx, pos, value) do{ if((ctx)->buffer) *(pos) = (value); } while(0)
#define PARSECTX_OR_AT_POS_BUF(ctx, pos, value) do{ if((ctx)->buffer) *(pos) |= (value); } while(0)
#define PARSECTX_WRITE_BUF(ctx, value)( ((ctx)->buffer)? (ctx)->buffer[(ctx)->currBufferPos++] = (value): (ctx)->currBufferPos++ )
#define PARSECTX_CHECK_LEN(ctx, len) (((ctx)->buffer)? (((ctx)->currBufferPos+len <= (ctx)->buffLen)? 0: 1): 0)
/* Operation to read from OID straight to buffer */
#define PARSECTX_WRITE_BUF_FROM_OID(ctx) (((ctx)->buffer)? (ctx)->buffer[(ctx)->currBufferPos++] = (ctx)->oid[(ctx)->currOidPos]: ((ctx)->currBufferPos++),((ctx)->currOidPos++))
#define IS_DIGIT(c) (((c) >= '0' && (c) <= '9'))
#define DIGIT2VALUE(c) (c-48)
#define HEX2VALUE(c) ( (IS_DIGIT(c))? DIGIT2VALUE(c) : ((c) >= 'A' && (c) <= 'F')? (c-55): (c-87) )
#define VALIDHEXSEP(c) ( (c) == ' ' || (c) == ':' || (c) == '-' )
#define VALIDHEX(c) ( ((c) >= '0' && (c) <= '9') || ((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f') )
#define VALIDHEXBYTE(s) ( VALIDHEX((s)[0]) && VALIDHEX((s)[1]) )
#define VALIDNUMBER(c) ((c) >= '0' && (c) <= '9')
#define VALIDASCIICHAR(c) (((guint8)c) >= 32 && ((guint8)c) <= 126 )
#define IS_ESCAPED(c) ( (c) == '(' || (c) == ')' || (c) == '[' || (c) == ']' || (c) == '{' || (c) == '}' || (c) == '\\' || (c) == '|' )
static guint8 parseFormatOID(struct parseCtx *ctx);
static guint8 parseHexField(struct parseCtx *ctx)
{
/* Hex fields start with { and end with } can contain space, dash and colon*/
if (PARSECTX_READ_CHAR_OID(ctx) == '{' && PARSECTX_PEEK_CHAR_OID(ctx) != '}')
{
while (PARSECTX_PEEK_CHAR_OID(ctx) != '}')
{
if (VALIDHEXBYTE(PARSECTX_GET_CURRENT_POS_OID(ctx)))
{
if (PARSECTX_CHECK_LEN(ctx, 1) == 0)
{
PARSECTX_WRITE_BUF(ctx, HEX2VALUE(PARSECTX_PEEK_CHAR_OID(ctx)) << 4 | HEX2VALUE(PARSECTX_PEEK_NEXT_CHAR_OID(ctx)));
PARSECTX_STEP_OID(ctx, 2);
if (VALIDHEXSEP(PARSECTX_PEEK_CHAR_OID(ctx)))
{
if (PARSECTX_PEEK_NEXT_CHAR_OID(ctx) == '}')
{
/* no seperator after byte block */
return 1;
}
PARSECTX_STEP_OID(ctx, 1);
}
}
else
{
return 1;
}
}
else
{
return 1;
}
}
PARSECTX_STEP_OID(ctx, 1);
return 0;
}
return 1;
}
static guint8 parseStringField(struct parseCtx *ctx)
{
/* Copy into buffer until end or */
while (ctx->currOidPos < (ctx->oidLen - 1))
{
char curr = PARSECTX_PEEK_CHAR_OID(ctx);
if (curr == ']' || curr == '(')
{
break; /* End of string field */
}
else if (curr == '\\')
{
/* Handle escaped char */
PARSECTX_STEP_OID(ctx, 1);
if (!IS_ESCAPED(PARSECTX_PEEK_CHAR_OID(ctx)) || PARSECTX_CHECK_LEN(ctx, 1) != 0)
return 1;
PARSECTX_WRITE_BUF_FROM_OID(ctx);
}
else
{
if (VALIDASCIICHAR(curr) && PARSECTX_CHECK_LEN(ctx, 1) == 0)
PARSECTX_WRITE_BUF_FROM_OID(ctx);
else
return 1;
}
}
return 0;
}
static guint8 OALMarshal_GetCompressedValueSize(guint8 maxSize, guint32 value)
{
guint8 lenbytes = (1 + (value > 0x7F) + (value > 0x3FFF));
if (lenbytes > 2)
return (maxSize);
return (lenbytes);
}
static guint32 OALMarshal_CompressValue(guint8 maxSize, guint32 value, guint32 bufLength, guint8 *buffer)
{
guint8 lenSize = OALMarshal_GetCompressedValueSize(maxSize, value);
if (bufLength < lenSize)
return 0;
switch (lenSize)
{
case 4:
*(buffer++) = (guint8)((value >> 24) & 0x3F) | 0xC0;
*(buffer++) = (guint8)((value >> 16) & 0xFF);
*(buffer++) = (guint8)((value >> 8) & 0xFF);
*(buffer++) = (guint8)(value & 0xFF);
break;
case 3:
*(buffer++) = (guint8)((value >> 16) & 0x3F) | 0xC0;
*(buffer++) = (guint8)((value >> 8) & 0xFF);
*(buffer++) = (guint8)(value & 0xFF);
break;
case 2:
if (maxSize == 2)
{
*(buffer++) = (guint8)((value >> 8) & 0x7F) | 0x80;
}
else
{
*(buffer++) = (guint8)((value >> 8) & 0x3F) | 0x80;
}
*(buffer++) = (guint8)(value & 0xFF);
break;
case 1:
*(buffer++) = (guint8)(value & 0x7F);
break;
default:
/* Invalid computed size! */
break;
}
return (lenSize);
}
static guint8 parseOIDClass(struct parseCtx *ctx)
{
if (PARSECTX_PEEK_CHAR_OID(ctx) == '{' && PARSECTX_PEEK_NEXT_CHAR_OID(ctx) != '}')
{
/* Hex */
guint8 classSize = 0;
guint32 oidClass = 0;
PARSECTX_STEP_OID(ctx, 1);
while (PARSECTX_PEEK_CHAR_OID(ctx) != '}')
{
if (VALIDHEXBYTE(PARSECTX_GET_CURRENT_POS_OID(ctx)))
{
oidClass <<= 8;
oidClass += (HEX2VALUE(PARSECTX_PEEK_CHAR_OID(ctx)) << 4 | HEX2VALUE(PARSECTX_PEEK_NEXT_CHAR_OID(ctx)));
PARSECTX_STEP_OID(ctx, 2);
if (VALIDHEXSEP(PARSECTX_PEEK_CHAR_OID(ctx)))
{
if (PARSECTX_PEEK_NEXT_CHAR_OID(ctx) == '}')
{
/* no seperator after byte block */
return 1;
}
PARSECTX_STEP_OID(ctx, 1);
}
}
else
{
return 1;
}
}
PARSECTX_STEP_OID(ctx, 1);
classSize = OALMarshal_GetCompressedValueSize(4, oidClass);
if (PARSECTX_CHECK_LEN(ctx, classSize) == 0)
{
if (PARSECTX_GET_CURRENT_POS_BUF(ctx))
classSize = OALMarshal_CompressValue(4, oidClass, classSize, PARSECTX_GET_CURRENT_POS_BUF(ctx));
PARSECTX_STEP_BUF(ctx, classSize);
}
return 0;
}
else
{
/* Number */
guint8 classSize = 0;
guint32 oidClass = 0;
while (IS_DIGIT(PARSECTX_PEEK_CHAR_OID(ctx)))
{
oidClass *= 10;
oidClass += DIGIT2VALUE(PARSECTX_PEEK_CHAR_OID(ctx));
PARSECTX_STEP_OID(ctx, 1);
}
classSize = OALMarshal_GetCompressedValueSize(4, oidClass);
if (PARSECTX_CHECK_LEN(ctx, classSize) == 0)
{
if (PARSECTX_GET_CURRENT_POS_BUF(ctx))
classSize = OALMarshal_CompressValue(4, oidClass, classSize, PARSECTX_GET_CURRENT_POS_BUF(ctx));
PARSECTX_STEP_BUF(ctx, classSize);
}
return 0;
}
}
static guint8 parseAttributeID(struct parseCtx *ctx)
{
if (PARSECTX_PEEK_CHAR_OID(ctx) == '{')
{
return parseHexField(ctx);
}
else
{
guint8 avpid = 0;
while (IS_DIGIT(PARSECTX_PEEK_CHAR_OID(ctx)))
{
avpid *= 10;
avpid += DIGIT2VALUE(PARSECTX_PEEK_CHAR_OID(ctx));
PARSECTX_STEP_OID(ctx, 1);
}
if (PARSECTX_CHECK_LEN(ctx, 1) == 0)
{
PARSECTX_WRITE_BUF(ctx, avpid);
return 0;
}
}
return 1;
}
static guint8 parseAttributeData(struct parseCtx *ctx)
{
if (PARSECTX_PEEK_CHAR_OID(ctx) == '[')
{
return parseFormatOID(ctx);
}
else if (PARSECTX_PEEK_CHAR_OID(ctx) == '{')
{
return parseHexField(ctx);
}
else
{
return parseStringField(ctx);
}
}
static guint8 parseAttribute(struct parseCtx *ctx)
{
if (parseAttributeID(ctx) == 0)
{
/* seperated by ':' */
if (PARSECTX_READ_CHAR_OID(ctx) == ':' && PARSECTX_CHECK_LEN(ctx, 1) == 0)
{
guint8 *length = PARSECTX_GET_CURRENT_POS_BUF(ctx);
if (length == NULL)
return 0;
PARSECTX_STEP_BUF(ctx, 1);
if (parseAttributeData(ctx) == 0)
{
PARSECTX_WRITE_AT_POS_BUF(ctx, length, (guint8)(PARSECTX_GET_CURRENT_POS_BUF(ctx) - (length + 1)));
return 0;
}
}
}
return 1;
}
static guint8 parseAttributes(struct parseCtx *ctx)
{
/* AVPs surrounded by '(' ')' but needs at least an avp */
if (PARSECTX_READ_CHAR_OID(ctx) == '(' && PARSECTX_PEEK_CHAR_OID(ctx) != ')')
{
while (PARSECTX_PEEK_CHAR_OID(ctx) != ')')
{
guint8 *avpID = PARSECTX_GET_CURRENT_POS_BUF(ctx);
if (avpID == NULL)
return 0;
if (parseAttribute(ctx) != 0)
return 1;
/* multiple seperated by '|' */
if (PARSECTX_PEEK_CHAR_OID(ctx) == '|' && PARSECTX_PEEK_NEXT_CHAR_OID(ctx) != ')')
{
PARSECTX_OR_AT_POS_BUF(ctx, avpID, 0x80); /* set that there is a next attribute */
PARSECTX_STEP_OID(ctx, 1);
}
}
PARSECTX_STEP_OID(ctx, 1);
return 0;
}
return 1;
}
static guint8 parseFormatOID(struct parseCtx *ctx)
{
/* oid must start with '[' */
if (PARSECTX_PEEK_CHAR_OID(ctx) == '[')
{
PARSECTX_STEP_OID(ctx, 1);
/* Get class id */
if (parseOIDClass(ctx) == 0)
{
/* seperated by ':' */
if (PARSECTX_READ_CHAR_OID(ctx) == ':' && PARSECTX_CHECK_LEN(ctx, 1) == 0)
{
guint8 *length = PARSECTX_GET_CURRENT_POS_BUF(ctx);
PARSECTX_STEP_BUF(ctx, 1);
/* Get data */
if (PARSECTX_PEEK_CHAR_OID(ctx) == '{')
{
/* hex data */
if (parseHexField(ctx) != 0)
return 1;
}
else
{
/* string data */
if (parseStringField(ctx) != 0)
return 1;
}
/* Write length */
if (length == NULL)
return 0;
PARSECTX_WRITE_AT_POS_BUF(ctx, length, (guint8)(PARSECTX_GET_CURRENT_POS_BUF(ctx) - (length + 1)));
/* Check if attributes exist */
if (PARSECTX_PEEK_CHAR_OID(ctx) == '(')
{
PARSECTX_OR_AT_POS_BUF(ctx, length, 0x80); /* set that there are attributes */
if (parseAttributes(ctx) != 0)
return 1;
}
/* Ends with ] */
if (PARSECTX_READ_CHAR_OID(ctx) == ']')
{
return 0;
}
}
}
}
return 1;
}
static guint8 dof_oid_create_internal(const char *oid, guint32 *size, guint8 *buffer)
{
struct parseCtx ctx;
ctx.oid = oid;
ctx.buffer = buffer;
ctx.currOidPos = 0;
ctx.currBufferPos = 0;
if (oid)
{
if (size)
{
ctx.buffLen = (*size);
ctx.oidLen = (guint32)strlen(oid);
if (PARSECTX_PEEK_CHAR_OID(&ctx) == '[')
{
/* Format OID */
if (parseFormatOID(&ctx) == 0)
{
(*size) = ctx.currBufferPos;
return 0;
}
}
else if (PARSECTX_PEEK_CHAR_OID(&ctx) == '{')
{
/* HEX OID */
if (parseHexField(&ctx) == 0)
{
(*size) = ctx.currBufferPos;
return 0;
}
}
(*size) = 0;
}
}
return 1;
}
static void dof_oid_new_standard_string(const char *data, guint32 *rsize, guint8 **oid)
{
if (data)
{
guint8 err;
guint32 size = 0;
/* Call parseInternal to find out how big the buffer needs to be. */
err = dof_oid_create_internal(data, &size, NULL);
if (err == 0)
{
/* Create the DOFObjectID using the size that was just computed. */
*oid = (guint8 *)g_malloc(size + 1); /* Adds space for null-terminator, just in case. */
if (*oid)
{
/* Now that the size is computed and the DOFObjectID is created, call parseInternal again to fill the oid buffer. */
err = dof_oid_create_internal(data, &size, *oid);
if (err == 0)
{
*rsize = size;
return;
}
g_free(*oid);
}
}
}
*rsize = 0;
*oid = NULL;
}
/* Binary Parsing Support */
/**
* Read a compressed 32-bit quantity (PDU Type.3).
* Since the value is variable length, the new offset is
* returned. The value can also be returned, along with the size, although
* NULL is allowed for those parameters.
*/
static gint read_c4(tvbuff_t *tvb, gint offset, guint32 *v, gint *L)
{
guint32 val = 0;
guint8 len = 0;
guint8 b = tvb_get_guint8(tvb, offset++);
int i;
if ((b & 0x80) == 0)
{
len = 1;
b = b & 0x7F;
}
else if ((b & 0x40) == 0)
{
len = 2;
b = b & 0x3F;
}
else
{
len = 4;
b = b & 0x3F;
}
val = b;
for (i = 1; i < len; i++)
val = (val << 8) | tvb_get_guint8(tvb, offset++);
if (L)
*L = len;
if (v)
*v = val;
return offset;
}
/**
* Validate PDU Type.3
* Validaes the encoding.
* Add Expert Info if format invalid
* This also validates Spec Type.3.1.
*/
static void validate_c4(packet_info *pinfo, proto_item *pi, guint32 val, gint len)
{
if (len > 1 && val < 0x80)
{
/* SPEC Type.3.1 Violation. */
expert_add_info_format(pinfo, pi, &ei_c2_c3_c4_format, "DOF Violation: Type.3.1: Compressed 32-bit Compression Manditory.");
}
if (len > 2 && val < 0x4000)
{
/* SPEC Type.3.1 Violation. */
expert_add_info_format(pinfo, pi, &ei_c2_c3_c4_format, "DOF Violation: Type.3.1: Compressed 32-bit Compression Manditory.");
}
}
/**
* Reads a compressed 24-bit quantity (PDU Type.2).
* Since the value is variable length, the new offset is
* returned.
* The value can also be returned, along with the size, although
* NULL is allowed for those parameters.
*/
static gint read_c3(tvbuff_t *tvb, gint offset, guint32 *v, gint *L)
{
guint32 val = 0;
guint8 len = 0;
guint8 b = tvb_get_guint8(tvb, offset++);
int i;
if ((b & 0x80) == 0)
{
len = 1;
b = b & 0x7F;
}
else if ((b & 0x40) == 0)
{
len = 2;
b = b & 0x3F;
}
else
{
len = 3;
b = b & 0x3F;
}
val = b;
for (i = 1; i < len; i++)
val = (val << 8) | tvb_get_guint8(tvb, offset++);
if (L)
*L = len;
if (v)
*v = val;
return offset;
}
/**
* Validate PDU Type.2
* Validaes the encoding.
* Adds Expert Info if format invalid
* This also validates Spec Type.2.1.
*/
static void validate_c3(packet_info *pinfo, proto_item *pi, guint32 val, gint len)
{
if (len > 1 && val < 0x80)
{
/* SPEC Type.2.1 Violation. */
expert_add_info_format(pinfo, pi, &ei_c2_c3_c4_format, "DOF Violation: Type.2.1: Compressed 24-bit Compression Manditory." );
}
if (len > 2 && val < 0x4000)
{
/* SPEC Type.2.1 Violation. */
expert_add_info_format(pinfo, pi, &ei_c2_c3_c4_format, "DOF Violation: Type.2.1: Compressed 24-bit Compression Manditory.");
}
}
/**
* Reads a compressed 16-bit quantity (PDU Type.1).
* Since the value is variable length, the new offset is
* returned. The value can also be returned, along with the size, although
* NULL is allowed for those parameters.
*/
static gint read_c2(tvbuff_t *tvb, gint offset, guint16 *v, gint *L)
{
guint16 val = 0;
guint8 b = tvb_get_guint8(tvb, offset++);
if (b & 0x80)
{
b = b & 0x7F;
val = (b << 8) | tvb_get_guint8(tvb, offset++);
if (L)
*L = 2;
}
else
{
val = b;
if (L)
*L = 1;
}
if (v)
*v = val;
return offset;
}
/**
* Validates PDU Type.1
* Validaes the encoding.
* Adds Expert Info if format invalid
* This also validates Spec Type.1.1.
*/
static void validate_c2(packet_info *pinfo, proto_item *pi, guint16 val, gint len)
{
if (len > 1 && val < 0x80)
{
/* SPEC Type.1.1 Violation. */
expert_add_info_format(pinfo, pi, &ei_c2_c3_c4_format, "DOF Violation: Type.1.1: Compressed 16-bit Compression Manditory." );
}
}
/**
* Given a packet data, and assuming that all of the prerequisite information is known,
* assign a SID ID to the packet if not already assigned.
* A SID ID is the *possibility* of a unique SID, but until the SID is learned the
* association is not made. Further, multiple SID ID may end up referring to the
* same SID, in which case the assignment must be repaired.
*/
static void assign_sid_id(dof_api_data *api_data)
{
node_key_to_sid_id_key lookup_key;
node_key_to_sid_id_key *key;
dof_session_data *session;
dof_packet_data *packet;
guint value;
/* Validate input. These represent dissector misuse, not decoding problems. */
/* TODO: Diagnostic/programmer message. */
if (!api_data || !api_data->packet || !api_data->session)
return;
session = api_data->session;
packet = (dof_packet_data *)api_data->packet;
/* Check if the sender_sid_id is already assigned, if so we are done. */
if (!packet->sender_sid_id)
{
/* Build a (non-allocated) key to do the lookup. */
lookup_key.transport_id = api_data->transport_session->transport_id;
lookup_key.transport_node_id = api_data->transport_packet->sender_id;
lookup_key.dof_id = session->dof_id;
lookup_key.dof_node_id = packet->sender_id;
lookup_key.dof_session_id = session->session_id;
value = GPOINTER_TO_UINT(g_hash_table_lookup(node_key_to_sid_id, &lookup_key));
if (value)
{
gpointer sid_id_key = GUINT_TO_POINTER(value);
gpointer sid_buffer;
/* We found a match. */
packet->sender_sid_id = value;
/* If we know the SID, we must get it now. */
sid_buffer = g_hash_table_lookup(sid_id_to_sid_buffer, sid_id_key);
if (sid_buffer)
{
/* We found a match. */
packet->sender_sid = (dof_2009_1_pdu_19_sid)sid_buffer;
}
}
else
{
/* No match, need to add a key. */
key = g_new0(node_key_to_sid_id_key, 1);
memcpy(key, &lookup_key, sizeof(node_key_to_sid_id_key));
/* Note, this is not multithread safe, but Wireshark isn't multithreaded. */
g_hash_table_insert(node_key_to_sid_id, key, GUINT_TO_POINTER(dpp_next_sid_id));
packet->sender_sid_id = dpp_next_sid_id++;
}
}
/* Check if the receiver_sid_id is already assigned, if so we are done. */
if (!packet->receiver_sid_id)
{
/* Build a (non-allocated) key to do the lookup. */
lookup_key.transport_id = api_data->transport_session->transport_id;
lookup_key.transport_node_id = api_data->transport_packet->receiver_id;
lookup_key.dof_id = session->dof_id;
lookup_key.dof_node_id = packet->receiver_id;
lookup_key.dof_session_id = session->session_id;
value = GPOINTER_TO_UINT(g_hash_table_lookup(node_key_to_sid_id, &lookup_key));
if (value)
{
gpointer sid_id_key = GUINT_TO_POINTER(value);
gpointer sid_buffer;
/* We found a match. */
packet->receiver_sid_id = value;
/* If we know the SID, we must get it now. */
sid_buffer = g_hash_table_lookup(sid_id_to_sid_buffer, sid_id_key);
if (sid_buffer)
{
/* We found a match. */
packet->receiver_sid = (dof_2009_1_pdu_19_sid)sid_buffer;
}
}
else
{
/* No match, need to add a key. */
key = g_new0(node_key_to_sid_id_key, 1);
memcpy(key, &lookup_key, sizeof(node_key_to_sid_id_key));
/* Note, this is not multithread safe, but Wireshark isn't multithreaded. */
g_hash_table_insert(node_key_to_sid_id, key, GUINT_TO_POINTER(dpp_next_sid_id));
packet->receiver_sid_id = dpp_next_sid_id++;
}
}
}
/**
* Declare that the sender of the packet is known to have a SID
* that is identified by the specified buffer. There are a few
* cases here:
* 1. The sid of the sender is already assigned. This is a NOP.
* 2. The sid has never been seen. This associates the SID with the sender SID ID.
* 3. The sid has been seen, and matches the SID ID of the sender. This just sets the sid field.
* 4. The sid has been seen, but with a different SID ID than ours. Patch up all the packets.
*/
static void learn_sender_sid(dof_api_data *api_data, guint8 length, const guint8 *sid)
{
dof_packet_data *packet;
guint8 lookup_key[256];
guint8 *key;
gpointer value;
/* Validate input. */
if (!api_data)
{
/* TODO: Print error. */
return;
}
if (!api_data->packet)
{
/* TODO: Print error. */
return;
}
packet = (dof_packet_data *)api_data->packet;
if (!packet->sender_sid_id)
return;
/* Check for sender SID already known. */
if (packet->sender_sid)
return;
/* Check for SID already known (has assigned SID ID) */
/* Build a (non-allocated) key to do the lookup. */
lookup_key[0] = length;
memcpy(lookup_key + 1, sid, length);
if (g_hash_table_lookup_extended(sid_buffer_to_sid_id, &lookup_key, (gpointer *)&key, &value))
{
guint sid_id = GPOINTER_TO_UINT(value);
/* We found a match. */
if (packet->sender_sid_id == sid_id)
{
/* It matches our SID ID. Set the sid field. */
packet->sender_sid = key;
return;
}
else
{
/* There is a mis-match between SID and SID ID. We have to go through
* all the packets that have SID ID (ours) and update them to SID ID (sid).
*/
guint sid_id_correct = sid_id;
guint sid_id_incorrect = packet->sender_sid_id;
dof_packet_data *ptr = globals.dof_packet_head;
while (ptr)
{
if (ptr->sender_sid_id == sid_id_incorrect)
ptr->sender_sid_id = sid_id_correct;
if (ptr->receiver_sid_id == sid_id_incorrect)
ptr->receiver_sid_id = sid_id_correct;
if (ptr->op.op_sid_id == sid_id_incorrect)
ptr->op.op_sid_id = sid_id_correct;
if (ptr->ref_op.op_sid_id == sid_id_incorrect)
ptr->ref_op.op_sid_id = sid_id_correct;
ptr = ptr->next;
}
}
return;
}
/* The SID has never been seen. Associate with the SID ID. */
key = (dof_2009_1_pdu_19_sid)g_malloc0(length + 1);
memcpy(key, lookup_key, length + 1);
/* Note, this is not multithread safe, but Wireshark isn't multithreaded. */
g_hash_table_insert(sid_buffer_to_sid_id, key, GUINT_TO_POINTER(packet->sender_sid_id));
g_hash_table_insert(sid_id_to_sid_buffer, GUINT_TO_POINTER(packet->sender_sid_id), key);
/* NOTE: We are storing a reference to the SID in the packet data. This memory
* will be freed by the dissector init routine when the SID hash table is destroyed.
* Nothing else should free this SID.
*/
packet->sender_sid = (dof_2009_1_pdu_19_sid)key;
/* We have learned the "correct" sid and sid_id, so we can set the sid of
* any packets that have this sid_id (saves hash lookups in the future).
*/
{
dof_packet_data *ptr = globals.dof_packet_head;
while (ptr)
{
if (ptr->sender_sid_id == packet->sender_sid_id)
ptr->sender_sid = key;
if (ptr->receiver_sid_id == packet->sender_sid_id)
ptr->receiver_sid = key;
ptr = ptr->next;
}
}
}
/**
* Learn a SID from an explict operation. This only defines sids and sid ids.
*/
static void learn_operation_sid(dof_2009_1_pdu_20_opid *opid, guint8 length, const guint8 *sid)
{
guint8 lookup_key[256];
guint8 *key;
gpointer value;
/* Check for sender SID already known. */
if (opid->op_sid)
return;
/* Check for SID already known (has assigned SID ID) */
/* Build a (non-allocated) key to do the lookup. */
lookup_key[0] = length;
memcpy(lookup_key + 1, sid, length);
if (g_hash_table_lookup_extended(sid_buffer_to_sid_id, &lookup_key, (gpointer *)&key, &value))
{
guint sid_id = GPOINTER_TO_UINT(value);
opid->op_sid_id = sid_id;
opid->op_sid = key;
return;
}
/* The SID has never been seen. Associate with the SID ID. */
key = (dof_2009_1_pdu_19_sid)g_malloc0(length + 1);
memcpy(key, lookup_key, length + 1);
/* Assign the op_sid_id. */
opid->op_sid_id = dpp_next_sid_id++;
/* Note, this is not multithread safe, but Wireshark isn't multithreaded. */
g_hash_table_insert(sid_buffer_to_sid_id, key, GUINT_TO_POINTER(opid->op_sid_id));
g_hash_table_insert(sid_id_to_sid_buffer, GUINT_TO_POINTER(opid->op_sid_id), key);
/* NOTE: We are storing a reference to the SID in the packet data. This memory
* will be freed by the dissector init routine when the SID hash table is destroyed.
* Nothing else should free this SID.
*/
opid->op_sid = (dof_2009_1_pdu_19_sid)key;
}
static void encryptInPlace(guint protocol_id, void *cipher_state, guint8 *ptct, guint8 ptct_len)
{
switch (protocol_id)
{
case DOF_PROTOCOL_CCM: /* Encrypt is AES */
{
rijndael_ctx *ctx = (rijndael_ctx *)cipher_state;
guint8 ct[16];
if (ptct_len != 16)
{
memset(ptct, 0, ptct_len);
return;
}
rijndael_encrypt(ctx, ptct, ct);
memcpy(ptct, ct, sizeof(ct));
}
break;
case DOF_PROTOCOL_TEP: /* Encrypt is AES */
{
rijndael_ctx *ctx = (rijndael_ctx *)cipher_state;
guint8 ct[16];
if (ptct_len != 16)
{
memset(ptct, 0, ptct_len);
return;
}
rijndael_encrypt(ctx, ptct, ct);
memcpy(ptct, ct, sizeof(ct));
}
break;
default: /* Unsupported, zero the mac. */
memset(ptct, 0, ptct_len);
return;
}
}
static void generateMac(guint protocol_id, void *cipher_state, guint8 *nonce, const guint8 *epp, gint a_len, guint8 *data, gint len, guint8 *mac, gint mac_len)
{
guint16 i;
guint16 cnt;
/* a_len = 1, t = mac_len, q = 4: (t-2)/2 : (q-1) -> 4B */
mac[0] = 0x43 | (((mac_len - 2) / 2) << 3);
memcpy(mac + 1, nonce, 11);
memset(mac + 12, 0, 4);
mac[14] = len >> 8;
mac[15] = len & 0xFF;
encryptInPlace(protocol_id, cipher_state, mac, 16);
mac[0] ^= (a_len >> 8);
mac[1] ^= (a_len);
i = 2;
for (cnt = 0; cnt < a_len; cnt++, i++)
{
if (i % 16 == 0)
encryptInPlace(protocol_id, cipher_state, mac, 16);
mac[i % 16] ^= epp[cnt];
}
i = 0;
for (cnt = 0; cnt < len; cnt++, i++)
{
if (i % 16 == 0)
encryptInPlace(protocol_id, cipher_state, mac, 16);
mac[i % 16] ^= data[cnt];
}
encryptInPlace(protocol_id, cipher_state, mac, 16);
}
static int decrypt(ccm_session_data *session, ccm_packet_data *pdata, guint8 *nonce, const guint8 *epp, gint a_len, guint8 *data, gint len)
{
unsigned short i;
unsigned char ctr[16];
unsigned char encrypted_ctr[16];
unsigned char mac[16];
unsigned char computed_mac[16];
unsigned int skip;
guint8 *ekey;
if (data == NULL || len == 0)
return 0;
/* Check the mac length. */
if (session->mac_len < 4 || session->mac_len > 16)
return 0;
if (pdata->period == 0)
ekey = (guint8 *)session->cipher_data;
else
ekey = (guint8 *)g_hash_table_lookup(session->cipher_data_table, GUINT_TO_POINTER(pdata->period));
if (!ekey)
return 0;
/* Determine how many blocks are skipped. */
#if 0 /* seems to be dead code... check this! */
skip = a_len + 2;
skip /= 16;
if ((a_len + 2) % 16)
skip += 1;
#endif
skip = 0;
/* This is hard-coded for q=4. This can only change with a protocol revision.
Note the value is stored as (q-1). */
ctr[0] = 0x03;
memcpy(ctr + 1, nonce, 11);
ctr[12] = 0;
ctr[13] = 0;
ctr[14] = 0;
ctr[15] = skip; /* Preincremented below. */
for (i = 0; i < len - session->mac_len; i++)
{
if (i % 16 == 0)
{
if (ctr[15] == 255)
ctr[14] += 1;
ctr[15] += 1;
memcpy(encrypted_ctr, ctr, 16);
encryptInPlace(session->protocol_id, session->cipher_data, encrypted_ctr, 16);
}
data[i] ^= encrypted_ctr[i % 16];
}
memcpy(mac, data + i, session->mac_len);
ctr[12] = 0;
ctr[13] = 0;
ctr[14] = 0;
ctr[15] = 0;
memcpy(encrypted_ctr, ctr, 16);
encryptInPlace(session->protocol_id, session->cipher_data, encrypted_ctr, 16);
for (i = 0; i < session->mac_len; i++)
mac[i] ^= encrypted_ctr[i];
/* Now we have to generate the MAC... */
generateMac(session->protocol_id, session->cipher_data, nonce, epp, a_len, data, (gint)(len - session->mac_len), computed_mac, session->mac_len);
if (!memcmp(mac, computed_mac, session->mac_len))
return 1;
/* Failure */
return 0;
}
/* Master Protocol Layer Handlers */
/**
* This dissector is handed a DPP packet of any version. It is responsible for decoding
* the common header fields and then passing off to the specific DPP dissector
*/
static int dissect_app_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
col_clear(pinfo->cinfo, COL_INFO);
/* Compute the APP control information. This is the version and the flags byte.
* The flags byte is either present, or is based on the version (and can be defaulted).
*/
{
guint16 app;
gint app_len;
read_c2(tvb, 0, &app, &app_len);
col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "APP(%u)", app);
/* call the next dissector */
if (dissector_try_uint_new(app_dissectors, app, tvb, pinfo, tree, TRUE, data))
{
col_set_fence(pinfo->cinfo, COL_PROTOCOL);
col_set_fence(pinfo->cinfo, COL_INFO);
return tvb_reported_length(tvb);
}
else
{
proto_tree_add_protocol_format(tree, proto_2008_1_app, tvb, 0, app_len,
DOF_APPLICATION_PROTOCOL ", Version: %u", app);
}
}
return 0;
}
/**
* This dissector is handed a DPP packet of any version. It is responsible for decoding
* the common header fields and then passing off to the specific DPP dissector
*/
static int dof_dissect_dpp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
dof_api_data *api_data = (dof_api_data *)data;
guint offset = 0;
DISSECTOR_ASSERT(api_data != NULL);
col_clear(pinfo->cinfo, COL_INFO);
/* Compute the DPP control information. This is the version and the flags byte.
* The flags byte is either present, or is based on the version (and can be defaulted).
*/
{
guint8 header = tvb_get_guint8(tvb, offset);
guint8 dpp_version = header & 0x7F;
guint8 dpp_flags_included = header & 0x80;
proto_item *hi;
proto_tree * dpp_root,*dpp_tree;
col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "DPPv%u", dpp_version);
hi = proto_tree_add_protocol_format(tree, proto_2008_1_dpp, tvb, offset, 0,
DOF_PRESENTATION_PROTOCOL " Version %u, Flags: %s", dpp_version, dpp_flags_included ? "Included" : "Default");
dpp_root = proto_item_add_subtree(hi, ett_2008_1_dpp);
dpp_tree = proto_tree_add_subtree(dpp_root, tvb, offset, 1, ett_2008_1_dpp_1_header, NULL, "Header");
/* Version and Flag bit */
proto_tree_add_item(dpp_tree, hf_2008_1_dpp_1_flag, tvb, offset, 1, ENC_NA);
proto_tree_add_item(dpp_tree, hf_2008_1_dpp_1_version, tvb, offset, 1, ENC_NA);
offset += 1;
/* This may, in some cases, be the end of the packet. This is only valid in some
* situations, which are checked here.
*/
if (offset == tvb_reported_length(tvb))
{
/* TODO: Complete this logic. */
proto_item_set_len(hi, offset);
if (!api_data)
return offset;
if (api_data->transport_session->is_streaming)
{
col_append_fstr(pinfo->cinfo, COL_INFO, "DNP/DPP Negotiation");
if (pinfo->fd->flags.visited &&
api_data->transport_session->negotiation_required &&
((api_data->transport_session->negotiation_complete_at == 0) || (api_data->transport_session->negotiation_complete_at_ts.secs - api_data->transport_session->session_start_ts.secs > 10)))
{
/* This is the second pass, so we can check for timeouts. */
expert_add_info(pinfo, hi, &ei_dof_6_timeout);
}
return offset;
}
}
/* call the next dissector */
if (dissector_try_uint_new(dof_dpp_dissectors, dpp_version, tvb, pinfo, dpp_root, FALSE, data))
{
col_set_fence(pinfo->cinfo, COL_PROTOCOL);
col_set_fence(pinfo->cinfo, COL_INFO);
return tvb_reported_length(tvb);
}
}
return 0;
}
/**
* This dissector is handed a DNP packet of any version. It is responsible for decoding
* the common header fields and then passing off to the specific DNP dissector
*/
static int dof_dissect_dnp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dof_api_data *api_data, gint offset)
{
guint8 header = tvb_get_guint8(tvb, offset);
guint8 dnp_version = header & 0x7F;
guint8 dnp_flags_included = header & 0x80;
proto_item *main_ti;
proto_tree * dnp_root,*dnp_tree;
col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "DNPv%u", dnp_version);
main_ti = proto_tree_add_protocol_format(tree, proto_2008_1_dnp, tvb, offset, 0,
DOF_NETWORK_PROTOCOL " Version %u, Flags: %s", dnp_version, dnp_flags_included ? "Included" : "Default");
dnp_root = proto_item_add_subtree(main_ti, ett_2008_1_dnp);
dnp_tree = proto_tree_add_subtree(dnp_root, tvb, offset, 1, ett_2008_1_dnp_header, NULL, "Header");
/* Version and Flag bit */
proto_tree_add_item(dnp_tree, hf_2008_1_dnp_1_flag, tvb, offset, 1, ENC_NA);
proto_tree_add_item(dnp_tree, hf_2008_1_dnp_1_version, tvb, offset, 1, ENC_NA);
/* call the next dissector */
if (dissector_try_uint_new(dnp_dissectors, dnp_version, tvb, pinfo, dnp_root, FALSE, api_data))
{
/* Since the transport may have additional packets in this frame, protect our work. */
col_set_fence(pinfo->cinfo, COL_PROTOCOL);
col_set_fence(pinfo->cinfo, COL_INFO);
}
else
{
proto_item_set_end(main_ti, tvb, 1);
/* During negotiation, we can move past DNP even if it is not known. */
if (((header & 0x80) == 0) && api_data->transport_session->negotiation_required && ((pinfo->fd->num < api_data->transport_session->negotiation_complete_at) || (api_data->transport_session->negotiation_complete_at == 0)))
{
offset += dof_dissect_dpp_common(tvb_new_subset_remaining(tvb, offset + 1), pinfo, tree, api_data);
}
}
if (dnp_flags_included && !api_data->transport_session->negotiation_complete_at)
{
api_data->transport_session->negotiation_complete_at = pinfo->fd->num;
api_data->transport_session->negotiation_complete_at_ts = pinfo->fd->abs_ts;
}
return offset;
}
/**
* This dissector is called for each DPS packet. It assumes that the first layer is
* DNP, but it does not know anything about versioning. Further, it only worries
* about decoding DNP (DNP will decode DPP, and so on).
*
* This routine is given the DPS packet for the first packet, but doesn't know anything
* about DPS sessions. It may understand transport sessions, but these are surprisingly
* worthless for DPS.
*/
static int dissect_dof_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
dof_api_data *api_data = (dof_api_data *)data;
proto_tree *dof_root;
dof_packet_data *packet;
DISSECTOR_ASSERT(api_data != NULL);
DISSECTOR_ASSERT(api_data->transport_session != NULL);
DISSECTOR_ASSERT(api_data->transport_packet != NULL);
packet = (dof_packet_data *)api_data->packet;
/* Create the packet if it doesn't exist. */
if (packet == NULL)
{
api_data->packet = packet = create_packet_data(pinfo);
DISSECTOR_ASSERT(packet != NULL);
/* TODO: This is not correct for reversed sessions. */
packet->is_sent_by_initiator = api_data->transport_packet->is_sent_by_client;
}
/* Assign the transport sequence if it does not exist. */
if (api_data->transport_session->transport_session_id == 0)
api_data->transport_session->transport_session_id = globals.next_transport_session++;
/* Compute the DPS information. This is a master holder for general information. */
{
proto_item *ti;
ti = proto_tree_add_protocol_format(tree, proto_2008_1_dof, tvb, 0, tvb_reported_length(tvb), DOF_PROTOCOL_STACK);
dof_root = proto_item_add_subtree(ti, ett_2008_1_dof);
/* Add the general packet information. */
{
ti = proto_tree_add_uint(dof_root, hf_2008_1_dof_session_transport, tvb, 0, 0, api_data->transport_session->transport_session_id);
PROTO_ITEM_SET_GENERATED(ti);
ti = proto_tree_add_boolean(dof_root, hf_2008_1_dof_is_2_node, tvb, 0, 0, api_data->transport_session->is_2_node);
PROTO_ITEM_SET_GENERATED(ti);
ti = proto_tree_add_boolean(dof_root, hf_2008_1_dof_is_streaming, tvb, 0, 0, api_data->transport_session->is_streaming);
PROTO_ITEM_SET_GENERATED(ti);
if (api_data->session)
{
ti = proto_tree_add_uint(dof_root, hf_2008_1_dof_session, tvb, 0, 0, api_data->session->session_id);
PROTO_ITEM_SET_GENERATED(ti);
}
if (api_data->secure_session)
{
ti = proto_tree_add_uint_format(dof_root, hf_2008_1_dof_session, tvb, 0, 0, api_data->secure_session->original_session_id, "DPS Session (Non-secure): %d", api_data->secure_session->original_session_id);
PROTO_ITEM_SET_GENERATED(ti);
}
ti = proto_tree_add_uint(dof_root, hf_2008_1_dof_frame, tvb, 0, 0, packet->dof_frame);
PROTO_ITEM_SET_GENERATED(ti);
ti = proto_tree_add_boolean(dof_root, hf_2008_1_dof_is_from_client, tvb, 0, 0, api_data->transport_packet->is_sent_by_client);
PROTO_ITEM_SET_GENERATED(ti);
}
}
dof_dissect_dnp_common(tvb, pinfo, tree, api_data, 0);
packet->processed = TRUE;
return tvb_reported_length(tvb);
}
/**
* This dissector is called for each DPS packet. It assumes that the first layer is
* ENP, but it does not know anything about versioning. Further, it only worries
* about decoding ENP (ENP will decode EPP, and so on).
*
* This routine is given the DPS packet for the first packet, but doesn't know anything
* about DPS sessions. It may understand transport sessions, but these are surprisingly
* worthless for DPS.
*/
static int dissect_tunnel_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
/* The packet data is the private_data, and must exist. */
tcp_dof_packet_ref *ref = (tcp_dof_packet_ref *)data;
gint offset = 0;
offset = 0;
/* Compute the APP control information. This is the version and the length bytes.
* The flags byte is either present, or is based on the version (and can be defaulted).
*/
{
guint8 version = tvb_get_guint8(tvb, offset);
guint8 opcode;
proto_item *ti;
proto_tree *app_root;
col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "TUNv%u", version);
ti = proto_tree_add_protocol_format(tree, proto_2012_1_tunnel, tvb, offset, 0,
"DOF Tunnel Protocol, Version: %u", version);
app_root = proto_item_add_subtree(ti, ett_2012_1_tunnel);
proto_tree_add_item(app_root, hf_2012_1_tunnel_1_version, tvb, offset, 1, ENC_NA);
proto_tree_add_item(app_root, hf_2012_1_tunnel_1_length, tvb, offset + 1, 2, ENC_BIG_ENDIAN);
opcode = tvb_get_guint8(tvb, offset + 3);
if (opcode == 3)
{
tvbuff_t *next_tvb = tvb_new_subset(tvb, offset + 5, -1, -1);
dissect_dof_common(next_tvb, pinfo, tree, &ref->api_data);
}
}
return tvb_captured_length(tvb);
}
static int dissect_tun_app_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
col_clear(pinfo->cinfo, COL_INFO);
/* Compute the APP control information. This is the version and the flags byte.
* The flags byte is either present, or is based on the version (and can be defaulted).
*/
{
guint16 app;
gint app_len;
app = tvb_get_guint8(tvb, 0);
app_len = 1;
col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "APP(%u)", app);
/* call the next dissector */
if (dissector_try_uint(dof_tun_app_dissectors, app, tvb, pinfo, tree))
{
col_set_fence(pinfo->cinfo, COL_PROTOCOL);
col_set_fence(pinfo->cinfo, COL_INFO);
return tvb_captured_length(tvb);
}
else
{
proto_tree_add_protocol_format(tree, proto_2012_1_tunnel, tvb, 0, app_len,
DOF_APPLICATION_PROTOCOL ", Version: %u", app);
}
}
return 0;
}
/* Packet and Session Data Creation */
static udp_session_data* create_udp_session_data(packet_info *pinfo, conversation_t *conversation _U_)
{
udp_session_data *packet = wmem_new0(wmem_file_scope(), udp_session_data);
/* TODO: Determine if this is valid or not. */
/* WMEM_COPY_ADDRESS( wmem_file_scope(), &packet->server.address, &conversation->key_ptr->addr1 );
packet->server.port = conversation->key_ptr->port1; */
copy_address_wmem(wmem_file_scope(), &packet->server.addr, &pinfo->dst);
packet->server.port = pinfo->destport;
packet->common.transport_id = proto_2008_1_dof_udp;
{
const guint8 *addr = (const guint8 *)packet->server.addr.data;
if ((packet->server.addr.type == AT_IPv4) && (addr != NULL) && (addr[0] != 224))
packet->common.is_2_node = TRUE;
else
packet->common.is_2_node = FALSE;
}
packet->common.is_streaming = FALSE;
packet->common.session_start_ts = pinfo->fd->abs_ts;
packet->common.negotiation_required = FALSE;
packet->common.negotiation_complete_at = 0;
return packet;
}
static tcp_session_data* create_tcp_session_data(packet_info *pinfo, conversation_t *conversation)
{
tcp_session_data *packet = wmem_new0(wmem_file_scope(), tcp_session_data);
copy_address_wmem(wmem_file_scope(), &packet->client.addr, &conversation->key_ptr->addr1);
packet->client.port = conversation->key_ptr->port1;
copy_address_wmem(wmem_file_scope(), &packet->server.addr, &conversation->key_ptr->addr2);
packet->server.port = conversation->key_ptr->port2;
packet->not_dps = FALSE;
packet->common.transport_id = proto_2008_1_dof_tcp;
packet->common.is_2_node = TRUE;
packet->common.is_streaming = TRUE;
packet->common.session_start_ts = pinfo->fd->abs_ts;
packet->common.negotiation_required = TRUE;
packet->common.negotiation_complete_at = 0;
return packet;
}
static dof_packet_data* create_packet_data(packet_info *pinfo)
{
/* Create the packet data. */
dof_packet_data *packet = wmem_new0(wmem_file_scope(), dof_packet_data);
packet->frame = pinfo->fd->num;
packet->dof_frame = next_dof_frame++;
/* Add the packet into the list of packets. */
if (!globals.dof_packet_head)
{
globals.dof_packet_head = packet;
globals.dof_packet_tail = packet;
}
else
{
globals.dof_packet_tail->next = packet;
globals.dof_packet_tail = packet;
}
return packet;
}
/* Dissectors for Transports (UDP/TCP) */
/**
* Dissect a UDP packet. The parent protocol is UDP. No assumptions about DPS
* data structures are made on input, but before calling common they must
* be set up.
* This dissector is registered with the UDP protocol on the standard DPS port.
* It will be used for anything that involves that port (source or destination).
*/
static int dissect_dof_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
dof_api_data *api_data = (dof_api_data *)p_get_proto_data(wmem_file_scope(), pinfo, proto_2008_1_dof_udp, 0);
if (api_data == NULL)
{
conversation_t *conversation;
udp_session_data *transport_session;
dof_transport_packet *transport_packet;
/* gboolean mcast = FALSE; */
/* {
guint8* addr = (guint8*) pinfo->dst.data;
if ( (pinfo->dst.type == AT_IPv4) && (addr != NULL) && (addr[0] != 224) )
mcast = TRUE;
} */
/* Register the source address as being DPS for the sender UDP port. */
conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, NO_ADDR_B | NO_PORT_B);
if (!conversation)
{
conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, NO_ADDR_B | NO_PORT_B);
conversation_set_dissector(conversation, dof_udp_handle);
}
/* Find or create the conversation for this transport session. For UDP, the transport session is determined entirely by the
* server port. This assumes that the first packet seen is from a client to the server.
*/
conversation = find_conversation(pinfo->fd->num, &pinfo->dst, &pinfo->src, PT_UDP, pinfo->destport, pinfo->srcport, NO_ADDR_B | NO_PORT_B);
if (conversation)
{
/* TODO: Determine if this is valid or not. */
/*if ( conversation->key_ptr->port1 != pinfo->destport || ! addresses_equal( &conversation->key_ptr->addr1, &pinfo->dst ) )
conversation = NULL; */
}
if (!conversation)
conversation = conversation_new(pinfo->fd->num, &pinfo->dst, &pinfo->src, PT_UDP, pinfo->destport, pinfo->srcport, NO_ADDR2 | NO_PORT2 | CONVERSATION_TEMPLATE);
transport_session = (udp_session_data *)conversation_get_proto_data(conversation, proto_2008_1_dof_udp);
if (transport_session == NULL)
{
transport_session = create_udp_session_data(pinfo, conversation);
conversation_add_proto_data(conversation, proto_2008_1_dof_udp, transport_session);
}
/* UDP has no framing or retransmission issues, so the dof_api_data is stored directly on the frame. */
api_data = wmem_new0(wmem_file_scope(), dof_api_data);
if (api_data == NULL)
return 0;
transport_packet = wmem_new0(wmem_file_scope(), dof_transport_packet);
if (transport_packet == NULL)
return 0;
transport_packet->is_sent_by_client = TRUE;
if (addresses_equal(&transport_session->server.addr, &pinfo->src) && (transport_session->server.port == pinfo->srcport))
transport_packet->is_sent_by_client = FALSE;
transport_packet->sender_id = assign_addr_port_id(&pinfo->src, pinfo->srcport);
transport_packet->receiver_id = assign_addr_port_id(&pinfo->dst, pinfo->destport);
api_data->transport_session = &transport_session->common;
api_data->transport_packet = transport_packet;
p_add_proto_data(wmem_file_scope(), pinfo, proto_2008_1_dof_udp, 0, api_data);
}
return dissect_dof_common(tvb, pinfo, tree, api_data);
}
/**
* Determine if the current offset has already been processed.
* This is specific to the TCP dissector.
*/
static gboolean is_retransmission(packet_info *pinfo, tcp_session_data *session, tcp_packet_data *packet, struct tcpinfo *tcpinfo)
{
/* TODO: Determine why we get big numbers sometimes... */
/* if ( tcpinfo->seq != 0 && tcpinfo->seq < 1000000) */
{
tcp_ignore_data *id;
guint32 sequence = tcpinfo->seq;
if (addresses_equal(&pinfo->src, &session->client.addr) && (pinfo->srcport == session->client.port))
{
id = packet->from_client_ignore_list;
}
else
{
id = packet->from_server_ignore_list;
}
while (id != NULL && id->sequence != sequence)
{
id = id->next;
}
if (id == NULL)
return FALSE;
return id->ignore;
}
return FALSE;
}
/**
* We have found and processed packets starting at offset, so
* don't allow the same (or previous) packets.
* This only applies to TCP dissector conversations.
*/
static void remember_offset(packet_info *pinfo, tcp_session_data *session, tcp_packet_data *packet, struct tcpinfo *tcpinfo)
{
gboolean ignore = FALSE;
/* TODO: Determine why we get big numbers sometimes... */
/* if ( tcpinfo->seq != 0 && tcpinfo->seq < 1000000) */
{
tcp_ignore_data **last;
tcp_ignore_data *id;
guint32 sequence;
guint32 *seqptr = NULL;
if (addresses_equal(&pinfo->src, &session->client.addr) && (pinfo->srcport == session->client.port))
{
last = &(packet->from_client_ignore_list);
id = packet->from_client_ignore_list;
sequence = tcpinfo->seq;
seqptr = &session->from_client_seq;
if (LE_SEQ(tcpinfo->seq, session->from_client_seq))
ignore = TRUE;
}
else
{
last = &(packet->from_server_ignore_list);
id = packet->from_server_ignore_list;
sequence = tcpinfo->seq;
seqptr = &session->from_server_seq;
if (LE_SEQ(tcpinfo->seq, session->from_server_seq))
ignore = TRUE;
}
while (id != NULL && id->sequence != tcpinfo->seq)
{
last = &(id->next);
id = id->next;
}
*seqptr = sequence;
if (id == NULL)
{
*last = (tcp_ignore_data *)wmem_alloc0(wmem_file_scope(), sizeof(tcp_ignore_data));
id = *last;
id->ignore = ignore;
id->sequence = tcpinfo->seq;
}
}
}
/**
* This dissector is registered with TCP using the standard port. It uses registered
* protocols to determine framing, and those dissectors will call into the base
* DPS dissector for each packet.
*/
static int dissect_dof_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
conversation_t *conversation;
tcp_session_data *session;
tcp_packet_data *packet;
struct tcpinfo *tcpinfo = (struct tcpinfo *)data;
guint8 header;
/* Get the TCP conversation. TCP creates a new conversation for each TCP connection,12
* so we can "mirror" that by attaching our own data to that conversation. If our
* data cannot be found, then it is a new connection (to us).
*/
conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
{
/* This should be impossible - the TCP dissector requires this conversation.
* Bail...
*/
DISSECTOR_ASSERT(conversation != NULL);
}
/* This requires explanation. TCP will call this dissector, and we know
* that the first byte (offset 0 of this tvb) is the first byte of an
* DPS packet. The TCP dissector ensures this.
*
* We do *not* know that this is the only packet, and
* so the dissector that we call below must handle framing. All of
* this state must be stored, and so we store it in a transport
* data structure. DPS packet data is created later and associated
* differently.
*
* Further, this routine MAY be called MULTIPLE times for the SAME
* frame with DIFFERENT sequence numbers. This makes handling
* retransmissions very difficult - we must track each call to this
* routine with its associated offset and ignore flag. However, due
* to the way that Wireshark handles asking for more data we cannot
* mark an offset as "duplicate" until after it has been processed.
*/
/* TCP packet data is only associated with TCP frames that hold DPS packets. */
session = (tcp_session_data *)conversation_get_proto_data(conversation, proto_2008_1_dof_tcp);
if (session == NULL)
{
session = create_tcp_session_data(pinfo, conversation);
if (!session)
{
fprintf(stderr, "! session");
return 0;
}
conversation_add_proto_data(conversation, proto_2008_1_dof_tcp, session);
}
if (session->not_dps)
return 0;
packet = (tcp_packet_data *)p_get_proto_data(wmem_file_scope(), pinfo, proto_2008_1_dof_tcp, 0);
if (packet == NULL)
{
packet = (tcp_packet_data *)wmem_alloc0(wmem_file_scope(), sizeof(tcp_packet_data));
if (!packet)
{
fprintf(stderr, "! packet");
return 0;
}
p_add_proto_data(wmem_file_scope(), pinfo, proto_2008_1_dof_tcp, 0, packet);
}
if (is_retransmission(pinfo, session, packet, tcpinfo))
return 0;
/* Loop, checking all the packets in this frame and communicating with the TCP
* desegmenter. The framing dissector entry is used to determine the size
* of the current frame.
*/
{
/* Note that we must handle fragmentation on TCP... */
gint offset = 0;
while (offset < (gint)tvb_reported_length(tvb))
{
gint available = tvb_ensure_captured_length_remaining(tvb, offset);
int packet_length;
header = tvb_get_guint8(tvb, offset);
/* If we are negotiating, then we do not need the framing dissector
* as we know the packet length is two. Note that for the first byte
* of a TCP session there are only two cases, both handled here. An error
* of not understanding the first byte will trigger that this is not
* a DPS session.
*/
if (((header & 0x80) == 0) && session->common.negotiation_required && ((pinfo->fd->num < session->common.negotiation_complete_at) || (session->common.negotiation_complete_at == 0)))
{
packet_length = 2;
if (header > DNP_MAX_VERSION)
{
session->not_dps = TRUE;
return 0;
}
}
else
{
packet_length = dof_dissect_dnp_length(tvb, pinfo, header & 0x7F, &offset);
if (packet_length < 0)
{
session->not_dps = TRUE;
return offset;
}
}
if (packet_length == 0)
{
pinfo->desegment_offset = offset;
pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
return offset;
}
if (available < packet_length)
{
pinfo->desegment_offset = offset;
pinfo->desegment_len = packet_length - available;
return offset;
}
remember_offset(pinfo, session, packet, tcpinfo);
if (is_retransmission(pinfo, session, packet, tcpinfo))
return 0;
/* We have a packet. We have to store the dof_packet_data in a list, as there may be
* multiple DPS packets in a single Wireshark frame.
*/
{
tvbuff_t *next_tvb = tvb_new_subset(tvb, offset, packet_length, packet_length);
tcp_dof_packet_ref *ref;
gint raw_offset = tvb_raw_offset(tvb) + offset;
gboolean ref_is_new = FALSE;
/* Get the packet data. This is a list in increasing sequence order. */
if (packet->dof_packets == NULL)
{
ref_is_new = TRUE;
ref = (tcp_dof_packet_ref *)wmem_alloc0(wmem_file_scope(), sizeof(tcp_dof_packet_ref));
ref->transport_packet.sender_id = assign_addr_port_id(&pinfo->src, pinfo->srcport);
ref->transport_packet.receiver_id = assign_addr_port_id(&pinfo->dst, pinfo->destport);
packet->dof_packets = ref;
ref->start_offset = raw_offset;
}
else
ref = packet->dof_packets;
/* Find the entry for our offset. */
while (ref->start_offset != raw_offset)
{
if (ref->next)
{
ref = ref->next;
continue;
}
{
tcp_dof_packet_ref *last = ref;
/* This is the default state, NULL and 0. */
ref_is_new = TRUE;
ref = wmem_new0(wmem_file_scope(), tcp_dof_packet_ref);
if (!ref)
{
fprintf(stderr, "! ref");
return offset;
}
ref->transport_packet.sender_id = last->transport_packet.sender_id;
ref->transport_packet.receiver_id = last->transport_packet.receiver_id;
ref->start_offset = raw_offset;
last->next = ref;
}
}
if (ref_is_new)
{
dof_transport_packet *tp = &(ref->transport_packet);
tp->is_sent_by_client = FALSE;
if (addresses_equal(&session->client.addr, &pinfo->src) &&
(session->client.port == pinfo->srcport))
tp->is_sent_by_client = TRUE;
ref->api_data.transport_session = (dof_transport_session *)&(session->common);
ref->api_data.transport_packet = tp;
}
dissect_dof_common(next_tvb, pinfo, tree, &ref->api_data);
}
offset += packet_length;
}
return offset;
}
}
#if 0 /* TODO not used yet */
/**
* This dissector is registered with the UDP protocol on the standard DPS port.
* It will be used for anything that involves that port (source or destination).
*/
#if 0
static int dissect_tunnel_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
conversation_t *conversation;
dof_packet_data *packet;
/* Initialize the default transport session structure. */
if (!udp_transport_session)
udp_transport_session = se_alloc0(sizeof(*udp_transport_session));
conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP, pinfo->srcport, pinfo->destport, 0);
if (!conversation)
{
conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, PT_UDP, pinfo->srcport, pinfo->destport, 0);
}
/* Add the packet data. */
packet = p_get_proto_data(wmem_file_scope(), proto_2012_1_tunnel, 0);
if (!packet)
{
packet = wmem_alloc0(wmem_file_scope(), sizeof(dof_packet_data));
packet->frame = pinfo->fd->num;
packet->next = NULL;
packet->start_offset = 0;
packet->session_counter = &session_counter;
packet->transport_session = udp_transport_session;
p_add_proto_data(wmem_file_scope(), proto_2012_1_tunnel, 0, packet);
}
pinfo->private_data = packet;
return dissect_tunnel_common(tvb, pinfo, tree);
#else
static int dissect_tunnel_udp(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_)
{
#endif
return 0;
}
#endif
/**
* This dissector is registered with TCP using the standard port. It uses registered
* protocols to determine framing, and those dissectors will call into the base
* DPS dissector for each packet.
*/
static int dissect_tunnel_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
conversation_t *conversation;
tcp_session_data *session;
tcp_packet_data *packet;
struct tcpinfo *tcpinfo = (struct tcpinfo *)data;
/* Get the TCP conversation. TCP creates a new conversation for each TCP connection,
* so we can "mirror" that by attaching our own data to that conversation. If our
* data cannot be found, then it is a new connection (to us).
*/
conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
{
/* This should be impossible - the TCP dissector requires this conversation.
* Bail...
*/
DISSECTOR_ASSERT(conversation != NULL);
}
/* This requires explanation. TCP will call this dissector, and we know
* that the first byte (offset 0 of this tvb) is the first byte of an
* DPS packet. The TCP dissector ensures this.
*
* We do *not* know that this is the only packet, and
* so the dissector that we call below must handle framing. All of
* this state must be stored, and so we store it in a transport
* data structure. DPS packet data is created later and associated
* differently.
*
* Further, this routine MAY be called MULTIPLE times for the SAME
* frame with DIFFERENT sequence numbers. This makes handling
* retransmissions very difficult - we must track each call to this
* routine with its associated offset and ignore flag. However, due
* to the way that Wireshark handles asking for more data we cannot
* mark an offset as "duplicate" until after it has been processed.
*/
/* TCP packet data is only associated with TCP frames that hold DPS packets. */
session = (tcp_session_data *)conversation_get_proto_data(conversation, proto_2012_1_tunnel);
if (session == NULL)
{
session = create_tcp_session_data(pinfo, conversation);
if (!session)
{
fprintf(stderr, "! session");
return 0;
}
conversation_add_proto_data(conversation, proto_2012_1_tunnel, session);
}
packet = (tcp_packet_data *)p_get_proto_data(wmem_file_scope(), pinfo, proto_2012_1_tunnel, 0);
if (packet == NULL)
{
packet = (tcp_packet_data *)wmem_alloc0(wmem_file_scope(), sizeof(tcp_packet_data));
if (!packet)
{
fprintf(stderr, "! packet");
return 0;
}
p_add_proto_data(wmem_file_scope(), pinfo, proto_2012_1_tunnel, 0, packet);
}
if (is_retransmission(pinfo, session, packet, tcpinfo))
return 0;
/* Loop, checking all the packets in this TCP frame.
*/
{
/* Note that we must handle fragmentation on TCP... */
gint offset = 0;
while (offset < (gint)tvb_reported_length(tvb))
{
gint available = tvb_reported_length_remaining(tvb, offset);
int packet_length;
int header_length;
int i;
if (available < 3)
{
pinfo->desegment_offset = offset;
pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
return offset + available;
}
packet_length = 0;
header_length = 3;
for (i = 0; i < 2; i++)
packet_length = packet_length * 256 + tvb_get_guint8(tvb, offset + 1 + i);
packet_length += header_length;
if (available < packet_length)
{
pinfo->desegment_offset = offset;
pinfo->desegment_len = packet_length - available;
return offset + available;
}
/* We have a packet. We have to store the dof_packet_data in a list, as there may be
* multiple DPS packets in a single Wireshark frame.
*/
{
tvbuff_t *next_tvb = tvb_new_subset(tvb, offset, packet_length, packet_length);
tcp_dof_packet_ref *ref;
gint raw_offset = tvb_raw_offset(tvb) + offset;
gboolean ref_is_new = FALSE;
/* Get the packet data. This is a list in increasing sequence order. */
if (packet->dof_packets == NULL)
{
ref_is_new = TRUE;
ref = (tcp_dof_packet_ref *)wmem_alloc0(wmem_file_scope(), sizeof(tcp_dof_packet_ref));
ref->transport_packet.sender_id = assign_addr_port_id(&pinfo->src, pinfo->srcport);
ref->transport_packet.receiver_id = assign_addr_port_id(&pinfo->dst, pinfo->destport);
packet->dof_packets = ref;
ref->start_offset = raw_offset;
}
else
ref = packet->dof_packets;
/* Find the entry for our offset. */
while (ref->start_offset != raw_offset)
{
if (ref->next)
{
ref = ref->next;
continue;
}
{
tcp_dof_packet_ref *last = ref;
/* This is the default state, NULL and 0. */
ref_is_new = TRUE;
ref = (tcp_dof_packet_ref *)wmem_alloc0(wmem_file_scope(), sizeof(tcp_dof_packet_ref));
if (!ref)
{
fprintf(stderr, "! ref");
return offset;
}
ref->transport_packet.sender_id = last->transport_packet.sender_id;
ref->transport_packet.receiver_id = last->transport_packet.receiver_id;
ref->start_offset = raw_offset;
last->next = ref;
}
}
if (ref_is_new)
{
dof_transport_packet *tp = &(ref->transport_packet);
tp->is_sent_by_client = FALSE;
if (addresses_equal(&session->client.addr, &pinfo->src) &&
(session->client.port == pinfo->srcport))
tp->is_sent_by_client = TRUE;
ref->api_data.transport_session = (dof_transport_session *)&(session->common);
ref->api_data.transport_packet = tp;
}
/* Manage the private data, restoring the existing value. Call the common dissector. */
{
dissect_tunnel_common(next_tvb, pinfo, tree, ref);
}
}
offset += packet_length;
}
return tvb_captured_length(tvb);
}
}
/* Dissectors */
static int dissect_dnp_0(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
guint offset = 0;
guint8 dnp_flags_included = 0;
offset = 0;
col_clear(pinfo->cinfo, COL_INFO);
/* Compute the DNP control information. This is the version and the flags byte.
* The flags byte is either present, or is based on the version (and can be defaulted).
*/
{
guint8 header = tvb_get_guint8(tvb, offset);
dnp_flags_included = (header & 0x80) != 0;
offset += 1;
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "DNPv0 ");
if (dnp_flags_included)
{
/* TODO: Protocol violation. */
}
if (tvb_reported_length(tvb) == offset)
col_set_str(pinfo->cinfo, COL_INFO, "Query");
else
{
guint8 first = tvb_get_guint8(tvb, offset);
if (first == 0)
{
/* Query with padding. */
col_set_str(pinfo->cinfo, COL_INFO, "Query");
proto_tree_add_item(tree, hf_2008_1_dnp_0_1_1_padding, tvb, offset, -1, ENC_NA);
}
else
{
/* Response. */
col_set_str(pinfo->cinfo, COL_INFO, "Query Response");
while (first)
{
proto_tree_add_item(tree, hf_2008_1_dnp_0_1_1_version, tvb, offset, 1, ENC_NA);
offset += 1;
if (offset == tvb_reported_length(tvb))
break;
first = tvb_get_guint8(tvb, offset);
}
if (offset < tvb_reported_length(tvb))
proto_tree_add_item(tree, hf_2008_1_dnp_0_1_1_padding, tvb, offset, -1, ENC_NA);
}
}
}
}
col_set_fence(pinfo->cinfo, COL_PROTOCOL);
col_set_fence(pinfo->cinfo, COL_INFO);
return tvb_reported_length(tvb);
}
/**
* Determine the length of the packet in tvb, starting at an offset that is passed as a
* pointer in private_data.
* Return 0 if the length cannot be determined because there is not enough data in
* the buffer, otherwise return the length of the packet.
*/
static int determine_packet_length_1(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, void *data)
{
/* Note that we must handle fragmentation on TCP... */
gint offset = *((gint *)data);
{
gint available = tvb_ensure_captured_length_remaining(tvb, offset);
guint8 header, flags;
guint8 size;
guint8 i;
gint data_len, header_len;
if (available < 2)
return 0;
header = tvb_get_guint8(tvb, offset);
data_len = 0;
if ((header & 0x80) == 0)
{
/* The length is fixed in this case... */
data_len = 0;
header_len = 2;
size = 0;
}
else
{
flags = tvb_get_guint8(tvb, offset + 1);
size = flags & 0x03;
header_len = 2 + size;
}
if (available < header_len)
return 0;
for (i = 0; i < size; i++)
data_len = data_len * 256 + tvb_get_guint8(tvb, offset + 2 + i);
return header_len + data_len;
}
}
static int dissect_dnp_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
gint offset = 0;
dof_api_data *api_data = (dof_api_data *)data;
dof_packet_data *packet;
gint8 dnp_version = -1;
guint8 dnp_flags_included = 0;
guint8 dnp_length_length = 0;
guint32 dnp_flags = 0;
guint length = 0;
guint encapsulated_length = 0;
int i;
proto_tree *dnp_tree = tree;
if (!api_data)
{
/* TODO: Print error */
return 0;
}
if (!api_data->packet)
{
/* TODO: Print error */
return 0;
}
packet = api_data->packet;
offset = 0;
col_clear(pinfo->cinfo, COL_INFO);
/* Compute the DNP control information. This is the version and the flags byte.
* The flags byte is either present, or is based on the version (and can be defaulted).
*/
{
guint8 header = tvb_get_guint8(tvb, offset);
guint32 dnp_src_port = 0;
guint32 dnp_dst_port = 0;
dnp_version = header & 0x7F;
dnp_flags_included = (header & 0x80) != 0;
offset += 1;
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "DNPv1 ");
if (dnp_flags_included)
{
/* Including flags always terminates negotiation. */
/* packet->negotiated = TRUE; */
dnp_flags = tvb_get_guint8(tvb, offset);
if ((dnp_flags & 0xF0) != 0)
expert_add_info(pinfo, NULL, &ei_dof_10_flags_zero);
proto_tree_add_bitmask(dnp_tree, tvb, offset, hf_2009_9_dnp_1_flags, ett_2009_9_dnp_1_flags, bitmask_2009_9_dnp_1_flags, ENC_BIG_ENDIAN);
offset += 1;
}
else
dnp_flags = DNP_V1_DEFAULT_FLAGS;
/* Determine the size of the length field. */
dnp_length_length = dnp_flags & 0x03;
if (dnp_length_length)
proto_tree_add_item(dnp_tree, hf_2009_9_dnp_1_length, tvb, offset, dnp_length_length, ENC_BIG_ENDIAN);
/* Read the length. */
length = 0;
for (i = 0; i < dnp_length_length; i++)
length = (length << 8) | tvb_get_guint8(tvb, offset + i);
/* Validate the length. */
#if 0
if ( (length == 0) && packet->negotiated && session && ! session->connectionless )
{
expert_add_info( pinfo, NULL, &ei_dof_13_length_specified );
}
#endif
offset += dnp_length_length;
/* If there isn't a length specified then use the packet size. */
if (dnp_length_length == 0)
length = tvb_reported_length(tvb) - offset;
encapsulated_length = length;
/* Read the srcport */
if (dnp_flags & 0x04)
{
gint s_offset = offset;
proto_item *item;
gint dnp_src_port_len;
offset = read_c3(tvb, offset, &dnp_src_port, &dnp_src_port_len);
item = proto_tree_add_uint_format(dnp_tree, hf_2009_9_dnp_1_srcport, tvb, s_offset, offset - s_offset, dnp_src_port, "Source Address: %u", dnp_src_port);
validate_c3(pinfo, item, dnp_src_port, dnp_src_port_len);
encapsulated_length -= (offset - s_offset);
}
else
{
proto_item *item = proto_tree_add_uint_format(dnp_tree, hf_2009_9_dnp_1_srcport, tvb, 0, 0, 0, "Source Address: %u", 0);
PROTO_ITEM_SET_GENERATED(item);
}
/* Read the dstport */
if (dnp_flags & 0x08)
{
gint s_offset = offset;
gint dnp_dst_port_len;
proto_item *item;
offset = read_c3(tvb, offset, &dnp_dst_port, &dnp_dst_port_len);
item = proto_tree_add_uint_format(dnp_tree, hf_2009_9_dnp_1_dstport, tvb, s_offset, offset - s_offset, dnp_dst_port, "Destination Address: %u", dnp_dst_port);
validate_c3(pinfo, item, dnp_dst_port, dnp_dst_port_len);
encapsulated_length -= (offset - s_offset);
}
else
{
proto_item *item = proto_tree_add_uint_format(dnp_tree, hf_2009_9_dnp_1_dstport, tvb, 0, 0, 0, "Destination Address: %u", 0);
PROTO_ITEM_SET_GENERATED(item);
}
}
proto_item_set_end(tree, tvb, offset);
/* Given the transport session and the DPS port information, determine the DPS session. */
if (api_data->session == NULL)
{
guint32 client;
guint32 server;
if (api_data->transport_packet->is_sent_by_client)
{
client = dnp_src_port;
server = dnp_dst_port;
}
else
{
client = dnp_dst_port;
server = dnp_src_port;
}
api_data->session = dof_ns_session_retrieve(api_data->transport_session->transport_session_id, client, server);
if (api_data->session == NULL)
{
dof_session_data *sdata = (dof_session_data *)wmem_alloc0(wmem_file_scope(), sizeof(dof_session_data));
dof_ns_session_define(api_data->transport_session->transport_session_id, client, server, sdata);
sdata->session_id = globals.next_session++;
sdata->dof_id = dnp_version;
api_data->session = sdata;
}
}
packet->sender_id = dnp_src_port;
packet->receiver_id = dnp_dst_port;
/* Assuming there is more, it must be DPP. */
/* We have a packet. */
{
tvbuff_t *next_tvb = tvb_new_subset(tvb, offset, encapsulated_length, tvb_reported_length(tvb) - offset);
offset += dof_dissect_dpp_common(next_tvb, pinfo, proto_item_get_parent(tree), data);
}
}
col_set_fence(pinfo->cinfo, COL_PROTOCOL);
col_set_fence(pinfo->cinfo, COL_INFO);
return offset;
}
static int dissect_dpp_0(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
guint offset = 0;
guint8 dpp_flags_included = 0;
offset = 0;
col_clear(pinfo->cinfo, COL_INFO);
/* Compute the DPP control information. This is the version and the flags byte.
* The flags byte is either present, or is based on the version (and can be defaulted).
*/
{
guint8 header = tvb_get_guint8(tvb, offset);
dpp_flags_included = (header & 0x80) != 0;
offset += 1;
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "DPPv0 ");
if (dpp_flags_included)
{
/* TODO: Protocol violation. */
}
if (tvb_reported_length(tvb) == offset)
col_set_str(pinfo->cinfo, COL_INFO, "Query");
else
{
guint8 first = tvb_get_guint8(tvb, offset);
/* Response. */
col_set_str(pinfo->cinfo, COL_INFO, "Query Response");
while (first)
{
proto_tree_add_item(tree, hf_2008_1_dpp_0_1_1_version, tvb, offset, 1, ENC_NA);
offset += 1;
if (offset == tvb_reported_length(tvb))
break;
first = tvb_get_guint8(tvb, offset);
}
}
}
}
col_set_fence(pinfo->cinfo, COL_PROTOCOL);
col_set_fence(pinfo->cinfo, COL_INFO);
return tvb_reported_length(tvb);
}
static int dissect_dpp_v2_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
dof_api_data *api_data = (dof_api_data *)data;
dof_packet_data *packet_data;
gint offset = 0;
guint8 opcode;
guint16 app;
gint app_len;
proto_item *ti;
proto_tree *dpps_tree;
proto_tree *opid_tree;
if (api_data == NULL)
{
/* TODO: Output error. */
return 0;
}
packet_data = api_data->packet;
if (packet_data == NULL)
{
/* TODO: Output error. */
return 0;
}
/* Make entries in Protocol column and Info column on summary display */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "DPPs ");
/* Create the protocol tree. */
offset = 0;
ti = proto_tree_add_item(tree, proto_2009_12_dpp_common, tvb, offset, -1, ENC_NA);
dpps_tree = proto_item_add_subtree(ti, ett_2009_12_dpp_common);
/* Add the APPID. */
offset = read_c2(tvb, offset, &app, &app_len);
ti = proto_tree_add_uint(dpps_tree, hf_2008_1_app_version, tvb, 0, app_len, app);
validate_c2(pinfo, ti, app, app_len);
/* Retrieve the opcode. */
opcode = tvb_get_guint8(tvb, offset);
if (!packet_data->is_command)
opcode |= OP_2009_12_RESPONSE_FLAG;
col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(opcode, strings_2009_12_dpp_common_opcodes, "Unknown Opcode (%d)"));
/* Opcode */
proto_tree_add_uint_format(dpps_tree, hf_2009_12_dpp_2_14_opcode, tvb, offset, 1, opcode & 0x3F, "Opcode: %s (%u)", val_to_str(opcode, strings_2009_12_dpp_common_opcodes, "Unknown Opcode (%d)"), opcode & 0x3F);
offset += 1;
switch (opcode)
{
case OP_2009_12_SOURCE_LOST_CMD:
case OP_2009_12_SOURCE_FOUND_CMD:
case OP_2009_12_RENAME_CMD:
packet_data->has_referenced_opid = TRUE;
/* FALL THROUGH */
case OP_2009_12_CANCEL_ALL_CMD:
case OP_2009_12_NODE_DOWN_CMD:
case OP_2009_12_QUERY_RSP:
/* SID */
{
proto_tree *oid_tree;
gint opid_len;
tvbuff_t *next_tvb;
if (packet_data->has_referenced_opid)
{
opid_tree = proto_tree_add_subtree(dpps_tree, tvb, offset, 0, ett_2009_12_dpp_2_opid, NULL, "Operation Identifier");
}
else
{
opid_tree = dpps_tree;
}
oid_tree = proto_tree_add_subtree(opid_tree, tvb, offset, 0, ett_2009_12_dpp_2_opid, NULL, "Source Identifier");
next_tvb = tvb_new_subset(tvb, offset, -1, tvb_reported_length(tvb) - offset);
opid_len = call_dissector_only(dof_oid_handle, next_tvb, pinfo, oid_tree, NULL);
learn_sender_sid(api_data, opid_len, tvb_get_ptr(next_tvb, 0, opid_len));
if (packet_data->has_referenced_opid)
learn_operation_sid(&packet_data->ref_op, opid_len, tvb_get_ptr(next_tvb, 0, opid_len));
offset += opid_len;
}
if (packet_data->has_referenced_opid)
{
guint32 opcnt;
gint opcnt_len;
proto_item *pi;
read_c4(tvb, offset, &opcnt, &opcnt_len);
pi = proto_tree_add_uint_format(opid_tree, hf_2009_12_dpp_2_1_opcnt, tvb, offset, opcnt_len, opcnt, "Operation Count: %u", opcnt);
validate_c4(pinfo, pi, opcnt, opcnt_len);
offset += opcnt_len;
packet_data->ref_op.op_cnt = opcnt;
}
break;
}
return offset;
}
static int dissect_dpp_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
dof_api_data *api_data = (dof_api_data *)data;
dof_packet_data *packet_data;
proto_item *ti = NULL;
proto_item *tf = NULL;
proto_item *opid = NULL;
gint opid_start = -1;
guint8 dpp_flags_included = 0;
guint32 dpp_flags = 0;
guint8 dpp_opid_keytype = 0;
proto_tree *dpp_flags_tree;
proto_tree *opid_tree = NULL;
gint offset = 0;
proto_tree *dpp_tree = tree;
if (api_data == NULL)
{
/* TODO: Output error. */
return 0;
}
packet_data = api_data->packet;
if (packet_data == NULL)
{
/* TODO: Output error. */
return 0;
}
/* We should have everything required for determining the SID ID. */
assign_sid_id(api_data);
offset = 0;
col_clear(pinfo->cinfo, COL_INFO);
/* Compute the DPP control information. This is the version and the flags byte.
* The flags byte is either present, or is based on the version (and can be defaulted).
*/
{
guint8 header = tvb_get_guint8(tvb, offset);
dpp_flags_included = (header & 0x80) != 0;
offset += 1;
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "DPPv2 ");
ti = proto_tree_add_uint_format(tree, hf_2008_1_dpp_sid_num, tvb,
0, 0, packet_data->sender_sid_id, "SID ID: %d", packet_data->sender_sid_id);
PROTO_ITEM_SET_GENERATED(ti);
if (packet_data->sender_sid)
{
const gchar *SID = dof_oid_create_standard_string(packet_data->sender_sid[0], packet_data->sender_sid + 1);
ti = proto_tree_add_bytes_format_value(tree, hf_2008_1_dpp_sid_str, tvb, 0, 0, packet_data->sender_sid, "%s", SID);
PROTO_ITEM_SET_GENERATED(ti);
}
ti = proto_tree_add_uint_format(tree, hf_2008_1_dpp_rid_num, tvb,
0, 0, packet_data->receiver_sid_id, "RID ID: %d", packet_data->receiver_sid_id);
PROTO_ITEM_SET_GENERATED(ti);
if (packet_data->receiver_sid)
{
const gchar *SID = dof_oid_create_standard_string(packet_data->receiver_sid[0], packet_data->receiver_sid + 1);
ti = proto_tree_add_bytes_format_value(tree, hf_2008_1_dpp_rid_str, tvb, 0, 0, packet_data->receiver_sid, "%s", SID);
PROTO_ITEM_SET_GENERATED(ti);
}
if (dpp_flags_included)
{
dpp_flags = tvb_get_guint8(tvb, offset);
if (((dpp_flags & 0x10) != 0) && ((dpp_flags & 0x0F) != 0))
expert_add_info(pinfo, NULL, &ei_dpp2_dof_10_flags_zero);
if (((dpp_flags & 0x10) == 0) && ((dpp_flags & 0x09) != 0))
expert_add_info(pinfo, NULL, &ei_dpp2_dof_10_flags_zero);
tf = proto_tree_add_uint_format(dpp_tree, hf_2009_12_dpp_2_1_flags, tvb,
offset, 1, dpp_flags, "Flags: 0x%02x", dpp_flags);
dpp_flags_tree = proto_item_add_subtree(tf, ett_2009_12_dpp_2_1_flags);
if (dpp_flags == DPP_V2_DEFAULT_FLAGS)
expert_add_info(pinfo, dpp_flags_tree, &ei_dpp_default_flags);
proto_tree_add_item(dpp_flags_tree, hf_2009_12_dpp_2_1_flag_security, tvb, offset, 1, ENC_NA);
proto_tree_add_item(dpp_flags_tree, hf_2009_12_dpp_2_1_flag_opid, tvb, offset, 1, ENC_NA);
proto_tree_add_item(dpp_flags_tree, hf_2009_12_dpp_2_1_flag_cmdrsp, tvb, offset, 1, ENC_NA);
if ((dpp_flags & 0x10) == 0)
{
proto_tree_add_item(dpp_flags_tree, hf_2009_12_dpp_2_1_flag_seq, tvb, offset, 1, ENC_NA);
proto_tree_add_item(dpp_flags_tree, hf_2009_12_dpp_2_1_flag_retry, tvb, offset, 1, ENC_NA);
}
offset += 1;
}
else
dpp_flags = DPP_V2_DEFAULT_FLAGS;
packet_data->is_command = (dpp_flags & 0x10) == 0;
/* We are allowed to be complete here if still negotiating. */
/*if ( ! packet->negotiated && (offset == tvb_reported_length(tvb)) )
{
col_set_str( pinfo->cinfo, COL_INFO, "DPS Negotiation" );
return 1;
}*/
dpp_opid_keytype = (dpp_flags & 0x60) >> 5;
switch (dpp_opid_keytype)
{
case 0: /* No OPID */
packet_data->has_opid = FALSE;
break;
case 1: /* Implied sender. */
packet_data->has_opid = TRUE;
packet_data->op.op_sid_id = packet_data->sender_sid_id;
packet_data->op.op_sid = packet_data->sender_sid;
break;
case 2: /* Implied receiver. */
packet_data->has_opid = TRUE;
packet_data->op.op_sid_id = packet_data->receiver_sid_id;
packet_data->op.op_sid = packet_data->receiver_sid;
break;
case 3: /* Explicit. */
packet_data->has_opid = TRUE;
break;
}
if (dpp_opid_keytype != 0)
{
opid_start = offset;
opid_tree = proto_tree_add_subtree(dpp_tree, tvb, offset, 0, ett_2009_12_dpp_2_opid, NULL, "Operation Identifier");
}
switch (dpp_opid_keytype)
{
case 0: /* We have no opid. */
break;
case 3: /* Explicit. */
{
proto_tree *oid_tree;
tvbuff_t *next_tvb;
gint opid_len;
oid_tree = proto_tree_add_subtree(opid_tree, tvb, offset, 0, ett_2009_12_dpp_2_opid, NULL, "Source Identifier");
next_tvb = tvb_new_subset(tvb, offset, -1, tvb_reported_length(tvb) - offset);
opid_len = call_dissector_only(dof_oid_handle, next_tvb, pinfo, oid_tree, NULL);
proto_item_set_len(oid_tree, opid_len);
learn_operation_sid(&packet_data->op, opid_len, tvb_get_ptr(next_tvb, 0, opid_len));
/* Warn if Explicit SID could be optimized. */
if (packet_data->op.op_sid_id == packet_data->sender_sid_id)
expert_add_info(pinfo, ti, &ei_dpp_explicit_sender_sid_included);
if (packet_data->op.op_sid_id == packet_data->receiver_sid_id)
expert_add_info(pinfo, ti, &ei_dpp_explicit_receiver_sid_included);
offset += opid_len;
}
/* FALL THROUGH */
case 1: /* Implied sender. */
case 2: /* Implied receiver. */
{
guint32 opcnt;
gint opcnt_len;
proto_item *pi;
/* Display the SID if known. */
if ((dpp_opid_keytype != 3) && packet_data->op.op_sid)
{
proto_tree *oid_tree;
tvbuff_t *next_tvb = tvb_new_child_real_data(tvb, packet_data->op.op_sid + 1, packet_data->op.op_sid[0], packet_data->op.op_sid[0]);
oid_tree = proto_tree_add_subtree(opid_tree, tvb, 0, 0, ett_2009_12_dpp_2_opid, NULL, "Source Identifier");
call_dissector_only(dof_oid_handle, next_tvb, pinfo, oid_tree, NULL);
PROTO_ITEM_SET_GENERATED(ti);
}
read_c4(tvb, offset, &opcnt, &opcnt_len);
pi = proto_tree_add_uint_format(opid_tree, hf_2009_12_dpp_2_1_opcnt, tvb, offset, opcnt_len, opcnt, "Operation Count: %u", opcnt);
validate_c4(pinfo, pi, opcnt, opcnt_len);
offset += opcnt_len;
proto_item_set_len(opid, offset - opid_start);
packet_data->op.op_cnt = opcnt;
/* At this point we have a packet with an operation identifier. We need to
* update the master list of operation identifiers, and do any checking that
* we can in order to validate things.
*/
if (packet_data->has_opid && !packet_data->opid_first)
{
dof_packet_data *first = (dof_packet_data *)g_hash_table_lookup(dpp_opid_to_packet_data, (gconstpointer) & packet_data->op);
if (first == NULL)
{
/* First reference to this operation. */
g_hash_table_insert(dpp_opid_to_packet_data, (gpointer) & packet_data->op, (gpointer)packet_data);
packet_data->opid_first = packet_data;
packet_data->opid_last = packet_data;
/* The first opid must be a command. */
}
else
{
/* Operation exists, time to patch things in. */
packet_data->opid_first = first;
first->opid_last->opid_next = packet_data;
first->opid_last = packet_data;
if (!packet_data->is_command)
{
if (!first->opid_first_response)
{
first->opid_first_response = packet_data;
first->opid_last_response = packet_data;
}
else
{
first->opid_last_response->opid_next_response = packet_data;
first->opid_last_response = packet_data;
}
}
}
}
/* Add all the reference information to the tree. */
if (globals.track_operations && tree)
{
proto_tree *ophistory_tree = proto_tree_add_subtree(tree, tvb, 0, 0, ett_2009_12_dpp_2_opid_history, NULL, "Operation History");
dof_packet_data *ptr = packet_data->opid_first;
if (ptr)
proto_tree_add_uint_format(ophistory_tree, hf_2008_1_dpp_first_command,
tvb, 0, 0, ptr->frame,
"First Operation: %u",
ptr->frame);
if (ptr->opid_last && ptr->opid_last != ptr)
proto_tree_add_uint_format(ophistory_tree, hf_2008_1_dpp_last_command,
tvb, 0, 0, ptr->opid_last->frame,
"Last Operation: %u",
ptr->opid_last->frame);
if (ptr->opid_first_response)
proto_tree_add_uint_format(ophistory_tree, hf_2008_1_dpp_first_response,
tvb, 0, 0, ptr->opid_first_response->frame,
"First Response: %u",
ptr->opid_first_response->frame);
if (ptr->opid_last_response && ptr->opid_last_response != ptr->opid_first_response)
proto_tree_add_uint_format(ophistory_tree, hf_2008_1_dpp_last_response,
tvb, 0, 0, ptr->opid_last_response->frame,
"Last Response: %u",
ptr->opid_last_response->frame);
/* Determine the window start, then output the number of packets. Output the number of skipped packets before
* and after.
*/
{
dof_packet_data *start = packet_data->opid_first;
guint diff = 0;
while (ptr)
{
if (ptr == packet_data)
break;
ptr = ptr->opid_next;
diff += 1;
if (diff > globals.track_operations_window)
{
start = start->opid_next;
diff -= 1;
}
}
ptr = start;
diff = 0;
while (ptr)
{
const char *THIS = "";
if (ptr == packet_data)
{
THIS = "this ";
diff = globals.track_operations_window + 1;
}
/* (DPS Frame) [ws WS Frame]: (SID)->(RID): (THIS) (SUMMARY) */
proto_tree_add_uint_format(ophistory_tree, hf_2008_1_dpp_related_frame,
tvb, 0, 0, ptr->frame,
"%u[ws %u]: %u->%u: %s%s",
ptr->dof_frame, ptr->frame,
ptr->sender_sid_id, ptr->receiver_sid_id,
THIS,
ptr->summary ? ptr->summary : "");
ptr = ptr->opid_next;
if (diff && !--diff)
break;
}
}
}
}
break;
}
proto_item_set_len(opid_tree, offset - opid_start);
{
if ((dpp_flags & 0x10) == 0)
{
guint8 dpp_seq = 0;
guint8 dpp_retry = 0;
guint16 dpp_delay = 0;
/* Extract SEQ */
if (dpp_flags & 0x04)
{
dpp_seq = tvb_get_guint8(tvb, offset);
proto_tree_add_uint_format(dpp_tree, hf_2009_12_dpp_2_1_seq, tvb, offset, 1, dpp_seq, "Sequence: %u", dpp_seq);
offset += 1;
}
/* Extract Retry */
if (dpp_flags & 0x02)
{
dpp_retry = tvb_get_guint8(tvb, offset);
proto_tree_add_uint_format(dpp_tree, hf_2009_12_dpp_2_1_retry, tvb, offset, 1, dpp_retry, "Retry: %u", dpp_retry);
offset += 1;
}
/* Extract Delay */
{
dpp_delay = tvb_get_guint8(tvb, offset);
if (dpp_delay > 128)
dpp_delay = 128 + ((dpp_delay - 128) * 32);
proto_tree_add_uint_format(dpp_tree, hf_2009_12_dpp_2_1_delay, tvb, offset, 1, dpp_delay, "Delay: %u seconds", dpp_delay);
offset += 1;
}
packet_data->summary = wmem_strdup_printf(wmem_file_scope(), "command seq %u, retry %u, delay %u", dpp_seq, dpp_retry, dpp_delay);
}
else
packet_data->summary = "response";
}
/* Extract session information. */
if (dpp_flags & 0x80)
{
guint32 sec_offset = offset;
guint8 sh_flags;
guint32 ssid;
proto_tree *security_tree;
proto_tree *sec_flags_tree;
proto_item *item;
security_tree = proto_tree_add_subtree(dpp_tree, tvb, offset, -1, ett_2009_12_dpp_2_3_security, NULL, "Security Header");
sh_flags = tvb_get_guint8(tvb, offset);
item = proto_tree_add_uint_format(security_tree, hf_2009_12_dpp_2_3_sec_flags, tvb,
offset, 1, sh_flags, "Flags: 0x%02x", sh_flags);
sec_flags_tree = proto_item_add_subtree(item, ett_2009_12_dpp_2_3_sec_flags);
proto_tree_add_item(sec_flags_tree, hf_2009_12_dpp_2_3_sec_flag_secure, tvb, offset, 1, ENC_NA);
proto_tree_add_item(sec_flags_tree, hf_2009_12_dpp_2_3_sec_flag_rdid, tvb, offset, 1, ENC_NA);
proto_tree_add_item(sec_flags_tree, hf_2009_12_dpp_2_3_sec_flag_partition, tvb, offset, 1, ENC_NA);
proto_tree_add_item(sec_flags_tree, hf_2009_12_dpp_2_3_sec_flag_as, tvb, offset, 1, ENC_NA);
proto_tree_add_item(sec_flags_tree, hf_2009_12_dpp_2_3_sec_flag_ssid, tvb, offset, 1, ENC_NA);
offset += 1;
ssid = 0;
if (sh_flags & DPP_V2_SEC_FLAG_S)
{
gint s_offset = offset;
gint ssid_len;
proto_item *pi;
offset = read_c4(tvb, offset, &ssid, &ssid_len);
pi = proto_tree_add_uint_format(security_tree, hf_2009_12_dpp_2_3_sec_ssid, tvb, s_offset, offset - s_offset, ssid, "Security State Identifier: %u (0x%x)", ssid, ssid);
validate_c4(pinfo, pi, ssid, ssid_len);
}
/* At this point we know the transport information, DNP port information, and the
* SSID. This means that we can isolate the session that this communication belongs
* to. Note that all uses of an SSID are scoped by the transport.
*/
if (sh_flags & DPP_V2_SEC_FLAG_A)
ssid |= AS_ASSIGNED_SSID;
if (api_data->session && !api_data->secure_session)
{
dof_secure_session_data *search = api_data->session->secure_sessions;
while (search)
{
if (ssid == search->ssid)
break;
search = search->next;
}
if (search)
{
api_data->session = search->parent;
api_data->secure_session = search;
}
}
if (sh_flags & DPP_V2_SEC_FLAG_D)
{
gint s_offset = offset;
guint32 rdid;
gint rdid_len;
proto_item *pi;
offset = read_c4(tvb, offset, &rdid, &rdid_len);
pi = proto_tree_add_uint_format(security_tree, hf_2009_12_dpp_2_3_sec_rdid, tvb, s_offset, offset - s_offset, rdid, "Remote Domain Identifier: %u (0x%x)", rdid, rdid);
validate_c4(pinfo, pi, rdid, rdid_len);
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_10, tvb, pinfo, security_tree,
offset, hf_2009_12_dpp_2_3_sec_remote_partition, ett_2009_12_dpp_2_3_sec_remote_partition, NULL);
}
if (sh_flags & DPP_V2_SEC_FLAG_P)
{
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_10, tvb, pinfo, security_tree,
offset, hf_2009_12_dpp_2_3_sec_partition, ett_2009_12_dpp_2_3_sec_partition, NULL);
}
if (sh_flags & DPP_V2_SEC_FLAG_E)
{
/* If we get here without success, then we can only bail. */
if (packet_data->security_session_error)
{
col_set_str(pinfo->cinfo, COL_INFO, packet_data->security_session_error);
proto_item_set_end(tree, tvb, offset);
expert_add_info(pinfo, security_tree, &ei_dpp_no_security_context);
{
tvbuff_t *data_tvb = tvb_new_subset_remaining(tvb, offset);
call_dissector(undissected_data_handle, data_tvb, pinfo, tree);
}
proto_item_set_len(security_tree, offset - sec_offset);
return offset;
}
if (!api_data->secure_session)
{
packet_data->security_session_error = "[Encrypted - No Session Available]";
proto_item_set_len(security_tree, offset - sec_offset);
return offset;
}
/* Security has not failed, and we have a security session. */
{
dissector_table_t sec_header = find_dissector_table("dof.secmode");
/* TODO: CCM is hardcoded. We should try all of the sessions, which could mean multiple security modes. */
dissector_handle_t dp = dissector_get_uint_handle(sec_header, 0x6001); /* packet_data->security_session->security_mode); */
if (dp)
{
dof_secmode_api_data sdata;
sdata.context = HEADER;
sdata.security_mode_offset = offset;
sdata.dof_api = api_data;
sdata.secure_session = api_data->secure_session;
sdata.session_key_data = NULL;
offset += call_dissector_only(dp, tvb, pinfo, security_tree, &sdata);
if (!packet_data->decrypted_buffer)
{
proto_item_set_end(tree, tvb, offset);
proto_item_set_len(security_tree, offset - sec_offset);
return offset;
}
}
}
}
proto_item_set_len(security_tree, offset - sec_offset);
}
/* The end of the packet must be called in the original tvb or chaos ensues... */
proto_item_set_end(tree, tvb, offset);
}
if (packet_data->decrypted_tvb)
{
tvb = packet_data->decrypted_tvb;
offset = packet_data->decrypted_offset;
}
/* Assuming there is more, it must be DPP. */
/* We have a packet. We must handle the special case of this being *our* application
* protocol (0x7FFF). If it is, then *we* are the dissector...
*/
{
guint16 app;
tvbuff_t *next_tvb = tvb_new_subset(tvb, offset, -1, tvb_reported_length(tvb) - offset);
read_c2(tvb, offset, &app, NULL);
if (app == 0x7FFF)
{
offset += dissect_dpp_v2_common(next_tvb, pinfo, proto_item_get_parent(tree), data);
}
else
{
offset += dissect_app_common(next_tvb, pinfo, proto_item_get_parent(tree), data);
}
}
}
col_set_fence(pinfo->cinfo, COL_PROTOCOL);
col_set_fence(pinfo->cinfo, COL_INFO);
return offset;
}
static int dissect_options(tvbuff_t *tvb, gint offset, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
while (offset < (gint)tvb_captured_length(tvb))
{
proto_tree *subtree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_2008_1_dsp_12_option, NULL, "Option");
tvbuff_t *next_tvb = tvb_new_subset(tvb, offset, -1, -1);
gint len = dissect_2008_1_dsp_1(next_tvb, pinfo, subtree);
proto_item_set_len(proto_tree_get_parent(subtree), len);
offset += len;
}
return offset;
}
static int dissect_dsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
dof_api_data *api_data = (dof_api_data *)data;
dof_packet_data *packet_data;
guint offset = 0;
guint8 opcode;
guint16 app;
gint app_len;
proto_item *ti;
proto_tree *dsp_tree;
proto_tree *options_tree;
if (api_data == NULL)
{
/* TODO: Output error. */
return 0;
}
packet_data = api_data->packet;
if (packet_data == NULL)
{
/* TODO: Output error. */
return 0;
}
/* Make entries in Protocol column and Info column on summary display */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "DSPv2 ");
/* Create the protocol tree. */
offset = 0;
ti = proto_tree_add_item(tree, proto_2008_1_dsp, tvb, offset, -1, ENC_NA);
dsp_tree = proto_item_add_subtree(ti, ett_2008_1_dsp_12);
/* Add the APPID. */
offset = read_c2(tvb, offset, &app, &app_len);
ti = proto_tree_add_uint(dsp_tree, hf_2008_1_app_version, tvb, 0, app_len, app);
validate_c2(pinfo, ti, app, app_len);
#if 0
if (!packet->is_streaming)
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "DSPv2 ");
if (tvb_captured_length(tvb) == offset)
col_set_str(pinfo->cinfo, COL_INFO, "Query");
else
{
col_set_str(pinfo->cinfo, COL_INFO, "Query Response");
while (offset < tvb_captured_length(tvb))
{
guint16 app;
gint start = offset;
offset = read_c2(tvb, offset, &app, NULL);
proto_tree_add_uint(dsp_tree, hf_2008_1_app_version, tvb, start, offset - start, app);
}
}
return offset;
}
#endif
if (offset == tvb_captured_length(tvb))
{
col_append_str(pinfo->cinfo, COL_INFO, "DSP [nop]");
expert_add_info(pinfo, dsp_tree, &ei_implicit_no_op);
return offset;
}
/* Determine the ESP opcode. */
opcode = tvb_get_guint8(tvb, offset);
if (!packet_data->is_command)
opcode |= OP_2008_1_RSP;
proto_tree_add_uint_format(dsp_tree, hf_2008_1_dsp_12_opcode, tvb, offset, 1, opcode, "Opcode: %s (%u)", val_to_str(opcode, strings_2008_1_dsp_opcodes, "Unknown Opcode (%d)"), opcode & 0x7F);
offset += 1;
col_append_sep_fstr(pinfo->cinfo, COL_INFO, "/", "%s", val_to_str(opcode, strings_2008_1_dsp_opcodes, "Unknown Opcode (%d)"));
switch (opcode)
{
case OP_2008_1_OPEN_CMD: /* 2008.1 DSP.14.1 */
break;
case OP_2008_1_OPEN_RSP: /* 2008.1 DSP.14.2 */
case OP_2008_1_OPEN_SECURE_RSP: /* 2008.1 DSP.14.3 */
{
while (offset < tvb_captured_length(tvb))
{
guint16 ap;
gint length;
proto_item *pi;
gint start = offset;
offset = read_c2(tvb, offset, &ap, &length);
pi = proto_tree_add_uint(dsp_tree, hf_2008_1_app_version, tvb, start, offset - start, ap);
validate_c2(pinfo, pi, ap, length);
}
}
break;
case OP_2008_1_QUERY_CMD:
break;
case OP_2008_1_QUERY_RSP:
break;
case OP_2008_1_CONFIG_ACK:
break;
case OP_2008_1_CONFIG_REQ:
/* This will start a session if not existing... */
/* FALL THROUGH */
case OP_2008_1_CONFIG_NAK:
{
gint length = tvb_captured_length(tvb) - offset;
options_tree = proto_tree_add_subtree_format(dsp_tree, tvb, offset, length, ett_2008_1_dsp_12_options, NULL,
"DSP Options: (%d byte%s)", length, plurality(length, "", "s"));
offset = dissect_options(tvb, offset, pinfo, options_tree, NULL);
}
break;
case OP_2008_1_CONFIG_REJ:
/* TODO: Handle reject. */
break;
case OP_2008_1_TERMINATE_CMD:
case OP_2008_1_TERMINATE_RSP:
/* Nothing */
break;
}
return offset;
}
static int dissect_ccm_dsp(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
{
/* We are handed a buffer that starts with an option and our protocol id. Any options follow that. */
gint offset = 0;
proto_item *parent = proto_tree_get_parent(tree);
guint8 len, strength_count, i;
proto_item *ti;
proto_tree *ccm_tree;
/* Append description to the parent. */
proto_item_append_text(parent, " (CCM)");
/* Compute the version and flags, masking off other bits. */
offset += 3; /* Skip the type and protocol. */
len = tvb_get_guint8(tvb, offset++);
ti = proto_tree_add_item(tree, hf_ccm_dsp_option, tvb, offset, len, ENC_NA);
ccm_tree = proto_item_add_subtree(ti, ett_ccm_dsp_option);
strength_count = tvb_get_guint8(tvb, offset);
proto_tree_add_item(ccm_tree, hf_ccm_dsp_strength_count, tvb, offset++, 1, ENC_NA);
for (i = 0; i < strength_count; i++)
proto_tree_add_item(ccm_tree, hf_ccm_dsp_strength, tvb, offset++, 1, ENC_NA);
proto_tree_add_item(ccm_tree, hf_ccm_dsp_e_flag, tvb, offset, 1, ENC_NA);
proto_tree_add_item(ccm_tree, hf_ccm_dsp_m_flag, tvb, offset, 1, ENC_NA);
proto_tree_add_item(ccm_tree, hf_ccm_dsp_tmax, tvb, offset, 1, ENC_NA);
proto_tree_add_item(ccm_tree, hf_ccm_dsp_tmin, tvb, offset, 1, ENC_NA);
offset += 1;
return offset;
}
/**
* This is the main entry point for the CCM dissector. It is always called from an DPS
* dissector, and is always passed the dof_secmode_data structure.
*/
static int dissect_ccm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
dof_secmode_api_data *secmode_api_data;
dof_session_key_exchange_data *key_data;
secmode_api_data = (dof_secmode_api_data *)data;
if (secmode_api_data == NULL)
{
fprintf(stderr, "secmode_api_data == NULL");
return 0;
}
key_data = secmode_api_data->session_key_data;
/* Based on the context of the request, handle the work. */
switch (secmode_api_data->context)
{
case INITIALIZE:
/* Parse off the initialization fields, and if necessary create the security mode state
* that is being initialized. This is passed the DPS data, DPS session data, and Key Exchange Data.
*/
{
ccm_session_data *ccm_data = (ccm_session_data *)key_data->security_mode_key_data;
gint offset = 0;
guint8 header;
guint16 length;
if (!ccm_data)
{
/* We need to parse the initialization data. */
ccm_data = (ccm_session_data *)wmem_alloc0(wmem_file_scope(), sizeof(ccm_session_data));
if (!ccm_data)
return 0;
key_data->security_mode_key_data = ccm_data;
if (!key_data->security_mode_data || key_data->security_mode_data_length < 3)
return 0;
/* TODO: Not sure that these are all right. */
ccm_data->protocol_id = DOF_PROTOCOL_CCM;
ccm_data->cipher = key_data->security_mode_data[1];
ccm_data->encrypted = key_data->security_mode_data[key_data->security_mode_data_length - 1] & 0x80;
ccm_data->mac_len = (key_data->security_mode_data[key_data->security_mode_data_length - 1] & 0x07) * 2 + 2;
ccm_data->client_datagram_number = 0;
ccm_data->server_datagram_number = 0;
switch (ccm_data->protocol_id)
{
case DOF_PROTOCOL_CCM:
ccm_data->cipher_data = wmem_alloc0(wmem_file_scope(), sizeof(rijndael_ctx));
break;
default:
return 0;
}
}
if (secmode_api_data->dof_api->transport_session->is_2_node)
{
switch (ccm_data->protocol_id)
{
case DOF_PROTOCOL_CCM:
rijndael_set_key((rijndael_ctx *)ccm_data->cipher_data, key_data->session_key, 256);
break;
default:
return 0;
}
/* This mode has a fixed size, so we can return here without parsing further. */
return 2;
}
offset = read_c2(tvb, offset, &length, NULL);
/* TODO validate C2 */
header = tvb_get_guint8(tvb, offset);
offset += 1;
/* Determine the period, and store the key. */
{
guint8 period = (header & 0x70) >> 4;
if (ccm_data->cipher_data_table == NULL)
{
guint8 *ekey = (guint8 *)wmem_alloc0(wmem_file_scope(), sizeof(rijndael_ctx));
/* TODO: This needs to be freed. */
ccm_data->cipher_data_table = g_hash_table_new(g_direct_hash, g_direct_equal);
ccm_data->period = 1;
ccm_data->periods[period] = ccm_data->period;
switch (ccm_data->protocol_id)
{
case DOF_PROTOCOL_CCM:
rijndael_set_key((rijndael_ctx *)ekey, key_data->session_key, 256);
break;
default:
return 0;
}
g_hash_table_insert(ccm_data->cipher_data_table, GUINT_TO_POINTER(ccm_data->period), ekey);
}
else
{
guint32 lookup = ccm_data->periods[period];
if (!lookup)
{
guint8 *ekey = (guint8 *)wmem_alloc0(wmem_file_scope(), sizeof(rijndael_ctx));
switch (ccm_data->protocol_id)
{
case DOF_PROTOCOL_CCM:
rijndael_set_key((rijndael_ctx *)ekey, key_data->session_key, 256);
break;
default:
return 0;
}
ccm_data->period += 1;
ccm_data->periods[period] = ccm_data->period;
g_hash_table_insert(ccm_data->cipher_data_table, GUINT_TO_POINTER(ccm_data->period), ekey);
}
else
{
guint8 *in_table = (guint8 *)g_hash_table_lookup(ccm_data->cipher_data_table, GUINT_TO_POINTER(lookup));
if (memcmp(key_data->session_key, in_table, 32) != 0)
{
guint8 *ekey = (guint8 *)wmem_alloc0(wmem_file_scope(), sizeof(rijndael_ctx));
switch (ccm_data->protocol_id)
{
case DOF_PROTOCOL_CCM:
rijndael_set_key((rijndael_ctx *)ekey, key_data->session_key, 256);
break;
default:
return 0;
}
ccm_data->period += 1;
ccm_data->periods[period] = ccm_data->period;
g_hash_table_insert(ccm_data->cipher_data_table, GUINT_TO_POINTER(ccm_data->period), ekey);
}
}
}
}
return offset + length - 1;
}
case HEADER:
{
ccm_session_data *session;
dof_transport_session *transport_session = (dof_transport_session *)secmode_api_data->dof_api->transport_session;
dof_secure_session_data *secure_session = secmode_api_data->secure_session;
dof_session_key_exchange_data *security_data = NULL;
dof_packet_data *dof_packet = secmode_api_data->dof_api->packet;
guint8 ccm_flags;
guint32 nid;
guint16 slot = 0;
guint32 pn = 0;
gboolean pn_present = FALSE;
guint32 tnid;
guint32 nnid;
proto_tree *ccm_flags_tree;
proto_tree *header_tree;
proto_item * item,*header;
ccm_packet_data *pdata;
gint offset = 0;
if (!dof_packet->security_session)
{
if (transport_session->is_streaming)
{
/* Find the first security data that is applicable - they are in order of packet sequence. */
security_data = secure_session->session_security_data;
while (security_data)
{
if (dof_packet->is_sent_by_initiator && (dof_packet->dof_frame > security_data->i_valid))
break;
if (!dof_packet->is_sent_by_initiator && (dof_packet->dof_frame > security_data->r_valid))
break;
security_data = security_data->next;
}
if (security_data)
dof_packet->security_session = security_data;
else
{
dof_packet->security_session_error = "[Encrypted - No Session Available]";
return offset;
}
}
else
{
dof_packet->security_session = secure_session->session_security_data;
security_data = dof_packet->security_session;
}
}
else
{
security_data = dof_packet->security_session;
}
if (!security_data || !security_data->session_key || !security_data->security_mode_key_data)
{
dof_packet->security_session_error = "[Encrypted - No Session Available]";
return offset;
}
session = (ccm_session_data *)security_data->security_mode_key_data;
offset = secmode_api_data->security_mode_offset;
/* Add a master header for this protocol. */
header = proto_tree_add_protocol_format(tree, proto_ccm, tvb, offset, 0,
"CCM Security Mode, Version: 1");
header_tree = proto_item_add_subtree(header, ett_header);
tree = header_tree;
ccm_flags = tvb_get_guint8(tvb, offset);
item = proto_tree_add_uint_format(tree, hf_epp_v1_ccm_flags, tvb,
offset, 1, ccm_flags, "Flags: 0x%02x", ccm_flags);
ccm_flags_tree = proto_item_add_subtree(item, ett_epp_v1_ccm_flags);
proto_tree_add_item(ccm_flags_tree, hf_epp_v1_ccm_flags_manager, tvb, offset, 1, ENC_NA);
proto_tree_add_item(ccm_flags_tree, hf_epp_v1_ccm_flags_period, tvb, offset, 1, ENC_NA);
proto_tree_add_item(ccm_flags_tree, hf_epp_v1_ccm_flags_target, tvb, offset, 1, ENC_NA);
proto_tree_add_item(ccm_flags_tree, hf_epp_v1_ccm_flags_next_nid, tvb, offset, 1, ENC_NA);
proto_tree_add_item(ccm_flags_tree, hf_epp_v1_ccm_flags_packet, tvb, offset, 1, ENC_NA);
offset += 1;
if (ccm_flags & 0x01)
pn_present = TRUE;
pdata = (ccm_packet_data *)dof_packet->security_packet;
if (!pdata)
{
pdata = (ccm_packet_data *)wmem_alloc0(wmem_file_scope(), sizeof(ccm_packet_data));
if (pdata)
{
dof_packet->security_packet = pdata;
if (transport_session->is_2_node)
{
if (dof_packet->is_sent_by_initiator)
{
pdata->nid = 0;
if (pn_present == FALSE)
pdata->dn = ++session->client_datagram_number;
else
pdata->dn = pn;
}
else
{
pdata->nid = 1;
if (pn_present == 0)
pdata->dn = ++session->server_datagram_number;
else
pdata->dn = pn;
}
}
else
{
guint8 packet_period = (ccm_flags & 0x70) >> 4;
pdata->period = session->periods[packet_period];
}
}
}
if (!pdata)
return offset - secmode_api_data->security_mode_offset;
if (!secure_session->is_2_node)
{
gint nid_len;
proto_item *pi;
read_c4(tvb, offset, &nid, &nid_len);
/* TODO: Do this right, as offset from BNID. */
nid /= 2;
pdata->nid = nid;
pi = proto_tree_add_uint_format(tree, hf_epp_v1_ccm_nid, tvb, offset, nid_len, nid, "Node ID: %u", nid);
validate_c4(pinfo, pi, nid, nid_len);
offset += nid_len;
}
else
{
item = proto_tree_add_uint_format(tree, hf_epp_v1_ccm_nid, tvb, 0, 0, pdata->nid, "Node ID: %u", pdata->nid);
PROTO_ITEM_SET_GENERATED(item);
}
if (!secure_session->is_2_node)
{
gint slot_len;
proto_item *pi;
read_c2(tvb, offset, &slot, &slot_len);
pi = proto_tree_add_uint_format(tree, hf_epp_v1_ccm_slot, tvb, offset, slot_len, slot, "Slot: %hu", slot);
validate_c2(pinfo, pi, slot, slot_len);
offset += slot_len;
}
else
{
item = proto_tree_add_uint_format(tree, hf_epp_v1_ccm_slot, tvb, 0, 0, 0, "Slot: %u", 0);
PROTO_ITEM_SET_GENERATED(item);
}
if (ccm_flags & 0x01)
{
gint pn_len;
proto_item *pi;
read_c4(tvb, offset, &pn, &pn_len);
pi = proto_tree_add_uint_format(tree, hf_epp_v1_ccm_pn, tvb, offset, pn_len, pn, "Packet Number: %u", pn);
validate_c4(pinfo, pi, pn, pn_len);
pdata->dn = pn;
offset += pn_len;
}
else
{
item = proto_tree_add_uint_format(tree, hf_epp_v1_ccm_pn, tvb, 0, 0, pdata->dn, "Packet Number: %u", pdata->dn);
PROTO_ITEM_SET_GENERATED(item);
}
if (ccm_flags & 0x08)
{
gint tnid_len;
proto_item *pi;
read_c4(tvb, offset, &tnid, &tnid_len);
pi = proto_tree_add_uint_format(tree, hf_epp_v1_ccm_tnid, tvb, offset, tnid_len, tnid, "Target Node ID: %u", tnid);
validate_c4(pinfo, pi, tnid, tnid_len);
offset += tnid_len;
}
if (ccm_flags & 0x02)
{
gint nnid_len;
proto_item *pi;
read_c4(tvb, offset, &nnid, &nnid_len);
pi = proto_tree_add_uint_format(tree, hf_epp_v1_ccm_nnid, tvb, offset, nnid_len, nnid, "Next Node ID: %u", nnid);
validate_c4(pinfo, pi, nnid, nnid_len);
offset += nnid_len;
}
proto_item_set_len(header, offset - secmode_api_data->security_mode_offset);
if (dof_packet->decrypted_buffer_error)
{
col_set_str(pinfo->cinfo, COL_INFO, dof_packet->decrypted_buffer_error);
expert_add_info(pinfo, tree, &ei_decode_failure);
return offset - secmode_api_data->security_mode_offset;
}
/* We have reached the encryption boundary. At this point the rest of the packet
* is encrypted, and we may or may not be able to decrypt it.
*
* If we can decrypt it (which for now means that it uses a Session Key of [0]
* the we switch to decoding the decrypted PDU. Otherwise we create an entry
* for the encrypted bytes and move on...
*/
{
gint e_len = tvb_captured_length(tvb) - offset;
const guint8 *epp_buf = tvb_get_ptr(tvb, 0, -1);
guint a_len = offset;
guint16 e_off;
guint8 *buf = (guint8 *)g_malloc(e_len);
tvbuff_t *app;
/* The default nonce is a function of whether or not this is the server
* or the client and the packet count. The packet count either comes from
* the PDU or is a function of the previous value (of the sending node).
*/
guint8 nonce[] = { 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
0x00,
0x00, 0x00, 0x00, 0x00 };
nonce[0] = (pdata->nid) >> 24;
nonce[1] = (pdata->nid) >> 16;
nonce[2] = (pdata->nid) >> 8;
nonce[3] = (guint8)(pdata->nid);
nonce[4] = slot >> 8;
nonce[5] = (guint8)slot;
nonce[7] = (pdata->dn) >> 24;
nonce[8] = (pdata->dn) >> 16;
nonce[9] = (pdata->dn) >> 8;
nonce[10] = (guint8)(pdata->dn);
/* Now the hard part. We need to determine the current packet number.
* This is a function of the sending node, the previous state and the
* current PDU.
*/
for (e_off = 0; e_off < e_len; e_off++)
buf[e_off] = tvb_get_guint8(tvb, offset + e_off);
app = NULL;
proto_item_set_end(tree, tvb, offset);
if (!session->encrypted)
{
/* There is still a MAC involved, and even though we don't need a new
* buffer we need to adjust the length of the existing buffer.
*/
g_free(buf);
app = tvb_new_subset(tvb, offset, e_len - session->mac_len, e_len - session->mac_len);
dof_packet->decrypted_tvb = app;
dof_packet->decrypted_offset = 0;
}
else
{
if (dof_packet->decrypted_buffer)
{
/* No need to decrypt, but still need to create buffer. */
app = tvb_new_real_data((const guint8 *)dof_packet->decrypted_buffer, e_len - session->mac_len, e_len - session->mac_len);
tvb_set_child_real_data_tvbuff(tvb, app);
add_new_data_source(pinfo, app, "Decrypted DOF");
dof_packet->decrypted_tvb = app;
dof_packet->decrypted_offset = 0;
}
else
{
if (decrypt(session, pdata, nonce, epp_buf, a_len, buf, e_len))
{
guint8 *cache = (guint8 *)wmem_alloc0(wmem_file_scope(), e_len - session->mac_len);
memcpy(cache, buf, e_len - session->mac_len);
app = tvb_new_real_data(cache, e_len - session->mac_len, e_len - session->mac_len);
tvb_set_child_real_data_tvbuff(tvb, app);
add_new_data_source(pinfo, app, "Decrypted DOF");
dof_packet->decrypted_buffer = cache;
dof_packet->decrypted_offset = 0;
dof_packet->decrypted_tvb = app;
g_free(buf);
}
else
{
/* Failure to decrypt or validate the MAC.
* The packet is secure, so there is nothing we can do!
*/
dof_packet->decrypted_buffer_error = "[Encrypted packet - decryption failure]";
g_free(buf);
}
}
}
}
return offset - secmode_api_data->security_mode_offset;
}
break;
case TRAILER:
/* TODO check this case */
break;
}
return 0;
}
static int dissect_ccm_app(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gint offset = 0;
guint8 opcode = 0;
guint16 app;
gint app_len;
proto_item *ti;
proto_tree *ccm_tree;
/* Make entries in Protocol column and Info column on summary display */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "CCM ");
/* Create the protocol tree. */
offset = 0;
ti = proto_tree_add_item(tree, proto_ccm_app, tvb, offset, -1, ENC_NA);
ccm_tree = proto_item_add_subtree(ti, ett_ccm);
/* Add the APPID. */
offset = read_c2(tvb, offset, &app, &app_len);
ti = proto_tree_add_uint(ccm_tree, hf_2008_1_app_version, tvb, 0, app_len, app);
validate_c2(pinfo, ti, app, app_len);
/* Retrieve the opcode. */
opcode = tvb_get_guint8(tvb, offset);
col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(opcode, ccm_opcode_strings, "Unknown Opcode (%d)"));
if (tree)
{
/* Opcode */
proto_tree_add_item(ccm_tree, hf_ccm_opcode, tvb, offset, 1, ENC_NA);
#if 0 /* this needs completion */
offset += 1;
switch (opcode)
{
case CCM_PDU_PROBE:
{
}
break;
}
#endif
}
return 1;
}
#if 0 /* TODO not used yet */
static int dissect_ccm_validate(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data)
{
dof_api_data *api_data = (dof_api_data *)data;
dof_packet_data *packet;
ccm_session_data *session;
gint offset;
guint8 ccm_flags;
guint32 nid;
guint16 slot;
guint32 pn;
guint32 tnid;
if (api_data == NULL)
{
fprintf(stderr, "api_data is NULL.");
return 0;
}
packet = api_data->packet;
if (packet == NULL)
{
fprintf(stderr, "api_data->packet is NULL.");
return 0;
}
if (!packet->security_session)
{
fprintf(stderr, "packet->security_session is NULL");
return 0;
}
if (packet->security_session->security_mode != DOF_PROTOCOL_CCM)
{
fprintf(stderr, "packet->security_session->security_mode != DOF_PROTOCOL_CCM");
return 0;
}
session = (ccm_session_data *)packet->security_session->security_mode_key_data;
/* The buffer we have been passed includes the entire EPP frame. The packet
* structure gives us the offset to our header.
*/
offset = 0;
ccm_flags = tvb_get_guint8(tvb, offset);
offset += 1;
/* TODO validate the C2 and C4 fields below? */
if (ccm_flags & 0x04)
offset = read_c4(tvb, offset, &nid, NULL);
if (ccm_flags & 0x02)
offset = read_c2(tvb, offset, &slot, NULL);
if (ccm_flags & 0x01)
offset = read_c4(tvb, offset, &pn, NULL);
if (ccm_flags & 0x08)
offset = read_c4(tvb, offset, &tnid, NULL);
/* We have reached the encryption boundary. At this point the rest of the packet
* is encrypted, and we may or may not be able to decrypt it.
*
* If we can decrypt it (which for now means that it uses a Session Key of [0]
* the we switch to decoding the decrypted PDU. Otherwise we create an entry
* for the encrypted bytes and move on...
*/
{
gint e_len = tvb_captured_length(tvb) - offset;
const guint8 *epp_buf = tvb_get_ptr(tvb, 0, -1);
guint a_len = offset - 0;
guint16 e_off;
guint8 *buf = (guint8 *)g_malloc(e_len);
/* The default nonce is a function of whether or not this is the server
* or the client and the packet count. The packet count either comes from
* the PDU or is a function of the previous value (of the sending node).
*/
guint8 nonce[] = { 0x00, 0x00, 0x00, 0x01,
0x00, 0x00,
0x00,
0x00, 0x00, 0x00, 0x00 };
nonce[0] = nid >> 24;
nonce[1] = nid >> 16;
nonce[2] = nid >> 8;
nonce[3] = (guint8)nid;
nonce[4] = slot >> 8;
nonce[5] = (guint8)slot;
nonce[7] = pn >> 24;
nonce[8] = pn >> 16;
nonce[9] = pn >> 8;
nonce[10] = (guint8)pn;
/* Now the hard part. We need to determine the current packet number.
* This is a function of the sending node, the previous state and the
* current PDU.
*/
for (e_off = 0; e_off < e_len; e_off++)
buf[e_off] = tvb_get_guint8(tvb, offset + e_off);
/* TODO: This is hardcoded for a 4-byte MAC */
proto_item_set_end(tree, tvb, offset);
if (decrypt(session, (ccm_packet_data *)packet->security_packet, nonce, epp_buf, a_len, buf, e_len))
{
g_free(buf);
return 1;
}
else
{
/* Failure to decrypt or validate the MAC.
* The packet is secure, so there is nothing we can do!
*/
g_free(buf);
return 1;
}
}
}
#endif
static int dissect_oap_dsp(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
{
/* We are handed a buffer that starts with our protocol id. Any options follow that. */
gint offset = 0;
/* We don't care except for the treeview. */
if (!tree)
return 0;
/* Compute the version and flags, masking off other bits. */
offset += 4; /* Skip the type and protocol. */
proto_tree_add_item(tree, hf_oap_1_dsp_option, tvb, 0, -1, ENC_NA);
return offset;
}
static int dissect_oap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
dof_api_data *api_data = (dof_api_data *)data;
dof_packet_data *packet_data;
gint offset = 0;
guint8 opcode = 0;
guint8 flags = 0;
guint16 item_id = 0;
guint16 app;
guint app_len;
oap_1_packet_data *oap_packet = NULL;
proto_item *ti;
proto_tree *oap_tree;
if (api_data == NULL)
{
fprintf(stderr, "api_data == NULL");
return 0;
}
packet_data = api_data->packet;
if (packet_data == NULL)
{
fprintf(stderr, "packet_data == NULL");
return 0;
}
/* Make entries in Protocol column and Info column on summary display */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "OAPv1 ");
/* Create the protocol tree. */
offset = 0;
ti = proto_tree_add_item(tree, proto_oap_1, tvb, offset, -1, ENC_NA);
oap_tree = proto_item_add_subtree(ti, ett_oap_1);
/* Add the APPID. */
offset = read_c2(tvb, offset, &app, &app_len);
ti = proto_tree_add_uint(oap_tree, hf_2008_1_app_version, tvb, 0, app_len, app);
validate_c2(pinfo, ti, app, app_len);
if (app_len == tvb_captured_length(tvb))
{
col_append_str(pinfo->cinfo, COL_INFO, "OAP [nop]");
expert_add_info(pinfo, oap_tree, &ei_implicit_no_op);
return app_len;
}
oap_packet = (oap_1_packet_data *)dof_packet_get_proto_data(packet_data, proto_oap_1);
if (!oap_packet)
{
oap_packet = (oap_1_packet_data *)wmem_alloc0(wmem_file_scope(), sizeof(oap_1_packet_data));
dof_packet_add_proto_data(packet_data, proto_oap_1, oap_packet);
}
/* Compute the version and flags, masking off other bits. */
opcode = tvb_get_guint8(tvb, offset) & 0x1F;
if (!packet_data->is_command)
opcode |= OAP_1_RESPONSE;
flags = tvb_get_guint8(tvb, offset) & 0xE0;
col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(opcode, oap_opcode_strings, "Unknown Opcode (%d)"));
/* Opcode */
{
guint8 mask = 0x10;
char str[20];
guint8 no_of_bits = 5;
guint8 i;
guint8 bit = 3;
g_strlcpy(str, "...", 20);
/* read the bits for the int */
for (i = 0; i < no_of_bits; i++)
{
if (bit && (!(bit % 4)))
g_strlcat(str, " ", 20);
bit++;
if (opcode & mask)
g_strlcat(str, "1", 20);
else
g_strlcat(str, "0", 20);
mask = mask >> 1;
}
proto_tree_add_uint_format(oap_tree, hf_oap_1_opcode, tvb, offset, 1, opcode & 0x1F, "%s = Opcode: %s (%u)", str, val_to_str(opcode, oap_opcode_strings, "Unknown Opcode (%d)"), opcode & 0x1F);
}
/* Flags, based on opcode.
* Each opcode needs to define the flags, however, the fall into major categories...
*/
switch (opcode)
{
/* Both alias and a flag that equals command control. */
case OAP_1_CMD_ACTIVATE:
case OAP_1_CMD_CONNECT:
case OAP_1_CMD_FULL_CONNECT:
case OAP_1_CMD_GET:
case OAP_1_CMD_INVOKE:
case OAP_1_CMD_REGISTER:
case OAP_1_CMD_SET:
case OAP_1_CMD_SUBSCRIBE:
case OAP_1_CMD_WATCH:
proto_tree_add_item(oap_tree, hf_oap_1_alias_size, tvb, offset, 1, ENC_NA);
proto_tree_add_item(oap_tree, hf_oap_1_flags, tvb, offset, 1, ENC_NA);
if (flags & 0x20)
{
offset += 1;
offset = oap_1_tree_add_cmdcontrol(pinfo, oap_tree, tvb, offset);
}
else
offset += 1;
break;
/* No alias, but flags for command control. */
case OAP_1_CMD_ADVERTISE:
/* TODO: Expert info on top two bits.*/
proto_tree_add_item(oap_tree, hf_oap_1_flags, tvb, offset, 1, ENC_NA);
if (flags & 0x20)
{
offset = oap_1_tree_add_cmdcontrol(pinfo, oap_tree, tvb, ENC_BIG_ENDIAN);
}
else
offset += 1;
break;
/* No alias, but flag for provider. */
case OAP_1_RSP_GET:
case OAP_1_RSP_INVOKE:
case OAP_1_RSP_REGISTER:
case OAP_1_RSP_SET:
case OAP_1_RSP_SUBSCRIBE:
/* TODO: Expert info on top two bits.*/
proto_tree_add_item(oap_tree, hf_oap_1_flags, tvb, offset, 1, ENC_NA);
if (flags & 0x20)
{
offset += 1;
offset = dof_dissect_pdu_as_field(dissect_2009_11_type_4, tvb, pinfo, oap_tree,
offset, hf_oap_1_providerid, ett_oap_1_1_providerid, NULL);
}
else
offset += 1;
if ((opcode == OAP_1_RSP_GET) || (opcode == OAP_1_RSP_INVOKE))
{
proto_tree_add_item(oap_tree, hf_oap_1_value_list, tvb, offset, -1, ENC_NA);
offset += tvb_reported_length_remaining(tvb, offset);
}
break;
/* Alias, but no flags. */
case OAP_1_CMD_CHANGE:
case OAP_1_CMD_OPEN:
case OAP_1_CMD_PROVIDE:
case OAP_1_CMD_SIGNAL:
proto_tree_add_item(oap_tree, hf_oap_1_alias_size, tvb, offset, 1, ENC_NA);
offset += 1;
break;
/* Special flags. */
case OAP_1_RSP_EXCEPTION:
proto_tree_add_item(oap_tree, hf_oap_1_exception_internal_flag, tvb, offset, 1, ENC_NA);
proto_tree_add_item(oap_tree, hf_oap_1_exception_final_flag, tvb, offset, 1, ENC_NA);
proto_tree_add_item(oap_tree, hf_oap_1_exception_provider_flag, tvb, offset, 1, ENC_NA);
offset += 1;
break;
/* No flags. */
case OAP_1_CMD_DEFINE:
case OAP_1_RSP_DEFINE:
case OAP_1_RSP_OPEN:
/* TODO: Non-zero not allowed.*/
offset += 1;
break;
default:
/* TODO: Illegal opcode.*/
return offset;
}
/* Parse off arguments based on opcodes. */
switch (opcode)
{
case OAP_1_CMD_SUBSCRIBE:
{
guint8 alias_len = (flags & 0xC0) >> 6;
if (alias_len == 3)
alias_len = 4;
/* The item identifier comes first, but it is compressed. */
{
gint item_id_len;
proto_item *pi;
read_c2(tvb, offset, &item_id, &item_id_len);
pi = proto_tree_add_uint_format(oap_tree, hf_oap_1_itemid, tvb, offset, item_id_len, item_id, "Item ID: %u", item_id);
validate_c2(pinfo, pi, item_id, item_id_len);
offset += item_id_len;
}
if (alias_len > 0)
{
if (api_data->session == NULL)
{
expert_add_info(pinfo, ti, &ei_oap_no_session);
return offset;
}
offset = oap_1_tree_add_alias(api_data, oap_packet, packet_data, oap_tree, tvb, offset, alias_len, TRUE);
}
else
offset = oap_1_tree_add_binding(oap_tree, pinfo, tvb, offset);
/* Read the miniumum delta. */
{
gint delta_len;
guint16 delta;
proto_item *pi;
read_c2(tvb, offset, &delta, &delta_len);
pi = proto_tree_add_uint_format(oap_tree, hf_oap_1_subscription_delta, tvb, offset, delta_len, delta, "Minimum Delta: %u", delta);
validate_c2(pinfo, pi, delta, delta_len);
offset += delta_len;
}
}
break;
case OAP_1_CMD_REGISTER:
{
guint8 alias_len = (flags & 0xC0) >> 6;
if (alias_len == 3)
alias_len = 4;
/* The item identifier comes first, but it is compressed. */
{
gint item_id_len;
proto_item *pi;
read_c2(tvb, offset, &item_id, &item_id_len);
pi = proto_tree_add_uint_format(oap_tree, hf_oap_1_itemid, tvb, offset, item_id_len, item_id, "Item ID: %u", item_id);
validate_c2(pinfo, pi, item_id, item_id_len);
offset += item_id_len;
}
if (alias_len > 0)
{
if (api_data->session == NULL)
{
expert_add_info(pinfo, ti, &ei_oap_no_session);
return offset;
}
offset = oap_1_tree_add_alias(api_data, oap_packet, packet_data, oap_tree, tvb, offset, alias_len, TRUE);
}
else
offset = oap_1_tree_add_binding(oap_tree, pinfo, tvb, offset);
}
break;
case OAP_1_RSP_REGISTER:
{
if (flags & 0x20)
{
/* offset = add_oid( tvb, offset, NULL, oap_tree ); */
}
/* Sequence is next. */
proto_tree_add_item(oap_tree, hf_oap_1_update_sequence, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
}
break;
case OAP_1_CMD_WATCH:
case OAP_1_CMD_ACTIVATE:
case OAP_1_CMD_CONNECT:
case OAP_1_CMD_FULL_CONNECT:
{
guint8 alias_len = (flags & 0xC0) >> 6;
if (alias_len == 3)
alias_len = 4;
if (alias_len > 0)
{
if (api_data->session == NULL)
{
expert_add_info(pinfo, ti, &ei_oap_no_session);
return offset;
}
offset = oap_1_tree_add_alias(api_data, oap_packet, packet_data, oap_tree, tvb, offset, alias_len, TRUE);
}
else
offset = oap_1_tree_add_binding(oap_tree, pinfo, tvb, offset);
}
break;
case OAP_1_CMD_ADVERTISE:
offset = oap_1_tree_add_binding(oap_tree, pinfo, tvb, offset);
break;
case OAP_1_CMD_GET:
case OAP_1_CMD_INVOKE:
case OAP_1_CMD_SET:
{
guint8 alias_len = (flags & 0xC0) >> 6;
if (alias_len == 3)
alias_len = 4;
/* The item identifier comes first, but it is compressed. */
{
gint item_id_len;
proto_item *pi;
read_c2(tvb, offset, &item_id, &item_id_len);
pi = proto_tree_add_uint_format(oap_tree, hf_oap_1_itemid, tvb, offset, item_id_len, item_id, "Item ID: %u", item_id);
validate_c2(pinfo, pi, item_id, item_id_len);
offset += item_id_len;
}
if (alias_len > 0)
{
if (api_data->session == NULL)
{
expert_add_info(pinfo, ti, &ei_oap_no_session);
return offset;
}
offset = oap_1_tree_add_alias(api_data, oap_packet, packet_data, oap_tree, tvb, offset, alias_len, TRUE);
}
else
offset = oap_1_tree_add_binding(oap_tree, pinfo, tvb, offset);
if ((opcode == OAP_1_CMD_SET) || (opcode == OAP_1_CMD_INVOKE))
{
proto_tree_add_item(oap_tree, hf_oap_1_value_list, tvb, offset, -1, ENC_NA);
offset += tvb_reported_length_remaining(tvb, offset);
}
}
break;
case OAP_1_CMD_OPEN:
{
guint8 alias_len = (flags & 0xC0) >> 6;
if (alias_len == 3)
alias_len = 4;
if (alias_len > 0)
{
if (api_data->session == NULL)
{
expert_add_info(pinfo, ti, &ei_oap_no_session);
return offset;
}
offset = oap_1_tree_add_alias(api_data, oap_packet, packet_data, oap_tree, tvb, offset, alias_len, TRUE);
}
else
offset = oap_1_tree_add_binding(oap_tree, pinfo, tvb, offset);
offset = oap_1_tree_add_interface(oap_tree, tvb, offset);
offset = dof_dissect_pdu_as_field(dissect_2009_11_type_4, tvb, pinfo, oap_tree,
offset, hf_oap_1_objectid, ett_oap_1_objectid, NULL);
}
break;
case OAP_1_CMD_PROVIDE:
{
guint8 alias_length = flags >> 6;
gint alias_offset;
gint iid_offset;
gint oid_offset;
if (alias_length == 3)
alias_length = 4;
alias_offset = offset;
if (alias_length == 0)
{
expert_add_info_format(pinfo, ti, &ei_malformed, "alias_length == 0");
return offset;
}
if (api_data->session == NULL)
{
expert_add_info(pinfo, ti, &ei_oap_no_session);
return offset;
}
offset = oap_1_tree_add_alias(api_data, oap_packet, packet_data, oap_tree, tvb, offset, alias_length, FALSE);
iid_offset = offset;
offset = oap_1_tree_add_interface(oap_tree, tvb, offset);
oid_offset = offset;
offset = dof_dissect_pdu_as_field(dissect_2009_11_type_4, tvb, pinfo, oap_tree,
offset, hf_oap_1_objectid, ett_oap_1_objectid, NULL);
if (alias_length && !packet_data->processed)
{
guint32 alias;
oap_1_binding *binding = (oap_1_binding *)wmem_alloc0(wmem_file_scope(), sizeof(oap_1_binding));
int i;
alias = 0;
for (i = 0; i < alias_length; i++)
alias = (alias << 8) | tvb_get_guint8(tvb, alias_offset + i);
binding->iid_length = oid_offset - iid_offset;
binding->iid = (guint8 *)wmem_alloc0(wmem_file_scope(), binding->iid_length);
tvb_memcpy(tvb, binding->iid, iid_offset, binding->iid_length);
binding->oid_length = offset - oid_offset;
binding->oid = (guint8 *)wmem_alloc0(wmem_file_scope(), binding->oid_length);
tvb_memcpy(tvb, binding->oid, oid_offset, binding->oid_length);
binding->frame = pinfo->fd->num;
oap_1_define_alias(api_data, alias, binding);
}
}
break;
case OAP_1_CMD_CHANGE:
case OAP_1_CMD_SIGNAL:
{
guint8 alias_len = (flags & 0xC0) >> 6;
if (alias_len == 3)
alias_len = 4;
/* The item identifier comes first, but it is compressed. */
{
gint item_id_len;
proto_item *pi;
read_c2(tvb, offset, &item_id, &item_id_len);
pi = proto_tree_add_uint_format(oap_tree, hf_oap_1_itemid, tvb, offset, item_id_len, item_id, "Item ID: %u", item_id);
validate_c2(pinfo, pi, item_id, item_id_len);
offset += item_id_len;
}
if (alias_len > 0)
{
if (api_data->session == NULL)
{
expert_add_info(pinfo, ti, &ei_oap_no_session);
return offset;
}
offset = oap_1_tree_add_alias(api_data, oap_packet, packet_data, oap_tree, tvb, offset, alias_len, TRUE);
}
else
offset = oap_1_tree_add_binding(oap_tree, pinfo, tvb, offset);
/* Sequence is next. */
proto_tree_add_item(oap_tree, hf_oap_1_update_sequence, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
proto_tree_add_item(oap_tree, hf_oap_1_value_list, tvb, offset, -1, ENC_NA);
offset += tvb_reported_length_remaining(tvb, offset);
}
break;
case OAP_1_RSP_EXCEPTION:
{
if (flags & 0x20)
{
/* offset = add_oid( tvb, offset, NULL, oap_tree );*/
}
/* The response code, compressed. */
{
gint rsp_len;
guint16 rsp;
/* TODO: Validate*/
read_c2(tvb, offset, &rsp, &rsp_len);
/* TODO: Add to tree with error codes. */
offset += rsp_len;
}
proto_tree_add_item(oap_tree, hf_oap_1_value_list, tvb, offset, -1, ENC_NA);
offset += tvb_reported_length_remaining(tvb, offset);
}
break;
default:
/* TODO: Bad opcode!*/
break;
}
return offset;
}
static int dissect_sgmp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
dof_api_data *api_data = (dof_api_data *)data;
dof_packet_data *packet_data;
guint offset = 0;
guint8 opcode;
guint16 app;
gint app_len;
proto_item *ti;
proto_tree *sgmp_tree;
if (api_data == NULL)
{
/* TODO: Output error. */
return 0;
}
packet_data = api_data->packet;
if (packet_data == NULL)
{
/* TODO: Output error. */
return 0;
}
/* Make entries in Protocol column and Info column on summary display */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SGMPv1 ");
/* Create the protocol tree. */
offset = 0;
ti = proto_tree_add_item(tree, proto_sgmp, tvb, offset, -1, ENC_NA);
sgmp_tree = proto_item_add_subtree(ti, ett_sgmp);
/* Add the APPID. */
offset = read_c2(tvb, offset, &app, &app_len);
ti = proto_tree_add_uint(sgmp_tree, hf_2008_1_app_version, tvb, 0, app_len, app);
validate_c2(pinfo, ti, app, app_len);
if (offset == tvb_captured_length(tvb))
{
col_append_str(pinfo->cinfo, COL_INFO, "SGMP [nop]");
expert_add_info(pinfo, sgmp_tree, &ei_implicit_no_op);
return offset;
}
/* Retrieve the opcode. */
opcode = tvb_get_guint8(tvb, offset);
if (!packet_data->is_command)
opcode |= SGMP_RESPONSE;
col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(opcode, sgmp_opcode_strings, "Unknown Opcode (%d)"));
/* Opcode */
proto_tree_add_item(sgmp_tree, hf_opcode, tvb, offset, 1, ENC_NA);
offset += 1;
switch (opcode)
{
case SGMP_CMD_EPOCH_CHANGED:
{
/* TMIN - 2 bytes */
{
proto_tree_add_item(sgmp_tree, hf_sgmp_tmin, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
}
/* EPOCH - 2 bytes */
{
proto_tree_add_item(sgmp_tree, hf_sgmp_epoch, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
}
}
break;
case SGMP_CMD_HEARTBEAT:
{
gint start_offset;
/* Latest SGMP Version - Type.1 */
{
guint16 version;
gint length;
proto_item *pi;
start_offset = offset;
offset = read_c2(tvb, offset, &version, &length);
pi = proto_tree_add_uint(sgmp_tree, hf_latest_version, tvb, start_offset, offset - start_offset, version);
validate_c2(pinfo, pi, version, length);
}
/* Desire - 1 byte */
{
proto_tree_add_item(sgmp_tree, hf_desire, tvb, offset, 1, ENC_NA);
offset += 1;
}
/* Tie Breaker - 4 bytes */
{
proto_tree_add_item(sgmp_tree, hf_tie_breaker, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
}
}
break;
case SGMP_CMD_REKEY:
case SGMP_CMD_REKEY_EPOCH:
case SGMP_CMD_REKEY_MERGE:
{
#if 0 /*TODO check this */
gint start_offset;
tvbuff_t *initial_state;
#endif
guint8 key[32];
/* Delay - one byte */
if (opcode != SGMP_CMD_REKEY_MERGE)
{
proto_tree_add_item(sgmp_tree, hf_delay, tvb, offset, 1, ENC_NA);
offset += 1;
}
/* Initial State - Security.9 (not REKEY_MERGE) */
{
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_9, tvb, pinfo, sgmp_tree,
offset, hf_initial_state, ett_initial_state, NULL);
#if 0 /*TODO check this */
initial_state = tvb_new_subset(tvb, start_offset, offset - start_offset, offset - start_offset);
#endif
}
/* Epoch - 2 bytes (only REKEY_EPOCH) */
if (opcode == SGMP_CMD_REKEY_EPOCH)
{
proto_tree_add_item(sgmp_tree, hf_sgmp_epoch, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
}
/* Kgm - 32 bytes */
{
proto_tree_add_item(sgmp_tree, hf_key, tvb, offset, 32, ENC_NA);
tvb_memcpy(tvb, key, offset, 32);
offset += 32;
}
/* Handle the initialization block. */
if (!packet_data->processed && api_data->session)
{
/*dof_session_data* session = (dof_session_data*)api_data->session;*/
/* Look up the field-dissector table, and determine if it is registered. */
dissector_table_t field_dissector = find_dissector_table("dof.secmode");
if (field_dissector != NULL)
{
#if 0
dissector_handle_t field_handle = dissector_get_port_handle(field_dissector, packet_data->security_mode);
if (field_handle != NULL)
{
void *saved_private = pinfo->private_data;
dof_secmode_api_data setup_data;
gint block_length;
setup_data.version = DOF_API_VERSION;
setup_data.context = INITIALIZE;
setup_data.dof_api = api_data;
setup_data.secure_session = rekey_data->security_session;
/* TODO FIX THIS setup_data.session_key = session_key; */
pinfo->private_data = &setup_data;
block_length = call_dissector_only(field_handle, NULL, pinfo, NULL);
pinfo->private_data = saved_private;
}
#endif
}
}
}
break;
case SGMP_CMD_REQUEST_GROUP:
{
guint8 *domain_buf = NULL;
guint8 domain_length = 0;
gint start_offset;
guint I_offset = offset;
sgmp_packet_data *sgmp_data = NULL;
guint16 epoch;
/* START OF I BLOCK */
/* Domain - Security.7 */
{
start_offset = offset;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_7, tvb, pinfo, sgmp_tree,
offset, hf_sgmp_domain, ett_sgmp_domain, NULL);
if (!packet_data->processed)
{
domain_length = offset - start_offset;
domain_buf = (guint8 *)wmem_alloc0(wmem_packet_scope(), domain_length);
tvb_memcpy(tvb, domain_buf, start_offset, domain_length);
}
}
/* Epoch - 2 bytes */
{
epoch = tvb_get_ntohs(tvb, offset);
proto_tree_add_item(sgmp_tree, hf_sgmp_epoch, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
}
/* Initiator Block - SGMP.6.3 */
{
/* SGMP Key Request - Security.4 */
{
dof_2008_16_security_4 response;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_4, tvb, pinfo, sgmp_tree,
offset, hf_initiator_block, ett_initiator_block, &response);
if (!packet_data->processed)
{
tvbuff_t *identity = response.identity;
guint8 identity_length = tvb_reported_length(identity);
guint8 *identity_buf = (guint8 *)wmem_alloc0(wmem_file_scope(), identity_length);
/* Get the buffer. */
tvb_memcpy(identity, identity_buf, 0, identity_length);
{
sgmp_data = (sgmp_packet_data *)wmem_alloc0(wmem_file_scope(), sizeof(sgmp_packet_data));
dof_packet_add_proto_data(packet_data, proto_sgmp, sgmp_data);
sgmp_data->domain_length = domain_length;
sgmp_data->domain = (guint8 *)wmem_alloc0(wmem_file_scope(), domain_length);
memcpy(sgmp_data->domain, domain_buf, domain_length);
sgmp_data->group_length = identity_length;
sgmp_data->group = (guint8 *)wmem_alloc0(wmem_file_scope(), identity_length);
memcpy(sgmp_data->group, identity_buf, identity_length);
sgmp_data->epoch = epoch;
sgmp_data->request_session = api_data->session;
}
}
}
}
/* Security Scope - Security.10 */
{
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_10, tvb, pinfo, sgmp_tree,
offset, hf_sgmp_security_scope, ett_sgmp_security_scope, NULL);
}
/* END OF I BLOCK */
if (sgmp_data && !sgmp_data->I)
{
sgmp_data->I_length = offset - I_offset;
sgmp_data->I = (guint8 *)wmem_alloc0(wmem_file_scope(), sgmp_data->I_length);
tvb_memcpy(tvb, sgmp_data->I, I_offset, sgmp_data->I_length);
}
}
break;
case SGMP_RSP_REQUEST_GROUP:
{
gint start_offset;
#if 0 /*TODO check this */
guint A_offset;
tvbuff_t *initial_state;
guint A_end;
#endif
/* START OF A BLOCK */
/* Initial State - SGMP.6.2.1 */
{
/* A_offset = offset;*/
/* Initial State - Security.9 */
{
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_9, tvb, pinfo, sgmp_tree,
offset, hf_initial_state, ett_initial_state, NULL);
#if 0 /*TODO check this */
initial_state = tvb_new_subset(tvb, start_offset, offset - start_offset, offset - start_offset);
#endif
}
/* Latest SGMP Version - Type.1 */
{
guint16 version;
gint length;
proto_item *pi;
start_offset = offset;
offset = read_c2(tvb, offset, &version, &length);
pi = proto_tree_add_uint(sgmp_tree, hf_latest_version, tvb, start_offset, offset - start_offset, version);
validate_c2(pinfo, pi, version, length);
}
/* Desire - 1 byte */
{
proto_tree_add_item(sgmp_tree, hf_desire, tvb, offset, 1, ENC_NA);
offset += 1;
}
}
/* END OF A BLOCK */
/* A block data handled in first part of the next block. */
#if 0 /*TODO check this */
A_end = offset;
#endif
/* Ticket - Security.5 */
{
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_5, tvb, pinfo, sgmp_tree,
offset, hf_ticket, ett_ticket, NULL);
}
/* Try to match up the information learned here with any groups that exist.
* Note that we do not know the SSID, and so we can only match based on the
* domain and group identifier. We will learn the SSID based on a successful
* match to a secure session.
*/
if (packet_data->opid_first && !api_data->secure_session)
{
#if 0
sgmp_packet_data* cmd_data = (sgmp_packet_data*)dof_packet_get_proto_data(packet_data->opid_first, proto_sgmp);
extern struct BlockCipher BlockCipher_AES_256;
struct BlockCipher* cipher = &BlockCipher_AES_256;
guint8* ekey = (guint8*)ep_alloc(cipher->keyStateSize);
if (cmd_data && !cmd_data->A)
{
cmd_data->A_length = A_end - A_offset;
cmd_data->A = (guint8*)wmem_alloc0(wmem_file_scope(), cmd_data->A_length);
tvb_memcpy(tvb, cmd_data->A, A_offset, cmd_data->A_length);
}
/* Search through the appropriate keks to find a match. */
{
dof_learned_group_data* group = globals.learned_group_data;
struct list;
struct list
{ dof_learned_group_data *group;
struct list *next; };
struct list *to_try = NULL;
guint8 confirmation[32];
guint8* discovered_kek = NULL;
dof_learned_group_auth_data *auth = NULL;
tvb_memcpy(tvb, confirmation, start_offset, 32);
while (group)
{
if ((cmd_data->domain_length == group->domain_length) &&
(memcmp(cmd_data->domain, group->domain, group->domain_length) == 0) &&
(cmd_data->group_length == group->group_length) &&
(memcmp(cmd_data->group, group->group, group->group_length) == 0))
{
struct list *n = (struct list *) ep_alloc0(sizeof(struct list));
n->group = group;
n->next = to_try;
to_try = n;
}
group = group->next;
}
/* At this point we may be able to learn the session key. */
while (to_try && !discovered_kek)
{
group = to_try->group;
auth = group->keys;
while (auth && !discovered_kek)
{
guint8 mac[32];
guint8 key[32];
int j;
/* It only makes sense to check matching epochs. */
if (auth->epoch == cmd_data->epoch)
{
tvb_memcpy(tvb, mac, start_offset, 32);
tvb_memcpy(tvb, key, start_offset + 32, 32);
if (cipher != NULL)
{
cipher->GenerateKeyState(ekey, auth->kek);
cipher->Encrypt(ekey, mac);
cipher->Encrypt(ekey, mac + 16);
}
for (j = 0; j < 32; j++)
key[j] ^= mac[j];
if (sgmp_validate_session_key(cmd_data, confirmation, auth->kek, key))
{
discovered_kek = (guint8*)se_alloc0(32);
memcpy(discovered_kek, key, 32);
break;
}
}
auth = auth->next;
}
to_try = to_try->next;
}
/* Determine if there is already a secure session for this information. If there is, then
* EPP will find it to decode any packets. If there is not, then we must create a secure
* session and initialize it so that future packets can be decoded.
* NOTE: None of the actual decoding is done here, because this packet is not encrypted
* in the session that it defines.
* NOTE: SGMP secure sessions are always attached to the DPS session, which is always
* associated with the transport session (server address).
*/
if (discovered_kek)
{
dissector_table_t field_dissector;
dissector_handle_t field_handle;
dof_session_key_exchange_data *key_exchange = NULL;
dof_secure_session_data *dof_secure_session = cmd_data->request_session->secure_sessions;
while (dof_secure_session)
{
if ((dof_secure_session->ssid == group->ssid) &&
(dof_secure_session->domain_length == group->domain_length) &&
(memcmp(dof_secure_session->domain, group->domain, group->domain_length) == 0))
break;
dof_secure_session = dof_secure_session->next;
}
if (!dof_secure_session)
{
dof_session_data *dof_session = wmem_alloc0(wmem_file_scope(), sizeof(dof_session_data));
dof_session->session_id = globals.next_session++;
dof_session->dof_id = api_data->session->dof_id;
dof_secure_session = wmem_alloc0(wmem_file_scope(), sizeof(dof_secure_session_data));
dof_secure_session->ssid = group->ssid;
dof_secure_session->domain_length = group->domain_length;
dof_secure_session->domain = group->domain;
dof_secure_session->original_session_id = cmd_data->request_session->session_id;
dof_secure_session->parent = dof_session;
dof_secure_session->is_2_node = FALSE;
dof_secure_session->next = cmd_data->request_session->secure_sessions;
cmd_data->request_session->secure_sessions = dof_secure_session;
}
/* This packet represents a new key exchange, and so a new key exchange data
* structure needs to be created.
*/
{
key_exchange = wmem_alloc0(wmem_file_scope(), sizeof(dof_session_key_exchange_data));
if (!key_exchange)
return offset;
key_exchange->i_valid = packet_data->opid_first->dof_frame;
key_exchange->r_valid = packet_data->dof_frame;
key_exchange->security_mode = auth->security_mode;
key_exchange->security_mode_data = auth->mode;
key_exchange->security_mode_data_length = auth->mode_length;
key_exchange->session_key = discovered_kek;
/* Insert the new key information at the front of the list. */
if (!dof_secure_session->session_security_data_last)
dof_secure_session->session_security_data = key_exchange;
else
dof_secure_session->session_security_data_last->next = key_exchange;
dof_secure_session->session_security_data_last = key_exchange;
}
/* Look up the field-dissector table, and determine if it is registered. */
field_dissector = find_dissector_table("dps.secmode");
if (field_dissector != NULL)
{
field_handle = dissector_get_uint_handle(field_dissector, auth->security_mode);
if (field_handle != NULL)
{
dof_secmode_api_data setup_data;
gint block_length;
tvbuff_t *ntvb = tvb_new_subset(tvb, A_offset, -1, -1);
setup_data.context = INITIALIZE;
setup_data.security_mode_offset = 0;
setup_data.dof_api = api_data;
setup_data.secure_session = dof_secure_session;
setup_data.session_key_data = key_exchange;
block_length = call_dissector_only(field_handle, ntvb, pinfo, tree, &setup_data);
}
}
}
}
#endif
}
}
break;
default:
break;
}
return offset;
}
#ifdef LIBGCRYPT_OK
static gboolean validate_session_key(tep_rekey_data *rekey, guint S_length, guint8 *S, guint8 *confirmation, guint8 *key)
{
guint8 pad[16];
gcry_mac_hd_t hmac;
gcry_error_t result;
memset(pad, 0, sizeof(pad));
result = gcry_mac_open(&hmac, GCRY_MAC_HMAC_SHA256, 0, NULL);
if (result != 0)
return FALSE;
gcry_mac_setkey(hmac, key, 32);
gcry_mac_write(hmac, pad, 16 - rekey->i_nonce_length);
gcry_mac_write(hmac, rekey->i_nonce, rekey->i_nonce_length);
gcry_mac_write(hmac, pad, 16 - rekey->r_nonce_length);
gcry_mac_write(hmac, rekey->r_nonce, rekey->r_nonce_length);
gcry_mac_write(hmac, S, S_length);
gcry_mac_write(hmac, rekey->r_identity, rekey->r_identity_length);
result = gcry_mac_verify(hmac, confirmation, 32);
return result == 0;
}
#else
static gboolean validate_session_key(tep_rekey_data *rekey _U_, guint S_length _U_, guint8 *S _U_, guint8 *confirmation _U_, guint8 *key _U_)
{
return FALSE;
}
#endif
static int dissect_tep_dsp(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
{
/* We are handed a buffer that starts with our protocol id. Any options follow that. */
gint offset = 0;
/* We don't care except for the treeview. */
if (!tree)
return 0;
/* Compute the version and flags, masking off other bits. */
offset += 4; /* Skip the type and protocol. */
proto_tree_add_item(tree, hf_dsp_option, tvb, 0, -1, ENC_NA);
return offset;
}
static int dissect_2008_4_tep_2_2_1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 *ssid, void *data)
{
gint offset = 0;
proto_item *ti;
dof_api_data *api_data = (dof_api_data *)data;
dof_packet_data *packet_data;
if (api_data == NULL)
{
/* TODO: Output error. */
return 0;
}
packet_data = api_data->packet;
if (packet_data == NULL)
{
/* TODO: Output error. */
return 0;
}
/* State Identifier - Only if Unsecured */
if (packet_data->decrypted_buffer == NULL)
{
proto_item *pi;
gint ssid_len;
gint start = offset;
offset = read_c4(tvb, offset, ssid, &ssid_len);
pi = proto_tree_add_uint(tree, hf_tep_2_2_1_state_identifier, tvb, start, offset - start, *ssid);
validate_c4(pinfo, pi, *ssid, ssid_len);
}
/* Initial State */
{
int block_length;
tvbuff_t *start = tvb_new_subset(tvb, offset, -1, -1);
ti = proto_tree_add_item(tree, hf_tep_2_2_1_initial_state, tvb, offset, 0, ENC_NA);
ti = proto_item_add_subtree(ti, ett_tep_2_2_1_initial_state);
block_length = dof_dissect_pdu(dissect_2008_16_security_9, start, pinfo, ti, NULL);
proto_item_set_len(ti, block_length);
offset += block_length;
}
return offset;
}
/**
* This is the main entry point for the CCM dissector.
* TEP operations create security periods.
* They can also create sessions when used with "None" sessions.
* In any case, these PDUs need to pass information between
* them.
* They also must maintain state for each rekey request, some of
* which modify the session key, some of which create new
* sessions, and others that determine new session information
* like permission sets.
*
* In order to store information appropriately, the following structures are
* used:
* 1. api_data (dof_api_data*) source for all other state.
* 2. packet (dof_packet_data*) dps packet information.
* 3. rekey_data (tep_rekey_data*) tep information for rekey/accept/confirm.
*/
static int dissect_tep(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
dof_api_data *api_data = (dof_api_data *)data;
dof_packet_data *packet;
tep_rekey_data *rekey_data;
guint offset = 0;
guint8 operation;
guint16 app;
gint app_len;
proto_item *ti;
proto_tree *tep_tree, *operation_tree;
if (api_data == NULL)
{
/* TODO: Output error. */
return 0;
}
packet = api_data->packet;
if (packet == NULL)
{
/* TODO: Output error. */
return 0;
}
/* Make entries in Protocol column and Info column on summary display */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "TEPv1 ");
/* Create the protocol tree. */
offset = 0;
ti = proto_tree_add_item(tree, proto_tep, tvb, offset, -1, ENC_NA);
tep_tree = proto_item_add_subtree(ti, ett_tep);
/* Add the APPID. */
offset = read_c2(tvb, offset, &app, &app_len);
ti = proto_tree_add_uint(tep_tree, hf_2008_1_app_version, tvb, 0, app_len, app);
validate_c2(pinfo,ti, app, app_len);
/* Check for empty packet. */
if (offset == tvb_captured_length(tvb))
{
col_append_str(pinfo->cinfo, COL_INFO, "TEP [nop]");
expert_add_info(pinfo, tep_tree, &ei_implicit_no_op);
return offset;
}
/* Retrieve the opcode. */
operation = tvb_get_guint8(tvb, offset);
if (!packet->is_command)
operation |= TEP_OPCODE_RSP;
col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(operation, tep_opcode_strings, "Unknown Opcode (%d)"));
ti = proto_tree_add_uint_format(tep_tree, hf_tep_operation, tvb, offset, 1, operation, "Operation: %s (%u)", val_to_str(operation, tep_opcode_strings, "Unknown Opcode (%d)"), operation);
operation_tree = proto_item_add_subtree(ti, ett_tep_operation);
ti = proto_tree_add_boolean(operation_tree, hf_tep_operation_type, tvb, offset, 0, operation);
PROTO_ITEM_SET_GENERATED(ti);
/* The flags are reserved except for OPCODE=1 & COMMAND */
if ((operation & 0x8F) == 0x01)
{
proto_tree_add_item(operation_tree, hf_tep_c, tvb, offset, 1, ENC_NA);
proto_tree_add_item(operation_tree, hf_tep_k, tvb, offset, 1, ENC_NA);
}
proto_tree_add_item(operation_tree, hf_tep_opcode, tvb, offset, 1, ENC_NA);
offset += 1;
switch (operation)
{
case TEP_PDU_REQUEST_KEY:
/* The K bit must be set, so there is a domain ONLY IF NOT SECURED. */
/* Remember the current request. */
rekey_data = (tep_rekey_data *)packet->opid_data;
if (!rekey_data)
{
packet->opid_data = rekey_data = (tep_rekey_data *)wmem_alloc0(wmem_file_scope(), sizeof(tep_rekey_data));
}
rekey_data->key_data = (dof_session_key_exchange_data *)wmem_alloc0(wmem_file_scope(), sizeof(dof_session_key_exchange_data));
rekey_data->is_rekey = TRUE;
/* The K bit must be set, so there is a domain ONLY IF NOT SECURED. */
if (packet->decrypted_buffer == NULL)
{
gint start_offset = offset;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_7, tvb, pinfo, tep_tree,
offset, hf_tep_2_1_domain, ett_tep_2_1_domain, NULL);
if (!rekey_data->domain)
{
rekey_data->domain_length = offset - start_offset;
rekey_data->domain = (guint8 *)wmem_alloc0(wmem_file_scope(), rekey_data->domain_length);
/* Get the buffer. */
tvb_memcpy(tvb, rekey_data->domain, start_offset, rekey_data->domain_length);
}
}
else
{
/* The domain is not present, but this is a secure packet and so the domain can be obtained
* through the session.
*/
if (!rekey_data->domain)
{
rekey_data->domain_length = api_data->secure_session->domain_length;
rekey_data->domain = api_data->secure_session->domain;
}
}
/* FALL THROUGH TO REQUEST */
case TEP_PDU_REQUEST:
/* Remember the current request. */
rekey_data = (tep_rekey_data *)packet->opid_data;
if (!rekey_data)
{
if (api_data->secure_session == NULL)
{
/* TODO: Output error. */
return 0;
}
packet->opid_data = rekey_data = (tep_rekey_data *)wmem_alloc0(wmem_file_scope(), sizeof(tep_rekey_data));
rekey_data->domain_length = api_data->secure_session->domain_length;
rekey_data->domain = api_data->secure_session->domain;
}
/* The C bit must be clear, so there is an Initiator Block. */
{
dof_2008_16_security_6_1 response;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_6_1, tvb, pinfo, tep_tree,
offset, hf_tep_2_1_initiator_block, ett_tep_2_1_initiator_block, &response);
if (!packet->processed)
{
tvbuff_t *inonce = response.i_nonce;
tvbuff_t *iidentity = response.i_identity;
rekey_data->i_nonce_length = tvb_reported_length(inonce);
rekey_data->i_nonce = (guint8 *)wmem_alloc0(wmem_file_scope(), rekey_data->i_nonce_length);
tvb_memcpy(inonce, rekey_data->i_nonce, 0, rekey_data->i_nonce_length);
rekey_data->i_identity_length = tvb_reported_length(iidentity);
rekey_data->i_identity = (guint8 *)wmem_alloc0(wmem_file_scope(), rekey_data->i_identity_length);
tvb_memcpy(iidentity, rekey_data->i_identity, 0, rekey_data->i_identity_length);
rekey_data->security_mode = response.security_mode;
rekey_data->security_mode_data_length = response.security_mode_data_length;
rekey_data->security_mode_data = response.security_mode_data;
}
}
break;
case TEP_PDU_ACCEPT:
{
guint32 ssid = 0;
guint8 *S = NULL;
guint8 S_length = 0;
guint8 confirmation[32];
typedef struct identity_key
{
guint8 *session_key;
struct identity_key *next;
} identity_key;
identity_key *identity_key_list = NULL;
dof_secure_session_data *dof_secure_session = NULL;
if (!packet->opid_first)
{
/* TODO: Print error */
return 0;
}
rekey_data = (tep_rekey_data *)packet->opid_first->opid_data;
if (!rekey_data)
return tvb_captured_length(tvb);
/* Initiator Ticket */
{
gint start_offset;
guint8 ticket[64];
start_offset = offset;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_5, tvb, pinfo, tep_tree,
offset, hf_tep_2_2_initiator_ticket, ett_tep_2_2_initiator_ticket, NULL);
if (!packet->processed && rekey_data)
{
rijndael_ctx cipher_state;
int i;
/* Produce a (possibly empty) list of potential keys based on our
* initiator secrets based on identity. These will be validated
* later on.
*/
for (i = 0; i < globals.global_security->identity_data_count; i++)
{
dof_identity_data *identity = globals.global_security->identity_data + i;
int j;
if (identity->domain_length != rekey_data->domain_length)
continue;
if (memcmp(identity->domain, rekey_data->domain, identity->domain_length) != 0)
continue;
if (identity->identity_length != rekey_data->i_identity_length)
continue;
if (memcmp(identity->identity, rekey_data->i_identity, identity->identity_length) != 0)
continue;
tvb_memcpy(tvb, ticket, start_offset, 64);
rijndael_set_key(&cipher_state, identity->secret, 256);
encryptInPlace(DOF_PROTOCOL_TEP, &cipher_state, ticket, 16);
encryptInPlace(DOF_PROTOCOL_TEP, &cipher_state, ticket + 16, 16);
for (j = 0; j < 32; j++)
ticket[j + 32] = ticket[j + 32] ^ ticket[j];
/* Add the key to the list - ep memory. */
{
identity_key *key = (identity_key *)wmem_alloc0(wmem_file_scope(), sizeof(*key));
key->session_key = (guint8 *)wmem_alloc0(wmem_file_scope(), 32);
memcpy(key->session_key, ticket + 32, 32);
key->next = identity_key_list;
identity_key_list = key;
}
}
}
}
/* Ticket Confirmation */
{
if (!packet->processed)
tvb_memcpy(tvb, confirmation, offset, sizeof(confirmation));
proto_tree_add_item(tep_tree, hf_tep_2_2_ticket_confirmation, tvb, offset, 32, ENC_NA);
offset += 32;
}
/* Add a field to show the session key that has been learned. */
if (rekey_data->key_data && rekey_data->key_data->session_key && tep_tree)
{
const gchar *SID = bytestring_to_str(NULL, rekey_data->key_data->session_key, 32, ':');
ti = proto_tree_add_bytes_format_value(tree, hf_tep_session_key, tvb, 0, 0, rekey_data->key_data->session_key, "%s", SID);
PROTO_ITEM_SET_GENERATED(ti);
}
/* Responder Initialization - present based on whether the command was a rekey */
{
if (rekey_data && rekey_data->is_rekey)
{
int block_length;
tvbuff_t *start = tvb_new_subset(tvb, offset, -1, -1);
ti = proto_tree_add_item(tep_tree, hf_tep_2_2_responder_initialization, tvb, offset, 0, ENC_NA);
ti = proto_item_add_subtree(ti, ett_tep_2_2_responder_initialization);
block_length = dissect_2008_4_tep_2_2_1(start, pinfo, ti, &ssid, data);
proto_item_set_len(ti, block_length);
offset += block_length;
if (!packet->processed)
{
S_length = block_length;
S = (guint8 *)wmem_alloc0(wmem_file_scope(), S_length);
tvb_memcpy(start, S, 0, S_length);
}
/* TEP can create new sessions when not used inside an existing secure
* session. Each session can use an SSID, present in TEP.2.2.1.
* Note that in this case there may be no existing session, and so
* we need to "backpedal" and create one.
*/
if (packet->decrypted_buffer == NULL && !packet->processed)
{
#if 0
if (api_data->session)
tep_session = (tep_session_data*)dof_session_get_proto_data((dof_session_data*)api_data->session, proto_tep);
if (!tep_session && api_data->session)
{
tep_session = (tep_session_data*)se_alloc0(sizeof(*tep_session));
dof_session_add_proto_data((dof_session_data*)api_data->session, proto_tep, tep_session);
}
tep_session->pending_rekey = cmd;
tep_session->pending_confirm = packet;
#endif
}
}
}
/* Responder Block */
{
dof_2008_16_security_6_2 response;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_6_2, tvb, pinfo, tep_tree,
offset, hf_tep_2_2_responder_block, ett_tep_2_2_responder_block, &response);
if (!packet->processed)
{
tvbuff_t *rnonce = response.r_nonce;
tvbuff_t *ridentity = response.r_identity;
rekey_data->r_nonce_length = tvb_reported_length(rnonce);
rekey_data->r_nonce = (guint8 *)wmem_alloc0(wmem_file_scope(), rekey_data->r_nonce_length);
tvb_memcpy(rnonce, rekey_data->r_nonce, 0, rekey_data->r_nonce_length);
rekey_data->r_identity_length = tvb_reported_length(ridentity);
rekey_data->r_identity = (guint8 *)wmem_alloc0(wmem_file_scope(), rekey_data->r_identity_length);
tvb_memcpy(ridentity, rekey_data->r_identity, 0, rekey_data->r_identity_length);
}
}
/* Authentication Initialization */
{
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_6_3, tvb, pinfo, tep_tree,
offset, hf_tep_2_2_authenticator_initialization, ett_tep_2_2_authenticator_initialization, NULL);
}
/* The request was accepted, and so a new secure session exists. We define the session,
* add it to the list of secure sessions for the unsecure session, and EPP will do the
* rest.
*/
if (packet->decrypted_buffer == NULL)
{
/* This triggers the creation of the corresponding secure DPS session if it is not already
* created. This allows information to be stored in that session even though no packets
* have used it yet. There is a problem, however, because at this point we do not know
* the SSID that (may) be associated with this session.
*/
{
dof_session_data *dof_session = api_data->session;
dof_secure_session = dof_session->secure_sessions;
while (dof_secure_session != NULL)
{
/* Determine matching session. The session list already is scoped by transport and DPS
* session, so the only thing remaining is the domain and secure session ID.
*/
if ((dof_secure_session->ssid == ssid) &&
(dof_secure_session->domain_length == rekey_data->domain_length) &&
(memcmp(dof_secure_session->domain, rekey_data->domain, rekey_data->domain_length) == 0))
break;
dof_secure_session = dof_secure_session->next;
}
if (!dof_secure_session)
{
dof_session = (dof_session_data *)wmem_alloc0(wmem_file_scope(), sizeof(dof_session_data));
dof_session->session_id = globals.next_session++;
dof_session->dof_id = api_data->session->dof_id;
dof_secure_session = (dof_secure_session_data *)wmem_alloc0(wmem_file_scope(), sizeof(dof_secure_session_data));
dof_secure_session->ssid = ssid;
dof_secure_session->domain_length = rekey_data->domain_length;
dof_secure_session->domain = rekey_data->domain;
dof_secure_session->original_session_id = api_data->session->session_id;
dof_secure_session->parent = dof_session;
dof_secure_session->is_2_node = TRUE;
dof_secure_session->next = api_data->session->secure_sessions;
api_data->session->secure_sessions = dof_secure_session;
if (!dof_secure_session->session_security_data_last)
dof_secure_session->session_security_data = rekey_data->key_data;
else
dof_secure_session->session_security_data_last->next = rekey_data->key_data;
dof_secure_session->session_security_data_last = rekey_data->key_data;
}
}
}
/* This PDU indicates the beginning of security for the responder. The next PDU
* sent will be encrypted with these settings. This means that we must determine
* the security settings and set them in the session.
*/
if (!packet->processed && rekey_data->is_rekey)
{
int i;
guint8 *session_key = NULL;
/* We have everything that we need. Determine the session secret if we can. */
/* Check any keys determined above by initiator identity. */
while (session_key == NULL && identity_key_list)
{
if (validate_session_key(rekey_data, S_length, S, confirmation, identity_key_list->session_key))
{
session_key = (guint8 *)wmem_alloc0(wmem_file_scope(), 32);
memcpy(session_key, identity_key_list->session_key, 32);
}
identity_key_list = identity_key_list->next;
}
/* For each key in the global configuration, see if we can validate the confirmation. */
for (i = 0; session_key == NULL && i < globals.global_security->session_key_count; i++)
{
if (validate_session_key(rekey_data, S_length, S, confirmation, globals.global_security->session_key[i].session_key))
session_key = globals.global_security->session_key[i].session_key;
}
/* Whether or not this can be decrypted, the security mode infomation
* should be kept with the session.
*/
{
rekey_data->key_data->r_valid = packet->dof_frame;
rekey_data->key_data->i_valid = G_MAXUINT32;
rekey_data->key_data->session_key = session_key;
rekey_data->key_data->security_mode = rekey_data->security_mode;
rekey_data->key_data->security_mode_data_length = rekey_data->security_mode_data_length;
rekey_data->key_data->security_mode_data = rekey_data->security_mode_data;
if (session_key && dof_secure_session)
{
/* Look up the field-dissector table, and determine if it is registered. */
dissector_table_t field_dissector = find_dissector_table("dof.secmode");
if (field_dissector != NULL)
{
dissector_handle_t field_handle = dissector_get_uint_handle(field_dissector, rekey_data->key_data->security_mode);
if (field_handle != NULL)
{
dof_secmode_api_data setup_data;
setup_data.context = INITIALIZE;
setup_data.security_mode_offset = 0;
setup_data.dof_api = api_data;
setup_data.secure_session = dof_secure_session;
setup_data.session_key_data = rekey_data->key_data;
call_dissector_only(field_handle, NULL, pinfo, NULL, &setup_data);
}
}
}
}
}
}
break;
case TEP_PDU_CONFIRM:
{
/* C is set, K is clear. */
/* Ticket Confirmation */
proto_tree_add_item(tep_tree, hf_tep_2_1_ticket_confirmation, tvb, offset, 32, ENC_NA);
offset += 32;
if (!packet->processed && api_data->session && packet->opid_first && packet->opid_first->opid_data)
{
dof_session_key_exchange_data *sk_data;
rekey_data = (tep_rekey_data *)packet->opid_first->opid_data;
sk_data = rekey_data->key_data;
/* TODO: Error if not found or if already set. */
if (sk_data)
sk_data->i_valid = packet->dof_frame;
}
}
break;
case TEP_PDU_END_SESSION:
case TEP_PDU_SESSION_ENDING:
break;
case TEP_PDU_REJECT:
{
/* Error Code */
proto_tree_add_item(tep_tree, hf_tep_reject_code, tvb, offset, 1, ENC_NA);
offset += 1;
/* Error Description */
if (tvb_captured_length(tvb) > offset)
proto_tree_add_item(tep_tree, hf_tep_reject_data, tvb, offset, -1, ENC_NA);
}
break;
default:
break;
}
return offset;
}
static int dissect_trp_dsp(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
{
/* We are handed a buffer that starts with our protocol id. Any options follow that. */
gint offset = 0;
/* We don't care except for the treeview. */
if (!tree)
return 0;
/* Compute the version and flags, masking off other bits. */
offset += 4; /* Skip the type and protocol. */
proto_tree_add_item(tree, hf_trp_dsp_option, tvb, 0, -1, ENC_NA);
return offset;
}
static int dissect_trp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
dof_api_data *api_data = (dof_api_data *)data;
dof_packet_data *packet_data;
guint offset = 0;
guint8 opcode;
guint16 app;
gint app_len;
proto_item *ti;
proto_tree *trp_tree;
trp_packet_data *trp_data;
/* Make entries in Protocol column and Info column on summary display */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "TRP ");
/* Create the protocol tree. */
offset = 0;
ti = proto_tree_add_item(tree, proto_trp, tvb, offset, -1, ENC_NA);
trp_tree = proto_item_add_subtree(ti, ett_trp);
/* Add the APPID. */
offset = read_c2(tvb, offset, &app, &app_len);
ti = proto_tree_add_uint(trp_tree, hf_2008_1_app_version, tvb, 0, app_len, app);
validate_c2(pinfo, ti, app, app_len);
if (api_data == NULL)
{
expert_add_info_format(pinfo, ti, &ei_malformed, "api_data == NULL");
return offset;
}
packet_data = api_data->packet;
if (packet_data == NULL)
{
expert_add_info_format(pinfo, ti, &ei_malformed, "api_data == NULL");
return offset;
}
trp_data = (trp_packet_data *)dof_packet_get_proto_data(packet_data, proto_trp);
if (offset == tvb_captured_length(tvb))
{
col_append_str(pinfo->cinfo, COL_INFO, "TRP [nop]");
expert_add_info(pinfo, trp_tree, &ei_implicit_no_op);
return offset;
}
/* Retrieve the opcode. */
opcode = tvb_get_guint8(tvb, offset);
if (!packet_data->is_command)
opcode |= TRP_RESPONSE;
col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(opcode, trp_opcode_strings, "Unknown Opcode (%d)"));
/* Opcode */
ti = proto_tree_add_uint_format(trp_tree, hf_trp_opcode, tvb, offset, 1, opcode & 0x7F, "Opcode: %s (%u)", val_to_str(opcode, trp_opcode_strings, "Unknown Opcode (%d)"), opcode & 0x7F);
offset += 1;
switch (opcode)
{
case TRP_RSP_REJECT:
{
/* Error Code */
proto_tree_add_item(trp_tree, hf_trp_errorcode, tvb, offset, 1, ENC_NA);
offset += 1;
}
break;
case TRP_CMD_REQUEST_KEK:
{
guint8 *domain_buf = NULL;
guint8 domain_length = 0;
gint start_offset;
if (trp_data && trp_data->identity_length)
{
expert_add_info(pinfo, ti, &ei_trp_initiator_id_known);
}
/* Domain - Security.7 */
start_offset = offset;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_7, tvb, pinfo, trp_tree, offset, hf_domain, ett_domain, NULL);
if (!packet_data->processed)
{
domain_length = offset - start_offset;
domain_buf = (guint8 *)wmem_alloc0(wmem_file_scope(), domain_length);
tvb_memcpy(tvb, domain_buf, start_offset, domain_length);
}
/* Initiator Block - TRP.4.1.1 */
{
dof_2008_16_security_4 response;
trp_packet_data *trp_pkt_data = NULL;
start_offset = offset;
/* Initiator Key Request - Security.4 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_4, tvb, pinfo, trp_tree,
offset, hf_initiator_request, ett_initiator_request, &response);
if (!packet_data->processed)
{
tvbuff_t *identity = response.identity;
guint8 identity_length = tvb_reported_length(identity);
guint8 *identity_buf = (guint8 *)wmem_alloc0(wmem_packet_scope(), identity_length);
int i;
/* Get the buffer. */
tvb_memcpy(identity, identity_buf, 0, identity_length);
/* Check to see if there is a matching identity. */
for (i = 0; i < globals.global_security->identity_data_count; i++)
{
dof_identity_data *gidentity = globals.global_security->identity_data + i;
if (domain_length != gidentity->domain_length ||
memcmp(domain_buf, gidentity->domain, domain_length) != 0)
continue;
if (identity_length == gidentity->identity_length &&
memcmp(identity_buf, gidentity->identity, identity_length) == 0)
{
trp_pkt_data = (trp_packet_data *)wmem_alloc0(wmem_file_scope(), sizeof(trp_packet_data));
dof_packet_add_proto_data(packet_data, proto_trp, trp_pkt_data);
trp_pkt_data->domain_length = domain_length;
trp_pkt_data->domain = (guint8 *)wmem_alloc0(wmem_file_scope(), domain_length);
memcpy(trp_pkt_data->domain, domain_buf, domain_length);
trp_pkt_data->identity_length = identity_length;
trp_pkt_data->identity = (guint8 *)wmem_alloc0(wmem_file_scope(), identity_length);
memcpy(trp_pkt_data->identity, identity_buf, identity_length);
trp_pkt_data->secret = gidentity->secret;
}
}
}
/* Group Identifier - Security.8 */
{
gint gid_start = offset;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_8, tvb, pinfo, trp_tree,
offset, hf_group_identifier, ett_group_identifier, NULL);
if (trp_pkt_data)
{
trp_pkt_data->group_length = offset - gid_start;
trp_pkt_data->group = (guint8 *)wmem_alloc0(wmem_file_scope(), trp_pkt_data->group_length);
tvb_memcpy(tvb, trp_pkt_data->group, gid_start, trp_pkt_data->group_length);
}
}
if (trp_pkt_data)
{
/* We need to store the entire block_I for later use. */
trp_pkt_data->block_I_length = offset - start_offset;
trp_pkt_data->block_I = (guint8 *)wmem_alloc0(wmem_file_scope(), trp_pkt_data->block_I_length);
tvb_memcpy(tvb, trp_pkt_data->block_I, start_offset, trp_pkt_data->block_I_length);
}
}
}
break;
case TRP_RSP_REQUEST_KEK:
{
gint start_offset;
guint32 ssid;
guint8 *mode;
guint8 mode_length;
guint8 *block_A;
guint8 block_A_length;
if (trp_data && trp_data->kek_known)
{
expert_add_info(pinfo, ti, &ei_trp_kek_discovered);
}
/* Initiator Ticket - Security.5 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_5, tvb, pinfo, trp_tree,
offset, hf_initiator_ticket, ett_initiator_ticket, NULL);
/* Initialization Block - TRP.4.2.1 */
/* A BLOCK */
{
start_offset = offset;
/* THB */
{
proto_tree_add_item(trp_tree, hf_thb, tvb, offset, 1, ENC_NA);
offset += 1;
}
/* TMIN */
{
proto_tree_add_item(trp_tree, hf_tmin, tvb, offset, 1, ENC_NA);
offset += 1;
}
/* TMAX */
{
proto_tree_add_item(trp_tree, hf_tmax, tvb, offset, 1, ENC_NA);
offset += 1;
}
/* Epoch */
{
proto_tree_add_item(trp_tree, hf_trp_epoch, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
}
/* SIDg - Type.4 */
{
offset = dof_dissect_pdu_as_field(dissect_2009_11_type_4, tvb, pinfo, trp_tree,
offset, hf_sidg, ett_sidg, NULL);
}
/* Initiator Node Security Scope - Security.10 */
{
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_10, tvb, pinfo, trp_tree,
offset, hf_security_scope, ett_security_scope, NULL);
}
/* Security Mode - Security.13 */
{
gint mode_start = offset;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_13, tvb, pinfo, trp_tree,
offset, hf_security_mode, ett_security_mode, NULL);
if (!packet_data->processed)
{
mode_length = offset - mode_start;
mode = (guint8 *)wmem_alloc0(wmem_packet_scope(), mode_length);
tvb_memcpy(tvb, mode, mode_start, mode_length);
}
}
/* State Identifier - Type.3 */
{
gint s_offset = offset;
gint ssid_len;
proto_item *pi;
offset = read_c4(tvb, offset, &ssid, &ssid_len);
ssid |= AS_ASSIGNED_SSID; /* TRP SSID are *always* assigned by the AS. */
pi = proto_tree_add_uint_format(trp_tree, hf_ssid, tvb, s_offset, offset - s_offset, ssid, "SSID: %u", ssid);
validate_c4(pinfo, pi, ssid, ssid_len);
}
/* PG - Security.2 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_2, tvb, pinfo, trp_tree,
offset, hf_responder_pg, ett_responder_pg, NULL);
/* Group Validation - Security.11 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_11, tvb, pinfo, trp_tree,
offset, hf_responder_validation, ett_responder_validation, NULL);
/* Initiator Validation - Security.11 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_11, tvb, pinfo, trp_tree,
offset, hf_initiator_validation, ett_initiator_validation, NULL);
block_A_length = offset - start_offset;
block_A = (guint8 *)wmem_alloc0(wmem_packet_scope(), block_A_length);
tvb_memcpy(tvb, block_A, start_offset, block_A_length);
}
/* Determine the KEK, if possible. This requires that either the initiator node's secret
* is known or that the group has been configured. In either case this requires knowledge
* from the matching command, including the domain, identity, and group information.
*/
if (packet_data->opid_first && !packet_data->processed)
{
#if 0
trp_packet_data* cmd_data = (trp_packet_data*)dof_packet_get_proto_data(packet_data->opid_first, proto_trp);
guint8 mac[32];
extern struct BlockCipher BlockCipher_AES_256;
struct BlockCipher* cipher = &BlockCipher_AES_256;
guint8* ekey = (guint8*)ep_alloc(cipher->keyStateSize);
int i;
if (cmd_data)
{
guint8 kek[32];
tvb_memcpy(tvb, mac, mac_offset, 32);
tvb_memcpy(tvb, kek, mac_offset + 32, 32);
if (cipher != NULL)
{
cipher->GenerateKeyState(ekey, cmd_data->secret);
cipher->Encrypt(ekey, mac);
cipher->Encrypt(ekey, mac + 16);
}
for (i = 0; i < 32; i++)
kek[i] ^= mac[i];
{
OALSecureHMACContext ctx;
OALSecureHMACDigest digest;
OALSecureHMAC_Start(&ctx, cmd_data->secret);
OALSecureHMAC_Digest(&ctx, cmd_data->domain_length, cmd_data->domain);
OALSecureHMAC_Digest(&ctx, cmd_data->block_I_length, cmd_data->block_I);
OALSecureHMAC_Digest(&ctx, block_A_length, block_A);
OALSecureHMAC_Digest(&ctx, 32, kek);
OALSecureHMAC_Finish(&ctx, digest);
tvb_memcpy(tvb, mac, mac_offset, 32);
if (memcmp(mac, digest, 32) == 0)
{
dof_learned_group_data* group = globals.learned_group_data;
dof_learned_group_auth_data *auth = NULL;
/* The KEK has been discovered, flag this for output on the PDU. */
if (!trp_data)
{
trp_data = wmem_alloc0(wmem_file_scope(), sizeof(trp_packet_data));
dof_packet_add_proto_data(packet_data, proto_trp, trp_data);
}
trp_data->kek_known = TRUE;
while (group)
{
if ((cmd_data->domain_length == group->domain_length) &&
(memcmp(cmd_data->domain, group->domain, group->domain_length) == 0) &&
(cmd_data->group_length == group->group_length) &&
(memcmp(cmd_data->group, group->group, group->group_length) == 0) &&
(ssid == group->ssid))
break;
group = group->next;
}
if (group == NULL)
{
group = wmem_alloc0(wmem_file_scope, sizeof(dof_learned_group_data));
group->domain_length = cmd_data->domain_length;
group->domain = cmd_data->domain;
group->group_length = cmd_data->group_length;
group->group = cmd_data->group;
group->ssid = ssid;
group->next = globals.learned_group_data;
globals.learned_group_data = group;
}
auth = group->keys;
while (auth)
{
if (epoch == auth->epoch)
break;
auth = auth->next;
}
if (auth == NULL)
{
auth = wmem_alloc0(wmem_file_scope(), sizeof(dof_learned_group_auth_data));
auth->epoch = epoch;
auth->next = group->keys;
group->keys = auth;
auth->kek = (guint8*)wmem_alloc0(wmem_file_scope(), 32);
memcpy(auth->kek, kek, 32);
auth->mode_length = mode_length;
auth->mode = (guint8*)wmem_alloc0(wmem_file_scope(), mode_length);
memcpy(auth->mode, mode, mode_length);
auth->security_mode = (mode[1] * 256) | mode[2];
auth->parent = group;
}
}
}
}
#endif
}
}
break;
case TRP_CMD_REQUEST_RANDOM:
{
guint8 *domain_buf = NULL;
guint8 domain_length = 0;
gint start_offset;
if (trp_data && trp_data->identity_length)
{
expert_add_info(pinfo, ti, &ei_trp_initiator_id_known);
}
/* Domain - Security.7 */
start_offset = offset;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_7, tvb, pinfo, trp_tree,
offset, hf_domain, ett_domain, NULL);
if (!packet_data->processed)
{
domain_length = offset - start_offset;
domain_buf = (guint8 *)wmem_alloc0(wmem_packet_scope(), domain_length);
tvb_memcpy(tvb, domain_buf, start_offset, domain_length);
}
/* Initiator Block - TRP.6.1.1 */
{
dof_2008_16_security_4 response;
trp_packet_data *trp_pkt_data = NULL;
start_offset = offset;
/* Initiator Key Request - Security.4 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_4, tvb, pinfo, trp_tree,
offset, hf_initiator_request, ett_initiator_request, &response);
if (!packet_data->processed)
{
tvbuff_t *identity = response.identity;
guint8 identity_length = tvb_reported_length(identity);
guint8 *identity_buf = (guint8 *)wmem_alloc0(wmem_packet_scope(), identity_length);
int i;
/* Get the buffer. */
tvb_memcpy(identity, identity_buf, 0, identity_length);
/* Check to see if there is a matching identity. */
for (i = 0; i < globals.global_security->identity_data_count; i++)
{
dof_identity_data *gidentity = globals.global_security->identity_data + i;
if (domain_length != gidentity->domain_length ||
memcmp(domain_buf, gidentity->domain, domain_length) != 0)
continue;
if (identity_length == gidentity->identity_length &&
memcmp(identity_buf, gidentity->identity, identity_length) == 0)
{
trp_pkt_data = (trp_packet_data *)wmem_alloc0(wmem_file_scope(), sizeof(trp_packet_data));
dof_packet_add_proto_data(packet_data, proto_trp, trp_pkt_data);
trp_pkt_data->domain_length = domain_length;
trp_pkt_data->domain = (guint8 *)wmem_alloc0(wmem_file_scope(), domain_length);
memcpy(trp_pkt_data->domain, domain_buf, domain_length);
trp_pkt_data->identity_length = identity_length;
trp_pkt_data->identity = (guint8 *)wmem_alloc0(wmem_file_scope(), identity_length);
memcpy(trp_pkt_data->identity, identity_buf, identity_length);
trp_pkt_data->secret = gidentity->secret;
}
}
}
if (trp_pkt_data)
{
/* We need to store the entire block_I for later use. */
trp_pkt_data->block_I_length = offset - start_offset;
trp_pkt_data->block_I = (guint8 *)wmem_alloc0(wmem_file_scope(), trp_pkt_data->block_I_length);
tvb_memcpy(tvb, trp_pkt_data->block_I, start_offset, trp_pkt_data->block_I_length);
}
}
}
break;
case TRP_RSP_REQUEST_RANDOM:
{
/* Initiator Ticket - Security.5 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_5, tvb, pinfo, trp_tree,
offset, hf_initiator_ticket, ett_initiator_ticket, NULL);
}
break;
case TRP_CMD_REQUEST_SECURITY_SCOPES:
{
guint8 *domain_buf = NULL;
guint8 domain_length = 0;
gint start_offset;
if (trp_data && trp_data->identity_length)
{
expert_add_info(pinfo, ti, &ei_trp_initiator_id_known);
}
/* Domain - Security.7 */
start_offset = offset;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_7, tvb, pinfo, trp_tree,
offset, hf_domain, ett_domain, NULL);
if (!packet_data->processed)
{
domain_length = offset - start_offset;
domain_buf = (guint8 *)wmem_alloc0(wmem_packet_scope(), domain_length);
tvb_memcpy(tvb, domain_buf, start_offset, domain_length);
}
/* Initiator Block - TRP.5.1.1 */
{
dof_2008_16_security_4 response;
trp_packet_data *trp_pk_data = NULL;
start_offset = offset;
/* Initiator Duration Request */
proto_tree_add_item(trp_tree, hf_trp_duration, tvb, offset, 1, ENC_NA);
offset += 1;
/* Initiator Key Request - Security.4 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_4, tvb, pinfo, trp_tree,
offset, hf_initiator_request, ett_initiator_request, &response);
if (!packet_data->processed)
{
tvbuff_t *identity = response.identity;
guint8 identity_length = tvb_reported_length(identity);
guint8 *identity_buf = (guint8 *)wmem_alloc0(wmem_packet_scope(), identity_length);
int i;
/* Get the buffer. */
tvb_memcpy(identity, identity_buf, 0, identity_length);
/* Check to see if there is a matching identity. */
for (i = 0; i < globals.global_security->identity_data_count; i++)
{
dof_identity_data *gidentity = globals.global_security->identity_data + i;
if (domain_length != gidentity->domain_length ||
memcmp(domain_buf, gidentity->domain, domain_length) != 0)
continue;
if (identity_length == gidentity->identity_length &&
memcmp(identity_buf, gidentity->identity, identity_length) == 0)
{
trp_pk_data = (trp_packet_data *)wmem_alloc0(wmem_file_scope(), sizeof(trp_packet_data));
dof_packet_add_proto_data(packet_data, proto_trp, trp_pk_data);
trp_pk_data->domain_length = domain_length;
trp_pk_data->domain = (guint8 *)wmem_alloc0(wmem_file_scope(), domain_length);
memcpy(trp_pk_data->domain, domain_buf, domain_length);
trp_pk_data->identity_length = identity_length;
trp_pk_data->identity = (guint8 *)wmem_alloc0(wmem_file_scope(), identity_length);
memcpy(trp_pk_data->identity, identity_buf, identity_length);
trp_pk_data->secret = gidentity->secret;
}
}
}
/* Node - Security.8 */
{
gint gid_start = offset;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_8, tvb, pinfo, trp_tree,
offset, hf_node_identifier, ett_node_identifier, NULL);
if (trp_pk_data)
{
trp_pk_data->group_length = offset - gid_start;
trp_pk_data->group = (guint8 *)wmem_alloc0(wmem_file_scope(), trp_pk_data->group_length);
tvb_memcpy(tvb, trp_pk_data->group, gid_start, trp_pk_data->group_length);
}
}
if (trp_pk_data)
{
/* We need to store the entire block_I for later use. */
trp_pk_data->block_I_length = offset - start_offset;
trp_pk_data->block_I = (guint8 *)wmem_alloc0(wmem_file_scope(), trp_pk_data->block_I_length);
tvb_memcpy(tvb, trp_pk_data->block_I, start_offset, trp_pk_data->block_I_length);
}
}
}
break;
case TRP_RSP_REQUEST_SECURITY_SCOPES:
{
gint start_offset;
guint8 *block_A;
guint8 block_A_length;
/* Initiator Ticket - Security.5 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_5, tvb, pinfo, trp_tree,
offset, hf_initiator_ticket, ett_initiator_ticket, NULL);
/* Initialization Block - TRP.5.2.1 */
/* A BLOCK */
{
start_offset = offset;
/* Initiator Duration Request */
proto_tree_add_item(trp_tree, hf_trp_duration, tvb, offset, 1, ENC_NA);
offset += 1;
/* Initiator Node Security Scope - Security.10 */
{
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_10, tvb, pinfo, trp_tree,
offset, hf_security_scope, ett_security_scope, NULL);
}
/* Validation - Security.11 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_11, tvb, pinfo, trp_tree,
offset, hf_initiator_validation, ett_initiator_validation, NULL);
block_A_length = offset - start_offset;
block_A = (guint8 *)wmem_alloc0(wmem_packet_scope(), block_A_length);
tvb_memcpy(tvb, block_A, start_offset, block_A_length);
}
}
break;
case TRP_CMD_RESOLVE_CREDENTIAL:
{
guint8 *domain_buf = NULL;
guint8 domain_length = 0;
gint start_offset;
/* Domain - Security.7 */
start_offset = offset;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_7, tvb, pinfo, trp_tree,
offset, hf_domain, ett_domain, NULL);
if (!packet_data->processed)
{
domain_length = offset - start_offset;
domain_buf = (guint8 *)wmem_alloc0(wmem_packet_scope(), domain_length);
tvb_memcpy(tvb, domain_buf, start_offset, domain_length);
}
/* Identity Resolution - Security.3.2 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_3_2, tvb, pinfo, trp_tree,
offset, hf_identity_resolution, ett_identity_resolution, NULL);
}
break;
case TRP_RSP_RESOLVE_CREDENTIAL:
{
/* Identity Resolution - Security.3.2 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_3_2, tvb, pinfo, trp_tree,
offset, hf_identity_resolution, ett_identity_resolution, NULL);
}
break;
case TRP_CMD_REQUEST_SESSION:
{
guint8 *domain_buf = NULL;
guint8 domain_length = 0;
gint start_offset;
if (trp_data && trp_data->identity_length)
{
expert_add_info(pinfo, ti, &ei_trp_initiator_id_known);
}
/* Domain - Security.7 */
start_offset = offset;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_7, tvb, pinfo, trp_tree,
offset, hf_domain, ett_domain, NULL);
if (!packet_data->processed)
{
domain_length = offset - start_offset;
domain_buf = (guint8 *)wmem_alloc0(wmem_packet_scope(), domain_length);
tvb_memcpy(tvb, domain_buf, start_offset, domain_length);
}
/* Responder Block - Security.6.2 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_6_2, tvb, pinfo, trp_tree,
offset, hf_responder_request, ett_responder_request, NULL);
/* Initiator Block - Security.6.1 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_6_1, tvb, pinfo, trp_tree,
offset, hf_initiator_request, ett_initiator_request, NULL);
}
break;
case TRP_RSP_REQUEST_SESSION:
{
gint start_offset;
guint8 *block_A;
guint8 block_A_length;
/* Responder Ticket - Security.5 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_5, tvb, pinfo, trp_tree,
offset, hf_responder_ticket, ett_responder_ticket, NULL);
/* Initiator Ticket - Security.5 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_5, tvb, pinfo, trp_tree,
offset, hf_initiator_ticket, ett_initiator_ticket, NULL);
/* Initialization Block - Security.6.3 */
/* A BLOCK */
{
start_offset = offset;
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_6_3, tvb, pinfo, trp_tree,
offset, hf_authentication_block, ett_authentication_block, NULL);
block_A_length = offset - start_offset;
block_A = (guint8 *)wmem_alloc0(wmem_packet_scope(), block_A_length);
tvb_memcpy(tvb, block_A, start_offset, block_A_length);
}
}
break;
case TRP_CMD_VALIDATE_CREDENTIAL:
{
tvbuff_t *data_tvb;
/* Domain - Security.7 */
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_7, tvb, pinfo, trp_tree,
offset, hf_domain, ett_domain, NULL);
offset = dof_dissect_pdu_as_field(dissect_2008_16_security_3_1, tvb, pinfo, trp_tree,
offset, hf_identity_resolution, ett_identity_resolution, NULL);
data_tvb = tvb_new_subset_remaining(tvb, offset);
call_dissector(undissected_data_handle, data_tvb, pinfo, trp_tree);
}
break;
case TRP_RSP_VALIDATE_CREDENTIAL:
{
tvbuff_t *data_tvb = tvb_new_subset_remaining(tvb, offset);
call_dissector(undissected_data_handle, data_tvb, pinfo, trp_tree);
}
break;
}
return offset;
}
/* Initialize Core Tunnel Functionality */
static void dof_tun_register(void)
{
static hf_register_info hf[] =
{
{ &hf_2012_1_tunnel_1_version,
{ "Version", "dof.2012_1.tunnel_1.version", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }
},
{ &hf_2012_1_tunnel_1_length,
{ "Length", "dof.2012_1.tunnel_1.length", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }
},
};
static gint *ett[] = {
&ett_2012_1_tunnel,
};
proto_2012_1_tunnel = proto_register_protocol(TUNNEL_PROTOCOL_STACK, "DTPS", "dtps");
proto_register_field_array(proto_2012_1_tunnel, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
register_dissector(TUNNEL_PROTOCOL_STACK, dissect_tunnel_common, proto_2012_1_tunnel);
dof_tun_app_dissectors = register_dissector_table("dof.tunnel.app", "DOF Tunnel Version", proto_2012_1_tunnel, FT_UINT8, BASE_DEC);
}
static void dof_tun_reset(void)
{
}
static void dof_tun_cleanup(void)
{
}
/* The registration hand-off routine */
static void dof_tun_handoff(void)
{
static dissector_handle_t tcp_handle;
register_dissector(TUNNEL_APPLICATION_PROTOCOL, dissect_tun_app_common, proto_2008_1_app);
tcp_handle = create_dissector_handle(dissect_tunnel_tcp, proto_2012_1_tunnel);
dissector_add_uint_with_preference("tcp.port", DOF_TUN_NON_SEC_TCP_PORT, tcp_handle);
}
/* Main DOF Registration Support */
static void dof_reset(void)
{
globals.next_session = 1;
globals.next_transport_session = 1;
globals.dof_packet_head = globals.dof_packet_tail = NULL;
globals.global_security = &global_security;
globals.learned_group_data = NULL;
globals.decrypt_all_packets = decrypt_all_packets;
globals.track_operations = track_operations;
globals.track_operations_window = track_operations_window;
init_addr_port_tables();
/* Reset the packet counter. */
next_dof_frame = 1;
/* Load the template values for different groups. */
{
secmode_field_t *list = secmode_list;
guint i;
global_security.group_data = g_new0(dof_group_data, num_secmode_list);
global_security.group_data_count = num_secmode_list;
for (i = 0; i < num_secmode_list; i++)
{
guint8 kek_len;
dof_group_data *group_data = global_security.group_data + i;
parse_hex_string(list[i].domain, &(group_data->domain), &(group_data->domain_length));
parse_hex_string(list[i].identity, &(group_data->identity), &(group_data->identity_length));
parse_hex_string(list[i].kek, &(group_data->kek), &kek_len);
}
}
/* Load the template values for different secrets. */
{
seckey_field_t *list = seckey_list;
guint i;
/* Clear existing. */
for (i = 0; i < global_security.session_key_count; i++)
{
dof_session_key_data *session_data = &global_security.session_key[i];
g_free(session_data->session_key);
}
g_free(global_security.session_key);
global_security.session_key = NULL;
global_security.session_key_count = 0;
global_security.session_key = g_new0(dof_session_key_data, num_seckey_list);
global_security.session_key_count = num_seckey_list;
for (i = 0; i < num_seckey_list; i++)
{
guint8 key_len;
dof_session_key_data *session_data = global_security.session_key + i;
parse_hex_string(list[i].key, &(session_data->session_key), &key_len);
}
}
/* Load the template values for different identities. */
{
identsecret_field_t *list = identsecret_list;
guint i;
/* Clear existing. */
for (i = 0; i < global_security.identity_data_count; i++)
{
dof_identity_data *identity_data = &global_security.identity_data[i];
g_free(identity_data->domain);
g_free(identity_data->identity);
g_free(identity_data->secret);
}
g_free(global_security.identity_data);
global_security.identity_data = NULL;
global_security.identity_data_count = 0;
global_security.identity_data = g_new0(dof_identity_data, num_identsecret_list);
global_security.identity_data_count = num_identsecret_list;
for (i = 0; i < num_identsecret_list; i++)
{
guint8 key_len;
guint32 size;
dof_identity_data *identity_data = global_security.identity_data + i;
if (VALIDHEX(list[i].domain[0]))
{
parse_hex_string(list[i].domain, &(identity_data->domain), &(identity_data->domain_length));
}
else
{
size = (guint32)strlen(list[i].domain);
dof_oid_new_standard_string(list[i].domain, &size, &(identity_data->domain));
identity_data->domain_length = size;
}
if (VALIDHEX(list[i].identity[0]))
{
parse_hex_string(list[i].identity, &(identity_data->identity), &(identity_data->identity_length));
}
else
{
size = (guint32)strlen(list[i].identity);
dof_oid_new_standard_string(list[i].identity, &size, &(identity_data->identity));
identity_data->identity_length = size;
}
parse_hex_string(list[i].secret, &(identity_data->secret), &key_len);
}
}
}
static void dof_cleanup(void)
{
guint i;
/* Clear existing. */
for (i = 0; i < global_security.group_data_count; i++)
{
dof_group_data *group_data = &global_security.group_data[i];
g_free(group_data->domain);
g_free(group_data->identity);
g_free(group_data->kek);
}
g_free(global_security.group_data);
global_security.group_data = NULL;
global_security.group_data_count = 0;
}
/**
* Initialize Core DPS Functionality
*/
static void dof_register(void)
{
static hf_register_info hf[] =
{
{ &hf_security_1_permission_type,
{ "Permission Type", "dof.2008.16.security.1.desired-duration", FT_UINT16, BASE_DEC, VALS(dof_2008_16_permission_type), 0, NULL, HFILL } },
{ &hf_security_1_length,
{ "Length", "dof.2008.16.security.1.length", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_security_1_data,
{ "Data", "dof.2008.16.security.1.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
/* Security.2 */
{ &hf_security_2_count,
{ "Count", "dof.2008.16.security.2.count", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_security_2_permission,
{ "Permission", "dof.2008.16.security.2.permission", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
/* Security.3.1 */
{ &hf_security_3_1_credential_type,
{ "Credential Type", "dof.2008.16.security.3.1.credential_type", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_security_3_1_stage,
{ "Stage", "dof.2008.16.security.3.1.stage", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_security_3_1_security_node_identifier,
{ "Security Node Identifier", "dof.2008.16.security.3.1.security_node_identifier", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
/* Security 3.2 */
{ &hf_security_3_2_credential_type,
{ "Credential Type", "dof.2008.16.security.3.2.credential_type", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_security_3_2_stage,
{ "Stage", "dof.2008.16.security.3.2.stage", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_security_3_2_length,
{ "Length", "dof.2008.16.security.3.2.length", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_security_3_2_public_data,
{ "Public Data", "dof.2008.16.security.3.2.public_data", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
/* Security.4 */
{ &hf_security_4_l,
{ "L", "dof.2008.16.security.4.l", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL } },
{ &hf_security_4_f,
{ "F", "dof.2008.16.security.4.f", FT_UINT8, BASE_DEC, NULL, 0x40, NULL, HFILL } },
{ &hf_security_4_ln,
{ "Ln", "dof.2008.16.security.4.ln", FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL } },
{ &hf_security_4_identity,
{ "Identity", "dof.2008.16.security.4.identity", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
{ &hf_security_4_nonce,
{ "Nonce", "dof.2008.16.security.4.nonce", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
{ &hf_security_4_permission_set,
{ "Permission Set", "dof.2008.16.security.4.permission_set", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
/* Security.5 */
{ &hf_security_5_mac,
{ "MAC", "dof.2008.16.security.5.mac", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
{ &hf_security_5_key,
{ "KEY", "dof.2008.16.security.5.key", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
/* Security.6.1 */
{ &hf_security_6_1_desired_duration,
{ "Desired Duration", "dof.2008.16.security.6.1.desired_duration", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_security_6_1_desired_security_mode,
{ "Desired Security Mode", "dof.2008.16.security.6.1.desired_security_mode", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
{ &hf_security_6_1_initiator_request,
{ "Initiator Request", "dof.2008.16.security.6.1.initiator_request", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
/* Security.6.2 */
{ &hf_security_6_2_responder_request,
{ "Responder Request", "dof.2008.16.security.6.2.responder_request", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
/* Security.6.3 */
{ &hf_security_6_3_granted_duration,
{ "Granted Duration", "dof.2008.16.security.6.3.granted_duration", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_security_6_3_session_security_scope,
{ "Session Security Scope", "dof.2008.16.security.6.3.session_security_scope", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
{ &hf_security_6_3_initiator_validation,
{ "Initiator Validation", "dof.2008.16.security.6.3.initiator_validation", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
{ &hf_security_6_3_responder_validation,
{ "Responder Validation", "dof.2008.16.security.6.3.responder_validation", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
/* Security.9 */
{ &hf_security_9_length,
{ "Length", "dof.2008.16.security.9.length", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_security_9_initial_state,
{ "Initial State", "dof.2008.16.security.9.initial_state", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
/* Security.10 */
{ &hf_security_10_count,
{ "Count", "dof.2008.16.security.10.count", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_security_10_permission_group_identifier,
{ "Permission Group Identifier", "dof.2008.16.security.10.permission_group_identifier", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
/* Security.11 */
{ &hf_security_11_count,
{ "Count", "dof.2008.16.security.11.count", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_security_11_permission_security_scope,
{ "Permission Security Scope", "dof.2008.16.security.11.permission_security_scope", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
/* Security.12 */
{ &hf_security_12_m,
{ "M", "dof.2008.16.security.12.m", FT_UINT8, BASE_DEC, VALS(dof_2008_16_security_12_m), 0xC0, NULL, HFILL } },
{ &hf_security_12_count,
{ "Count", "dof.2008.16.security.12.count", FT_UINT8, BASE_DEC, NULL, 0x3F, NULL, HFILL } },
{ &hf_security_12_permission_group_identifier,
{ "Permission Group Identifier", "dof.2008.16.security.12.permission_group_identifier", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_2008_1_dof_session_transport,
{ "Transport Session", "dof.transport_session", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
{ &hf_2008_1_dof_session,
{ "DPS Session", "dof.session", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
{ &hf_2008_1_dof_frame,
{ "DPS Frame", "dof.frame", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
{ &hf_2008_1_dof_is_2_node,
{ "DPS Is 2 Node", "dof.is_2_node", FT_BOOLEAN, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
{ &hf_2008_1_dof_is_streaming,
{ "DPS Is Streaming", "dof.is_streaming", FT_BOOLEAN, BASE_DEC, NULL, 0x00, NULL, HFILL }
},
{ &hf_2008_1_dof_is_from_client,
{ "DPS Is From Client", "dof.is_from_client", FT_BOOLEAN, BASE_DEC, NULL, 0x00, NULL, HFILL }
}
};
static gint *ett[] = {
/* Security.2 */
&ett_security_2_permission,
&ett_security_3_1_security_node_identifier,
/* Security.11 */
&ett_security_11_permission_security_scope,
&ett_security_6_1_desired_security_mode,
&ett_security_6_1_initiator_request,
&ett_security_6_2_responder_request,
&ett_security_6_3_session_security_scope,
&ett_security_6_3_initiator_validation,
&ett_security_6_3_responder_validation,
&ett_security_4_identity,
&ett_security_4_permission_set,
&ett_2008_1_dof,
};
static ei_register_info ei[] =
{
#if 0
{ &ei_undecoded, { "dof.undecoded", PI_UNDECODED, PI_WARN, "DOF: Some protocol octets were not decoded", EXPFILL } },
#endif
{ &ei_malformed, { "dof.malformed", PI_MALFORMED, PI_ERROR, "Malformed:", EXPFILL } },
{ &ei_implicit_no_op, { "dof.implicit_no_op", PI_PROTOCOL, PI_COMMENT, "Implicit No-op", EXPFILL } },
{ &ei_c2_c3_c4_format, { "dof.c2_c3_c4_format", PI_MALFORMED, PI_WARN, "DOF: Cx IE format", EXPFILL } },
{ &ei_security_3_1_invalid_stage, { "dof.security.3.1.invalid_stage", PI_MALFORMED, PI_ERROR, "DPS: Security.3.1: Stage invalid.", EXPFILL } },
{ &ei_security_4_invalid_bit, { "dof.security.4.invalid_bit", PI_MALFORMED, PI_WARN, "DPS: Security.4: Reserved bit set.", EXPFILL } },
{ &ei_security_13_out_of_range, { "dof.security.13.out_of_range", PI_MALFORMED, PI_ERROR, "DPS: Security.13: Attribute Data out of range.", EXPFILL } },
};
/* Security mode of operation templates. */
static uat_field_t secmode_uat_fields[] = {
UAT_FLD_CSTRING(secmode_list, domain, "Domain", "The domain, coded as hex digits of PDU Security.7."),
UAT_FLD_CSTRING(secmode_list, identity, "Group ID", "The group identifer, coded as hex digits of PDU Security.8."),
UAT_FLD_CSTRING(secmode_list, kek, "KEK", "The KEK, coded as hex digits representing the KEK (256-bit)."),
UAT_END_FIELDS
};
/* Security keys. */
static uat_field_t seckey_uat_fields[] = {
UAT_FLD_CSTRING(seckey_list, key, "Session Key", "The session key to try to use, coded as hex digits representing the key (256-bit)."),
UAT_END_FIELDS
};
/* Identity secrets. */
static uat_field_t identsecret_uat_fields[] = {
UAT_FLD_CSTRING(identsecret_list, domain, "Domain", "The domain, coded as hex digits of PDU Security.7."),
UAT_FLD_CSTRING(identsecret_list, identity, "Identity", "The group identifer, coded as hex digits of PDU Security.8."),
UAT_FLD_CSTRING_OTHER(identsecret_list, secret, "Secret", identsecret_chk_cb, "The resolved secret for a given identity, coded as hex digits representing the secret (256-bit)."),
UAT_END_FIELDS
};
module_t *dof_module;
uat_t *secmode_uat;
uat_t *seckey_uat;
uat_t *identsecret_uat;
char *uat_load_err;
expert_module_t *expert_security;
dsp_option_dissectors = register_dissector_table("dof.dsp.options", "DSP Protocol Options", proto_2008_1_dsp, FT_UINT32, BASE_DEC);
dof_sec_dissectors = register_dissector_table("dof.secmode", "DOF Security Mode of Operation", proto_2008_1_dof, FT_UINT16, BASE_DEC);
register_dissector_table("dof.2008.1", "DOF Common PDU", proto_2008_1_dof, FT_STRING, BASE_DEC);
proto_2008_1_dof = proto_register_protocol(DOF_PROTOCOL_STACK, "DOF", "dof");
proto_2008_1_dof_tcp = proto_register_protocol(DOF_PROTOCOL_STACK" TCP", "DOF-TCP", "dof-tcp");
proto_2008_1_dof_udp = proto_register_protocol(DOF_PROTOCOL_STACK" UDP", "DOF-UDP", "dof-udp");
proto_register_field_array(proto_2008_1_dof, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_security = expert_register_protocol(proto_2008_1_dof);
expert_register_field_array(expert_security, ei, array_length(ei));
dof_module = prefs_register_protocol(proto_2008_1_dof, dof_reset);
secmode_uat = uat_new((const char *)"DPS Security Mode Templates",
(size_t)sizeof(secmode_field_t),
(const char *)"custom_dof_secmode_list",
(gboolean)TRUE,
(void **)&secmode_list,
(guint *)&num_secmode_list,
(guint)(UAT_AFFECTS_DISSECTION | UAT_AFFECTS_FIELDS),
(const char *)NULL,
(uat_copy_cb_t)secmode_list_copy_cb,
(uat_update_cb_t)secmode_list_update_cb,
(uat_free_cb_t)secmode_list_free_cb,
(uat_post_update_cb_t)secmode_list_post_update_cb,
(uat_field_t *)secmode_uat_fields
);
seckey_uat = uat_new((const char *)"DPS Session Keys",
(size_t)sizeof(seckey_field_t),
(const char *)"custom_dof_seckey_list",
(gboolean)TRUE,
(void **)&seckey_list,
(guint *)&num_seckey_list,
(guint)(UAT_AFFECTS_DISSECTION | UAT_AFFECTS_FIELDS),
(const char *)NULL,
(uat_copy_cb_t)seckey_list_copy_cb,
(uat_update_cb_t)seckey_list_update_cb,
(uat_free_cb_t)seckey_list_free_cb,
(uat_post_update_cb_t)seckey_list_post_update_cb,
(uat_field_t *)seckey_uat_fields
);
identsecret_uat = uat_new((const char *)"DPS Identity Secrets",
(size_t)sizeof(identsecret_field_t),
(const char *)"custom_dof_identsecret_list",
(gboolean)TRUE,
(void **)&identsecret_list,
(guint *)&num_identsecret_list,
(guint)(UAT_AFFECTS_DISSECTION | UAT_AFFECTS_FIELDS),
(const char *)NULL,
(uat_copy_cb_t)identsecret_list_copy_cb,
(uat_update_cb_t)identsecret_list_update_cb,
(uat_free_cb_t)identsecret_list_free_cb,
(uat_post_update_cb_t)identsecret_list_post_update_cb,
(uat_field_t *)identsecret_uat_fields
);
prefs_register_bool_preference(dof_module, "custom_dof_decrypt_all",
"Attempt to decrypt all packets",
"Specifies that decryption should be attempted on all packets, even if the session initialization wasn't captured.",
&decrypt_all_packets);
prefs_register_bool_preference(dof_module, "custom_dof_track_operations",
"Track DPS operations",
"Specifies that operations should be tracked across multiple packets, providing summary lists. This takes time and memory.",
&track_operations);
prefs_register_uint_preference(dof_module, "custom_dof_track_operations_window",
"Track DPS window",
"Limits the number of operations shown before and after the current operations",
10, &track_operations_window);
prefs_register_static_text_preference(dof_module, "name4567", "The following are tables not preferences.", "These tables are not controlled by OK, Apply, and Cancel of this dialog.");
prefs_register_uat_preference(dof_module, "custom_dof_secmode_list", "DPS Security Mode Templates",
"A table of security modes and initialization data that will be tried if no security mode is found.",
secmode_uat);
prefs_register_uat_preference(dof_module, "custom_dof_seckey_list", "DPS Session Keys",
"A table of session keys to attempt if none is known.",
seckey_uat);
prefs_register_uat_preference(dof_module, "custom_dof_identsecret_list", "DPS Identity Secrets",
"A table of secrets for different identities.",
identsecret_uat);
uat_load(secmode_uat, &uat_load_err);
uat_load(seckey_uat, &uat_load_err);
uat_load(identsecret_uat, &uat_load_err);
}
static void dof_handoff(void)
{
static dissector_handle_t tcp_handle;
dof_oid_handle = register_dissector(DOF_OBJECT_IDENTIFIER, dissect_2009_11_type_4, oid_proto);
tcp_handle = create_dissector_handle(dissect_dof_tcp, proto_2008_1_dof);
dof_udp_handle = create_dissector_handle(dissect_dof_udp, proto_2008_1_dof);
undissected_data_handle = find_dissector("data");
dissector_add_uint_with_preference("tcp.port", DOF_P2P_NEG_SEC_TCP_PORT, tcp_handle);
dissector_add_uint_range_with_preference("udp.port", DOF_NEG_SEC_UDP_PORT_RANGE, dof_udp_handle);
}
/* OID Registration Support */
static void oid_reset(void)
{
}
static void oid_cleanup(void)
{
}
/* Initialize OID */
static void oid_register(void)
{
static hf_register_info hf[] = {
{ &hf_oid_class,
{ "Class", "dof.oid.class", FT_UINT32, BASE_DEC, NULL, 0, "DPS Object Identifier Class", HFILL }
},
{ &hf_oid_header,
{ "Header", "dof.oid.header", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
},
{ &hf_oid_attribute,
{ "Attribute", "dof.oid.attribute", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL }
},
{ &hf_oid_length,
{ "Length", "dof.oid.length", FT_UINT8, BASE_DEC, NULL, 0x3F, NULL, HFILL }
},
{ &hf_oid_data,
{ "Data", "dof.oid.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }
},
{ &hf_oid_all_attribute_data,
{ "Attribute Data", "dof.oid.attribute-data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }
},
{ &hf_oid_attribute_header,
{ "Header", "dof.attribute.header", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
},
{ &hf_oid_attribute_attribute,
{ "Attribute", "dof.attribute.attribute", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL }
},
{ &hf_oid_attribute_id,
{ "ID", "dof.attribute.id", FT_UINT8, BASE_DEC, NULL, 0x7F, NULL, HFILL }
},
{ &hf_oid_attribute_length,
{ "Length", "dof.attribute.length", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }
},
{ &hf_oid_attribute_data,
{ "Data", "dof.attribute.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }
},
{ &hf_oid_attribute_oid,
{ "OID", "dof.attribute.oid", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }
},
};
static gint *ett[] = {
&ett_oid,
&ett_oid_header,
&ett_oid_attribute,
&ett_oid_attribute_header,
&ett_oid_attribute_oid,
};
static ei_register_info ei[] =
{
{ &ei_type_4_header_zero, { "dof.oid.header_zero", PI_MALFORMED, PI_ERROR, "DOF Violation: Type.4: Header bit mandated 0.", EXPFILL } },
};
if (oid_proto == -1)
{
expert_module_t *expert_oid;
oid_proto = proto_register_protocol(DOF_OBJECT_IDENTIFIER, "DPS.OID", "dof.oid");
proto_register_field_array(oid_proto, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_oid = expert_register_protocol(oid_proto);
expert_register_field_array(expert_oid, ei, array_length(ei));
}
}
static void oid_handoff(void)
{
}
/* DNP Registration Support */
static guint dof_ns_session_key_hash_fn(gconstpointer key)
{
const dof_ns_session_key *session_key = (const dof_ns_session_key *)key;
guint result = 0;
result += g_int_hash(&session_key->transport_session_id);
result += g_int_hash(&session_key->client);
result += g_int_hash(&session_key->server);
return result;
}
static gboolean dof_ns_session_key_equal_fn(gconstpointer key1, gconstpointer key2)
{
const dof_ns_session_key *session_key_ptr1 = (const dof_ns_session_key *)key1;
const dof_ns_session_key *session_key_ptr2 = (const dof_ns_session_key *)key2;
if (session_key_ptr1->transport_session_id != session_key_ptr2->transport_session_id)
return FALSE;
if (session_key_ptr1->client != session_key_ptr2->client)
return FALSE;
if (session_key_ptr1->server != session_key_ptr2->server)
return FALSE;
return TRUE;
}
static void dof_dnp_reset(void)
{
dof_ns_session_lookup = g_hash_table_new_full(dof_ns_session_key_hash_fn, dof_ns_session_key_equal_fn, g_free, NULL);
}
static void dof_dnp_cleanup(void)
{
g_hash_table_destroy(dof_ns_session_lookup);
dof_ns_session_lookup = NULL;
}
static void dof_register_dnp_0(void)
{
static hf_register_info hf[] =
{
{ &hf_2008_1_dnp_0_1_1_padding,
{ "Padding", "dof.dnp.v0.padding", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }
},
{ &hf_2008_1_dnp_0_1_1_version,
{ "Version", "dof.dnp.v0.version", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
},
};
if (proto_2008_1_dnp_0 == -1)
{
proto_2008_1_dnp_0 = proto_register_protocol(DOF_NETWORK_PROTOCOL " V0", "DPS.DNP.V0", "dof.dnp.v0");
proto_register_field_array(proto_2008_1_dnp_0, hf, array_length(hf));
}
}
/**
* The registration hand-off routine
*/
static void dof_reg_handoff_dnp_0(void)
{
dissector_handle_t dnp_handle;
dnp_handle = create_dissector_handle(dissect_dnp_0, proto_2008_1_dnp_0);
dissector_add_uint("dof.dnp", 0, dnp_handle);
}
static void dof_register_dnp_1(void)
{
expert_module_t *expert_dnp;
static hf_register_info hf[] =
{
{ &hf_2009_9_dnp_1_flags,
{ "Flags", "dof.2009_9.dnp_1.flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
},
{ &hf_2009_9_dnp_1_flag_length,
{ "Length Size", "dof.2009_9.dnp_1.flags.lengthsize", FT_UINT8, BASE_DEC, NULL, 0x03, NULL, HFILL }
},
{ &hf_2009_9_dnp_1_flag_srcport,
{ "Source Port", "dof.2009_9.dnp_1.flags.srcport", FT_UINT8, BASE_DEC, NULL, 0x04, NULL, HFILL }
},
{ &hf_2009_9_dnp_1_flag_dstport,
{ "Destination Port", "dof.2009_9.dnp_1.flags.dstport", FT_UINT8, BASE_DEC, NULL, 0x08, NULL, HFILL }
},
{ &hf_2009_9_dnp_1_length,
{ "Length", "dof.2009_9.dnp_1.length", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_2009_9_dnp_1_srcport,
{ "Source Port", "dof.2009_9.dnp_1.srcport", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_2009_9_dnp_1_dstport,
{ "Destination Port", "dof.2009_9.dnp_1.dstport", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
};
static gint *ett[] =
{
&ett_2009_9_dnp_1_flags,
};
static ei_register_info ei[] =
{
{ &ei_dof_10_flags_zero, { "dof.dnp.v1.flags_zero", PI_UNDECODED, PI_ERROR, "DPS-10: Reserved flag bits must be zero.", EXPFILL } },
#if 0
{ &ei_dof_13_length_specified, { "dof.dnp.v1.length_specified", PI_UNDECODED, PI_ERROR, "DPS-13: Length must be specified on a connection.", EXPFILL } },
#endif
};
if (proto_2009_9_dnp_1 == -1)
{
proto_2009_9_dnp_1 = proto_register_protocol(DOF_NETWORK_PROTOCOL " V1", "DOF.DNP.V1", "dof.dnp.v1");
proto_register_field_array(proto_2009_9_dnp_1, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_dnp = expert_register_protocol(proto_2009_9_dnp_1);
expert_register_field_array(expert_dnp, ei, array_length(ei));
}
}
/**
* The registration hand-off routine
*/
static void dof_reg_handoff_dnp_1(void)
{
dissector_handle_t dnp_handle, dnp_frame_handle;
dnp_handle = create_dissector_handle(dissect_dnp_1, proto_2009_9_dnp_1);
dnp_frame_handle = create_dissector_handle(determine_packet_length_1, proto_2009_9_dnp_1);
dissector_add_uint("dof.dnp", 1, dnp_handle);
dissector_add_uint("dof.dnp.frame", 1, dnp_frame_handle);
}
static void dof_dnp_handoff(void)
{
dof_reg_handoff_dnp_0();
dof_reg_handoff_dnp_1();
}
/**
* Initialize Core DNP Functionality
*/
static void dof_dnp_register(void)
{
static hf_register_info hf[] =
{
{ &hf_2008_1_dnp_1_flag,
{ "Flag", "dof.2008_1.dnp_1.flag", FT_BOOLEAN, 8, TFS(&tfs_present_not_present), 0x80, NULL, HFILL }
},
{ &hf_2008_1_dnp_1_version,
{ "Version", "dof.2008_1.dnp_1.version", FT_UINT8, BASE_DEC, NULL, 0x7F, NULL, HFILL }
},
};
static gint *ett[] =
{
&ett_2008_1_dnp,
&ett_2008_1_dnp_header,
};
proto_2008_1_dnp = proto_register_protocol(DOF_NETWORK_PROTOCOL, "DPS.DNP", "dof.dnp");
proto_register_field_array(proto_2008_1_dnp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
dnp_dissectors = register_dissector_table("dof.dnp", "DOF DNP Version", proto_2008_1_dnp, FT_UINT8, BASE_DEC);
dnp_framing_dissectors = register_dissector_table("dof.dnp.frame", "DOF DNP Framing", proto_2008_1_dnp, FT_UINT8, BASE_DEC);
dof_register_dnp_0();
dof_register_dnp_1();
}
/* DPP Registration Support */
/**
* This routine is called each time the system is reset (file load, capture)
* and so it should take care of freeing any of our persistent stuff.
*/
static void dof_dpp_reset(void)
{
dpp_reset_opid_support();
dpp_reset_sid_support();
}
static void dof_dpp_cleanup(void)
{
}
static void dof_register_dpp_0(void)
{
static hf_register_info hf[] =
{
{ &hf_2008_1_dpp_0_1_1_version,
{ "Version", "dof.dpp.v0.version", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
},
};
if (proto_2008_1_dpp_0 == -1)
{
proto_2008_1_dpp_0 = proto_register_protocol(DOF_PRESENTATION_PROTOCOL " V0", "DPS.DPP.V0", "dof.dpp.v0");
proto_register_field_array(proto_2008_1_dpp_0, hf, array_length(hf));
}
}
/**
* The registration hand-off routine
*/
static void dof_reg_handoff_dpp_0(void)
{
dissector_handle_t dpp_handle;
dpp_handle = create_dissector_handle(dissect_dpp_0, proto_2008_1_dpp_0);
dissector_add_uint("dof.dpp", 0, dpp_handle);
}
static void dof_register_dpp_2(void)
{
expert_module_t *expert_dpp;
static hf_register_info hf[] =
{
{ &hf_2009_12_dpp_2_1_flags,
{ "Flags", "dof.dpp.v2.flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }
},
{ &hf_2009_12_dpp_2_1_flag_security,
{ "Secure", "dof.dpp.v2.flags.security", FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL }
},
{ &hf_2009_12_dpp_2_1_flag_opid,
{ "Operation ID Type", "dof.dpp.v2.flags.opidtype", FT_UINT8, BASE_DEC, VALS(strings_2009_12_dpp_opid_types), 0x60, NULL, HFILL } },
{ &hf_2009_12_dpp_2_1_flag_cmdrsp,
{ "Command/Response", "dof.dpp.v2.flags.cmdrsp", FT_BOOLEAN, 8, TFS(&tfs_response_command), 0x10, NULL, HFILL } },
{ &hf_2009_12_dpp_2_1_flag_seq,
{ "Sequence", "dof.dpp.v2.flags.sequence", FT_BOOLEAN, 8, TFS(&tfs_present_not_present), 0x04, NULL, HFILL } },
{ &hf_2009_12_dpp_2_1_flag_retry,
{ "Retry", "dof.dpp.v2.flags.retry", FT_BOOLEAN, 8, TFS(&tfs_present_not_present), 0x02, NULL, HFILL } },
{ &hf_2009_12_dpp_2_3_sec_flags,
{ "Flags", "dof.dpp.v2.security.flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
{ &hf_2009_12_dpp_2_3_sec_flag_secure,
{ "Security Mode Header", "dof.dpp.v2.security.flags.securitymodeheader", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL } },
{ &hf_2009_12_dpp_2_3_sec_flag_rdid,
{ "Remote Domain ID", "dof.dpp.v2.security.flags.rdid", FT_UINT8, BASE_DEC, NULL, 0x08, NULL, HFILL } },
{ &hf_2009_12_dpp_2_3_sec_flag_partition,
{ "Partition Present", "dof.dpp.v2.security.flags.partition", FT_UINT8, BASE_DEC, NULL, 0x04, NULL, HFILL } },
{ &hf_2009_12_dpp_2_3_sec_flag_ssid,
{ "SSID Present", "dof.dpp.v2.security.flags.ssid", FT_UINT8, BASE_DEC, NULL, 0x01, NULL, HFILL } },
{ &hf_2009_12_dpp_2_3_sec_flag_as,
{ "AS Present", "dof.dpp.v2.security.flags.as", FT_UINT8, BASE_DEC, NULL, 0x02, NULL, HFILL } },
{ &hf_2009_12_dpp_2_3_sec_ssid,
{ "Security State Identifier", "dof.dpp.v2.security.ssid", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_2009_12_dpp_2_3_sec_rdid,
{ "Remote Domain Identifier", "dof.dpp.v2.security.rdid", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_2009_12_dpp_2_3_sec_remote_partition,
{ "Remote Security Scope", "dof.dpp.v2.security.remote-scope", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_2009_12_dpp_2_3_sec_partition,
{ "Security Scope", "dof.dpp.v2.security.scope", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_2009_12_dpp_2_1_opcnt,
{ "Operation Count", "dof.dpp.v2.opcnt", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_2009_12_dpp_2_1_seq,
{ "Sequence", "dof.dpp.v2.sequence", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_2009_12_dpp_2_1_retry,
{ "Retry", "dof.dpp.v2.retry", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_2009_12_dpp_2_1_delay,
{ "Delay", "dof.dpp.v2.delay", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
};
static hf_register_info shf[] =
{
{ &hf_2009_12_dpp_2_14_opcode,
{ "Opcode", "dof.dpp.v2s.opcode", FT_UINT8, BASE_DEC, VALS(strings_2009_12_dpp_common_opcodes), 0x0, NULL, HFILL } },
};
static gint *ett[] =
{
&ett_2009_12_dpp_2_1_flags,
&ett_2009_12_dpp_2_opid,
&ett_2009_12_dpp_2_opid_history,
&ett_2009_12_dpp_2_3_security,
&ett_2009_12_dpp_2_3_sec_flags,
&ett_2009_12_dpp_2_3_sec_remote_partition,
&ett_2009_12_dpp_2_3_sec_partition,
};
static ei_register_info ei[] =
{
{ &ei_dpp2_dof_10_flags_zero, { "dof.dpp.v2.flags_zero", PI_UNDECODED, PI_ERROR, "DPS-10: Reserved flag bits must be zero.", EXPFILL } },
{ &ei_dpp_default_flags, { "dof.dpp.v2.flags_included", PI_COMMENTS_GROUP, PI_NOTE, "Default flag value is included explicitly.", EXPFILL } },
{ &ei_dpp_explicit_sender_sid_included, { "dof.dpp.v2.sender_sid_included", PI_COMMENT, PI_NOTE, "Explicit SID could be optimized, same as sender.", EXPFILL } },
{ &ei_dpp_explicit_receiver_sid_included, { "dof.dpp.v2.receiver_sid_included", PI_COMMENT, PI_NOTE, "Explicit SID could be optimized, same as receiver.", EXPFILL } },
#ifdef LIBGCRYPT_OK
{ &ei_dpp_no_security_context, { "dof.dpp.v2.no_context", PI_UNDECODED, PI_WARN, "No security context to enable packet decryption.", EXPFILL } },
#else
{ &ei_dpp_no_security_context, { "dof.dpp.v2.no_context", PI_UNDECODED, PI_WARN, "This version of wireshark was built without DOF decryption capability", EXPFILL } },
#endif
};
static gint *sett[] =
{
&ett_2009_12_dpp_common,
};
if (proto_2009_12_dpp == -1)
{
proto_2009_12_dpp = proto_register_protocol(DOF_PRESENTATION_PROTOCOL " V2", "DPS.DPP.V2", "dof.dpp.v2");
proto_register_field_array(proto_2009_12_dpp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
if (proto_2009_12_dpp_common == -1)
{
proto_2009_12_dpp_common = proto_register_protocol(DOF_PRESENTATION_PROTOCOL " V2 Support", "DPS.DPP.V2S", "dof.dpp.v2s");
proto_register_field_array(proto_2009_12_dpp, shf, array_length(shf));
proto_register_subtree_array(sett, array_length(sett));
expert_dpp = expert_register_protocol(proto_2009_12_dpp);
expert_register_field_array(expert_dpp, ei, array_length(ei));
}
}
/**
* The registration hand-off routine
*/
static void dof_reg_handoff_dpp_2(void)
{
dissector_handle_t dpp_handle;
dpp_handle = create_dissector_handle(dissect_dpp_2, proto_2009_12_dpp);
dissector_add_uint("dof.dpp", 2, dpp_handle);
}
/**
* Initialize Core DPP Functionality
*/
static void dof_dpp_register(void)
{
static hf_register_info hf[] =
{
{ &hf_2008_1_dpp_sid_num,
{ "SID ID", "dof.dpp.v2.sid-id", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_2008_1_dpp_sid_str,
{ "SID", "dof.dpp.v2.sid", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_2008_1_dpp_rid_num,
{ "RID ID", "dof.dpp.v2.rid-id", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_2008_1_dpp_rid_str,
{ "RID", "dof.dpp.v2.rid", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_2008_1_dpp_first_command,
{ "First Operation", "dof.dpp.v2.first-operation", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_2008_1_dpp_last_command,
{ "Last Operation", "dof.dpp.v2.last-operation", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_2008_1_dpp_first_response,
{ "First Response", "dof.dpp.v2.first-response", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_2008_1_dpp_last_response,
{ "Last Response", "dof.dpp.v2.last-response", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_2008_1_dpp_related_frame,
{ "Related Frame", "dof.dpp.v2.related-frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_2008_1_dpp_1_flag,
{ "Flags", "dof.dpp.flag", FT_BOOLEAN, 8, TFS(&tfs_present_not_present), 0x80, NULL, HFILL }
},
{ &hf_2008_1_dpp_1_version,
{ "Version", "dof.dpp.version", FT_UINT8, BASE_DEC, NULL, 0x7F, NULL, HFILL }
},
};
static gint *ett[] =
{
&ett_2008_1_dpp,
&ett_2008_1_dpp_1_header,
};
static ei_register_info ei[] =
{
{ &ei_dof_6_timeout, { "dof.dpp.timeout", PI_PROTOCOL, PI_ERROR, "DOF Violation: DPS.6: Negotiation not complete within 10 seconds.", EXPFILL } },
};
if (proto_2008_1_dpp == -1)
{
expert_module_t *expert_dpp;
proto_2008_1_dpp = proto_register_protocol(DOF_PRESENTATION_PROTOCOL, "DPS.DPP", "dof.dpp");
proto_register_field_array(proto_2008_1_dpp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
dof_dpp_dissectors = register_dissector_table("dof.dpp", "DOF DPP Version", proto_2008_1_dpp, FT_UINT8, BASE_DEC);
expert_dpp = expert_register_protocol(proto_2008_1_dpp);
expert_register_field_array(expert_dpp, ei, array_length(ei));
}
dof_register_dpp_0();
dof_register_dpp_2();
}
static void dof_dpp_handoff(void)
{
dof_reg_handoff_dpp_0();
dof_reg_handoff_dpp_2();
}
/* General Application Registration Support */
static void app_reset(void)
{
}
static void app_cleanup(void)
{
}
/**
* Initialize Core DPP Functionality
*/
static void app_register(void)
{
if (proto_2008_1_app == -1)
{
proto_2008_1_app = proto_register_protocol(DOF_APPLICATION_PROTOCOL, "DPS.APP", "dof.app");
app_dissectors = register_dissector_table("dof.app", "DOF APP Version", proto_2008_1_app, FT_UINT16, BASE_DEC);
}
}
static void app_handoff(void)
{
}
/* DSP Registration Support */
static void dof_dsp_reset(void)
{
}
static void dof_dsp_cleanup(void)
{
}
static void dof_register_dsp_0(void)
{
static hf_register_info hf[] =
{
{ &hf_2008_1_app_version,
{ "APPID", "dof.app.v0.appid", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }
},
{ &hf_2008_1_dsp_12_opcode,
{ "Opcode", "dof.dsp.opcode", FT_UINT8, BASE_DEC, VALS(strings_2008_1_dsp_opcodes), 0x0, NULL, HFILL } },
{ &hf_2008_1_dsp_attribute_code,
{ "Attribute Code", "dof.dsp.avp.attribute-code", FT_UINT8, BASE_DEC, VALS(strings_2008_1_dsp_attribute_codes), 0x00, NULL, HFILL } },
{ &hf_2008_1_dsp_attribute_data,
{ "Attribute Data", "dof.dsp.avp.attribute-data", FT_UINT16, BASE_HEX, NULL, 0x00, NULL, HFILL } },
{ &hf_2008_1_dsp_value_length,
{ "Value Length", "dof.dsp.avp.value-length", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_2008_1_dsp_value_data,
{ "Value Data", "dof.dsp.avp.value-data", FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL } },
};
static gint *ett[] =
{
&ett_2008_1_dsp_12,
&ett_2008_1_dsp_12_options,
&ett_2008_1_dsp_12_option,
};
proto_2008_1_dsp = proto_register_protocol("DOF Session Protocol", "DOF.ESP", "dof.esp");
proto_register_field_array(proto_2008_1_dsp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
/**
* The registration hand-off routine
*/
static void dof_reg_handoff_dsp_0(void)
{
dissector_handle_t dsp_handle = create_dissector_handle(dissect_dsp, proto_2008_1_dsp);
dissector_add_uint("dof.app", 0, dsp_handle);
}
static void dof_dsp_register(void)
{
dof_register_dsp_0();
}
static void dof_dsp_handoff(void)
{
dof_reg_handoff_dsp_0();
}
/* CCM Registration Support */
static void dof_ccm_reset(void)
{
}
static void dof_ccm_cleanup(void)
{
}
static void dof_register_ccm_24577(void)
{
expert_module_t *expert_ccm;
static hf_register_info hfdsp[] =
{
{ &hf_ccm_dsp_option,
{ "CCM Security Mode", "dof.ccm.dsp_opt", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_ccm_dsp_strength_count,
{ "CCM Strength Count", "dof.ccm.strength-count", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_ccm_dsp_strength,
{ "CCM Strength", "dof.ccm.strength", FT_UINT8, BASE_DEC, VALS(ccm_strengths), 0x0, NULL, HFILL } },
{ &hf_ccm_dsp_e_flag,
{ "CCM Minimum Encrypt", "dof.ccm.encrypt.min", FT_BOOLEAN, 8, TFS(&tfs_encrypt_do_not_encrypt), 0x80, NULL, HFILL } },
{ &hf_ccm_dsp_m_flag,
{ "CCM Maximum Encrypt", "dof.ccm.encrypt.max", FT_BOOLEAN, 8, TFS(&tfs_encrypt_do_not_encrypt), 0x40, NULL, HFILL } },
{ &hf_ccm_dsp_tmax,
{ "CCM Maximum MAC", "dof.ccm.mac.max", FT_UINT8, BASE_DEC, NULL, 0x38, NULL, HFILL } },
{ &hf_ccm_dsp_tmin,
{ "CCM Minimum MAC", "dof.ccm.mac.min", FT_UINT8, BASE_DEC, NULL, 0x07, NULL, HFILL } },
};
static hf_register_info hf[] =
{
{ &hf_ccm_opcode,
{ "Opcode", "dof.ccm.opcode", FT_UINT8, BASE_DEC, VALS(ccm_opcode_strings), 0x0, NULL, HFILL } },
};
static gint *ett[] =
{
&ett_ccm_dsp_option,
&ett_ccm_dsp,
&ett_ccm,
};
static hf_register_info hfheader[] =
{
{ &hf_epp_v1_ccm_flags,
{ "Flags", "dof.epp.v1.ccm.flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
{ &hf_epp_v1_ccm_flags_manager,
{ "Manager", "dof.epp.v1.ccm.flags.manager", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL } },
{ &hf_epp_v1_ccm_flags_period,
{ "Period", "dof.epp.v1.ccm.flags.period", FT_UINT8, BASE_DEC, NULL, 0x70, NULL, HFILL } },
{ &hf_epp_v1_ccm_flags_target,
{ "Target", "dof.epp.v1.ccm.flags.target", FT_UINT8, BASE_DEC, NULL, 0x08, NULL, HFILL } },
{ &hf_epp_v1_ccm_flags_next_nid,
{ "Next Node Identifier", "dof.epp.v1.ccm.flags.next-nid", FT_UINT8, BASE_DEC, NULL, 0x02, NULL, HFILL } },
{ &hf_epp_v1_ccm_flags_packet,
{ "Packet", "dof.epp.v1.ccm.flags.packet", FT_UINT8, BASE_DEC, NULL, 0x01, NULL, HFILL } },
{ &hf_epp_v1_ccm_nid,
{ "Node ID", "dof.epp.v1.ccm.nodeid", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_epp_v1_ccm_slot,
{ "Slot", "dof.epp.v1.ccm.slot", FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_epp_v1_ccm_pn,
{ "Packet", "dof.epp.v1.ccm.packet", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_epp_v1_ccm_tnid,
{ "Target Node ID", "dof.epp.v1.ccm.target", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_epp_v1_ccm_nnid,
{ "Next Node ID", "dof.epp.v1.ccm.nnid", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
};
static gint *ettheader[] =
{
&ett_epp_v1_ccm_flags,
&ett_header,
};
static ei_register_info ei[] =
{
{ &ei_decode_failure, { "dof.ccm.decode_failure", PI_UNDECODED, PI_WARN, "Failure to decrypt packet.", EXPFILL } },
};
/* No Configuration options to register? */
proto_ccm_app = proto_register_protocol("DOF CCM Security Mode App", "DOF.CCM.APP", "dof.ccm.app");
proto_ccm = proto_register_protocol("DOF CCM Security Mode of Operation", "DOF.CCM", "dof.ccm");
proto_ccm_dsp = proto_register_protocol("DOF CCM Security Mode DSP Options", "DOF.CCM.DSP", "dof.ccm.dsp");
proto_register_field_array(proto_ccm_app, hf, array_length(hf));
proto_register_field_array(proto_ccm_dsp, hfdsp, array_length(hfdsp));
proto_register_subtree_array(ett, array_length(ett));
proto_register_field_array(proto_ccm, hfheader, array_length(hfheader));
proto_register_subtree_array(ettheader, array_length(ettheader));
expert_ccm = expert_register_protocol(proto_ccm);
expert_register_field_array(expert_ccm, ei, array_length(ei));
}
/**
* The registration hand-off routine
*/
static void dof_reg_handoff_ccm_24577(void)
{
static dissector_handle_t ccm_app_handle;
static dissector_handle_t dsp_handle;
static dissector_handle_t ccm_handle;
ccm_app_handle = create_dissector_handle(dissect_ccm_app, proto_ccm_app);
dsp_handle = create_dissector_handle(dissect_ccm_dsp, proto_ccm_dsp);
ccm_handle = create_dissector_handle(dissect_ccm, proto_ccm);
dissector_add_uint("dof.app", DOF_PROTOCOL_CCM, ccm_app_handle);
dissector_add_uint("dof.dsp.options", DSP_CCM_FAMILY | DOF_PROTOCOL_CCM, dsp_handle);
dissector_add_uint("dof.secmode", DOF_PROTOCOL_CCM, ccm_handle);
}
static void dof_ccm_register(void)
{
dof_register_ccm_24577();
}
static void dof_ccm_handoff(void)
{
dof_reg_handoff_ccm_24577();
}
/* OAP Registration Support */
static void dof_oap_reset(void)
{
/* The value is not allocated, so does not need to be freed. */
oap_1_alias_to_binding = g_hash_table_new_full(oap_1_alias_hash_func, oap_1_alias_equal_func, NULL, NULL);
}
static void dof_oap_cleanup(void)
{
g_hash_table_destroy(oap_1_alias_to_binding);
oap_1_alias_to_binding = NULL;
}
static void dof_register_oap_1(void)
{
expert_module_t *expert_oap;
static hf_register_info hfdsp[] =
{
{ &hf_oap_1_dsp_option,
{ "Object Access Protocol", "dof.oap.dsp_opt", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
};
static hf_register_info hf[] =
{
{ &hf_oap_1_opcode,
{ "Opcode", "dof.oap.opcode", FT_UINT8, BASE_DEC, VALS(oap_opcode_strings), 0x1F, NULL, HFILL } },
{ &hf_oap_1_alias_size,
{ "Alias Length", "dof.oap.aliaslen", FT_UINT8, BASE_DEC, NULL, 0xC0, NULL, HFILL } },
{ &hf_oap_1_flags,
{ "Flags", "dof.oap.flags", FT_UINT8, BASE_DEC, NULL, 0x20, NULL, HFILL } },
{ &hf_oap_1_exception_internal_flag,
{ "Internal Exception", "dof.oap.exception.internal", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL } },
{ &hf_oap_1_exception_final_flag,
{ "Final Exception", "dof.oap.exception.final", FT_UINT8, BASE_DEC, NULL, 0x40, NULL, HFILL } },
{ &hf_oap_1_exception_provider_flag,
{ "Exception Provider", "dof.oap.exception.provider", FT_UINT8, BASE_DEC, NULL, 0x20, NULL, HFILL } },
{ &hf_oap_1_cmdcontrol,
{ "Command Control", "dof.oap.cmdcontrol", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
{ &hf_oap_1_cmdcontrol_cache_flag,
{ "Cache Delay Flag", "dof.oap.cmdcontrol.flag.cache", FT_UINT8, BASE_HEX, NULL, 0x40, NULL, HFILL } },
{ &hf_oap_1_cmdcontrol_cache,
{ "Cache Delay", "dof.oap.cmdcontrol.cache", FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL } },
{ &hf_oap_1_cmdcontrol_verbosity_flag,
{ "Verbosity Flag", "dof.oap.cmdcontrol.flag.verbosity", FT_UINT8, BASE_HEX, NULL, 0x30, NULL, HFILL } },
{ &hf_oap_1_cmdcontrol_noexecute_flag,
{ "No Execute Flag", "dof.oap.cmdcontrol.flag.noexecute", FT_UINT8, BASE_HEX, NULL, 0x08, NULL, HFILL } },
{ &hf_oap_1_cmdcontrol_ack_flag,
{ "Ack List Flag", "dof.oap.cmdcontrol.flag.ack", FT_UINT8, BASE_HEX, NULL, 0x04, NULL, HFILL } },
{ &hf_oap_1_cmdcontrol_ackcnt,
{ "Ack List Count", "dof.oap.cmdcontrol.ackcnt", FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL } },
{ &hf_oap_1_cmdcontrol_ack,
{ "Ack", "dof.oap.cmdcontrol.ack", FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL } },
{ &hf_oap_1_cmdcontrol_delay_flag,
{ "Execution Delay Flag", "dof.oap.cmdcontrol.flag.delay", FT_UINT8, BASE_HEX, NULL, 0x02, NULL, HFILL } },
{ &hf_oap_1_cmdcontrol_heuristic_flag,
{ "Heuristic Flag", "dof.oap.cmdcontrol.flag.heuristic", FT_UINT8, BASE_HEX, NULL, 0x01, NULL, HFILL } },
{ &hf_oap_1_cmdcontrol_heuristic,
{ "Heuristic", "dof.oap.cmdcontrol.heuristic", FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL } },
{ &hf_oap_1_providerid,
{ "Provider ID", "dof.oap.provider-id", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_oap_1_objectid,
{ "Object ID", "dof.oap.object-id", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_oap_1_interfaceid,
{ "Interface ID", "dof.oap.interface-id", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_oap_1_itemid,
{ "Item ID", "dof.oap.item-id", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
#if 0 /* not used yet */
{ &hf_oap_1_distance,
{ "Distance", "dof.oap.distance", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
#endif
{ &hf_oap_1_alias,
{ "Alias", "dof.oap.alias", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_oap_1_alias_frame,
{ "Alias Frame", "dof.oap.alias-frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
#if 0 /* not used yet */
{ &hf_oap_1_opinfo_start_frame,
{ "Command Frame", "dof.oap.command-frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_oap_1_opinfo_end_frame,
{ "Response Frame", "dof.oap.response-frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_oap_1_opinfo_timeout,
{ "Operation Timeout", "dof.oap.opid.timeout", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL } },
#endif
{ &hf_oap_1_subscription_delta,
{ "Minimum Delta", "dof.oap.subscription.min-delta", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_oap_1_update_sequence,
{ "Sequence", "dof.oap.sequence", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_oap_1_value_list,
{ "OAP Value List", "dof.oap.value_list", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
};
static gint *ett[] =
{
&ett_oap_1_dsp,
&ett_oap_1_dsp_options,
&ett_oap_1,
&ett_oap_1_opinfo,
&ett_oap_1_cmdcontrol,
&ett_oap_1_cmdcontrol_flags,
&ett_oap_1_cmdcontrol_ack,
&ett_oap_1_alias,
&ett_oap_1_objectid,
&ett_oap_1_1_providerid,
};
static ei_register_info ei[] =
{
{ &ei_oap_no_session, { "dof.oap.no_session", PI_PROTOCOL, PI_ERROR, "Session not found", EXPFILL } },
};
proto_oap_1 = proto_register_protocol("DOF Object Access Protocol", "DOF.OAP", "dof.oap");
proto_oap_1_dsp = proto_register_protocol("DOF Object Access Protocol DSP Options", "DOF.OAP.DSP", "dof.oap.dsp");
proto_register_field_array(proto_oap_1, hf, array_length(hf));
proto_register_field_array(proto_oap_1_dsp, hfdsp, array_length(hfdsp));
proto_register_subtree_array(ett, array_length(ett));
expert_oap = expert_register_protocol(proto_oap_1);
expert_register_field_array(expert_oap, ei, array_length(ei));
}
/**
* The registration hand-off routine
*/
static void dof_reg_handoff_oap_1(void)
{
dissector_handle_t oap_handle = create_dissector_handle(dissect_oap, proto_oap_1);
dissector_handle_t dsp_handle = create_dissector_handle(dissect_oap_dsp, proto_oap_1_dsp);
dissector_add_uint("dof.app", DOF_PROTOCOL_OAP_1, oap_handle);
dissector_add_uint("dof.dsp.options", DSP_OAP_FAMILY | DOF_PROTOCOL_OAP_1, dsp_handle);
}
static void dof_oap_register(void)
{
dof_register_oap_1();
}
static void dof_oap_handoff(void)
{
dof_reg_handoff_oap_1();
}
/* SGMP Registration Support */
void dof_register_sgmp_130(void);
void dof_reg_handoff_sgmp_130(void);
static void dof_sgmp_reset(void)
{
}
static void dof_sgmp_cleanup(void)
{
}
void dof_register_sgmp_130(void)
{
static hf_register_info hf[] =
{
{ &hf_opcode,
{ "Opcode", "dof.sgmp.v1.opcode", FT_UINT8, BASE_DEC, VALS(sgmp_opcode_strings), 0x0, NULL, HFILL } },
{ &hf_sgmp_domain,
{ "Domain", "dof.sgmp.v1.domain", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
{ &hf_sgmp_epoch,
{ "Epoch", "dof.sgmp.v1.epoch", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
{ &hf_initiator_block,
{ "Initiator Block", "dof.sgmp.v1.initiator-block", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
{ &hf_sgmp_security_scope,
{ "Security Scope", "dof.sgmp.v1.security-scope", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
{ &hf_initial_state,
{ "Initial State", "dof.sgmp.v1.initial-state", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
{ &hf_latest_version,
{ "Latest SGMP Version", "dof.sgmp.v1.latest-sgmp-version", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
{ &hf_desire,
{ "Desire", "dof.sgmp.v1.desire", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
{ &hf_ticket,
{ "Ticket", "dof.sgmp.v1.ticket", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
{ &hf_sgmp_tmin,
{ "TMIN", "dof.sgmp.v1.tmin", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
{ &hf_tie_breaker,
{ "Tie Breaker", "dof.sgmp.v1.tie-breaker", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
{ &hf_delay,
{ "Delay", "dof.sgmp.v1.delay", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
{ &hf_key,
{ "Key", "dof.sgmp.v1.key", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
};
static gint *ett[] =
{
&ett_sgmp,
&ett_sgmp_domain,
&ett_initiator_block,
&ett_sgmp_security_scope,
&ett_initial_state,
&ett_ticket,
};
proto_sgmp = proto_register_protocol("DOF Secure Group Management Protocol", "DOF.SGMP", "dof.sgmp");
proto_register_field_array(proto_sgmp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
/**
* The registration hand-off routine
*/
void dof_reg_handoff_sgmp_130(void)
{
dissector_handle_t sgmp_handle = create_dissector_handle(dissect_sgmp, proto_sgmp);
dissector_add_uint("dof.app", DOF_PROTOCOL_SGMP, sgmp_handle);
}
static void dof_sgmp_register(void)
{
dof_register_sgmp_130();
}
static void dof_sgmp_handoff(void)
{
dof_reg_handoff_sgmp_130();
}
/* TEP Registration Support */
static void dof_tep_reset(void)
{
}
static void dof_tep_cleanup(void)
{
}
static void dof_register_tep_128(void)
{
static hf_register_info hfdsp[] =
{
{ &hf_dsp_option,
{ "Ticket Exchange Protocol Version 1", "dof.tep1.dsp_opt", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
};
static hf_register_info hf[] =
{
{ &hf_tep_operation,
{ "Operation", "dof.tep1.operation", FT_UINT8, BASE_DEC, VALS(tep_opcode_strings), 0x00, NULL, HFILL } },
{ &hf_tep_operation_type,
{ "Operation Type", "dof.tep1.operation_type", FT_BOOLEAN, 8, TFS(&tep_optype_vals), TEP_OPCODE_RSP, NULL, HFILL } },
{ &hf_tep_opcode,
{ "Opcode", "dof.tep1.opcode", FT_UINT8, BASE_DEC, VALS(tep_opcode_strings), 0x0F, NULL, HFILL } },
{ &hf_tep_k,
{ "K", "dof.tep1.k", FT_UINT8, BASE_DEC, NULL, 0x10, NULL, HFILL } },
{ &hf_tep_c,
{ "C", "dof.tep1.c", FT_UINT8, BASE_DEC, NULL, 0x20, NULL, HFILL } },
{ &hf_tep_reject_code,
{ "Code", "dof.tep1.reject.code", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_tep_reject_data,
{ "Data", "dof.tep1.reject.data", FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL } },
/* TEP.2.1 */
{ &hf_tep_2_1_domain,
{ "Domain", "dof.2008.4.tep1.2.1.domain", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_tep_2_1_initiator_block,
{ "Initiator Block", "dof.2008.4.tep1.2.1.initiator_block", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
{ &hf_tep_2_1_ticket_confirmation,
{ "Ticket Confirmation", "dof.2008.4.tep1.2.1.ticket_confirmation", FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL } },
/* TEP.2.2 */
{ &hf_tep_2_2_initiator_ticket,
{ "Initiator Ticket", "dof.2008.4.tep1.2.2.initiator_ticket", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
{ &hf_tep_2_2_ticket_confirmation,
{ "Ticket Confirmation", "dof.2008.4.tep1.2.2.ticket_confirmation", FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL } },
{ &hf_tep_2_2_responder_initialization,
{ "Responder Initialization", "dof.2008.4.tep1.2.2.responder_initialization", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
{ &hf_tep_2_2_responder_block,
{ "Responder Block", "dof.2008.4.tep1.2.2.responder_block", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
{ &hf_tep_2_2_authenticator_initialization,
{ "Authenticator Initialization", "dof.2008.4.tep1.2.2.authenticator_initialization", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
/* TEP.2.2.1 */
{ &hf_tep_2_2_1_state_identifier,
{ "State Identifier", "dof.2008.4.tep1.2.2.1.state_identifier", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_tep_2_2_1_initial_state,
{ "Initial State", "dof.2008.4.tep1.2.2.1.initial_state", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
{ &hf_tep_session_key,
{ "Session Key", "dof.session_key", FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL } },
};
static gint *ett[] =
{
&ett_tep_dsp,
&ett_tep_dsp_options,
&ett_tep,
&ett_tep_operation,
&ett_tep_2_1_domain,
&ett_tep_2_1_initiator_block,
&ett_tep_2_2_initiator_ticket,
&ett_tep_2_2_responder_initialization,
&ett_tep_2_2_responder_block,
&ett_tep_2_2_authenticator_initialization,
&ett_tep_2_2_1_initial_state,
};
/* module_t *tep_module;*/
/* No Configuration options to register? */
proto_tep = proto_register_protocol("DOF Ticket Exchange Protocol Version 1", "DOF.TEP1", "dof.tep1");
proto_tep_dsp = proto_register_protocol("DOF Ticket Exchange Protocol DSP Options", "DOF.TEP1.DSP", "dof.tep1.dsp");
proto_register_field_array(proto_tep, hf, array_length(hf));
proto_register_field_array(proto_tep_dsp, hfdsp, array_length(hfdsp));
proto_register_subtree_array(ett, array_length(ett));
/* tep_module = prefs_register_protocol( proto_tep, NULL );*/
}
/**
* The registration hand-off routine
*/
static void dof_reg_handoff_tep_128(void)
{
dissector_handle_t tep_handle = create_dissector_handle(dissect_tep, proto_tep);
dissector_handle_t dsp_handle = create_dissector_handle(dissect_tep_dsp, proto_tep_dsp);
dissector_add_uint("dof.app", DOF_PROTOCOL_TEP, tep_handle);
dissector_add_uint("dof.dsp.options", DSP_TEP_FAMILY | DOF_PROTOCOL_TEP, dsp_handle);
}
static void dof_tep_register(void)
{
dof_register_tep_128();
}
static void dof_tep_handoff(void)
{
dof_reg_handoff_tep_128();
}
/* TRP Registration Support */
static void dof_trp_reset(void)
{
}
static void dof_trp_cleanup(void)
{
}
static void dof_register_trp_129(void)
{
expert_module_t *expert_trp;
static hf_register_info hfdsp[] =
{
{ &hf_trp_dsp_option,
{ "Ticket Request Protocol", "dof.trp.dsp_opt", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
};
static hf_register_info hf[] =
{
{ &hf_trp_opcode,
{ "Opcode", "dof.trp.opcode", FT_UINT8, BASE_DEC, VALS(trp_opcode_strings), 0x0, NULL, HFILL } },
{ &hf_domain,
{ "Domain", "dof.trp.domain", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_identity_resolution,
{ "Identity Resolution", "dof.trp.identity_resolution", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_initiator_request,
{ "Initiator Request", "dof.trp.initiator_request", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_responder_request,
{ "Responder Request", "dof.trp.responder_request", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_initiator_ticket,
{ "Initiator Ticket", "dof.trp.initiator_ticket", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_responder_ticket,
{ "Responder Ticket", "dof.trp.responder_ticket", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_authentication_block,
{ "Authentication Block", "dof.trp.authentication_block", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_group_identifier,
{ "Group Identifier", "dof.trp.group_identifier", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_node_identifier,
{ "Node Identifier", "dof.trp.node_identifier", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_thb,
{ "Thb", "dof.trp.thb", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_tmin,
{ "Tmin", "dof.trp.tmin", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_tmax,
{ "Tmax", "dof.trp.tmax", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_trp_epoch,
{ "Epoch", "dof.trp.epoch", FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL } },
{ &hf_sidg,
{ "SIDg", "dof.trp.sid_g", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_security_scope,
{ "Security Scope", "dof.trp.security_scope", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_security_mode,
{ "Security Mode", "dof.trp.security_mode", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_ssid,
{ "SSID", "dof.trp.ssid", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
#if 0 /* not used yet */
{ &hf_initiator_pg,
{ "Initiator Permissions", "dof.trp.initiator_pg", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
#endif
{ &hf_initiator_validation,
{ "Initiator Validation", "dof.trp.initiator_validation", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_responder_pg,
{ "Responder Permissions", "dof.trp.responder_pg", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_responder_validation,
{ "Responder Validation", "dof.trp.responder_validation", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_errorcode,
{ "Error Code", "dof.trp.errorcode", FT_UINT8, BASE_DEC, VALS(trp_error_strings), 0x0, NULL, HFILL } },
{ &hf_trp_duration,
{ "Duration", "dof.trp.duration", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
#if 0 /* not used yet */
{ &hf_trp_rnonce,
{ "Requestor Nonce", "dof.trp.rnonce", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_pnonce,
{ "Provider Nonce", "dof.trp.pnonce", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_reqid,
{ "Requestor ID", "dof.trp.reqid", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_provid,
{ "Provider ID", "dof.trp.provid", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_perm_count,
{ "Permission Count", "dof.trp.perm.count", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_perm_type,
{ "Permission Type", "dof.trp.perm.type", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_perm_rflags,
{ "Requestor SRP Flags", "dof.trp.rflags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_perm_rcache,
{ "Requestor SRP Cache", "dof.trp.rcache", FT_BOOLEAN, 8, NULL, 0x2, NULL, HFILL } },
{ &hf_trp_perm_rsrp,
{ "Requestor SRP", "dof.trp.rsrp", FT_BOOLEAN, 8, NULL, 0x1, NULL, HFILL } },
{ &hf_trp_perm_rsrp_a,
{ "Requestor SRP A", "dof.trp.rsrp.a", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_perm_rsrp_u,
{ "Requestor SRP u", "dof.trp.rsrp.u", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_perm_pflags,
{ "Provider SRP Flags", "dof.trp.pflags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_perm_pcache,
{ "Provider SRP Cache", "dof.trp.pcache", FT_BOOLEAN, 8, NULL, 0x2, NULL, HFILL } },
{ &hf_trp_perm_psrp,
{ "Provider SRP", "dof.trp.psrp", FT_BOOLEAN, 8, NULL, 0x1, NULL, HFILL } },
{ &hf_trp_perm_psrp_a,
{ "Provider SRP A", "dof.trp.psrp.a", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_perm_psrp_u,
{ "Provider SRP u", "dof.trp.psrp.u", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_perm_psrp_b,
{ "Provider SRP B", "dof.trp.psrp.b", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_perm_psrp_s,
{ "Provider SRP S", "dof.trp.psrp.s", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_confirmation,
{ "Confirmation", "dof.trp.confirmation", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_perm_pke,
{ "Provider Key Expression", "dof.trp.pke", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_trp_perm_pka,
{ "Provider Key Authenticator", "dof.trp.pka", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
#endif
};
static gint *ett[] =
{
&ett_trp_dsp,
&ett_trp,
&ett_domain,
&ett_identity_resolution,
&ett_initiator_request,
&ett_initiator_ticket,
&ett_responder_request,
&ett_responder_ticket,
&ett_authentication_block,
&ett_group_identifier,
&ett_node_identifier,
&ett_sidg,
&ett_security_scope,
&ett_security_mode,
&ett_initiator_pg,
&ett_initiator_validation,
&ett_responder_pg,
&ett_responder_validation,
&ett_trp_permset,
&ett_srp_flags,
&ett_trp_ticket,
};
static ei_register_info ei[] =
{
{ &ei_trp_initiator_id_known, { "dof.trp.initiator_id_known", PI_PROTOCOL, PI_COMMENT, "Initiator identity known", EXPFILL } },
{ &ei_trp_kek_discovered, { "dof.trp.kek_discovered", PI_PROTOCOL, PI_COMMENT, "KEK discovered", EXPFILL } },
};
/* No Configuration options to register? */
proto_trp = proto_register_protocol("DOF Ticket Request Protocol", "DOF.TRP", "dof.trp");
proto_trp_dsp = proto_register_protocol("DOF Ticket Request Protocol DSP Options", "DOF.TRP.DSP", "dof.trp.dsp");
proto_register_field_array(proto_trp, hf, array_length(hf));
proto_register_field_array(proto_trp_dsp, hfdsp, array_length(hfdsp));
proto_register_subtree_array(ett, array_length(ett));
expert_trp = expert_register_protocol(proto_trp);
expert_register_field_array(expert_trp, ei, array_length(ei));
}
/**
* The registration hand-off routine
*/
static void dof_reg_handoff_trp_129(void)
{
dissector_handle_t trp_handle = create_dissector_handle(dissect_trp, proto_trp);
dissector_handle_t dsp_handle = create_dissector_handle(dissect_trp_dsp, proto_trp_dsp);
dissector_add_uint("dof.app", DOF_PROTOCOL_TRP, trp_handle);
dissector_add_uint("dof.dsp.options", DSP_TRP_FAMILY | DOF_PROTOCOL_TRP, dsp_handle);
}
static void dof_trp_register(void)
{
dof_register_trp_129();
}
static void dof_trp_handoff(void)
{
dof_reg_handoff_trp_129();
}
/* Wireshark Dissector Registration Proper */
/**
* This is called only during reset (file load, reload, etc.).
*/
static void dof_reset_routine(void)
{
dof_tun_reset();
dof_reset();
oid_reset();
dof_dnp_reset();
dof_dpp_reset();
app_reset();
dof_dsp_reset();
dof_ccm_reset();
dof_oap_reset();
dof_sgmp_reset();
dof_tep_reset();
dof_trp_reset();
}
static void dof_cleanup_routine(void)
{
dof_tun_cleanup();
dof_cleanup();
oid_cleanup();
dof_dnp_cleanup();
dof_dpp_cleanup();
app_cleanup();
dof_dsp_cleanup();
dof_ccm_cleanup();
dof_oap_cleanup();
dof_sgmp_cleanup();
dof_tep_cleanup();
dof_trp_cleanup();
}
/**
* This is the first entry point into the dissector, called on program launch.
*/
void proto_register_dof(void)
{
dof_tun_register();
dof_register();
oid_register();
dof_dnp_register();
dof_dpp_register();
app_register();
dof_dsp_register();
dof_ccm_register();
dof_oap_register();
dof_sgmp_register();
dof_tep_register();
dof_trp_register();
register_init_routine(&dof_reset_routine);
register_cleanup_routine(&dof_cleanup_routine);
}
/**
* This routine is called after initialization and whenever the preferences are changed.
*/
void proto_reg_handoff_dof(void)
{
dof_tun_handoff();
dof_handoff();
oid_handoff();
dof_dnp_handoff();
dof_dpp_handoff();
app_handoff();
dof_dsp_handoff();
dof_ccm_handoff();
dof_oap_handoff();
dof_sgmp_handoff();
dof_tep_handoff();
dof_trp_handoff();
}
/**
* Protocol-specific data attached to a conversation_t structure - protocol
* index and opaque pointer.
*/
typedef struct _dof_proto_data {
int proto;
void *proto_data;
} dof_proto_data;
static gint p_compare(gconstpointer a, gconstpointer b)
{
const dof_proto_data *ap = (const dof_proto_data *)a;
const dof_proto_data *bp = (const dof_proto_data *)b;
if (ap->proto > bp->proto)
return 1;
else if (ap->proto == bp->proto)
return 0;
else
return -1;
}
#if 0 /* TODO not used yet */
static void dof_session_add_proto_data(dof_session_data *session, int proto, void *proto_data)
{
dof_proto_data *p1 = wmem_new0(wmem_packet_scope(), dof_proto_data);
p1->proto = proto;
p1->proto_data = proto_data;
/* Add it to the list of items for this conversation. */
session->data_list = g_slist_insert_sorted(session->data_list, (gpointer *)p1, p_compare);
}
static void *dof_session_get_proto_data(dof_session_data *session, int proto)
{
dof_proto_data temp, *p1;
GSList *item;
temp.proto = proto;
temp.proto_data = NULL;
item = g_slist_find_custom(session->data_list, (gpointer *)&temp,
p_compare);
if (item != NULL)
{
p1 = (dof_proto_data *)item->data;
return p1->proto_data;
}
return NULL;
}
static void dof_session_delete_proto_data(dof_session_data *session, int proto)
{
dof_proto_data temp;
GSList *item;
temp.proto = proto;
temp.proto_data = NULL;
item = g_slist_find_custom(session->data_list, (gpointer *)&temp,
p_compare);
while (item)
{
session->data_list = g_slist_remove(session->data_list, item->data);
item = item->next;
}
}
#endif
static void dof_packet_add_proto_data(dof_packet_data *packet, int proto, void *proto_data)
{
dof_proto_data *p1 = wmem_new0(wmem_file_scope(), dof_proto_data);
p1->proto = proto;
p1->proto_data = proto_data;
/* Add it to the list of items for this conversation. */
packet->data_list = g_slist_insert_sorted(packet->data_list, (gpointer *)p1, p_compare);
}
static void *dof_packet_get_proto_data(dof_packet_data *packet, int proto)
{
dof_proto_data temp, *p1;
GSList *item;
temp.proto = proto;
temp.proto_data = NULL;
item = g_slist_find_custom(packet->data_list, (gpointer *)&temp,
p_compare);
if (item != NULL)
{
p1 = (dof_proto_data *)item->data;
return p1->proto_data;
}
return NULL;
}
#if 0 /* TODO not used yet */
static void dof_packet_delete_proto_data(dof_packet_data *packet, int proto)
{
dof_proto_data temp;
GSList *item;
temp.proto = proto;
temp.proto_data = NULL;
item = g_slist_find_custom(packet->data_list, (gpointer *)&temp,
p_compare);
while (item)
{
packet->data_list = g_slist_remove(packet->data_list, item->data);
item = item->next;
}
}
#endif
static gint dof_dissect_pdu_as_field(dissector_t dissector, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, int item, int ett, void *result)
{
int block_length;
tvbuff_t *start = tvb_new_subset(tvb, offset, -1, -1);
proto_tree *my_tree;
proto_item *ti = proto_tree_add_item(tree, item, tvb, offset, -1, ENC_NA);
my_tree = proto_item_add_subtree(ti, ett);
block_length = dof_dissect_pdu(dissector, start, pinfo, my_tree, result);
return offset + block_length;
}
static gint dof_dissect_pdu(dissector_t dissector, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *result)
{
gint len = dissector(tvb, pinfo, tree, result);
proto_item_set_len(proto_tree_get_parent(tree), len);
return len;
}
static int dof_dissect_dnp_length(tvbuff_t *tvb, packet_info *pinfo, guint8 version, gint *offset)
{
dissector_handle_t dp;
dp = dissector_get_uint_handle(dnp_framing_dissectors, version);
if (!dp)
return -1;
return call_dissector_only(dp, tvb, pinfo, NULL, offset);
}
/*
* Editor modelines - http://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:
*/