aboutsummaryrefslogtreecommitdiffstats
path: root/epan
diff options
context:
space:
mode:
authorMarkku Leiniö <markku@iki.fi>2023-07-08 22:59:21 +0300
committerMarkku Leiniö <markku@iki.fi>2023-07-24 17:38:15 +0300
commit261c2f24cc08fa49504e93ca03381bb1aa9e19e8 (patch)
tree9d3f8830d8d761e2985a16044da4141194f645ce /epan
parent6885d787fda5f74a2d1f9eeea443fecf8dd58528 (diff)
Add Zabbix protocol dissector
Features: - Supports also compressed and TLS-encrypted Zabbix connections as well as TCP desegmenting - Dissects both passive agent connections (10050/tcp, plaintext-based) and active agent, proxy and sender/trapper connections (10051/tcp, JSON-based), ports are configurable - Detects passive agent conversations by checking the request being non-JSON (not depending on the well-known TCP ports) - Calculates response times using protocol data saved in conversations - Detects the connection type (proxy, agent, sender/trapper) and shows tree and Info column information accordingly - Dissects protocols up to Zabbix version 6.4 (currently latest) and 7.0 (currently in alpha) - Does not support passive agent connections in Zabbix 3.x or earlier (it does not have the normal Zabbix header; note that Zabbix 4.0 was released in 2018)
Diffstat (limited to 'epan')
-rw-r--r--epan/dissectors/CMakeLists.txt1
-rw-r--r--epan/dissectors/packet-zabbix.c1107
2 files changed, 1108 insertions, 0 deletions
diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt
index 1bf4cebc45..2880485207 100644
--- a/epan/dissectors/CMakeLists.txt
+++ b/epan/dissectors/CMakeLists.txt
@@ -2087,6 +2087,7 @@ set(DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-ypserv.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-ypxfr.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-z3950.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/packet-zabbix.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-zbee-aps.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-zbee-nwk.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-zbee-nwk-gp.c
diff --git a/epan/dissectors/packet-zabbix.c b/epan/dissectors/packet-zabbix.c
new file mode 100644
index 0000000000..ccc2c1b799
--- /dev/null
+++ b/epan/dissectors/packet-zabbix.c
@@ -0,0 +1,1107 @@
+/* packet-zabbix.c
+ * Routines for Zabbix protocol dissection
+ * Copyright 2023, Markku Leiniö <markku.leinio@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Zabbix protocol specifications can be found in Zabbix documentation:
+ * https://www.zabbix.com/documentation/current/en/manual/appendix/protocols
+ */
+
+#include "config.h"
+
+#include <epan/conversation.h>
+#include <epan/expert.h>
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include <wsutil/inet_addr.h>
+#include <wsutil/nstime.h>
+#include <wsutil/wsjson.h>
+#include "packet-tcp.h"
+
+void proto_register_zabbix(void);
+void proto_reg_handoff_zabbix(void);
+
+static dissector_handle_t zabbix_handle;
+
+/* Desegmentation of Zabbix protocol over TCP */
+static bool zabbix_desegment = true;
+
+/* Initialize the protocol and registered fields */
+static int proto_zabbix = -1;
+static int hf_zabbix_header = -1;
+static int hf_zabbix_flags = -1;
+static int hf_zabbix_flag_zabbix_communications = -1;
+static int hf_zabbix_flag_compressed = -1;
+static int hf_zabbix_flag_largepacket = -1;
+static int hf_zabbix_flag_reserved = -1;
+static int hf_zabbix_length = -1;
+static int hf_zabbix_reserved = -1;
+static int hf_zabbix_uncompressed_length = -1;
+static int hf_zabbix_large_length = -1;
+static int hf_zabbix_large_reserved = -1;
+static int hf_zabbix_large_uncompressed_length = -1;
+static int hf_zabbix_data = -1;
+static int hf_zabbix_time = -1;
+static int hf_zabbix_agent = -1;
+static int hf_zabbix_agent_config = -1;
+static int hf_zabbix_agent_data = -1;
+static int hf_zabbix_agent_passive = -1;
+static int hf_zabbix_agent_name = -1;
+static int hf_zabbix_agent_hb = -1;
+static int hf_zabbix_agent_hb_freq = -1;
+static int hf_zabbix_agent_hostmetadata = -1;
+static int hf_zabbix_agent_hostinterface = -1;
+static int hf_zabbix_agent_listenipv4 = -1;
+static int hf_zabbix_agent_listenipv6 = -1;
+static int hf_zabbix_agent_listenport = -1;
+static int hf_zabbix_proxy = -1;
+static int hf_zabbix_proxy_hb = -1;
+static int hf_zabbix_proxy_name = -1;
+static int hf_zabbix_proxy_data = -1;
+static int hf_zabbix_proxy_config = -1;
+static int hf_zabbix_proxy_fullsync = -1;
+static int hf_zabbix_proxy_incr_config = -1;
+static int hf_zabbix_proxy_no_config_change = -1;
+static int hf_zabbix_sender = -1;
+static int hf_zabbix_sender_name = -1;
+static int hf_zabbix_request = -1;
+static int hf_zabbix_response = -1;
+static int hf_zabbix_success = -1;
+static int hf_zabbix_failed = -1;
+static int hf_zabbix_config_revision = -1;
+static int hf_zabbix_session = -1;
+static int hf_zabbix_version = -1;
+
+/* Initialize the subtree pointers */
+static int ett_zabbix = -1;
+
+/* Initialize expert fields */
+static expert_field ei_zabbix_packet_too_large = EI_INIT;
+static expert_field ei_zabbix_json_error = EI_INIT;
+
+/* Other dissector-specifics */
+static range_t *zabbix_port_range;
+
+static const guint8 ZABBIX_HDR_SIGNATURE[] = "ZBXD";
+static const char ZABBIX_UNKNOWN[] = "<unknown>";
+
+typedef struct _zabbix_conv_info_t {
+ guint32 req_framenum;
+ nstime_t req_timestamp;
+ uint16_t oper_flags; /* ZABBIX_T_XXX macros below */
+ const guint8 *host_name;
+} zabbix_conv_info_t;
+
+#define ZABBIX_HDR_MIN_LEN 13 /* When not large packet */
+#define ZABBIX_HDR_MAX_LEN 21 /* When large packet */
+#define ZABBIX_MAX_LENGTH_ALLOWED 1024*1024*1024 /* 1 GB */
+#define ZABBIX_TCP_PORTS "10050,10051" /* IANA registered ports */
+
+#define ZABBIX_FLAG_ZABBIX_COMMUNICATIONS 0x01
+#define ZABBIX_FLAG_COMPRESSED 0x02
+#define ZABBIX_FLAG_LARGEPACKET 0x04
+#define ZABBIX_FLAG_RESERVED 0xf8
+
+/* Response flags are not saved in the conversations */
+#define ZABBIX_RESPONSE_SUCCESS 0x01
+#define ZABBIX_RESPONSE_FAILED 0x02
+#define ZABBIX_RESPONSE_FULLSYNC 0x04
+#define ZABBIX_RESPONSE_INCREMENTAL 0x08
+#define ZABBIX_RESPONSE_NOCHANGE 0x10
+
+/* Flags for saving and comparing operation types,
+ * max 16 bits as defined in zabbix_conv_info_t above */
+#define ZABBIX_T_REQUEST 0x00000001 /* Not set for heartbeats */
+#define ZABBIX_T_RESPONSE 0x00000002
+#define ZABBIX_T_ACTIVE 0x00000004
+#define ZABBIX_T_PASSIVE 0x00000008
+#define ZABBIX_T_AGENT 0x00000010
+#define ZABBIX_T_PROXY 0x00000020
+#define ZABBIX_T_SENDER 0x00000040
+#define ZABBIX_T_CONFIG 0x00000080
+#define ZABBIX_T_DATA 0x00000100
+#define ZABBIX_T_HEARTBEAT 0x00000200
+
+#define ADD_ZABBIX_T_FLAGS(flags) (zabbix_info->oper_flags |= (flags))
+#define CLEAR_ZABBIX_T_FLAGS(flags) (zabbix_info->oper_flags &= (0xffff-(flags)))
+#define IS_ZABBIX_T_FLAGS(flags) ((zabbix_info->oper_flags & (flags)) == (flags))
+
+#define CONV_IS_ZABBIX_REQUEST(zabbix_info,pinfo) ((zabbix_info)->req_framenum == (pinfo)->fd->num)
+#define CONV_IS_ZABBIX_RESPONSE(zabbix_info,pinfo) ((zabbix_info)->req_framenum != (pinfo)->fd->num)
+
+
+static zabbix_conv_info_t*
+zabbix_find_conversation_and_get_conv_data(packet_info *pinfo)
+{
+ conversation_t *conversation;
+ zabbix_conv_info_t *zabbix_info = NULL;
+
+ conversation = find_conversation_pinfo(pinfo, 0);
+ if (conversation) {
+ zabbix_info = (zabbix_conv_info_t *)conversation_get_proto_data(conversation, proto_zabbix);
+ } else {
+ conversation = conversation_new(pinfo->num, &pinfo->src, &pinfo->dst,
+ conversation_pt_to_conversation_type(pinfo->ptype),
+ pinfo->srcport, pinfo->destport, 0);
+ }
+ if (!zabbix_info) {
+ /* New conversation, or there was no Zabbix data yet in the existing conv */
+ zabbix_info = wmem_alloc(wmem_file_scope(), sizeof(zabbix_conv_info_t));
+ if (value_is_in_range(zabbix_port_range, pinfo->destport)) {
+ /* Let's assume this is the first Zabbix packet (request) */
+ zabbix_info->req_framenum = pinfo->fd->num;
+ zabbix_info->req_timestamp = pinfo->abs_ts;
+ }
+ else {
+ /* For any reason we didn't have Zabbix data yet but this is not
+ * the first packet for the connection, so don't save it as a request
+ */
+ zabbix_info->req_framenum = 0;
+ nstime_set_unset(&zabbix_info->req_timestamp);
+ /* For some reason this produces "syntax error: '{'" when compiling:
+ zabbix_info->req_timestamp = NSTIME_INIT_UNSET;
+ */
+ }
+ zabbix_info->oper_flags = 0;
+ zabbix_info->host_name = NULL;
+ conversation_add_proto_data(conversation, proto_zabbix, (void *)zabbix_info);
+ }
+ return zabbix_info;
+}
+
+static void
+zabbix_add_expert_info_if_too_large(packet_info *pinfo, proto_tree *tree_item,
+ uint64_t length, bool *is_too_large)
+{
+ if (length > ZABBIX_MAX_LENGTH_ALLOWED) {
+ expert_add_info(pinfo, tree_item, &ei_zabbix_packet_too_large);
+ *is_too_large = true;
+ }
+ return;
+}
+
+static int
+dissect_zabbix_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
+{
+ int offset = 0;
+ int agent_hb_freq = 0;
+ unsigned oper_response = 0;
+ proto_item *ti;
+ proto_item *pi;
+ proto_tree *temp_ti;
+ proto_tree *zabbix_tree = NULL;
+ uint8_t flags;
+ uint16_t agent_listenport = 0;
+ uint64_t length;
+ uint64_t uncompressed_length;
+ uint64_t datalen;
+ int64_t config_revision = -1;
+ bool is_compressed;
+ bool is_large_packet;
+ bool is_too_large = false;
+ char *json_str;
+ jsmntok_t *data_array = NULL;
+ jsmntok_t *data_object = NULL;
+ const char *agent_name = NULL;
+ const char *agent_hostmetadata = NULL;
+ const char *agent_hostinterface = NULL;
+ const char *agent_listenip = NULL;
+ const char *proxy_name = NULL;
+ const char *sender_name = NULL;
+ const char *session = NULL;
+ const char *request_type = NULL;
+ const char *response_status = NULL;
+ const char *version = NULL;
+ gdouble temp_double;
+ tvbuff_t *next_tvb;
+ zabbix_conv_info_t *zabbix_info;
+ static int* const flagbits[] = {
+ &hf_zabbix_flag_reserved,
+ &hf_zabbix_flag_largepacket,
+ &hf_zabbix_flag_compressed,
+ &hf_zabbix_flag_zabbix_communications,
+ NULL
+ };
+
+ /* Make entries in Protocol column and Info column on summary display */
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "Zabbix");
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ if ((tvb_reported_length(tvb) < ZABBIX_HDR_MIN_LEN) ||
+ (tvb_memeql(tvb, offset, ZABBIX_HDR_SIGNATURE, 4) == -1)) {
+ /* Encrypted or not Zabbix at all */
+ return 0;
+ }
+ flags = tvb_get_guint8(tvb, offset+4);
+ if (!(flags & ZABBIX_FLAG_ZABBIX_COMMUNICATIONS)) {
+ return 0;
+ }
+
+ zabbix_info = zabbix_find_conversation_and_get_conv_data(pinfo);
+
+ is_compressed = (flags & ZABBIX_FLAG_COMPRESSED) > 0;
+ is_large_packet = (flags & ZABBIX_FLAG_LARGEPACKET) > 0;
+
+ /* create display subtree for the protocol */
+ ti = proto_tree_add_item(tree, proto_zabbix, tvb, 0, -1, ENC_NA);
+ zabbix_tree = proto_item_add_subtree(ti, ett_zabbix);
+ proto_tree_add_item(zabbix_tree, hf_zabbix_header, tvb, offset, 4, ENC_UTF_8);
+ offset += 4;
+ proto_tree_add_bitmask(zabbix_tree, tvb, offset, hf_zabbix_flags, ett_zabbix, flagbits, ENC_BIG_ENDIAN);
+ offset += 1;
+ if (is_large_packet) {
+ /* 8-byte values */
+ temp_ti = proto_tree_add_item_ret_uint64(zabbix_tree,
+ hf_zabbix_large_length, tvb, offset, 8, ENC_LITTLE_ENDIAN, &length);
+ zabbix_add_expert_info_if_too_large(pinfo, temp_ti, length, &is_too_large);
+ offset += 8;
+ if (is_compressed) {
+ temp_ti = proto_tree_add_item_ret_uint64(zabbix_tree,
+ hf_zabbix_large_uncompressed_length, tvb, offset, 8, ENC_LITTLE_ENDIAN, &uncompressed_length);
+ zabbix_add_expert_info_if_too_large(pinfo, temp_ti, uncompressed_length, &is_too_large);
+ } else {
+ proto_tree_add_item(zabbix_tree, hf_zabbix_large_reserved, tvb, offset, 8, ENC_LITTLE_ENDIAN);
+ }
+ offset += 8;
+ } else {
+ /* 4-byte values */
+ uint32_t temp_uint32;
+ temp_ti = proto_tree_add_item_ret_uint(zabbix_tree,
+ hf_zabbix_length, tvb, offset, 4, ENC_LITTLE_ENDIAN, &temp_uint32);
+ length = (uint64_t)temp_uint32;
+ zabbix_add_expert_info_if_too_large(pinfo, temp_ti, length, &is_too_large);
+ offset += 4;
+ if (is_compressed) {
+ temp_ti = proto_tree_add_item_ret_uint(zabbix_tree,
+ hf_zabbix_uncompressed_length, tvb, offset, 4, ENC_LITTLE_ENDIAN, &temp_uint32);
+ uncompressed_length = (uint64_t)temp_uint32;
+ zabbix_add_expert_info_if_too_large(pinfo, temp_ti, uncompressed_length, &is_too_large);
+ } else {
+ proto_tree_add_item(zabbix_tree, hf_zabbix_reserved, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ }
+ offset += 4;
+ }
+ if (is_too_large) {
+ /* Set next_tvb for response time calculation to work later */
+ next_tvb = tvb_new_subset_remaining(tvb, offset);
+ /* ... but don't do any content-based inspection, just skip to the end */
+ goto final_outputs;
+ } else if (is_compressed) {
+ next_tvb = tvb_uncompress(tvb, offset, tvb_reported_length_remaining(tvb, offset));
+ if (next_tvb) {
+ tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+ add_new_data_source(pinfo, next_tvb, "Uncompressed data");
+ datalen = uncompressed_length;
+ } else {
+ /* Handle uncompressed */
+ next_tvb = tvb_new_subset_remaining(tvb, offset);
+ datalen = length;
+ }
+ } else {
+ next_tvb = tvb_new_subset_remaining(tvb, offset);
+ datalen = length;
+ }
+
+ /* Use only next_tvb and datalen for data extraction from here on! */
+ offset = 0;
+
+ /* Rewrite the default texts in the protocol tree and initialize request/response flags */
+ if (CONV_IS_ZABBIX_REQUEST(zabbix_info, pinfo)) {
+ proto_item_set_text(ti, "Zabbix Protocol request");
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_REQUEST);
+ CLEAR_ZABBIX_T_FLAGS(ZABBIX_T_RESPONSE);
+ }
+ else if (CONV_IS_ZABBIX_RESPONSE(zabbix_info, pinfo)) {
+ proto_item_set_text(ti, "Zabbix Protocol response");
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_RESPONSE);
+ CLEAR_ZABBIX_T_FLAGS(ZABBIX_T_REQUEST);
+ }
+
+ /*
+ * Note that json_str is modified when using json_get_xxx() functions below!
+ * So don't use it to anything else (make a wmem_strdup() if needed)
+ */
+ json_str = tvb_get_string_enc(pinfo->pool, next_tvb, offset, (int)datalen, ENC_UTF_8);
+ if (CONV_IS_ZABBIX_REQUEST(zabbix_info, pinfo) && !json_validate(json_str, datalen)) {
+ /* The only non-JSON Zabbix request is passive agent, update the conversation data */
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_AGENT | ZABBIX_T_PASSIVE);
+ }
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_AGENT | ZABBIX_T_PASSIVE)) {
+ if (CONV_IS_ZABBIX_REQUEST(zabbix_info, pinfo)) {
+ proto_item_set_text(ti, "Zabbix Passive agent request");
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Passive agent request");
+ } else if (CONV_IS_ZABBIX_RESPONSE(zabbix_info, pinfo)) {
+ proto_item_set_text(ti, "Zabbix Passive agent response");
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Passive agent response");
+ }
+ /* Don't do content-based searches for passive agents */
+ goto show_agent_outputs;
+ }
+ /* Parse JSON, first get the token count */
+ int token_count = json_parse(json_str, NULL, 0);
+ if (token_count <= 0) {
+ temp_ti = proto_tree_add_item(zabbix_tree, hf_zabbix_data, next_tvb, 0, (int)datalen, ENC_UTF_8);
+ expert_add_info_format(pinfo, temp_ti, &ei_zabbix_json_error, "Error in initial JSON parse");
+ goto final_outputs;
+ }
+ jsmntok_t *tokens = wmem_alloc_array(pinfo->pool, jsmntok_t, token_count);
+ int ret = json_parse(json_str, tokens, token_count);
+ if (ret <= 0) {
+ temp_ti = proto_tree_add_item(zabbix_tree, hf_zabbix_data, next_tvb, 0, (int)datalen, ENC_UTF_8);
+ expert_add_info_format(pinfo, temp_ti, &ei_zabbix_json_error, "Error parsing JSON tokens");
+ goto final_outputs;
+ }
+
+ /*
+ * Now we have JSON tokens analyzed, let's do all the logic to populate the fields.
+ * Also set Zabbix tree item and Info column texts, Len= and ports will be added later below.
+ */
+
+ /* First populate common fields */
+ version = json_get_string(json_str, tokens, "version");
+ session = json_get_string(json_str, tokens, "session");
+ if (json_get_double(json_str, tokens, "config_revision", &temp_double)) {
+ config_revision = (int64_t)temp_double;
+ }
+ request_type = json_get_string(json_str, tokens, "request");
+ response_status = json_get_string(json_str, tokens, "response");
+ data_array = json_get_array(json_str, tokens, "data");
+ data_object = json_get_object(json_str, tokens, "data");
+ if (request_type) {
+ if (strcmp(request_type, "active checks") == 0) {
+ /* Active agent requesting configs */
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_AGENT | ZABBIX_T_CONFIG | ZABBIX_T_ACTIVE);
+ agent_name = json_get_string(json_str, tokens, "host");
+ zabbix_info->host_name = wmem_strdup(wmem_file_scope(), agent_name);
+ proto_item_set_text(ti,
+ "Zabbix Request for active checks for \"%s\"", (agent_name ? agent_name : ZABBIX_UNKNOWN));
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Request for active checks for \"%s\"", (agent_name ? agent_name : ZABBIX_UNKNOWN));
+ agent_hostmetadata = json_get_string(json_str, tokens, "host_metadata");
+ agent_hostinterface = json_get_string(json_str, tokens, "interface");
+ agent_listenip = json_get_string(json_str, tokens, "ip");
+ if (json_get_double(json_str, tokens, "port", &temp_double)) {
+ agent_listenport = (uint16_t)temp_double;
+ }
+ }
+ else if (strcmp(request_type, "agent data") == 0) {
+ /* Active agent sending data */
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_AGENT | ZABBIX_T_DATA | ZABBIX_T_ACTIVE);
+ /* Zabbix Agent 2 has Host in the top level */
+ agent_name = json_get_string(json_str, tokens, "host");
+ if (!agent_name) {
+ /* For Zabbix Agent try parsing agent name inside data array */
+ jsmntok_t *tok = json_get_array(json_str, tokens, "data");
+ if (tok && json_get_array_len(tok) > 0) {
+ jsmntok_t *datatok = json_get_array_index(tok, 0);
+ agent_name = json_get_string(json_str, datatok, "host");
+ }
+ }
+ zabbix_info->host_name = wmem_strdup(wmem_file_scope(), agent_name);
+ proto_item_set_text(ti,
+ "Zabbix Send agent data from \"%s\"", (agent_name ? agent_name : ZABBIX_UNKNOWN));
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Send agent data from \"%s\"", (agent_name ? agent_name : ZABBIX_UNKNOWN));
+ }
+ else if (strcmp(request_type, "active check heartbeat") == 0) {
+ /* Active agent sending heartbeat */
+ /* Clear the request flag first */
+ zabbix_info->oper_flags = 0;
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_AGENT | ZABBIX_T_HEARTBEAT | ZABBIX_T_ACTIVE);
+ agent_name = json_get_string(json_str, tokens, "host");
+ if (json_get_double(json_str, tokens, "heartbeat_freq", &temp_double)) {
+ agent_hb_freq = (int)temp_double;
+ }
+ proto_item_set_text(ti,
+ "Zabbix Agent heartbeat from \"%s\"", (agent_name ? agent_name : ZABBIX_UNKNOWN));
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Agent heartbeat from \"%s\"", (agent_name ? agent_name : ZABBIX_UNKNOWN));
+ }
+ else if (strcmp(request_type, "sender data") == 0) {
+ /* Sender/trapper */
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_SENDER);
+ /* Try to get the sender name from the first data array item */
+ jsmntok_t *tok = json_get_array(json_str, tokens, "data");
+ if (tok && json_get_array_len(tok) > 0) {
+ jsmntok_t *datatok = json_get_array_index(tok, 0);
+ sender_name = json_get_string(json_str, datatok, "host");
+ }
+ zabbix_info->host_name = wmem_strdup(wmem_file_scope(), sender_name);
+ proto_item_set_text(ti,
+ "Zabbix Sender data from \"%s\"", (sender_name ? sender_name : ZABBIX_UNKNOWN));
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Sender data from \"%s\"", (sender_name ? sender_name : ZABBIX_UNKNOWN));
+ }
+ else if ((strcmp(request_type, "proxy data") == 0) ||
+ (strcmp(request_type, "host availability") == 0) ||
+ (strcmp(request_type, "history data") == 0) ||
+ (strcmp(request_type, "discovery data") == 0) ||
+ (strcmp(request_type, "auto registration") == 0)) {
+ /* Either active or passive proxy; "proxy data" = Zabbix 3.4+,
+ * others = Zabbix 3.2 or older */
+ proxy_name = json_get_string(json_str, tokens, "host");
+ if (token_count == 3) { /* Only '{"request":"xxx"}' */
+ /* This is Zabbix server connecting to passive proxy */
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_PROXY | ZABBIX_T_DATA | ZABBIX_T_PASSIVE);
+ proto_item_set_text(ti, "Zabbix Request for passive proxy data");
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Request for passive proxy data");
+ }
+ else if (proxy_name) {
+ /* This is an active proxy connecting to server */
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_PROXY | ZABBIX_T_DATA | ZABBIX_T_ACTIVE);
+ zabbix_info->host_name = wmem_strdup(wmem_file_scope(), proxy_name);
+ proto_item_set_text(ti, "Zabbix Proxy data from \"%s\"", proxy_name);
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Proxy data from \"%s\"", proxy_name);
+ }
+ }
+ else if (strcmp(request_type, "proxy config") == 0) {
+ /* Either active or passive proxy */
+ proxy_name = json_get_string(json_str, tokens, "host");
+ if (token_count == 3) { /* Only '{"request":"proxy config"}' */
+ /* This is Zabbix 6.4+ server connecting to passive proxy */
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_PROXY | ZABBIX_T_CONFIG | ZABBIX_T_PASSIVE);
+ proto_item_set_text(ti, "Zabbix Start send proxy config to passive proxy");
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Start send proxy config to passive proxy");
+ }
+ else if (proxy_name) {
+ /* This is an active proxy connecting to server */
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_PROXY | ZABBIX_T_CONFIG | ZABBIX_T_ACTIVE);
+ zabbix_info->host_name = wmem_strdup(wmem_file_scope(), proxy_name);
+ proto_item_set_text(ti, "Zabbix Request proxy config for \"%s\"", proxy_name);
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Request proxy config for \"%s\"", proxy_name);
+ }
+ }
+ else if (strcmp(request_type, "proxy heartbeat") == 0) {
+ /* Heartbeat from active proxy, not used in Zabbix 6.4+ */
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_PROXY | ZABBIX_T_HEARTBEAT | ZABBIX_T_ACTIVE);
+ proxy_name = json_get_string(json_str, tokens, "host");
+ zabbix_info->host_name = wmem_strdup(wmem_file_scope(), proxy_name);
+ proto_item_set_text(ti, "Zabbix Proxy heartbeat from \"%s\"", proxy_name);
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Proxy heartbeat from \"%s\"", proxy_name);
+ }
+ }
+ else if (json_get_object(json_str, tokens, "globalmacro")) {
+ /* This is Zabbix server before 6.4 sending configurations to active proxy */
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_PROXY | ZABBIX_T_CONFIG | ZABBIX_T_ACTIVE);
+ proxy_name = zabbix_info->host_name;
+ proto_item_set_text(ti,
+ "Zabbix Response for proxy config for \"%s\"", (proxy_name ? proxy_name : ZABBIX_UNKNOWN));
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Response for proxy config for \"%s\"", (proxy_name ? proxy_name : ZABBIX_UNKNOWN));
+ }
+ else if (json_get_double(json_str, tokens, "full_sync", &temp_double)) {
+ /* This is Zabbix 6.4+ server sending proxy config to active or passive proxy */
+ /* Only present when value is 1 */
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_PROXY | ZABBIX_T_CONFIG);
+ oper_response |= ZABBIX_RESPONSE_FULLSYNC;
+ /* Active/passive flag was set in the earlier packet */
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_PASSIVE)) {
+ /* There is no proxy name anywhere to use */
+ proto_item_set_text(ti, "Zabbix Passive proxy config");
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Passive proxy config");
+ }
+ else {
+ proxy_name = zabbix_info->host_name;
+ proto_item_set_text(ti,
+ "Zabbix Response for proxy config for \"%s\"", (proxy_name ? proxy_name : ZABBIX_UNKNOWN));
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Response for proxy config for \"%s\"", (proxy_name ? proxy_name : ZABBIX_UNKNOWN));
+ }
+ }
+ else if (response_status) {
+ if (strcmp(response_status, "success") == 0) {
+ oper_response |= ZABBIX_RESPONSE_SUCCESS;
+ }
+ else if (strcmp(response_status, "failed") == 0) {
+ oper_response |= ZABBIX_RESPONSE_FAILED;
+ }
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_AGENT)) {
+ agent_name = zabbix_info->host_name;
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_CONFIG | ZABBIX_T_ACTIVE)) {
+ proto_item_set_text(ti,
+ "Zabbix Response for active checks for \"%s\" (%s)", (agent_name ? agent_name : ZABBIX_UNKNOWN), response_status);
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Response for active checks for \"%s\" (%s)", (agent_name ? agent_name : ZABBIX_UNKNOWN), response_status);
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_DATA | ZABBIX_T_ACTIVE)) {
+ proto_item_set_text(ti,
+ "Zabbix Response for agent data for \"%s\" (%s)", (agent_name ? agent_name : ZABBIX_UNKNOWN), response_status);
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Response for agent data for \"%s\" (%s)", (agent_name ? agent_name : ZABBIX_UNKNOWN), response_status);
+ }
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_PROXY)) {
+ proxy_name = zabbix_info->host_name;
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_CONFIG | ZABBIX_T_ACTIVE)) {
+ proto_item_set_text(ti,
+ "Zabbix Response for active proxy config request for \"%s\" (%s)", (proxy_name ? proxy_name : ZABBIX_UNKNOWN), response_status);
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Response for active proxy config request for \"%s\" (%s)", (proxy_name ? proxy_name : ZABBIX_UNKNOWN), response_status);
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_DATA | ZABBIX_T_ACTIVE)) {
+ proto_item_set_text(ti,
+ "Zabbix Response for active proxy data for \"%s\" (%s)", (proxy_name ? proxy_name : ZABBIX_UNKNOWN), response_status);
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Response for active proxy data for \"%s\" (%s)", (proxy_name ? proxy_name : ZABBIX_UNKNOWN), response_status);
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_CONFIG | ZABBIX_T_PASSIVE)) {
+ proto_item_set_text(ti,
+ "Zabbix Response for passive proxy config (%s)", response_status);
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Response for passive proxy config (%s)", response_status);
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_DATA | ZABBIX_T_PASSIVE)) {
+ proto_item_set_text(ti,
+ "Zabbix Response for passive proxy data (%s)", response_status);
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Response for passive proxy data (%s)", response_status);
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_HEARTBEAT | ZABBIX_T_ACTIVE)) {
+ proto_item_set_text(ti,
+ "Zabbix Response for active proxy heartbeat for \"%s\" (%s)", (proxy_name ? proxy_name : ZABBIX_UNKNOWN), response_status);
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Response for active proxy heartbeat for \"%s\" (%s)", (proxy_name ? proxy_name : ZABBIX_UNKNOWN), response_status);
+ }
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_SENDER)) {
+ sender_name = zabbix_info->host_name;
+ proto_item_set_text(ti,
+ "Zabbix Response for sender data for \"%s\" (%s)", (sender_name ? sender_name : ZABBIX_UNKNOWN), response_status);
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Response for sender data for \"%s\" (%s)", (sender_name ? sender_name : ZABBIX_UNKNOWN), response_status);
+ }
+ }
+ else if (data_object || data_array) {
+ /* No other match above, let's assume this is server sending incremental
+ * configuration to a proxy
+ */
+ ADD_ZABBIX_T_FLAGS(ZABBIX_T_PROXY | ZABBIX_T_CONFIG);
+ if (data_object && (data_object->size == 0)) {
+ /* Empty data object */
+ oper_response |= ZABBIX_RESPONSE_NOCHANGE;
+ }
+ else if (data_array) {
+ /* This was not a "full_sync" but data array exists */
+ oper_response |= ZABBIX_RESPONSE_INCREMENTAL;
+ }
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_PASSIVE)) {
+ /* There is no proxy name anywhere to use */
+ proto_item_set_text(ti, "Zabbix Passive proxy config");
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Passive proxy config");
+ }
+ else {
+ proxy_name = zabbix_info->host_name;
+ proto_item_set_text(ti,
+ "Zabbix Response for proxy config for \"%s\"", (proxy_name ? proxy_name : ZABBIX_UNKNOWN));
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Zabbix Response for proxy config for \"%s\"", (proxy_name ? proxy_name : ZABBIX_UNKNOWN));
+ }
+ }
+ else if (session && version) {
+ /* Last guesses: responses from passive proxy */
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_PROXY | ZABBIX_T_CONFIG | ZABBIX_T_PASSIVE)) {
+ proto_item_set_text(ti, "Zabbix Passive proxy response for config push");
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Passive proxy response for config push");
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_PROXY | ZABBIX_T_DATA | ZABBIX_T_PASSIVE)) {
+ proto_item_set_text(ti, "Zabbix Passive proxy data response");
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Passive proxy data response");
+ }
+ }
+
+
+ /* Add all relevant fields to the tree */
+
+show_agent_outputs:
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_AGENT)) {
+ temp_ti = proto_tree_add_boolean(zabbix_tree, hf_zabbix_agent, NULL, 0, 0, true);
+ proto_item_set_text(temp_ti, "This is an agent connection");
+ proto_item_set_generated(temp_ti);
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_DATA)) {
+ temp_ti = proto_tree_add_boolean(zabbix_tree, hf_zabbix_agent_data, NULL, 0, 0, true);
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_RESPONSE)) {
+ /* Set as generated, not seen in data */
+ proto_item_set_generated(temp_ti);
+ }
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_CONFIG)) {
+ temp_ti = proto_tree_add_boolean(zabbix_tree, hf_zabbix_agent_config, NULL, 0, 0, true);
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_RESPONSE)) {
+ /* Set as generated, not seen in data */
+ proto_item_set_generated(temp_ti);
+ }
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_HEARTBEAT)) {
+ proto_tree_add_boolean(zabbix_tree, hf_zabbix_agent_hb, NULL, 0, 0, true);
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_PASSIVE)) {
+ temp_ti = proto_tree_add_boolean(zabbix_tree, hf_zabbix_agent_passive, NULL, 0, 0, true);
+ proto_item_set_text(temp_ti, "Agent is in passive mode");
+ proto_item_set_generated(temp_ti);
+ }
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_PROXY)) {
+ temp_ti = proto_tree_add_boolean(zabbix_tree, hf_zabbix_proxy, NULL, 0, 0, true);
+ proto_item_set_text(temp_ti, "This is a proxy connection");
+ proto_item_set_generated(temp_ti);
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_SENDER)) {
+ temp_ti = proto_tree_add_boolean(zabbix_tree, hf_zabbix_sender, NULL, 0, 0, true);
+ proto_item_set_text(temp_ti, "This is a sender connection");
+ proto_item_set_generated(temp_ti);
+ }
+ if (oper_response & ZABBIX_RESPONSE_SUCCESS) {
+ proto_tree_add_boolean(zabbix_tree, hf_zabbix_success, NULL, 0, 0, true);
+ }
+ else if (oper_response & ZABBIX_RESPONSE_FAILED) {
+ proto_tree_add_boolean(zabbix_tree, hf_zabbix_failed, NULL, 0, 0, true);
+ }
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_AGENT)) {
+ if (agent_name) {
+ temp_ti = proto_tree_add_string(zabbix_tree, hf_zabbix_agent_name, NULL, 0, 0, agent_name);
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_RESPONSE)) {
+ /* agent_name was populated from the conversation */
+ proto_item_set_generated(temp_ti);
+ proto_item_append_text(temp_ti, " (from the request)");
+ }
+ }
+ if (agent_hb_freq) {
+ proto_tree_add_int(zabbix_tree, hf_zabbix_agent_hb_freq, NULL, 0, 0, agent_hb_freq);
+ }
+ if (agent_hostmetadata) {
+ proto_tree_add_string(zabbix_tree, hf_zabbix_agent_hostmetadata, NULL, 0, 0, agent_hostmetadata);
+ }
+ if (agent_hostinterface) {
+ proto_tree_add_string(zabbix_tree, hf_zabbix_agent_hostinterface, NULL, 0, 0, agent_hostinterface);
+ }
+ if (agent_listenip) {
+ if (strstr(agent_listenip, ":") != NULL) {
+ ws_in6_addr addr6;
+ if (ws_inet_pton6(agent_listenip, &addr6)) {
+ proto_tree_add_ipv6(zabbix_tree, hf_zabbix_agent_listenipv6, NULL, 0, 0, &addr6);
+ }
+ }
+ else {
+ ws_in4_addr addr4;
+ if (ws_inet_pton4(agent_listenip, &addr4)) {
+ proto_tree_add_ipv4(zabbix_tree, hf_zabbix_agent_listenipv4, NULL, 0, 0, addr4);
+ }
+ }
+ }
+ if (agent_listenport) {
+ proto_tree_add_uint(zabbix_tree, hf_zabbix_agent_listenport, NULL, 0, 0, agent_listenport);
+ }
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_PROXY)) {
+ if (proxy_name) {
+ temp_ti = proto_tree_add_string(zabbix_tree, hf_zabbix_proxy_name, NULL, 0, 0, proxy_name);
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_RESPONSE)) {
+ /* proxy_name was populated from the conversation */
+ proto_item_set_generated(temp_ti);
+ proto_item_append_text(temp_ti, " (from the request)");
+ }
+ }
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_DATA)) {
+ proto_tree_add_boolean(zabbix_tree, hf_zabbix_proxy_data, NULL, 0, 0, true);
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_CONFIG)) {
+ proto_tree_add_boolean(zabbix_tree, hf_zabbix_proxy_config, NULL, 0, 0, true);
+ if (oper_response & ZABBIX_RESPONSE_FULLSYNC) {
+ proto_tree_add_boolean(zabbix_tree, hf_zabbix_proxy_fullsync, NULL, 0, 0, true);
+ }
+ else if (oper_response & ZABBIX_RESPONSE_INCREMENTAL) {
+ proto_tree_add_boolean(zabbix_tree, hf_zabbix_proxy_incr_config, NULL, 0, 0, true);
+ }
+ else if (oper_response & ZABBIX_RESPONSE_NOCHANGE) {
+ proto_tree_add_boolean(zabbix_tree, hf_zabbix_proxy_no_config_change, NULL, 0, 0, true);
+ }
+ }
+ else if (IS_ZABBIX_T_FLAGS(ZABBIX_T_HEARTBEAT)) {
+ proto_tree_add_boolean(zabbix_tree, hf_zabbix_proxy_hb, NULL, 0, 0, true);
+ }
+ }
+ else if (sender_name) {
+ temp_ti = proto_tree_add_string(zabbix_tree, hf_zabbix_sender_name, NULL, 0, 0, sender_name);
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_RESPONSE)) {
+ /* sender_name was populated from the conversation */
+ proto_item_set_generated(temp_ti);
+ proto_item_append_text(temp_ti, " (from the request)");
+ }
+ }
+ if (version) {
+ proto_tree_add_string(zabbix_tree, hf_zabbix_version, NULL, 0, 0, version);
+ }
+ if (config_revision > -1) {
+ proto_tree_add_int64(zabbix_tree, hf_zabbix_config_revision, NULL, 0, 0, config_revision);
+ }
+ if (session) {
+ proto_tree_add_string(zabbix_tree, hf_zabbix_session, NULL, 0, 0, session);
+ }
+ /* Show also the full JSON (or passive agent request/response) */
+ proto_tree_add_item(zabbix_tree, hf_zabbix_data, next_tvb, 0, (int)datalen, ENC_UTF_8);
+
+final_outputs:
+
+ /* These are common for all cases, too large or not */
+
+ /* Check the ZABBIX_T_REQUEST flag (and not CONV_IS_ZABBIX_REQUEST macro) because
+ * heartbeats are not marked as requests */
+ if (IS_ZABBIX_T_FLAGS(ZABBIX_T_REQUEST)) {
+ temp_ti = proto_tree_add_boolean(zabbix_tree, hf_zabbix_request, NULL, 0, 0, true);
+ proto_item_set_text(temp_ti, "This is Zabbix request");
+ } else if (CONV_IS_ZABBIX_RESPONSE(zabbix_info, pinfo)) {
+ temp_ti = proto_tree_add_boolean(zabbix_tree, hf_zabbix_response, NULL, 0, 0, true);
+ proto_item_set_text(temp_ti, "This is Zabbix response");
+ if (!nstime_is_unset(&zabbix_info->req_timestamp)) {
+ nstime_t delta;
+ nstime_delta(&delta, &pinfo->abs_ts, &zabbix_info->req_timestamp);
+ pi = proto_tree_add_time(zabbix_tree, hf_zabbix_time, next_tvb, 0, 0, &delta);
+ proto_item_set_generated(pi);
+ }
+ }
+
+ /* Add length to the Zabbix tree text */
+ proto_item_append_text(ti, ", Len=%u", (unsigned)length);
+ /* Add/set Info column texts */
+ const gchar *info_text = col_get_text(pinfo->cinfo, COL_INFO);
+ if (!info_text || !strlen(info_text)) {
+ /* Info column is still empty, set the default text */
+ if (CONV_IS_ZABBIX_REQUEST(zabbix_info, pinfo)) {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Protocol request, Flags=0x%02x", flags);
+ } else if (CONV_IS_ZABBIX_RESPONSE(zabbix_info, pinfo)) {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Protocol response, Flags=0x%02x", flags);
+ } else {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Zabbix Protocol, Flags=0x%02x", flags);
+ }
+ }
+ col_append_fstr(pinfo->cinfo, COL_INFO, ", Len=%u (", (unsigned)length);
+ col_append_ports(pinfo->cinfo, COL_INFO, PT_TCP, pinfo->srcport, pinfo->destport);
+ col_append_str(pinfo->cinfo, COL_INFO, ")");
+
+ return tvb_reported_length(tvb);
+}
+
+/* Determine PDU length of Zabbix protocol */
+static unsigned
+get_zabbix_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
+{
+ uint8_t flags;
+ uint64_t length;
+
+ flags = tvb_get_guint8(tvb, offset+4);
+ if (flags & ZABBIX_FLAG_LARGEPACKET) {
+ /* 8-byte length field
+ * Note that ZABBIX_HDR_MIN_LEN check (in dissect_zabbix()) is still enough
+ * due to the header structure (there are reserved bytes)
+ */
+ length = tvb_get_guint64(tvb, offset+5, ENC_LITTLE_ENDIAN) + ZABBIX_HDR_MAX_LEN;
+ } else {
+ /* 4-byte length */
+ length = tvb_get_guint32(tvb, offset+5, ENC_LITTLE_ENDIAN) + ZABBIX_HDR_MIN_LEN;
+ }
+ return (unsigned)length;
+}
+
+/* The main dissecting routine */
+static int
+dissect_zabbix(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
+{
+ uint8_t flags;
+
+ if (tvb_captured_length(tvb) < ZABBIX_HDR_MIN_LEN) {
+ /* Not enough data */
+ return 0;
+ }
+ if (tvb_memeql(tvb, 0, ZABBIX_HDR_SIGNATURE, 4)) {
+ /* Encrypted or not Zabbix at all */
+ return 0;
+ }
+ flags = tvb_get_guint8(tvb, 4);
+ if (!(flags & ZABBIX_FLAG_ZABBIX_COMMUNICATIONS)) {
+ return 0;
+ }
+ /* This is unencrypted Zabbix protocol, continue with dissecting it */
+ tcp_dissect_pdus(tvb, pinfo, tree, zabbix_desegment, ZABBIX_HDR_MIN_LEN,
+ get_zabbix_pdu_len, dissect_zabbix_pdu, data);
+ return tvb_reported_length(tvb);
+}
+
+/* Register the protocol with Wireshark */
+
+void
+proto_register_zabbix(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_zabbix_header,
+ { "Header", "zabbix.header",
+ FT_STRING, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_flags,
+ { "Flags", "zabbix.flags",
+ FT_UINT8, BASE_HEX, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_flag_zabbix_communications,
+ { "Zabbix communications protocol", "zabbix.flags.zabbix",
+ FT_BOOLEAN, 8, TFS(&tfs_yes_no), ZABBIX_FLAG_ZABBIX_COMMUNICATIONS,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_flag_compressed,
+ { "Compressed", "zabbix.flags.compressed",
+ FT_BOOLEAN, 8, TFS(&tfs_yes_no), ZABBIX_FLAG_COMPRESSED,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_flag_largepacket,
+ { "Large packet", "zabbix.flags.large_packet",
+ FT_BOOLEAN, 8, TFS(&tfs_yes_no), ZABBIX_FLAG_LARGEPACKET,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_flag_reserved,
+ { "Reserved bits", "zabbix.flags.reserved",
+ FT_UINT8, BASE_DEC, NULL, ZABBIX_FLAG_RESERVED,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_length,
+ { "Length", "zabbix.len",
+ FT_UINT32, BASE_DEC, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_reserved,
+ { "Reserved", "zabbix.reserved",
+ FT_UINT32, BASE_DEC, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_uncompressed_length,
+ { "Uncompressed length", "zabbix.uncompressed_len",
+ FT_UINT32, BASE_DEC, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_large_length,
+ { "Large length", "zabbix.large.len",
+ FT_UINT64, BASE_DEC, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_large_reserved,
+ { "Large reserved", "zabbix.large.reserved",
+ FT_UINT64, BASE_DEC, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_large_uncompressed_length,
+ { "Large uncompressed length", "zabbix.large.uncompressed_len",
+ FT_UINT64, BASE_DEC, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_data,
+ { "Data", "zabbix.data",
+ FT_STRING, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_time,
+ { "Response time", "zabbix.time",
+ FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_request,
+ { "Zabbix protocol request", "zabbix.request",
+ FT_BOOLEAN, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_response,
+ { "Zabbix protocol response", "zabbix.response",
+ FT_BOOLEAN, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_success,
+ { "Success", "zabbix.success",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_failed,
+ { "Failed", "zabbix.failed",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_agent,
+ { "Zabbix agent connection", "zabbix.agent",
+ FT_BOOLEAN, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_agent_config,
+ { "Zabbix agent config", "zabbix.agent.config",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_agent_data,
+ { "Zabbix agent data", "zabbix.agent.data",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_agent_passive,
+ { "Passive agent", "zabbix.agent.passive",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_agent_name,
+ { "Agent name", "zabbix.agent.name",
+ FT_STRING, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_agent_hb,
+ { "Agent heartbeat", "zabbix.agent.heartbeat",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_agent_hb_freq,
+ { "Agent heartbeat frequency", "zabbix.agent.heartbeat_freq",
+ FT_INT32, BASE_DEC, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_agent_hostmetadata,
+ { "Agent host metadata", "zabbix.agent.host_metadata",
+ FT_STRING, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_agent_hostinterface,
+ { "Agent host interface", "zabbix.agent.host_interface",
+ FT_STRING, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_agent_listenipv4,
+ { "Agent listen IPv4", "zabbix.agent.listen_ipv4",
+ FT_IPv4, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_agent_listenipv6,
+ { "Agent listen IPv6", "zabbix.agent.listen_ipv6",
+ FT_IPv6, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_agent_listenport,
+ { "Agent listen port", "zabbix.agent.listen_port",
+ FT_UINT16, BASE_DEC, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_proxy,
+ { "Proxy connection", "zabbix.proxy",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_proxy_name,
+ { "Proxy name", "zabbix.proxy.name",
+ FT_STRING, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_proxy_hb,
+ { "Proxy heartbeat", "zabbix.proxy.heartbeat",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_proxy_data,
+ { "Proxy data", "zabbix.proxy.data",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_proxy_config,
+ { "Proxy config", "zabbix.proxy.config",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_proxy_fullsync,
+ { "Proxy config full sync", "zabbix.proxy.full_sync",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_proxy_incr_config,
+ { "Proxy incremental config", "zabbix.proxy.incremental_config",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_proxy_no_config_change,
+ { "Proxy no config changes", "zabbix.proxy.no_config_changes",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_sender,
+ { "Sender connection", "zabbix.sender",
+ FT_BOOLEAN, BASE_NONE, TFS(&tfs_yes_no), 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_sender_name,
+ { "Sender name", "zabbix.sender.name",
+ FT_STRING, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_version,
+ { "Version", "zabbix.version",
+ FT_STRING, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_session,
+ { "Session", "zabbix.session",
+ FT_STRING, BASE_NONE, NULL, 0,
+ NULL, HFILL }
+ },
+ { &hf_zabbix_config_revision,
+ { "Config revision", "zabbix.config_revision",
+ FT_INT64, BASE_DEC, NULL, 0,
+ NULL, HFILL }
+ },
+ };
+
+ static ei_register_info ei[] = {
+ {
+ &ei_zabbix_packet_too_large,
+ { "zabbix.packet_too_large", PI_UNDECODED, PI_WARN,
+ "Packet is too large for detailed dissection", EXPFILL }
+ },
+ {
+ &ei_zabbix_json_error,
+ { "zabbix.json_error", PI_PROTOCOL, PI_ERROR,
+ "Cannot parse JSON", EXPFILL }
+ },
+ };
+
+ /* Setup protocol subtree array */
+ static gint *ett[] = {
+ &ett_zabbix,
+ };
+
+ module_t *zabbix_module;
+ expert_module_t *expert_zabbix;
+
+ /* Register the protocol name and description */
+ proto_zabbix = proto_register_protocol("Zabbix Protocol", "Zabbix", "zabbix");
+
+ /* Required function calls to register the header fields and subtrees used */
+ proto_register_field_array(proto_zabbix, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ zabbix_module = prefs_register_protocol(proto_zabbix, NULL);
+
+ prefs_register_bool_preference(zabbix_module, "desegment",
+ "Reassemble Zabbix messages spanning multiple TCP segments",
+ "Whether the Zabbix protocol dissector should reassemble messages spanning multiple TCP segments."
+ " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
+ (gboolean *)&zabbix_desegment);
+
+ zabbix_handle = register_dissector("zabbix", dissect_zabbix, proto_zabbix);
+
+ expert_zabbix = expert_register_protocol(proto_zabbix);
+ expert_register_field_array(expert_zabbix, ei, array_length(ei));
+}
+
+void
+proto_reg_handoff_zabbix(void)
+{
+ dissector_add_uint_range_with_preference("tcp.port", ZABBIX_TCP_PORTS, zabbix_handle);
+ zabbix_port_range = prefs_get_range_value("Zabbix", "tcp.port");
+ dissector_add_uint_range("tls.port", zabbix_port_range, zabbix_handle);
+}