diff --git a/epan/dissectors/packet-metamako.c b/epan/dissectors/packet-metamako.c index 35e5ce8a87..9ec2117107 100644 --- a/epan/dissectors/packet-metamako.c +++ b/epan/dissectors/packet-metamako.c @@ -19,14 +19,52 @@ #include "config.h" #include +#include +#include +#include #include +#define TRAILER_MIN_LENGTH_NO_FCS 16 +#define TRAILER_NS_UPPER_BOUND 1000000000 +#define TRAILER_SECS_BOUNDS_DFLT "3600-" +#define TRAILER_DAYS_DIFF_LIMIT_DFLT 30 +#define SECS_IN_ONE_DAY (60 * 60 * 24) + void proto_register_metamako(void); void proto_reg_handoff_metamako(void); +/* FCS Options */ +static gboolean metamako_check_fcs = TRUE; +static gint metamako_fcs_len = -1; /* By default, try to autodetect the FCS. */ +/* Heuristic Options */ +static gint metamako_trailer_present = -1; /* By default, try to autodetect the trailer. */ +static range_t* metamako_trailer_secs_bounds = NULL; +static guint metamako_trailer_days_diff_limit = TRAILER_DAYS_DIFF_LIMIT_DFLT; + +/* Protocols and Header Fields */ static int proto_metamako = -1; +/* + Metamako trailer format + 0.............7...............15..............23..............31 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Original FCS | + +---------------+---------------+---------------+---------------+ + | ... | + +---------------+---------------+---------------+---------------+ + | TLV Extensions | + +---------------+---------------+---------------+---------------+ + | ... | + +---------------+---------------+---------------+---------------+ + | Seconds | + +---------------+---------------+---------------+---------------+ + | Nanoseconds | + +---------------+---------------+---------------+---------------+ + | Flags | Device ID | Port ID | + +---------------+---------------+---------------+---------------+ +*/ + static int hf_metamako_origfcs = -1; static int hf_metamako_trailerext = -1; static int hf_metamako_unknownext = -1; @@ -35,11 +73,14 @@ static int hf_metamako_tagstring = -1; static int hf_metamako_fracns = -1; static int hf_metamako_crchash = -1; static int hf_metamako_egress_seqnum = -1; -static int hf_metamako_time = -1; +static int hf_metamako_time_abs = -1; +static int hf_metamako_time_rel = -1; static int hf_metamako_flags = -1; static int hf_metamako_srcport = -1; static int hf_metamako_srcdevice = -1; -static int hf_metamako_tdiff = -1; +static int hf_metamako_time_diff = -1; +static int hf_metamako_fcs = -1; +static int hf_metamako_fcs_status = -1; static int hf_metamako_flags_orig_fcs_vld = -1; static int hf_metamako_flags_has_ext = -1; @@ -53,9 +94,33 @@ static gint ett_metamako_timestamp = -1; static gint ett_metamako_extensions = -1; static gint ett_metamako_flags = -1; -static const true_false_string tfs_pcs49_btf = {"0x78", "0x33 or 0x66"}; +static const enum_val_t metamako_fcs_vals[] = { + {"heuristic", "According to heuristic", -1}, + {"never", "Never", 0}, + {"always", "Always", 4}, + {NULL, NULL, 0} +}; -static int * const flags[] = { +static const enum_val_t metamako_trailer_present_vals[] = { + {"heuristic", "According to heuristic", -1}, + {"never", "Never", 0}, + {"always", "Always", 1}, + {NULL, NULL, 0} +}; + +static const value_string tfs_pcs49_btf_vals[] = { + { 0x0, "0x33 or 0x66" }, + { 0x1, "0x78"}, + { 0, NULL } +}; + +static const value_string tfs_orig_fcs_status_vals[] = { + { 0x0, "Bad" }, + { 0x1, "Good"}, + { 0, NULL } +}; + +static int* const flags[] = { &hf_metamako_flags_control_block_type, &hf_metamako_flags_ts_degraded, &hf_metamako_flags_duplicate, @@ -65,50 +130,63 @@ static int * const flags[] = { NULL }; +static expert_field ei_metamako_fcs_bad = EI_INIT; + static void -sub_nanos_base_custom(gchar *result, guint32 value) +sub_nanos_base_custom(gchar* result, guint32 value) { double temp_double; - temp_double = ((double)value) / (1U << 24); + temp_double = ((double)value) / (1ULL << 24); snprintf(result, ITEM_LABEL_LENGTH, "%1.9fns", temp_double); } static int -validate_metamako_timestamp(nstime_t *metamako_time, packet_info *pinfo) +validate_metamako_timestamp(nstime_t* metamako_time, packet_info* pinfo) { - if ( metamako_time->secs > 3600 && metamako_time->nsecs < 1000000000 ) { - if ( metamako_time->secs > pinfo->abs_ts.secs ) { - if ( metamako_time->secs - pinfo->abs_ts.secs > 2592000 ) /* 30 days */ - return 0; - } else { - if ( pinfo->abs_ts.secs - metamako_time->secs > 2592000 ) /* 30 days */ - return 0; - } + + /* Check that we have a valid nanoseconds field. */ + if (metamako_time->nsecs >= TRAILER_NS_UPPER_BOUND) + return 0; + + /* Check that the seconds in the trailer timestamp are in the user-specified bounds. */ + if (!value_is_in_range(metamako_trailer_secs_bounds, (guint32)metamako_time->secs)) + return 0; + + /* Check that the number of days between the trailer timestamp + and the capture timestamp are within the user-specified bounds. + Don't use the abs() function because it is not supported on all + platforms and has type ambiguity. */ + if (metamako_time->secs > pinfo->abs_ts.secs) { + if (metamako_time->secs - pinfo->abs_ts.secs > (time_t)metamako_trailer_days_diff_limit * SECS_IN_ONE_DAY) + return 0; } else { - return 0; + if (pinfo->abs_ts.secs - metamako_time->secs > (time_t)metamako_trailer_days_diff_limit * SECS_IN_ONE_DAY) + return 0; } + return 1; } static int -dissect_metamako(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +dissect_metamako(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data _U_) { - guint i, j; + guint i, i_start, i_end, j; - proto_tree *ti, *parent, *item; - proto_tree *metamako_tree, *timestamp_tree; - proto_tree *extensions_tree; + proto_tree* ti, * parent, * item; + proto_tree* metamako_tree, * timestamp_tree; + proto_tree* extensions_tree; guint offset = 0; guint captured_trailer_bytes; guint metamako_trailer_bytes; - guint trailer_valid; + guint trailer_min_length; + gboolean trailer_valid; + gboolean has_fcs; - nstime_t metamako_time, timediff; + nstime_t metamako_time, time_diff, time_rel; guint metamako_meta; - guint metamako_tlv_present; guint metamako_tlv_count; guint metamako_tlv_firstword; @@ -121,7 +199,7 @@ dissect_metamako(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data guint16 metamako_srcdevice; guint8 metamako_flags; - struct tm *tmp; + struct tm* tmp; /* The Metamako trailer is made up of: 4 bytes -- original FCS @@ -131,53 +209,69 @@ dissect_metamako(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data 1 byte -- flags 2 bytes -- device ID 1 byte -- port ID + + The new FCS is not a part of the trailer specification, + but it may be passed to this dissector in the course of + dissecting the Ethernet trailer. + If `metamako_fcs_len` is 0, we know it's not present; + if `metamako_fcs_len` is 4, we know it's present; + if `metamako_fcs_len` is -1, we need some heuristics to + determine whether it's present. + 4 bytes -- New (valid) FCS (may or may not have been captured) */ - /* First get the captured length of the trailer */ - captured_trailer_bytes = tvb_captured_length(tvb); - - /* If we have less than 12 bytes captured, we can't - read the timestamp to confirm the heuristic */ - if ( captured_trailer_bytes < 12 ) + /* If the user has told us that the Metamako trailer is not present, + then exit immediately. */ + if (metamako_trailer_present == 0) return 0; - /* Init variables before loop */ - metamako_trailer_bytes = captured_trailer_bytes; - metamako_tlv_count = 0; - metamako_tlv_bytes = 0; - metamako_tlv_present = 0; + /* First get the captured length of the trailer. */ + captured_trailer_bytes = tvb_captured_length(tvb); - /* Default state is no valid trailer found */ - trailer_valid = 0; + /* Determine the minimum trailer length required, based on the user's + setting of the assumed FCS capture. */ + trailer_min_length = metamako_fcs_len == 4 ? TRAILER_MIN_LENGTH_NO_FCS + 4 : TRAILER_MIN_LENGTH_NO_FCS; - /* Loop through the trailer bytes, try to find a valid trailer. - * Only try twice: - * - First try the case where IS NO trailing FCS - * - Second try the case where there IS a trailing FCS + /* If we have less than `trailer_min_length` bytes captured, we can't + read the trailer. */ + if (captured_trailer_bytes < trailer_min_length) + return 0; + + /* Adjust the state of the loop variables to account for the user options. */ + trailer_valid = FALSE; + i_start = metamako_fcs_len == 4 ? 1 : 0; + i_end = metamako_fcs_len == 0 ? 1 : 2; + + /* Loop through the trailer bytes, trying to find a valid trailer. + * When: + * i == 0, we assume there IS NO trailing FCS + * i == 1, we assume ther IS a trailing FCS */ - for ( i = 0; i < 2 && metamako_trailer_bytes >= 12 && !trailer_valid; i++ ) { - /* Start at the tail of the trailer and work inwards */ - metamako_meta = tvb_get_ntohl(tvb, metamako_trailer_bytes - 4); - metamako_flags = (metamako_meta >> 24) & 0xFF; + for (i = i_start; i < i_end && !trailer_valid; i++) { + has_fcs = i == 1; + captured_trailer_bytes -= 4 * i; + metamako_trailer_bytes = captured_trailer_bytes; + /* Start at the tail of the trailer and work inwards. */ + metamako_meta = tvb_get_ntohl(tvb, metamako_trailer_bytes - 4); + metamako_flags = (metamako_meta >> 24) & 0xFF; metamako_trailer_bytes -= 4; metamako_time.nsecs = tvb_get_ntohl(tvb, metamako_trailer_bytes - 4); metamako_trailer_bytes -= 4; - metamako_time.secs = tvb_get_ntohl(tvb, metamako_trailer_bytes - 4); + metamako_time.secs = tvb_get_ntohl(tvb, metamako_trailer_bytes - 4); metamako_trailer_bytes -= 4; - /* Check the validity of the candidate timestamp */ - if ( validate_metamako_timestamp(&metamako_time, pinfo) ) { - /* Check if the trailer has tlv extensions */ - metamako_tlv_present = metamako_flags & 0x2; + /* Check the validity of the candidate timestamp. */ + if ((metamako_trailer_present == 1) || validate_metamako_timestamp(&metamako_time, pinfo)) { metamako_tlv_bytes = 0; metamako_tlv_count = 0; - if ( metamako_tlv_present ) { + /* If the trailer has TLV extensions, "walk" them backwards to the Orig FCS field. */ + if (metamako_flags & 0x2) { /* Extensions are flagged as included, check if there is bytes * to support this, and try to decode. */ - while ( metamako_trailer_bytes >= 4 ) { - /* Bytes are here, decode as TLVs */ + while (metamako_trailer_bytes >= 4) { + /* Bytes are here, decode as TLVs. */ metamako_tlv_firstword = tvb_get_ntohl(tvb, metamako_trailer_bytes - 4); metamako_tlv_count++; metamako_tlv_bytes += 4; @@ -186,51 +280,47 @@ dissect_metamako(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data /* Extract length */ metamako_tlv_len = (metamako_tlv_firstword >> 6) & 0x3; - /* If its a secondary tag header, extract the extended length */ - if ( ( metamako_tlv_firstword & 0x1F ) == 0x1F ) - metamako_tlv_len = ( (metamako_tlv_firstword >> 6) & 0x3FF ) + 1; + /* If it's a secondary tag header, extract the extended length. */ + if ((metamako_tlv_firstword & 0x1F) == 0x1F) + metamako_tlv_len = ((metamako_tlv_firstword >> 6) & 0x3FF) + 1; - /* Skip over the data, find the next tag */ - while ( metamako_tlv_len > 0 ) { + /* Skip over the data and find the next tag. We do this in a loop + rather than subtracting `4 * metamako_tlv_len` in case the + dissection has picked up an invalid TLV length in its + heuristic search. This prevents the "walk" from going past the + original length of the trailer. */ + while ((metamako_tlv_len > 0) && (metamako_trailer_bytes >= 4)) { metamako_tlv_len--; metamako_tlv_bytes += 4; metamako_trailer_bytes -= 4; } - /* If this is flagged as the last TLV, stop */ - if ( ( metamako_tlv_firstword >> 5 ) & 0x1 ) { + /* If this is flagged as the last TLV, stop. */ + if ((metamako_tlv_firstword >> 5) & 0x1) { break; } } } - /* There should be at least enough bytes for the Orig FCS left - * any bytes before this are padding - */ - if ( metamako_trailer_bytes >= 4 ) { - /* Decrement by the number of bytes in the Orig FCS field */ + /* There should be at least enough bytes for the Orig FCS field. + * Any bytes before this are padding. */ + if (metamako_trailer_bytes >= 4) { + /* Decrement by the number of bytes in the Orig FCS field. */ metamako_trailer_bytes -= 4; - - /* This byte offset is the beginning of the Metamako trailer */ + /* This byte offset is the beginning of the Metamako trailer. */ offset = metamako_trailer_bytes; - - /* If we've made it this far, it appears we've got a valid trailer */ - trailer_valid = 1; + /* If we've made it this far, it appears we've got a valid trailer. */ + trailer_valid = TRUE; } } - - /* If we didn't find a valid metamako trailer, try again using 4 bytes less */ - if ( !trailer_valid ) { - captured_trailer_bytes -= 4; - metamako_trailer_bytes = captured_trailer_bytes; - } } /* Did we find a valid trailer? */ - if ( !trailer_valid ) + if (!trailer_valid) return 0; /* Everything looks good! Now dissect the trailer. */ + col_append_str(pinfo->cinfo, COL_INFO, " with Metamako trailer"); ti = proto_tree_add_item(tree, proto_metamako, tvb, offset, (captured_trailer_bytes - offset), ENC_NA); metamako_tree = proto_item_add_subtree(ti, ett_metamako); @@ -239,121 +329,109 @@ dissect_metamako(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data offset += 4; /* TLV Extensions */ - if ( metamako_tlv_present ) { + if (metamako_tlv_bytes > 0) { parent = proto_tree_add_item(metamako_tree, hf_metamako_trailerext, tvb, captured_trailer_bytes - 12 - metamako_tlv_bytes, metamako_tlv_bytes, ENC_NA); extensions_tree = proto_item_add_subtree(parent, ett_metamako_extensions); - while ( metamako_tlv_count > 0 ) { + while (metamako_tlv_count > 0) { metamako_tlv_pos = captured_trailer_bytes - 16; i = metamako_tlv_count; do { - /* Read TLV word and decode */ + /* Read TLV word and decode. */ metamako_tlv_firstword = tvb_get_ntohl(tvb, metamako_tlv_pos); - metamako_tlv_len = ( metamako_tlv_firstword >> 6 ) & 0x3; - metamako_tlv_bytes = ( metamako_tlv_len * 4 ) + 3; - metamako_tlv_tag = ( metamako_tlv_firstword & 0x1F ); + metamako_tlv_len = (metamako_tlv_firstword >> 6) & 0x3; + metamako_tlv_bytes = (metamako_tlv_len * 4) + 3; + metamako_tlv_tag = (metamako_tlv_firstword & 0x1F); - /* If this is a Secondary Tag Header, decode the tag extensions */ - if ( ( metamako_tlv_firstword & 0x1F ) == 0x1F ) { - metamako_tlv_len = ( ( metamako_tlv_firstword >> 6 ) & 0x3FF ) + 1; - metamako_tlv_bytes = ( metamako_tlv_len * 4 ); - metamako_tlv_tag += ( ( metamako_tlv_firstword >> 16 ) & 0xFFFF ); + /* If this is a Secondary Tag Header, decode the tag extensions. */ + if ((metamako_tlv_firstword & 0x1F) == 0x1F) { + metamako_tlv_len = ((metamako_tlv_firstword >> 6) & 0x3FF) + 1; + metamako_tlv_bytes = (metamako_tlv_len * 4); + metamako_tlv_tag += ((metamako_tlv_firstword >> 16) & 0xFFFF); } - /* Decrement TLV count */ + /* Decrement TLV count. */ i--; - /* Skip over the data if this is not our destination */ - if ( i != 0 ) - metamako_tlv_pos -= ( metamako_tlv_len + 1 ) * 4; - } - while ( i > 0 ); + /* Skip over the data if this is not our destination. */ + if (i != 0) + metamako_tlv_pos -= (metamako_tlv_len + 1) * 4; + } while (i > 0); - /* We've skipped to the i-th TLV, decode it */ - switch ( metamako_tlv_tag ) { - case 0: - /* Sequence Number */ - metamako_tlv_pos -= ( metamako_tlv_len + 1 ) * 4; - proto_tree_add_item(extensions_tree, hf_metamako_seqnum, tvb, metamako_tlv_pos + 5, 2, ENC_BIG_ENDIAN); - proto_item_append_text(parent, ", Sequence No: %d", tvb_get_ntohs(tvb, metamako_tlv_pos + 5)); - /* Increment the offset by the Data + Tag size */ - offset += ( metamako_tlv_len + 1 ) * 4; - break; + metamako_tlv_pos -= (metamako_tlv_len + 1) * 4; + /* We've skipped to the i-th TLV, decode it. */ + switch (metamako_tlv_tag) { + case 0x00: + /* Ingress Sequence Number */ + proto_tree_add_item(extensions_tree, hf_metamako_seqnum, tvb, metamako_tlv_pos + 5, 2, ENC_BIG_ENDIAN); + proto_item_append_text(parent, ", Sequence No: %d", tvb_get_ntohs(tvb, metamako_tlv_pos + 5)); + break; - case 1: - /* Sub-nanoseconds */ - metamako_tlv_pos -= ( metamako_tlv_len + 1 ) * 4; - proto_tree_add_item(extensions_tree, hf_metamako_fracns, tvb, metamako_tlv_pos + 4, 3, ENC_BIG_ENDIAN); - proto_item_append_text(parent, ", Sub-nanoseconds: %1.9fns", ((double)(tvb_get_ntohl(tvb, metamako_tlv_pos + 3) & 0x00FFFFFF)) / (1U << 24)); - /* Increment the offset by the Data + Tag size */ - offset += ( metamako_tlv_len + 1 ) * 4; - break; + case 0x01: + /* Sub-nanoseconds */ + proto_tree_add_item(extensions_tree, hf_metamako_fracns, tvb, metamako_tlv_pos + 4, 3, ENC_BIG_ENDIAN); + proto_item_append_text(parent, ", Sub-nanoseconds: %1.9fns", ((double)(tvb_get_ntohl(tvb, metamako_tlv_pos + 3) & 0x00FFFFFF)) / (1ULL << 24)); + break; - case 2: - /* De-duplication CRC64 Hash */ - metamako_tlv_pos -= ( metamako_tlv_len + 1 ) * 4; - proto_tree_add_item(extensions_tree, hf_metamako_crchash, tvb, metamako_tlv_pos + 4, 8, ENC_BIG_ENDIAN); - proto_item_append_text(parent, ", CRC64 ECMA Hash: 0x%" PRIx64, tvb_get_ntoh64(tvb, metamako_tlv_pos + 4)); - /* Increment the offset by the Data + Tag size */ - offset += ( metamako_tlv_len + 1 ) * 4; - break; + case 0x02: + /* Deduplication CRC64 Hash */ + proto_tree_add_item(extensions_tree, hf_metamako_crchash, tvb, metamako_tlv_pos + 4, 8, ENC_BIG_ENDIAN); + proto_item_append_text(parent, ", CRC64 ECMA Hash: 0x%" PRIx64, tvb_get_ntoh64(tvb, metamako_tlv_pos + 4)); + break; - case 3: - /* Egress Sequence Number */ - metamako_tlv_pos -= ( metamako_tlv_len + 1 ) * 4; - proto_tree_add_item(extensions_tree, hf_metamako_egress_seqnum, tvb, metamako_tlv_pos + 4, 3, ENC_BIG_ENDIAN); - proto_item_append_text(parent, ", Egress Sequence No: %d", tvb_get_ntohl(tvb, metamako_tlv_pos + 3) & 0x000FFFFF); - /* Increment the offset by the Data + Tag size */ - offset += ( metamako_tlv_len + 1 ) * 4; - break; + case 0x03: + /* Egress Sequence Number */ + proto_tree_add_item(extensions_tree, hf_metamako_egress_seqnum, tvb, metamako_tlv_pos + 4, 3, ENC_BIG_ENDIAN); + proto_item_append_text(parent, ", Egress Sequence No: %d", tvb_get_ntohl(tvb, metamako_tlv_pos + 3) & 0x000FFFFF); + break; - case 31: - /* Tag String */ - metamako_tlv_pos -= ( metamako_tlv_len + 1 ) * 4; - proto_tree_add_item(extensions_tree, hf_metamako_tagstring, tvb, metamako_tlv_pos + 4, metamako_tlv_len * 4, ENC_ASCII); - /* Increment the offset by the Data + Tag size */ - offset += ( metamako_tlv_len + 1 ) * 4; - break; + case 0x1F: + /* Tag String */ + proto_tree_add_item(extensions_tree, hf_metamako_tagstring, tvb, metamako_tlv_pos + 4, metamako_tlv_len * 4, ENC_ASCII); + break; - default: - /* Unknown tag: just print it's ID and Data */ - metamako_tlv_pos -= ( metamako_tlv_len + 1 ) * 4; - item = proto_tree_add_item(extensions_tree, hf_metamako_unknownext, tvb, metamako_tlv_pos + 4, metamako_tlv_bytes, ENC_NA); - /* Start with the Tag */ - proto_item_set_text(item, "Unknown Tag [0x%05x]: ", metamako_tlv_tag); - /* Iterate through the data appending as we go */ - for ( j = 0; j < metamako_tlv_bytes; j++ ) { - proto_item_append_text(item, "%02x", tvb_get_guint8(tvb, metamako_tlv_pos + 4 + j)); - if ( (28 + j*2) >= ITEM_LABEL_LENGTH ) { - proto_item_append_text(item, "..."); - break; - } + default: + /* Unknown tag: just print it's ID and Data. */ + item = proto_tree_add_item(extensions_tree, hf_metamako_unknownext, tvb, metamako_tlv_pos + 4, metamako_tlv_bytes, ENC_NA); + /* Start with the Tag */ + proto_item_set_text(item, "Unknown Tag [0x%05" PRIx32 "]: ", metamako_tlv_tag); + /* Iterate through the data appending as we go */ + for (j = 0; j < metamako_tlv_bytes; j++) { + proto_item_append_text(item, "%02" PRIx8, tvb_get_guint8(tvb, metamako_tlv_pos + 4 + j)); + if ((28 + j * 2) >= ITEM_LABEL_LENGTH) { + proto_item_append_text(item, "..."); + break; } - /* Increment the offset by the Data + Tag size */ - offset += ( metamako_tlv_len + 1 ) * 4; - break; + } + break; } + /* Increment the offset by the Data + Tag size */ + offset += (metamako_tlv_len + 1) * 4; /* Decrement count as we've just decoded a TLV */ metamako_tlv_count--; } } /* Timestamp */ - item = proto_tree_add_time(metamako_tree, hf_metamako_time, tvb, offset, 8, &metamako_time); + item = proto_tree_add_time(metamako_tree, hf_metamako_time_abs, tvb, offset, 8, &metamako_time); timestamp_tree = proto_item_add_subtree(item, ett_metamako_timestamp); tmp = localtime(&metamako_time.secs); if (tmp) proto_item_append_text(ti, ", Timestamp: %02d:%02d:%02d.%09ld", - tmp->tm_hour, tmp->tm_min, tmp->tm_sec,(long)metamako_time.nsecs); + tmp->tm_hour, tmp->tm_min, tmp->tm_sec, (long)metamako_time.nsecs); else proto_item_append_text(ti, ", Timestamp: "); - /* [Timestamp difference from pcap time] */ - nstime_delta(&timediff, &metamako_time, &pinfo->abs_ts); - item = proto_tree_add_time(timestamp_tree, hf_metamako_tdiff, tvb, offset, 8, &timediff); + /* [Timestamp in seconds] */ + item = proto_tree_add_time_item(timestamp_tree, hf_metamako_time_rel, tvb, offset, 8, + ENC_BIG_ENDIAN, &time_rel, NULL, NULL); proto_item_set_generated(item); + /* [Timestamp difference from pcap time] */ + nstime_delta(&time_diff, &metamako_time, &pinfo->abs_ts); + item = proto_tree_add_time(timestamp_tree, hf_metamako_time_diff, tvb, offset, 8, &time_diff); + proto_item_set_generated(item); offset += 8; /* Flags */ @@ -372,6 +450,23 @@ dissect_metamako(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data proto_item_append_text(ti, ", Source Port: %d", metamako_srcport); offset++; + if (has_fcs) { + guint32 sent_fcs = tvb_get_ntohl(tvb, offset); + if (metamako_check_fcs) { + tvbuff_t* parent_tvb = tvb_get_ds_tvb(tvb); + guint32 fcs = crc32_802_tvb(parent_tvb, tvb_captured_length(parent_tvb) - 4); + proto_tree_add_checksum(tree, tvb, offset, hf_metamako_fcs, hf_metamako_fcs_status, &ei_metamako_fcs_bad, pinfo, fcs, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY); + + if (fcs != sent_fcs) { + col_append_str(pinfo->cinfo, COL_INFO, " [ETHERNET FRAME CHECK SEQUENCE INCORRECT]"); + } + } + else { + proto_tree_add_checksum(tree, tvb, offset, hf_metamako_fcs, hf_metamako_fcs_status, &ei_metamako_fcs_bad, pinfo, 0, ENC_BIG_ENDIAN, PROTO_CHECKSUM_NO_FLAGS); + } + offset += 4; + } + return offset; } @@ -419,29 +514,34 @@ proto_register_metamako(void) FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, - { &hf_metamako_time, { - "Time Stamp", "metamako.time", + { &hf_metamako_time_abs, { + "Time Stamp", "metamako.time.abs", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, NULL, HFILL }}, - {&hf_metamako_flags, { + { &hf_metamako_time_rel, { + "Time Stamp", "metamako.time.rel", + FT_RELATIVE_TIME, ENC_BIG_ENDIAN, NULL, 0x0, + NULL, HFILL }}, + + { &hf_metamako_time_diff, { + "Time Difference", "metamako.time.diff", + FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, + "Difference from capture timestamp", HFILL }}, + + {&hf_metamako_flags, { "Flags", "metamako.flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}}, - {&hf_metamako_flags_orig_fcs_vld, { - "Original FCS Valid", "metamako.flags.orig_fcs_vld", - FT_BOOLEAN, 8, TFS(&tfs_valid_invalid), 0x01, + {&hf_metamako_reserved, { + "Reserved", "metamako.reserved", + FT_UINT8, BASE_HEX, NULL, 0xC8, NULL, HFILL}}, - {&hf_metamako_flags_has_ext, { - "Has Trailer Extensions", "metamako.flags.has_extensions", - FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x02, - NULL, HFILL}}, - - {&hf_metamako_flags_duplicate, { - "Duplicate Packet", "metamako.flags.is_duplicate", - FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x04, + {&hf_metamako_flags_control_block_type, { + "Clause 49 BTF", "metamako.flags.pcs49_btf", + FT_UINT8, BASE_HEX, VALS(tfs_pcs49_btf_vals), 0x20, NULL, HFILL}}, {&hf_metamako_flags_ts_degraded, { @@ -449,14 +549,19 @@ proto_register_metamako(void) FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x10, NULL, HFILL}}, - {&hf_metamako_flags_control_block_type, { - "Clause 49 BTF", "metamako.flags.pcs49_btf", - FT_BOOLEAN, 8, TFS(&tfs_pcs49_btf), 0x20, + {&hf_metamako_flags_duplicate, { + "Duplicate Packet", "metamako.flags.is_duplicate", + FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x04, NULL, HFILL}}, - {&hf_metamako_reserved, { - "Reserved", "metamako.reserved", - FT_UINT8, BASE_HEX, NULL, 0xC8, + {&hf_metamako_flags_has_ext, { + "Has Trailer Extensions", "metamako.flags.has_extensions", + FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x02, + NULL, HFILL}}, + + {&hf_metamako_flags_orig_fcs_vld, { + "Original FCS Status", "metamako.flags.orig_fcs_status", + FT_UINT8, BASE_HEX, VALS(tfs_orig_fcs_status_vals), 0x01, NULL, HFILL}}, { &hf_metamako_srcdevice, { @@ -469,22 +574,81 @@ proto_register_metamako(void) FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, - { &hf_metamako_tdiff, { - "Time Difference", "metamako.tdiff", - FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, - "Difference from capture timestamp", HFILL }} + { &hf_metamako_fcs, { + "Frame check sequence", "metamako.fcs", + FT_UINT32, BASE_HEX, NULL, 0x0, + "Ethernet checksum", HFILL }}, + + { &hf_metamako_fcs_status, { + "FCS Status", "metamako.fcs.status", + FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0, + NULL, HFILL }}, }; - static gint *ett[] = { + static gint* ett[] = { &ett_metamako, &ett_metamako_extensions, &ett_metamako_timestamp, &ett_metamako_flags }; + static ei_register_info ei[] = { + { &ei_metamako_fcs_bad, { "metamako.fcs_bad", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }}, + }; + + module_t* metamako_module; + + /* Register the Metamako trailer. */ proto_metamako = proto_register_protocol("Metamako ethernet trailer", "Metamako", "metamako"); + + /* Register header fields. */ proto_register_field_array(proto_metamako, hf, array_length(hf)); + + /* Register subtree types. */ proto_register_subtree_array(ett, array_length(ett)); + + /* Register the expert module. */ + expert_register_field_array(expert_register_protocol(proto_metamako), ei, array_length(ei)); + + /* Register configuration preferences */ + metamako_module = prefs_register_protocol(proto_metamako, NULL); + + range_convert_str(wmem_epan_scope(), &metamako_trailer_secs_bounds, TRAILER_SECS_BOUNDS_DFLT, 0xffffffff); + prefs_register_range_preference(metamako_module, "secs_bounds", + "Heuristic: Bounds of the seconds value in the trailer timestamp", + "If the trailer is found using heuristics, then the trailer may or may not be added " + "and the FCS may or may not be captured. One of the heuristics is the timestamp seconds " + "value being within specified bounds. " + "Set ranges of valid seconds to adjust this particular heuristic.", + &metamako_trailer_secs_bounds, 0xffffffff); + + prefs_register_uint_preference(metamako_module, "days_diff_limit", + "Heuristic: Max. number of days difference between capture and trailer timestamps", + "If the trailer is found using heuristics, then the trailer may or may not be added " + "and the FCS may or may not be captured. One of the heuristics is the number of days " + "difference between the capture (PCAP) timestamp and the Ethernet trailer timestamp. " + "Set an upper bound (in days) to adjust this particular heuristic.", + 10, &metamako_trailer_days_diff_limit); + + prefs_register_enum_preference(metamako_module, "trailer_present", + "Assume packets have a Metamako trailer", + "This option can override the trailer detection heuristic so that the Metamako " + "trailer is either never or always present.", + &metamako_trailer_present, metamako_trailer_present_vals, FALSE); + + prefs_register_enum_preference(metamako_module, "fcs", + "Assume packets have FCS", + "Some Ethernet adapters and drivers include the FCS at the end of a packet, others do not. " + "Some capture file formats and protocols do not indicate whether or not the FCS is included. " + "The Metamako dissector attempts to guess whether a captured packet has an FCS, " + "but it cannot always guess correctly. This option can override that heuristic " + "and assume that the FCS is either never or always present.", + &metamako_fcs_len, metamako_fcs_vals, FALSE); + + prefs_register_bool_preference(metamako_module, "check_fcs", + "Validate the Ethernet checksum if possible", + "Whether to validate the Frame Check Sequence", + &metamako_check_fcs); } void