2 * Routines for dissection of Metamako trailers. Forked from
3 * packet-vssmonitoring.c on 20th December, 2015.
4 * See https://www.metamako.com for further details.
6 * Copyright VSS-Monitoring 2011
7 * Copyright Metamako LP 2015
9 * 20111205 - First edition by Sake Blok (sake.blok@SYN-bit.nl)
10 * 20151220 - Forked to become packet-metamako.c
12 * Wireshark - Network traffic analyzer
13 * By Gerald Combs <gerald@wireshark.org>
14 * Copyright 1998 Gerald Combs
16 * SPDX-License-Identifier: GPL-2.0-or-later
21 #include <epan/packet.h>
24 #include <glib/gprintf.h>
26 void proto_register_metamako(void);
27 void proto_reg_handoff_metamako(void);
29 static int proto_metamako = -1;
31 static int hf_metamako_origfcs = -1;
32 static int hf_metamako_trailerext = -1;
33 static int hf_metamako_unknownext = -1;
34 static int hf_metamako_seqnum = -1;
35 static int hf_metamako_tagstring = -1;
36 static int hf_metamako_fracns = -1;
37 static int hf_metamako_time = -1;
38 static int hf_metamako_flags = -1;
39 static int hf_metamako_srcport = -1;
40 static int hf_metamako_srcdevice = -1;
41 static int hf_metamako_tdiff = -1;
43 static int hf_metamako_flags_orig_fcs_vld = -1;
44 static int hf_metamako_flags_has_ext = -1;
45 static int hf_metamako_reserved = -1;
47 static gint ett_metamako = -1;
48 static gint ett_metamako_timestamp = -1;
49 static gint ett_metamako_extensions = -1;
50 static gint ett_metamako_flags = -1;
52 static const int * flags[] = {
53 &hf_metamako_flags_orig_fcs_vld,
54 &hf_metamako_flags_has_ext,
55 &hf_metamako_reserved,
60 sub_nanos_base_custom(gchar *result, guint32 value)
63 temp_double = ((double)value) / (1U << 24);
64 g_snprintf(result, ITEM_LABEL_LENGTH, "%1.9fns", temp_double);
68 validate_metamako_timestamp(nstime_t *metamako_time, packet_info *pinfo)
70 if ( metamako_time->secs > 3600 && metamako_time->nsecs < 1000000000 ) {
71 if ( metamako_time->secs > pinfo->fd->abs_ts.secs ) {
72 if ( metamako_time->secs - pinfo->fd->abs_ts.secs > 2592000 ) /* 30 days */
75 if ( pinfo->fd->abs_ts.secs - metamako_time->secs > 2592000 ) /* 30 days */
86 dissect_metamako(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
90 proto_tree *ti, *parent, *item;
91 proto_tree *metamako_tree, *timestamp_tree;
92 proto_tree *extensions_tree;
96 guint captured_trailer_bytes;
97 guint metamako_trailer_bytes;
100 nstime_t metamako_time, timediff;
102 guint metamako_tlv_present;
104 guint metamako_tlv_count;
105 guint metamako_tlv_firstword;
106 guint metamako_tlv_len;
107 guint metamako_tlv_tag;
108 guint metamako_tlv_pos;
109 guint metamako_tlv_bytes;
111 guint8 metamako_srcport;
112 guint16 metamako_srcdevice;
113 guint8 metamako_flags;
117 /* The Metamako trailer is made up of:
118 4 bytes -- original FCS
119 N bytes -- trailer extensions
121 4 bytes -- nanoseconds
125 4 bytes -- New (valid) FCS (may or may not have been captured)
128 /* First get the captured length of the trailer */
129 captured_trailer_bytes = tvb_captured_length(tvb);
131 /* If we have less than 12 bytes captured, we can't
132 read the timestamp to confirm the heuristic */
133 if ( captured_trailer_bytes < 12 )
136 /* Init variables before loop */
137 metamako_trailer_bytes = captured_trailer_bytes;
138 metamako_tlv_count = 0;
139 metamako_tlv_bytes = 0;
140 metamako_tlv_present = 0;
142 /* Default state is no valid trailer found */
145 /* Loop through the trailer bytes, try to find a valid trailer.
147 * - First try the case there there IS NO trailing FCS
148 * - Second try the case where where IS a trailing FCS
150 for ( i = 0; i < 2 && metamako_trailer_bytes >= 12 && !trailer_valid; i++ ) {
151 /* Start at the tail of the trailer and work inwards */
152 metamako_meta = tvb_get_ntohl(tvb, metamako_trailer_bytes - 4);
153 metamako_flags = (metamako_meta >> 24) & 0xFF;
154 metamako_trailer_bytes -= 4;
155 metamako_time.nsecs = tvb_get_ntohl(tvb, metamako_trailer_bytes - 4);
156 metamako_trailer_bytes -= 4;
157 metamako_time.secs = tvb_get_ntohl(tvb, metamako_trailer_bytes - 4);
158 metamako_trailer_bytes -= 4;
160 /* Check the validity of the candidate timestamp */
161 if ( validate_metamako_timestamp(&metamako_time, pinfo) ) {
162 /* Check if the trailer has tlv extensions */
163 metamako_tlv_present = metamako_flags & 0x2;
164 metamako_tlv_bytes = 0;
165 metamako_tlv_count = 0;
166 if ( metamako_tlv_present ) {
167 /* Extensions are flagged as included, check if there is bytes
168 * to support this, and try to decode.
170 while ( metamako_trailer_bytes >= 4 ) {
171 /* Bytes are here, decode as TLVs */
172 metamako_tlv_firstword = tvb_get_ntohl(tvb, metamako_trailer_bytes - 4);
173 metamako_tlv_count++;
174 metamako_tlv_bytes += 4;
175 metamako_trailer_bytes -= 4;
178 metamako_tlv_len = (metamako_tlv_firstword >> 6) & 0x3;
180 /* If its a secondary tag header, extract the extended length */
181 if ( ( metamako_tlv_firstword & 0x1F ) == 0x1F )
182 metamako_tlv_len = ( (metamako_tlv_firstword >> 6) & 0x3FF ) + 1;
184 /* Skip over the data, find the next tag */
185 while ( metamako_tlv_len > 0 ) {
187 metamako_tlv_bytes += 4;
188 metamako_trailer_bytes -= 4;
191 /* If this is flagged as the last TLV, stop */
192 if ( ( metamako_tlv_firstword >> 5 ) & 0x1 ) {
198 /* There should be at least enough bytes for the Orig FCS left
199 * any bytes before this are padding
201 if ( metamako_trailer_bytes >= 4 ) {
202 /* Decrement by the number of bytes in the Orig FCS field */
203 metamako_trailer_bytes -= 4;
205 /* This byte offset is the beginning of the Metamako trailer */
206 offset = metamako_trailer_bytes;
208 /* If we've made it this far, it appears we've got a valid trailer */
213 /* If we didn't find a valid metamako trailer, try again using 4 bytes less */
214 if ( !trailer_valid ) {
215 captured_trailer_bytes -= 4;
216 metamako_trailer_bytes = captured_trailer_bytes;
220 /* Did we find a valid trailer? */
221 if ( !trailer_valid )
224 /* Everything looks good! Now dissect the trailer. */
225 ti = proto_tree_add_item(tree, proto_metamako, tvb, offset, (captured_trailer_bytes - offset), ENC_NA);
226 metamako_tree = proto_item_add_subtree(ti, ett_metamako);
229 proto_tree_add_item(metamako_tree, hf_metamako_origfcs, tvb, offset, 4, ENC_BIG_ENDIAN);
233 if ( metamako_tlv_present ) {
234 parent = proto_tree_add_item(metamako_tree, hf_metamako_trailerext, tvb, captured_trailer_bytes - 12 - metamako_tlv_bytes, metamako_tlv_bytes, ENC_NA);
235 extensions_tree = proto_item_add_subtree(parent, ett_metamako_extensions);
236 while ( metamako_tlv_count > 0 ) {
237 metamako_tlv_pos = captured_trailer_bytes - 16;
238 i = metamako_tlv_count;
240 /* Read TLV word and decode */
241 metamako_tlv_firstword = tvb_get_ntohl(tvb, metamako_tlv_pos);
242 metamako_tlv_len = ( metamako_tlv_firstword >> 6 ) & 0x3;
243 metamako_tlv_bytes = ( metamako_tlv_len * 4 ) + 3;
244 metamako_tlv_tag = ( metamako_tlv_firstword & 0x1F );
246 /* If this is a Secondary Tag Header, decode the tag extensions */
247 if ( ( metamako_tlv_firstword & 0x1F ) == 0x1F ) {
248 metamako_tlv_len = ( ( metamako_tlv_firstword >> 6 ) & 0x3FF ) + 1;
249 metamako_tlv_bytes = ( metamako_tlv_len * 4 );
250 metamako_tlv_tag += ( ( metamako_tlv_firstword >> 16 ) & 0xFFFF );
253 /* Decrement TLV count */
256 /* Skip over the data if this is not our destination */
258 metamako_tlv_pos -= ( metamako_tlv_len + 1 ) * 4;
262 /* We've skipped to the i-th TLV, decode it */
263 switch ( metamako_tlv_tag ) {
265 /* Sequence Number */
266 metamako_tlv_pos -= ( metamako_tlv_len + 1 ) * 4;
267 proto_tree_add_item(extensions_tree, hf_metamako_seqnum, tvb, metamako_tlv_pos + 5, 2, ENC_BIG_ENDIAN);
268 proto_item_append_text(parent, ", Sequence No: %d", tvb_get_ntohs(tvb, metamako_tlv_pos + 5));
269 /* Increment the offset by the Data + Tag size */
270 offset += ( metamako_tlv_len + 1 ) * 4;
274 /* Sub-nanoseconds */
275 metamako_tlv_pos -= ( metamako_tlv_len + 1 ) * 4;
276 proto_tree_add_item(extensions_tree, hf_metamako_fracns, tvb, metamako_tlv_pos + 4, 3, ENC_BIG_ENDIAN);
277 proto_item_append_text(parent, ", Sub-nanoseconds: %1.9fns", ((double)(tvb_get_ntohl(tvb, metamako_tlv_pos + 3) & 0x00FFFFFF)) / (1U << 24));
278 /* Increment the offset by the Data + Tag size */
279 offset += ( metamako_tlv_len + 1 ) * 4;
284 metamako_tlv_pos -= ( metamako_tlv_len + 1 ) * 4;
285 proto_tree_add_item(extensions_tree, hf_metamako_tagstring, tvb, metamako_tlv_pos + 4, metamako_tlv_len * 4, ENC_ASCII|ENC_NA);
286 /* Increment the offset by the Data + Tag size */
287 offset += ( metamako_tlv_len + 1 ) * 4;
291 /* Unknown tag: just print it's ID and Data */
292 metamako_tlv_pos -= ( metamako_tlv_len + 1 ) * 4;
293 item = proto_tree_add_item(extensions_tree, hf_metamako_unknownext, tvb, metamako_tlv_pos + 4, metamako_tlv_bytes, ENC_NA);
294 /* Start with the Tag */
295 proto_item_set_text(item, "Unknown Tag [0x%05x]: ", metamako_tlv_tag);
296 /* Iterate through the data appending as we go */
297 for ( j = 0; j < metamako_tlv_bytes; j++ ) {
298 proto_item_append_text(item, "%02x", tvb_get_guint8(tvb, metamako_tlv_pos + 4 + j));
299 if ( (28 + j*2) >= ITEM_LABEL_LENGTH ) {
300 proto_item_append_text(item, "...");
304 /* Increment the offset by the Data + Tag size */
305 offset += ( metamako_tlv_len + 1 ) * 4;
309 /* Decrement count as we've just decoded a TLV */
310 metamako_tlv_count--;
315 item = proto_tree_add_time(metamako_tree, hf_metamako_time, tvb, offset, 8, &metamako_time);
316 timestamp_tree = proto_item_add_subtree(item, ett_metamako_timestamp);
318 tmp = localtime(&metamako_time.secs);
320 proto_item_append_text(ti, ", Timestamp: %02d:%02d:%02d.%09ld",
321 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,(long)metamako_time.nsecs);
323 proto_item_append_text(ti, ", Timestamp: <Not representable>");
325 /* [Timestamp difference from pcap time] */
326 nstime_delta(&timediff, &metamako_time, &pinfo->fd->abs_ts);
327 item = proto_tree_add_time(timestamp_tree, hf_metamako_tdiff, tvb, offset, 8, &timediff);
328 PROTO_ITEM_SET_GENERATED(item);
333 proto_tree_add_bitmask(metamako_tree, tvb, offset, hf_metamako_flags, ett_metamako_flags, flags, ENC_BIG_ENDIAN);
337 metamako_srcdevice = tvb_get_ntohs(tvb, offset);
338 proto_tree_add_item(metamako_tree, hf_metamako_srcdevice, tvb, offset, 2, ENC_BIG_ENDIAN);
339 proto_item_append_text(ti, ", Source Device: %d", metamako_srcdevice);
343 metamako_srcport = tvb_get_guint8(tvb, offset);
344 proto_tree_add_item(metamako_tree, hf_metamako_srcport, tvb, offset, 1, ENC_BIG_ENDIAN);
345 proto_item_append_text(ti, ", Source Port: %d", metamako_srcport);
352 proto_register_metamako(void)
354 static hf_register_info hf[] = {
355 { &hf_metamako_origfcs, {
356 "Original FCS", "metamako.orig_fcs",
357 FT_UINT32, BASE_HEX, NULL, 0x0,
360 { &hf_metamako_trailerext, {
361 "Trailer Extensions", "metamako.ext",
362 FT_NONE, BASE_NONE, NULL, 0x0,
365 { &hf_metamako_unknownext, {
366 "Unknown Tag", "metamako.unknown",
367 FT_BYTES, BASE_NONE, NULL, 0x0,
370 { &hf_metamako_seqnum, {
371 "Sequence Number", "metamako.seqnum",
372 FT_UINT16, BASE_DEC, NULL, 0x0,
375 { &hf_metamako_fracns, {
376 "Sub-nanoseconds", "metamako.subns",
377 FT_UINT24, BASE_CUSTOM, CF_FUNC(sub_nanos_base_custom), 0x0,
380 { &hf_metamako_tagstring, {
381 "Tag String", "metamako.tagstring",
382 FT_STRING, BASE_NONE, NULL, 0x0,
385 { &hf_metamako_time, {
386 "Time Stamp", "metamako.time",
387 FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0,
390 {&hf_metamako_flags, {
391 "Flags", "metamako.flags",
392 FT_UINT8, BASE_HEX, NULL, 0x0,
395 {&hf_metamako_flags_orig_fcs_vld, {
396 "Original FCS Valid", "metamako.flags.orig_fcs_vld",
397 FT_BOOLEAN, 8, TFS(&tfs_valid_invalid), 0x01,
400 {&hf_metamako_flags_has_ext, {
401 "Has Trailer Extensions", "metamako.flags.has_extensions",
402 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x02,
405 {&hf_metamako_reserved, {
406 "Reserved", "metamako.reserved",
407 FT_UINT8, BASE_HEX, NULL, 0xFC,
410 { &hf_metamako_srcdevice, {
411 "Source Device ID", "metamako.srcdevice",
412 FT_UINT16, BASE_DEC, NULL, 0x0,
415 { &hf_metamako_srcport, {
416 "Source Port", "metamako.srcport",
417 FT_UINT16, BASE_DEC, NULL, 0x0,
420 { &hf_metamako_tdiff, {
421 "Time Difference", "metamako.tdiff",
422 FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
423 "Difference from capture timestamp", HFILL }}
426 static gint *ett[] = {
428 &ett_metamako_extensions,
429 &ett_metamako_timestamp,
433 proto_metamako = proto_register_protocol("Metamako ethernet trailer", "Metamako", "metamako");
434 proto_register_field_array(proto_metamako, hf, array_length(hf));
435 proto_register_subtree_array(ett, array_length(ett));
439 proto_reg_handoff_metamako(void)
441 heur_dissector_add("eth.trailer", dissect_metamako, "Metamako ethernet trailer", "metamako_eth", proto_metamako, HEURISTIC_ENABLE);
445 * Editor modelines - http://www.wireshark.org/tools/modelines.html
450 * indent-tabs-mode: nil
453 * ex: set shiftwidth=2 tabstop=8 expandtab:
454 * :indentSize=2:tabSize=8:noTabs=true: