diff --git a/man/busctl.xml b/man/busctl.xml index cc5a6508ef..bcd3f4805e 100644 --- a/man/busctl.xml +++ b/man/busctl.xml @@ -78,10 +78,10 @@ capture SERVICE Similar to monitor but - writes the output in pcap format (for details, see the Libpcap - File Format description). Make sure to redirect - standard output to a file. Tools like + writes the output in pcapng format (for details, see + + PCAP Next Generation (pcapng) Capture File Format). + Make sure to redirect standard output to a file or pipe. Tools like wireshark1 may be used to dissect and view the resulting files. diff --git a/meson.build b/meson.build index c80abc437a..93c0cdd230 100644 --- a/meson.build +++ b/meson.build @@ -3267,6 +3267,7 @@ public_programs += executable( busctl_sources, include_directories : includes, link_with : [libshared], + dependencies : [versiondep], install_rpath : rootlibexecdir, install : true) diff --git a/shell-completion/zsh/_busctl b/shell-completion/zsh/_busctl index ae87ddc9a1..6b8d8a2eaf 100644 --- a/shell-completion/zsh/_busctl +++ b/shell-completion/zsh/_busctl @@ -25,7 +25,7 @@ "list:List bus names" "status:Show bus service, process or bus owner credentials" "monitor:Show bus traffic" - "capture:Capture bus traffix as pcap" + "capture:Capture bus traffic" "tree:Show object tree of service" "introspect:Introspect object" "call:Call a method" diff --git a/src/basic/pcapng.h b/src/basic/pcapng.h new file mode 100644 index 0000000000..57c3af501a --- /dev/null +++ b/src/basic/pcapng.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +/* + * For details about the file format see RFC: + * https://www.ietf.org/id/draft-tuexen-opsawg-pcapng-03.html + * and + * https://github.com/pcapng/pcapng/ + */ +enum pcapng_block_types { + PCAPNG_INTERFACE_BLOCK = 1, + PCAPNG_PACKET_BLOCK, /* Obsolete */ + PCAPNG_SIMPLE_PACKET_BLOCK, + PCAPNG_NAME_RESOLUTION_BLOCK, + PCAPNG_INTERFACE_STATS_BLOCK, + PCAPNG_ENHANCED_PACKET_BLOCK, + + PCAPNG_SECTION_BLOCK = 0x0A0D0D0A, +}; + +struct pcapng_option { + uint16_t code; + uint16_t length; + uint8_t data[]; +}; + +#define PCAPNG_BYTE_ORDER_MAGIC 0x1A2B3C4D +#define PCAPNG_MAJOR_VERS 1 +#define PCAPNG_MINOR_VERS 0 + +enum pcapng_opt { + PCAPNG_OPT_END = 0, + PCAPNG_OPT_COMMENT = 1, +}; + +struct pcapng_section { + uint32_t block_type; + uint32_t block_length; + uint32_t byte_order_magic; + uint16_t major_version; + uint16_t minor_version; + uint64_t section_length; +}; + +enum pcapng_section_opt { + PCAPNG_SHB_HARDWARE = 2, + PCAPNG_SHB_OS = 3, + PCAPNG_SHB_USERAPPL = 4, +}; + +struct pcapng_interface_block { + uint32_t block_type; /* 1 */ + uint32_t block_length; + uint16_t link_type; + uint16_t reserved; + uint32_t snap_len; +}; + +enum pcapng_interface_options { + PCAPNG_IFB_NAME = 2, + PCAPNG_IFB_DESCRIPTION, + PCAPNG_IFB_IPV4ADDR, + PCAPNG_IFB_IPV6ADDR, + PCAPNG_IFB_MACADDR, + PCAPNG_IFB_EUIADDR, + PCAPNG_IFB_SPEED, + PCAPNG_IFB_TSRESOL, + PCAPNG_IFB_TZONE, + PCAPNG_IFB_FILTER, + PCAPNG_IFB_OS, + PCAPNG_IFB_FCSLEN, + PCAPNG_IFB_TSOFFSET, + PCAPNG_IFB_HARDWARE, +}; + +struct pcapng_enhance_packet_block { + uint32_t block_type; /* 6 */ + uint32_t block_length; + uint32_t interface_id; + uint32_t timestamp_hi; + uint32_t timestamp_lo; + uint32_t capture_length; + uint32_t original_length; +}; + +/* Flags values */ +#define PCAPNG_IFB_INBOUND 0b01 +#define PCAPNG_IFB_OUTBOUND 0b10 + +enum pcapng_epb_options { + PCAPNG_EPB_FLAGS = 2, + PCAPNG_EPB_HASH, + PCAPNG_EPB_DROPCOUNT, + PCAPNG_EPB_PACKETID, + PCAPNG_EPB_QUEUE, + PCAPNG_EPB_VERDICT, +}; + +struct pcapng_statistics_block { + uint32_t block_type; /* 5 */ + uint32_t block_length; + uint32_t interface_id; + uint32_t timestamp_hi; + uint32_t timestamp_lo; +}; + +enum pcapng_isb_options { + PCAPNG_ISB_STARTTIME = 2, + PCAPNG_ISB_ENDTIME, + PCAPNG_ISB_IFRECV, + PCAPNG_ISB_IFDROP, + PCAPNG_ISB_FILTERACCEPT, + PCAPNG_ISB_OSDROP, + PCAPNG_ISB_USRDELIV, +}; diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index c0c9b23ae9..b532de4f9b 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -20,6 +20,7 @@ #include "json.h" #include "log.h" #include "main-func.h" +#include "os-util.h" #include "pager.h" #include "parse-argument.h" #include "parse-util.h" @@ -31,6 +32,7 @@ #include "terminal-util.h" #include "user-util.h" #include "verbs.h" +#include "version.h" static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; static PagerFlags arg_pager_flags = 0; @@ -1329,13 +1331,20 @@ static int verb_monitor(int argc, char **argv, void *userdata) { } static int verb_capture(int argc, char **argv, void *userdata) { + _cleanup_free_ char *osname = NULL; + static const char info[] = + "busctl (systemd) " STRINGIFY(PROJECT_VERSION) " (Git " GIT_VERSION ")"; int r; if (isatty(fileno(stdout)) > 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Refusing to write message data to console, please redirect output to a file."); - bus_pcap_header(arg_snaplen, stdout); + r = parse_os_release(NULL, "PRETTY_NAME", &osname); + if (r < 0) + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_INFO, r, + "Failed to read os-release file, ignoring: %m"); + bus_pcap_header(arg_snaplen, osname, info, stdout); r = monitor(argc, argv, message_pcap); if (r < 0) diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c index 0a4b3cbf68..19040452bc 100644 --- a/src/libsystemd/sd-bus/bus-dump.c +++ b/src/libsystemd/sd-bus/bus-dump.c @@ -13,6 +13,7 @@ #include "format-util.h" #include "glyph-util.h" #include "macro.h" +#include "pcapng.h" #include "string-util.h" #include "strv.h" #include "terminal-util.h" @@ -502,57 +503,99 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { return 0; } -/* - * For details about the file format, see: - * - * http://wiki.wireshark.org/Development/LibpcapFileFormat - */ +static uint16_t pcapng_optlen(size_t len) { + return ALIGN4(len + sizeof(struct pcapng_option)); +} -typedef struct _packed_ pcap_hdr_s { - uint32_t magic_number; /* magic number */ - uint16_t version_major; /* major version number */ - uint16_t version_minor; /* minor version number */ - int32_t thiszone; /* GMT to local correction */ - uint32_t sigfigs; /* accuracy of timestamps */ - uint32_t snaplen; /* max length of captured packets, in octets */ - uint32_t network; /* data link type */ -} pcap_hdr_t ; - -typedef struct _packed_ pcaprec_hdr_s { - uint32_t ts_sec; /* timestamp seconds */ - uint32_t ts_usec; /* timestamp microseconds */ - uint32_t incl_len; /* number of octets of packet saved in file */ - uint32_t orig_len; /* actual length of packet */ -} pcaprec_hdr_t; - -int bus_pcap_header(size_t snaplen, FILE *f) { - - pcap_hdr_t hdr = { - .magic_number = 0xa1b2c3d4U, - .version_major = 2, - .version_minor = 4, - .thiszone = 0, /* UTC */ - .sigfigs = 0, - .network = 231, /* D-Bus */ +static void pcapng_putopt(FILE *f, uint16_t code, const void *data, size_t len) { + struct pcapng_option opt = { + .code = code, + .length = len, }; - if (!f) - f = stdout; + assert(f); + assert((uint16_t) len == len); + assert(data || len == 0); + fwrite(&opt, 1, sizeof(opt), f); + if (len > 0) { + size_t pad = ALIGN4(len) - len; + + fwrite(data, 1, len, f); + + assert(pad < sizeof(uint32_t)); + while (pad-- > 0) + fputc('\0', f); + } +} + +static void pcapng_section_header(FILE *f, const char *os, const char *app) { + uint32_t len; + + assert(f); + + /* determine length of section header and options */ + len = sizeof(struct pcapng_section); + if (os) + len += pcapng_optlen(strlen(os)); + if (app) + len += pcapng_optlen(strlen(app)); + len += pcapng_optlen(0); /* OPT_END */ + len += sizeof(uint32_t); /* trailer length */ + + struct pcapng_section hdr = { + .block_type = PCAPNG_SECTION_BLOCK, + .block_length = len, + .byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC, + .major_version = PCAPNG_MAJOR_VERS, + .minor_version = PCAPNG_MINOR_VERS, + .section_length = UINT64_MAX, + }; + + fwrite(&hdr, 1, sizeof(hdr), f); + if (os) + pcapng_putopt(f, PCAPNG_SHB_OS, os, strlen(os)); + if (app) + pcapng_putopt(f, PCAPNG_SHB_USERAPPL, app, strlen(app)); + pcapng_putopt(f, PCAPNG_OPT_END, NULL, 0); + fwrite(&len, 1, sizeof(uint32_t), f); +} + +/* Only have a single instance of dbus pseudo interface */ +static void pcapng_interface_header(FILE *f, size_t snaplen) { + uint32_t len; + + assert(f); assert(snaplen > 0); assert((size_t) (uint32_t) snaplen == snaplen); - hdr.snaplen = (uint32_t) snaplen; + /* no options (yet) */ + len = sizeof(struct pcapng_interface_block) + sizeof(uint32_t); + struct pcapng_interface_block hdr = { + .block_type = PCAPNG_INTERFACE_BLOCK, + .block_length = len, + .link_type = 231, /* D-Bus */ + .snap_len = snaplen, + }; fwrite(&hdr, 1, sizeof(hdr), f); + fwrite(&len, 1, sizeof(uint32_t), f); +} +int bus_pcap_header(size_t snaplen, const char *os, const char *info, FILE *f) { + if (!f) + f = stdout; + + pcapng_section_header(f, os, info); + pcapng_interface_header(f, snaplen); return fflush_and_check(f); } int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) { struct bus_body_part *part; - pcaprec_hdr_t hdr = {}; - struct timeval tv; + size_t msglen, caplen, pad; + uint32_t length; + uint64_t ts; unsigned i; size_t w; @@ -563,18 +606,27 @@ int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) { assert(snaplen > 0); assert((size_t) (uint32_t) snaplen == snaplen); - if (m->realtime != 0) - timeval_store(&tv, m->realtime); - else - assert_se(gettimeofday(&tv, NULL) >= 0); + ts = m->realtime ?: now(CLOCK_REALTIME); + msglen = BUS_MESSAGE_SIZE(m); + caplen = MIN(msglen, snaplen); + pad = ALIGN4(caplen) - caplen; - hdr.ts_sec = tv.tv_sec; - hdr.ts_usec = tv.tv_usec; - hdr.orig_len = BUS_MESSAGE_SIZE(m); - hdr.incl_len = MIN(hdr.orig_len, snaplen); + /* packet block has no options */ + length = sizeof(struct pcapng_enhance_packet_block) + + caplen + pad + sizeof(uint32_t); - /* write the pcap header */ - fwrite(&hdr, 1, sizeof(hdr), f); + struct pcapng_enhance_packet_block epb = { + .block_type = PCAPNG_ENHANCED_PACKET_BLOCK, + .block_length = length, + .interface_id = 0, + .timestamp_hi = (uint32_t)(ts >> 32), + .timestamp_lo = (uint32_t)ts, + .original_length = msglen, + .capture_length = caplen, + }; + + /* write the pcapng enhanced packet block header */ + fwrite(&epb, 1, sizeof(epb), f); /* write the dbus header */ w = MIN(BUS_MESSAGE_BODY_BEGIN(m), snaplen); @@ -591,5 +643,11 @@ int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) { snaplen -= w; } + while (pad-- > 0) + fputc('\0', f); + + /* trailing block length */ + fwrite(&length, 1, sizeof(uint32_t), f); + return fflush_and_check(f); } diff --git a/src/libsystemd/sd-bus/bus-dump.h b/src/libsystemd/sd-bus/bus-dump.h index aeb4616e9a..e7470ba681 100644 --- a/src/libsystemd/sd-bus/bus-dump.h +++ b/src/libsystemd/sd-bus/bus-dump.h @@ -8,5 +8,5 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse); -int bus_pcap_header(size_t snaplen, FILE *f); +int bus_pcap_header(size_t snaplen, const char *os, const char *app, FILE *f); int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f);