Skip to content

Commit

Permalink
Implement the pcap action
Browse files Browse the repository at this point in the history
Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
  • Loading branch information
kvanhees committed Sep 23, 2023
1 parent b1374f2 commit ab98443
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 56 deletions.
133 changes: 108 additions & 25 deletions libdtrace/dt_cg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down
53 changes: 30 additions & 23 deletions libdtrace/dt_consume.c
Original file line number Diff line number Diff line change
Expand Up @@ -1427,46 +1427,43 @@ 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);

/*
* Dump pcap data to dump file if handler command/output file is
* 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;
}

/*
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions libdtrace/dt_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
4 changes: 2 additions & 2 deletions libdtrace/pcap.d
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 0 additions & 1 deletion test/unittest/pcap/tst.pcap.file.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions test/unittest/pcap/tst.pcap.stdout-fork-error.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
# skb->data.
#

# @@xfail: need ip provider

if (( $# != 1 )); then
echo "expected one argument: <dtrace-path>" >&2
exit 2
Expand Down
2 changes: 0 additions & 2 deletions test/unittest/pcap/tst.pcap.stdout.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
# skb->data.
#

# @@xfail: need ip provider

if (( $# != 1 )); then
echo "expected one argument: <dtrace-path>" >&2
exit 2
Expand Down
1 change: 0 additions & 1 deletion test/unittest/pcap/tst.pcap.tshark.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ab98443

Please sign in to comment.