* [PATCH v5 0/6] DTrace TCP provider
@ 2025-07-22 10:07 Alan Maguire
2025-07-22 10:07 ` [PATCH v5 1/6] cg: refactor get_member() to use dt_cg_ctf_offsetof Alan Maguire
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Alan Maguire @ 2025-07-22 10:07 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire
This series adds TCP provider support, where the probes are
implemented via underlying fbt and sdt probes.
Due to the use of the sock/inet_sock_set_state tracepoint, intended
for ~5.15 kernels and later. Tried replacing this with
fbt::tcp_set_state:entry
but this misses a few state transitions, so stuck with using
the tracepoint.
All tests under test/unittest/tcp pass unmodified on an upstream
(6.15) kernel and 5.15 UEK7U3 kernel. The updated remote address
retrieval patch helps ensure tests can all be run.
It implements all documented TCP provider probes:
accept-established, accept-refused, connnect-request,
connect-established, connect-refused, receive, send,
state-change
Note that to function correctly this series depends on the
cg-related patches:
parser: add dt_node_is_tstring()
cg: validate tstring alloc/free
cg: optimize ternary expressions for strings
tstring: fix leaks
Changes since v4:
- updated patch 1 to rename dt_cg_get_member() to
dt_cg_tramp_get_member() and have it make use of
dt_cg_ctf_offsetof() (Kris, patch 1)
- remove tslot update patch since it is handled elsewhere
(Kris)
- added R-Bs from Kris
Changes since v3:
- fix patch 2 to really use/describe 6 tstring slots (patch 2)
Changes since v2:
- document tslots change properly, add test (Eugene, patch 2, 3)
- move more generic type definitions to net.d
- retain depends_on provider tcp in tcp.d (Kris, patch 5)
- use depends_on library ip.d to ensure loading order (patch 5)
- revert tcp flags to uint8_t (Kris, patch 5)
- improve comments in tcp.d (Kris, Eugene, patch 5)
- fix up handling of ip:::send for older kernels to avoid null
dereferences (Eugene, patch 5)
- fix up provider arguments to match spec (Eugene, patch 5)
Changes since RFC:
- fixed issues with test failures on UEK7 due to missing
SYN_RCV state change
- moved get_member() to dt_cg.c (patch 1)
Alan Maguire (6):
cg: refactor get_member() to use dt_cg_ctf_offsetof
test/operators: extend ternary tests to cover inet_ntoa*()s
providers: move network-generic definitions to net.d
tcp: new provider
dlibs: sync ip.d, net.d and tcp.d
unittest/tcp: update test.x
dlibs/aarch64/5.11/ip.d | 27 +-
dlibs/aarch64/5.11/net.d | 31 +-
dlibs/aarch64/5.11/tcp.d | 67 +++--
dlibs/aarch64/5.12/ip.d | 27 +-
dlibs/aarch64/5.12/net.d | 31 +-
dlibs/aarch64/5.12/tcp.d | 67 +++--
dlibs/aarch64/5.14/ip.d | 27 +-
dlibs/aarch64/5.14/net.d | 31 +-
dlibs/aarch64/5.14/tcp.d | 67 +++--
dlibs/aarch64/5.16/ip.d | 27 +-
dlibs/aarch64/5.16/net.d | 31 +-
dlibs/aarch64/5.16/tcp.d | 67 +++--
dlibs/aarch64/5.2/ip.d | 27 +-
dlibs/aarch64/5.2/net.d | 31 +-
dlibs/aarch64/5.2/tcp.d | 67 +++--
dlibs/aarch64/5.6/ip.d | 27 +-
dlibs/aarch64/5.6/net.d | 31 +-
dlibs/aarch64/5.6/tcp.d | 67 +++--
dlibs/aarch64/6.1/ip.d | 27 +-
dlibs/aarch64/6.1/net.d | 31 +-
dlibs/aarch64/6.1/tcp.d | 67 +++--
dlibs/aarch64/6.10/ip.d | 27 +-
dlibs/aarch64/6.10/net.d | 31 +-
dlibs/aarch64/6.10/tcp.d | 67 +++--
dlibs/x86_64/5.11/ip.d | 27 +-
dlibs/x86_64/5.11/net.d | 31 +-
dlibs/x86_64/5.11/tcp.d | 67 +++--
dlibs/x86_64/5.12/ip.d | 27 +-
dlibs/x86_64/5.12/net.d | 31 +-
dlibs/x86_64/5.12/tcp.d | 67 +++--
dlibs/x86_64/5.14/ip.d | 27 +-
dlibs/x86_64/5.14/net.d | 31 +-
dlibs/x86_64/5.14/tcp.d | 67 +++--
dlibs/x86_64/5.16/ip.d | 27 +-
dlibs/x86_64/5.16/net.d | 31 +-
dlibs/x86_64/5.16/tcp.d | 67 +++--
dlibs/x86_64/5.2/ip.d | 27 +-
dlibs/x86_64/5.2/net.d | 31 +-
dlibs/x86_64/5.2/tcp.d | 67 +++--
dlibs/x86_64/5.6/ip.d | 27 +-
dlibs/x86_64/5.6/net.d | 31 +-
dlibs/x86_64/5.6/tcp.d | 67 +++--
dlibs/x86_64/6.1/ip.d | 27 +-
dlibs/x86_64/6.1/net.d | 31 +-
dlibs/x86_64/6.1/tcp.d | 67 +++--
dlibs/x86_64/6.10/ip.d | 27 +-
dlibs/x86_64/6.10/net.d | 31 +-
dlibs/x86_64/6.10/tcp.d | 67 +++--
libdtrace/Build | 2 +
libdtrace/dt_cg.c | 57 +++-
libdtrace/dt_cg.h | 4 +-
libdtrace/dt_prov_io.c | 84 +++---
libdtrace/dt_prov_ip.c | 45 +--
libdtrace/dt_prov_proc.c | 4 +-
libdtrace/dt_prov_sched.c | 2 +-
libdtrace/dt_prov_tcp.c | 413 ++++++++++++++++++++++++++
libdtrace/dt_prov_uprobe.c | 4 +-
libdtrace/dt_provider.c | 1 +
libdtrace/dt_provider.h | 1 +
libdtrace/ip.d | 27 +-
libdtrace/net.d | 31 +-
libdtrace/tcp.d | 67 +++--
test/unittest/operators/tst.ternary.d | 20 +-
test/unittest/operators/tst.ternary.r | 2 +
test/unittest/tcp/test.x | 8 +-
65 files changed, 1731 insertions(+), 1041 deletions(-)
create mode 100644 libdtrace/dt_prov_tcp.c
--
2.43.5
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v5 1/6] cg: refactor get_member() to use dt_cg_ctf_offsetof
2025-07-22 10:07 [PATCH v5 0/6] DTrace TCP provider Alan Maguire
@ 2025-07-22 10:07 ` Alan Maguire
2025-07-22 10:07 ` [PATCH v5 2/6] test/operators: extend ternary tests to cover inet_ntoa*()s Alan Maguire
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Alan Maguire @ 2025-07-22 10:07 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire, Kris Van Hees
Add an optional ldopp parameter to support retrieving load size.
and rename get_member() to dt_cg_tramp_get_member() as it is
intended for trampoline context.
It will be used by both dt_prov_ip.c and dt_prov_tcp.c.
Co-developed-by: Kris Van Hees <kris.van.hees@oracle.com>
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
libdtrace/dt_cg.c | 57 ++++++++++++++++++++++----
libdtrace/dt_cg.h | 4 +-
libdtrace/dt_prov_io.c | 84 +++++++++++++++++++-------------------
libdtrace/dt_prov_ip.c | 45 ++------------------
libdtrace/dt_prov_proc.c | 4 +-
libdtrace/dt_prov_sched.c | 2 +-
libdtrace/dt_prov_uprobe.c | 4 +-
7 files changed, 102 insertions(+), 98 deletions(-)
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 0607cd4e..80d8c8e3 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -1893,7 +1893,7 @@ dt_cg_clsflags(dt_pcb_t *pcb, dtrace_actkind_t kind, const dt_node_t *dnp)
*/
int
dt_cg_ctf_offsetof(const char *structname, const char *membername,
- size_t *sizep, int relaxed)
+ size_t *sizep, uint_t *ldopp, int relaxed)
{
dtrace_typeinfo_t sym;
ctf_file_t *ctfp;
@@ -1913,12 +1913,51 @@ dt_cg_ctf_offsetof(const char *structname, const char *membername,
longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
}
- if (sizep)
- *sizep = ctf_type_size(ctfp, ctm.ctm_type);
+ if (sizep || ldopp) {
+ uint_t ldop;
+
+ ldop = dt_cg_ldsize(NULL, ctfp, ctm.ctm_type, sizep);
+ if (ldopp)
+ *ldopp = ldop;
+ }
return (ctm.ctm_offset / NBBY);
}
+/*
+ * Retrieve the value of a member in a given struct.
+ *
+ * Entry:
+ * reg = TYPE *ptr
+ *
+ * Return:
+ * %r0 = ptr->member
+ * Clobbers:
+ * %r1 .. %r5
+ */
+void
+dt_cg_tramp_get_member(dt_pcb_t *pcb, const char *name, int reg,
+ const char *member)
+{
+ dtrace_hdl_t *dtp = pcb->pcb_hdl;
+ dt_irlist_t *dlp = &pcb->pcb_ir;
+ int offset;
+ size_t size;
+ uint_t ldop;
+
+ offset = dt_cg_ctf_offsetof(name, member, &size, &ldop, 0);
+ if (offset == -1)
+ return;
+
+ emit(dlp, BPF_MOV_REG(BPF_REG_3, reg));
+ emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, offset));
+ emit(dlp, BPF_MOV_IMM(BPF_REG_2, size));
+ emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_FP));
+ emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_TRAMP_SP_BASE));
+ emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
+ emit(dlp, BPF_LOAD(ldop, BPF_REG_0, BPF_REG_FP, DT_TRAMP_SP_BASE));
+}
+
static void
dt_cg_act_breakpoint(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
{
@@ -2210,7 +2249,7 @@ dt_cg_act_pcap(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
*/
dt_cg_node(addr, dlp, drp);
- off = dt_cg_ctf_offsetof("struct sk_buff", "len", NULL, 0);
+ off = dt_cg_ctf_offsetof("struct sk_buff", "len", NULL, NULL, 0);
if (dt_regset_xalloc_args(drp) == -1)
longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
@@ -2230,7 +2269,7 @@ dt_cg_act_pcap(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
emit(dlp, BPF_LOAD(BPF_DW, lenreg, BPF_REG_FP, DT_STK_SP));
emit(dlp, BPF_LOAD(BPF_W, lenreg, lenreg, 0));
- off = dt_cg_ctf_offsetof("struct sk_buff", "data_len", NULL, 0);
+ off = dt_cg_ctf_offsetof("struct sk_buff", "data_len", NULL, NULL, 0);
if (dt_regset_xalloc_args(drp) == -1)
longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
@@ -2253,7 +2292,7 @@ dt_cg_act_pcap(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
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, 0);
+ off = dt_cg_ctf_offsetof("struct sk_buff", "data", NULL, NULL, 0);
if (dt_regset_xalloc_args(drp) == -1)
longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
@@ -4977,11 +5016,11 @@ dt_cg_uregs(unsigned int idx, dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp
char *memnames[] = { "ds", "es", "fsbase", "gsbase", "trap_nr" };
/* Look up task->thread offset. */
- offset = dt_cg_ctf_offsetof("struct task_struct", "thread", NULL, 0);
+ offset = dt_cg_ctf_offsetof("struct task_struct", "thread", NULL, NULL, 0);
/* Add the thread->member offset. */
offset += dt_cg_ctf_offsetof("struct thread_struct",
- memnames[idx - 21], &size, 0);
+ memnames[idx - 21], &size, NULL, 0);
/* Get task. */
if (dt_regset_xalloc_args(drp) == -1)
@@ -5051,7 +5090,7 @@ dt_cg_uregs(unsigned int idx, dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp
/* Copy contents at task->stack to %fp+DT_STK_SP (scratch space). */
emit(dlp, BPF_MOV_REG(BPF_REG_3, BPF_REG_0));
emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3,
- dt_cg_ctf_offsetof("struct task_struct", "stack", NULL, 0)));
+ dt_cg_ctf_offsetof("struct task_struct", "stack", NULL, NULL, 0)));
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));
emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
diff --git a/libdtrace/dt_cg.h b/libdtrace/dt_cg.h
index 81b79399..3d8d6a9f 100644
--- a/libdtrace/dt_cg.h
+++ b/libdtrace/dt_cg.h
@@ -43,7 +43,9 @@ extern void dt_cg_tramp_epilogue(dt_pcb_t *pcb);
extern void dt_cg_tramp_epilogue_advance(dt_pcb_t *pcb, dt_activity_t act);
extern void dt_cg_tramp_error(dt_pcb_t *pcb);
extern int dt_cg_ctf_offsetof(const char *structname, const char *membername,
- size_t *sizep, int relaxed);
+ size_t *sizep, uint_t *ldopp, int relaxed);
+extern void dt_cg_tramp_get_member(dt_pcb_t *pcb, const char *name, int reg,
+ const char *member);
extern uint_t dt_cg_ldsize(dt_node_t *dnp, ctf_file_t *ctfp, ctf_id_t type,
ssize_t *ret_size);
extern uint_t bpf_ldst_size(ssize_t size, int store);
diff --git a/libdtrace/dt_prov_io.c b/libdtrace/dt_prov_io.c
index 823904d7..fc45870c 100644
--- a/libdtrace/dt_prov_io.c
+++ b/libdtrace/dt_prov_io.c
@@ -181,7 +181,7 @@ static void io_nfs_args_v1(dt_pcb_t *pcb, dt_irlist_t *dlp, uint_t exitlbl,
dt_cg_tramp_get_var(pcb, "this->-io-bio", 1, BPF_REG_6);
/* Fill in bi_opf */
- off = dt_cg_ctf_offsetof("struct bio", "bi_opf", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_opf", &siz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
if (strstr(uprb, "read"))
emit(dlp, BPF_STORE_IMM(siz, BPF_REG_6, off, REQ_OP_READ));
@@ -198,13 +198,13 @@ static void io_nfs_args_v1(dt_pcb_t *pcb, dt_irlist_t *dlp, uint_t exitlbl,
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_7, DMST_ARG(2)));
} else {
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_7, DMST_ARG(1)));
- off = dt_cg_ctf_offsetof("struct nfs_pgio_header", "res", NULL, 0)
- + dt_cg_ctf_offsetof("struct nfs_pgio_res", "count", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct nfs_pgio_header", "res", NULL, NULL, 0)
+ + dt_cg_ctf_offsetof("struct nfs_pgio_res", "count", &siz, NULL, 0);
deref_r3(dlp, exitlbl, off, siz, BPF_REG_0);
}
- off = dt_cg_ctf_offsetof("struct bio", "bi_iter", NULL, 0)
- + dt_cg_ctf_offsetof("struct bvec_iter", "bi_size", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_iter", NULL, NULL, 0)
+ + dt_cg_ctf_offsetof("struct bvec_iter", "bi_size", &siz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
emit(dlp, BPF_STORE(siz, BPF_REG_6, off, BPF_REG_0));
@@ -218,25 +218,25 @@ static void io_nfs_args_v1(dt_pcb_t *pcb, dt_irlist_t *dlp, uint_t exitlbl,
/* use hdr->inode, hdr is arg1 */
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_7, DMST_ARG(1)));
- off = dt_cg_ctf_offsetof("struct nfs_pgio_header", "inode", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct nfs_pgio_header", "inode", &siz, NULL, 0);
deref_r3(dlp, exitlbl, off, siz, BPF_REG_3);
}
- off = dt_cg_ctf_offsetof("struct nfs_inode", "fileid", &siz, 0)
- - dt_cg_ctf_offsetof("struct nfs_inode", "vfs_inode", NULL, 0);
+ off = dt_cg_ctf_offsetof("struct nfs_inode", "fileid", &siz, NULL, 0)
+ - dt_cg_ctf_offsetof("struct nfs_inode", "vfs_inode", NULL, NULL, 0);
deref_r3(dlp, exitlbl, off, siz, BPF_REG_0);
- off = dt_cg_ctf_offsetof("struct bio", "bi_iter", NULL, 0)
- + dt_cg_ctf_offsetof("struct bvec_iter", "bi_sector", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_iter", NULL, NULL, 0)
+ + dt_cg_ctf_offsetof("struct bvec_iter", "bi_sector", &siz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
emit(dlp, BPF_STORE(siz, BPF_REG_6, off, BPF_REG_0));
/*
* bio.bi_bdev = 0;
*/
- off = dt_cg_ctf_offsetof("struct bio", "bi_bdev", &siz, 1);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_bdev", &siz, NULL, 1);
if (off == -1)
- off = dt_cg_ctf_offsetof("struct bio", "bi_disk", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_disk", &siz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
emit(dlp, BPF_STORE_IMM(siz, BPF_REG_6, off, 0));
@@ -278,7 +278,7 @@ static void io_nfs_args_v2(dt_pcb_t *pcb, dt_irlist_t *dlp, uint_t exitlbl,
dt_cg_tramp_get_var(pcb, "this->-io-bio", 1, BPF_REG_6);
/* Fill in bi_opf */
- off = dt_cg_ctf_offsetof("struct bio", "bi_opf", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_opf", &siz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
if (strstr(uprb, "read"))
emit(dlp, BPF_STORE_IMM(siz, BPF_REG_6, off, REQ_OP_READ));
@@ -294,16 +294,16 @@ static void io_nfs_args_v2(dt_pcb_t *pcb, dt_irlist_t *dlp, uint_t exitlbl,
*/
if (strcmp(prb, "start") == 0) {
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_7, DMST_ARG(0)));
- off = dt_cg_ctf_offsetof("struct nfs_pgio_header", "args", NULL, 0)
- + dt_cg_ctf_offsetof("struct nfs_pgio_args", "count", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct nfs_pgio_header", "args", NULL, NULL, 0)
+ + dt_cg_ctf_offsetof("struct nfs_pgio_args", "count", &siz, NULL, 0);
} else {
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_7, DMST_ARG(1)));
- off = dt_cg_ctf_offsetof("struct nfs_pgio_header", "res", NULL, 0)
- + dt_cg_ctf_offsetof("struct nfs_pgio_res", "count", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct nfs_pgio_header", "res", NULL, NULL, 0)
+ + dt_cg_ctf_offsetof("struct nfs_pgio_res", "count", &siz, NULL, 0);
}
deref_r3(dlp, exitlbl, off, siz, BPF_REG_0);
- off = dt_cg_ctf_offsetof("struct bio", "bi_iter", NULL, 0)
- + dt_cg_ctf_offsetof("struct bvec_iter", "bi_size", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_iter", NULL, NULL, 0)
+ + dt_cg_ctf_offsetof("struct bvec_iter", "bi_size", &siz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
emit(dlp, BPF_STORE(siz, BPF_REG_6, off, BPF_REG_0));
@@ -316,25 +316,25 @@ static void io_nfs_args_v2(dt_pcb_t *pcb, dt_irlist_t *dlp, uint_t exitlbl,
else
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_7, DMST_ARG(1)));
- off = dt_cg_ctf_offsetof("struct nfs_pgio_header", "inode", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct nfs_pgio_header", "inode", &siz, NULL, 0);
deref_r3(dlp, exitlbl, off, siz, BPF_REG_3);
- off = dt_cg_ctf_offsetof("struct nfs_inode", "fileid", &siz, 0)
- - dt_cg_ctf_offsetof("struct nfs_inode", "vfs_inode", NULL, 0);
+ off = dt_cg_ctf_offsetof("struct nfs_inode", "fileid", &siz, NULL, 0)
+ - dt_cg_ctf_offsetof("struct nfs_inode", "vfs_inode", NULL, NULL, 0);
deref_r3(dlp, exitlbl, off, siz, BPF_REG_0);
- off = dt_cg_ctf_offsetof("struct bio", "bi_iter", NULL, 0)
- + dt_cg_ctf_offsetof("struct bvec_iter", "bi_sector", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_iter", NULL, NULL, 0)
+ + dt_cg_ctf_offsetof("struct bvec_iter", "bi_sector", &siz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
emit(dlp, BPF_STORE(siz, BPF_REG_6, off, BPF_REG_0));
/*
* bio.bi_bdev = 0;
*/
- off = dt_cg_ctf_offsetof("struct bio", "bi_bdev", &siz, 1);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_bdev", &siz, NULL, 1);
if (off == -1)
- off = dt_cg_ctf_offsetof("struct bio", "bi_disk", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_disk", &siz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
emit(dlp, BPF_STORE_IMM(siz, BPF_REG_6, off, 0));
@@ -379,14 +379,14 @@ static void io_xfs_args(dt_pcb_t *pcb, dt_irlist_t *dlp, uint_t exitlbl)
/* bio.bi_opf = (bp->b_flags & XBF_WRITE) ? REQ_OP_WRITE : REQ_OP_READ; */
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_7, DMST_ARG(0)));
- off = dt_cg_ctf_offsetof("struct xfs_buf", "b_flags", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct xfs_buf", "b_flags", &siz, NULL, 0);
deref_r3(dlp, exitlbl, off, siz, BPF_REG_0);
emit(dlp, BPF_ALU64_IMM(BPF_AND, BPF_REG_0, XBF_WRITE));
{
uint_t Lzero = dt_irlist_label(dlp);
uint_t Ldone = dt_irlist_label(dlp);
- off = dt_cg_ctf_offsetof("struct bio", "bi_opf", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_opf", &siz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, BPF_REG_0, 0, Lzero));
@@ -403,10 +403,10 @@ static void io_xfs_args(dt_pcb_t *pcb, dt_irlist_t *dlp, uint_t exitlbl)
* bio.bi_iter.bi_size = bp->b_length;
*/
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_7, DMST_ARG(0)));
- off = dt_cg_ctf_offsetof("struct xfs_buf", "b_length", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct xfs_buf", "b_length", &siz, NULL, 0);
deref_r3(dlp, exitlbl, off, siz, BPF_REG_0);
- off = dt_cg_ctf_offsetof("struct bio", "bi_iter", NULL, 0)
- + dt_cg_ctf_offsetof("struct bvec_iter", "bi_size", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_iter", NULL, NULL, 0)
+ + dt_cg_ctf_offsetof("struct bvec_iter", "bi_size", &siz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
emit(dlp, BPF_STORE(siz, BPF_REG_6, off, BPF_REG_0));
@@ -446,12 +446,12 @@ static void io_xfs_args(dt_pcb_t *pcb, dt_irlist_t *dlp, uint_t exitlbl)
* }
*/
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_7, DMST_ARG(0)));
- off = dt_cg_ctf_offsetof("struct xfs_buf", "b_maps", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct xfs_buf", "b_maps", &siz, NULL, 0);
deref_r3(dlp, exitlbl, off, siz, BPF_REG_3);
- off = dt_cg_ctf_offsetof("struct xfs_buf_map", "bm_bn", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct xfs_buf_map", "bm_bn", &siz, NULL, 0);
deref_r3(dlp, exitlbl, off, siz, BPF_REG_0);
- off = dt_cg_ctf_offsetof("struct bio", "bi_iter", NULL, 0)
- + dt_cg_ctf_offsetof("struct bvec_iter", "bi_sector", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_iter", NULL, NULL, 0)
+ + dt_cg_ctf_offsetof("struct bvec_iter", "bi_sector", &siz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
emit(dlp, BPF_STORE(siz, BPF_REG_6, off, BPF_REG_0));
@@ -459,24 +459,24 @@ static void io_xfs_args(dt_pcb_t *pcb, dt_irlist_t *dlp, uint_t exitlbl)
* bio.bi_bdev = (bp)->b_target->bt_bdev
*/
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_7, DMST_ARG(0)));
- off = dt_cg_ctf_offsetof("struct xfs_buf", "b_target", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct xfs_buf", "b_target", &siz, NULL, 0);
assert(siz == sizeof(void *));
deref_r3(dlp, exitlbl, off, 8, BPF_REG_3);
- off = dt_cg_ctf_offsetof("struct xfs_buftarg", "bt_bdev", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct xfs_buftarg", "bt_bdev", &siz, NULL, 0);
deref_r3(dlp, exitlbl, off, siz, BPF_REG_3);
- off = dt_cg_ctf_offsetof("struct bio", "bi_bdev", &siz, 1);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_bdev", &siz, NULL, 1);
if (off == -1)
- off = dt_cg_ctf_offsetof("struct bio", "bi_disk", &siz, 0);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_disk", &siz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
emit(dlp, BPF_STORE(siz, BPF_REG_6, off, BPF_REG_0));
/* Populate bi_partno if it exists. */
- off = dt_cg_ctf_offsetof("struct bio", "bi_partno", &siz, 1);
+ off = dt_cg_ctf_offsetof("struct bio", "bi_partno", &siz, NULL, 1);
if (off >= 0) {
int poff;
size_t psiz;
- poff = dt_cg_ctf_offsetof("struct block_device", "bd_partno", &psiz, 0);
+ poff = dt_cg_ctf_offsetof("struct block_device", "bd_partno", &psiz, NULL, 0);
siz = bpf_ldst_size(siz, 1);
deref_r3(dlp, exitlbl, poff, psiz, BPF_REG_0);
emit(dlp, BPF_STORE(siz, BPF_REG_6, off, BPF_REG_0));
@@ -542,7 +542,7 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl)
size_t size;
size_t off;
- off = dt_cg_ctf_offsetof("struct request", "bio", &size, 0);
+ off = dt_cg_ctf_offsetof("struct request", "bio", &size, NULL, 0);
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_7, DMST_ARG(0)));
deref_r3(dlp, exitlbl, off, size, BPF_REG_0);
diff --git a/libdtrace/dt_prov_ip.c b/libdtrace/dt_prov_ip.c
index c4a3a6e2..922cfd8d 100644
--- a/libdtrace/dt_prov_ip.c
+++ b/libdtrace/dt_prov_ip.c
@@ -62,43 +62,6 @@ static int populate(dtrace_hdl_t *dtp)
probe_args, probes);
}
-/*
- * Retrieve the value of a member in a given struct.
- *
- * Entry:
- * reg = TYPE *ptr
- *
- * Return:
- * %r0 = ptr->member
- * Clobbers:
- * %r1 .. %r5
- */
-static int get_member(dt_pcb_t *pcb, const char *name, int reg,
- const char *member) {
- dtrace_hdl_t *dtp = pcb->pcb_hdl;
- dt_irlist_t *dlp = &pcb->pcb_ir;
- dtrace_typeinfo_t tt;
- ctf_membinfo_t ctm;
- size_t size;
- uint_t ldop;
-
- if (dtrace_lookup_by_type(dtp, DTRACE_OBJ_KMODS, name, &tt) == -1 ||
- ctf_member_info(tt.dtt_ctfp, tt.dtt_type, member, &ctm) == CTF_ERR)
- return -1;
-
- ldop = dt_cg_ldsize(NULL, tt.dtt_ctfp, ctm.ctm_type, &size);
-
- emit(dlp, BPF_MOV_REG(BPF_REG_3, reg));
- emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, ctm.ctm_offset / NBBY));
- emit(dlp, BPF_MOV_IMM(BPF_REG_2, size));
- emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_FP));
- emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_TRAMP_SP_BASE));
- emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
- emit(dlp, BPF_LOAD(ldop, BPF_REG_0, BPF_REG_FP, DT_TRAMP_SP_BASE));
-
- return 0;
-}
-
/*
* Generate a BPF trampoline for a SDT probe.
*
@@ -142,7 +105,7 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl)
emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(0), BPF_REG_6));
- get_member(pcb, "struct sk_buff", BPF_REG_6, "sk");
+ dt_cg_tramp_get_member(pcb, "struct sk_buff", BPF_REG_6, "sk");
emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(1), BPF_REG_0));
/*
@@ -150,11 +113,11 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl)
* skb_network_header(skb) = (include/linux/ip.h)
* skb->head + skb->network_header (include/linux/skbuff.h)
*/
- get_member(pcb, "struct sk_buff", BPF_REG_6, "head");
+ dt_cg_tramp_get_member(pcb, "struct sk_buff", BPF_REG_6, "head");
emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(2), BPF_REG_0));
emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(4), BPF_REG_0));
emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(5), BPF_REG_0));
- get_member(pcb, "struct sk_buff", BPF_REG_6, "network_header");
+ dt_cg_tramp_get_member(pcb, "struct sk_buff", BPF_REG_6, "network_header");
emit(dlp, BPF_XADD_REG(BPF_DW, BPF_REG_7, DMST_ARG(2), BPF_REG_0));
emit(dlp, BPF_XADD_REG(BPF_DW, BPF_REG_7, DMST_ARG(4), BPF_REG_0));
emit(dlp, BPF_XADD_REG(BPF_DW, BPF_REG_7, DMST_ARG(5), BPF_REG_0));
@@ -168,7 +131,7 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl)
else
emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(5), 0));
- get_member(pcb, "struct sk_buff", BPF_REG_6, "dev");
+ dt_cg_tramp_get_member(pcb, "struct sk_buff", BPF_REG_6, "dev");
emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(3), BPF_REG_0));
return 0;
diff --git a/libdtrace/dt_prov_proc.c b/libdtrace/dt_prov_proc.c
index 15fde6c9..cf08ce7f 100644
--- a/libdtrace/dt_prov_proc.c
+++ b/libdtrace/dt_prov_proc.c
@@ -206,7 +206,7 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl)
emit(dlp, BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, SIGKILL, lbl_keep));
emit(dlp, BPF_CALL_HELPER(BPF_FUNC_get_current_task));
- off = dt_cg_ctf_offsetof("struct task_struct", "signal", &sz, 0);
+ off = dt_cg_ctf_offsetof("struct task_struct", "signal", &sz, NULL, 0);
emit(dlp, BPF_MOV_REG(BPF_REG_3, BPF_REG_0));
emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, off));
emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_FP));
@@ -214,7 +214,7 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl)
emit(dlp, BPF_MOV_IMM(BPF_REG_2, sz));
emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read));
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_FP, DT_TRAMP_SP_SLOT(0)));
- off = dt_cg_ctf_offsetof("struct signal_struct", "group_exit_code", &sz, 0);
+ off = dt_cg_ctf_offsetof("struct signal_struct", "group_exit_code", &sz, NULL, 0);
emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, off));
emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_FP));
emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_TRAMP_SP_SLOT(0)));
diff --git a/libdtrace/dt_prov_sched.c b/libdtrace/dt_prov_sched.c
index a548e679..25f745c1 100644
--- a/libdtrace/dt_prov_sched.c
+++ b/libdtrace/dt_prov_sched.c
@@ -101,7 +101,7 @@ static void get_cpuinfo(dtrace_hdl_t *dtp, dt_irlist_t *dlp, uint_t exitlbl)
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_7, DMST_ARG(0)));
/* Turn it into a pointer to its cpu member. */
- emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, dt_cg_ctf_offsetof("struct rq", "cpu", NULL, 1)));
+ emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, dt_cg_ctf_offsetof("struct rq", "cpu", NULL, NULL, 1)));
/* Call bpf_probe_read_kernel(%fp + DT_TRAMP_SP_SLOT[0], sizeof(int), %r3) */
emit(dlp, BPF_MOV_IMM(BPF_REG_2, (int) sizeof(int)));
diff --git a/libdtrace/dt_prov_uprobe.c b/libdtrace/dt_prov_uprobe.c
index cf5cfd43..0b60bf57 100644
--- a/libdtrace/dt_prov_uprobe.c
+++ b/libdtrace/dt_prov_uprobe.c
@@ -250,11 +250,11 @@ get_asm_reg(dt_provider_t *pvp, const char *name)
i++, rp++) {
#if defined(__amd64)
rp->off = dt_cg_ctf_offsetof("struct pt_regs",
- rp->mname, NULL, 0) +
+ rp->mname, NULL, NULL, 0) +
rp->moff;
#elif defined(__aarch64__)
rp->off = dt_cg_ctf_offsetof("struct user_pt_regs",
- rp->mname, NULL, 0) +
+ rp->mname, NULL, NULL, 0) +
rp->moff;
#endif
--
2.43.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 2/6] test/operators: extend ternary tests to cover inet_ntoa*()s
2025-07-22 10:07 [PATCH v5 0/6] DTrace TCP provider Alan Maguire
2025-07-22 10:07 ` [PATCH v5 1/6] cg: refactor get_member() to use dt_cg_ctf_offsetof Alan Maguire
@ 2025-07-22 10:07 ` Alan Maguire
2025-07-22 10:07 ` [PATCH v5 3/6] providers: move network-generic definitions to net.d Alan Maguire
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Alan Maguire @ 2025-07-22 10:07 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire, Eugene Loh, Kris Van Hees
inet_ntoa*() require temporary strings and ternary operators
need temporaries for left and right; ensure ternary ops succeed
with inet_ntoa*()s.
Suggested-by: Eugene Loh <eugene.loh@oracle.com>
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
---
test/unittest/operators/tst.ternary.d | 20 +++++++++++++++++++-
test/unittest/operators/tst.ternary.r | 2 ++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/test/unittest/operators/tst.ternary.d b/test/unittest/operators/tst.ternary.d
index 9d47fcc2..aeced7ac 100644
--- a/test/unittest/operators/tst.ternary.d
+++ b/test/unittest/operators/tst.ternary.d
@@ -8,7 +8,10 @@
/*
* ASSERTION:
* Test the ternary operator. Test left-hand side true, right-hand side true,
- * and multiple nested instances of the ternary operator.
+ * and multiple nested instances of the ternary operator. Since inet_ntoa*()
+ * relies on temporary strings, and ternary operators need both left- and
+ * right-side temporaries, test ternary inet_ntoa*() also.
+ *
*
* SECTION: Types, Operators, and Expressions/Conditional Expressions
*/
@@ -18,10 +21,25 @@
BEGIN
{
x = 0;
+ y = 1;
printf("x is %s\n", x == 0 ? "zero" : "one");
x = 1;
printf("x is %s\n", x == 0 ? "zero" : "one");
x = 2;
printf("x is %s\n", x == 0 ? "zero" : x == 1 ? "one" : "two");
+ ipaddr = (ipaddr_t *)alloca(sizeof(ipaddr_t));
+ ipaddr2 = (in6_addr_t *)alloca(sizeof(in6_addr_t));
+ ipaddr2->in6_u.u6_addr32[0] = 0xffffffff;
+ ipaddr2->in6_u.u6_addr32[1] = 0xffffffff;
+ ipaddr2->in6_u.u6_addr32[2] = 0xffffffff;
+ ipaddr2->in6_u.u6_addr32[3] = 0xffffffff;
+ printf("ipaddr is %s\n", x > 1 ? inet_ntoa(ipaddr) :
+ x > 2 ? inet_ntoa(ipaddr) :
+ x > 3 ? inet_ntoa(ipaddr) :
+ inet_ntoa(ipaddr));
+ printf("ipaddr2 is %s\n", x > 1 ? inet_ntoa6(ipaddr2) :
+ x > 2 ? inet_ntoa6(ipaddr2) :
+ x > 3 ? inet_ntoa6(ipaddr2) :
+ inet_ntoa6(ipaddr2));
exit(0);
}
diff --git a/test/unittest/operators/tst.ternary.r b/test/unittest/operators/tst.ternary.r
index ec30800e..72201682 100644
--- a/test/unittest/operators/tst.ternary.r
+++ b/test/unittest/operators/tst.ternary.r
@@ -1,4 +1,6 @@
x is zero
x is one
x is two
+ipaddr is 0.0.0.0
+ipaddr2 is ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
--
2.43.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 3/6] providers: move network-generic definitions to net.d
2025-07-22 10:07 [PATCH v5 0/6] DTrace TCP provider Alan Maguire
2025-07-22 10:07 ` [PATCH v5 1/6] cg: refactor get_member() to use dt_cg_ctf_offsetof Alan Maguire
2025-07-22 10:07 ` [PATCH v5 2/6] test/operators: extend ternary tests to cover inet_ntoa*()s Alan Maguire
@ 2025-07-22 10:07 ` Alan Maguire
2025-07-22 10:07 ` [PATCH v5 4/6] tcp: new provider Alan Maguire
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Alan Maguire @ 2025-07-22 10:07 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire, Kris Van Hees
tcp.d and ip.d both need some of these generic definitions so move them
to the net.d library they both depend on.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
---
libdtrace/ip.d | 25 -------------------------
libdtrace/net.d | 25 +++++++++++++++++++++++++
2 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/libdtrace/ip.d b/libdtrace/ip.d
index f8b77f12..b498bc07 100644
--- a/libdtrace/ip.d
+++ b/libdtrace/ip.d
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/libdtrace/net.d b/libdtrace/net.d
index 6ac34287..4c7bc61f 100644
--- a/libdtrace/net.d
+++ b/libdtrace/net.d
@@ -25,6 +25,31 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
* with sending (outbound) or receiving (inbound).
--
2.43.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 4/6] tcp: new provider
2025-07-22 10:07 [PATCH v5 0/6] DTrace TCP provider Alan Maguire
` (2 preceding siblings ...)
2025-07-22 10:07 ` [PATCH v5 3/6] providers: move network-generic definitions to net.d Alan Maguire
@ 2025-07-22 10:07 ` Alan Maguire
2025-07-22 10:07 ` [PATCH v5 5/6] dlibs: sync ip.d, net.d and tcp.d Alan Maguire
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Alan Maguire @ 2025-07-22 10:07 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire
Based upon various fbt probe points support TCP send, receive,
state-change, accept-established, accept-refused, connect-request,
connect-established and connect-refused probes.
A few tweaks were needed to tcp.d to support the probes fully.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
libdtrace/Build | 2 +
libdtrace/dt_prov_tcp.c | 413 ++++++++++++++++++++++++++++++++++++++++
libdtrace/dt_provider.c | 1 +
libdtrace/dt_provider.h | 1 +
libdtrace/ip.d | 2 +-
libdtrace/net.d | 6 +-
libdtrace/tcp.d | 67 ++++---
7 files changed, 462 insertions(+), 30 deletions(-)
create mode 100644 libdtrace/dt_prov_tcp.c
diff --git a/libdtrace/Build b/libdtrace/Build
index 219ff9b3..b0862ffa 100644
--- a/libdtrace/Build
+++ b/libdtrace/Build
@@ -58,6 +58,7 @@ libdtrace-build_SOURCES = dt_aggregate.c \
dt_prov_sched.c \
dt_prov_sdt.c \
dt_prov_syscall.c \
+ dt_prov_tcp.c \
dt_prov_uprobe.c \
dt_provider.c \
dt_provider_sdt.c \
@@ -116,6 +117,7 @@ dt_prov_rawtp.c_CFLAGS := -Wno-pedantic
dt_prov_sched.c_CFLAGS := -Wno-pedantic
dt_prov_sdt.c_CFLAGS := -Wno-pedantic
dt_prov_syscall.c_CFLAGS := -Wno-pedantic
+dt_prov_tcp.c_CFLAGS := -Wno-pedantic
dt_prov_uprobe.c_CFLAGS := -Wno-pedantic
dt_debug.c_CFLAGS := -Wno-prio-ctor-dtor
diff --git a/libdtrace/dt_prov_tcp.c b/libdtrace/dt_prov_tcp.c
new file mode 100644
index 00000000..6a8b956d
--- /dev/null
+++ b/libdtrace/dt_prov_tcp.c
@@ -0,0 +1,413 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2025, 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.
+ *
+ * The 'tcp' SDT provider for DTrace-specific probes.
+ */
+#include <assert.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#include "dt_dctx.h"
+#include "dt_cg.h"
+#include "dt_provider_sdt.h"
+#include "dt_probe.h"
+
+static const char prvname[] = "tcp";
+static const char modname[] = "vmlinux";
+
+enum {
+ NET_PROBE_OUTBOUND = 0,
+ NET_PROBE_INBOUND,
+ NET_PROBE_STATE
+};
+
+static probe_dep_t probes[] = {
+ /* does not fire on UEK7 unless rawfbt; no idea why... */
+ { "accept-established",
+ DTRACE_PROBESPEC_NAME, "rawfbt::tcp_init_transfer:entry" },
+ { "accept-refused",
+ DTRACE_PROBESPEC_NAME, "fbt::tcp_v4_send_reset:entry" },
+ { "accept-refused",
+ DTRACE_PROBESPEC_NAME, "fbt::tcp_v6_send_reset:entry" },
+ { "connect-established",
+ DTRACE_PROBESPEC_NAME, "fbt::tcp_finish_connect:entry" },
+ { "connect-refused",
+ DTRACE_PROBESPEC_NAME, "fbt::tcp_reset:entry" },
+ { "connect-request",
+ DTRACE_PROBESPEC_NAME, "fbt::ip_queue_xmit:entry" },
+ /* ip6_xmit has > 6 args so cannot fentry on aarch64; use rawfbt */
+ { "connect-request",
+ DTRACE_PROBESPEC_NAME, "rawfbt::ip6_xmit:entry" },
+ { "receive",
+ DTRACE_PROBESPEC_NAME, "fbt::tcp_rcv_established:entry" },
+ { "receive",
+ DTRACE_PROBESPEC_NAME, "fbt::tcp_rcv_state_process:entry" },
+ { "receive",
+ DTRACE_PROBESPEC_NAME, "fbt::tcp_v4_send_reset:entry" },
+ { "send",
+ DTRACE_PROBESPEC_NAME, "fbt::ip_queue_xmit:entry" },
+ /* ip_send_unicast_reply has 10 args so cannot fentry; use rawfbt */
+ { "send",
+ DTRACE_PROBESPEC_NAME, "rawfbt::ip_send_unicast_reply:entry" },
+ { "send",
+ DTRACE_PROBESPEC_NAME, "fbt::ip_build_and_send_pkt" },
+ /* ip6_xmit has > 6 args so cannot fentry on aarch64; use rawfbt */
+ { "send",
+ DTRACE_PROBESPEC_NAME, "rawfbt::ip6_xmit:entry" },
+ { "state-change",
+ DTRACE_PROBESPEC_NAME, "sdt:::inet_sock_set_state" },
+ { "state-change",
+ DTRACE_PROBESPEC_NAME, "fbt::tcp_time_wait:entry" },
+ { "state-change",
+ DTRACE_PROBESPEC_NAME, "fbt::inet_csk_clone_lock:entry" },
+ { NULL, }
+};
+
+static probe_arg_t probe_args[] = {
+ { "accept-established", 0, { 0, 0, "struct sk_buff *", "pktinfo_t *" } },
+ { "accept-established", 1, { 1, 0, "struct sock *", "csinfo_t *" } },
+ { "accept-established", 2, { 2, 0, "void_ip_t *", "ipinfo_t *" } },
+ { "accept-established", 3, { 3, 0, "struct tcp_sock *", "tcpsinfo_t *" } },
+ { "accept-established", 4, { 4, 0, "struct tcphdr *", "tcpinfo_t *" } },
+ { "accept-established", 5, { 5, 0, "void", "void" } },
+
+ { "accept-refused", 0, { 0, 0, "struct sk_buff *", "pktinfo_t *" } },
+ { "accept-refused", 1, { 1, 0, "struct sock *", "csinfo_t *" } },
+ { "accept-refused", 2, { 2, 0, "void_ip_t *", "ipinfo_t *" } },
+ { "accept-refused", 3, { 3, 0, "struct tcp_sock *", "tcpsinfo_t *" } },
+ { "accept-refused", 4, { 4, 0, "struct tcphdr *", "tcpinfo_t *" } },
+ { "accept-refused", 5, { 5, 0, "void", "void"} },
+
+ { "connect-established", 0, { 0, 0, "struct sk_buff *", "pktinfo_t *" } },
+ { "connect-established", 1, { 1, 0, "struct sock *", "csinfo_t *" } },
+ { "connect-established", 2, { 2, 0, "void_ip_t *", "ipinfo_t *" } },
+ { "connect-established", 3, { 3, 0, "struct tcp_sock *", "tcpsinfo_t *" } },
+ { "connect-established", 4, { 4, 0, "struct tcphdr *", "tcpinfo_t *" } },
+ { "connect-established", 5, { 5, 0, "void", "void"} },
+
+ { "connect-refused", 0, { 0, 0, "struct sk_buff *", "pktinfo_t *" } },
+ { "connect-refused", 1, { 1, 0, "struct sock *", "csinfo_t *" } },
+ { "connect-refused", 2, { 2, 0, "void_ip_t *", "ipinfo_t *" } },
+ { "connect-refused", 3, { 3, 0, "struct tcp_sock *", "tcpsinfo_t *" } },
+ { "connect-refused", 4, { 4, 0, "struct tcphdr *", "tcpinfo_t *" } },
+ { "connect-refused", 5, { 5, 0, "void", "void"} },
+
+ { "connect-request", 0, { 0, 0, "struct sk_buff *", "pktinfo_t *" } },
+ { "connect-request", 1, { 1, 0, "struct sock *", "csinfo_t *" } },
+ { "connect-request", 2, { 2, 0, "__dtrace_tcp_void_ip_t *", "ipinfo_t *" } },
+ { "connect-request", 3, { 3, 0, "struct tcp_sock *", "tcpsinfo_t *" } },
+ { "connect-request", 4, { 4, 0, "struct tcphdr *", "tcpinfo_t *" } },
+ { "connect-request", 5, { 5, 0, "void", "void"} },
+
+ { "receive", 0, { 0, 0, "struct sk_buff *", "pktinfo_t *" } },
+ { "receive", 1, { 1, 0, "struct sock *", "csinfo_t *" } },
+ { "receive", 2, { 2, 0, "void_ip_t *", "ipinfo_t *" } },
+ { "receive", 3, { 3, 0, "struct tcp_sock *", "tcpsinfo_t *" } },
+ { "receive", 4, { 4, 0, "struct tcphdr *", "tcpinfo_t *" } },
+ { "receive", 5, { 5, 0, "void", "void"} },
+
+ { "send", 0, { 0, 0, "struct sk_buff *", "pktinfo_t *" } },
+ { "send", 1, { 1, 0, "struct sock *", "csinfo_t *" } },
+ { "send", 2, { 2, 0, "__dtrace_tcp_void_ip_t *", "ipinfo_t *" } },
+ { "send", 3, { 3, 0, "struct tcp_sock *", "tcpsinfo_t *" } },
+ { "send", 4, { 4, 0, "struct tcphdr *", "tcpinfo_t *" } },
+ { "send", 5, { 5, 0, "void", "void"} },
+
+ { "state-change", 0, { 0, 0, "void", "void", } },
+ { "state-change", 1, { 1, 0, "struct sock *", "csinfo_t *" } },
+ { "state-change", 2, { 2, 0, "void", "void" } },
+ { "state-change", 3, { 3, 0, "struct tcp_sock *", "tcpsinfo_t *" } },
+ { "state-change", 4, { 4, 0, "void", "void" } },
+ { "state-change", 5, { 5, 0, "int", "tcplsinfo_t *" } },
+
+ { NULL, }
+};
+
+static const dtrace_pattr_t pattr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+};
+
+/*
+ * Provide all the "tcp" SDT probes.
+ */
+static int populate(dtrace_hdl_t *dtp)
+{
+ return dt_sdt_populate(dtp, prvname, modname, &dt_tcp, &pattr,
+ probe_args, probes);
+}
+
+/*
+ * Generate a BPF trampoline for a SDT probe.
+ *
+ * The trampoline function is called when a SDT probe triggers, and it must
+ * satisfy the following prototype:
+ *
+ * int dt_tcp(void *data)
+ *
+ * The trampoline will populate a dt_dctx_t struct and then call the function
+ * that implements the compiled D clause. It returns the value that it gets
+ * back from that function.
+ */
+static int trampoline(dt_pcb_t *pcb, uint_t exitlbl)
+{
+ dtrace_hdl_t *dtp = pcb->pcb_hdl;
+ dt_irlist_t *dlp = &pcb->pcb_ir;
+ dt_probe_t *prp = pcb->pcb_probe;
+ dt_probe_t *uprp = pcb->pcb_parent_probe;
+ int direction, have_iphdr;
+ int skarg = 0, skbarg = 1, tcparg = 0;
+ int skarg_maybe_null;
+ int skstate = 0;
+
+ /*
+ * We construct the tcp::: probe arguments as follows:
+ * arg0 = skb
+ * arg1 = sk
+ * arg2 = ip_hdr(skb) [if available]
+ * arg3 = sk [struct tcp_sock *]
+ * arg4 = tcp_hdr(skb)
+ * arg5 = new_sk_state [for state_change]
+ * arg6 = NET_PROBE_INBOUND (0x1) | NET_PROBE_OUTBOUND (0x0) |
+ * NET_PROBE_STATE (0x2)
+ * arg6 never makes it into supported args[], it is simply set to
+ * help inform translators about whether it is an inbound, outbound or
+ * state transition probe.
+ */
+
+ if (strcmp(prp->desc->prb, "state-change") == 0) {
+ int newstatearg;
+ int skip_state = 0;
+ int check_proto = IPPROTO_TCP;
+
+ /* For pre-6.14 kernels, inet_sock_state_change() to
+ * TCP_SYN_RCV is broken in that the cloned socket has
+ * not yet copied info of interest like addresses, ports.
+ * This is fixed in 6.14 via
+ *
+ * commit a3a128f611a965fddf8a02dd45716f96e0738e00
+ * Author: Eric Dumazet <edumazet@google.com>
+ * Date: Wed Feb 12 13:13:28 2025 +0000
+ *
+ * inet: consolidate inet_csk_clone_lock()
+ *
+ * To work around this we trace inet_csk_clone_lock and
+ * use the reqsk (arg1) as the means to populate the
+ * struct tcpinfo. We need then to explictly set the
+ * state to TCP_SYN_RCV and also skip the case where
+ * inet_sock_set_state() specifies TCP_SYN_RCV otherwise
+ * we will get a probe double-firing. So we set skip_state
+ * to that state to avoid that double-firing.
+ */
+ if (strcmp(uprp->desc->fun, "inet_csk_clone_lock") == 0) {
+ skarg = 1;
+ newstatearg = 2;
+ check_proto = 0;
+ emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(2),
+ BPF_TCP_SYN_RECV));
+ } else if (strcmp(uprp->desc->fun, "tcp_time_wait") == 0) {
+ skarg = 0;
+ newstatearg = 1;
+ } else {
+ skarg = 0;
+ newstatearg = 2;
+ skip_state = BPF_TCP_SYN_RECV;
+ }
+ emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(skarg)));
+ emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, BPF_REG_6, 0, exitlbl));
+ /* check it is a TCP socket */
+ if (check_proto) {
+ dt_cg_tramp_get_member(pcb, "struct sock", BPF_REG_6,
+ "sk_protocol");
+ emit(dlp, BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0,
+ IPPROTO_TCP, exitlbl));
+ }
+ /* save sk */
+ emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(3), BPF_REG_6));
+
+ /* save new state */
+ emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(newstatearg)));
+ if (skip_state) {
+ emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, BPF_REG_6, skip_state,
+ exitlbl));
+ }
+ emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(5), BPF_REG_6));
+
+ /* save sk */
+ emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(3)));
+ emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(1), BPF_REG_6));
+
+ /* save empty args */
+ emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(0), 0));
+ emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(2), 0));
+ emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(4), 0));
+
+ /* NET_PROBE_STATE */
+ emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(6),
+ NET_PROBE_STATE));
+ return 0;
+ }
+
+ if (strcmp(prp->desc->prb, "accept-established") == 0) {
+ direction = NET_PROBE_OUTBOUND;
+ have_iphdr = 1;
+ /* skb in arg2 not arg1 */
+ skbarg = 2;
+ skarg_maybe_null = 0;
+ /* ensure arg1 is BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB */
+ emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(1)));
+ emit(dlp, BPF_BRANCH_IMM(BPF_JNE, BPF_REG_6,
+ BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB,
+ exitlbl));
+ } else if (strcmp(prp->desc->prb, "receive") == 0 ||
+ strcmp(prp->desc->prb, "accept-refused") == 0) {
+ direction = NET_PROBE_INBOUND;
+ have_iphdr = 1;
+ if (strcmp(uprp->desc->fun, "tcp_v4_send_reset") == 0 ||
+ strcmp(uprp->desc->fun, "tcp_v6_send_reset") == 0)
+ skarg_maybe_null = 1;
+ else
+ skarg_maybe_null = 0;
+ } else if (strcmp(prp->desc->prb, "connect-established") == 0) {
+ direction = NET_PROBE_INBOUND;
+ have_iphdr = 1;
+ skarg_maybe_null = 0;
+ } else if (strcmp(prp->desc->prb, "connect-refused") == 0) {
+ direction = NET_PROBE_INBOUND;
+ have_iphdr = 1;
+ skarg_maybe_null = 0;
+ skstate = BPF_TCP_SYN_SENT;
+ } else {
+ direction = NET_PROBE_OUTBOUND;
+ if (strcmp(uprp->desc->fun, "ip_send_unicast_reply") == 0) {
+ dtrace_typeinfo_t sym;
+ ctf_funcinfo_t fi;
+ int rc;
+
+ /* Newer kernels pass the original socket as second
+ * arg to ip_send_unicast_reply(); if that function
+ * has an extra (> 9) argument we know we have to
+ * find sk, skb in arg1, arg2 not arg0, arg1.
+ * tcp header is in ip_reply_arg which is in
+ * arg5/arg6 depending on whether extra parameter
+ * for original sk is present.
+ */
+ rc = dtrace_lookup_by_type(dtp, DTRACE_OBJ_EVERY,
+ uprp->desc->fun, &sym);
+ if (rc == 0 &&
+ ctf_type_kind(sym.dtt_ctfp, sym.dtt_type) == CTF_K_FUNCTION &&
+ ctf_func_type_info(sym.dtt_ctfp, sym.dtt_type, &fi) == 0 &&
+ fi.ctc_argc > 9) {
+ /* NULL sk in arg1 not arg2 (dont want ctl_sk) */
+ skarg = 1;
+ /* skb in arg2 not arg1 */
+ skbarg = 2;
+ tcparg = 6;
+ } else {
+ skarg = 0;
+ skbarg = 1;
+ tcparg = 5;
+ }
+ have_iphdr = 1;
+ tcparg = 6;
+ skarg_maybe_null = 1;
+ } else if (strcmp(uprp->desc->fun, "ip_build_and_send_pkt") == 0) {
+ skarg = 1;
+ skbarg = 0;
+ have_iphdr = 0;
+ skarg_maybe_null = 1;
+ } else if (strcmp(prp->desc->prb, "connect-request") == 0) {
+ skstate = BPF_TCP_SYN_SENT;
+ have_iphdr = 0;
+ skarg_maybe_null = 0;
+ } else {
+ have_iphdr = 0;
+ skarg_maybe_null = 0;
+ }
+ }
+
+ /* first save sk to args[3]; this avoids overwriting it when we
+ * populate args[0,1] below.
+ */
+ emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(skarg)));
+ /* only allow NULL sk for ip_send_unicast_reply() */
+ if (!skarg_maybe_null)
+ emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, BPF_REG_6, 0, exitlbl));
+ emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(3), BPF_REG_6));
+
+ /* then save skb to args[0] */
+ emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(skbarg)));
+ emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, BPF_REG_6, 0, exitlbl));
+ emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(0), BPF_REG_6));
+
+ /* next save sk to args[1] now that we have skb in args[0] */
+ emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(3)));
+ emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(1), BPF_REG_6));
+
+ /*
+ * ip_hdr(skb) =
+ * skb_network_header(skb) = (include/linux/ip.h)
+ * skb->head + skb->network_header (include/linux/skbuff.h)
+ */
+ emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(0)));
+ dt_cg_tramp_get_member(pcb, "struct sk_buff", BPF_REG_6, "head");
+ if (have_iphdr)
+ emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(2), BPF_REG_0));
+ else
+ emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(2), 0));
+
+ if (have_iphdr) {
+ dt_cg_tramp_get_member(pcb, "struct sk_buff", BPF_REG_6,
+ "network_header");
+ emit(dlp, BPF_XADD_REG(BPF_DW, BPF_REG_7, DMST_ARG(2), BPF_REG_0));
+ }
+ /*
+ * tcp_hdr(skb) =
+ * skb_transport_header(skb) = (include/linux/ip.h)
+ * skb->head + skb->transport_header (include/linux/skbuff.h)
+ */
+ emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(tcparg)));
+ if (tcparg) {
+ /* struct ip_reply_arg * has a kvec containing the tcp header */
+ dt_cg_tramp_get_member(pcb, "struct kvec", BPF_REG_6, "iov_base");
+ emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(4), BPF_REG_0));
+ } else {
+ dt_cg_tramp_get_member(pcb, "struct sk_buff", BPF_REG_6, "head");
+ emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(4), BPF_REG_0));
+ dt_cg_tramp_get_member(pcb, "struct sk_buff", BPF_REG_6,
+ "transport_header");
+ emit(dlp, BPF_XADD_REG(BPF_DW, BPF_REG_7, DMST_ARG(4), BPF_REG_0));
+ }
+
+ if (!skarg_maybe_null) {
+ /* save sk state */
+ emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(3)));
+ dt_cg_tramp_get_member(pcb, "struct sock_common", BPF_REG_6,
+ "skc_state");
+ /* ensure sk state - if specified - is what we expect */
+ if (skstate)
+ emit(dlp, BPF_BRANCH_IMM(BPF_JNE, BPF_REG_0, skstate,
+ exitlbl));
+ emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(5), BPF_REG_0));
+ }
+ emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(6), direction));
+
+ return 0;
+}
+
+dt_provimpl_t dt_tcp = {
+ .name = prvname,
+ .prog_type = BPF_PROG_TYPE_UNSPEC,
+ .populate = &populate,
+ .enable = &dt_sdt_enable,
+ .load_prog = &dt_bpf_prog_load,
+ .trampoline = &trampoline,
+ .probe_info = &dt_sdt_probe_info,
+ .destroy = &dt_sdt_destroy,
+};
diff --git a/libdtrace/dt_provider.c b/libdtrace/dt_provider.c
index 0c459aba..b9a7196b 100644
--- a/libdtrace/dt_provider.c
+++ b/libdtrace/dt_provider.c
@@ -40,6 +40,7 @@ const dt_provimpl_t *dt_providers[] = {
&dt_sched,
&dt_sdt,
&dt_syscall,
+ &dt_tcp,
&dt_uprobe,
&dt_usdt,
NULL
diff --git a/libdtrace/dt_provider.h b/libdtrace/dt_provider.h
index a7263f5d..9d60e5ec 100644
--- a/libdtrace/dt_provider.h
+++ b/libdtrace/dt_provider.h
@@ -88,6 +88,7 @@ extern dt_provimpl_t dt_rawtp;
extern dt_provimpl_t dt_sched;
extern dt_provimpl_t dt_sdt;
extern dt_provimpl_t dt_syscall;
+extern dt_provimpl_t dt_tcp;
extern dt_provimpl_t dt_uprobe;
extern dt_provimpl_t dt_usdt;
diff --git a/libdtrace/ip.d b/libdtrace/ip.d
index b498bc07..493b75a0 100644
--- a/libdtrace/ip.d
+++ b/libdtrace/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
diff --git a/libdtrace/net.d b/libdtrace/net.d
index 4c7bc61f..f1291696 100644
--- a/libdtrace/net.d
+++ b/libdtrace/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -52,7 +52,9 @@ typedef struct csinfo {
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/libdtrace/tcp.d b/libdtrace/tcp.d
index 54e310cb..48d9adb4 100644
--- a/libdtrace/tcp.d
+++ b/libdtrace/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
--
2.43.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 5/6] dlibs: sync ip.d, net.d and tcp.d
2025-07-22 10:07 [PATCH v5 0/6] DTrace TCP provider Alan Maguire
` (3 preceding siblings ...)
2025-07-22 10:07 ` [PATCH v5 4/6] tcp: new provider Alan Maguire
@ 2025-07-22 10:07 ` Alan Maguire
2025-07-22 10:07 ` [PATCH v5 6/6] unittest/tcp: update test.x Alan Maguire
2025-07-22 13:53 ` [PATCH v5 0/6] DTrace TCP provider Alan Maguire
6 siblings, 0 replies; 8+ messages in thread
From: Alan Maguire @ 2025-07-22 10:07 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire, Kris Van Hees
Sync dlibs with libdtrace versions.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
---
dlibs/aarch64/5.11/ip.d | 27 +---------------
dlibs/aarch64/5.11/net.d | 31 +++++++++++++++++--
dlibs/aarch64/5.11/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/aarch64/5.12/ip.d | 27 +---------------
dlibs/aarch64/5.12/net.d | 31 +++++++++++++++++--
dlibs/aarch64/5.12/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/aarch64/5.14/ip.d | 27 +---------------
dlibs/aarch64/5.14/net.d | 31 +++++++++++++++++--
dlibs/aarch64/5.14/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/aarch64/5.16/ip.d | 27 +---------------
dlibs/aarch64/5.16/net.d | 31 +++++++++++++++++--
dlibs/aarch64/5.16/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/aarch64/5.2/ip.d | 27 +---------------
dlibs/aarch64/5.2/net.d | 31 +++++++++++++++++--
dlibs/aarch64/5.2/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/aarch64/5.6/ip.d | 27 +---------------
dlibs/aarch64/5.6/net.d | 31 +++++++++++++++++--
dlibs/aarch64/5.6/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/aarch64/6.1/ip.d | 27 +---------------
dlibs/aarch64/6.1/net.d | 31 +++++++++++++++++--
dlibs/aarch64/6.1/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/aarch64/6.10/ip.d | 27 +---------------
dlibs/aarch64/6.10/net.d | 31 +++++++++++++++++--
dlibs/aarch64/6.10/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/x86_64/5.11/ip.d | 27 +---------------
dlibs/x86_64/5.11/net.d | 31 +++++++++++++++++--
dlibs/x86_64/5.11/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/x86_64/5.12/ip.d | 27 +---------------
dlibs/x86_64/5.12/net.d | 31 +++++++++++++++++--
dlibs/x86_64/5.12/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/x86_64/5.14/ip.d | 27 +---------------
dlibs/x86_64/5.14/net.d | 31 +++++++++++++++++--
dlibs/x86_64/5.14/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/x86_64/5.16/ip.d | 27 +---------------
dlibs/x86_64/5.16/net.d | 31 +++++++++++++++++--
dlibs/x86_64/5.16/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/x86_64/5.2/ip.d | 27 +---------------
dlibs/x86_64/5.2/net.d | 31 +++++++++++++++++--
dlibs/x86_64/5.2/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/x86_64/5.6/ip.d | 27 +---------------
dlibs/x86_64/5.6/net.d | 31 +++++++++++++++++--
dlibs/x86_64/5.6/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/x86_64/6.1/ip.d | 27 +---------------
dlibs/x86_64/6.1/net.d | 31 +++++++++++++++++--
dlibs/x86_64/6.1/tcp.d | 67 ++++++++++++++++++++++++----------------
dlibs/x86_64/6.10/ip.d | 27 +---------------
dlibs/x86_64/6.10/net.d | 31 +++++++++++++++++--
dlibs/x86_64/6.10/tcp.d | 67 ++++++++++++++++++++++++----------------
48 files changed, 1120 insertions(+), 880 deletions(-)
diff --git a/dlibs/aarch64/5.11/ip.d b/dlibs/aarch64/5.11/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/aarch64/5.11/ip.d
+++ b/dlibs/aarch64/5.11/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/aarch64/5.11/net.d b/dlibs/aarch64/5.11/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/aarch64/5.11/net.d
+++ b/dlibs/aarch64/5.11/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/aarch64/5.11/tcp.d b/dlibs/aarch64/5.11/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/aarch64/5.11/tcp.d
+++ b/dlibs/aarch64/5.11/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/aarch64/5.12/ip.d b/dlibs/aarch64/5.12/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/aarch64/5.12/ip.d
+++ b/dlibs/aarch64/5.12/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/aarch64/5.12/net.d b/dlibs/aarch64/5.12/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/aarch64/5.12/net.d
+++ b/dlibs/aarch64/5.12/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/aarch64/5.12/tcp.d b/dlibs/aarch64/5.12/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/aarch64/5.12/tcp.d
+++ b/dlibs/aarch64/5.12/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/aarch64/5.14/ip.d b/dlibs/aarch64/5.14/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/aarch64/5.14/ip.d
+++ b/dlibs/aarch64/5.14/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/aarch64/5.14/net.d b/dlibs/aarch64/5.14/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/aarch64/5.14/net.d
+++ b/dlibs/aarch64/5.14/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/aarch64/5.14/tcp.d b/dlibs/aarch64/5.14/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/aarch64/5.14/tcp.d
+++ b/dlibs/aarch64/5.14/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/aarch64/5.16/ip.d b/dlibs/aarch64/5.16/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/aarch64/5.16/ip.d
+++ b/dlibs/aarch64/5.16/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/aarch64/5.16/net.d b/dlibs/aarch64/5.16/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/aarch64/5.16/net.d
+++ b/dlibs/aarch64/5.16/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/aarch64/5.16/tcp.d b/dlibs/aarch64/5.16/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/aarch64/5.16/tcp.d
+++ b/dlibs/aarch64/5.16/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/aarch64/5.2/ip.d b/dlibs/aarch64/5.2/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/aarch64/5.2/ip.d
+++ b/dlibs/aarch64/5.2/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/aarch64/5.2/net.d b/dlibs/aarch64/5.2/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/aarch64/5.2/net.d
+++ b/dlibs/aarch64/5.2/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/aarch64/5.2/tcp.d b/dlibs/aarch64/5.2/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/aarch64/5.2/tcp.d
+++ b/dlibs/aarch64/5.2/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/aarch64/5.6/ip.d b/dlibs/aarch64/5.6/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/aarch64/5.6/ip.d
+++ b/dlibs/aarch64/5.6/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/aarch64/5.6/net.d b/dlibs/aarch64/5.6/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/aarch64/5.6/net.d
+++ b/dlibs/aarch64/5.6/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/aarch64/5.6/tcp.d b/dlibs/aarch64/5.6/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/aarch64/5.6/tcp.d
+++ b/dlibs/aarch64/5.6/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/aarch64/6.1/ip.d b/dlibs/aarch64/6.1/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/aarch64/6.1/ip.d
+++ b/dlibs/aarch64/6.1/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/aarch64/6.1/net.d b/dlibs/aarch64/6.1/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/aarch64/6.1/net.d
+++ b/dlibs/aarch64/6.1/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/aarch64/6.1/tcp.d b/dlibs/aarch64/6.1/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/aarch64/6.1/tcp.d
+++ b/dlibs/aarch64/6.1/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/aarch64/6.10/ip.d b/dlibs/aarch64/6.10/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/aarch64/6.10/ip.d
+++ b/dlibs/aarch64/6.10/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/aarch64/6.10/net.d b/dlibs/aarch64/6.10/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/aarch64/6.10/net.d
+++ b/dlibs/aarch64/6.10/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/aarch64/6.10/tcp.d b/dlibs/aarch64/6.10/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/aarch64/6.10/tcp.d
+++ b/dlibs/aarch64/6.10/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/x86_64/5.11/ip.d b/dlibs/x86_64/5.11/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/x86_64/5.11/ip.d
+++ b/dlibs/x86_64/5.11/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/x86_64/5.11/net.d b/dlibs/x86_64/5.11/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/x86_64/5.11/net.d
+++ b/dlibs/x86_64/5.11/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/x86_64/5.11/tcp.d b/dlibs/x86_64/5.11/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/x86_64/5.11/tcp.d
+++ b/dlibs/x86_64/5.11/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/x86_64/5.12/ip.d b/dlibs/x86_64/5.12/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/x86_64/5.12/ip.d
+++ b/dlibs/x86_64/5.12/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/x86_64/5.12/net.d b/dlibs/x86_64/5.12/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/x86_64/5.12/net.d
+++ b/dlibs/x86_64/5.12/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/x86_64/5.12/tcp.d b/dlibs/x86_64/5.12/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/x86_64/5.12/tcp.d
+++ b/dlibs/x86_64/5.12/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/x86_64/5.14/ip.d b/dlibs/x86_64/5.14/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/x86_64/5.14/ip.d
+++ b/dlibs/x86_64/5.14/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/x86_64/5.14/net.d b/dlibs/x86_64/5.14/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/x86_64/5.14/net.d
+++ b/dlibs/x86_64/5.14/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/x86_64/5.14/tcp.d b/dlibs/x86_64/5.14/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/x86_64/5.14/tcp.d
+++ b/dlibs/x86_64/5.14/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/x86_64/5.16/ip.d b/dlibs/x86_64/5.16/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/x86_64/5.16/ip.d
+++ b/dlibs/x86_64/5.16/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/x86_64/5.16/net.d b/dlibs/x86_64/5.16/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/x86_64/5.16/net.d
+++ b/dlibs/x86_64/5.16/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/x86_64/5.16/tcp.d b/dlibs/x86_64/5.16/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/x86_64/5.16/tcp.d
+++ b/dlibs/x86_64/5.16/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/x86_64/5.2/ip.d b/dlibs/x86_64/5.2/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/x86_64/5.2/ip.d
+++ b/dlibs/x86_64/5.2/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/x86_64/5.2/net.d b/dlibs/x86_64/5.2/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/x86_64/5.2/net.d
+++ b/dlibs/x86_64/5.2/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/x86_64/5.2/tcp.d b/dlibs/x86_64/5.2/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/x86_64/5.2/tcp.d
+++ b/dlibs/x86_64/5.2/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/x86_64/5.6/ip.d b/dlibs/x86_64/5.6/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/x86_64/5.6/ip.d
+++ b/dlibs/x86_64/5.6/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/x86_64/5.6/net.d b/dlibs/x86_64/5.6/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/x86_64/5.6/net.d
+++ b/dlibs/x86_64/5.6/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/x86_64/5.6/tcp.d b/dlibs/x86_64/5.6/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/x86_64/5.6/tcp.d
+++ b/dlibs/x86_64/5.6/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/x86_64/6.1/ip.d b/dlibs/x86_64/6.1/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/x86_64/6.1/ip.d
+++ b/dlibs/x86_64/6.1/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/x86_64/6.1/net.d b/dlibs/x86_64/6.1/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/x86_64/6.1/net.d
+++ b/dlibs/x86_64/6.1/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/x86_64/6.1/tcp.d b/dlibs/x86_64/6.1/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/x86_64/6.1/tcp.d
+++ b/dlibs/x86_64/6.1/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
diff --git a/dlibs/x86_64/6.10/ip.d b/dlibs/x86_64/6.10/ip.d
index f8b77f12..493b75a0 100644
--- a/dlibs/x86_64/6.10/ip.d
+++ b/dlibs/x86_64/6.10/ip.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
inline int TCP_MIN_HEADER_LENGTH = 20;
-/*
- * For compatibility with Solaris. Here the netstackid will be the pointer
- * to the net namespace (nd_net in struct net_device).
- */
-typedef uint64_t netstackid_t;
-typedef __be32 ipaddr_t;
-typedef struct in6_addr in6_addr_t;
-
-/*
- * pktinfo is where packet ID info can be made available for deeper
- * analysis if packet IDs become supported by the kernel in the future.
- * The pkt_addr member is currently always NULL.
- */
-typedef struct pktinfo {
- uintptr_t pkt_addr;
-} pktinfo_t;
-
-/*
- * csinfo is where connection state info is made available.
- */
-typedef struct csinfo {
- uintptr_t cs_addr;
- uint64_t cs_cid;
-} csinfo_t;
-
/*
* ipinfo contains common IP info for both IPv4 and IPv6.
*/
diff --git a/dlibs/x86_64/6.10/net.d b/dlibs/x86_64/6.10/net.d
index 6ac34287..f1291696 100644
--- a/dlibs/x86_64/6.10/net.d
+++ b/dlibs/x86_64/6.10/net.d
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2025, 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.
*/
@@ -25,9 +25,36 @@ typedef struct conninfo {
string ci_protocol; /* protocol (ipv4, ipv6, etc) */
} conninfo_t;
+/*
+ * For compatibility with Solaris. Here the netstackid will be the pointer
+ * to the net namespace (nd_net in struct net_device).
+ */
+typedef uint64_t netstackid_t;
+typedef __be32 ipaddr_t;
+typedef struct in6_addr in6_addr_t;
+
+/*
+ * pktinfo is where packet ID info can be made available for deeper
+ * analysis if packet IDs become supported by the kernel in the future.
+ * The pkt_addr member is currently always NULL.
+ */
+typedef struct pktinfo {
+ uintptr_t pkt_addr;
+} pktinfo_t;
+
+/*
+ * csinfo is where connection state info is made available.
+ */
+typedef struct csinfo {
+ uintptr_t cs_addr;
+ uint64_t cs_cid;
+} csinfo_t;
+
/*
* We use these values to determine if a probe point is associated
- * with sending (outbound) or receiving (inbound).
+ * with sending (outbound) or receiving (inbound) or a state-related
+ * probe (i.e. neither inbound our outbound).
*/
inline int NET_PROBE_OUTBOUND = 0x00;
inline int NET_PROBE_INBOUND = 0x01;
+inline int NET_PROBE_STATE = 0x02;
diff --git a/dlibs/x86_64/6.10/tcp.d b/dlibs/x86_64/6.10/tcp.d
index 54e310cb..48d9adb4 100644
--- a/dlibs/x86_64/6.10/tcp.d
+++ b/dlibs/x86_64/6.10/tcp.d
@@ -1,13 +1,13 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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 library net.d
-#pragma D depends_on provider ip
+#pragma D depends_on library ip.d
#pragma D depends_on provider tcp
inline int TH_FIN = 0x01;
@@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
tcp_seq = T ? ntohl(T->seq) : 0;
tcp_ack = T ? ntohl(T->ack_seq) : 0;
tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
- tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
+ tcp_flags = T ? *((uint8_t *)T + 13) : 0;
tcp_window = T ? ntohs(T->window) : 0;
tcp_checksum = T ? ntohs(T->check) : 0;
tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
tcp_hdr = (uintptr_t)T;
};
+/* timewait sockets and inet connection sockets do not populate all fields
+ * and are not classified as full sockets; this inline helps translators
+ * spot them and act appropriately.
+ */
+inline int tcp_fullsock[struct tcp_sock *sk] =
+ (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
+ ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
/*
* In the main we simply translate from the "struct [tcp_]sock *" to
* a tcpsinfo_t *. However there are a few exceptions:
*
- * - tcps_state is always derived from arg6. The reason is that in some
+ * - tcps_state for state-change is arg5. The reason is that in some
* state transitions sock->sk_state does not reflect the actual TCP
* connection state. For example the TIME_WAIT state is handled in
* Linux by creating a separate timewait socket and the state of the
* original socket is CLOSED. In some other cases we also need to
- * instrument state transition prior to the update of sk_state. To do
- * all of this we rely on arg6.
+ * instrument state transition _prior_ to the update of sk_state. To do
+ * all of this we rely on arg5 to hold the new state. arg6 is set to
+ * NET_PROBE_STATE to quickly identify state-change probes.
* - we sometimes need to retrieve local/remote port/address settings from
* TCP and IP headers directly, for example prior to the address/port
* being committed to the socket. To do this effectively we need to know
* if the packet data is inbound (in which case the local IP/port are the
* destination) or outbound (in which case the local IP/port are the source).
- * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
+ * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
* to reconstruct the address/port info where necessary. arg2 used for IP
* information while arg4 contains the TCP header, so it is used for port data.
* NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
@@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
: 0;
- tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
+ tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
+ tcp_fullsock[T] ?
ntohs(((struct inet_sock *)T)->inet_sport) :
(T && ((struct inet_sock *)T)->inet_sport == 0) ?
- ntohs(((struct sock *)T)->__sk_common.skc_num) :
+ ((struct sock *)T)->__sk_common.skc_num :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
+ ((struct tcphdr *)arg4)->dest :
+ ((struct tcphdr *)arg4)->source) :
0;
tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
ntohs(((struct sock *)T)->__sk_common.skc_dport) :
arg4 != NULL ?
ntohs(arg7 == NET_PROBE_INBOUND ?
- ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
+ ((struct tcphdr *)arg4)->source :
+ ((struct tcphdr *)arg4)->dest) :
0;
tcps_laddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->daddr :
- &((struct ipv6hdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
"<unknown>";
tcps_raddr =
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
- arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
- inet_ntoa(arg7 == NET_PROBE_INBOUND ?
- &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
- arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
- inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
- &((struct ipv6hdr *)arg2)->saddr :
- &((struct ipv6hdr *)arg2)->daddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
+ inet_ntoa(&((struct iphdr *)arg2)->saddr) :
+ arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
+ inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
"<unknown>";
- tcps_state = arg6;
+ /* For state-change we probe right before state has changed, but
+ * provider definition wants new state in tcps_state; for
+ * state-change probes the trampoline stores it in arg5.
+ */
+ tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
+ T ? ((struct sock *)T)->__sk_common.skc_state : 0;
tcps_iss = T ?
T->snd_una - (uint32_t)T->bytes_acked : 0;
tcps_suna = T ? T->snd_una : 0;
@@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
T->rcv_nxt - (uint32_t)T->bytes_received : 0;
};
+/* state-change trampoline stores new state in arg5; at time of firing,
+ * state has not been updated, so last state is in tcp_sock state.
+ */
#pragma D binding "1.6.3" translator
translator tcplsinfo_t < int I > {
- tcps_state = I;
+ tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
};
--
2.43.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 6/6] unittest/tcp: update test.x
2025-07-22 10:07 [PATCH v5 0/6] DTrace TCP provider Alan Maguire
` (4 preceding siblings ...)
2025-07-22 10:07 ` [PATCH v5 5/6] dlibs: sync ip.d, net.d and tcp.d Alan Maguire
@ 2025-07-22 10:07 ` Alan Maguire
2025-07-22 13:53 ` [PATCH v5 0/6] DTrace TCP provider Alan Maguire
6 siblings, 0 replies; 8+ messages in thread
From: Alan Maguire @ 2025-07-22 10:07 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire, Kris Van Hees
Now that the tcp provider is supported, update test.x.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
---
test/unittest/tcp/test.x | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/test/unittest/tcp/test.x b/test/unittest/tcp/test.x
index e0ea2a5d..4de906a9 100755
--- a/test/unittest/tcp/test.x
+++ b/test/unittest/tcp/test.x
@@ -1,16 +1,10 @@
#!/bin/bash
#
# Oracle Linux DTrace.
-# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2017, 2025, 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.
-check_provider tcp
-if (( $? != 0 )); then
- echo "Could not load tcp provider"
- exit 1
-fi
-
if grep -qF ../ip/client.ip.pl $_test &&
! perl -MIO::Socket::IP -e 'exit(0);' 2>/dev/null; then
echo "No IO::Socket::IP"
--
2.43.5
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v5 0/6] DTrace TCP provider
2025-07-22 10:07 [PATCH v5 0/6] DTrace TCP provider Alan Maguire
` (5 preceding siblings ...)
2025-07-22 10:07 ` [PATCH v5 6/6] unittest/tcp: update test.x Alan Maguire
@ 2025-07-22 13:53 ` Alan Maguire
6 siblings, 0 replies; 8+ messages in thread
From: Alan Maguire @ 2025-07-22 13:53 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel
looks like this crossed with the merging of v4 + cg changes, please
ignore. Thanks!
On 22/07/2025 11:07, Alan Maguire wrote:
> This series adds TCP provider support, where the probes are
> implemented via underlying fbt and sdt probes.
>
> Due to the use of the sock/inet_sock_set_state tracepoint, intended
> for ~5.15 kernels and later. Tried replacing this with
>
> fbt::tcp_set_state:entry
>
> but this misses a few state transitions, so stuck with using
> the tracepoint.
>
> All tests under test/unittest/tcp pass unmodified on an upstream
> (6.15) kernel and 5.15 UEK7U3 kernel. The updated remote address
> retrieval patch helps ensure tests can all be run.
>
> It implements all documented TCP provider probes:
>
> accept-established, accept-refused, connnect-request,
> connect-established, connect-refused, receive, send,
> state-change
>
> Note that to function correctly this series depends on the
> cg-related patches:
>
> parser: add dt_node_is_tstring()
> cg: validate tstring alloc/free
> cg: optimize ternary expressions for strings
> tstring: fix leaks
>
> Changes since v4:
>
> - updated patch 1 to rename dt_cg_get_member() to
> dt_cg_tramp_get_member() and have it make use of
> dt_cg_ctf_offsetof() (Kris, patch 1)
> - remove tslot update patch since it is handled elsewhere
> (Kris)
> - added R-Bs from Kris
>
> Changes since v3:
>
> - fix patch 2 to really use/describe 6 tstring slots (patch 2)
>
> Changes since v2:
>
> - document tslots change properly, add test (Eugene, patch 2, 3)
> - move more generic type definitions to net.d
> - retain depends_on provider tcp in tcp.d (Kris, patch 5)
> - use depends_on library ip.d to ensure loading order (patch 5)
> - revert tcp flags to uint8_t (Kris, patch 5)
> - improve comments in tcp.d (Kris, Eugene, patch 5)
> - fix up handling of ip:::send for older kernels to avoid null
> dereferences (Eugene, patch 5)
> - fix up provider arguments to match spec (Eugene, patch 5)
>
> Changes since RFC:
>
> - fixed issues with test failures on UEK7 due to missing
> SYN_RCV state change
> - moved get_member() to dt_cg.c (patch 1)
>
> Alan Maguire (6):
> cg: refactor get_member() to use dt_cg_ctf_offsetof
> test/operators: extend ternary tests to cover inet_ntoa*()s
> providers: move network-generic definitions to net.d
> tcp: new provider
> dlibs: sync ip.d, net.d and tcp.d
> unittest/tcp: update test.x
>
> dlibs/aarch64/5.11/ip.d | 27 +-
> dlibs/aarch64/5.11/net.d | 31 +-
> dlibs/aarch64/5.11/tcp.d | 67 +++--
> dlibs/aarch64/5.12/ip.d | 27 +-
> dlibs/aarch64/5.12/net.d | 31 +-
> dlibs/aarch64/5.12/tcp.d | 67 +++--
> dlibs/aarch64/5.14/ip.d | 27 +-
> dlibs/aarch64/5.14/net.d | 31 +-
> dlibs/aarch64/5.14/tcp.d | 67 +++--
> dlibs/aarch64/5.16/ip.d | 27 +-
> dlibs/aarch64/5.16/net.d | 31 +-
> dlibs/aarch64/5.16/tcp.d | 67 +++--
> dlibs/aarch64/5.2/ip.d | 27 +-
> dlibs/aarch64/5.2/net.d | 31 +-
> dlibs/aarch64/5.2/tcp.d | 67 +++--
> dlibs/aarch64/5.6/ip.d | 27 +-
> dlibs/aarch64/5.6/net.d | 31 +-
> dlibs/aarch64/5.6/tcp.d | 67 +++--
> dlibs/aarch64/6.1/ip.d | 27 +-
> dlibs/aarch64/6.1/net.d | 31 +-
> dlibs/aarch64/6.1/tcp.d | 67 +++--
> dlibs/aarch64/6.10/ip.d | 27 +-
> dlibs/aarch64/6.10/net.d | 31 +-
> dlibs/aarch64/6.10/tcp.d | 67 +++--
> dlibs/x86_64/5.11/ip.d | 27 +-
> dlibs/x86_64/5.11/net.d | 31 +-
> dlibs/x86_64/5.11/tcp.d | 67 +++--
> dlibs/x86_64/5.12/ip.d | 27 +-
> dlibs/x86_64/5.12/net.d | 31 +-
> dlibs/x86_64/5.12/tcp.d | 67 +++--
> dlibs/x86_64/5.14/ip.d | 27 +-
> dlibs/x86_64/5.14/net.d | 31 +-
> dlibs/x86_64/5.14/tcp.d | 67 +++--
> dlibs/x86_64/5.16/ip.d | 27 +-
> dlibs/x86_64/5.16/net.d | 31 +-
> dlibs/x86_64/5.16/tcp.d | 67 +++--
> dlibs/x86_64/5.2/ip.d | 27 +-
> dlibs/x86_64/5.2/net.d | 31 +-
> dlibs/x86_64/5.2/tcp.d | 67 +++--
> dlibs/x86_64/5.6/ip.d | 27 +-
> dlibs/x86_64/5.6/net.d | 31 +-
> dlibs/x86_64/5.6/tcp.d | 67 +++--
> dlibs/x86_64/6.1/ip.d | 27 +-
> dlibs/x86_64/6.1/net.d | 31 +-
> dlibs/x86_64/6.1/tcp.d | 67 +++--
> dlibs/x86_64/6.10/ip.d | 27 +-
> dlibs/x86_64/6.10/net.d | 31 +-
> dlibs/x86_64/6.10/tcp.d | 67 +++--
> libdtrace/Build | 2 +
> libdtrace/dt_cg.c | 57 +++-
> libdtrace/dt_cg.h | 4 +-
> libdtrace/dt_prov_io.c | 84 +++---
> libdtrace/dt_prov_ip.c | 45 +--
> libdtrace/dt_prov_proc.c | 4 +-
> libdtrace/dt_prov_sched.c | 2 +-
> libdtrace/dt_prov_tcp.c | 413 ++++++++++++++++++++++++++
> libdtrace/dt_prov_uprobe.c | 4 +-
> libdtrace/dt_provider.c | 1 +
> libdtrace/dt_provider.h | 1 +
> libdtrace/ip.d | 27 +-
> libdtrace/net.d | 31 +-
> libdtrace/tcp.d | 67 +++--
> test/unittest/operators/tst.ternary.d | 20 +-
> test/unittest/operators/tst.ternary.r | 2 +
> test/unittest/tcp/test.x | 8 +-
> 65 files changed, 1731 insertions(+), 1041 deletions(-)
> create mode 100644 libdtrace/dt_prov_tcp.c
>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-07-22 13:54 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-22 10:07 [PATCH v5 0/6] DTrace TCP provider Alan Maguire
2025-07-22 10:07 ` [PATCH v5 1/6] cg: refactor get_member() to use dt_cg_ctf_offsetof Alan Maguire
2025-07-22 10:07 ` [PATCH v5 2/6] test/operators: extend ternary tests to cover inet_ntoa*()s Alan Maguire
2025-07-22 10:07 ` [PATCH v5 3/6] providers: move network-generic definitions to net.d Alan Maguire
2025-07-22 10:07 ` [PATCH v5 4/6] tcp: new provider Alan Maguire
2025-07-22 10:07 ` [PATCH v5 5/6] dlibs: sync ip.d, net.d and tcp.d Alan Maguire
2025-07-22 10:07 ` [PATCH v5 6/6] unittest/tcp: update test.x Alan Maguire
2025-07-22 13:53 ` [PATCH v5 0/6] DTrace TCP provider Alan Maguire
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox