Add a systemd Journal Export Format parser and dissector.

Add a file parser and dissector that can handle the output of
`journalctl -o export`. From here we can add a systemd journal extcap
and possibly support for the JSON and binary formats.

Change-Id: I01576959b2c347ce7ac9aa57cdb5c119c81d61e9
Reviewed-on: https://code.wireshark.org/review/29311
Petri-Dish: Anders Broman <a.broman58@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Gerald Combs 2018-08-27 08:17:32 -07:00 committed by Anders Broman
parent 94735eb2bc
commit 50b9da7bc2
11 changed files with 1238 additions and 99 deletions

View File

@ -62,25 +62,26 @@ since version 2.6.0:
// Add one protocol per line between the -- delimiters.
[commaize]
--
Cisco Meraki Discovery Protocol (MDP)
DoIP (ISO 13400-2 Diagnostic communication over Internet Protocol)
DXL protocol
E1AP (5G) protocol
Exablaze trailers
GLOW Lawo Emberplus Data format
GSM-R protocol (User-to-User Information Element usage)
GSUP (Osmocom Generic Subscriber Update Protocol)
MsgPack protocol
NGAP (5G) protocol
NR (5G) PDCP protocol
TPM 2.0 protocol
PROXY (v2) protocol
Ruby Marshal format
Ruby Distributed protocol
GSM-R protocol (User-to-User Information Element usage)
Ruby Marshal format
S101 Lawo Emberplus transport frame
GLOW Lawo Emberplus Data format
STCSIG (Spirent Test Center Signature decoding for Ethernet and FibreChannel, disabled by default)
Exablaze trailers
systemd Journal Export
TPM 2.0 protocol
Ubiquiti Discovery Protocol (UBDP)
Cisco Meraki Discovery Protocol (MDP)
XnAP (5G) protocol
E1AP (5G) protocol
MsgPack protocol
DXL protocol
--
=== Updated Protocol Support
@ -92,6 +93,9 @@ Too many protocols have been updated to list here.
//_Non-empty section placeholder._
// Add one file type per line between the --sort-and-group-- delimiters.
[commaize]
--
systemd Journal Export
--
=== New and Updated Capture Interfaces support

View File

@ -170,6 +170,7 @@ set(CLEAN_ASN1_DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-snmp.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-spnego.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-sv.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-systemd-journal.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-t124.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-t125.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-t38.c
@ -554,6 +555,7 @@ set(DISSECTOR_PUBLIC_HEADERS
packet-stat.h
packet-stat-notify.h
packet-sv.h
packet-syslog.h
packet-t124.h
packet-t30.h
packet-t38.h

View File

@ -20,6 +20,8 @@
#include <epan/packet.h>
#include <epan/strutil.h>
#include "packet-syslog.h"
#define UDP_PORT_SYSLOG 514
#define PRIORITY_MASK 0x0007 /* 0000 0111 */
@ -31,15 +33,7 @@ void proto_register_syslog(void);
/* The maximum number if priority digits to read in. */
#define MAX_DIGITS 3
#define LEVEL_EMERG 0
#define LEVEL_ALERT 1
#define LEVEL_CRIT 2
#define LEVEL_ERR 3
#define LEVEL_WARNING 4
#define LEVEL_NOTICE 5
#define LEVEL_INFO 6
#define LEVEL_DEBUG 7
static const value_string short_lev[] = {
static const value_string short_level_vals[] = {
{ LEVEL_EMERG, "EMERG" },
{ LEVEL_ALERT, "ALERT" },
{ LEVEL_CRIT, "CRIT" },
@ -51,31 +45,7 @@ static const value_string short_lev[] = {
{ 0, NULL }
};
#define FAC_KERN 0
#define FAC_USER 1
#define FAC_MAIL 2
#define FAC_DAEMON 3
#define FAC_AUTH 4
#define FAC_SYSLOG 5
#define FAC_LPR 6
#define FAC_NEWS 7
#define FAC_UUCP 8
#define FAC_CRON 9
#define FAC_AUTHPRIV 10
#define FAC_FTP 11
#define FAC_NTP 12
#define FAC_LOGAUDIT 13
#define FAC_LOGALERT 14
#define FAC_CRON_SOL 15
#define FAC_LOCAL0 16
#define FAC_LOCAL1 17
#define FAC_LOCAL2 18
#define FAC_LOCAL3 19
#define FAC_LOCAL4 20
#define FAC_LOCAL5 21
#define FAC_LOCAL6 22
#define FAC_LOCAL7 23
static const value_string short_fac[] = {
static const value_string short_facility_vals[] = {
{ FAC_KERN, "KERN" },
{ FAC_USER, "USER" },
{ FAC_MAIL, "MAIL" },
@ -103,46 +73,6 @@ static const value_string short_fac[] = {
{ 0, NULL }
};
static const value_string long_lev[] = {
{ LEVEL_EMERG, "EMERG - system is unusable" },
{ LEVEL_ALERT, "ALERT - action must be taken immediately" },
{ LEVEL_CRIT, "CRIT - critical conditions" },
{ LEVEL_ERR, "ERR - error conditions" },
{ LEVEL_WARNING, "WARNING - warning conditions" },
{ LEVEL_NOTICE, "NOTICE - normal but significant condition" },
{ LEVEL_INFO, "INFO - informational" },
{ LEVEL_DEBUG, "DEBUG - debug-level messages" },
{ 0, NULL }
};
static const value_string long_fac[] = {
{ FAC_KERN, "KERN - kernel messages" },
{ FAC_USER, "USER - random user-level messages" },
{ FAC_MAIL, "MAIL - mail system" },
{ FAC_DAEMON, "DAEMON - system daemons" },
{ FAC_AUTH, "AUTH - security/authorization messages" },
{ FAC_SYSLOG, "SYSLOG - messages generated internally by syslogd" },
{ FAC_LPR, "LPR - line printer subsystem" },
{ FAC_NEWS, "NEWS - network news subsystem" },
{ FAC_UUCP, "UUCP - UUCP subsystem" },
{ FAC_CRON, "CRON - clock daemon (BSD, Linux)" },
{ FAC_AUTHPRIV, "AUTHPRIV - security/authorization messages (private)" },
{ FAC_FTP, "FTP - ftp daemon" },
{ FAC_NTP, "NTP - ntp subsystem" },
{ FAC_LOGAUDIT, "LOGAUDIT - log audit" },
{ FAC_LOGALERT, "LOGALERT - log alert" },
{ FAC_CRON_SOL, "CRON - clock daemon (Solaris)" },
{ FAC_LOCAL0, "LOCAL0 - reserved for local use" },
{ FAC_LOCAL1, "LOCAL1 - reserved for local use" },
{ FAC_LOCAL2, "LOCAL2 - reserved for local use" },
{ FAC_LOCAL3, "LOCAL3 - reserved for local use" },
{ FAC_LOCAL4, "LOCAL4 - reserved for local use" },
{ FAC_LOCAL5, "LOCAL5 - reserved for local use" },
{ FAC_LOCAL6, "LOCAL6 - reserved for local use" },
{ FAC_LOCAL7, "LOCAL7 - reserved for local use" },
{ 0, NULL }
};
static gint proto_syslog = -1;
static gint hf_syslog_level = -1;
static gint hf_syslog_facility = -1;
@ -296,8 +226,8 @@ dissect_syslog(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _
if (mtp3_tvb == NULL) {
if (pri >= 0) {
col_add_fstr(pinfo->cinfo, COL_INFO, "%s.%s: %s",
val_to_str_const(fac, short_fac, "UNKNOWN"),
val_to_str_const(lev, short_lev, "UNKNOWN"), msg_str);
val_to_str_const(fac, short_facility_vals, "UNKNOWN"),
val_to_str_const(lev, short_level_vals, "UNKNOWN"), msg_str);
} else {
col_add_str(pinfo->cinfo, COL_INFO, msg_str);
}
@ -307,8 +237,8 @@ dissect_syslog(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _
if (pri >= 0) {
ti = proto_tree_add_protocol_format(tree, proto_syslog, tvb, 0, -1,
"Syslog message: %s.%s: %s",
val_to_str_const(fac, short_fac, "UNKNOWN"),
val_to_str_const(lev, short_lev, "UNKNOWN"), msg_str);
val_to_str_const(fac, short_facility_vals, "UNKNOWN"),
val_to_str_const(lev, short_level_vals, "UNKNOWN"), msg_str);
} else {
ti = proto_tree_add_protocol_format(tree, proto_syslog, tvb, 0, -1,
"Syslog message: (unknown): %s", msg_str);
@ -349,12 +279,12 @@ void proto_register_syslog(void)
static hf_register_info hf[] = {
{ &hf_syslog_facility,
{ "Facility", "syslog.facility",
FT_UINT8, BASE_DEC, VALS(long_fac), FACILITY_MASK,
FT_UINT8, BASE_DEC, VALS(syslog_facility_vals), FACILITY_MASK,
"Message facility", HFILL }
},
{ &hf_syslog_level,
{ "Level", "syslog.level",
FT_UINT8, BASE_DEC, VALS(long_lev), PRIORITY_MASK,
FT_UINT8, BASE_DEC, VALS(syslog_level_vals), PRIORITY_MASK,
"Message level", HFILL }
},
{ &hf_syslog_msg,

View File

@ -0,0 +1,104 @@
/* packet-syslog.h
* Routines for syslog message dissection
*
* Copyright 2000, Gerald Combs <gerald[AT]wireshark.org>
*
* Support for passing SS7 MSUs (from the Cisco ITP Packet Logging
* facility) to the MTP3 dissector by Abhik Sarkar <sarkar.abhik[AT]gmail.com>
* with some rework by Jeff Morriss <jeff.morriss.ws [AT] gmail.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald[AT]wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef __PACKET_SYSLOG_H__
#define __PACKET_SYSLOG_H__
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Level / Priority */
#define LEVEL_EMERG 0
#define LEVEL_ALERT 1
#define LEVEL_CRIT 2
#define LEVEL_ERR 3
#define LEVEL_WARNING 4
#define LEVEL_NOTICE 5
#define LEVEL_INFO 6
#define LEVEL_DEBUG 7
static const value_string syslog_level_vals[] = {
{ LEVEL_EMERG, "EMERG - system is unusable" },
{ LEVEL_ALERT, "ALERT - action must be taken immediately" },
{ LEVEL_CRIT, "CRIT - critical conditions" },
{ LEVEL_ERR, "ERR - error conditions" },
{ LEVEL_WARNING, "WARNING - warning conditions" },
{ LEVEL_NOTICE, "NOTICE - normal but significant condition" },
{ LEVEL_INFO, "INFO - informational" },
{ LEVEL_DEBUG, "DEBUG - debug-level messages" },
{ 0, NULL }
};
/* Facility */
#define FAC_KERN 0
#define FAC_USER 1
#define FAC_MAIL 2
#define FAC_DAEMON 3
#define FAC_AUTH 4
#define FAC_SYSLOG 5
#define FAC_LPR 6
#define FAC_NEWS 7
#define FAC_UUCP 8
#define FAC_CRON 9
#define FAC_AUTHPRIV 10
#define FAC_FTP 11
#define FAC_NTP 12
#define FAC_LOGAUDIT 13
#define FAC_LOGALERT 14
#define FAC_CRON_SOL 15
#define FAC_LOCAL0 16
#define FAC_LOCAL1 17
#define FAC_LOCAL2 18
#define FAC_LOCAL3 19
#define FAC_LOCAL4 20
#define FAC_LOCAL5 21
#define FAC_LOCAL6 22
#define FAC_LOCAL7 23
static const value_string syslog_facility_vals[] = {
{ FAC_KERN, "KERN - kernel messages" },
{ FAC_USER, "USER - random user-level messages" },
{ FAC_MAIL, "MAIL - mail system" },
{ FAC_DAEMON, "DAEMON - system daemons" },
{ FAC_AUTH, "AUTH - security/authorization messages" },
{ FAC_SYSLOG, "SYSLOG - messages generated internally by syslogd" },
{ FAC_LPR, "LPR - line printer subsystem" },
{ FAC_NEWS, "NEWS - network news subsystem" },
{ FAC_UUCP, "UUCP - UUCP subsystem" },
{ FAC_CRON, "CRON - clock daemon (BSD, Linux)" },
{ FAC_AUTHPRIV, "AUTHPRIV - security/authorization messages (private)" },
{ FAC_FTP, "FTP - ftp daemon" },
{ FAC_NTP, "NTP - ntp subsystem" },
{ FAC_LOGAUDIT, "LOGAUDIT - log audit" },
{ FAC_LOGALERT, "LOGALERT - log alert" },
{ FAC_CRON_SOL, "CRON - clock daemon (Solaris)" },
{ FAC_LOCAL0, "LOCAL0 - reserved for local use" },
{ FAC_LOCAL1, "LOCAL1 - reserved for local use" },
{ FAC_LOCAL2, "LOCAL2 - reserved for local use" },
{ FAC_LOCAL3, "LOCAL3 - reserved for local use" },
{ FAC_LOCAL4, "LOCAL4 - reserved for local use" },
{ FAC_LOCAL5, "LOCAL5 - reserved for local use" },
{ FAC_LOCAL6, "LOCAL6 - reserved for local use" },
{ FAC_LOCAL7, "LOCAL7 - reserved for local use" },
{ 0, NULL }
};
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif // __PACKET_SYSLOG_H__

View File

@ -0,0 +1,839 @@
/* packet-systemd-journal-export.c
* Routines for systemd journal export (application/vnd.fdo.journal) dissection
* Copyright 2018, Gerald Combs <gerald@wireshark.org>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
/*
* Dissector for systemd's mostly-text-based Journal Export Format described
* at https://www.freedesktop.org/wiki/Software/systemd/export/.
*
* Registered MIME type: application/vnd.fdo.journal
*
* To do:
* - Add an extcap module.
* - Add errno strings.
* - Pretty-print _CAP_EFFECTIVE
* - Handle Journal JSON Format? https://www.freedesktop.org/wiki/Software/systemd/json/
* - Handle raw journal files? https://www.freedesktop.org/wiki/Software/systemd/journal-files/
*/
#include <config.h>
#include <epan/exceptions.h>
#include <epan/packet.h>
#include <epan/expert.h>
#include "packet-syslog.h"
#define PNAME "systemd Journal Entry"
#define PSNAME "systemd Journal"
#define PFNAME "systemd_journal"
void proto_reg_handoff_systemd_journal(void);
void proto_register_systemd_journal(void);
/* Initialize the protocol and registered fields */
static int proto_systemd_journal = -1;
// Official entries, listed in
// https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html
// as of 2018-08.
static int hf_sj_message = -1;
static int hf_sj_message_id = -1;
static int hf_sj_priority = -1;
static int hf_sj_code_file = -1;
static int hf_sj_code_line = -1;
static int hf_sj_code_func = -1;
static int hf_sj_errno = -1;
static int hf_sj_syslog_facility = -1;
static int hf_sj_syslog_identifier = -1;
static int hf_sj_syslog_pid = -1;
static int hf_sj_pid = -1;
static int hf_sj_uid = -1;
static int hf_sj_gid = -1;
static int hf_sj_comm = -1;
static int hf_sj_exe = -1;
static int hf_sj_cmdline = -1;
static int hf_sj_cap_effective = -1;
static int hf_sj_audit_session = -1;
static int hf_sj_audit_loginuid = -1;
static int hf_sj_systemd_cgroup = -1;
static int hf_sj_systemd_slice = -1;
static int hf_sj_systemd_unit = -1;
static int hf_sj_systemd_user_unit = -1;
static int hf_sj_systemd_session = -1;
static int hf_sj_systemd_owner_uid = -1;
static int hf_sj_selinux_context = -1;
static int hf_sj_source_realtime_timestamp = -1;
static int hf_sj_boot_id = -1;
static int hf_sj_machine_id = -1;
static int hf_sj_systemd_invocation_id = -1;
static int hf_sj_hostname = -1;
static int hf_sj_transport = -1;
static int hf_sj_stream_id = -1;
static int hf_sj_line_break = -1;
static int hf_sj_kernel_device = -1;
static int hf_sj_kernel_subsystem = -1;
static int hf_sj_udev_sysname = -1;
static int hf_sj_udev_devnode = -1;
static int hf_sj_udev_devlink = -1;
static int hf_sj_coredump_unit = -1;
static int hf_sj_coredump_user_unit = -1;
static int hf_sj_object_pid = -1;
static int hf_sj_object_uid = -1;
static int hf_sj_object_gid = -1;
static int hf_sj_object_comm = -1;
static int hf_sj_object_exe = -1;
static int hf_sj_object_cmdline = -1;
static int hf_sj_object_audit_session = -1;
static int hf_sj_object_audit_loginuid = -1;
static int hf_sj_object_systemd_cgroup = -1;
static int hf_sj_object_systemd_session = -1;
static int hf_sj_object_systemd_owner_uid = -1;
static int hf_sj_object_systemd_unit = -1;
static int hf_sj_object_systemd_user_unit = -1;
static int hf_sj_cursor = -1;
static int hf_sj_realtime_timestamp = -1;
static int hf_sj_monotonic_timestamp = -1;
// Unofficial(?) fields. Not listed in the documentation but present in logs.
static int hf_sj_result = -1;
static int hf_sj_source_monotonic_timestamp = -1;
static int hf_sj_journal_name = -1;
static int hf_sj_journal_path = -1;
static int hf_sj_current_use = -1;
static int hf_sj_current_use_pretty = -1;
static int hf_sj_max_use = -1;
static int hf_sj_max_use_pretty = -1;
static int hf_sj_disk_keep_free = -1;
static int hf_sj_disk_keep_free_pretty = -1;
static int hf_sj_disk_available = -1;
static int hf_sj_disk_available_pretty = -1;
static int hf_sj_limit = -1;
static int hf_sj_limit_pretty = -1;
static int hf_sj_available = -1;
static int hf_sj_available_pretty = -1;
static int hf_sj_audit_type = -1;
static int hf_sj_audit_id = -1;
static int hf_sj_audit_field_apparmor = -1;
static int hf_sj_audit_field_operation = -1;
static int hf_sj_audit_field_profile = -1;
static int hf_sj_audit_field_name = -1;
static int hf_sj_seat_id = -1;
static int hf_sj_kernel_usec = -1;
static int hf_sj_userspace_usec = -1;
static int hf_sj_session_id = -1;
static int hf_sj_user_id = -1;
static int hf_sj_leader = -1;
// Metadata.
static int hf_sj_binary_data_len = -1;
static int hf_sj_unkown_field = -1;
static int hf_sj_unkown_field_name = -1;
static int hf_sj_unkown_field_value = -1;
static int hf_sj_unkown_field_data = -1;
static int hf_sj_unhandled_field_type = -1;
static expert_field ei_unhandled_field_type = EI_INIT;
static expert_field ei_nonbinary_field = EI_INIT;
#define MAX_DATA_SIZE 262144 // WTAP_MAX_PACKET_SIZE_STANDARD. Increase if needed.
/* Initialize the subtree pointers */
static gint ett_systemd_journal_entry = -1;
static gint ett_systemd_binary_data = -1;
static gint ett_systemd_unknown_field = -1;
// XXX Use a value_string instead?
typedef struct _journal_field_hf_map {
int hfid;
const char *name;
} journal_field_hf_map;
static journal_field_hf_map *jf_to_hf;
void init_jf_to_hf_map(void) {
journal_field_hf_map jhmap[] = {
// Official.
{ hf_sj_message, "MESSAGE=" },
{ hf_sj_message_id, "MESSAGE_ID=" },
{ hf_sj_priority, "PRIORITY=" },
{ hf_sj_code_file, "CODE_FILE=" },
{ hf_sj_code_line, "CODE_LINE=" },
{ hf_sj_code_func, "CODE_FUNC=" },
{ hf_sj_result, "RESULT=" },
{ hf_sj_errno, "ERRNO=" },
{ hf_sj_syslog_facility, "SYSLOG_FACILITY=" },
{ hf_sj_syslog_identifier, "SYSLOG_IDENTIFIER=" },
{ hf_sj_syslog_pid, "SYSLOG_PID=" },
{ hf_sj_pid, "_PID=" },
{ hf_sj_uid, "_UID=" },
{ hf_sj_gid, "_GID=" },
{ hf_sj_comm, "_COMM=" },
{ hf_sj_exe, "_EXE=" },
{ hf_sj_cmdline, "_CMDLINE=" },
{ hf_sj_cap_effective, "_CAP_EFFECTIVE=" },
{ hf_sj_audit_session, "_AUDIT_SESSION=" },
{ hf_sj_audit_loginuid, "_AUDIT_LOGINUID=" },
{ hf_sj_systemd_cgroup, "_SYSTEMD_CGROUP=" },
{ hf_sj_systemd_slice, "_SYSTEMD_SLICE=" },
{ hf_sj_systemd_unit, "_SYSTEMD_UNIT=" },
{ hf_sj_systemd_user_unit, "_SYSTEMD_USER_UNIT=" },
{ hf_sj_systemd_session, "_SYSTEMD_SESSION=" },
{ hf_sj_systemd_owner_uid, "_SYSTEMD_OWNER_UID=" },
{ hf_sj_selinux_context, "_SELINUX_CONTEXT=" },
{ hf_sj_source_realtime_timestamp, "_SOURCE_REALTIME_TIMESTAMP=" },
{ hf_sj_source_monotonic_timestamp, "_SOURCE_MONOTONIC_TIMESTAMP=" },
{ hf_sj_boot_id, "_BOOT_ID=" },
{ hf_sj_machine_id, "_MACHINE_ID=" },
{ hf_sj_systemd_invocation_id, "_SYSTEMD_INVOCATION_ID=" },
{ hf_sj_hostname, "_HOSTNAME=" },
{ hf_sj_transport, "_TRANSPORT=" },
{ hf_sj_stream_id, "_STREAM_ID=" },
{ hf_sj_line_break, "_LINE_BREAK=" },
{ hf_sj_kernel_device, "_KERNEL_DEVICE=" },
{ hf_sj_kernel_subsystem, "_KERNEL_SUBSYSTEM=" },
{ hf_sj_udev_sysname, "_UDEV_SYSNAME=" },
{ hf_sj_udev_devnode, "_UDEV_DEVNODE=" },
{ hf_sj_udev_devlink, "_UDEV_DEVLINK=" },
{ hf_sj_coredump_unit, "COREDUMP_UNIT=" },
{ hf_sj_coredump_user_unit, "COREDUMP_USER_UNIT=" },
{ hf_sj_object_pid, "OBJECT_PID=" },
{ hf_sj_object_uid, "OBJECT_UID=" },
{ hf_sj_object_gid, "OBJECT_GID=" },
{ hf_sj_object_comm, "OBJECT_COMM=" },
{ hf_sj_object_exe, "OBJECT_EXE=" },
{ hf_sj_object_cmdline, "OBJECT_CMDLINE=" },
{ hf_sj_object_audit_session, "OBJECT_AUDIT_SESSION=" },
{ hf_sj_object_audit_loginuid, "OBJECT_AUDIT_LOGINUID=" },
{ hf_sj_object_systemd_cgroup, "OBJECT_SYSTEMD_CGROUP=" },
{ hf_sj_object_systemd_session, "OBJECT_SYSTEMD_SESSION=" },
{ hf_sj_object_systemd_owner_uid, "OBJECT_SYSTEMD_OWNER_UID=" },
{ hf_sj_object_systemd_unit, "OBJECT_SYSTEMD_UNIT=" },
{ hf_sj_object_systemd_user_unit, "OBJECT_SYSTEMD_USER_UNIT=" },
{ hf_sj_cursor, "__CURSOR=" },
{ hf_sj_realtime_timestamp, "__REALTIME_TIMESTAMP=" },
{ hf_sj_monotonic_timestamp, "__MONOTONIC_TIMESTAMP=" },
// Unofficial?
{ hf_sj_journal_name, "JOURNAL_NAME=" }, // systemd-journald: Runtime journal (/run/log/journal/) is ...
{ hf_sj_journal_path, "JOURNAL_PATH=" }, // ""
{ hf_sj_current_use, "CURRENT_USE=" }, // ""
{ hf_sj_current_use_pretty, "CURRENT_USE_PRETTY=" }, // ""
{ hf_sj_max_use, "MAX_USE=" }, // ""
{ hf_sj_max_use_pretty, "MAX_USE_PRETTY=" }, // ""
{ hf_sj_disk_keep_free, "DISK_KEEP_FREE=" }, // ""
{ hf_sj_disk_keep_free_pretty, "DISK_KEEP_FREE_PRETTY=" }, // ""
{ hf_sj_disk_available, "DISK_AVAILABLE=" }, // ""
{ hf_sj_disk_available_pretty, "DISK_AVAILABLE_PRETTY=" }, // ""
{ hf_sj_limit, "LIMIT=" }, // ""
{ hf_sj_limit_pretty, "LIMIT_PRETTY=" }, // ""
{ hf_sj_available, "AVAILABLE=" }, // ""
{ hf_sj_available_pretty, "AVAILABLE_PRETTY=" }, // ""
{ hf_sj_code_func, "CODE_FUNCTION=" }, // Dup / alias of CODE_FUNC?
{ hf_sj_systemd_user_unit, "UNIT=" }, // Dup / alias of _SYSTEMD_UNIT?
{ hf_sj_systemd_user_unit, "USER_UNIT=" }, // Dup / alias of _SYSTEMD_USER_UNIT?
{ hf_sj_audit_type, "_AUDIT_TYPE=" },
{ hf_sj_audit_id, "_AUDIT_ID=" },
{ hf_sj_audit_field_apparmor, "_AUDIT_FIELD_APPARMOR=" },
{ hf_sj_audit_field_operation, "_AUDIT_FIELD_OPERATION=" },
{ hf_sj_audit_field_profile, "_AUDIT_FIELD_PROFILE=" },
{ hf_sj_audit_field_name, "_AUDIT_FIELD_NAME=" },
{ hf_sj_seat_id, "SEAT_ID=" },
{ hf_sj_kernel_usec, "KERNEL_USEC=" },
{ hf_sj_userspace_usec, "USERSPACE_USEC" },
{ hf_sj_session_id, "SESSION_ID" },
{ hf_sj_user_id, "USER_ID" },
{ hf_sj_leader, "LEADER" },
{ 0, NULL }
};
jf_to_hf = (journal_field_hf_map*) g_memdup(jhmap, sizeof(jhmap));
}
static void
dissect_sjle_time_usecs(proto_tree *tree, int hf_idx, tvbuff_t *tvb, int offset, int len) {
unsigned long rt_ts = strtoul(tvb_format_text(tvb, offset, len), NULL, 10);
// XXX Check errno?
nstime_t ts;
ts.secs = (time_t) rt_ts / 1000000;
ts.nsecs = (rt_ts % 1000000) * 1000;
proto_tree_add_time(tree, hf_idx, tvb, offset, len, &ts);
}
static void
dissect_sjle_uint(proto_tree *tree, int hf_idx, tvbuff_t *tvb, int offset, int len) {
guint32 uint_val = (guint32) strtoul(tvb_format_text(tvb, offset, len), NULL, 10);
proto_tree_add_uint(tree, hf_idx, tvb, offset, len, uint_val);
}
static void
dissect_sjle_int(proto_tree *tree, int hf_idx, tvbuff_t *tvb, int offset, int len) {
gint32 int_val = (gint32) strtol(tvb_format_text(tvb, offset, len), NULL, 10);
proto_tree_add_int(tree, hf_idx, tvb, offset, len, int_val);
}
/* Dissect a line-based journal export entry */
static int
dissect_systemd_journal_line_entry(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
void *data _U_)
{
proto_item *ti;
proto_tree *sje_tree;
int offset = 0, next_offset = 0;
int line_len;
col_set_str(pinfo->cinfo, COL_PROTOCOL, PSNAME);
col_clear(pinfo->cinfo, COL_INFO);
col_set_str(pinfo->cinfo, COL_INFO, "Journal Entry");
ti = proto_tree_add_item(tree, proto_systemd_journal, tvb, 0, -1, ENC_NA);
sje_tree = proto_item_add_subtree(ti, ett_systemd_journal_entry);
while ((line_len = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE)) >= 3) { // A=1
gboolean found = FALSE;
int eq_off = tvb_find_guint8(tvb, offset, line_len, '=') + 1;
int val_len = offset + line_len - eq_off;
for (int i = 0; jf_to_hf[i].name; i++) {
if (tvb_memeql(tvb, offset, (const guint8*) jf_to_hf[i].name, strlen(jf_to_hf[i].name)) == 0) {
int hf_idx = jf_to_hf[i].hfid;
switch (proto_registrar_get_ftype(hf_idx)) {
case FT_ABSOLUTE_TIME:
case FT_RELATIVE_TIME:
dissect_sjle_time_usecs(sje_tree, hf_idx, tvb, eq_off, val_len);
break;
case FT_UINT32:
case FT_UINT16:
case FT_UINT8:
dissect_sjle_uint(sje_tree, hf_idx, tvb, eq_off, val_len);
break;
case FT_INT32:
case FT_INT16:
case FT_INT8:
dissect_sjle_int(sje_tree, hf_idx, tvb, eq_off, val_len);
break;
case FT_STRING:
proto_tree_add_item(sje_tree, jf_to_hf[i].hfid, tvb, eq_off, val_len, ENC_UTF_8|ENC_NA);
break;
default:
{
proto_item *expert_ti = proto_tree_add_item(sje_tree, hf_sj_unhandled_field_type, tvb, offset, line_len,
ENC_UTF_8|ENC_NA);
expert_add_info(pinfo, expert_ti, &ei_unhandled_field_type);
break;
}
}
if (hf_idx == hf_sj_message) {
col_clear(pinfo->cinfo, COL_INFO);
col_set_str(pinfo->cinfo, COL_INFO, tvb_format_text(tvb, eq_off, val_len));
}
found = TRUE;
}
}
if (!found && eq_off > offset + 1) {
proto_item *unk_ti = proto_tree_add_none_format(sje_tree, hf_sj_unkown_field, tvb, offset, line_len,
"Unknown text field: %s", tvb_format_text(tvb, offset, eq_off - offset - 1));
proto_tree *unk_tree = proto_item_add_subtree(unk_ti, ett_systemd_unknown_field);
proto_tree_add_item(unk_tree, hf_sj_unkown_field_name, tvb, offset, eq_off - offset - 1, ENC_UTF_8|ENC_NA);
proto_tree_add_item(unk_tree, hf_sj_unkown_field_value, tvb, eq_off, val_len, ENC_UTF_8|ENC_NA);
offset = next_offset;
continue;
}
// Try again, looking for binary fields.
if (!found) {
for (int i = 0; jf_to_hf[i].name; i++) {
int noeql_len = (int) strlen(jf_to_hf[i].name) - 1;
if (tvb_memeql(tvb, offset, (const guint8 *) jf_to_hf[i].name, (size_t) noeql_len) == 0 && tvb_memeql(tvb, offset+noeql_len, (const guint8 *) "\n", 1) == 0) {
int hf_idx = jf_to_hf[i].hfid;
guint64 data_len = tvb_get_letoh64(tvb, offset + noeql_len + 1);
int data_off = offset + noeql_len + 1 + 8; // \n + data len
next_offset = data_off + (int) data_len + 1;
if (proto_registrar_get_ftype(hf_idx) == FT_STRING) {
proto_item *bin_ti = proto_tree_add_item(sje_tree, hf_idx, tvb, data_off, (int) data_len, ENC_NA);
proto_tree *bin_tree = proto_item_add_subtree(bin_ti, ett_systemd_binary_data);
proto_tree_add_item(bin_tree, hf_sj_binary_data_len, tvb, offset + noeql_len + 1, 8, ENC_LITTLE_ENDIAN);
if (hf_idx == hf_sj_message) {
col_clear(pinfo->cinfo, COL_INFO);
col_set_str(pinfo->cinfo, COL_INFO, tvb_format_text(tvb, data_off, (int) data_len));
}
} else {
proto_item *unk_ti = proto_tree_add_none_format(sje_tree, hf_sj_unkown_field, tvb, offset, line_len,
"Unknown data field: %s", tvb_format_text(tvb, offset, eq_off - offset - 1));
proto_tree *unk_tree = proto_item_add_subtree(unk_ti, ett_systemd_unknown_field);
proto_item *expert_ti = proto_tree_add_item(unk_tree, hf_sj_unkown_field_name, tvb, offset, offset + noeql_len, ENC_UTF_8|ENC_NA);
proto_tree_add_item(unk_tree, hf_sj_unkown_field_data, tvb, data_off, (int) data_len, ENC_UTF_8|ENC_NA);
expert_add_info(pinfo, expert_ti, &ei_nonbinary_field);
}
}
}
}
offset = next_offset;
}
return offset;
}
/*
* Register the protocol with Wireshark.
*/
void
proto_register_systemd_journal(void)
{
expert_module_t *expert_systemd_journal;
static hf_register_info hf[] = {
{ &hf_sj_message,
{ "Message", "systemd_journal.message",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_message_id,
{ "Message ID", "systemd_journal.message_id",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_priority,
{ "Priority", "systemd_journal.priority",
FT_UINT8, BASE_DEC, VALS(syslog_level_vals), 0x0, NULL, HFILL }
},
{ &hf_sj_code_file,
{ "Code file", "systemd_journal.code_file",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_code_line,
{ "Code line", "systemd_journal.code_line",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_code_func,
{ "Code func", "systemd_journal.code_func",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_errno,
{ "Errno", "systemd_journal.errno",
FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_syslog_facility,
{ "Syslog facility", "systemd_journal.syslog_facility",
FT_UINT8, BASE_NONE, VALS(syslog_facility_vals), 0x0, NULL, HFILL }
},
{ &hf_sj_syslog_identifier,
{ "Syslog identifier", "systemd_journal.syslog_id",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_syslog_pid,
{ "Syslog PID", "systemd_journal.syslog_pid",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_pid,
{ "PID", "systemd_journal.pid",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_uid,
{ "UID", "systemd_journal.uid",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_gid,
{ "GID", "systemd_journal.gid",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_comm,
{ "Command name", "systemd_journal.comm",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_exe,
{ "Executable path", "systemd_journal.exe",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_cmdline,
{ "Command line", "systemd_journal.cmdline",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_cap_effective,
{ "Effective capability", "systemd_journal.cap_effective",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_audit_session,
{ "Audit session", "systemd_journal.audit_session",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_audit_loginuid,
{ "Audit login UID", "systemd_journal.audit_loginuid",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_systemd_cgroup,
{ "Systemd cgroup", "systemd_journal.systemd_cgroup",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_systemd_slice,
{ "Systemd slice", "systemd_journal.systemd_slice",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_systemd_unit,
{ "Systemd unit", "systemd_journal.systemd_unit",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_systemd_user_unit,
{ "Systemd user unit", "systemd_journal.systemd_user_unit",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_systemd_session,
{ "Systemd session", "systemd_journal.systemd_session",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_systemd_owner_uid,
{ "Systemd owner UID", "systemd_journal.systemd_owner_uid",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_selinux_context,
{ "SELinux context", "systemd_journal.selinux_context",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_source_realtime_timestamp,
{ "Source realtime timestamp", "systemd_journal.source_realtime_timestamp",
FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_boot_id,
{ "Boot ID", "systemd_journal.boot_id",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_machine_id,
{ "Machine ID", "systemd_journal.machine_id",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_systemd_invocation_id,
{ "Systemd invocation ID", "systemd_journal.systemd_invocation_id",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_hostname,
{ "Hostname", "systemd_journal.hostname",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_transport,
{ "Transport", "systemd_journal.transport",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_stream_id,
{ "Stream ID", "systemd_journal.stream_id",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_line_break,
{ "Line break", "systemd_journal.line_break",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_kernel_device,
{ "Kernel device", "systemd_journal.kernel_device",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_kernel_subsystem,
{ "Kernel subsystem", "systemd_journal.kernel_subsystem",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_udev_sysname,
{ "Device tree name", "systemd_journal.udev_sysname",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_udev_devnode,
{ "Device tree node", "systemd_journal.udev_devnode",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_udev_devlink,
{ "Device tree symlink", "systemd_journal.udev_devlink",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_coredump_unit,
{ "Coredump unit", "systemd_journal.coredump_unit",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_coredump_user_unit,
{ "Coredump user unit", "systemd_journal.coredump_user_unit",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_pid,
{ "Object PID", "systemd_journal.object_pid",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_uid,
{ "Object UID", "systemd_journal.object_uid",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_gid,
{ "Object GID", "systemd_journal.object_gid",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_comm,
{ "Object command name", "systemd_journal.object_comm",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_exe,
{ "Object executable path", "systemd_journal.object_exe",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_cmdline,
{ "Object command line", "systemd_journal.object_cmdline",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_audit_session,
{ "Object audit session", "systemd_journal.object_audit_session",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_audit_loginuid,
{ "Object audit login UID", "systemd_journal.object_audit_loginuid",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_systemd_cgroup,
{ "Object systemd cgroup", "systemd_journal.object_systemd_cgroup",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_systemd_session,
{ "Object systemd session", "systemd_journal.object_systemd_session",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_systemd_owner_uid,
{ "Object systemd owner UID", "systemd_journal.object_systemd_owner_uid",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_systemd_unit,
{ "Object systemd unit", "systemd_journal.object_systemd_unit",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_object_systemd_user_unit,
{ "Object systemd user unit", "systemd_journal.object_systemd_user_unit",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_cursor,
{ "Cursor", "systemd_journal.cursor",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_realtime_timestamp,
{ "Realtime Timestamp", "systemd_journal.realtime_timestamp",
FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_monotonic_timestamp,
{ "Monotonic Timestamp", "systemd_journal.monotonic_timestamp",
FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_journal_name,
{ "Journal name", "systemd_journal.journal_name",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_journal_path,
{ "Journal path", "systemd_journal.journal_path",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_current_use,
{ "Current use", "systemd_journal.current_use",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_current_use_pretty,
{ "Human readable current use", "systemd_journal.current_use_pretty",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_max_use,
{ "Max use", "systemd_journal.max_use",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_max_use_pretty,
{ "Human readable max use", "systemd_journal.max_use_pretty",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_disk_keep_free,
{ "Disk keep free", "systemd_journal.disk_keep_free",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_disk_keep_free_pretty,
{ "Human readable disk keep free", "systemd_journal.disk_keep_free_pretty",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_disk_available,
{ "Disk available", "systemd_journal.disk_available",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_disk_available_pretty,
{ "Human readable disk available", "systemd_journal.disk_available_pretty",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_limit,
{ "Limit", "systemd_journal.limit",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_limit_pretty,
{ "Human readable limit", "systemd_journal.limit_pretty",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_available,
{ "Available", "systemd_journal.available",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_available_pretty,
{ "Human readable available", "systemd_journal.available_pretty",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_result,
{ "Result", "systemd_journal.result",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_source_monotonic_timestamp,
{ "Source monotonic timestamp", "systemd_journal.source_monotonic_timestamp",
FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_audit_type,
{ "Audit type", "systemd_journal.audit_type",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_audit_id,
{ "Audit ID", "systemd_journal.audit_id",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_audit_field_apparmor,
{ "Audit field AppArmor", "systemd_journal.audit_field_apparmor",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_audit_field_operation,
{ "Audit field operation", "systemd_journal.audit_field_operation",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_audit_field_profile,
{ "Audit field profile", "systemd_journal.audit_field_profile",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_audit_field_name,
{ "Audit field name", "systemd_journal.audit_field_name",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_seat_id,
{ "Seat ID", "systemd_journal.seat_id",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_kernel_usec,
{ "Kernel microseconds", "systemd_journal.kernel_usec",
FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_userspace_usec,
{ "Userspace microseconds", "systemd_journal.userspace_usec",
FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_session_id,
{ "Session ID", "systemd_journal.session_id",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_user_id,
{ "User ID", "systemd_journal.user_id",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_leader,
{ "Leader", "systemd_journal.leader",
FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_binary_data_len,
{ "Binary data length", "systemd_journal.binary_data_len",
FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_unkown_field,
{ "Unknown field", "systemd_journal.field",
FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_unkown_field_name,
{ "Field name", "systemd_journal.field.name",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_unkown_field_value,
{ "Field value", "systemd_journal.field.value",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_unkown_field_data,
{ "Field data", "systemd_journal.field.data",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
},
{ &hf_sj_unhandled_field_type,
{ "Field data", "systemd_journal.unhandled_field_type",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
}
};
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_systemd_journal_entry,
&ett_systemd_binary_data,
&ett_systemd_unknown_field
};
/* Setup protocol expert items */
static ei_register_info ei[] = {
{ &ei_unhandled_field_type,
{ "systemd_journal.unhandled_field_type", PI_UNDECODED, PI_ERROR,
"Unhandled field type", EXPFILL }
},
{ &ei_nonbinary_field,
{ "systemd_journal.nonbinary_field", PI_UNDECODED, PI_WARN,
"Field shouldn't be binary", EXPFILL }
}
};
/* Register the protocol name and description */
proto_systemd_journal = proto_register_protocol(PNAME, PSNAME, PFNAME);
/* Required function calls to register the header fields and subtrees */
proto_register_field_array(proto_systemd_journal, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
/* Required function calls to register expert items */
expert_systemd_journal = expert_register_protocol(proto_systemd_journal);
expert_register_field_array(expert_systemd_journal, ei, array_length(ei));
init_jf_to_hf_map();
}
void
proto_reg_handoff_systemd_journal(void)
{
static dissector_handle_t sje_handle = NULL;
if (!sje_handle) {
sje_handle = create_dissector_handle(dissect_systemd_journal_line_entry,
proto_systemd_journal);
}
dissector_add_uint("wtap_encap", WTAP_ENCAP_SYSTEMD_JOURNAL, sje_handle);
// It's possible to ship journal entries over HTTP/HTTPS using
// systemd-journal-remote. Dissecting them on the wire isn't very
// useful since it's easy to end up with a packet containing a
// single, huge reassembled journal with many entries.
dissector_add_string("media_type", "application/vnd.fdo.journal", sje_handle);
}
/*
* 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:
*/

View File

@ -74,6 +74,7 @@ set(WIRETAP_NONGENERATED_FILES
ruby_marshal.c
snoop.c
stanag4607.c
systemd_journal.c
tnef.c
toshiba.c
visual.c

View File

@ -32,22 +32,22 @@ To add the ability to read a new capture file format, you have to:
otherwise leave it set to NULL;
add a pointer to the "open" routine to the "open_routines_base[]"
table in "wiretap/file_access.c" - if it uses a magic number, put
it in the first section of that list, and, if it uses a heuristic,
table in "wiretap/file_access.c" - if it uses a magic number, put
it in the first section of that list, and, if it uses a heuristic,
put it in the second section, preferably putting the heuristic
routines for binary files before the heuristic routines for text
files;
add an entry for that file type in the "dump_open_table_base[]" in
"wiretap/file_access.c", giving a descriptive name, a short name
"wiretap/file_access.c", giving a descriptive name, a short name
that's convenient to type on a command line (no blanks or capital
letters, please), common file extensions to open and save, a flag
if it can be compressed with gzip (currently unused) and pointers
to the "can_write_encap" and "dump_open" routines if writing that
letters, please), common file extensions to open and save, a flag
if it can be compressed with gzip (currently unused) and pointers
to the "can_write_encap" and "dump_open" routines if writing that
file is supported (see below), otherwise just null pointers.
Wiretap applications typically first perform sequential reads through
the capture file and may later do "seek and read" for individual frames.
the capture file and may later do "seek and read" for individual frames.
The "read" routine should set the variable data_offset to the byte
offset within the capture file from which the "seek and read" routine
will read. If the capture records consist of:
@ -58,9 +58,9 @@ will read. If the capture records consist of:
then data_offset should point to the pseudo-header. The first
sequential read pass will process and store the capture record header
data, but it will not store the pseudo-header. Note that the
data, but it will not store the pseudo-header. Note that the
seek_and_read routine should work with the "random_fh" file handle
of the passed in wtap struct, instead of the "fh" file habndle used
of the passed in wtap struct, instead of the "fh" file handle used
in the normal read routine.
To add the ability to write a new capture file format, you have to:

View File

@ -75,6 +75,7 @@
#include "dpa400.h"
#include "rfc7468.h"
#include "ruby_marshal.h"
#include "systemd_journal.h"
/*
* Add an extension, and all compressed versions thereof, to a GSList
@ -405,7 +406,8 @@ static const struct open_info open_info_base[] = {
{ "Ixia IxVeriWave .vwr Raw Capture", OPEN_INFO_HEURISTIC, vwr_open, "vwr", NULL, NULL },
{ "CAM Inspector file", OPEN_INFO_HEURISTIC, camins_open, "camins", NULL, NULL },
{ "JavaScript Object Notation", OPEN_INFO_HEURISTIC, json_open, "json", NULL, NULL },
{ "Ruby Marshal Object", OPEN_INFO_HEURISTIC, ruby_marshal_open, "", NULL, NULL }
{ "Ruby Marshal Object", OPEN_INFO_HEURISTIC, ruby_marshal_open, "", NULL, NULL },
{ "Systemd Journal", OPEN_INFO_HEURISTIC, systemd_journal_open, "log;jnl;journal", NULL, NULL }
};
/* this is only used to build the dynamic array on load, do NOT use this

236
wiretap/systemd_journal.c Normal file
View File

@ -0,0 +1,236 @@
/* systemd_journal.c
*
* Wiretap Library
* Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "wtap-int.h"
#include "file_wrappers.h"
#include "systemd_journal.h"
// To do:
// - Request a pcap encapsulation type.
// - Should we add separate types for binary, plain, and JSON or add a metadata header?
// Systemd journals are stored in the following formats:
// Journal File Format (native binary): https://www.freedesktop.org/wiki/Software/systemd/journal-files/
// Journal Export Format: https://www.freedesktop.org/wiki/Software/systemd/export/
// Journal JSON format: https://www.freedesktop.org/wiki/Software/systemd/json/
// This reads Journal Export Format files but could be extended to support
// the binary and JSON formats.
// Example data:
// __CURSOR=s=1d56bab64d414960b9907ab0cc7f7c62;i=2;b=1497926e8b4b4d3ca6a5805e157fa73c;m=5d0ae5;t=56f2f5b66ce6f;x=20cb01e28bb496a8
// __REALTIME_TIMESTAMP=1529624071163503
// __MONOTONIC_TIMESTAMP=6097637
// _BOOT_ID=1497926e8b4b4d3ca6a5805e157fa73c
// PRIORITY=6
// _MACHINE_ID=62c342838a6e436dacea041aa4b5064b
// _HOSTNAME=example.wireshark.org
// _SOURCE_MONOTONIC_TIMESTAMP=0
// _TRANSPORT=kernel
// SYSLOG_FACILITY=0
// SYSLOG_IDENTIFIER=kernel
// MESSAGE=Initializing cgroup subsys cpuset
static gboolean systemd_journal_read(wtap *wth, int *err, gchar **err_info,
gint64 *data_offset);
static gboolean systemd_journal_seek_read(wtap *wth, gint64 seek_off,
wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
static gboolean systemd_journal_read_export_entry(FILE_T fh, wtap_rec *rec,
Buffer *buf, int *err, gchar **err_info);
// The Journal Export Format specification doesn't place limits on entry
// lengths or lines per entry. We do.
#define MAX_EXPORT_ENTRY_LENGTH WTAP_MAX_PACKET_SIZE_STANDARD
#define MAX_EXPORT_ENTRY_LINES 100
#define FLD__CURSOR "__CURSOR="
#define FLD__REALTIME_TIMESTAMP "__REALTIME_TIMESTAMP="
#define FLD__MONOTONIC_TIMESTAMP "__MONOTONIC_TIMESTAMP="
wtap_open_return_val systemd_journal_open(wtap *wth, int *err _U_, gchar **err_info _U_)
{
gchar *entry_buff = (gchar*) g_malloc(MAX_EXPORT_ENTRY_LENGTH);
gchar *entry_line = NULL;
gboolean got_cursor = FALSE;
gboolean got_rt_ts = FALSE;
gboolean got_mt_ts = FALSE;
int line_num;
errno = 0;
for (line_num = 0; line_num < MAX_EXPORT_ENTRY_LINES; line_num++) {
entry_line = file_gets(entry_buff, MAX_EXPORT_ENTRY_LENGTH, wth->fh);
if (!entry_line) {
break;
}
if (entry_line[0] == '\n') {
break;
} else if (strncmp(entry_line, FLD__CURSOR, strlen(FLD__CURSOR)) == 0) {
got_cursor = TRUE;
} else if (strncmp(entry_line, FLD__REALTIME_TIMESTAMP, strlen(FLD__REALTIME_TIMESTAMP)) == 0) {
got_rt_ts = TRUE;
} else if (strncmp(entry_line, FLD__MONOTONIC_TIMESTAMP, strlen(FLD__MONOTONIC_TIMESTAMP)) == 0) {
got_mt_ts = TRUE;
}
}
g_free(entry_buff);
if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
return WTAP_OPEN_ERROR;
}
if (!got_cursor || !got_rt_ts || !got_mt_ts) {
return WTAP_OPEN_NOT_MINE;
}
wth->subtype_read = systemd_journal_read;
wth->subtype_seek_read = systemd_journal_seek_read;
wth->file_encap = WTAP_ENCAP_SYSTEMD_JOURNAL;
wth->file_tsprec = WTAP_TSPREC_USEC;
return WTAP_OPEN_MINE;
}
/* Read the next packet */
static gboolean systemd_journal_read(wtap *wth, int *err _U_, gchar **err_info,
gint64 *data_offset)
{
*data_offset = file_tell(wth->fh);
/* Read record. */
if (!systemd_journal_read_export_entry(wth->fh, &wth->rec, wth->rec_data, err, err_info)) {
/* Read error or EOF */
return FALSE;
}
return TRUE;
}
static gboolean
systemd_journal_seek_read(wtap *wth, gint64 seek_off,
wtap_rec *rec, Buffer *buf,
int *err, gchar **err_info)
{
if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
return FALSE;
/* Read record. */
if (!systemd_journal_read_export_entry(wth->random_fh, rec, buf, err, err_info)) {
/* Read error or EOF */
if (*err == 0) {
/* EOF means "short read" in random-access mode */
*err = WTAP_ERR_SHORT_READ;
}
return FALSE;
}
return TRUE;
}
static gboolean
systemd_journal_read_export_entry(FILE_T fh, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
{
size_t fld_end = 0;
gchar *entry_buff = (gchar*) g_malloc(MAX_EXPORT_ENTRY_LENGTH);
gchar *entry_line = NULL;
gboolean got_cursor = FALSE;
gboolean got_rt_ts = FALSE;
gboolean got_mt_ts = FALSE;
gboolean got_double_newline = FALSE;
int line_num;
size_t rt_ts_len = strlen(FLD__REALTIME_TIMESTAMP);
for (line_num = 0; line_num < MAX_EXPORT_ENTRY_LINES; line_num++) {
entry_line = file_gets(entry_buff + fld_end, MAX_EXPORT_ENTRY_LENGTH - (int) fld_end, fh);
if (!entry_line) {
break;
}
fld_end += strlen(entry_line);
if (entry_line[0] == '\n') {
got_double_newline = TRUE;
break;
} else if (strncmp(entry_line, FLD__CURSOR, strlen(FLD__CURSOR)) == 0) {
got_cursor = TRUE;
} else if (strncmp(entry_line, FLD__REALTIME_TIMESTAMP, rt_ts_len) == 0) {
errno = 0;
unsigned long rt_ts = strtoul(entry_line+rt_ts_len, NULL, 10);
if (!errno) {
rec->ts.secs = (time_t) rt_ts / 1000000;
rec->ts.nsecs = (rt_ts % 1000000) * 1000;
rec->tsprec = WTAP_TSPREC_USEC;
got_rt_ts = TRUE;
}
} else if (strncmp(entry_line, FLD__MONOTONIC_TIMESTAMP, strlen(FLD__MONOTONIC_TIMESTAMP)) == 0) {
got_mt_ts = TRUE;
} else if (!strstr(entry_line, "=")) {
// Start of binary data.
if (fld_end >= MAX_EXPORT_ENTRY_LENGTH - 8) {
*err = WTAP_ERR_BAD_FILE;
*err_info = g_strdup_printf("systemd: binary length too long");
return FALSE;
}
guint64 data_len, le_data_len;
if (!wtap_read_bytes(fh, &le_data_len, 8, err, err_info)) {
return FALSE;
}
memcpy(entry_buff + fld_end, &le_data_len, 8);
fld_end += 8;
data_len = pletoh64(&le_data_len);
if (data_len < 1 || data_len - 1 >= MAX_EXPORT_ENTRY_LENGTH - fld_end) {
*err = WTAP_ERR_BAD_FILE;
*err_info = g_strdup_printf("systemd: binary data too long");
return FALSE;
}
// Data + trailing \n
if (!wtap_read_bytes(fh, entry_buff + fld_end, (unsigned) data_len + 1, err, err_info)) {
return FALSE;
}
fld_end += data_len + 1;
}
if (MAX_EXPORT_ENTRY_LENGTH < fld_end + 2) { // \n\0
break;
}
}
if (!got_cursor || !got_rt_ts || !got_mt_ts) {
g_free(entry_buff);
return FALSE;
}
if (!got_double_newline && !file_eof(fh)) {
g_free(entry_buff);
return FALSE;
}
rec->rec_type = REC_TYPE_PACKET;
rec->presence_flags = WTAP_HAS_TS;
rec->rec_header.packet_header.caplen = (guint32) fld_end;
rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen + 1);
guint8 *pd = ws_buffer_start_ptr(buf);
entry_buff[fld_end+1] = '\0';
memcpy(pd, entry_buff, rec->rec_header.packet_header.caplen + 1);
g_free(entry_buff);
return TRUE;
}
/*
* Editor modelines - http://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:
*/

19
wiretap/systemd_journal.h Normal file
View File

@ -0,0 +1,19 @@
/* systemd_journal.h
*
* Wiretap Library
* Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
*/
#ifndef __SYSTEMD_JOURNAL_H__
#define __SYSTEMD_JOURNAL_H__
#include <glib.h>
#include "wtap.h"
#include "ws_symbol_export.h"
wtap_open_return_val systemd_journal_open(wtap *wth, int *err, gchar **err_info);
#endif // __SYSTEMD_JOURNAL_H__

View File

@ -283,6 +283,7 @@ extern "C" {
#define WTAP_ENCAP_DPAUXMON 200
#define WTAP_ENCAP_RUBY_MARSHAL 201
#define WTAP_ENCAP_RFC7468 202
#define WTAP_ENCAP_SYSTEMD_JOURNAL 203
/* After adding new item here, please also add new item to encap_table_base array */
@ -377,6 +378,7 @@ extern "C" {
#define WTAP_FILE_TYPE_SUBTYPE_DPA400 81
#define WTAP_FILE_TYPE_SUBTYPE_RFC7468 82
#define WTAP_FILE_TYPE_SUBTYPE_RUBY_MARSHAL 83
#define WTAP_FILE_TYPE_SUBTYPE_SYSTEMD_JOURNAL 84
#define WTAP_NUM_FILE_TYPES_SUBTYPES wtap_get_num_file_types_subtypes()