wireshark/epan/dissectors/packet-fpp.c

953 lines
34 KiB
C

/* packet-fpp.c
* Routines for IEEE 802.3br Frame Preemption Protocol packet disassembly
*
* Copyright 2017, Anton Glukhov <anton.a.glukhov@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 <wiretap/wtap.h>
#include <epan/expert.h>
#include <epan/conversation.h>
#include <wsutil/crc32.h>
#include <epan/crc32-tvb.h>
#include <epan/reassemble.h>
#include <epan/proto_data.h>
void proto_register_fpp(void);
void proto_reg_handoff_fpp(void);
static int proto_fpp = -1;
static dissector_handle_t fpp_handle;
static int hf_fpp_preamble = -1;
static int hf_fpp_preamble_pad = -1;
static int hf_fpp_preamble_smd = -1;
static int hf_fpp_preamble_frag_count = -1;
static int hf_fpp_mdata = -1;
static int hf_fpp_crc32 = -1;
static int hf_fpp_crc32_status = -1;
static int hf_fpp_mcrc32 = -1;
static int hf_fpp_mcrc32_status = -1;
static expert_field ei_fpp_crc32 = EI_INIT;
static expert_field ei_fpp_mcrc32 = EI_INIT;
static gint ett_fpp = -1;
static gint ett_fpp_preamble = -1;
static reassembly_table fpp_reassembly_table;
static dissector_handle_t ethl2_handle;
/* Reassembly Data */
static int hf_fpp_fragments = -1;
static int hf_fpp_fragment = -1;
static int hf_fpp_fragment_overlap = -1;
static int hf_fpp_fragment_overlap_conflicts = -1;
static int hf_fpp_fragment_multiple_tails = -1;
static int hf_fpp_fragment_too_long_fragment = -1;
static int hf_fpp_fragment_error = -1;
static int hf_fpp_fragment_count = -1;
static int hf_fpp_reassembled_in = -1;
static int hf_fpp_reassembled_length = -1;
static gint ett_fpp_fragment = -1;
static gint ett_fpp_fragments = -1;
static const fragment_items fpp_frag_items = {
/* Fragment subtrees */
&ett_fpp_fragment,
&ett_fpp_fragments,
/* Fragment fields */
&hf_fpp_fragments,
&hf_fpp_fragment,
&hf_fpp_fragment_overlap,
&hf_fpp_fragment_overlap_conflicts,
&hf_fpp_fragment_multiple_tails,
&hf_fpp_fragment_too_long_fragment,
&hf_fpp_fragment_error,
&hf_fpp_fragment_count,
/* Reassembled in field */
&hf_fpp_reassembled_in,
/* Reassembled length field */
&hf_fpp_reassembled_length,
/* Reassembled data field */
NULL,
/* Tag */
"fpp fragments"
};
#define FPP_DEFAULT_PREAMBLE_LENGTH 8
#define FPP_CRC_LENGTH 4
typedef enum {
FPP_Packet_Expess,
FPP_Packet_Verify,
FPP_Packet_Response,
FPP_Packet_Init,
FPP_Packet_Cont,
FPP_Packet_Invalid,
} fpp_packet_t;
typedef enum {
SMD_Verify = 0x7,
SMD_Respond = 0x19,
SMD_Express = 0xd5,
SMD_PP_Start_0 = 0xe6,
SMD_PP_Start_1 = 0x4c,
SMD_PP_Start_2 = 0x7f,
SMD_PP_Start_3 = 0xb3,
FragCount_0 = SMD_PP_Start_0,
FragCount_1 = SMD_PP_Start_1,
FragCount_2 = SMD_PP_Start_2,
FragCount_3 = SMD_PP_Start_3
} first_delim;
typedef enum {
Octet_0x55 = 0x55,
SMD_PP_ContFrag_0 = 0x61,
SMD_PP_ContFrag_1 = 0x52,
SMD_PP_ContFrag_2 = 0x9e,
SMD_PP_ContFrag_3 = 0x2a,
} second_delim;
typedef enum {
CRC_CRC,
CRC_mCRC,
CRC_FALSE
} fpp_crc_t;
typedef enum {
PACKET_DIRECTION_INBOUND = 0x1,
PACKET_DIRECTION_OUTBOUND = 0x2,
PACKET_DIRECTION_UNKNOWN = 0x0,
} packet_direction_enum;
/* Packets with correct CRC sum */
static const value_string preemptive_delim_desc[] = {
{ SMD_PP_Start_0, "[Non-fragmented packet: SMD-S0]" },
{ SMD_PP_Start_1, "[Non-fragmented packet: SMD-S1]" },
{ SMD_PP_Start_2, "[Non-fragmented packet: SMD-S2]" },
{ SMD_PP_Start_3, "[Non-fragmented packet: SMD-S3]" },
{ 0x0, NULL }
};
/* Packets with correct mCRC sum */
static const value_string initial_delim_desc[] = {
{ SMD_PP_Start_0, "[Initial fragment: SMD-S0]" },
{ SMD_PP_Start_1, "[Initial fragment: SMD-S1]" },
{ SMD_PP_Start_2, "[Initial fragment: SMD-S2]" },
{ SMD_PP_Start_3, "[Initial fragment: SMD-S3]" },
{ 0x0, NULL }
};
/* Packets with incorrect checksum */
static const value_string corrupted_delim_desc[] = {
{ SMD_PP_Start_0, "[Corrupted fragment: SMD-S0]" },
{ SMD_PP_Start_1, "[Corrupted fragment: SMD-S1]" },
{ SMD_PP_Start_2, "[Corrupted fragment: SMD-S2]" },
{ SMD_PP_Start_3, "[Corrupted fragment: SMD-S3]" },
{ 0x0, NULL }
};
static const value_string continuation_delim_desc[] = {
{ SMD_PP_ContFrag_0, "[Continuation fragment: SMD-C0]" },
{ SMD_PP_ContFrag_1, "[Continuation fragment: SMD-C1]" },
{ SMD_PP_ContFrag_2, "[Continuation fragment: SMD-C2]" },
{ SMD_PP_ContFrag_3, "[Continuation fragment: SMD-C3]" },
{ 0x0, NULL }
};
static const value_string frag_count_delim_desc[] = {
{ FragCount_0, "[#0]"},
{ FragCount_1, "[#1]"},
{ FragCount_2, "[#2]"},
{ FragCount_3, "[#3]"},
{ 0x0, NULL }
};
static const value_string delim_desc[] = {
{ SMD_Verify, "[SMD-V]" },
{ SMD_Respond, "[SMD-R]" },
{ SMD_Express, "[SMD-E]" },
{ SMD_PP_Start_0, "[SMD-S0]" },
{ SMD_PP_Start_1, "[SMD-S1]" },
{ SMD_PP_Start_2, "[SMD-S2]" },
{ SMD_PP_Start_3, "[SMD-S3]" },
{ SMD_PP_ContFrag_0, "[SMD-C0]" },
{ SMD_PP_ContFrag_1, "[SMD-C1]" },
{ SMD_PP_ContFrag_2, "[SMD-C2]" },
{ SMD_PP_ContFrag_3, "[SMD-C3]" },
{ 0x0, NULL }
};
static guint32
get_preamble_length(tvbuff_t *tvb) {
guint32 offset = 0;
if( 0x50 == tvb_get_guint8(tvb, offset) )
{
//First octet contains preamble alignment bits. Ignore it.
offset = 1;
}
while( tvb_get_guint8(tvb, offset) == Octet_0x55 && ( offset + 2 < tvb_reported_length(tvb) ) )
{
offset++;
}
guint8 smd1 = tvb_get_guint8(tvb, offset);
switch (smd1) {
case SMD_PP_Start_0:
case SMD_PP_Start_1:
case SMD_PP_Start_2:
case SMD_PP_Start_3:
case SMD_Verify:
case SMD_Respond:
case SMD_Express:
return offset + 1;
case SMD_PP_ContFrag_0:
case SMD_PP_ContFrag_1:
case SMD_PP_ContFrag_2:
case SMD_PP_ContFrag_3:
return offset + 2;
default:
return FPP_DEFAULT_PREAMBLE_LENGTH;
}
}
static fpp_crc_t
get_crc_stat(tvbuff_t *tvb, guint32 crc, guint32 mcrc) {
fpp_crc_t crc_val;
guint32 received_crc = tvb_get_guint32(tvb, tvb_reported_length(tvb) - FPP_CRC_LENGTH, ENC_BIG_ENDIAN);
if (received_crc == crc) {
crc_val = CRC_CRC;
} else if (received_crc == mcrc) {
crc_val = CRC_mCRC;
} else {
crc_val = CRC_FALSE;
}
return crc_val;
}
static fpp_crc_t
get_express_crc_stat(tvbuff_t *tvb, guint32 express_crc) {
fpp_crc_t crc_val;
guint32 received_crc = tvb_get_guint32(tvb, tvb_reported_length(tvb) - FPP_CRC_LENGTH, ENC_BIG_ENDIAN);
if (received_crc == express_crc) {
crc_val = CRC_CRC;
} else {
crc_val = CRC_FALSE;
}
return crc_val;
}
static fpp_packet_t
get_packet_type(tvbuff_t *tvb) {
/* function analyze a packet based on preamble and ignore crc */
guint32 offset = 0;
if( 0x50 == tvb_get_guint8(tvb, offset) )
{
//First octet contains preamble alignment bits. Ignore it.
offset = 1;
}
while( tvb_get_guint8(tvb, offset) == Octet_0x55 && ( offset + 2 < tvb_reported_length(tvb) ) )
{
offset++;
}
guint8 smd1 = tvb_get_guint8(tvb, offset);
guint8 smd2 = tvb_get_guint8(tvb, offset + 1);
switch (smd1) {
case SMD_PP_Start_0:
case SMD_PP_Start_1:
case SMD_PP_Start_2:
case SMD_PP_Start_3:
return FPP_Packet_Init;
case SMD_Verify:
return FPP_Packet_Verify;
case SMD_Respond:
return FPP_Packet_Response;
case SMD_Express:
return FPP_Packet_Expess;
case SMD_PP_ContFrag_0:
case SMD_PP_ContFrag_1:
case SMD_PP_ContFrag_2:
case SMD_PP_ContFrag_3:
switch (smd2) {
case FragCount_0:
case FragCount_1:
case FragCount_2:
case FragCount_3:
return FPP_Packet_Cont;
default:
return FPP_Packet_Invalid;
}
default:
return FPP_Packet_Invalid;
}
return FPP_Packet_Invalid;
}
static void
col_fstr_process(tvbuff_t *tvb, packet_info *pinfo, fpp_crc_t crc_val) {
guint preamble_length = get_preamble_length( tvb );
switch( get_packet_type(tvb) ) {
case FPP_Packet_Expess:
col_add_str(pinfo->cinfo, COL_INFO, "[Express]");
break;
case FPP_Packet_Verify:
col_add_str(pinfo->cinfo, COL_INFO, "[Verify]");
break;
case FPP_Packet_Response:
col_add_str(pinfo->cinfo, COL_INFO, "[Respond]");
break;
case FPP_Packet_Init:
if (crc_val == CRC_CRC)
col_add_fstr(pinfo->cinfo, COL_INFO, "%s", try_val_to_str(tvb_get_guint8(tvb, preamble_length-1), preemptive_delim_desc));
else if (crc_val == CRC_mCRC)
col_add_fstr(pinfo->cinfo, COL_INFO, "%s", try_val_to_str(tvb_get_guint8(tvb, preamble_length-1), initial_delim_desc));
else
col_add_fstr(pinfo->cinfo, COL_INFO, "%s", try_val_to_str(tvb_get_guint8(tvb, preamble_length-1), corrupted_delim_desc));
break;
case FPP_Packet_Cont:
col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s", try_val_to_str(tvb_get_guint8(tvb, preamble_length-2), continuation_delim_desc),
try_val_to_str(tvb_get_guint8(tvb, preamble_length-1), frag_count_delim_desc));
break;
default:
break;
}
}
struct _fpp_ctx_t {
gboolean preemption;
guint8 frame_cnt;
guint8 frag_cnt;
guint32 size;
guint32 crc;
wmem_map_t *crc_history;
};
typedef struct _fpp_ctx_t fpp_ctx_t;
static packet_direction_enum
get_packet_direction(packet_info *pinfo) {
switch (pinfo->p2p_dir) {
case P2P_DIR_RECV:
return PACKET_DIRECTION_INBOUND;
case P2P_DIR_SENT:
return PACKET_DIRECTION_OUTBOUND;
default:
return PACKET_DIRECTION_UNKNOWN;
}
}
static void
init_fpp_ctx(struct _fpp_ctx_t *ctx, guint8 frame_cnt, guint32 crc) {
ctx->preemption = TRUE;
ctx->frame_cnt = frame_cnt;
ctx->frag_cnt = FragCount_3;
ctx->size = 0;
ctx->crc = crc;
ctx->crc_history = wmem_map_new(wmem_epan_scope(), g_int_hash, g_int_equal);
}
static guint8
frag_cnt_next(guint8 cur_num) {
switch(cur_num) {
case FragCount_0:
return FragCount_1;
case FragCount_1:
return FragCount_2;
case FragCount_2:
return FragCount_3;
case FragCount_3:
default:
return FragCount_0;
}
}
static guint8
get_cont_by_start(guint8 start_cnt) {
if (start_cnt == SMD_PP_Start_0)
return SMD_PP_ContFrag_0;
else if (start_cnt == SMD_PP_Start_1)
return SMD_PP_ContFrag_1;
else if (start_cnt == SMD_PP_Start_2)
return SMD_PP_ContFrag_2;
else if (start_cnt == SMD_PP_Start_3)
return SMD_PP_ContFrag_3;
else
return SMD_PP_ContFrag_0;
}
struct _fpp_pdata_t {
/* struct for future possible usage */
guint32 offset;
};
typedef struct _fpp_pdata_t fpp_pdata_t;
static void
drop_conversation(conversation_t *conv) {
fpp_ctx_t *ctx;
ctx = (fpp_ctx_t*)conversation_get_proto_data(conv, proto_fpp);
if (ctx != NULL) {
wmem_free(wmem_file_scope(), ctx);
}
conversation_delete_proto_data(conv, proto_fpp);
}
static void
drop_fragments(packet_info *pinfo) {
tvbuff_t *tvbuf;
guint interface_id;
packet_direction_enum packet_direction = get_packet_direction(pinfo);
if (pinfo->rec->presence_flags & WTAP_HAS_INTERFACE_ID)
interface_id = pinfo->rec->rec_header.packet_header.interface_id;
else
interface_id = 0;
interface_id = interface_id << 0x2;
tvbuf = fragment_delete(&fpp_reassembly_table, pinfo, interface_id | packet_direction, NULL);
if (tvbuf != NULL) {
tvb_free(tvbuf);
}
}
static tvbuff_t*
dissect_preemption(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
fpp_packet_t pck_type;
guint preamble_length = get_preamble_length( tvb );
guint preamble_bit_length = preamble_length * 8;
gboolean preamble_unaligned = FALSE;
guint8 smd1 = tvb_get_guint8(tvb, preamble_length - 2);
guint8 smd2 = tvb_get_guint8(tvb, preamble_length - 1);
guint crc_offset = tvb_reported_length(tvb) - FPP_CRC_LENGTH;
gint frag_size = tvb_reported_length(tvb) - preamble_length - FPP_CRC_LENGTH;
/* Reassembly parameters. */
tvbuff_t *new_tvb = NULL;
fragment_head *frag_data;
gboolean save_fragmented;
conversation_t *conv;
fpp_ctx_t *ctx;
guint interface_id;
packet_direction_enum packet_direction = get_packet_direction(pinfo);
fpp_crc_t crc_val;
/* mCRC calculations needs previous crc */
guint32 crc, mcrc, prev_crc;
if (pinfo->rec->presence_flags & WTAP_HAS_INTERFACE_ID)
interface_id = pinfo->rec->rec_header.packet_header.interface_id;
else
interface_id = 0;
interface_id = interface_id << 0x2;
/* Create a tree for the preamble. */
proto_item *ti_preamble = proto_tree_add_item(tree, hf_fpp_preamble, tvb, 0, preamble_length, ENC_NA);
if( 0x50 == tvb_get_guint8(tvb, 0) )
{
//First octet contains preamble alignment bits.
preamble_bit_length -= 4;
preamble_unaligned = TRUE;
}
if( preamble_bit_length == FPP_DEFAULT_PREAMBLE_LENGTH * 8 ) {
proto_item_append_text(ti_preamble, " [Preamble length: Normal]" );
} else if( preamble_bit_length < FPP_DEFAULT_PREAMBLE_LENGTH * 8 ) {
proto_item_append_text(ti_preamble, " [Preamble length: Shortened by %d bits]", FPP_DEFAULT_PREAMBLE_LENGTH * 8 - preamble_bit_length );
} else if( preamble_bit_length > FPP_DEFAULT_PREAMBLE_LENGTH * 8 ) {
proto_item_append_text(ti_preamble, " [Preamble length: Lengthened by %d bits]", preamble_bit_length - FPP_DEFAULT_PREAMBLE_LENGTH * 8 );
}
proto_tree_add_item(tree, hf_fpp_mdata, tvb, preamble_length, frag_size, ENC_NA);
proto_tree *fpp_preamble_tree = proto_item_add_subtree(ti_preamble, ett_fpp_preamble);
if( preamble_unaligned ) {
proto_tree_add_item(fpp_preamble_tree, hf_fpp_preamble_pad, tvb, 0, 1, ENC_BIG_ENDIAN);
}
pck_type = get_packet_type(tvb);
if(pck_type == FPP_Packet_Cont)
{
proto_item *ti_smd = proto_tree_add_item(fpp_preamble_tree, hf_fpp_preamble_smd, tvb, preamble_length - 2, 1, ENC_BIG_ENDIAN);
proto_item *ti_fragcnt = proto_tree_add_item(fpp_preamble_tree, hf_fpp_preamble_frag_count, tvb, preamble_length - 1, 1, ENC_BIG_ENDIAN);
proto_item_append_text(ti_smd, " %s", try_val_to_str(tvb_get_guint8(tvb, preamble_length-2), delim_desc) );
proto_item_append_text(ti_fragcnt, " %s", try_val_to_str(tvb_get_guint8(tvb, preamble_length-1), frag_count_delim_desc) );
}
else
{
proto_item *ti_smd = proto_tree_add_item(fpp_preamble_tree, hf_fpp_preamble_smd, tvb, preamble_length - 1, 1, ENC_BIG_ENDIAN);
proto_item_append_text(ti_smd, " %s", try_val_to_str(tvb_get_guint8(tvb, preamble_length-1), delim_desc) );
}
prev_crc = 0;
conv = find_conversation_by_id(pinfo->num, CONVERSATION_NONE, interface_id | packet_direction);
/* Create a conversation at every SMD-S fragment.
Find the conversation for every SMD-C fragment.*/
if (pck_type == FPP_Packet_Init) {
/* will be used for seeding the crc calculation */
if (!PINFO_FD_VISITED(pinfo)) {
conv = conversation_new_by_id(pinfo->num, CONVERSATION_NONE, interface_id | packet_direction);
/* XXX Is this needed? */
find_conversation_pinfo(pinfo, 0);
}
}
else if (pck_type == FPP_Packet_Cont && conv) {
ctx = (fpp_ctx_t *)conversation_get_proto_data(conv, proto_fpp);
if (ctx) {
if (!PINFO_FD_VISITED(pinfo)) {
if ((ctx->preemption) && (ctx->frame_cnt == smd1) && (frag_cnt_next(ctx->frag_cnt) == smd2)) {
prev_crc = ctx->crc;
}
/* create a copy of frame number and previous crc and store in crc_history */
guint32 *copy_of_pinfo_num = wmem_new(wmem_epan_scope(), guint32);
guint32 *copy_of_prev_crc = wmem_new(wmem_epan_scope(), guint32);
*copy_of_pinfo_num = pinfo->num;
*copy_of_prev_crc = prev_crc;
wmem_map_insert(ctx->crc_history, copy_of_pinfo_num, copy_of_prev_crc);
}
else {
prev_crc = *(guint32 *)wmem_map_lookup(ctx->crc_history, &pinfo->num);
}
}
}
crc = GUINT32_SWAP_LE_BE(crc32_ccitt_tvb_offset_seed(tvb, preamble_length, frag_size, GUINT32_SWAP_LE_BE(prev_crc) ^ 0xffffffff));
mcrc = crc ^ 0xffff0000;
crc_val = get_crc_stat(tvb, crc, mcrc); /* might be crc if last part or mcrc if continuation */
/* fill column Info */
col_fstr_process(tvb, pinfo, crc_val);
if (pck_type == FPP_Packet_Init) {
/* Add data to this new conversation during first iteration*/
if (conv && !PINFO_FD_VISITED(pinfo)) {
ctx = wmem_new(wmem_file_scope(), struct _fpp_ctx_t);
init_fpp_ctx(ctx, get_cont_by_start(smd2), crc);
ctx->size = frag_size;
conversation_add_proto_data(conv, proto_fpp, ctx);
}
if (crc_val == CRC_CRC) {
/* Non-fragmented packet
end of continuation */
drop_fragments(pinfo);
if (conv && !PINFO_FD_VISITED(pinfo)) {
drop_conversation(conv);
}
proto_tree_add_checksum(tree, tvb, crc_offset, hf_fpp_crc32, hf_fpp_crc32_status, &ei_fpp_crc32, pinfo, crc, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
return tvb_new_subset_length(tvb, preamble_length, frag_size);
}
else if (crc_val == CRC_mCRC) {
/* First fragment */
drop_fragments(pinfo);
frag_data = fragment_add_check(&fpp_reassembly_table,
tvb, preamble_length, pinfo, interface_id | packet_direction, NULL,
0, frag_size, TRUE);
set_address_tvb(&pinfo->dl_dst, AT_ETHER, 6, tvb, 8);
set_address_tvb(&pinfo->dst, AT_ETHER, 6, tvb, 8);
set_address_tvb(&pinfo->dl_src, AT_ETHER, 6, tvb, 14);
set_address_tvb(&pinfo->src, AT_ETHER, 6, tvb, 14);
proto_tree_add_checksum(tree, tvb, crc_offset, hf_fpp_mcrc32, hf_fpp_mcrc32_status, &ei_fpp_mcrc32, pinfo, mcrc, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
if (frag_data != NULL) {
col_append_frame_number(pinfo, COL_INFO, " [Reassembled in #%u]", frag_data->reassembled_in);
process_reassembled_data(tvb, preamble_length, pinfo,
"Reassembled FPP", frag_data, &fpp_frag_items,
NULL, tree);
}
} else {
/* Possibly first fragment */
drop_fragments(pinfo);
proto_tree_add_checksum(tree, tvb, crc_offset, hf_fpp_mcrc32, hf_fpp_mcrc32_status, &ei_fpp_mcrc32, pinfo, mcrc, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
}
} else if (pck_type == FPP_Packet_Cont) {
if (crc_val == CRC_mCRC) {
/* Continuation fragment */
/* Update data of this conversation */
if (!PINFO_FD_VISITED(pinfo) && conv) {
ctx = (fpp_ctx_t*)conversation_get_proto_data(conv, proto_fpp);
if (ctx) {
fpp_pdata_t *fpp_pdata = wmem_new(wmem_file_scope(), fpp_pdata_t);
fpp_pdata->offset = ctx->size;
p_add_proto_data(wmem_file_scope(), pinfo, proto_fpp, interface_id | packet_direction, fpp_pdata);
ctx->size += frag_size;
ctx->frag_cnt = smd2;
ctx->crc = crc;
}
}
proto_tree_add_checksum(tree, tvb, crc_offset, hf_fpp_mcrc32, hf_fpp_mcrc32_status, &ei_fpp_mcrc32, pinfo, mcrc, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
fpp_pdata_t *fpp_pdata = (fpp_pdata_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fpp, interface_id | packet_direction);
if (fpp_pdata) {
frag_data = fragment_add_check(&fpp_reassembly_table,
tvb, preamble_length, pinfo, interface_id | packet_direction, NULL,
fpp_pdata->offset, frag_size, TRUE);
if (frag_data != NULL) {
col_append_frame_number(pinfo, COL_INFO, " [Reassembled in #%u]", frag_data->reassembled_in);
process_reassembled_data(tvb, preamble_length, pinfo,
"Reassembled FPP", frag_data, &fpp_frag_items,
NULL, tree);
}
} else {
drop_fragments(pinfo);
}
} else if (crc_val == CRC_CRC) {
/* Suppose that the last fragment dissected
1. preemption is active
2. check frame count and frag count values
After these steps check crc of entire reassembled frame
*/
if (conv) {
ctx = (fpp_ctx_t*)conversation_get_proto_data(conv, proto_fpp);
if ((ctx) && (ctx->preemption) && (ctx->frame_cnt == smd1) && (frag_cnt_next(ctx->frag_cnt) == smd2)) {
fpp_pdata_t *fpp_pdata = wmem_new(wmem_file_scope(), fpp_pdata_t);
if (!PINFO_FD_VISITED(pinfo)) {
fpp_pdata->offset = ctx->size;
p_add_proto_data(wmem_file_scope(), pinfo, proto_fpp, interface_id | packet_direction, fpp_pdata);
}
}
}
fpp_pdata_t *fpp_pdata = (fpp_pdata_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fpp, interface_id | packet_direction);
if (fpp_pdata) {
save_fragmented = pinfo->fragmented;
pinfo->fragmented = TRUE;
frag_data = fragment_add_check(&fpp_reassembly_table,
tvb, preamble_length, pinfo, interface_id | packet_direction, NULL,
fpp_pdata->offset, frag_size, FALSE);
// Attempt reassembly.
new_tvb = process_reassembled_data(tvb, preamble_length, pinfo,
"Reassembled FPP", frag_data, &fpp_frag_items,
NULL, tree);
pinfo->fragmented = save_fragmented;
} else {
drop_fragments(pinfo);
proto_tree_add_checksum(tree, tvb, crc_offset, hf_fpp_mcrc32, hf_fpp_mcrc32_status, &ei_fpp_mcrc32, pinfo, mcrc, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
}
if (new_tvb) {
/* Reassembly was successful; return the completed datagram. */
guint32 reassembled_crc = GUINT32_SWAP_LE_BE(crc32_ccitt_tvb_offset(new_tvb, 0, tvb_reported_length(new_tvb)));
/* Reassembly frame takes place regardless of whether the check sum was correct or not. */
proto_tree_add_checksum(tree, tvb, crc_offset, hf_fpp_crc32, -1, &ei_fpp_crc32, pinfo, reassembled_crc, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
return new_tvb;
} else {
/* Reassembly was unsuccessful; show this fragment. This may
just mean that we don't yet have all the fragments, so
we should not just continue dissecting. */
proto_tree_add_checksum(tree, tvb, crc_offset, hf_fpp_mcrc32, hf_fpp_mcrc32_status, &ei_fpp_mcrc32, pinfo, crc, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
return NULL;
}
} else {
/* Invalid packet */
if (!PINFO_FD_VISITED(pinfo) && conv) {
ctx = (fpp_ctx_t *)conversation_get_proto_data(conv, proto_fpp);
if (ctx) {
fpp_pdata_t *fpp_pdata = wmem_new(wmem_file_scope(), fpp_pdata_t);
fpp_pdata->offset = ctx->size;
p_add_proto_data(wmem_file_scope(), pinfo, proto_fpp, interface_id | packet_direction, fpp_pdata);
ctx->size += frag_size;
ctx->frag_cnt = smd2;
ctx->crc = crc;
}
}
proto_tree_add_checksum(tree, tvb, crc_offset, hf_fpp_mcrc32, hf_fpp_mcrc32_status, &ei_fpp_mcrc32, pinfo, mcrc, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
}
} else if (pck_type == FPP_Packet_Verify) {
proto_tree_add_checksum(tree, tvb, crc_offset, hf_fpp_mcrc32, -1, &ei_fpp_mcrc32, pinfo, mcrc, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
} else if (pck_type == FPP_Packet_Response) {
proto_tree_add_checksum(tree, tvb, crc_offset, hf_fpp_mcrc32, hf_fpp_mcrc32_status, &ei_fpp_mcrc32, pinfo, mcrc, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
}
return NULL;
}
static tvbuff_t *
dissect_express(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 crc, fpp_crc_t crc_val) {
guint crc_offset = tvb_reported_length(tvb) - FPP_CRC_LENGTH;
guint offset = 0;
guint preamble_length = get_preamble_length( tvb );
guint preamble_bit_length = preamble_length * 8;
gboolean preamble_unaligned = FALSE;
guint pdu_data_len = tvb_reported_length(tvb) - preamble_length - FPP_CRC_LENGTH;
proto_item *ti_preamble = proto_tree_add_item(tree, hf_fpp_preamble, tvb, offset, preamble_length, ENC_NA);
offset += preamble_length;
if( 0x50 == tvb_get_guint8(tvb, 0) )
{
//First octet contains preamble alignment bits.
preamble_bit_length -= 4;
preamble_unaligned = TRUE;
}
if( preamble_bit_length == FPP_DEFAULT_PREAMBLE_LENGTH * 8 ) {
proto_item_append_text(ti_preamble, " [Preamble length: Normal]" );
} else if( preamble_bit_length < FPP_DEFAULT_PREAMBLE_LENGTH * 8 ) {
proto_item_append_text(ti_preamble, " [Preamble length: Shortened by %d bits]", FPP_DEFAULT_PREAMBLE_LENGTH * 8 - preamble_bit_length );
} else if( preamble_bit_length > FPP_DEFAULT_PREAMBLE_LENGTH * 8 ) {
proto_item_append_text(ti_preamble, " [Preamble length: Lengthened by %d bits]", preamble_bit_length - FPP_DEFAULT_PREAMBLE_LENGTH * 8 );
}
proto_tree_add_item(tree, hf_fpp_mdata, tvb, offset, pdu_data_len, ENC_NA);
proto_tree *fpp_preamble_tree = proto_item_add_subtree(ti_preamble, ett_fpp_preamble);
if( preamble_unaligned ) {
proto_tree_add_item(fpp_preamble_tree, hf_fpp_preamble_pad, tvb, 0, 1, ENC_BIG_ENDIAN);
}
proto_item *ti_smd = proto_tree_add_item(fpp_preamble_tree, hf_fpp_preamble_smd, tvb, preamble_length - 1, 1, ENC_BIG_ENDIAN);
proto_item_append_text(ti_smd, " [SMD-E]" );
proto_tree_add_checksum(tree, tvb, crc_offset, hf_fpp_crc32, hf_fpp_crc32_status, &ei_fpp_crc32, pinfo, crc, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
if (crc_val == CRC_CRC) {
return tvb_new_subset_length(tvb, preamble_length, pdu_data_len);
}
return NULL;
}
static int
dissect_fpp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
guint32 express_crc;
fpp_crc_t crc_val;
tvbuff_t *next = tvb;
guint preamble_length = get_preamble_length( tvb );
guint pdu_data_len = tvb_reported_length(tvb) - preamble_length - FPP_CRC_LENGTH;
col_set_str(pinfo->cinfo, COL_PROTOCOL, "FPP");
col_clear(pinfo->cinfo,COL_INFO);
proto_item *ti = proto_tree_add_item(tree, proto_fpp, tvb, 0, -1, ENC_NA);
proto_tree *fpp_tree = proto_item_add_subtree(ti, ett_fpp);
switch (get_packet_type(tvb)) {
case FPP_Packet_Expess:
/* this is the old crc calculation which is only valid for express frames */
express_crc = GUINT32_SWAP_LE_BE(crc32_ccitt_tvb_offset(tvb, preamble_length, pdu_data_len));
/* is express_crc valid */
crc_val = get_express_crc_stat(tvb, express_crc);
/* fill column Info */
col_fstr_process(tvb, pinfo, crc_val);
next = dissect_express(tvb, pinfo, fpp_tree, express_crc, crc_val);
break;
case FPP_Packet_Init:
case FPP_Packet_Cont:
case FPP_Packet_Verify:
case FPP_Packet_Response:
next = dissect_preemption(tvb, pinfo, fpp_tree);
break;
default:
break;
}
if (next) {
call_dissector(ethl2_handle, next, pinfo, tree);
} else {
tvbuff_t *new_tvb = tvb_new_subset_length(tvb, preamble_length, pdu_data_len);
call_data_dissector(new_tvb, pinfo, tree);
}
return tvb_captured_length(tvb);
}
void
proto_register_fpp(void)
{
static hf_register_info hf[] = {
{ &hf_fpp_preamble,
{ "Preamble", "fpp.preamble",
FT_BYTES, BASE_NONE,
NULL, 0x0,
NULL, HFILL }
},
{ &hf_fpp_preamble_pad,
{ "Alignment padding, not part of frame", "fpp.preamble.pad",
FT_UINT8, BASE_HEX,
NULL, 0x0F,
NULL, HFILL }
},
{ &hf_fpp_preamble_smd,
{ "SMD", "fpp.preamble.smd",
FT_UINT8, BASE_HEX,
NULL, 0x0,
NULL, HFILL }
},
{ &hf_fpp_preamble_frag_count,
{ "Fragment count", "fpp.preamble.frag_count",
FT_UINT8, BASE_HEX,
NULL, 0x0,
NULL, HFILL }
},
{ &hf_fpp_mdata,
{ "mData", "fpp.mdata",
FT_BYTES, BASE_NONE,
NULL, 0x0,
NULL, HFILL }
},
{ &hf_fpp_crc32,
{ "CRC", "fpp.crc32",
FT_UINT32, BASE_HEX,
NULL, 0x0,
NULL, HFILL }
},
{ &hf_fpp_crc32_status,
{ "Checksum Status", "fpp.checksum.status",
FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0,
NULL, HFILL
},
},
{ &hf_fpp_mcrc32,
{ "mCRC", "fpp.mcrc32",
FT_UINT32, BASE_HEX,
NULL, 0x0,
NULL, HFILL }
},
{ &hf_fpp_mcrc32_status,
{ "Checksum Status", "fpp.checksum.status",
FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0,
NULL, HFILL
},
},
/* Reassembly fields. */
{ &hf_fpp_fragments,
{ "Message fragments", "fpp.fragments",
FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL }},
{ &hf_fpp_fragment,
{ "Message fragment", "fpp.fragment",
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL }},
{ &hf_fpp_fragment_overlap,
{ "Message fragment overlap", "fpp.fragment.overlap",
FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL }},
{ &hf_fpp_fragment_overlap_conflicts,
{ "Message fragment overlapping with conflicting data", "fpp.fragment.overlap.conflicts",
FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL }},
{ &hf_fpp_fragment_multiple_tails,
{ "Message has multiple tail fragments", "fpp.fragment.multiple_tails",
FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL }},
{ &hf_fpp_fragment_too_long_fragment,
{ "Message fragment too long", "fpp.fragment.too_long_fragment",
FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL }},
{ &hf_fpp_fragment_error,
{ "Message defragmentation error", "fpp.fragment.error",
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL }},
{ &hf_fpp_fragment_count,
{ "Message fragment count", "fpp.fragment.count",
FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }},
{ &hf_fpp_reassembled_in,
{ "Reassembled in", "fpp.reassembled.in",
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL }},
{ &hf_fpp_reassembled_length,
{ "Reassembled fpp length", "fpp.reassembled.length",
FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }},
};
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_fpp,
&ett_fpp_preamble,
/* Reassembly subtrees. */
&ett_fpp_fragment,
&ett_fpp_fragments
};
static ei_register_info ei[] = {
{ &ei_fpp_mcrc32,
{ "fpp.mcrc32_bad", PI_CHECKSUM, PI_ERROR,
"Bad mCRC checksum", EXPFILL }
},
{ &ei_fpp_crc32,
{ "fpp.crc32_bad", PI_CHECKSUM, PI_ERROR,
"Bad CRC checksum", EXPFILL }
},
};
expert_module_t* expert_fpp;
proto_fpp = proto_register_protocol (
"IEEE 802.3br Frame Preemption Protocol",
"Frame Preemption Protocol",
"fpp"
);
proto_register_field_array(proto_fpp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_fpp = expert_register_protocol(proto_fpp);
expert_register_field_array(expert_fpp, ei, array_length(ei));
reassembly_table_register(&fpp_reassembly_table, &addresses_reassembly_table_functions);
fpp_handle = register_dissector("fpp", dissect_fpp, proto_fpp);
}
void
proto_reg_handoff_fpp(void)
{
dissector_add_uint("wtap_encap", WTAP_ENCAP_ETHERNET_MPACKET, fpp_handle);
ethl2_handle = find_dissector_add_dependency("eth_withoutfcs", proto_fpp);
}
/*
* 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=false:
*/