* [PATCH v3 0/7] DTrace TCP provider
@ 2025-07-09 14:09 Alan Maguire
2025-07-09 14:09 ` [PATCH v3 1/7] cg: move get_member() to dt_cg.c Alan Maguire
` (6 more replies)
0 siblings, 7 replies; 10+ messages in thread
From: Alan Maguire @ 2025-07-09 14:09 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
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 (7):
cg: move get_member() to dt_cg.c
cg: bump number of TSLOTS to 6
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 | 39 +++
libdtrace/dt_cg.h | 2 +
libdtrace/dt_impl.h | 9 +-
libdtrace/dt_prov_ip.c | 45 +--
libdtrace/dt_prov_tcp.c | 413 ++++++++++++++++++++++++++
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 +-
62 files changed, 1680 insertions(+), 987 deletions(-)
create mode 100644 libdtrace/dt_prov_tcp.c
--
2.39.3
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 1/7] cg: move get_member() to dt_cg.c
2025-07-09 14:09 [PATCH v3 0/7] DTrace TCP provider Alan Maguire
@ 2025-07-09 14:09 ` Alan Maguire
2025-07-21 20:37 ` [DTrace-devel] " Kris Van Hees
2025-07-09 14:09 ` [PATCH v3 2/7] cg: bump number of TSLOTS to 6 Alan Maguire
` (5 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Alan Maguire @ 2025-07-09 14:09 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire, Eugene Loh
It will be used by both dt_prov_ip.c and dt_prov_tcp.c.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Reviewed-by: Eugene Loh <eugene.loh@oracle.com>
---
libdtrace/dt_cg.c | 39 ++++++++++++++++++++++++++++++++++++
libdtrace/dt_cg.h | 2 ++
libdtrace/dt_prov_ip.c | 45 ++++--------------------------------------
3 files changed, 45 insertions(+), 41 deletions(-)
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index bd0763d6..d6fc8259 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -1901,6 +1901,45 @@ dt_cg_ctf_offsetof(const char *structname, const char *membername,
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
+ */
+int
+dt_cg_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;
+}
+
static void
dt_cg_act_breakpoint(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
{
diff --git a/libdtrace/dt_cg.h b/libdtrace/dt_cg.h
index 81b79399..36f587d3 100644
--- a/libdtrace/dt_cg.h
+++ b/libdtrace/dt_cg.h
@@ -44,6 +44,8 @@ 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);
+extern int dt_cg_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_ip.c b/libdtrace/dt_prov_ip.c
index c4a3a6e2..37f91e3d 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_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_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_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_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;
--
2.39.3
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 2/7] cg: bump number of TSLOTS to 6
2025-07-09 14:09 [PATCH v3 0/7] DTrace TCP provider Alan Maguire
2025-07-09 14:09 ` [PATCH v3 1/7] cg: move get_member() to dt_cg.c Alan Maguire
@ 2025-07-09 14:09 ` Alan Maguire
2025-07-21 20:39 ` [DTrace-devel] " Kris Van Hees
2025-07-09 14:09 ` [PATCH v3 3/7] test/operators: extend ternary tests to cover inet_ntoa*()s Alan Maguire
` (4 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Alan Maguire @ 2025-07-09 14:09 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire
Because of the ternary operations using inet_ntoa*() in the
TCP translators more temporary string slots are needed.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
libdtrace/dt_impl.h | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index 2adc1252..b8c0f94c 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -203,14 +203,17 @@ typedef struct dt_kern_path {
#define DT_DM_KERN_UNLOADED 0x8 /* module not loaded into the kernel */
/*
- * Why do we need (only) 4 slots? The maximum amount of string arguments to
+ * Why do we need 8 slots? The maximum amount of string arguments to
* any function is 2, and if the result is a string as well, that means we may
* need 3 temporary strings during code generation for that function.
+ * However if operations like inet_ntoa6 are used in ternary operations we need
+ * 2x the number of slots for left and right.
*
* Since string functions can be nested, we can (at most) end up with 1 tstring
* (from a nested function for which we already generated code) along with a
* nested function being processed which needs 3 temporary strings as mentioned
- * above. That brings us to a total of 4.
+ * above. That brings us to a total of 4, but since the ternary case requires
+ * 2x we need 8 in total.
*
* Each tstring needs to be large enough to hold the largest possible string
* and accomodate the largest known need for tstring space in subroutines.
@@ -222,7 +225,7 @@ typedef struct dt_kern_path {
* - cleanpath() holds a prepended '/' char, a string, an appended '/' char,
* and a terminating NUL char, or STRSZ + 3 chars altogether
*/
-#define DT_TSTRING_SLOTS 4
+#define DT_TSTRING_SLOTS 8
#define DT_TSTRING_SIZE(dtp) \
MAX(P2ROUNDUP((dtp)->dt_options[DTRACEOPT_STRSIZE] + 3, 8), \
72)
--
2.39.3
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 3/7] test/operators: extend ternary tests to cover inet_ntoa*()s
2025-07-09 14:09 [PATCH v3 0/7] DTrace TCP provider Alan Maguire
2025-07-09 14:09 ` [PATCH v3 1/7] cg: move get_member() to dt_cg.c Alan Maguire
2025-07-09 14:09 ` [PATCH v3 2/7] cg: bump number of TSLOTS to 6 Alan Maguire
@ 2025-07-09 14:09 ` Alan Maguire
2025-07-09 14:09 ` [PATCH v3 4/7] providers: move network-generic definitions to net.d Alan Maguire
` (3 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Alan Maguire @ 2025-07-09 14:09 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire, Eugene Loh
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>
---
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.39.3
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 4/7] providers: move network-generic definitions to net.d
2025-07-09 14:09 [PATCH v3 0/7] DTrace TCP provider Alan Maguire
` (2 preceding siblings ...)
2025-07-09 14:09 ` [PATCH v3 3/7] test/operators: extend ternary tests to cover inet_ntoa*()s Alan Maguire
@ 2025-07-09 14:09 ` Alan Maguire
2025-07-09 14:09 ` [PATCH v3 5/7] tcp: new provider Alan Maguire
` (2 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Alan Maguire @ 2025-07-09 14:09 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire
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>
---
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.39.3
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 5/7] tcp: new provider
2025-07-09 14:09 [PATCH v3 0/7] DTrace TCP provider Alan Maguire
` (3 preceding siblings ...)
2025-07-09 14:09 ` [PATCH v3 4/7] providers: move network-generic definitions to net.d Alan Maguire
@ 2025-07-09 14:09 ` Alan Maguire
2025-07-09 14:09 ` [PATCH v3 6/7] dlibs: sync ip.d, net.d and tcp.d Alan Maguire
2025-07-09 14:09 ` [PATCH v3 7/7] unittest/tcp: update test.x Alan Maguire
6 siblings, 0 replies; 10+ messages in thread
From: Alan Maguire @ 2025-07-09 14:09 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..84b4954f
--- /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_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_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_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_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_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_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_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 2a3bba80..8a0280cc 100644
--- a/libdtrace/dt_provider.h
+++ b/libdtrace/dt_provider.h
@@ -86,6 +86,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.39.3
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 6/7] dlibs: sync ip.d, net.d and tcp.d
2025-07-09 14:09 [PATCH v3 0/7] DTrace TCP provider Alan Maguire
` (4 preceding siblings ...)
2025-07-09 14:09 ` [PATCH v3 5/7] tcp: new provider Alan Maguire
@ 2025-07-09 14:09 ` Alan Maguire
2025-07-09 14:09 ` [PATCH v3 7/7] unittest/tcp: update test.x Alan Maguire
6 siblings, 0 replies; 10+ messages in thread
From: Alan Maguire @ 2025-07-09 14:09 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire
Sync dlibs with libdtrace versions.
Signed-off-by: Alan Maguire <alan.maguire@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.39.3
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 7/7] unittest/tcp: update test.x
2025-07-09 14:09 [PATCH v3 0/7] DTrace TCP provider Alan Maguire
` (5 preceding siblings ...)
2025-07-09 14:09 ` [PATCH v3 6/7] dlibs: sync ip.d, net.d and tcp.d Alan Maguire
@ 2025-07-09 14:09 ` Alan Maguire
6 siblings, 0 replies; 10+ messages in thread
From: Alan Maguire @ 2025-07-09 14:09 UTC (permalink / raw)
To: dtrace; +Cc: dtrace-devel, Alan Maguire
Now that the tcp provider is supported, update test.x.
Signed-off-by: Alan Maguire <alan.maguire@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.39.3
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [DTrace-devel] [PATCH v3 1/7] cg: move get_member() to dt_cg.c
2025-07-09 14:09 ` [PATCH v3 1/7] cg: move get_member() to dt_cg.c Alan Maguire
@ 2025-07-21 20:37 ` Kris Van Hees
0 siblings, 0 replies; 10+ messages in thread
From: Kris Van Hees @ 2025-07-21 20:37 UTC (permalink / raw)
To: Alan Maguire; +Cc: dtrace, dtrace-devel
On Wed, Jul 09, 2025 at 03:09:42PM +0100, Alan Maguire via DTrace-devel wrote:
> It will be used by both dt_prov_ip.c and dt_prov_tcp.c.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> Reviewed-by: Eugene Loh <eugene.loh@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
... albeit a newer version that is a collaboration of Alan and I to improve
(what is now called) dt_cg_tramp_get_member()
> ---
> libdtrace/dt_cg.c | 39 ++++++++++++++++++++++++++++++++++++
> libdtrace/dt_cg.h | 2 ++
> libdtrace/dt_prov_ip.c | 45 ++++--------------------------------------
> 3 files changed, 45 insertions(+), 41 deletions(-)
>
> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> index bd0763d6..d6fc8259 100644
> --- a/libdtrace/dt_cg.c
> +++ b/libdtrace/dt_cg.c
> @@ -1901,6 +1901,45 @@ dt_cg_ctf_offsetof(const char *structname, const char *membername,
> 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
> + */
> +int
> +dt_cg_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;
> +}
> +
> static void
> dt_cg_act_breakpoint(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
> {
> diff --git a/libdtrace/dt_cg.h b/libdtrace/dt_cg.h
> index 81b79399..36f587d3 100644
> --- a/libdtrace/dt_cg.h
> +++ b/libdtrace/dt_cg.h
> @@ -44,6 +44,8 @@ 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);
> +extern int dt_cg_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_ip.c b/libdtrace/dt_prov_ip.c
> index c4a3a6e2..37f91e3d 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_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_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_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_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;
> --
> 2.39.3
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel@oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [DTrace-devel] [PATCH v3 2/7] cg: bump number of TSLOTS to 6
2025-07-09 14:09 ` [PATCH v3 2/7] cg: bump number of TSLOTS to 6 Alan Maguire
@ 2025-07-21 20:39 ` Kris Van Hees
0 siblings, 0 replies; 10+ messages in thread
From: Kris Van Hees @ 2025-07-21 20:39 UTC (permalink / raw)
To: Alan Maguire; +Cc: dtrace, dtrace-devel
This patch is superseded by:
5d1dc16c parser: add dt_node_is_tstring()
429f828b cg: validate tstring alloc/free
307d90a1 cg: optimize ternary expressions for strings
c2e719f3 tstring: fix leaks
On Wed, Jul 09, 2025 at 03:09:43PM +0100, Alan Maguire via DTrace-devel wrote:
> Because of the ternary operations using inet_ntoa*() in the
> TCP translators more temporary string slots are needed.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
> libdtrace/dt_impl.h | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
> index 2adc1252..b8c0f94c 100644
> --- a/libdtrace/dt_impl.h
> +++ b/libdtrace/dt_impl.h
> @@ -203,14 +203,17 @@ typedef struct dt_kern_path {
> #define DT_DM_KERN_UNLOADED 0x8 /* module not loaded into the kernel */
>
> /*
> - * Why do we need (only) 4 slots? The maximum amount of string arguments to
> + * Why do we need 8 slots? The maximum amount of string arguments to
> * any function is 2, and if the result is a string as well, that means we may
> * need 3 temporary strings during code generation for that function.
> + * However if operations like inet_ntoa6 are used in ternary operations we need
> + * 2x the number of slots for left and right.
> *
> * Since string functions can be nested, we can (at most) end up with 1 tstring
> * (from a nested function for which we already generated code) along with a
> * nested function being processed which needs 3 temporary strings as mentioned
> - * above. That brings us to a total of 4.
> + * above. That brings us to a total of 4, but since the ternary case requires
> + * 2x we need 8 in total.
> *
> * Each tstring needs to be large enough to hold the largest possible string
> * and accomodate the largest known need for tstring space in subroutines.
> @@ -222,7 +225,7 @@ typedef struct dt_kern_path {
> * - cleanpath() holds a prepended '/' char, a string, an appended '/' char,
> * and a terminating NUL char, or STRSZ + 3 chars altogether
> */
> -#define DT_TSTRING_SLOTS 4
> +#define DT_TSTRING_SLOTS 8
> #define DT_TSTRING_SIZE(dtp) \
> MAX(P2ROUNDUP((dtp)->dt_options[DTRACEOPT_STRSIZE] + 3, 8), \
> 72)
> --
> 2.39.3
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel@oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2025-07-21 20:39 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-09 14:09 [PATCH v3 0/7] DTrace TCP provider Alan Maguire
2025-07-09 14:09 ` [PATCH v3 1/7] cg: move get_member() to dt_cg.c Alan Maguire
2025-07-21 20:37 ` [DTrace-devel] " Kris Van Hees
2025-07-09 14:09 ` [PATCH v3 2/7] cg: bump number of TSLOTS to 6 Alan Maguire
2025-07-21 20:39 ` [DTrace-devel] " Kris Van Hees
2025-07-09 14:09 ` [PATCH v3 3/7] test/operators: extend ternary tests to cover inet_ntoa*()s Alan Maguire
2025-07-09 14:09 ` [PATCH v3 4/7] providers: move network-generic definitions to net.d Alan Maguire
2025-07-09 14:09 ` [PATCH v3 5/7] tcp: new provider Alan Maguire
2025-07-09 14:09 ` [PATCH v3 6/7] dlibs: sync ip.d, net.d and tcp.d Alan Maguire
2025-07-09 14:09 ` [PATCH v3 7/7] unittest/tcp: update test.x Alan Maguire
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox