New protocol support for Lawo EmberPlus which is Glow embedded in S101

Bug: 14718
Change-Id: I7ad6503634eb6bc98cee20ef069db3156a6a1e1e
Reviewed-on: https://code.wireshark.org/review/27247
Reviewed-by: Pascal Quantin <pascal.quantin@gmail.com>
This commit is contained in:
Gilles Dufour 2018-05-02 08:56:09 +02:00 committed by Pascal Quantin
parent e020ccab49
commit 1c7b101b35
9 changed files with 3434 additions and 2 deletions

View File

@ -69,9 +69,9 @@ TPM 2.0 protocol
PROXY (v2) protocol
Ruby Marshal format
Ruby Distributed protocol
--
GSM-R protocol (User-to-User Information Element usage)
--
S101 Lawo Emberplus transport frame
GLOW Lawo Emberplus Data format
STCSIG (Spirent Test Center Signature decoding for Ethernet and FibreChannel, disabled by default)
--

View File

@ -96,6 +96,7 @@ set(CLEAN_ASN1_DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-ess.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-f1ap.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-ftam.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-glow.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-goose.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-gsm_map.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-HI2Operations.c
@ -1598,6 +1599,7 @@ set(DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-rudp.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-rwall.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-rx.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-s101.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-s5066sis.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-s5066dts.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-s7comm.c

View File

@ -34,6 +34,7 @@ set(ASN1_SRC_DIRS
ess
f1ap
ftam
glow
goose
gprscdr
gsm_map

View File

@ -0,0 +1,34 @@
# CMakeLists.txt
#
# Wireshark - Network traffic analyzer
# By Gerald Combs <gerald@wireshark.org>
# Copyright 1998 Gerald Combs
#
# SPDX-License-Identifier: GPL-2.0-or-later
#
set( PROTOCOL_NAME glow )
set( PROTO_OPT )
set( EXT_ASN_FILE_LIST
)
set( ASN_FILE_LIST
${PROTOCOL_NAME}.asn
)
set( EXTRA_DIST
${ASN_FILE_LIST}
packet-${PROTOCOL_NAME}-template.c
${PROTOCOL_NAME}.cnf
)
set( SRC_FILES
${EXTRA_DIST}
${EXT_ASN_FILE_LIST}
)
set( A2W_FLAGS -b )
ASN2WRS()

View File

@ -0,0 +1,621 @@
--
-- GlowDtd.asn1
-- Lawo GmbH
--
-- This file defines the Glow DTD used with the Ember+ protocol.
--
-- Change Log:
--
-- 2.50:
-- - Added nullable parameter values.
-- 2.40:
-- - NOTE: This version describes the data schema of Ember+ 1.4.
-- - Added Template Extensions
-- 2.30:
-- - NOTE: This version describes the data schema of Ember+ 1.3.
-- - Added Schema Extensions
-- 2.20:
-- - NOTE: This version describes the data schema of Ember+ 1.2.
-- - Added Function Extensions (see type Function)
-- 2.10:
-- - NOTE: This version describes the data schema of Ember+ 1.1.
-- - Added Matrix Extensions (see type Matrix)
-- - Added "isOnline" field to NodeContents
-- 2.5:
-- - NOTE: This version describes the data schema of Ember+ 1.0.
-- - NOTE: This version introduces breaking changes!
-- - Changed Parameter.isCommand (BOOLEAN) to an enumeration named "type".
-- To determine the effective type of a parameter, follow this rule:
-- - If the parameter has the "type" field that equals "trigger", its
-- type is "trigger".
-- - If the parameter has either the "enumeration" or the "enumMap" field,
-- its type is "enum".
-- - If the parameter has the "value" field, its type corresponds to the
-- BER type of the value.
-- - If the parameter has the "type" field, its type is the value of this
-- field.
-- This is useful for parameters that do not specify a current value -
-- e.g. "trigger" parameters or parameters that have write-only access.
-- - Changed Parameter.isWriteable (BOOLEAN) to an enumeration named
-- "access".
-- - More options for Value - now also supports OCTET STRING and BOOLEAN
-- - Introduces QualifiedParameter and QualifiedNode types
-- - Introduces RootElement and RootElementCollection types:
-- At the root level, a different set of supported types is available.
-- - StreamCollection can also be used as root container.
-- - Introduces the StreamDescription type and the field "streamDescriptor"
-- in type ParameterContents.
-- 2.4:
-- - NOTE: This version introduces breaking changes!
-- - moved "children" in Parameter and Node out of
-- "contents" SET.
-- 2.3:
-- - Added size constraints for INTEGER values.
-- - Renamed EnumEntry to StringIntegerPair
-- - Renamed EnumCollection to StringIntegerCollection
-- 2.2:
-- - Added new field "enumMap" to Parameter and types to describe
-- enum entries: EnumEntry and EnumCollection
-- 2.1:
-- - NOTE: This version introduces breaking changes!
-- - Replaced all APPLICATION tags for fields with CONTEXT-SPECIFIC tags
-- APPLICATION tags are only used for custom types now.
-- 2.0:
-- Initial Release
--
EmberPlus-Glow DEFINITIONS EXPLICIT TAGS ::= BEGIN
-- ======================================================
--
-- Primitive Types
--
-- ======================================================
EmberString ::= UTF8String
Integer32 ::= INTEGER (-2147483648 .. 2147483647)
Integer64 ::= INTEGER (-9223372036854775808 .. 9223372036854775807)
-- this is the base oid for all RELATIVE-OID values defined in this document.
-- when using the RELATIVE-OID type, defining a base oid is required by ASN.1.
-- does not have any impact upon the DTD.
baseOid OBJECT IDENTIFIER ::= { iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) lsb(37411) lsb-mgmt(2) emberPlus(1) glow(1) glowVolatile(100) }
-- ======================================================
--
-- Template
--
-- ======================================================
Template ::=
[APPLICATION 24] IMPLICIT SET {
number [0] Integer32,
element [1] TemplateElement OPTIONAL,
description [2] EmberString OPTIONAL
}
QualifiedTemplate ::=
[APPLICATION 25] IMPLICIT SET {
path [0] RELATIVE-OID,
element [1] TemplateElement OPTIONAL,
description [2] EmberString OPTIONAL
}
TemplateElement ::=
CHOICE {
parameter Parameter,
node Node,
matrix Matrix,
function Function
}
-- ======================================================
--
-- Parameter
--
-- ======================================================
Parameter ::=
[APPLICATION 1] IMPLICIT
SEQUENCE {
number [0] Integer32,
contents [1] ParameterContents OPTIONAL,
children [2] ElementCollection OPTIONAL
}
QualifiedParameter ::=
[APPLICATION 9] IMPLICIT
SEQUENCE {
path [0] RELATIVE-OID,
contents [1] ParameterContents OPTIONAL,
children [2] ElementCollection OPTIONAL
}
ParameterContents ::=
SET {
identifier [ 0] EmberString OPTIONAL,
description [ 1] EmberString OPTIONAL,
value [ 2] Value OPTIONAL,
minimum [ 3] MinMax OPTIONAL,
maximum [ 4] MinMax OPTIONAL,
access [ 5] ParameterAccess OPTIONAL,
format [ 6] EmberString OPTIONAL,
enumeration [ 7] EmberString OPTIONAL,
factor [ 8] Integer32 OPTIONAL,
isOnline [ 9] BOOLEAN OPTIONAL,
formula [10] EmberString OPTIONAL,
step [11] Integer32 OPTIONAL,
default [12] Value OPTIONAL,
type [13] ParameterType OPTIONAL,
streamIdentifier [14] Integer32 OPTIONAL,
enumMap [15] StringIntegerCollection OPTIONAL,
streamDescriptor [16] StreamDescription OPTIONAL,
schemaIdentifiers[17] EmberString OPTIONAL,
templateReference[18] RELATIVE-OID OPTIONAL
}
Value ::=
CHOICE {
integer Integer64,
real REAL,
string EmberString,
boolean BOOLEAN,
octets OCTET STRING,
null NULL
}
MinMax ::=
CHOICE {
integer Integer64,
real REAL,
null NULL
}
ParameterType ::=
INTEGER {
null (0),
integer (1),
real (2),
string (3),
boolean (4),
trigger (5),
enum (6),
octets (7)
}
ParameterAccess ::=
INTEGER {
none (0),
read (1), -- default
write (2),
readWrite (3)
}
StringIntegerPair ::=
[APPLICATION 7] IMPLICIT
SEQUENCE {
entryString [0] EmberString,
entryInteger [1] Integer32
}
StringIntegerCollection ::=
[APPLICATION 8] IMPLICIT
SEQUENCE OF [0] StringIntegerPair
StreamDescription ::=
[APPLICATION 12] IMPLICIT
SEQUENCE {
streamFormat [0] StreamFormat,
offset [1] Integer32 -- byte offset of the value in the streamed blob.
}
-- type: 0=uint, 1=int, 2=float
-- size: 0=1byte, 1=2byte, 2=4byte, 3=8byte
-- endianness: 0=big, 1=little
StreamFormat ::=
INTEGER {
unsignedInt8 ( 0), -- 00000 00 0
unsignedInt16BigEndian ( 2), -- 00000 01 0
unsignedInt16LittleEndian ( 3), -- 00000 01 1
unsignedInt32BigEndian ( 4), -- 00000 10 0
unsignedInt32LittleEndian ( 5), -- 00000 10 1
unsignedInt64BigEndian ( 6), -- 00000 11 0
unsignedInt64LittleEndian ( 7), -- 00000 11 1
signedInt8 ( 8), -- 00001 00 0
signedInt16BigEndian (10), -- 00001 01 0
signedInt16LittleEndian (11), -- 00001 01 1
signedInt32BigEndian (12), -- 00001 10 0
signedInt32LittleEndian (13), -- 00001 10 1
signedInt64BigEndian (14), -- 00001 11 0
signedInt64LittleEndian (15), -- 00001 11 1
ieeeFloat32BigEndian (20), -- 00010 10 0
ieeeFloat32LittleEndian (21), -- 00010 10 1
ieeeFloat64BigEndian (22), -- 00010 11 0
ieeeFloat64LittleEndian (23) -- 00010 11 1
}
-- ======================================================
--
-- Command
--
-- ======================================================
Command ::=
[APPLICATION 2] IMPLICIT
SEQUENCE {
number [0] CommandType,
options CHOICE {
dirFieldMask [1] FieldFlags, -- only valid if number is getDirectory(32)
invocation [2] Invocation -- only valid if number is invoke(33)
} OPTIONAL
}
CommandType ::=
INTEGER {
subscribe (30),
unsubscribe (31),
getDirectory (32),
invoke (33)
}
FieldFlags ::=
INTEGER {
sparse (-2),
all (-1),
default ( 0), -- same as "all"
identifier ( 1),
description ( 2),
tree ( 3),
value ( 4),
connections ( 5)
}
-- ======================================================
--
-- Node
--
-- ======================================================
Node ::=
[APPLICATION 3] IMPLICIT
SEQUENCE {
number [0] Integer32,
contents [1] NodeContents OPTIONAL,
children [2] ElementCollection OPTIONAL
}
QualifiedNode ::=
[APPLICATION 10] IMPLICIT
SEQUENCE {
path [0] RELATIVE-OID,
contents [1] NodeContents OPTIONAL,
children [2] ElementCollection OPTIONAL
}
NodeContents ::=
SET {
identifier [0] EmberString OPTIONAL,
description [1] EmberString OPTIONAL,
isRoot [2] BOOLEAN OPTIONAL,
isOnline [3] BOOLEAN OPTIONAL, -- default is true
schemaIdentifiers[4] EmberString OPTIONAL,
templateReference[5] RELATIVE-OID OPTIONAL
}
-- ======================================================
--
-- Matrix
--
-- ======================================================
Matrix ::=
[APPLICATION 13] IMPLICIT
SEQUENCE {
number [0] Integer32,
contents [1] MatrixContents OPTIONAL,
children [2] ElementCollection OPTIONAL,
targetList [3] TargetCollection OPTIONAL,
sourceList [4] SourceCollection OPTIONAL,
connections [5] ConnectionCollection OPTIONAL
}
MatrixContents ::=
SET {
identifier [ 0] EmberString,
description [ 1] EmberString OPTIONAL,
type [ 2] MatrixType OPTIONAL,
addressingMode [ 3] MatrixAddressingMode OPTIONAL,
targetCount [ 4] Integer32, -- linear: matrix X size; nonLinear: number of targets
sourceCount [ 5] Integer32, -- linear: matrix Y size; nonLinear: number of sources
maximumTotalConnects [ 6] Integer32 OPTIONAL, -- nToN: max number of set connections
maximumConnectsPerTarget [ 7] Integer32 OPTIONAL, -- nToN: max number of sources connected to one target
parametersLocation [ 8] ParametersLocation OPTIONAL,
gainParameterNumber [ 9] Integer32 OPTIONAL, -- nToN: number of connection gain parameter
labels [10] LabelCollection OPTIONAL,
schemaIdentifiers [11] EmberString OPTIONAL,
templateReference [12] RELATIVE-OID OPTIONAL
}
-- Addressing scheme for node at MatrixContents.parametersLocation:
-- N 0001 targets.<targetNumber>: subtree containing parameters attached to target with <targetNumber>
-- N 0002 sources.<sourceNumber>: subtree containing parameters attached to source with <targetNumber>
-- N 0003 connections.<targetNumber>.<sourceNumber>: : subtree containing parameters attached to connection <targetNumber>/<sourceNumber>
MatrixType ::=
INTEGER {
oneToN (0), -- default
oneToOne (1),
nToN (2)
}
MatrixAddressingMode ::=
INTEGER {
linear (0), -- default
nonLinear (1)
}
ParametersLocation ::=
CHOICE {
basePath RELATIVE-OID, -- absolute path to node containing parameters for targets, sources and connections
inline Integer32 -- subidentifier to node containing parameters for targets, sources and connections
}
LabelCollection ::=
SEQUENCE OF [0] Label
Label ::=
[APPLICATION 18] IMPLICIT
SEQUENCE {
basePath [0] RELATIVE-OID,
description [1] EmberString
}
TargetCollection ::=
SEQUENCE OF [0] Target
Target ::=
[APPLICATION 14] IMPLICIT
Signal
Signal ::=
SEQUENCE {
number [0] Integer32
}
SourceCollection ::=
SEQUENCE OF [0] Source
Source ::=
[APPLICATION 15] IMPLICIT
Signal
ConnectionCollection ::=
SEQUENCE OF [0] Connection
Connection ::=
[APPLICATION 16] IMPLICIT
SEQUENCE {
target [0] Integer32,
sources [1] PackedNumbers OPTIONAL, -- not present or empty array means "none"
operation [2] ConnectionOperation OPTIONAL,
disposition [3] ConnectionDisposition OPTIONAL
}
-- Use case 1: Tally (Provider to consumer)
-- Connection: { target:1, sources:[5,2], operation:absolute, disposition:tally }
-- Use case 2: Take (Consumer to provider)
-- Connection: { target:1, sources:[4], operation:absolute|connect|disconnect }
-- Use case 3: TakeResponse (Provider to consumer)
-- Connection: { target:1, sources:[4], operation:absolute, disposition:modified|pending|locked|... }
PackedNumbers ::=
RELATIVE-OID
ConnectionOperation ::=
INTEGER {
absolute (0), -- default. sources contains absolute information
connect (1), -- nToN only. sources contains sources to add to connection
disconnect (2) -- nToN only. sources contains sources to remove from connection
}
ConnectionDisposition ::=
INTEGER {
tally (0), -- default
modified (1), -- sources contains new current state
pending (2), -- sources contains future state
locked (3) -- error: target locked. sources contains current state
-- more tbd.
}
QualifiedMatrix ::=
[APPLICATION 17] IMPLICIT
SEQUENCE {
path [0] RELATIVE-OID,
contents [1] MatrixContents OPTIONAL,
children [2] ElementCollection OPTIONAL,
targetList [3] TargetCollection OPTIONAL,
sourceList [4] SourceCollection OPTIONAL,
connections [5] ConnectionCollection OPTIONAL
}
-- ======================================================
--
-- Function
--
-- ======================================================
Function ::=
[APPLICATION 19] IMPLICIT
SEQUENCE {
number [0] Integer32,
contents [1] FunctionContents OPTIONAL,
children [2] ElementCollection OPTIONAL
}
QualifiedFunction ::=
[APPLICATION 20] IMPLICIT
SEQUENCE {
path [0] RELATIVE-OID,
contents [1] FunctionContents OPTIONAL,
children [2] ElementCollection OPTIONAL
}
FunctionContents ::=
SET {
identifier [0] EmberString OPTIONAL,
description [1] EmberString OPTIONAL,
arguments [2] TupleDescription OPTIONAL,
result [3] TupleDescription OPTIONAL,
templateReference[4] RELATIVE-OID OPTIONAL
}
TupleDescription ::=
SEQUENCE OF [0] TupleItemDescription
TupleItemDescription ::=
[APPLICATION 21] IMPLICIT
SEQUENCE {
type [0] ParameterType,
name [1] EmberString OPTIONAL
}
Invocation ::=
[APPLICATION 22] IMPLICIT
SEQUENCE {
invocationId [0] Integer32 OPTIONAL,
arguments [1] Tuple OPTIONAL
}
Tuple ::=
SEQUENCE OF [0] Value
InvocationResult ::=
[APPLICATION 23] IMPLICIT
SEQUENCE {
invocationId [0] Integer32,
success [1] BOOLEAN OPTIONAL,
result [2] Tuple OPTIONAL
}
-- ======================================================
--
-- ElementCollection
--
-- ======================================================
ElementCollection ::=
[APPLICATION 4] IMPLICIT
SEQUENCE OF [0] Element
Element ::=
CHOICE {
parameter Parameter,
node Node,
command Command,
matrix Matrix,
function Function,
template Template
}
-- ======================================================
--
-- Streams
--
-- ======================================================
StreamEntry ::=
[APPLICATION 5] IMPLICIT
SEQUENCE {
streamIdentifier [0] Integer32,
streamValue [1] Value
}
StreamCollection ::=
[APPLICATION 6] IMPLICIT
SEQUENCE OF [0] StreamEntry
-- ======================================================
--
-- Root
--
-- ======================================================
Root ::=
[APPLICATION 0]
CHOICE {
elements RootElementCollection,
streams StreamCollection,
invocationResult InvocationResult
}
RootElementCollection ::=
[APPLICATION 11] IMPLICIT
SEQUENCE OF [0] RootElement
RootElement ::=
CHOICE {
element Element,
qualifiedParameter QualifiedParameter,
qualifiedNode QualifiedNode,
qualifiedMatrix QualifiedMatrix,
qualifiedFunction QualifiedFunction,
qualifiedTemplate QualifiedTemplate
}
END

View File

@ -0,0 +1,16 @@
# Glow conformation file
#.MODULE_IMPORT
#.EXPORTS
#.PDU
Root
#.NO_EMIT
#.TYPE_RENAME
#.FIELD_RENAME
#.END

View File

@ -0,0 +1,90 @@
/* packet-glow.c
* Routines for GLOW packet dissection
*
* Copyright 2018, Gilles Dufour <dufour.gilles@gmail.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
# include "config.h"
#include <epan/packet.h>
#include "packet-ber.h"
#define PNAME "Glow"
#define PSNAME "GLOW"
#define PFNAME "glow"
void proto_register_glow(void);
static dissector_handle_t glow_handle=NULL;
static int proto_glow = -1;
#include "packet-glow-hf.c"
/* Initialize the subtree pointers */
static int ett_glow = -1;
#include "packet-glow-ett.c"
#include "packet-glow-fn.c"
static int
dissect_glow(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
proto_item *glow_item = NULL;
proto_tree *glow_tree = NULL;
/* make entry in the Protocol column on summary display */
col_set_str(pinfo->cinfo, COL_PROTOCOL, PNAME);
/* create the glow protocol tree */
glow_item = proto_tree_add_item(tree, proto_glow, tvb, 0, -1, ENC_NA);
glow_tree = proto_item_add_subtree(glow_item, ett_glow);
dissect_Root_PDU(tvb, pinfo, glow_tree, data);
return tvb_captured_length(tvb);
}
void proto_register_glow(void) {
/* List of fields */
static hf_register_info hf[] = {
#include "packet-glow-hfarr.c"
};
/* List of subtrees */
static gint *ett[] = {
&ett_glow,
#include "packet-glow-ettarr.c"
};
/* Register protocol */
proto_glow = proto_register_protocol(PNAME, PSNAME, PFNAME);
glow_handle = register_dissector("glow", dissect_glow, proto_glow);
/* Register fields and subtrees */
proto_register_field_array(proto_glow, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

File diff suppressed because it is too large Load Diff

689
epan/dissectors/packet-s101.c Executable file
View File

@ -0,0 +1,689 @@
/* packet-S101.c
* Routines for S101 dissection
* Copyright 2018, Gilles Dufour <dufour.gilles@gmail.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
/*
* This Dissector will dissect S101 frames used by Lawo Ember Plus protocol.
* https://github.com/Lawo/ember-plus/
*/
#include <config.h>
#include <epan/packet.h> /* Should be first Wireshark include (other than config.h) */
#include <epan/prefs.h>
#include <epan/reassemble.h>
#include <wsutil/crc16.h>
#include <epan/expert.h>
#define S101_HEADER_DATA_LENGTH 9
#define S101_BOF 0xFE
#define S101_EOF 0xFF
#define S101_CE 0xFD
#define S101_XOR 0x20
#define S101_INV 0xF8
#define S101_SLOT 0x00
#define S101_MSG_EMBER 0x0E
#define S101_CMD_EMBER 0x00
#define S101_CMD_KEEPALIVE_REQ 0x01
#define S101_CMD_KEEPALIVE_RESP 0x02
#define S101_VERSION 0x01
#define FLAG_SINGLE_PACKET 0xC0
#define FLAG_FIRST_MULTI_PACKET 0x80
#define FLAG_LAST_MULTI_PACKET 0x40
#define FLAG_EMPTY_PACKET 0x20
#define FLAG_MULTI_PACKET 0x00
#define S101_DTD_GLOW 0x01
#define S101_DTD_VERSION_MAJOR 0x02
#define S101_DTD_VERSION_MINOR 0x1F
#define S101_VALID_CRC 0xF0B8
#define APP_BYTES_LEN 2
static int hf_S101_frame_format = -1;
static int hf_S101_length_size = -1;
static int hf_S101_message_length = -1;
static int hf_S101_slot = -1;
static int hf_S101_message_type = -1;
static int hf_S101_cmd_type = -1;
static int hf_S101_version = -1;
static int hf_S101_flags = -1;
static int hf_S101_dtd_type = -1;
static int hf_S101_app_bytes_len = -1;
static int hf_S101_dtd_minor_ver = -1;
static int hf_S101_dtd_major_ver = -1;
static int hf_S101_crc = -1;
static int hf_S101_crc_status = -1;
static int hf_S101_eof = -1;
static int hf_S101_error = -1;
static dissector_handle_t glow_handle = NULL;
static reassembly_table s101_data_reassembly_table;
typedef struct _s101_fragment_t {
guint32 id;
int offset;
} s101_fragment_t;
/* Prototypes */
/* (Required to prevent [-Wmissing-prototypes] warnings */
void proto_reg_handoff_S101(void);
void proto_register_S101(void);
tvbuff_t *decode_s101_escaped_buffer(tvbuff_t *tvb, packet_info *pinfo, int *offset, guint16 *crc);
guint32 get_fragment_pdu_id(packet_info *pinfo);
s101_fragment_t* new_fragment_info(packet_info *pinfo);
void display_expert_info(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, int offset, int len);
/* Initialize the protocol and registered fields */
static int proto_S101 = -1;
/* Global sample port preference - real port preferences should generally
* default to 0 unless there is an IANA-registered (or equivalent) port for your
* protocol. */
#define S101_TCP_PORT 9000
static guint tcp_port_pref = S101_TCP_PORT;
/* Initialize the subtree pointers */
static gint ett_S101 = -1;
static gint ett_decoding_error = -1;
#define S101_MIN_LENGTH 5
static int hf_msg_fragments = -1;
static int hf_msg_fragment = -1;
static int hf_msg_fragment_overlap = -1;
static int hf_msg_fragment_overlap_conflicts = -1;
static int hf_msg_fragment_multiple_tails = -1;
static int hf_msg_fragment_too_long_fragment = -1;
static int hf_msg_fragment_error = -1;
static int hf_msg_fragment_count = -1;
static int hf_msg_reassembled_in = -1;
static int hf_msg_reassembled_length = -1;
static int hf_msg_reassembled_data = -1;
static expert_field ei_s101_failed_reassembly = EI_INIT;
static gint ett_msg_fragment = -1;
static gint ett_msg_fragments = -1;
static const fragment_items msg_frag_items = {
/* Fragment subtrees */
&ett_msg_fragment,
&ett_msg_fragments,
/* Fragment fields */
&hf_msg_fragments,
&hf_msg_fragment,
&hf_msg_fragment_overlap,
&hf_msg_fragment_overlap_conflicts,
&hf_msg_fragment_multiple_tails,
&hf_msg_fragment_too_long_fragment,
&hf_msg_fragment_error,
&hf_msg_fragment_count,
/* Reassembled in field */
&hf_msg_reassembled_in,
/* Reassembled length field */
&hf_msg_reassembled_length,
&hf_msg_reassembled_data,
/* Tag */
"Message fragments"
};
/*
Create a unique id to link fragments together.
This is a 4 bytes value:
| SRCPORT (16) | SRC_ADDRESS (16) |
SRC_ADDRESS is last 2 bytes of the src address.
*/
guint32 get_fragment_pdu_id(packet_info *pinfo) {
guint32 id = pinfo->srcport << 16;
const guint8 *data = (const guint8*)pinfo->src.data;
if (pinfo->src.len >= 2) {
id = id + (((guint32)data[pinfo->src.len - 2]) << 8) + (guint32)data[pinfo->src.len - 1];
}
return id;
}
static wmem_map_t* s101_fragment_info_hash = NULL;
s101_fragment_t* new_fragment_info(packet_info *pinfo) {
s101_fragment_t* fi = (s101_fragment_t*)wmem_alloc(wmem_file_scope(), sizeof(s101_fragment_t));
if (NULL == fi) { return fi; }
fi->id = pinfo->num;
fi->offset = 0;
return fi;
}
/* Get 1 byte
If byte escaped, get the unescaped value
*/
static guint8 get_byte(tvbuff_t *tvb, int *offset, guint16 *crc) {
guint8 b = tvb_get_guint8(tvb, *offset);
*crc = crc16_ccitt_seed(&b, 1, *crc) ^ 0xFFFF;
*offset = *offset + 1;
if (b == S101_CE) {
b = tvb_get_guint8(tvb, *offset);
*crc = crc16_ccitt_seed(&b, 1, *crc) ^ 0xFFFF;
*offset = *offset + 1;
return (b ^ S101_XOR);
} else {
return b;
}
}
static const value_string frame_format_vs[] = {
{ S101_BOF , "Escaped Frame" },
{ S101_INV , "UnEscaped Frame"},
{ 0, NULL}
};
static const value_string message_type_vs[] = {
{ S101_MSG_EMBER , "Ember" },
{ 0, NULL}
};
static const value_string command_type_vs[] = {
{ S101_CMD_EMBER , "Ember Command" },
{ S101_CMD_KEEPALIVE_REQ , "Keepalive Request" },
{ S101_CMD_KEEPALIVE_RESP , "Keepalive Response" },
{ 0, NULL}
};
static const value_string flags_vs[] = {
{ FLAG_SINGLE_PACKET , "Single Packet" },
{ FLAG_EMPTY_PACKET , "Empty Packet" },
{ FLAG_MULTI_PACKET , "Multi Packet" },
{ FLAG_LAST_MULTI_PACKET , "Last Packet" },
{ FLAG_FIRST_MULTI_PACKET , "First Packet" },
{ 0, NULL}
};
static const value_string dtd_type_vs[] = {
{ S101_DTD_GLOW , "DTD Glow" },
{ 0, NULL}
};
void
display_expert_info(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, int offset, int len) {
proto_item* pi;
proto_tree *error_tree;
error_tree = proto_tree_add_subtree(tree, tvb, offset, len,
ett_decoding_error, &pi, "S101 Error");
pi = proto_tree_add_string_format_value(
error_tree, hf_S101_error, tvb, offset, len, "s101_error",
"reassembly error");
expert_add_info(pinfo, pi, &ei_s101_failed_reassembly);
}
/*
Check s101 packet header format.
If not valid, return 0.
If valid, extract all header parameters and return 1.
If variant1, set msgLength to zero. We will have to search the end of frame.
If variant2, the msgLength contains the msgLength in bytes 0-6 and byte 7 is the number of bytes.
*/
static int
find_s101_packet_header(tvbuff_t *tvb, int* offset, guint8 *start, guint8 *slot, guint8 *message, guint8 *version, guint8 *dtd, guint8 *command,
guint8 *flags, guint8* app_bytes, guint64 *msgLength, guint16 *crc)
{
guint8 app_bytes_len;
int i;
*start = tvb_get_guint8(tvb, *offset); // no CRC and no escaping on first bytes.
*offset = *offset + 1;
if (*start == S101_INV) { // Variant 2 of header - unescaped data
//Read the frame length
app_bytes_len = tvb_get_guint8(tvb, *offset) & 0x7;
*offset = *offset + 1;
if (app_bytes_len > 1) {
*msgLength = tvb_get_bits64 (tvb, *offset, app_bytes_len * 8, ENC_BIG_ENDIAN);
*msgLength = *msgLength + (((guint64)app_bytes_len) << 56);
*offset = app_bytes_len;
}
}
else if (*start != S101_BOF) {
// IF NOT Begining of Frame - variant 1 - escaped data
return 0;
}
else {
*msgLength = 0;
}
*slot = get_byte(tvb, offset, crc);
*message = get_byte(tvb, offset, crc);
*command = get_byte(tvb, offset, crc);
*version = get_byte(tvb, offset, crc);
if (*command == S101_CMD_EMBER) {
*flags = get_byte(tvb, offset, crc);
*dtd = get_byte(tvb, offset, crc);
app_bytes_len = get_byte(tvb, offset, crc);
}
if ((S101_SLOT != *slot) ||
(S101_MSG_EMBER != *message) || (*command > S101_CMD_KEEPALIVE_RESP) ||
(S101_VERSION != *version ) ||
((*command == S101_CMD_EMBER) &&
((*flags & 0xF) || (S101_DTD_GLOW != *dtd) || (APP_BYTES_LEN != app_bytes_len)))) {
return 0;
}
if (*command == S101_CMD_EMBER) {
for(i = 0; i < APP_BYTES_LEN; i++) {
app_bytes[i] = get_byte(tvb, offset, crc);
}
}
return 1;
}
tvbuff_t *
decode_s101_escaped_buffer(tvbuff_t *tvb, packet_info *pinfo, int *offset, guint16 *crc) {
tvbuff_t *next_tvb;
int len;
int i;
guchar *decoded_buffer;
guint8 b;
len = tvb_captured_length(tvb);
if (len <= 0) {
return tvb;
}
decoded_buffer = (guchar*)wmem_alloc(pinfo->pool, len);
if (decoded_buffer == NULL) {
return tvb;
}
for(i = 0; *offset < len; ) {
b = tvb_get_guint8(tvb, *offset);
*offset = *offset + 1;
if (b == S101_CE) {
// Escaped Byte
b = tvb_get_guint8(tvb, *offset);
*offset = *offset + 1;
b = (b ^ S101_XOR);
decoded_buffer[i++] = b;
}
else {
decoded_buffer[i] = b;
if (b == S101_EOF) { // End of Frame
// let's remove the CRC and the EOF
if (i > 2) {
i -= 2;
}
break;
}
i++;
}
*crc = crc16_ccitt_seed(&b, 1, *crc) ^ 0xFFFF;
}
next_tvb = tvb_new_child_real_data(tvb, decoded_buffer, i, i);
add_new_data_source(pinfo, next_tvb, "Decoded Data");
return next_tvb;
}
/* Code to actually dissect the packets */
static int
dissect_S101(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void *data _U_)
{
/* Set up structures needed to add the protocol subtree and manage it */
proto_item *ti;
proto_tree *S101_tree;
tvbuff_t *tvb_payload;
/* Other misc. local variables. */
int offset = 0;
int parsedLen = 0;
int len = 0;
int current_offset;
int datalen = 0;
guint16 crc_data;
guint64 msgLength = 0;
guint16 crc;
guint8 start, slot, message, version, dtd, command, flags = 0xFF, app_bytes[APP_BYTES_LEN];
/* Check that the packet is long enough for it to belong to us. */
len = tvb_reported_length(tvb);
if (len < S101_MIN_LENGTH)
return 0;
/* Set the Protocol column to the constant string of S101 */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "S101");
current_offset = 0;
do {
offset = current_offset;
crc = 0xFFFF;
if (0 == find_s101_packet_header(tvb, &offset, &start, &slot, &message, &version, &dtd, &command, &flags, &app_bytes[0], &msgLength, &crc)) {
break;
}
/* create display subtree for the protocol */
ti = proto_tree_add_item(tree, proto_S101, tvb, current_offset, -1, ENC_NA);
S101_tree = proto_item_add_subtree(ti, ett_S101);
proto_tree_add_item(S101_tree, hf_S101_frame_format, tvb, current_offset++, 1, ENC_BIG_ENDIAN);
if (msgLength != 0) {
// Variant 2, the header contains a frame length
int lengthSize = (int)(msgLength >> 56) & 0xF;
proto_tree_add_item(S101_tree, hf_S101_length_size, tvb, current_offset++, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(S101_tree, hf_S101_message_length, tvb, current_offset,lengthSize, ENC_NA);
current_offset += lengthSize;
}
proto_tree_add_item(S101_tree, hf_S101_slot, tvb, current_offset++, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(S101_tree, hf_S101_message_type, tvb, current_offset++,1, ENC_BIG_ENDIAN);
proto_tree_add_item(S101_tree, hf_S101_cmd_type, tvb,current_offset++, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(S101_tree, hf_S101_version, tvb, current_offset++, 1, ENC_BIG_ENDIAN);
if (command == S101_CMD_EMBER) {
proto_tree_add_item(S101_tree, hf_S101_flags, tvb, current_offset++, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(S101_tree, hf_S101_dtd_type, tvb, current_offset++, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(S101_tree, hf_S101_app_bytes_len, tvb, current_offset++, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(S101_tree, hf_S101_dtd_minor_ver, tvb, current_offset++, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(S101_tree, hf_S101_dtd_major_ver, tvb, current_offset++, 1, ENC_BIG_ENDIAN);
}
if (msgLength == 0) {
//Variant 1 - data is encoded with escaped bytes.
tvb_payload = decode_s101_escaped_buffer(tvb, pinfo, &current_offset, &crc);
datalen = tvb_captured_length(tvb_payload);
crc_data = tvb_get_ntohs(tvb, current_offset - 3);
proto_tree_add_checksum(S101_tree, tvb, current_offset - 3, hf_S101_crc, hf_S101_crc_status, NULL,
pinfo, crc == S101_VALID_CRC ? crc_data : crc ^ crc_data, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
proto_tree_add_item(S101_tree, hf_S101_eof, tvb, current_offset - 1, 1, ENC_BIG_ENDIAN);
}
else {
//variant 2. Packet size is provided and no encoding
datalen = (int)(msgLength & 0x0FFFFFFF) - S101_HEADER_DATA_LENGTH;
tvb_payload = tvb_new_subset_length(tvb, current_offset, datalen);
current_offset += datalen;
}
proto_item_set_len(ti, current_offset - offset);
if (command == S101_CMD_EMBER) {
if (flags != FLAG_SINGLE_PACKET) {
fragment_head *frag_msg = NULL;
guint32 id = get_fragment_pdu_id(pinfo);
s101_fragment_t* fi = (s101_fragment_t*)wmem_map_lookup(s101_fragment_info_hash, &id);
pinfo->fragmented = TRUE;
if (flags == FLAG_FIRST_MULTI_PACKET) {
if (NULL == fi) {
fi = new_fragment_info(pinfo);
wmem_map_insert(s101_fragment_info_hash, &id, fi);
}
else {
fi->id = pinfo->num;
}
fragment_add(&s101_data_reassembly_table, tvb_payload, 0,
pinfo, fi->id, NULL,
0, datalen,
TRUE);
fi->offset = datalen;
}
else if (flags == FLAG_LAST_MULTI_PACKET) {
if (NULL != fi) {
// last fragment
frag_msg = fragment_add(&s101_data_reassembly_table, tvb_payload, 0,
pinfo, fi->id, NULL,
fi->offset, datalen,
FALSE);
tvb_payload = process_reassembled_data(tvb, offset, pinfo,
"Reassembled Message", frag_msg, &msg_frag_items,
NULL, S101_tree);
}
if (frag_msg) { /* Reassembled */
col_append_str(pinfo->cinfo, COL_INFO,
" (Message Reassembled)");
}
else {
display_expert_info(S101_tree, tvb, pinfo, offset, current_offset - offset);
}
}
else if (NULL == fi) {
display_expert_info(S101_tree, tvb, pinfo, offset, current_offset - offset);
}
else if (flags == FLAG_MULTI_PACKET) {
fragment_add(&s101_data_reassembly_table, tvb_payload, 0,
pinfo, fi->id, NULL,
fi->offset, datalen,
TRUE);
fi->offset += datalen;
col_append_fstr(pinfo->cinfo, COL_INFO,
" (Message fragment)");
}
}
// Call ASN1 Glow dissector - see epan/dissectors/asn1/glow/ if packet is complete.
if ((flags == FLAG_LAST_MULTI_PACKET) && (tvb_payload == NULL)) {
proto_item* pi;
proto_tree_add_subtree(S101_tree, tvb, offset, current_offset - offset,
ett_decoding_error, &pi, "S101 Error");
expert_add_info(pinfo, pi, &ei_s101_failed_reassembly);
}
else if ((glow_handle != NULL) && ((flags == FLAG_LAST_MULTI_PACKET) || (flags == FLAG_SINGLE_PACKET))) {
parsedLen = call_dissector_only(glow_handle, tvb_payload, pinfo, S101_tree, data);
if (parsedLen <= 0) {
break;
}
}
}
}while(current_offset < len);
return current_offset;
}
/* Register the protocol with Wireshark.
*
* This format is require because a script is used to build the C function that
* calls all the protocol registration.
*/
void
proto_register_S101(void)
{
module_t *S101_module;
expert_module_t* expert_s101;
/* Setup list of header fields See Section 1.5 of README.dissector for
* details. */
static hf_register_info hf[] = {
{ &hf_S101_frame_format,
{ "Frame Format", "s101.format",
FT_UINT8, BASE_HEX, VALS(frame_format_vs), 0x0, NULL, HFILL }},
{ &hf_S101_length_size,
{ "Bytes for Length", "s101.lensize",
FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_S101_message_length,
{ "Message Length", "s101.msglen",
FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_S101_slot,
{ "Slot", "s101.slot",
FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
{ &hf_S101_message_type,
{ "Message Type", "s101.msgtype",
FT_UINT8, BASE_HEX, VALS(message_type_vs), 0x0, NULL, HFILL }},
{ &hf_S101_cmd_type,
{ "Command Type", "s101.cmdtype",
FT_UINT8, BASE_HEX, VALS(command_type_vs), 0x0, NULL, HFILL }},
{ &hf_S101_version,
{ "Version", "s101.version",
FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_S101_flags,
{ "Flags", "s101.flags",
FT_UINT8, BASE_HEX, VALS(flags_vs), 0x0, NULL, HFILL }},
{ &hf_S101_dtd_type,
{ "DTD Type", "s101.dtdtype",
FT_UINT8, BASE_DEC, VALS(dtd_type_vs), 0x0, NULL, HFILL }},
{ &hf_S101_app_bytes_len,
{ "App Bytes Length", "s101.applen",
FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_S101_dtd_minor_ver,
{ "App Minor Version", "s101.appminver",
FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_S101_dtd_major_ver,
{ "App Major Version", "s101.appmajver",
FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_S101_crc,
{ "CRC", "s101.crc",
FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
{ &hf_S101_crc_status,
{ "Checksum Status", "s101.crc.status",
FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0,
NULL, HFILL }},
{ &hf_S101_eof,
{ "End of Frane", "s101.eof",
FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
{ &hf_S101_error, {
"S101 Error", "s101.error", FT_STRING, BASE_NONE,
NULL, 0, NULL, HFILL }},
// fragments info
{&hf_msg_fragments,
{ "Message fragments", "s101.msg.fragments",
FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL }},
{&hf_msg_fragment,
{ "Message fragment", "s101.msg.fragment",
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL }},
{&hf_msg_fragment_overlap,
{ "Message fragment overlap", "s101.msg.fragment.overlap",
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL }},
{&hf_msg_fragment_overlap_conflicts,
{ "Message fragment overlapping with conflicting data", "s101.msg.fragment.overlap.conflicts",
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL }},
{&hf_msg_fragment_multiple_tails,
{ "Message has multiple tail fragments", "s101.msg.fragment.multiple_tails",
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL }},
{&hf_msg_fragment_too_long_fragment,
{ "Message fragment too long", "s101.msg.fragment.too_long_fragment",
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL }},
{&hf_msg_fragment_error,
{ "Message defragmentation error", "s101.msg.fragment.error",
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL }},
{&hf_msg_fragment_count,
{ "Message fragment count", "s101.msg.fragment.count",
FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }},
{&hf_msg_reassembled_in,
{ "Reassembled in", "s101.msg.reassembled.in",
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL }},
{&hf_msg_reassembled_length,
{ "Reassembled length", "s101.msg.reassembled.length",
FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }},
{&hf_msg_reassembled_data,
{ "Reassembled Data", "s101.msg.reassembled.data",
FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL }},
};
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_S101,
&ett_msg_fragment,
&ett_msg_fragments,
&ett_decoding_error
};
static ei_register_info ei[] = {
{ &ei_s101_failed_reassembly, { "s101.reassembly_error", PI_MALFORMED, PI_WARN, "Reassembly Error", EXPFILL }},
};
/* Register the protocol name and description */
proto_S101 = proto_register_protocol("S101",
"S101", "s101");
/* Required function calls to register the header fields and subtrees */
proto_register_field_array(proto_S101, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
reassembly_table_register(&s101_data_reassembly_table,
&addresses_ports_reassembly_table_functions);
s101_fragment_info_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(),
g_direct_hash, g_direct_equal);
S101_module = prefs_register_protocol(proto_S101,
proto_reg_handoff_S101);
expert_s101 = expert_register_protocol(proto_S101);
expert_register_field_array(expert_s101, ei, array_length(ei));
/* Register an example port preference */
prefs_register_uint_preference(S101_module, "tcp.port", "S101 TCP Port",
"S101 TCP port if other than the default",
10, &tcp_port_pref);
}
void
proto_reg_handoff_S101(void)
{
static gboolean initialized = FALSE;
static dissector_handle_t S101_handle;
static int current_port;
if (!initialized) {
S101_handle = create_dissector_handle(dissect_S101,
proto_S101);
glow_handle = find_dissector_add_dependency("glow", proto_S101);
initialized = TRUE;
} else {
dissector_delete_uint("tcp.port", current_port, S101_handle);
}
current_port = tcp_port_pref;
dissector_add_uint("tcp.port", current_port, S101_handle);
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/