* [PATCH v4 1/7] cg: move get_member() to dt_cg.c
2025-07-09 14:46 [PATCH v4 0/7] DTrace TCP provider Alan Maguire
@ 2025-07-09 14:46 ` Alan Maguire
2025-07-09 16:28 ` [DTrace-devel] " Kris Van Hees
2025-07-09 14:46 ` [PATCH v4 2/7] cg: bump number of TSLOTS to 6 Alan Maguire
` (5 subsequent siblings)
6 siblings, 1 reply; 16+ messages in thread
From: Alan Maguire @ 2025-07-09 14:46 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] 16+ messages in thread* Re: [DTrace-devel] [PATCH v4 1/7] cg: move get_member() to dt_cg.c
2025-07-09 14:46 ` [PATCH v4 1/7] cg: move get_member() to dt_cg.c Alan Maguire
@ 2025-07-09 16:28 ` Kris Van Hees
2025-07-09 16:47 ` Kris Van Hees
0 siblings, 1 reply; 16+ messages in thread
From: Kris Van Hees @ 2025-07-09 16:28 UTC (permalink / raw)
To: Alan Maguire; +Cc: dtrace, dtrace-devel
Some comments below... Sorry - I got distracted by the other patches and
failed to give this one attention.
On Wed, Jul 09, 2025 at 03:46:54PM +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>
> ---
> 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)
Given that this is meant to be used from trampoline code (where we currently
do not do register tracking - i.e. we expect the code writer to make sure that
no collisions of register use occur), it probably should be named dt_cg_tramp_*
rather than dt_cg_*. That also signals people looking at this that register
allocation is done manually, and with care, so we can expect things like %r0
getting assigned a value here, and that we expect that the passed in reg is NOT
%r0.
> +{
> + 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;
The dt_cg_tramp_get_member name would have me assume that this can get me the
member of any struct, not just those defined in kernel modules (or the kernel
proper - which in DTrace terms is a module). So, if the intent is that it can
only use structs defined at the kernel level, then the function name should
probably reflect that. If not (and that is what I would prefer), then this
should do a lookup for DTRACE_OBJ_EVERY, like e.g. dt_cg_ctf_offsetof does.
On that note, perhaps it would be easier to extend dt_cg_ctf_offsetof() to
take another (optional) arg to put the ldop in, populate that one if a ptr
is passed to store it in, and then use that here? Because you are essentially
doing the same lookup, you need the offset, and the size, and so the only
extra think needed (that dt_cg_ctf_offsetof() could provide easily) is the
ldop.
So, I think dt_cg_ctf_offsetof() could become:
dt_cg_ctf_offsetof(const char *structname, const char *membername,
size_t *sizep, uint_t *ldopp, int relaxed)
and instead of:
if (sizep)
*sizep = ctf_type_size(ctfp, ctm.ctm_type);
it could use:
if (sizep || ldopp) {
uint_t ldop;
ldop = dt_cg_ldsize(NULL, tt.dtt_ctfp, ctm.ctm_type, sizep);
if (ldopp)
*ldopp = ldop;
}
and then the dt_cg_tramp_get_member can just use:
offset = dt_cg_ctf_offsetof(name, member, &size, &ldop, 0);
instead of the lookup above, and the dt_cg_ldsize() call below.
> +
> + 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));
This would use offset.
> + emit(dlp, BPF_MOV_IMM(BPF_REG_2, size));
> + emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_FP));
> + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_TRAMP_SP_BASE));
> + emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
> + emit(dlp, BPF_LOAD(ldop, BPF_REG_0, BPF_REG_FP, DT_TRAMP_SP_BASE));
> +
> + 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] 16+ messages in thread* Re: [DTrace-devel] [PATCH v4 1/7] cg: move get_member() to dt_cg.c
2025-07-09 16:28 ` [DTrace-devel] " Kris Van Hees
@ 2025-07-09 16:47 ` Kris Van Hees
2025-07-09 19:47 ` Alan Maguire
0 siblings, 1 reply; 16+ messages in thread
From: Kris Van Hees @ 2025-07-09 16:47 UTC (permalink / raw)
To: Kris Van Hees; +Cc: Alan Maguire, dtrace, dtrace-devel
Woops, one more.
On Wed, Jul 09, 2025 at 12:28:15PM -0400, Kris Van Hees wrote:
> Some comments below... Sorry - I got distracted by the other patches and
> failed to give this one attention.
>
> On Wed, Jul 09, 2025 at 03:46:54PM +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>
> > ---
> > 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)
Why is dt_cg_get_member() returning int? Nothing looks at it anyway. ANd
with the change to use dt_cg_ctf_offset(), compilation error will be repotred
from there.
> Given that this is meant to be used from trampoline code (where we currently
> do not do register tracking - i.e. we expect the code writer to make sure that
> no collisions of register use occur), it probably should be named dt_cg_tramp_*
> rather than dt_cg_*. That also signals people looking at this that register
> allocation is done manually, and with care, so we can expect things like %r0
> getting assigned a value here, and that we expect that the passed in reg is NOT
> %r0.
>
> > +{
> > + 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;
>
> The dt_cg_tramp_get_member name would have me assume that this can get me the
> member of any struct, not just those defined in kernel modules (or the kernel
> proper - which in DTrace terms is a module). So, if the intent is that it can
> only use structs defined at the kernel level, then the function name should
> probably reflect that. If not (and that is what I would prefer), then this
> should do a lookup for DTRACE_OBJ_EVERY, like e.g. dt_cg_ctf_offsetof does.
>
> On that note, perhaps it would be easier to extend dt_cg_ctf_offsetof() to
> take another (optional) arg to put the ldop in, populate that one if a ptr
> is passed to store it in, and then use that here? Because you are essentially
> doing the same lookup, you need the offset, and the size, and so the only
> extra think needed (that dt_cg_ctf_offsetof() could provide easily) is the
> ldop.
>
> So, I think dt_cg_ctf_offsetof() could become:
>
> dt_cg_ctf_offsetof(const char *structname, const char *membername,
> size_t *sizep, uint_t *ldopp, int relaxed)
>
> and instead of:
>
> if (sizep)
> *sizep = ctf_type_size(ctfp, ctm.ctm_type);
>
> it could use:
>
> if (sizep || ldopp) {
> uint_t ldop;
>
> ldop = dt_cg_ldsize(NULL, tt.dtt_ctfp, ctm.ctm_type, sizep);
> if (ldopp)
> *ldopp = ldop;
> }
>
> and then the dt_cg_tramp_get_member can just use:
>
> offset = dt_cg_ctf_offsetof(name, member, &size, &ldop, 0);
>
> instead of the lookup above, and the dt_cg_ldsize() call below.
>
> > +
> > + 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));
>
> This would use offset.
>
> > + emit(dlp, BPF_MOV_IMM(BPF_REG_2, size));
> > + emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_FP));
> > + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_TRAMP_SP_BASE));
> > + emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
> > + emit(dlp, BPF_LOAD(ldop, BPF_REG_0, BPF_REG_FP, DT_TRAMP_SP_BASE));
> > +
> > + 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] 16+ messages in thread* Re: [DTrace-devel] [PATCH v4 1/7] cg: move get_member() to dt_cg.c
2025-07-09 16:47 ` Kris Van Hees
@ 2025-07-09 19:47 ` Alan Maguire
0 siblings, 0 replies; 16+ messages in thread
From: Alan Maguire @ 2025-07-09 19:47 UTC (permalink / raw)
To: Kris Van Hees; +Cc: dtrace, dtrace-devel
On 09/07/2025 17:47, Kris Van Hees wrote:
> Woops, one more.
>
> On Wed, Jul 09, 2025 at 12:28:15PM -0400, Kris Van Hees wrote:
>> Some comments below... Sorry - I got distracted by the other patches and
>> failed to give this one attention.
>>
>> On Wed, Jul 09, 2025 at 03:46:54PM +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>
>>> ---
>>> 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)
>
> Why is dt_cg_get_member() returning int? Nothing looks at it anyway. ANd
> with the change to use dt_cg_ctf_offset(), compilation error will be repotred
> from there.
>
all sounds good; I've removed the return value and refactored as you
suggest, all working now. I'll hold off on sending an updated v5 patch
series since this one's only just out the door. Thanks!
Alan
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v4 2/7] cg: bump number of TSLOTS to 6
2025-07-09 14:46 [PATCH v4 0/7] DTrace TCP provider Alan Maguire
2025-07-09 14:46 ` [PATCH v4 1/7] cg: move get_member() to dt_cg.c Alan Maguire
@ 2025-07-09 14:46 ` Alan Maguire
2025-07-10 4:46 ` [DTrace-devel] " Kris Van Hees
2025-07-09 14:46 ` [PATCH v4 3/7] test/operators: extend ternary tests to cover inet_ntoa*()s Alan Maguire
` (4 subsequent siblings)
6 siblings, 1 reply; 16+ messages in thread
From: Alan Maguire @ 2025-07-09 14:46 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 | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index 2adc1252..a47b6d40 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -203,14 +203,16 @@ 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 6 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.
*
* 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 +224,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 6
#define DT_TSTRING_SIZE(dtp) \
MAX(P2ROUNDUP((dtp)->dt_options[DTRACEOPT_STRSIZE] + 3, 8), \
72)
--
2.39.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [DTrace-devel] [PATCH v4 2/7] cg: bump number of TSLOTS to 6
2025-07-09 14:46 ` [PATCH v4 2/7] cg: bump number of TSLOTS to 6 Alan Maguire
@ 2025-07-10 4:46 ` Kris Van Hees
0 siblings, 0 replies; 16+ messages in thread
From: Kris Van Hees @ 2025-07-10 4:46 UTC (permalink / raw)
To: Alan Maguire; +Cc: dtrace, dtrace-devel
Stay tuned for a patch I will post tomorrow (Thursday) with an optimization
for the ternary operator for strings that will reduce the use of tstrings
quite a bit. With that optimization, the test you added for nested ternaries
still won't use more than 4 slots.
In the future though we should change the tstring handling to dynamically
determine the max number of slots needed for the compiled code, and allocate
based on that rather than having a hard limit.
On Wed, Jul 09, 2025 at 03:46:55PM +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 | 8 +++++---
> 1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
> index 2adc1252..a47b6d40 100644
> --- a/libdtrace/dt_impl.h
> +++ b/libdtrace/dt_impl.h
> @@ -203,14 +203,16 @@ 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 6 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.
> *
> * 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 +224,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 6
> #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] 16+ messages in thread
* [PATCH v4 3/7] test/operators: extend ternary tests to cover inet_ntoa*()s
2025-07-09 14:46 [PATCH v4 0/7] DTrace TCP provider Alan Maguire
2025-07-09 14:46 ` [PATCH v4 1/7] cg: move get_member() to dt_cg.c Alan Maguire
2025-07-09 14:46 ` [PATCH v4 2/7] cg: bump number of TSLOTS to 6 Alan Maguire
@ 2025-07-09 14:46 ` Alan Maguire
2025-07-21 19:59 ` [DTrace-devel] " Kris Van Hees
2025-07-09 14:46 ` [PATCH v4 4/7] providers: move network-generic definitions to net.d Alan Maguire
` (3 subsequent siblings)
6 siblings, 1 reply; 16+ messages in thread
From: Alan Maguire @ 2025-07-09 14:46 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] 16+ messages in thread* Re: [DTrace-devel] [PATCH v4 3/7] test/operators: extend ternary tests to cover inet_ntoa*()s
2025-07-09 14:46 ` [PATCH v4 3/7] test/operators: extend ternary tests to cover inet_ntoa*()s Alan Maguire
@ 2025-07-21 19:59 ` Kris Van Hees
0 siblings, 0 replies; 16+ messages in thread
From: Kris Van Hees @ 2025-07-21 19:59 UTC (permalink / raw)
To: Alan Maguire; +Cc: dtrace, dtrace-devel
On Wed, Jul 09, 2025 at 03:46:56PM +0100, Alan Maguire via DTrace-devel wrote:
> inet_ntoa*() require temporary strings and ternary operators
> need temporaries for left and right; ensure ternary ops succeed
> with inet_ntoa*()s.
>
> Suggested-by: Eugene Loh <eugene.loh@oracle.com>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
> ---
> test/unittest/operators/tst.ternary.d | 20 +++++++++++++++++++-
> test/unittest/operators/tst.ternary.r | 2 ++
> 2 files changed, 21 insertions(+), 1 deletion(-)
>
> diff --git a/test/unittest/operators/tst.ternary.d b/test/unittest/operators/tst.ternary.d
> index 9d47fcc2..aeced7ac 100644
> --- a/test/unittest/operators/tst.ternary.d
> +++ b/test/unittest/operators/tst.ternary.d
> @@ -8,7 +8,10 @@
> /*
> * ASSERTION:
> * Test the ternary operator. Test left-hand side true, right-hand side true,
> - * and multiple nested instances of the ternary operator.
> + * and multiple nested instances of the ternary operator. Since inet_ntoa*()
> + * relies on temporary strings, and ternary operators need both left- and
> + * right-side temporaries, test ternary inet_ntoa*() also.
> + *
> *
> * SECTION: Types, Operators, and Expressions/Conditional Expressions
> */
> @@ -18,10 +21,25 @@
> BEGIN
> {
> x = 0;
> + y = 1;
> printf("x is %s\n", x == 0 ? "zero" : "one");
> x = 1;
> printf("x is %s\n", x == 0 ? "zero" : "one");
> x = 2;
> printf("x is %s\n", x == 0 ? "zero" : x == 1 ? "one" : "two");
> + ipaddr = (ipaddr_t *)alloca(sizeof(ipaddr_t));
> + ipaddr2 = (in6_addr_t *)alloca(sizeof(in6_addr_t));
> + ipaddr2->in6_u.u6_addr32[0] = 0xffffffff;
> + ipaddr2->in6_u.u6_addr32[1] = 0xffffffff;
> + ipaddr2->in6_u.u6_addr32[2] = 0xffffffff;
> + ipaddr2->in6_u.u6_addr32[3] = 0xffffffff;
> + printf("ipaddr is %s\n", x > 1 ? inet_ntoa(ipaddr) :
> + x > 2 ? inet_ntoa(ipaddr) :
> + x > 3 ? inet_ntoa(ipaddr) :
> + inet_ntoa(ipaddr));
> + printf("ipaddr2 is %s\n", x > 1 ? inet_ntoa6(ipaddr2) :
> + x > 2 ? inet_ntoa6(ipaddr2) :
> + x > 3 ? inet_ntoa6(ipaddr2) :
> + inet_ntoa6(ipaddr2));
> exit(0);
> }
> diff --git a/test/unittest/operators/tst.ternary.r b/test/unittest/operators/tst.ternary.r
> index ec30800e..72201682 100644
> --- a/test/unittest/operators/tst.ternary.r
> +++ b/test/unittest/operators/tst.ternary.r
> @@ -1,4 +1,6 @@
> x is zero
> x is one
> x is two
> +ipaddr is 0.0.0.0
> +ipaddr2 is ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
>
> --
> 2.39.3
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel@oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v4 4/7] providers: move network-generic definitions to net.d
2025-07-09 14:46 [PATCH v4 0/7] DTrace TCP provider Alan Maguire
` (2 preceding siblings ...)
2025-07-09 14:46 ` [PATCH v4 3/7] test/operators: extend ternary tests to cover inet_ntoa*()s Alan Maguire
@ 2025-07-09 14:46 ` Alan Maguire
2025-07-21 20:24 ` [DTrace-devel] " Kris Van Hees
2025-07-09 14:46 ` [PATCH v4 5/7] tcp: new provider Alan Maguire
` (2 subsequent siblings)
6 siblings, 1 reply; 16+ messages in thread
From: Alan Maguire @ 2025-07-09 14:46 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] 16+ messages in thread* Re: [DTrace-devel] [PATCH v4 4/7] providers: move network-generic definitions to net.d
2025-07-09 14:46 ` [PATCH v4 4/7] providers: move network-generic definitions to net.d Alan Maguire
@ 2025-07-21 20:24 ` Kris Van Hees
0 siblings, 0 replies; 16+ messages in thread
From: Kris Van Hees @ 2025-07-21 20:24 UTC (permalink / raw)
To: Alan Maguire; +Cc: dtrace, dtrace-devel
On Wed, Jul 09, 2025 at 03:46:57PM +0100, Alan Maguire via DTrace-devel wrote:
> tcp.d and ip.d both need some of these generic definitions so move them
> to the net.d library they both depend on.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
> ---
> libdtrace/ip.d | 25 -------------------------
> libdtrace/net.d | 25 +++++++++++++++++++++++++
> 2 files changed, 25 insertions(+), 25 deletions(-)
>
> diff --git a/libdtrace/ip.d b/libdtrace/ip.d
> index f8b77f12..b498bc07 100644
> --- a/libdtrace/ip.d
> +++ b/libdtrace/ip.d
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/libdtrace/net.d b/libdtrace/net.d
> index 6ac34287..4c7bc61f 100644
> --- a/libdtrace/net.d
> +++ b/libdtrace/net.d
> @@ -25,6 +25,31 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> * with sending (outbound) or receiving (inbound).
> --
> 2.39.3
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel@oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v4 5/7] tcp: new provider
2025-07-09 14:46 [PATCH v4 0/7] DTrace TCP provider Alan Maguire
` (3 preceding siblings ...)
2025-07-09 14:46 ` [PATCH v4 4/7] providers: move network-generic definitions to net.d Alan Maguire
@ 2025-07-09 14:46 ` Alan Maguire
2025-07-09 14:46 ` [PATCH v4 6/7] dlibs: sync ip.d, net.d and tcp.d Alan Maguire
2025-07-09 14:47 ` [PATCH v4 7/7] unittest/tcp: update test.x Alan Maguire
6 siblings, 0 replies; 16+ messages in thread
From: Alan Maguire @ 2025-07-09 14:46 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] 16+ messages in thread* [PATCH v4 6/7] dlibs: sync ip.d, net.d and tcp.d
2025-07-09 14:46 [PATCH v4 0/7] DTrace TCP provider Alan Maguire
` (4 preceding siblings ...)
2025-07-09 14:46 ` [PATCH v4 5/7] tcp: new provider Alan Maguire
@ 2025-07-09 14:46 ` Alan Maguire
2025-07-21 22:27 ` [DTrace-devel] " Kris Van Hees
2025-07-09 14:47 ` [PATCH v4 7/7] unittest/tcp: update test.x Alan Maguire
6 siblings, 1 reply; 16+ messages in thread
From: Alan Maguire @ 2025-07-09 14:46 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] 16+ messages in thread* Re: [DTrace-devel] [PATCH v4 6/7] dlibs: sync ip.d, net.d and tcp.d
2025-07-09 14:46 ` [PATCH v4 6/7] dlibs: sync ip.d, net.d and tcp.d Alan Maguire
@ 2025-07-21 22:27 ` Kris Van Hees
0 siblings, 0 replies; 16+ messages in thread
From: Kris Van Hees @ 2025-07-21 22:27 UTC (permalink / raw)
To: Alan Maguire; +Cc: dtrace, dtrace-devel
On Wed, Jul 09, 2025 at 03:46:59PM +0100, Alan Maguire via DTrace-devel wrote:
> Sync dlibs with libdtrace versions.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
> ---
> dlibs/aarch64/5.11/ip.d | 27 +---------------
> dlibs/aarch64/5.11/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/5.11/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/5.12/ip.d | 27 +---------------
> dlibs/aarch64/5.12/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/5.12/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/5.14/ip.d | 27 +---------------
> dlibs/aarch64/5.14/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/5.14/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/5.16/ip.d | 27 +---------------
> dlibs/aarch64/5.16/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/5.16/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/5.2/ip.d | 27 +---------------
> dlibs/aarch64/5.2/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/5.2/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/5.6/ip.d | 27 +---------------
> dlibs/aarch64/5.6/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/5.6/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/6.1/ip.d | 27 +---------------
> dlibs/aarch64/6.1/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/6.1/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/6.10/ip.d | 27 +---------------
> dlibs/aarch64/6.10/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/6.10/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/5.11/ip.d | 27 +---------------
> dlibs/x86_64/5.11/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/5.11/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/5.12/ip.d | 27 +---------------
> dlibs/x86_64/5.12/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/5.12/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/5.14/ip.d | 27 +---------------
> dlibs/x86_64/5.14/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/5.14/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/5.16/ip.d | 27 +---------------
> dlibs/x86_64/5.16/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/5.16/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/5.2/ip.d | 27 +---------------
> dlibs/x86_64/5.2/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/5.2/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/5.6/ip.d | 27 +---------------
> dlibs/x86_64/5.6/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/5.6/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/6.1/ip.d | 27 +---------------
> dlibs/x86_64/6.1/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/6.1/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/6.10/ip.d | 27 +---------------
> dlibs/x86_64/6.10/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/6.10/tcp.d | 67 ++++++++++++++++++++++++----------------
> 48 files changed, 1120 insertions(+), 880 deletions(-)
>
> diff --git a/dlibs/aarch64/5.11/ip.d b/dlibs/aarch64/5.11/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.11/ip.d
> +++ b/dlibs/aarch64/5.11/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/5.11/net.d b/dlibs/aarch64/5.11/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.11/net.d
> +++ b/dlibs/aarch64/5.11/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/5.11/tcp.d b/dlibs/aarch64/5.11/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.11/tcp.d
> +++ b/dlibs/aarch64/5.11/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/5.12/ip.d b/dlibs/aarch64/5.12/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.12/ip.d
> +++ b/dlibs/aarch64/5.12/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/5.12/net.d b/dlibs/aarch64/5.12/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.12/net.d
> +++ b/dlibs/aarch64/5.12/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/5.12/tcp.d b/dlibs/aarch64/5.12/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.12/tcp.d
> +++ b/dlibs/aarch64/5.12/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/5.14/ip.d b/dlibs/aarch64/5.14/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.14/ip.d
> +++ b/dlibs/aarch64/5.14/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/5.14/net.d b/dlibs/aarch64/5.14/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.14/net.d
> +++ b/dlibs/aarch64/5.14/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/5.14/tcp.d b/dlibs/aarch64/5.14/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.14/tcp.d
> +++ b/dlibs/aarch64/5.14/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/5.16/ip.d b/dlibs/aarch64/5.16/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.16/ip.d
> +++ b/dlibs/aarch64/5.16/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/5.16/net.d b/dlibs/aarch64/5.16/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.16/net.d
> +++ b/dlibs/aarch64/5.16/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/5.16/tcp.d b/dlibs/aarch64/5.16/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.16/tcp.d
> +++ b/dlibs/aarch64/5.16/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/5.2/ip.d b/dlibs/aarch64/5.2/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.2/ip.d
> +++ b/dlibs/aarch64/5.2/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/5.2/net.d b/dlibs/aarch64/5.2/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.2/net.d
> +++ b/dlibs/aarch64/5.2/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/5.2/tcp.d b/dlibs/aarch64/5.2/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.2/tcp.d
> +++ b/dlibs/aarch64/5.2/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/5.6/ip.d b/dlibs/aarch64/5.6/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.6/ip.d
> +++ b/dlibs/aarch64/5.6/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/5.6/net.d b/dlibs/aarch64/5.6/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.6/net.d
> +++ b/dlibs/aarch64/5.6/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/5.6/tcp.d b/dlibs/aarch64/5.6/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.6/tcp.d
> +++ b/dlibs/aarch64/5.6/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/6.1/ip.d b/dlibs/aarch64/6.1/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/6.1/ip.d
> +++ b/dlibs/aarch64/6.1/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/6.1/net.d b/dlibs/aarch64/6.1/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/6.1/net.d
> +++ b/dlibs/aarch64/6.1/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/6.1/tcp.d b/dlibs/aarch64/6.1/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/6.1/tcp.d
> +++ b/dlibs/aarch64/6.1/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/6.10/ip.d b/dlibs/aarch64/6.10/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/6.10/ip.d
> +++ b/dlibs/aarch64/6.10/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/6.10/net.d b/dlibs/aarch64/6.10/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/6.10/net.d
> +++ b/dlibs/aarch64/6.10/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/6.10/tcp.d b/dlibs/aarch64/6.10/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/6.10/tcp.d
> +++ b/dlibs/aarch64/6.10/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/5.11/ip.d b/dlibs/x86_64/5.11/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.11/ip.d
> +++ b/dlibs/x86_64/5.11/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/5.11/net.d b/dlibs/x86_64/5.11/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.11/net.d
> +++ b/dlibs/x86_64/5.11/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/5.11/tcp.d b/dlibs/x86_64/5.11/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.11/tcp.d
> +++ b/dlibs/x86_64/5.11/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/5.12/ip.d b/dlibs/x86_64/5.12/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.12/ip.d
> +++ b/dlibs/x86_64/5.12/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/5.12/net.d b/dlibs/x86_64/5.12/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.12/net.d
> +++ b/dlibs/x86_64/5.12/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/5.12/tcp.d b/dlibs/x86_64/5.12/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.12/tcp.d
> +++ b/dlibs/x86_64/5.12/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/5.14/ip.d b/dlibs/x86_64/5.14/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.14/ip.d
> +++ b/dlibs/x86_64/5.14/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/5.14/net.d b/dlibs/x86_64/5.14/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.14/net.d
> +++ b/dlibs/x86_64/5.14/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/5.14/tcp.d b/dlibs/x86_64/5.14/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.14/tcp.d
> +++ b/dlibs/x86_64/5.14/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/5.16/ip.d b/dlibs/x86_64/5.16/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.16/ip.d
> +++ b/dlibs/x86_64/5.16/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/5.16/net.d b/dlibs/x86_64/5.16/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.16/net.d
> +++ b/dlibs/x86_64/5.16/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/5.16/tcp.d b/dlibs/x86_64/5.16/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.16/tcp.d
> +++ b/dlibs/x86_64/5.16/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/5.2/ip.d b/dlibs/x86_64/5.2/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.2/ip.d
> +++ b/dlibs/x86_64/5.2/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/5.2/net.d b/dlibs/x86_64/5.2/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.2/net.d
> +++ b/dlibs/x86_64/5.2/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/5.2/tcp.d b/dlibs/x86_64/5.2/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.2/tcp.d
> +++ b/dlibs/x86_64/5.2/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/5.6/ip.d b/dlibs/x86_64/5.6/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.6/ip.d
> +++ b/dlibs/x86_64/5.6/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/5.6/net.d b/dlibs/x86_64/5.6/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.6/net.d
> +++ b/dlibs/x86_64/5.6/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/5.6/tcp.d b/dlibs/x86_64/5.6/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.6/tcp.d
> +++ b/dlibs/x86_64/5.6/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/6.1/ip.d b/dlibs/x86_64/6.1/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/6.1/ip.d
> +++ b/dlibs/x86_64/6.1/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/6.1/net.d b/dlibs/x86_64/6.1/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/6.1/net.d
> +++ b/dlibs/x86_64/6.1/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/6.1/tcp.d b/dlibs/x86_64/6.1/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/6.1/tcp.d
> +++ b/dlibs/x86_64/6.1/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/6.10/ip.d b/dlibs/x86_64/6.10/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/6.10/ip.d
> +++ b/dlibs/x86_64/6.10/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/6.10/net.d b/dlibs/x86_64/6.10/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/6.10/net.d
> +++ b/dlibs/x86_64/6.10/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/6.10/tcp.d b/dlibs/x86_64/6.10/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/6.10/tcp.d
> +++ b/dlibs/x86_64/6.10/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> --
> 2.39.3
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel@oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v4 7/7] unittest/tcp: update test.x
2025-07-09 14:46 [PATCH v4 0/7] DTrace TCP provider Alan Maguire
` (5 preceding siblings ...)
2025-07-09 14:46 ` [PATCH v4 6/7] dlibs: sync ip.d, net.d and tcp.d Alan Maguire
@ 2025-07-09 14:47 ` Alan Maguire
2025-07-21 22:36 ` [DTrace-devel] " Kris Van Hees
6 siblings, 1 reply; 16+ messages in thread
From: Alan Maguire @ 2025-07-09 14:47 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] 16+ messages in thread* Re: [DTrace-devel] [PATCH v4 7/7] unittest/tcp: update test.x
2025-07-09 14:47 ` [PATCH v4 7/7] unittest/tcp: update test.x Alan Maguire
@ 2025-07-21 22:36 ` Kris Van Hees
0 siblings, 0 replies; 16+ messages in thread
From: Kris Van Hees @ 2025-07-21 22:36 UTC (permalink / raw)
To: Alan Maguire; +Cc: dtrace, dtrace-devel
On Wed, Jul 09, 2025 at 03:47:00PM +0100, Alan Maguire via DTrace-devel wrote:
> Now that the tcp provider is supported, update test.x.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
> ---
> test/unittest/tcp/test.x | 8 +-------
> 1 file changed, 1 insertion(+), 7 deletions(-)
>
> diff --git a/test/unittest/tcp/test.x b/test/unittest/tcp/test.x
> index e0ea2a5d..4de906a9 100755
> --- a/test/unittest/tcp/test.x
> +++ b/test/unittest/tcp/test.x
> @@ -1,16 +1,10 @@
> #!/bin/bash
> #
> # Oracle Linux DTrace.
> -# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
> +# Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
> # Licensed under the Universal Permissive License v 1.0 as shown at
> # http://oss.oracle.com/licenses/upl.
>
> -check_provider tcp
> -if (( $? != 0 )); then
> - echo "Could not load tcp provider"
> - exit 1
> -fi
> -
> if grep -qF ../ip/client.ip.pl $_test &&
> ! perl -MIO::Socket::IP -e 'exit(0);' 2>/dev/null; then
> echo "No IO::Socket::IP"
> --
> 2.39.3
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel@oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
^ permalink raw reply [flat|nested] 16+ messages in thread