[DTrace-devel] [PATCH 2/2] Implement the pcap action

Kris Van Hees kris.van.hees at oracle.com
Sat Sep 23 19:14:20 UTC 2023


Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 libdtrace/dt_cg.c                             | 133 ++++++++++++++----
 libdtrace/dt_consume.c                        |  53 ++++---
 libdtrace/dt_open.c                           |   5 +
 libdtrace/pcap.d                              |   4 +-
 test/unittest/pcap/tst.pcap.file.sh           |   1 -
 .../pcap/tst.pcap.stdout-fork-error.sh        |   2 -
 test/unittest/pcap/tst.pcap.stdout.sh         |   2 -
 test/unittest/pcap/tst.pcap.tshark.sh         |   1 -
 8 files changed, 145 insertions(+), 56 deletions(-)

diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 86ee6908..79e233b4 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -1632,6 +1632,29 @@ dt_cg_clsflags(dt_pcb_t *pcb, dtrace_actkind_t kind, const dt_node_t *dnp)
 	*cfp |= DT_CLSFLAG_DATAREC;
 }
 
+/*
+ * Get offsetof(structname, membername) information from CTF.
+ * Optionally, also get member size.
+ */
+static int
+dt_cg_ctf_offsetof(const char *structname, const char *membername, size_t *sizep)
+{
+	ctf_file_t *cfp = yypcb->pcb_hdl->dt_shared_ctf;
+	ctf_id_t type;
+	ctf_membinfo_t ctm;
+
+	if (!cfp)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
+	type = ctf_lookup_by_name(cfp, structname);
+	if (type == CTF_ERR)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
+	if (ctf_member_info(cfp, type, membername, &ctm) == CTF_ERR)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
+	if (sizep)
+		*sizep = ctf_type_size(cfp, ctm.ctm_type);
+	return (ctm.ctm_offset / NBBY);
+}
+
 static void
 dt_cg_act_breakpoint(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
 {
@@ -1882,8 +1905,91 @@ dt_cg_act_panic(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
 static void
 dt_cg_act_pcap(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
 {
-	dnerror(dnp, D_UNKNOWN, "pcap() is not implemented (yet)\n");
-	/* FIXME: Needs implementation */
+	dtrace_hdl_t	*dtp = pcb->pcb_hdl;
+	dt_regset_t	*drp = pcb->pcb_regs;
+	dt_irlist_t	*dlp = &pcb->pcb_ir;
+	dt_node_t	*addr = dnp->dn_args;
+	dt_node_t	*proto = addr->dn_list;
+	uint_t		time_off, size_off, data_off, off;
+	uint_t		lbl_lenok = dt_irlist_label(dlp);
+	uint64_t	pcapsz = dtp->dt_options[DTRACEOPT_PCAPSIZE];
+	int		lenreg;
+
+	TRACE_REGSET("pcap(): Begin ");
+
+	/*
+	 * Create records for timestamp, size, protocol id, and packet data.
+	 * The protocol id is stored immediately from the passed in argument.
+	 */
+	time_off = dt_rec_add(dtp, dt_cg_fill_gap, DTRACEACT_PCAP,
+			      sizeof(uint64_t), sizeof(uint64_t), NULL, 0);
+	size_off = dt_rec_add(dtp, dt_cg_fill_gap, DTRACEACT_PCAP,
+			      sizeof(uint32_t), sizeof(uint32_t), NULL, 0);
+	dt_cg_store_val(pcb, proto, DTRACEACT_PCAP, NULL, 0);
+	data_off = dt_rec_add(dtp, dt_cg_fill_gap, DTRACEACT_PCAP,
+			      dtp->dt_options[DTRACEOPT_PCAPSIZE], 1, NULL, 0);
+
+	/* Store timestamp (ns since boot time). */
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emit(dlp,  BPF_CALL_HELPER(BPF_FUNC_ktime_get_ns));
+	dt_regset_free_args(drp);
+	emit(dlp,  BPF_STORE(BPF_DW, BPF_REG_9, time_off, BPF_REG_0));
+	dt_regset_free(drp, BPF_REG_0);
+
+	/*
+	 * Determine and store the packet data size.
+	 * The size of the data to be copied (and reported as size of the
+	 * packet capture) is the lesser of the data size (skb->len) and the
+	 * capture size.
+	 */
+	dt_cg_node(addr, dlp, drp);
+	off = dt_cg_ctf_offsetof("struct sk_buff", "len", NULL);
+
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	emit(dlp,  BPF_MOV_REG(BPF_REG_3, addr->dn_reg));
+	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, off));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_2, sizeof(uint32_t)));
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_SP));
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emit(dlp,  BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
+	dt_regset_free_args(drp);
+	dt_regset_free(drp, BPF_REG_0);
+
+	if ((lenreg = dt_regset_alloc(drp)) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+
+	emit(dlp,  BPF_LOAD(BPF_DW, lenreg, BPF_REG_FP, DT_STK_SP));
+	emit(dlp,  BPF_LOAD(BPF_W, lenreg, lenreg, 0));
+	emit(dlp,  BPF_BRANCH_IMM(BPF_JLE, lenreg, pcapsz, lbl_lenok));
+	emit(dlp,  BPF_MOV_IMM(lenreg, pcapsz));
+	emitl(dlp, lbl_lenok,
+		   BPF_STORE(BPF_W, BPF_REG_9, size_off, lenreg));
+
+	/* Copy the packet data to the output buffer. */
+	off = dt_cg_ctf_offsetof("struct sk_buff", "data", NULL);
+
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	emit(dlp,  BPF_MOV_REG(BPF_REG_3, addr->dn_reg));
+	dt_regset_free(drp, addr->dn_reg);
+	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, off));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_2, sizeof(uint64_t)));
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_SP));
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emit(dlp,  BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_FP, DT_STK_SP));
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_3, 0));
+	emit(dlp,  BPF_MOV_REG(BPF_REG_2, lenreg));
+	emit(dlp,  BPF_MOV_REG(BPF_REG_1, BPF_REG_9));
+	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, data_off));
+	emit(dlp,  BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
+	dt_regset_free_args(drp);
+	dt_regset_free(drp, BPF_REG_0);
+
+	TRACE_REGSET("pcap(): End   ");
 }
 
 static void
@@ -4276,29 +4382,6 @@ dt_cg_asgn_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	}
 }
 
-/*
- * Get offsetof(structname, membername) information from CTF.
- * Optionally, also get member size.
- */
-static int
-dt_cg_ctf_offsetof(const char *structname, const char *membername, size_t *sizep)
-{
-	ctf_file_t *cfp = yypcb->pcb_hdl->dt_shared_ctf;
-	ctf_id_t type;
-	ctf_membinfo_t ctm;
-
-	if (!cfp)
-		longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
-	type = ctf_lookup_by_name(cfp, structname);
-	if (type == CTF_ERR)
-		longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
-	if (ctf_member_info(cfp, type, membername, &ctm) == CTF_ERR)
-		longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
-	if (sizep)
-		*sizep = ctf_type_size(cfp, ctm.ctm_type);
-	return (ctm.ctm_offset / NBBY);
-}
-
 static void
 dt_cg_uregs(unsigned int idx, dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 {
diff --git a/libdtrace/dt_consume.c b/libdtrace/dt_consume.c
index 5054533b..2d050e95 100644
--- a/libdtrace/dt_consume.c
+++ b/libdtrace/dt_consume.c
@@ -1427,28 +1427,26 @@ dt_print_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr)
 }
 
 int
-dt_print_pcap(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec,
+dt_print_pcap(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, uint_t nrecs,
 	      const caddr_t buf)
 {
-	caddr_t	addr;
-	uint64_t time, proto, pktlen, maxlen;
-	const char *filename;
+	caddr_t			data;
+	uint64_t		time, proto, pktlen;
+	uint64_t		maxlen = dtp->dt_options[DTRACEOPT_PCAPSIZE];
+	const char		*filename;
 
-	addr = (caddr_t)buf + rec->dtrd_offset;
+	if (nrecs < 4)
+		return dt_set_errno(dtp, EDT_PCAP);
 
 	if (dt_read_scalar(buf, rec, &time) < 0 ||
-	    dt_read_scalar(buf + sizeof(uint64_t), rec, &pktlen) < 0)
+	    dt_read_scalar(buf, rec + 1, &pktlen) < 0)
 		return dt_set_errno(dtp, EDT_PCAP);
 
-	if (pktlen == 0) {
-		/*
-		 * skb must have been NULL, skip without capturing.
-		 */
+	/* If pktlen is 0, the skb must have been ULL, so don't capture it. */
+	if (pktlen == 0)
 		return 0;
-	}
-	maxlen = DT_PCAPSIZE(dtp->dt_options[DTRACEOPT_PCAPSIZE]);
 
-	if (dt_read_scalar(buf, rec + 1, &proto) < 0)
+	if (dt_read_scalar(buf, rec + 2, &proto) < 0)
 		return dt_set_errno(dtp, EDT_PCAP);
 
 	/*
@@ -1456,17 +1454,16 @@ dt_print_pcap(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec,
 	 * specified or if we have been able to connect a pipe to tshark,
 	 * otherwise print tracemem-like output.
 	 */
+	data = buf + (rec + 3)->dtrd_offset;
 	filename = dt_pcap_filename(dtp, fp);
-	if (filename != NULL) {
-		dt_pcap_dump(dtp, filename, proto, time,
-			     addr + (2 * sizeof(uint64_t)), (uint32_t)pktlen,
-			     (uint32_t)maxlen);
-	} else {
-		if (dt_print_rawbytes(dtp, fp, addr + (2 * sizeof(uint64_t)),
-		    pktlen > maxlen ? maxlen : pktlen) < 0)
-			return -1;
-	}
-	return 0;
+	if (filename != NULL)
+		dt_pcap_dump(dtp, filename, proto, time, data,
+			     (uint32_t)pktlen, (uint32_t)maxlen);
+	else if (dt_print_rawbytes(dtp, fp, data,
+				   pktlen > maxlen ? maxlen : pktlen) < 0)
+		return -1;
+
+	return 4;
 }
 
 /*
@@ -2508,6 +2505,16 @@ dt_consume_one_probe(dtrace_hdl_t *dtp, FILE *fp, char *data, uint32_t size,
 			i += n - 1;
 			continue;
 		}
+		case DTRACEACT_PCAP: {
+			int	nrecs;
+
+			nrecs = epd->dtdd_nrecs - i;
+			n = dt_print_pcap(dtp, fp, rec, nrecs, data);
+			if (n < 0)
+				return -1;
+			i += n - 1;
+			continue;
+		}
 		case DTRACEACT_PRINTF:
 			func = dtrace_fprintf;
 			break;
diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
index a2d8ebd3..df694f0e 100644
--- a/libdtrace/dt_open.c
+++ b/libdtrace/dt_open.c
@@ -798,6 +798,11 @@ dt_vopen(int version, int flags, int *errp,
 		return set_open_errno(dtp, errp, EDT_READMAXSTACK);
 	fclose(fd);
 
+	/*
+	 * Set the default pcap capture size.
+	 */
+	dtp->dt_options[DTRACEOPT_PCAPSIZE] = DT_PCAP_DEF_PKTSIZE;
+
 	/*
 	 * Set the default data rates.
 	 */
diff --git a/libdtrace/pcap.d b/libdtrace/pcap.d
index 5d0dc90d..21a8e74a 100644
--- a/libdtrace/pcap.d
+++ b/libdtrace/pcap.d
@@ -1,12 +1,12 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
  * Licensed under the Universal Permissive License v 1.0 as shown at
  * http://oss.oracle.com/licenses/upl.
  */
 
 #pragma D depends_on module vmlinux
-#pragma D depends_on provider tcp
+#pragma D depends_on provider ip
 
 /*
  * In general, PCAP_<type> names are chosen to match DL_<type> DLPI
diff --git a/test/unittest/pcap/tst.pcap.file.sh b/test/unittest/pcap/tst.pcap.file.sh
index f3be6541..8973971a 100755
--- a/test/unittest/pcap/tst.pcap.file.sh
+++ b/test/unittest/pcap/tst.pcap.file.sh
@@ -4,7 +4,6 @@
 # Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
 # Licensed under the Universal Permissive License v 1.0 as shown at
 # http://oss.oracle.com/licenses/upl.
-# @@xfail: dtv2
 
 #
 # Ensure pcap() action directed to file succeeds, and verify the content
diff --git a/test/unittest/pcap/tst.pcap.stdout-fork-error.sh b/test/unittest/pcap/tst.pcap.stdout-fork-error.sh
index 5681936a..9e7b92de 100755
--- a/test/unittest/pcap/tst.pcap.stdout-fork-error.sh
+++ b/test/unittest/pcap/tst.pcap.stdout-fork-error.sh
@@ -12,8 +12,6 @@
 # skb->data.
 #
 
-# @@xfail: need ip provider
-
 if (( $# != 1 )); then
 	echo "expected one argument: <dtrace-path>" >&2
 	exit 2
diff --git a/test/unittest/pcap/tst.pcap.stdout.sh b/test/unittest/pcap/tst.pcap.stdout.sh
index bb0dc242..6ad322dc 100755
--- a/test/unittest/pcap/tst.pcap.stdout.sh
+++ b/test/unittest/pcap/tst.pcap.stdout.sh
@@ -11,8 +11,6 @@
 # skb->data.
 #
 
-# @@xfail: need ip provider
-
 if (( $# != 1 )); then
 	echo "expected one argument: <dtrace-path>" >&2
 	exit 2
diff --git a/test/unittest/pcap/tst.pcap.tshark.sh b/test/unittest/pcap/tst.pcap.tshark.sh
index 1d8ebe5b..f3127d27 100755
--- a/test/unittest/pcap/tst.pcap.tshark.sh
+++ b/test/unittest/pcap/tst.pcap.tshark.sh
@@ -4,7 +4,6 @@
 # Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
 # Licensed under the Universal Permissive License v 1.0 as shown at
 # http://oss.oracle.com/licenses/upl.
-# @@xfail: dtv2
 
 #
 # Ensure pcap() action in absence of freopen() results in tshark output
-- 
2.40.1




More information about the DTrace-devel mailing list