aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSlava Bacherikov <slava@bacher09.org>2019-05-13 21:09:33 +0300
committerAnders Broman <a.broman58@gmail.com>2019-05-28 03:38:13 +0000
commit9fa13ff70d16049eaa16eedb43a51bd95456407b (patch)
treed05dc1a99520ef52b1a2cd60af688d8d79839ef3
parent1f75694f141de7ad75d157bc1a8dc93962bbde2d (diff)
[packet-gelf.c] Add dissector for GELF protocol
Add dissection for Graylog Extended Log Format (GELF) over UDP. Bug: 15776 Change-Id: Ie976a1dee8d3441532f209061aef5c804219f289 Reviewed-on: https://code.wireshark.org/review/33184 Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com> Reviewed-by: Anders Broman <a.broman58@gmail.com>
-rw-r--r--docbook/release-notes.adoc1
-rw-r--r--epan/dissectors/CMakeLists.txt1
-rw-r--r--epan/dissectors/packet-gelf.c432
3 files changed, 434 insertions, 0 deletions
diff --git a/docbook/release-notes.adoc b/docbook/release-notes.adoc
index 777a8b8f19..14a3ec8221 100644
--- a/docbook/release-notes.adoc
+++ b/docbook/release-notes.adoc
@@ -57,6 +57,7 @@ evolved Common Public Radio Interface (eCPRI)
Distributed Replicated Block Device (DRBD)
GSM/3GPP CBSP (Cell Broadcast Service Protocol)
NVM Express over Fabrics for TCP (nvme-tcp)
+Graylog Extended Log Format over UDP (GELF)
--
=== Updated Protocol Support
diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt
index ce23bc88bb..da7059ba27 100644
--- a/epan/dissectors/CMakeLists.txt
+++ b/epan/dissectors/CMakeLists.txt
@@ -1058,6 +1058,7 @@ set(DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-gearman.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-ged125.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-geneve.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/packet-gelf.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-geonw.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-gfp.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-gift.c
diff --git a/epan/dissectors/packet-gelf.c b/epan/dissectors/packet-gelf.c
new file mode 100644
index 0000000000..35456479dc
--- /dev/null
+++ b/epan/dissectors/packet-gelf.c
@@ -0,0 +1,432 @@
+/* packet-gelf.c
+ * Routines for Graylog Extended Log Format (GELF) dissection
+ *
+ * Slava Bacherikov <slava@bacher09.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <epan/packet.h>
+#include <epan/expert.h>
+#include <epan/to_str.h>
+#include <epan/reassemble.h>
+
+#define HEADER_GZIP 0x1f8b
+#define HEADER_CHUNKED 0x1e0f
+/* not sure if this really used
+seen this here: https://github.com/lusis/gelfd/blob/229cf5f1f913a35db648b195300d1aaae841d522/lib/gelfd.rb#L7 */
+#define HEADER_UNCOMPRESSED 0x1f3c
+#define HEADER_UNCOMPRESSED_PLAIN 0x7b22 // {", json payload without real header
+
+/* minimal size of json message with only required fields */
+#define MIN_PLAIN_MSG 48
+#define MIN_ZLIB_MSG 46
+
+/* make 32 bit message id from 64 bit message id */
+#define BUILD_MESSAGE_ID(X) ((X[3] << 3 | X[2] << 2 | X[1] << 1 | X[0]) ^ \
+ (X[4] << 3 | X[5] << 2 | X[6] << 1 | X[7]))
+
+
+void proto_register_gelf(void);
+void proto_reg_handoff_gelf(void);
+
+static dissector_handle_t json_handle;
+static int proto_gelf = -1;
+static dissector_handle_t gelf_udp_handle;
+
+static gint ett_gelf = -1;
+static gint hf_gelf_pdu_type = -1;
+static gint hf_gelf_pdu_message_id = -1;
+static gint hf_gelf_pdu_chunk_number = -1;
+static gint hf_gelf_pdu_chunk_count = -1;
+static gint hf_gelf_pdu_chunked = -1;
+
+static const value_string gelf_udp_types[] = {
+ { HEADER_GZIP, "gzip" },
+ { 0x7801, "zlib" },
+ { 0x785e, "zlib" },
+ { 0x789c, "zlib" },
+ { 0x78da, "zlib" },
+ { HEADER_CHUNKED, "chunked" },
+ { HEADER_UNCOMPRESSED, "uncompressed" },
+ { HEADER_UNCOMPRESSED_PLAIN, "uncompressed plain json" },
+ { 0, NULL }
+};
+
+static reassembly_table gelf_udp_reassembly_table;
+
+static gint ett_gelf_fragment = -1;
+static gint ett_gelf_fragments = -1;
+
+static int hf_gelf_fragments = -1;
+static int hf_gelf_fragment = -1;
+static int hf_gelf_fragment_overlap = -1;
+static int hf_gelf_fragment_overlap_conflict = -1;
+static int hf_gelf_fragment_multiple_tails = -1;
+static int hf_gelf_fragment_too_long_fragment = -1;
+static int hf_gelf_fragment_error = -1;
+static int hf_gelf_fragment_count = -1;
+static int hf_gelf_reassembled_in = -1;
+static int hf_gelf_reassembled_length = -1;
+
+static const fragment_items gelf_fragment_items = {
+ &ett_gelf_fragment,
+ &ett_gelf_fragments,
+ &hf_gelf_fragments,
+ &hf_gelf_fragment,
+ &hf_gelf_fragment_overlap,
+ &hf_gelf_fragment_overlap_conflict,
+ &hf_gelf_fragment_multiple_tails,
+ &hf_gelf_fragment_too_long_fragment,
+ &hf_gelf_fragment_error,
+ &hf_gelf_fragment_count,
+ &hf_gelf_reassembled_in,
+ &hf_gelf_reassembled_length,
+ NULL,
+ "GELF fragments"
+};
+
+static expert_field ei_gelf_invalid_header = EI_INIT;
+static expert_field ei_gelf_broken_compression = EI_INIT;
+
+static inline gboolean
+is_simple_zlib(guint16 header) {
+ return header == 0x7801 || header == 0x785e || header == 0x789c || header == 0x78da;
+}
+
+static int
+dissect_gelf_simple_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint16 header,
+ proto_item* pdu_item)
+{
+ int len;
+ tvbuff_t *next_tvb;
+
+ len = tvb_captured_length(tvb);
+ if (header == HEADER_GZIP || is_simple_zlib(header)) {
+ next_tvb = tvb_uncompress(tvb, 0, len);
+ if (next_tvb) {
+ add_new_data_source(pinfo, next_tvb, "compressed data");
+ call_dissector(json_handle, next_tvb, pinfo, tree);
+ } else {
+ expert_add_info_format(pinfo, pdu_item, &ei_gelf_broken_compression,
+ "Can't uncompress message");
+ }
+ return len;
+ } else if (header == HEADER_UNCOMPRESSED) {
+ next_tvb = tvb_new_subset_remaining(tvb, 2);
+ if (next_tvb) {
+ call_dissector(json_handle, next_tvb, pinfo, tree);
+ }
+ return len;
+ } else if (header == HEADER_UNCOMPRESSED_PLAIN) {
+ if (call_dissector(json_handle, tvb, pinfo, tree) == 0) {
+ return 0;
+ }
+ return len;
+ }
+ return 0;
+}
+
+static int
+dissect_gelf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean heur_check)
+{
+ guint16 header;
+ guint captured_length;
+ proto_item *it;
+
+ captured_length = tvb_captured_length(tvb);
+
+ if (captured_length < 2)
+ return 0;
+
+ header = tvb_get_ntohs(tvb, 0);
+
+ if (heur_check) {
+ guint min_len;
+ guint8 number, count;
+
+ switch(header) {
+ case HEADER_GZIP:
+ min_len = MIN_ZLIB_MSG;
+ break;
+ case HEADER_UNCOMPRESSED_PLAIN:
+ min_len = MIN_PLAIN_MSG;
+ break;
+ case HEADER_UNCOMPRESSED:
+ min_len = MIN_PLAIN_MSG + 2;
+ break;
+ case HEADER_CHUNKED:
+ /* 10 bytes is chunked header + 2 bytes of data */
+ min_len = 10 + 2;
+ break;
+ default:
+ if (is_simple_zlib(header)) {
+ min_len = MIN_ZLIB_MSG;
+ } else {
+ return 0;
+ }
+ break;
+ }
+
+ if (tvb_reported_length(tvb) < min_len)
+ return 0;
+
+ if (header == HEADER_CHUNKED && captured_length >= 10) {
+ number = tvb_get_guint8(tvb, 10);
+ count = tvb_get_guint8(tvb, 11);
+ if (number >= count)
+ return 0;
+ }
+ }
+
+
+ proto_item *ti = proto_tree_add_item(tree, proto_gelf, tvb, 0, -1, ENC_NA);
+ proto_tree *gelf_tree = proto_item_add_subtree(ti, ett_gelf);
+ proto_item *pdu_item = proto_tree_add_item(gelf_tree, hf_gelf_pdu_type, tvb, 0, 2, ENC_BIG_ENDIAN);
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "GELF");
+
+ if (header == HEADER_CHUNKED) {
+ guint32 number, count, short_id, data_len;
+ GByteArray *bytes;
+ char message_id[17];
+ gboolean more_frags;
+ fragment_head *fd_head;
+
+ message_id[0] = '\0';
+ bytes = g_byte_array_sized_new(8);
+
+ it = proto_tree_add_boolean(gelf_tree, hf_gelf_pdu_chunked, tvb, 0, 2, TRUE);
+ proto_item_set_generated(it);
+ proto_tree_add_bytes_item(gelf_tree, hf_gelf_pdu_message_id, tvb, 2, 8, ENC_BIG_ENDIAN, bytes,
+ NULL, NULL);
+ proto_tree_add_item_ret_uint(gelf_tree, hf_gelf_pdu_chunk_number, tvb, 10, 1, ENC_BIG_ENDIAN,
+ &number);
+ proto_tree_add_item_ret_uint(gelf_tree, hf_gelf_pdu_chunk_count, tvb, 11, 1, ENC_BIG_ENDIAN,
+ &count);
+ bytes_to_hexstr(message_id, bytes->data, 8);
+ message_id[16] = '\0';
+ // HACK: convert 64 bit message id to 32 bit :)
+ short_id = BUILD_MESSAGE_ID(bytes->data);
+ g_byte_array_free(bytes, TRUE);
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Chunked packet: id: %s, number %u, count %u", message_id,
+ number, count);
+ data_len = tvb_captured_length_remaining(tvb, 12);
+ more_frags = (count == number + 1) ? FALSE : TRUE;
+ fd_head = fragment_add_seq_check(&gelf_udp_reassembly_table, tvb, 12, pinfo, short_id, NULL, number,
+ data_len, more_frags);
+ if (fd_head != NULL) {
+ tvbuff_t *newtvb;
+ newtvb = process_reassembled_data(tvb, 12, pinfo, "Reassembled GELF", fd_head,
+ &gelf_fragment_items, NULL, gelf_tree);
+ if (newtvb != NULL) {
+ guint16 newheader = tvb_get_ntohs(newtvb, 0);
+ dissect_gelf_simple_udp(newtvb, pinfo, tree, newheader, pdu_item);
+ }
+ }
+ return captured_length;
+ } else {
+ it = proto_tree_add_boolean(gelf_tree, hf_gelf_pdu_chunked, tvb, 0, 2, FALSE);
+ proto_item_set_generated(it);
+
+ switch(header) {
+ case HEADER_GZIP:
+ col_set_str(pinfo->cinfo, COL_INFO, "GZIP");
+ break;
+ case HEADER_UNCOMPRESSED_PLAIN:
+ col_set_str(pinfo->cinfo, COL_INFO, "uncompressed plain");
+ break;
+ case HEADER_UNCOMPRESSED:
+ col_set_str(pinfo->cinfo, COL_INFO, "uncompressed");
+ break;
+ default:
+ if (is_simple_zlib(header)) {
+ col_set_str(pinfo->cinfo, COL_INFO, "ZLIB");
+ } else {
+ expert_add_info_format(pinfo, pdu_item, &ei_gelf_invalid_header,
+ "Invalid header magic");
+ return 0;
+ }
+ break;
+ }
+
+ return dissect_gelf_simple_udp(tvb, pinfo, tree, header, pdu_item);
+ }
+}
+
+static int
+dissect_gelf_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
+ return dissect_gelf(tvb, pinfo, tree, FALSE);
+}
+
+static gboolean
+dissect_gelf_heur_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
+{
+ if (dissect_gelf(tvb, pinfo, tree, TRUE) > 0) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+void
+proto_register_gelf(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_gelf_pdu_type,
+ {
+ "GELF Type", "gelf.type", FT_UINT16,
+ BASE_HEX, VALS(gelf_udp_types), 0x0,
+ NULL, HFILL
+ }
+ },
+ { &hf_gelf_pdu_message_id,
+ {
+ "Message id", "gelf.chunk.msg_id", FT_BYTES,
+ BASE_NONE, NULL, 0x0,
+ NULL, HFILL
+ }
+ },
+ { &hf_gelf_pdu_chunk_number,
+ {
+ "Chunk number", "gelf.chunk.number", FT_UINT8,
+ BASE_DEC, NULL, 0x0,
+ NULL, HFILL
+ }
+ },
+ { &hf_gelf_pdu_chunk_count,
+ {
+ "Chunk count", "gelf.chunk.count", FT_UINT8,
+ BASE_DEC, NULL, 0x0,
+ NULL, HFILL
+ }
+ },
+ { &hf_gelf_pdu_chunked,
+ {
+ "Chunked message", "gelf.chunked", FT_BOOLEAN,
+ BASE_NONE, NULL, 0x0,
+ NULL, HFILL
+ }
+ }
+ /* Fragmentation */,
+ { &hf_gelf_fragments,
+ {
+ "GELF fragments", "gelf.fragments", FT_NONE, BASE_NONE,
+ NULL, 0x00, NULL, HFILL
+ }
+ },
+ { &hf_gelf_fragment,
+ {
+ "GELF fragment", "gelf.fragment", FT_FRAMENUM, BASE_NONE,
+ NULL, 0x00, NULL, HFILL
+ }
+ },
+ { &hf_gelf_fragment_overlap,
+ {
+ "GELF fragment overlap", "gelf.fragment.overlap", FT_BOOLEAN,
+ BASE_NONE, NULL, 0x00, NULL, HFILL
+ }
+ },
+ { &hf_gelf_fragment_overlap_conflict,
+ {
+ "GELF fragment overlapping with conflicting data",
+ "gelf.fragment.overlap.conflicts", FT_BOOLEAN, BASE_NONE,
+ NULL, 0x00, NULL, HFILL
+ }
+ },
+ { &hf_gelf_fragment_multiple_tails,
+ {
+ "GELF has multiple tail fragments",
+ "gelf.fragment.multiple_tails", FT_BOOLEAN, BASE_NONE,
+ NULL, 0x00, NULL, HFILL
+ }
+ },
+ { &hf_gelf_fragment_too_long_fragment,
+ {
+ "GELF fragment too long", "gelf.fragment.too_long_fragment",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL
+ }
+ },
+ { &hf_gelf_fragment_error,
+ {
+ "GELF defragmentation error", "gelf.fragment.error", FT_FRAMENUM,
+ BASE_NONE, NULL, 0x00, NULL, HFILL
+ }
+ },
+ { &hf_gelf_fragment_count,
+ {
+ "GELF fragment count", "gelf.fragment.count", FT_UINT32, BASE_DEC,
+ NULL, 0x00, NULL, HFILL
+ }
+ },
+ { &hf_gelf_reassembled_in,
+ {
+ "Reassembled GELF in frame", "gelf.reassembled.in", FT_FRAMENUM, BASE_NONE,
+ NULL, 0x00, "This GELF packet is reassembled in this frame", HFILL
+ }
+ },
+ { &hf_gelf_reassembled_length,
+ {
+ "Reassembled GELF length", "gelf.reassembled.length", FT_UINT32, BASE_DEC,
+ NULL, 0x00, "The total length of the reassembled payload", HFILL
+ }
+ },
+ };
+
+ static ei_register_info ei_gelf[] = {
+ { &ei_gelf_invalid_header,
+ {
+ "gelf.invalid_header", PI_MALFORMED, PI_ERROR, "Invalid header", EXPFILL
+ }
+ },
+ { &ei_gelf_broken_compression,
+ {
+ "gelf.broken_compression", PI_MALFORMED, PI_ERROR, "Can't unpack message", EXPFILL
+ }
+ }
+ };
+
+ static gint *ett[] = {
+ &ett_gelf,
+ &ett_gelf_fragment,
+ &ett_gelf_fragments
+ };
+
+ expert_module_t *expert_gelf;
+
+ proto_gelf = proto_register_protocol("Graylog Extended Log Format", "GELF", "gelf");
+ gelf_udp_handle = register_dissector("gelf-udp", dissect_gelf_udp, proto_gelf);
+ proto_register_field_array(proto_gelf, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+ expert_gelf = expert_register_protocol(proto_gelf);
+ expert_register_field_array(expert_gelf, ei_gelf, array_length(ei_gelf));
+ reassembly_table_register(&gelf_udp_reassembly_table, &addresses_reassembly_table_functions);
+}
+
+
+void
+proto_reg_handoff_gelf(void)
+{
+ dissector_add_for_decode_as("udp.port", gelf_udp_handle);
+ heur_dissector_add("udp", dissect_gelf_heur_udp, "GELF over UDP", "gelf_udp", proto_gelf,
+ HEURISTIC_DISABLE);
+ json_handle = find_dissector_add_dependency("json", proto_gelf);
+}
+
+/*
+ * 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:
+ */