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);