Follow stream: Modify YAML format, add timestamps and peers

Modify YAML output format so it includes information about peers and
absolute timestamps for each packet.
This also adds yaml output to tshark: -z follow,tcp,yaml,X
This commit is contained in:
Toff 2020-09-09 17:06:15 +02:00 committed by Wireshark GitLab Utility
parent 48ba793ef6
commit 2df04e5bb0
10 changed files with 202 additions and 23 deletions

View File

@ -1195,6 +1195,7 @@ I<mode> specifies the output mode. It can be one of:
ebcdic EBCDIC output with dots for non-printable characters ebcdic EBCDIC output with dots for non-printable characters
hex Hexadecimal and ASCII data with offsets hex Hexadecimal and ASCII data with offsets
raw Hexadecimal data raw Hexadecimal data
yaml YAML format
Since the output in B<ascii> or B<ebcdic> mode may contain newlines, the length Since the output in B<ascii> or B<ebcdic> mode may contain newlines, the length
of each section of output plus a newline precedes each section of output. of each section of output plus a newline precedes each section of output.

View File

@ -69,6 +69,9 @@ They previously shipped with Npcap 1.20.
* Follow stream is now able to follow SIP call by SIP.Call-ID * Follow stream is now able to follow SIP call by SIP.Call-ID
* Follow stream YAML output formats has been changed to add timestamps and peers information (for more details see the users guide,
{wireshark-users-guide-url}/ChAdvFollowStreamSection.html[Following Protocol Streams])
// === Removed Features and Support // === Removed Features and Support
// === Removed Dissectors // === Removed Dissectors

View File

@ -97,6 +97,78 @@ menu:UTF-16[]:: Like ASCII, but decode the data as UTF-16.
menu:YAML[]:: This allows you to load the stream as YAML. menu:YAML[]:: This allows you to load the stream as YAML.
The YAML output is divided into 2 main sections:
* The `peers` section where for each `peer` you found the peer index, the `host` address and the `port` number.
* The `packets` section where for each `packet` you found the packet number in the original capture, the `peer` index,
the packet `index` for this peer, the `timestamp` in seconds and the `data` in base64 encoding.
.Follow Stream YAML output
====
[source,yaml]
----
peers:
- peer: 0
host: 127.0.0.1
port: 54048
- peer: 1
host: 127.0.10.1
port: 5000
packets:
- packet: 1
peer: 0
index: 0
timestamp: 1599485409.693955274
data: !!binary |
aGVsbG8K
- packet: 3
peer: 1
index: 0
timestamp: 1599485423.885866692
data: !!binary |
Ym9uam91cgo=
----
====
The same example but in old YAML format (before version 3.5):
[source,yaml]
----
# Packet 1
peer0_0: !!binary |
aGVsbG8K
# Packet 3
peer1_0: !!binary |
Ym9uam91cgo=
----
How the old format data can be found in the new format:
[options="header"]
|===
|New YAML format |Old YAML format |
a|
----
...
packets:
- packet: AAA
peer: BBB
index: CCC
data: !!binary \|
DDD
----
a|
----
# Packet AAA
peerBBB_CCC !!binary \|
DDD
----
a|
AAA: packet number in the original capture
BBB: peer index
CCC: packet index for this peer
DDD: data in base64 encoding
|===
menu:Raw[]:: This allows you to load the unaltered stream data into a different menu:Raw[]:: This allows you to load the unaltered stream data into a different
program for further examination. The display will look the same as the ASCII program for further examination. The display will look the same as the ASCII
setting, but “Save As” will result in a binary file. setting, but “Save As” will result in a binary file.

View File

@ -1089,6 +1089,7 @@ check_follow_fragments(follow_info_t *follow_info, gboolean is_server, guint32 a
follow_record->is_server = is_server; follow_record->is_server = is_server;
follow_record->packet_num = fragment->packet_num; follow_record->packet_num = fragment->packet_num;
follow_record->abs_ts = fragment->abs_ts;
follow_record->seq = follow_info->seq[is_server] + new_frag_size; follow_record->seq = follow_info->seq[is_server] + new_frag_size;
follow_record->data = g_byte_array_append(g_byte_array_new(), follow_record->data = g_byte_array_append(g_byte_array_new(),
@ -1222,6 +1223,7 @@ follow_tcp_tap_listener(void *tapdata, packet_info *pinfo,
follow_record = g_new0(follow_record_t, 1); follow_record = g_new0(follow_record_t, 1);
follow_record->is_server = is_server; follow_record->is_server = is_server;
follow_record->packet_num = pinfo->fd->num; follow_record->packet_num = pinfo->fd->num;
follow_record->abs_ts = pinfo->fd->abs_ts;
follow_record->seq = sequence; /* start of fragment, used by check_follow_fragments. */ follow_record->seq = sequence; /* start of fragment, used by check_follow_fragments. */
follow_record->data = g_byte_array_append(g_byte_array_new(), follow_record->data = g_byte_array_append(g_byte_array_new(),
tvb_get_ptr(follow_data->tvb, data_offset, data_length), tvb_get_ptr(follow_data->tvb, data_offset, data_length),

View File

@ -463,6 +463,7 @@ ssl_follow_tap_listener(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _
follow_record->is_server = (from == FROM_SERVER); follow_record->is_server = (from == FROM_SERVER);
follow_record->packet_num = pinfo->num; follow_record->packet_num = pinfo->num;
follow_record->abs_ts = pinfo->abs_ts;
follow_record->data = g_byte_array_sized_new(appl_data->data_len); follow_record->data = g_byte_array_sized_new(appl_data->data_len);
follow_record->data = g_byte_array_append(follow_record->data, follow_record->data = g_byte_array_append(follow_record->data,

View File

@ -190,6 +190,7 @@ follow_tvb_tap_listener(void *tapdata, packet_info *pinfo,
tvb_get_ptr(next_tvb, 0, -1), tvb_get_ptr(next_tvb, 0, -1),
tvb_captured_length(next_tvb)); tvb_captured_length(next_tvb));
follow_record->packet_num = pinfo->fd->num; follow_record->packet_num = pinfo->fd->num;
follow_record->abs_ts = pinfo->fd->abs_ts;
if (follow_info->client_port == 0) { if (follow_info->client_port == 0) {
follow_info->client_port = pinfo->srcport; follow_info->client_port = pinfo->srcport;

View File

@ -82,6 +82,7 @@ typedef struct {
gboolean is_server; gboolean is_server;
guint32 packet_num; guint32 packet_num;
guint32 seq; /* TCP only */ guint32 seq; /* TCP only */
nstime_t abs_ts; /**< Packet absolute time stamp */
GByteArray *data; GByteArray *data;
} follow_record_t; } follow_record_t;

View File

@ -53,6 +53,7 @@ typedef struct _cli_follow_info {
#define STR_ASCII ",ascii" #define STR_ASCII ",ascii"
#define STR_EBCDIC ",ebcdic" #define STR_EBCDIC ",ebcdic"
#define STR_RAW ",raw" #define STR_RAW ",raw"
#define STR_YAML ",yaml"
WS_NORETURN static void follow_exit(const char *strp) WS_NORETURN static void follow_exit(const char *strp)
{ {
@ -68,6 +69,7 @@ static const char * follow_str_type(cli_follow_info_t* cli_follow_info)
case SHOW_ASCII: return "ascii"; case SHOW_ASCII: return "ascii";
case SHOW_EBCDIC: return "ebcdic"; case SHOW_EBCDIC: return "ebcdic";
case SHOW_RAW: return "raw"; case SHOW_RAW: return "raw";
case SHOW_YAML: return "yaml";
default: default:
g_assert_not_reached(); g_assert_not_reached();
break; break;
@ -168,22 +170,43 @@ static void follow_draw(void *contextp)
GList *cur; GList *cur;
follow_record_t *follow_record; follow_record_t *follow_record;
guint chunk; guint chunk;
gchar *b64encoded;
const guint32 base64_raw_len = 57; /* Encodes to 76 bytes, common in RFCs */
printf("\n%s", separator); /* Print header */
printf("Follow: %s,%s\n", proto_get_protocol_filter_name(get_follow_proto_id(cli_follow_info->follower)), follow_str_type(cli_follow_info)); switch (cli_follow_info->show_type)
printf("Filter: %s\n", follow_info->filter_out_filter); {
case SHOW_YAML:
printf("peers:\n");
printf(" - peer: 0\n");
address_to_str_buf(&follow_info->client_ip, buf, sizeof buf);
printf(" host: %s\n", buf);
printf(" port: %d\n", follow_info->client_port);
printf(" - peer: 1\n");
address_to_str_buf(&follow_info->server_ip, buf, sizeof buf);
printf(" host: %s\n", buf);
printf(" port: %d\n", follow_info->server_port);
printf("packets:\n");
break;
address_to_str_buf(&follow_info->client_ip, buf, sizeof buf); default:
if (follow_info->client_ip.type == AT_IPv6) printf("\n%s", separator);
printf("Node 0: [%s]:%u\n", buf, follow_info->client_port); printf("Follow: %s,%s\n", proto_get_protocol_filter_name(get_follow_proto_id(cli_follow_info->follower)), follow_str_type(cli_follow_info));
else printf("Filter: %s\n", follow_info->filter_out_filter);
printf("Node 0: %s:%u\n", buf, follow_info->client_port);
address_to_str_buf(&follow_info->server_ip, buf, sizeof buf); address_to_str_buf(&follow_info->client_ip, buf, sizeof buf);
if (follow_info->client_ip.type == AT_IPv6) if (follow_info->client_ip.type == AT_IPv6)
printf("Node 1: [%s]:%u\n", buf, follow_info->server_port); printf("Node 0: [%s]:%u\n", buf, follow_info->client_port);
else else
printf("Node 1: %s:%u\n", buf, follow_info->server_port); printf("Node 0: %s:%u\n", buf, follow_info->client_port);
address_to_str_buf(&follow_info->server_ip, buf, sizeof buf);
if (follow_info->server_ip.type == AT_IPv6)
printf("Node 1: [%s]:%u\n", buf, follow_info->server_port);
else
printf("Node 1: %s:%u\n", buf, follow_info->server_port);
break;
}
for (cur = g_list_last(follow_info->payload), chunk = 1; for (cur = g_list_last(follow_info->payload), chunk = 1;
cur != NULL; cur != NULL;
@ -202,9 +225,11 @@ static void follow_draw(void *contextp)
continue; continue;
} }
/* Print start of line */
switch (cli_follow_info->show_type) switch (cli_follow_info->show_type)
{ {
case SHOW_HEXDUMP: case SHOW_HEXDUMP:
case SHOW_YAML:
break; break;
case SHOW_ASCII: case SHOW_ASCII:
@ -218,10 +243,12 @@ static void follow_draw(void *contextp)
putchar('\t'); putchar('\t');
} }
break; break;
default: default:
g_assert_not_reached(); g_assert_not_reached();
} }
/* Print data */
switch (cli_follow_info->show_type) switch (cli_follow_info->show_type)
{ {
case SHOW_HEXDUMP: case SHOW_HEXDUMP:
@ -271,12 +298,38 @@ static void follow_draw(void *contextp)
g_free(buffer); g_free(buffer);
break; break;
case SHOW_YAML:
printf(" - packet: %d\n", follow_record->packet_num);
printf(" peer: %d\n", follow_record->is_server ? 1 : 0);
printf(" timestamp: %" G_GINT64_MODIFIER "u.%09d\n", follow_record->abs_ts.secs, follow_record->abs_ts.nsecs);
printf(" data: !!binary |\n");
ii = 0;
while (ii < follow_record->data->len) {
guint32 len = ii + base64_raw_len < follow_record->data->len
? base64_raw_len
: follow_record->data->len - ii;
b64encoded = g_base64_encode(&follow_record->data->data[ii], len);
printf(" %s\n", b64encoded);
g_free(b64encoded);
ii += len;
}
break;
default: default:
g_assert_not_reached(); g_assert_not_reached();
} }
} }
printf("%s", separator); /* Print footer */
switch (cli_follow_info->show_type)
{
case SHOW_YAML:
break;
default:
printf("%s", separator);
break;
}
} }
static gboolean follow_arg_strncmp(const char **opt_argp, const char *strp) static gboolean follow_arg_strncmp(const char **opt_argp, const char *strp)
@ -312,6 +365,10 @@ follow_arg_mode(const char **opt_argp, follow_info_t *follow_info)
{ {
cli_follow_info->show_type = SHOW_RAW; cli_follow_info->show_type = SHOW_RAW;
} }
else if (follow_arg_strncmp(opt_argp, STR_YAML))
{
cli_follow_info->show_type = SHOW_YAML;
}
else else
{ {
follow_exit("Invalid display mode."); follow_exit("Invalid display mode.");

View File

@ -554,7 +554,7 @@ FollowStreamDialog::followStream()
} }
const int FollowStreamDialog::max_document_length_ = 500 * 1000 * 1000; // Just a guess const int FollowStreamDialog::max_document_length_ = 500 * 1000 * 1000; // Just a guess
void FollowStreamDialog::addText(QString text, gboolean is_from_server, guint32 packet_num) void FollowStreamDialog::addText(QString text, gboolean is_from_server, guint32 packet_num, gboolean colorize)
{ {
if (truncated_) { if (truncated_) {
return; return;
@ -569,8 +569,12 @@ void FollowStreamDialog::addText(QString text, gboolean is_from_server, guint32
setUpdatesEnabled(false); setUpdatesEnabled(false);
int cur_pos = ui->teStreamContent->verticalScrollBar()->value(); int cur_pos = ui->teStreamContent->verticalScrollBar()->value();
ui->teStreamContent->moveCursor(QTextCursor::End); ui->teStreamContent->moveCursor(QTextCursor::End);
QTextCharFormat tcf = ui->teStreamContent->currentCharFormat(); QTextCharFormat tcf = ui->teStreamContent->currentCharFormat();
if (is_from_server) { if (!colorize) {
tcf.setBackground(palette().window().color());
tcf.setForeground(palette().windowText().color());
} else if (is_from_server) {
tcf.setForeground(ColorUtils::fromColorT(prefs.st_server_fg)); tcf.setForeground(ColorUtils::fromColorT(prefs.st_server_fg));
tcf.setBackground(ColorUtils::fromColorT(prefs.st_server_bg)); tcf.setBackground(ColorUtils::fromColorT(prefs.st_server_bg));
} else { } else {
@ -654,7 +658,7 @@ static inline void sanitize_buffer(char *buffer, size_t nchars) {
frs_return_t frs_return_t
FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_server, guint32 packet_num, FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_server, guint32 packet_num,
guint32 *global_pos) nstime_t abs_ts, guint32 *global_pos)
{ {
gchar initbuf[256]; gchar initbuf[256];
guint32 current_pos; guint32 current_pos;
@ -795,17 +799,53 @@ FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_ser
const int base64_raw_len = 57; // Encodes to 76 bytes, common in RFCs const int base64_raw_len = 57; // Encodes to 76 bytes, common in RFCs
current_pos = 0; current_pos = 0;
if (last_packet_ == 0) {
// Header with general info about peers
const char *hostname0 = address_to_name(&follow_info_.client_ip);
const char *hostname1 = address_to_name(&follow_info_.server_ip);
char *port0 = get_follow_port_to_display(follower_)(NULL, follow_info_.client_port);
char *port1 = get_follow_port_to_display(follower_)(NULL, follow_info_.server_port);
addText("peers:\n", false, 0, false);
addText(QString(
" - peer: 0\n"
" host: %1\n"
" port: %2\n")
.arg(hostname0)
.arg(port0), false, 0);
addText(QString(
" - peer: 1\n"
" host: %1\n"
" port: %2\n")
.arg(hostname1)
.arg(port1), true, 0);
wmem_free(NULL, port0);
wmem_free(NULL, port1);
addText("packets:\n", false, 0, false);
}
if (packet_num != last_packet_) { if (packet_num != last_packet_) {
yaml_text.append(QString("# Packet %1\npeer%2_%3: !!binary |\n") yaml_text.append(QString(" - packet: %1\n")
.arg(packet_num) .arg(packet_num));
.arg(is_from_server ? 1 : 0) yaml_text.append(QString(" peer: %1\n")
.arg(is_from_server ? 1 : 0));
yaml_text.append(QString(" index: %1\n")
.arg(is_from_server ? server_buffer_count_++ : client_buffer_count_++)); .arg(is_from_server ? server_buffer_count_++ : client_buffer_count_++));
yaml_text.append(QString(" timestamp: %1.%2\n")
.arg(abs_ts.secs)
.arg(abs_ts.nsecs, 9, 10, QChar('0')));
yaml_text.append(QString(" data: !!binary |\n"));
} }
while (current_pos < nchars) { while (current_pos < nchars) {
int len = current_pos + base64_raw_len < nchars ? base64_raw_len : (int) nchars - current_pos; int len = current_pos + base64_raw_len < nchars ? base64_raw_len : (int) nchars - current_pos;
QByteArray base64_data(&buffer[current_pos], len); QByteArray base64_data(&buffer[current_pos], len);
yaml_text += " " + base64_data.toBase64() + "\n"; yaml_text += " " + base64_data.toBase64() + "\n";
current_pos += len; current_pos += len;
(*global_pos) += len; (*global_pos) += len;
@ -1160,6 +1200,7 @@ FollowStreamDialog::readFollowStream()
follow_record->data->len, follow_record->data->len,
follow_record->is_server, follow_record->is_server,
follow_record->packet_num, follow_record->packet_num,
follow_record->abs_ts,
global_pos); global_pos);
if (frs_return == FRS_PRINT_ERROR) if (frs_return == FRS_PRINT_ERROR)
return frs_return; return frs_return;

View File

@ -83,14 +83,14 @@ private:
void updateWidgets() { updateWidgets(false); } // Needed for WiresharkDialog? void updateWidgets() { updateWidgets(false); } // Needed for WiresharkDialog?
frs_return_t frs_return_t
showBuffer(char *buffer, size_t nchars, gboolean is_from_server, showBuffer(char *buffer, size_t nchars, gboolean is_from_server,
guint32 packet_num, guint32 *global_pos); guint32 packet_num, nstime_t abs_ts, guint32 *global_pos);
frs_return_t readStream(); frs_return_t readStream();
frs_return_t readFollowStream(); frs_return_t readFollowStream();
frs_return_t readSslStream(); frs_return_t readSslStream();
void followStream(); void followStream();
void addText(QString text, gboolean is_from_server, guint32 packet_num); void addText(QString text, gboolean is_from_server, guint32 packet_num, gboolean colorize = true);
Ui::FollowStreamDialog *ui; Ui::FollowStreamDialog *ui;