summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Hemminger <sthemmin@microsoft.com>2021-12-24 22:07:40 -0800
committerGitHub <noreply@github.com>2021-12-25 15:07:40 +0900
commit7c4bd9ac98db6f8804fca6a362a583057f6058f7 (patch)
tree9b7174a8c603500cfed7d87f6584b3b07a40eed3
parent402b81ffd8548249e846b045b10d6e8c852d2904 (diff)
downloadsystemd-7c4bd9ac98db6f8804fca6a362a583057f6058f7.tar.gz
bus-dump: change capture output to use pcapng (#21738)
This patch changes busctl capture to generate pcapng format instead of the legacy pcap format files. It includes basic meta-data in the file and still uses microsecond time resolution. In future, more things can be added such as high resolution timestams, statistics, etc. PCAP Next Generation capture file format is what tshark uses and is in process of being standardized in IETF. It is also readable with libpcap. $ capinfos /tmp/new.pcapng File name: /tmp/new.pcapng File type: Wireshark/... - pcapng File encapsulation: D-Bus File timestamp precision: microseconds (6) Packet size limit: file hdr: (not set) Packet size limit: inferred: 4096 bytes Number of packets: 22 File size: 21kB Data size: 20kB Capture duration: 0.005694 seconds First packet time: 2021-12-11 11:57:42.788374 Last packet time: 2021-12-11 11:57:42.794068 Data byte rate: 3,671kBps Data bit rate: 29Mbps Average packet size: 950.27 bytes Average packet rate: 3,863 packets/s SHA256: b85ed8b094af60c64aa6d9db4a91404e841736d36b9e662d707db9e4096148f1 RIPEMD160: 81f9bac7ec0ec5cd1d55ede136a5c90413894e3a SHA1: 8400822ef724b934d6000f5b7604b9e6e91be011 Strict time order: True Capture oper-sys: Linux 5.14.0-0.bpo.2-amd64 Capture application: systemd 250 (250-rc2-33-gdc79ae2+) Number of interfaces in file: 1 Interface #0 info: Encapsulation = D-Bus (146 - dbus) Capture length = 4096 Time precision = microseconds (6) Time ticks per second = 1000000 Number of stat entries = 0 Number of packets = 22
-rw-r--r--man/busctl.xml8
-rw-r--r--meson.build1
-rw-r--r--shell-completion/zsh/_busctl2
-rw-r--r--src/basic/pcapng.h115
-rw-r--r--src/busctl/busctl.c11
-rw-r--r--src/libsystemd/sd-bus/bus-dump.c154
-rw-r--r--src/libsystemd/sd-bus/bus-dump.h2
7 files changed, 238 insertions, 55 deletions
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 @@
<term><command>capture</command> <arg choice="opt" rep="repeat"><replaceable>SERVICE</replaceable></arg></term>
<listitem><para>Similar to <command>monitor</command> but
- writes the output in pcap format (for details, see the <ulink
- url="https://wiki.wireshark.org/Development/LibpcapFileFormat">Libpcap
- File Format</ulink> description). Make sure to redirect
- standard output to a file. Tools like
+ writes the output in pcapng format (for details, see
+ <ulink url="https://github.com/pcapng/pcapng/">
+ PCAP Next Generation (pcapng) Capture File Format</ulink>).
+ Make sure to redirect standard output to a file or pipe. Tools like
<citerefentry project='die-net'><refentrytitle>wireshark</refentrytitle><manvolnum>1</manvolnum></citerefentry>
may be used to dissect and view the resulting
files.</para></listitem>
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
- */
-
-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 uint16_t pcapng_optlen(size_t len) {
+ return ALIGN4(len + sizeof(struct pcapng_option));
+}
+
+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;
+
+ /* packet block has no options */
+ length = sizeof(struct pcapng_enhance_packet_block)
+ + caplen + pad + sizeof(uint32_t);
+
+ 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,
+ };
- 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);
-
- /* write the pcap header */
- fwrite(&hdr, 1, sizeof(hdr), f);
+ /* 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);