diff --git a/docbook/release-notes.asciidoc b/docbook/release-notes.asciidoc index 2893af1a69..b1d3858959 100644 --- a/docbook/release-notes.asciidoc +++ b/docbook/release-notes.asciidoc @@ -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 diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt index 2f4b850783..8e68634f2b 100644 --- a/epan/dissectors/CMakeLists.txt +++ b/epan/dissectors/CMakeLists.txt @@ -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 diff --git a/epan/dissectors/packet-syslog.c b/epan/dissectors/packet-syslog.c index 6b7d74d453..f591af02eb 100644 --- a/epan/dissectors/packet-syslog.c +++ b/epan/dissectors/packet-syslog.c @@ -20,6 +20,8 @@ #include #include +#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, diff --git a/epan/dissectors/packet-syslog.h b/epan/dissectors/packet-syslog.h new file mode 100644 index 0000000000..4a5bcf8662 --- /dev/null +++ b/epan/dissectors/packet-syslog.h @@ -0,0 +1,104 @@ +/* packet-syslog.h + * Routines for syslog message dissection + * + * Copyright 2000, Gerald Combs + * + * Support for passing SS7 MSUs (from the Cisco ITP Packet Logging + * facility) to the MTP3 dissector by Abhik Sarkar + * with some rework by Jeff Morriss + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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__ diff --git a/epan/dissectors/packet-systemd-journal.c b/epan/dissectors/packet-systemd-journal.c new file mode 100644 index 0000000000..f424ded49e --- /dev/null +++ b/epan/dissectors/packet-systemd-journal.c @@ -0,0 +1,839 @@ +/* packet-systemd-journal-export.c + * Routines for systemd journal export (application/vnd.fdo.journal) dissection + * Copyright 2018, Gerald Combs + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 + +#include +#include +#include + +#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: + */ diff --git a/wiretap/CMakeLists.txt b/wiretap/CMakeLists.txt index 99848e93bf..c2f0eea98f 100644 --- a/wiretap/CMakeLists.txt +++ b/wiretap/CMakeLists.txt @@ -74,6 +74,7 @@ set(WIRETAP_NONGENERATED_FILES ruby_marshal.c snoop.c stanag4607.c + systemd_journal.c tnef.c toshiba.c visual.c diff --git a/wiretap/README.developer b/wiretap/README.developer index 9b5211fa22..e887227321 100644 --- a/wiretap/README.developer +++ b/wiretap/README.developer @@ -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: diff --git a/wiretap/file_access.c b/wiretap/file_access.c index 4f9b3ffb6f..9b2f1011c0 100644 --- a/wiretap/file_access.c +++ b/wiretap/file_access.c @@ -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 diff --git a/wiretap/systemd_journal.c b/wiretap/systemd_journal.c new file mode 100644 index 0000000000..fbff4656d9 --- /dev/null +++ b/wiretap/systemd_journal.c @@ -0,0 +1,236 @@ +/* systemd_journal.c + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include +#include +#include +#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: + */ diff --git a/wiretap/systemd_journal.h b/wiretap/systemd_journal.h new file mode 100644 index 0000000000..f288ef87af --- /dev/null +++ b/wiretap/systemd_journal.h @@ -0,0 +1,19 @@ +/* systemd_journal.h + * + * Wiretap Library + * Copyright (c) 1998 by Gilbert Ramirez + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ + +#ifndef __SYSTEMD_JOURNAL_H__ +#define __SYSTEMD_JOURNAL_H__ + +#include +#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__ diff --git a/wiretap/wtap.h b/wiretap/wtap.h index 8c7a8dc50f..be00f67337 100644 --- a/wiretap/wtap.h +++ b/wiretap/wtap.h @@ -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()