public inbox for dtrace@lists.linux.dev
 help / color / mirror / Atom feed
* [PATCH v2 0/4] DTrace TCP provider
@ 2025-06-10 13:58 Alan Maguire
  2025-06-10 13:58 ` [PATCH v2 1/4] dtrace: move get_member() to dt_cg.c Alan Maguire
                   ` (4 more replies)
  0 siblings, 5 replies; 21+ messages in thread
From: Alan Maguire @ 2025-06-10 13:58 UTC (permalink / raw)
  To: dtrace; +Cc: dtrace-devel, Alan Maguire

This series is a first draft of TCP provider support, where the
probes are implemented via underlying fbt and sdt probes.

Due to the use of the sock/inet_sock_set_state tracepoint, intended
for ~5.15 kernels and later.  Tried replacing this with

fbt::tcp_set_state:entry

but this misses a few state transitions, so stuck with using
the tracepoint.

All tests under test/unittest/tcp pass unmodified on an upstream
(6.15) kernel and 5.15 UEK7U3 kernel.

It implements all documented TCP provider probes:

accept-established, accept-refused, connnect-request,
connect-established, connect-refused, receive, send,
state-change

Changes since RFC:

- fixed issues with test failures on UEK7 due to missing
  SYN_RCV state change
- moved get_member() to dt_cg.c (patch 1)

Alan Maguire (4):
  dtrace: move get_member() to dt_cg.c
  dt_impl: bump number of TSLOTS to 8
  dtrace: add tcp provider
  dtrace: sync dlibs with tcp.d, ip.d and net.d changes

 dlibs/aarch64/5.14/ip.d  |   1 -
 dlibs/aarch64/5.14/net.d |   6 +-
 dlibs/aarch64/5.14/tcp.d |  52 ++---
 dlibs/aarch64/5.16/ip.d  |   1 -
 dlibs/aarch64/5.16/net.d |   6 +-
 dlibs/aarch64/5.16/tcp.d |  52 ++---
 dlibs/aarch64/6.1/ip.d   |   1 -
 dlibs/aarch64/6.1/net.d  |   6 +-
 dlibs/aarch64/6.1/tcp.d  |  52 ++---
 dlibs/aarch64/6.10/ip.d  |   1 -
 dlibs/aarch64/6.10/net.d |   6 +-
 dlibs/aarch64/6.10/tcp.d |  52 ++---
 dlibs/x86_64/5.14/ip.d   |   1 -
 dlibs/x86_64/5.14/net.d  |   6 +-
 dlibs/x86_64/5.14/tcp.d  |  52 ++---
 dlibs/x86_64/5.16/ip.d   |   1 -
 dlibs/x86_64/5.16/net.d  |   6 +-
 dlibs/x86_64/5.16/tcp.d  |  52 ++---
 dlibs/x86_64/6.1/ip.d    |   1 -
 dlibs/x86_64/6.1/net.d   |   6 +-
 dlibs/x86_64/6.1/tcp.d   |  52 ++---
 dlibs/x86_64/6.10/ip.d   |   1 -
 dlibs/x86_64/6.10/net.d  |   6 +-
 dlibs/x86_64/6.10/tcp.d  |  52 ++---
 libdtrace/Build          |   2 +
 libdtrace/dt_cg.c        |  39 ++++
 libdtrace/dt_cg.h        |   2 +
 libdtrace/dt_impl.h      |   2 +-
 libdtrace/dt_prov_ip.c   |  45 +----
 libdtrace/dt_prov_tcp.c  | 405 +++++++++++++++++++++++++++++++++++++++
 libdtrace/dt_provider.c  |   1 +
 libdtrace/dt_provider.h  |   1 +
 libdtrace/ip.d           |   1 -
 libdtrace/net.d          |   6 +-
 libdtrace/tcp.d          |  52 ++---
 35 files changed, 761 insertions(+), 267 deletions(-)
 create mode 100644 libdtrace/dt_prov_tcp.c

-- 
2.39.3


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 1/4] dtrace: move get_member() to dt_cg.c
  2025-06-10 13:58 [PATCH v2 0/4] DTrace TCP provider Alan Maguire
@ 2025-06-10 13:58 ` Alan Maguire
  2025-07-01 18:23   ` Eugene Loh
  2025-06-10 13:58 ` [PATCH v2 2/4] dt_impl: bump number of TSLOTS to 8 Alan Maguire
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 21+ messages in thread
From: Alan Maguire @ 2025-06-10 13:58 UTC (permalink / raw)
  To: dtrace; +Cc: dtrace-devel, Alan Maguire

It will be used by both dt_prov_ip.c and dt_prov_tcp.c.

Signed-off-by: Alan Maguire <alan.maguire@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 c68a5d45..ad761d3b 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 fb26c125..c4aa610a 100644
--- a/libdtrace/dt_cg.h
+++ b/libdtrace/dt_cg.h
@@ -43,6 +43,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] 21+ messages in thread

* [PATCH v2 2/4] dt_impl: bump number of TSLOTS to 8
  2025-06-10 13:58 [PATCH v2 0/4] DTrace TCP provider Alan Maguire
  2025-06-10 13:58 ` [PATCH v2 1/4] dtrace: move get_member() to dt_cg.c Alan Maguire
@ 2025-06-10 13:58 ` Alan Maguire
  2025-07-01 18:31   ` Eugene Loh
  2025-06-10 13:58 ` [PATCH v2 3/4] dtrace: add tcp provider Alan Maguire
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 21+ messages in thread
From: Alan Maguire @ 2025-06-10 13:58 UTC (permalink / raw)
  To: dtrace; +Cc: dtrace-devel, Alan Maguire

Because of the complexity of the TCP translators, more tslots are
needed.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 libdtrace/dt_impl.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index 68fb8ec5..10424f9c 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -218,7 +218,7 @@ typedef struct dt_kern_path {
  * - cleanpath() holds a prepended '/' char, a string, an appended '/' char,
  *   and a terminating NUL char, or STRSZ + 3 chars altogether
  */
-#define DT_TSTRING_SLOTS	4
+#define DT_TSTRING_SLOTS	8
 #define DT_TSTRING_SIZE(dtp)	\
 		MAX(P2ROUNDUP((dtp)->dt_options[DTRACEOPT_STRSIZE] + 3, 8), \
 		    72)
-- 
2.39.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 3/4] dtrace: add tcp provider
  2025-06-10 13:58 [PATCH v2 0/4] DTrace TCP provider Alan Maguire
  2025-06-10 13:58 ` [PATCH v2 1/4] dtrace: move get_member() to dt_cg.c Alan Maguire
  2025-06-10 13:58 ` [PATCH v2 2/4] dt_impl: bump number of TSLOTS to 8 Alan Maguire
@ 2025-06-10 13:58 ` Alan Maguire
  2025-07-01 23:16   ` Eugene Loh
                     ` (2 more replies)
  2025-06-10 13:58 ` [PATCH v2 4/4] dtrace: sync dlibs with tcp.d, ip.d and net.d changes Alan Maguire
  2025-07-01 19:08 ` [PATCH v2 0/4] DTrace TCP provider Eugene Loh
  4 siblings, 3 replies; 21+ messages in thread
From: Alan Maguire @ 2025-06-10 13:58 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 | 405 ++++++++++++++++++++++++++++++++++++++++
 libdtrace/dt_provider.c |   1 +
 libdtrace/dt_provider.h |   1 +
 libdtrace/ip.d          |   1 -
 libdtrace/net.d         |   6 +-
 libdtrace/tcp.d         |  52 +++---
 7 files changed, 443 insertions(+), 25 deletions(-)
 create mode 100644 libdtrace/dt_prov_tcp.c

diff --git a/libdtrace/Build b/libdtrace/Build
index 7e6e8a38..a5439354 100644
--- a/libdtrace/Build
+++ b/libdtrace/Build
@@ -59,6 +59,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 \
@@ -117,6 +118,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..75e1e3a9
--- /dev/null
+++ b/libdtrace/dt_prov_tcp.c
@@ -0,0 +1,405 @@
+/*
+ * 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, "unsigned char", "int"} },
+	{ "accept-established", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
+	{ "accept-established", 7, { 7, 0, "int", "int" } },
+
+	{ "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, "unsigned char", "int"} },
+	{ "accept-refused", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
+	{ "accept-refused", 7, { 7, 0, "int", "int" } },
+
+	{ "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, "unsigned char", "int"} },
+	{ "connect-established", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
+	{ "connect-established", 7, { 7, 0, "int", "int" } },
+
+	{ "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, "unsigned char", "int"} },
+	{ "connect-refused", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
+	{ "connect-refused", 7, { 7, 0, "int", "int" } },
+
+	{ "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, "unsigned char", "int"} },
+	{ "connect-request", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
+	{ "connect-request", 7, { 7, 0, "int", "int" } },
+
+	{ "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, "unsigned char", "int"} },
+	{ "receive", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
+	{ "receive", 7, { 7, 0, "int", "int" } },
+
+	{ "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, "unsigned char", "int"} },
+	{ "send", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
+	{ "send", 7, { 7, 0, "int", "int" } },
+
+	{ "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, "void *", "void *" } },
+	{ "state-change", 6, { 6, 0, "struct sock *", "tcplsinfo_t *" } },
+	{ "state-change", 7, { 7, 0, "int", "int" } },
+
+	{ 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)
+{
+	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:
+	 *      args[0] = skb
+	 *      args[1] = sk
+	 *      args[2] = ip_hdr(skb) [if available]
+	 *      args[3] = sk [struct tcp_sock *]
+	 *      args[4] = tcp_hdr(skb)
+	 *      args[5] = sk->sk_state
+	 *      args[6] = sk->sk_state
+	 *      args[7] = NET_PROBE_INBOUND (0x1) | NET_PROBE_OUTBOUND (0x0)
+	 */
+
+	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 explicitly 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.
+		 */
+		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_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(skarg)));
+		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(6), 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));
+		emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(5), 0));
+
+		/* NET_PROBE_STATE */
+		emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(7),
+					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) {
+			/* NULL sk in arg1 not arg2 (we dont want ctl_sk) */
+			skarg = 1;
+			/* skb in arg2 not arg1 */
+			skbarg = 2;
+			have_iphdr = 1;
+			/* tcp hdr in ip_reply_arg * */
+			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(BPF_DW, BPF_REG_7, DMST_ARG(6), BPF_REG_0));
+	}
+	emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(7), 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 0c621197..798e67ee 100644
--- a/libdtrace/dt_provider.c
+++ b/libdtrace/dt_provider.c
@@ -41,6 +41,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 59a8d62e..4db89b45 100644
--- a/libdtrace/dt_provider.h
+++ b/libdtrace/dt_provider.h
@@ -87,6 +87,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 f8b77f12..d59bb436 100644
--- a/libdtrace/ip.d
+++ b/libdtrace/ip.d
@@ -51,7 +51,6 @@ inline int TCP_MIN_HEADER_LENGTH =	20;
  * 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;
 
 /*
diff --git a/libdtrace/net.d b/libdtrace/net.d
index 6ac34287..45b5cba3 100644
--- a/libdtrace/net.d
+++ b/libdtrace/net.d
@@ -25,9 +25,13 @@ typedef struct conninfo {
 	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
 } conninfo_t;
 
+typedef __be32 ipaddr_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 in 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..d4beea87 100644
--- a/libdtrace/tcp.d
+++ b/libdtrace/tcp.d
@@ -8,7 +8,6 @@
 #pragma D depends_on module vmlinux
 #pragma D depends_on library net.d
 #pragma D depends_on provider ip
-#pragma D depends_on provider tcp
 
 inline int TH_FIN =	0x01;
 inline int TH_SYN =	0x02;
@@ -60,7 +59,7 @@ typedef struct tcpinfo {
 	uint32_t tcp_seq;		/* sequence number */
 	uint32_t tcp_ack;		/* acknowledgment number */
 	uint8_t tcp_offset;		/* data offset, in bytes */
-	uint8_t tcp_flags;		/* flags */
+	uint16_t tcp_flags;		/* flags */
 	uint16_t tcp_window;		/* window size */
 	uint16_t tcp_checksum;		/* checksum */
 	uint16_t tcp_urgent;		/* urgent data pointer */
@@ -111,13 +110,16 @@ 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;
 };
 
+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:
@@ -158,47 +160,45 @@ 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) :
-	    "<unknown>";
-	tcps_state = arg6;
+	    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 = arg7 == NET_PROBE_STATE ? arg6 :
+	    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;
@@ -229,3 +229,9 @@ translator tcpsinfo_t < struct tcp_sock *T > {
 translator tcplsinfo_t < int I > {
 	tcps_state = I;
 };
+
+/* For tracepoint, the last state is in the sock state, next passed as arg6 */
+#pragma D binding "1.6.3" translator
+translator tcplsinfo_t < struct sock *S > {
+	tcps_state = S ? S->__sk_common.skc_state : 0;
+};
-- 
2.39.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 4/4] dtrace: sync dlibs with tcp.d, ip.d and net.d changes
  2025-06-10 13:58 [PATCH v2 0/4] DTrace TCP provider Alan Maguire
                   ` (2 preceding siblings ...)
  2025-06-10 13:58 ` [PATCH v2 3/4] dtrace: add tcp provider Alan Maguire
@ 2025-06-10 13:58 ` Alan Maguire
  2025-07-01 19:08 ` [PATCH v2 0/4] DTrace TCP provider Eugene Loh
  4 siblings, 0 replies; 21+ messages in thread
From: Alan Maguire @ 2025-06-10 13:58 UTC (permalink / raw)
  To: dtrace; +Cc: dtrace-devel, Alan Maguire

As part of adding tcp provider tcp.d, ip.d and net.d were changed;
sync the version-specific dlibs.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 dlibs/aarch64/5.14/ip.d  |  1 -
 dlibs/aarch64/5.14/net.d |  6 ++++-
 dlibs/aarch64/5.14/tcp.d | 52 ++++++++++++++++++++++------------------
 dlibs/aarch64/5.16/ip.d  |  1 -
 dlibs/aarch64/5.16/net.d |  6 ++++-
 dlibs/aarch64/5.16/tcp.d | 52 ++++++++++++++++++++++------------------
 dlibs/aarch64/6.1/ip.d   |  1 -
 dlibs/aarch64/6.1/net.d  |  6 ++++-
 dlibs/aarch64/6.1/tcp.d  | 52 ++++++++++++++++++++++------------------
 dlibs/aarch64/6.10/ip.d  |  1 -
 dlibs/aarch64/6.10/net.d |  6 ++++-
 dlibs/aarch64/6.10/tcp.d | 52 ++++++++++++++++++++++------------------
 dlibs/x86_64/5.14/ip.d   |  1 -
 dlibs/x86_64/5.14/net.d  |  6 ++++-
 dlibs/x86_64/5.14/tcp.d  | 52 ++++++++++++++++++++++------------------
 dlibs/x86_64/5.16/ip.d   |  1 -
 dlibs/x86_64/5.16/net.d  |  6 ++++-
 dlibs/x86_64/5.16/tcp.d  | 52 ++++++++++++++++++++++------------------
 dlibs/x86_64/6.1/ip.d    |  1 -
 dlibs/x86_64/6.1/net.d   |  6 ++++-
 dlibs/x86_64/6.1/tcp.d   | 52 ++++++++++++++++++++++------------------
 dlibs/x86_64/6.10/ip.d   |  1 -
 dlibs/x86_64/6.10/net.d  |  6 ++++-
 dlibs/x86_64/6.10/tcp.d  | 52 ++++++++++++++++++++++------------------
 24 files changed, 272 insertions(+), 200 deletions(-)

diff --git a/dlibs/aarch64/5.14/ip.d b/dlibs/aarch64/5.14/ip.d
index f8b77f12..d59bb436 100644
--- a/dlibs/aarch64/5.14/ip.d
+++ b/dlibs/aarch64/5.14/ip.d
@@ -51,7 +51,6 @@ inline int TCP_MIN_HEADER_LENGTH =	20;
  * 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;
 
 /*
diff --git a/dlibs/aarch64/5.14/net.d b/dlibs/aarch64/5.14/net.d
index 6ac34287..45b5cba3 100644
--- a/dlibs/aarch64/5.14/net.d
+++ b/dlibs/aarch64/5.14/net.d
@@ -25,9 +25,13 @@ typedef struct conninfo {
 	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
 } conninfo_t;
 
+typedef __be32 ipaddr_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 in 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..d4beea87 100644
--- a/dlibs/aarch64/5.14/tcp.d
+++ b/dlibs/aarch64/5.14/tcp.d
@@ -8,7 +8,6 @@
 #pragma D depends_on module vmlinux
 #pragma D depends_on library net.d
 #pragma D depends_on provider ip
-#pragma D depends_on provider tcp
 
 inline int TH_FIN =	0x01;
 inline int TH_SYN =	0x02;
@@ -60,7 +59,7 @@ typedef struct tcpinfo {
 	uint32_t tcp_seq;		/* sequence number */
 	uint32_t tcp_ack;		/* acknowledgment number */
 	uint8_t tcp_offset;		/* data offset, in bytes */
-	uint8_t tcp_flags;		/* flags */
+	uint16_t tcp_flags;		/* flags */
 	uint16_t tcp_window;		/* window size */
 	uint16_t tcp_checksum;		/* checksum */
 	uint16_t tcp_urgent;		/* urgent data pointer */
@@ -111,13 +110,16 @@ 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;
 };
 
+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:
@@ -158,47 +160,45 @@ 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) :
-	    "<unknown>";
-	tcps_state = arg6;
+	    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 = arg7 == NET_PROBE_STATE ? arg6 :
+	    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;
@@ -229,3 +229,9 @@ translator tcpsinfo_t < struct tcp_sock *T > {
 translator tcplsinfo_t < int I > {
 	tcps_state = I;
 };
+
+/* For tracepoint, the last state is in the sock state, next passed as arg6 */
+#pragma D binding "1.6.3" translator
+translator tcplsinfo_t < struct sock *S > {
+	tcps_state = S ? S->__sk_common.skc_state : 0;
+};
diff --git a/dlibs/aarch64/5.16/ip.d b/dlibs/aarch64/5.16/ip.d
index f8b77f12..d59bb436 100644
--- a/dlibs/aarch64/5.16/ip.d
+++ b/dlibs/aarch64/5.16/ip.d
@@ -51,7 +51,6 @@ inline int TCP_MIN_HEADER_LENGTH =	20;
  * 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;
 
 /*
diff --git a/dlibs/aarch64/5.16/net.d b/dlibs/aarch64/5.16/net.d
index 6ac34287..45b5cba3 100644
--- a/dlibs/aarch64/5.16/net.d
+++ b/dlibs/aarch64/5.16/net.d
@@ -25,9 +25,13 @@ typedef struct conninfo {
 	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
 } conninfo_t;
 
+typedef __be32 ipaddr_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 in 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..d4beea87 100644
--- a/dlibs/aarch64/5.16/tcp.d
+++ b/dlibs/aarch64/5.16/tcp.d
@@ -8,7 +8,6 @@
 #pragma D depends_on module vmlinux
 #pragma D depends_on library net.d
 #pragma D depends_on provider ip
-#pragma D depends_on provider tcp
 
 inline int TH_FIN =	0x01;
 inline int TH_SYN =	0x02;
@@ -60,7 +59,7 @@ typedef struct tcpinfo {
 	uint32_t tcp_seq;		/* sequence number */
 	uint32_t tcp_ack;		/* acknowledgment number */
 	uint8_t tcp_offset;		/* data offset, in bytes */
-	uint8_t tcp_flags;		/* flags */
+	uint16_t tcp_flags;		/* flags */
 	uint16_t tcp_window;		/* window size */
 	uint16_t tcp_checksum;		/* checksum */
 	uint16_t tcp_urgent;		/* urgent data pointer */
@@ -111,13 +110,16 @@ 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;
 };
 
+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:
@@ -158,47 +160,45 @@ 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) :
-	    "<unknown>";
-	tcps_state = arg6;
+	    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 = arg7 == NET_PROBE_STATE ? arg6 :
+	    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;
@@ -229,3 +229,9 @@ translator tcpsinfo_t < struct tcp_sock *T > {
 translator tcplsinfo_t < int I > {
 	tcps_state = I;
 };
+
+/* For tracepoint, the last state is in the sock state, next passed as arg6 */
+#pragma D binding "1.6.3" translator
+translator tcplsinfo_t < struct sock *S > {
+	tcps_state = S ? S->__sk_common.skc_state : 0;
+};
diff --git a/dlibs/aarch64/6.1/ip.d b/dlibs/aarch64/6.1/ip.d
index f8b77f12..d59bb436 100644
--- a/dlibs/aarch64/6.1/ip.d
+++ b/dlibs/aarch64/6.1/ip.d
@@ -51,7 +51,6 @@ inline int TCP_MIN_HEADER_LENGTH =	20;
  * 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;
 
 /*
diff --git a/dlibs/aarch64/6.1/net.d b/dlibs/aarch64/6.1/net.d
index 6ac34287..45b5cba3 100644
--- a/dlibs/aarch64/6.1/net.d
+++ b/dlibs/aarch64/6.1/net.d
@@ -25,9 +25,13 @@ typedef struct conninfo {
 	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
 } conninfo_t;
 
+typedef __be32 ipaddr_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 in 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..d4beea87 100644
--- a/dlibs/aarch64/6.1/tcp.d
+++ b/dlibs/aarch64/6.1/tcp.d
@@ -8,7 +8,6 @@
 #pragma D depends_on module vmlinux
 #pragma D depends_on library net.d
 #pragma D depends_on provider ip
-#pragma D depends_on provider tcp
 
 inline int TH_FIN =	0x01;
 inline int TH_SYN =	0x02;
@@ -60,7 +59,7 @@ typedef struct tcpinfo {
 	uint32_t tcp_seq;		/* sequence number */
 	uint32_t tcp_ack;		/* acknowledgment number */
 	uint8_t tcp_offset;		/* data offset, in bytes */
-	uint8_t tcp_flags;		/* flags */
+	uint16_t tcp_flags;		/* flags */
 	uint16_t tcp_window;		/* window size */
 	uint16_t tcp_checksum;		/* checksum */
 	uint16_t tcp_urgent;		/* urgent data pointer */
@@ -111,13 +110,16 @@ 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;
 };
 
+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:
@@ -158,47 +160,45 @@ 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) :
-	    "<unknown>";
-	tcps_state = arg6;
+	    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 = arg7 == NET_PROBE_STATE ? arg6 :
+	    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;
@@ -229,3 +229,9 @@ translator tcpsinfo_t < struct tcp_sock *T > {
 translator tcplsinfo_t < int I > {
 	tcps_state = I;
 };
+
+/* For tracepoint, the last state is in the sock state, next passed as arg6 */
+#pragma D binding "1.6.3" translator
+translator tcplsinfo_t < struct sock *S > {
+	tcps_state = S ? S->__sk_common.skc_state : 0;
+};
diff --git a/dlibs/aarch64/6.10/ip.d b/dlibs/aarch64/6.10/ip.d
index f8b77f12..d59bb436 100644
--- a/dlibs/aarch64/6.10/ip.d
+++ b/dlibs/aarch64/6.10/ip.d
@@ -51,7 +51,6 @@ inline int TCP_MIN_HEADER_LENGTH =	20;
  * 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;
 
 /*
diff --git a/dlibs/aarch64/6.10/net.d b/dlibs/aarch64/6.10/net.d
index 6ac34287..45b5cba3 100644
--- a/dlibs/aarch64/6.10/net.d
+++ b/dlibs/aarch64/6.10/net.d
@@ -25,9 +25,13 @@ typedef struct conninfo {
 	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
 } conninfo_t;
 
+typedef __be32 ipaddr_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 in 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..d4beea87 100644
--- a/dlibs/aarch64/6.10/tcp.d
+++ b/dlibs/aarch64/6.10/tcp.d
@@ -8,7 +8,6 @@
 #pragma D depends_on module vmlinux
 #pragma D depends_on library net.d
 #pragma D depends_on provider ip
-#pragma D depends_on provider tcp
 
 inline int TH_FIN =	0x01;
 inline int TH_SYN =	0x02;
@@ -60,7 +59,7 @@ typedef struct tcpinfo {
 	uint32_t tcp_seq;		/* sequence number */
 	uint32_t tcp_ack;		/* acknowledgment number */
 	uint8_t tcp_offset;		/* data offset, in bytes */
-	uint8_t tcp_flags;		/* flags */
+	uint16_t tcp_flags;		/* flags */
 	uint16_t tcp_window;		/* window size */
 	uint16_t tcp_checksum;		/* checksum */
 	uint16_t tcp_urgent;		/* urgent data pointer */
@@ -111,13 +110,16 @@ 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;
 };
 
+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:
@@ -158,47 +160,45 @@ 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) :
-	    "<unknown>";
-	tcps_state = arg6;
+	    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 = arg7 == NET_PROBE_STATE ? arg6 :
+	    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;
@@ -229,3 +229,9 @@ translator tcpsinfo_t < struct tcp_sock *T > {
 translator tcplsinfo_t < int I > {
 	tcps_state = I;
 };
+
+/* For tracepoint, the last state is in the sock state, next passed as arg6 */
+#pragma D binding "1.6.3" translator
+translator tcplsinfo_t < struct sock *S > {
+	tcps_state = S ? S->__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..d59bb436 100644
--- a/dlibs/x86_64/5.14/ip.d
+++ b/dlibs/x86_64/5.14/ip.d
@@ -51,7 +51,6 @@ inline int TCP_MIN_HEADER_LENGTH =	20;
  * 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;
 
 /*
diff --git a/dlibs/x86_64/5.14/net.d b/dlibs/x86_64/5.14/net.d
index 6ac34287..45b5cba3 100644
--- a/dlibs/x86_64/5.14/net.d
+++ b/dlibs/x86_64/5.14/net.d
@@ -25,9 +25,13 @@ typedef struct conninfo {
 	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
 } conninfo_t;
 
+typedef __be32 ipaddr_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 in 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..d4beea87 100644
--- a/dlibs/x86_64/5.14/tcp.d
+++ b/dlibs/x86_64/5.14/tcp.d
@@ -8,7 +8,6 @@
 #pragma D depends_on module vmlinux
 #pragma D depends_on library net.d
 #pragma D depends_on provider ip
-#pragma D depends_on provider tcp
 
 inline int TH_FIN =	0x01;
 inline int TH_SYN =	0x02;
@@ -60,7 +59,7 @@ typedef struct tcpinfo {
 	uint32_t tcp_seq;		/* sequence number */
 	uint32_t tcp_ack;		/* acknowledgment number */
 	uint8_t tcp_offset;		/* data offset, in bytes */
-	uint8_t tcp_flags;		/* flags */
+	uint16_t tcp_flags;		/* flags */
 	uint16_t tcp_window;		/* window size */
 	uint16_t tcp_checksum;		/* checksum */
 	uint16_t tcp_urgent;		/* urgent data pointer */
@@ -111,13 +110,16 @@ 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;
 };
 
+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:
@@ -158,47 +160,45 @@ 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) :
-	    "<unknown>";
-	tcps_state = arg6;
+	    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 = arg7 == NET_PROBE_STATE ? arg6 :
+	    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;
@@ -229,3 +229,9 @@ translator tcpsinfo_t < struct tcp_sock *T > {
 translator tcplsinfo_t < int I > {
 	tcps_state = I;
 };
+
+/* For tracepoint, the last state is in the sock state, next passed as arg6 */
+#pragma D binding "1.6.3" translator
+translator tcplsinfo_t < struct sock *S > {
+	tcps_state = S ? S->__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..d59bb436 100644
--- a/dlibs/x86_64/5.16/ip.d
+++ b/dlibs/x86_64/5.16/ip.d
@@ -51,7 +51,6 @@ inline int TCP_MIN_HEADER_LENGTH =	20;
  * 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;
 
 /*
diff --git a/dlibs/x86_64/5.16/net.d b/dlibs/x86_64/5.16/net.d
index 6ac34287..45b5cba3 100644
--- a/dlibs/x86_64/5.16/net.d
+++ b/dlibs/x86_64/5.16/net.d
@@ -25,9 +25,13 @@ typedef struct conninfo {
 	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
 } conninfo_t;
 
+typedef __be32 ipaddr_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 in 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..d4beea87 100644
--- a/dlibs/x86_64/5.16/tcp.d
+++ b/dlibs/x86_64/5.16/tcp.d
@@ -8,7 +8,6 @@
 #pragma D depends_on module vmlinux
 #pragma D depends_on library net.d
 #pragma D depends_on provider ip
-#pragma D depends_on provider tcp
 
 inline int TH_FIN =	0x01;
 inline int TH_SYN =	0x02;
@@ -60,7 +59,7 @@ typedef struct tcpinfo {
 	uint32_t tcp_seq;		/* sequence number */
 	uint32_t tcp_ack;		/* acknowledgment number */
 	uint8_t tcp_offset;		/* data offset, in bytes */
-	uint8_t tcp_flags;		/* flags */
+	uint16_t tcp_flags;		/* flags */
 	uint16_t tcp_window;		/* window size */
 	uint16_t tcp_checksum;		/* checksum */
 	uint16_t tcp_urgent;		/* urgent data pointer */
@@ -111,13 +110,16 @@ 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;
 };
 
+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:
@@ -158,47 +160,45 @@ 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) :
-	    "<unknown>";
-	tcps_state = arg6;
+	    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 = arg7 == NET_PROBE_STATE ? arg6 :
+	    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;
@@ -229,3 +229,9 @@ translator tcpsinfo_t < struct tcp_sock *T > {
 translator tcplsinfo_t < int I > {
 	tcps_state = I;
 };
+
+/* For tracepoint, the last state is in the sock state, next passed as arg6 */
+#pragma D binding "1.6.3" translator
+translator tcplsinfo_t < struct sock *S > {
+	tcps_state = S ? S->__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..d59bb436 100644
--- a/dlibs/x86_64/6.1/ip.d
+++ b/dlibs/x86_64/6.1/ip.d
@@ -51,7 +51,6 @@ inline int TCP_MIN_HEADER_LENGTH =	20;
  * 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;
 
 /*
diff --git a/dlibs/x86_64/6.1/net.d b/dlibs/x86_64/6.1/net.d
index 6ac34287..45b5cba3 100644
--- a/dlibs/x86_64/6.1/net.d
+++ b/dlibs/x86_64/6.1/net.d
@@ -25,9 +25,13 @@ typedef struct conninfo {
 	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
 } conninfo_t;
 
+typedef __be32 ipaddr_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 in 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..d4beea87 100644
--- a/dlibs/x86_64/6.1/tcp.d
+++ b/dlibs/x86_64/6.1/tcp.d
@@ -8,7 +8,6 @@
 #pragma D depends_on module vmlinux
 #pragma D depends_on library net.d
 #pragma D depends_on provider ip
-#pragma D depends_on provider tcp
 
 inline int TH_FIN =	0x01;
 inline int TH_SYN =	0x02;
@@ -60,7 +59,7 @@ typedef struct tcpinfo {
 	uint32_t tcp_seq;		/* sequence number */
 	uint32_t tcp_ack;		/* acknowledgment number */
 	uint8_t tcp_offset;		/* data offset, in bytes */
-	uint8_t tcp_flags;		/* flags */
+	uint16_t tcp_flags;		/* flags */
 	uint16_t tcp_window;		/* window size */
 	uint16_t tcp_checksum;		/* checksum */
 	uint16_t tcp_urgent;		/* urgent data pointer */
@@ -111,13 +110,16 @@ 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;
 };
 
+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:
@@ -158,47 +160,45 @@ 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) :
-	    "<unknown>";
-	tcps_state = arg6;
+	    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 = arg7 == NET_PROBE_STATE ? arg6 :
+	    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;
@@ -229,3 +229,9 @@ translator tcpsinfo_t < struct tcp_sock *T > {
 translator tcplsinfo_t < int I > {
 	tcps_state = I;
 };
+
+/* For tracepoint, the last state is in the sock state, next passed as arg6 */
+#pragma D binding "1.6.3" translator
+translator tcplsinfo_t < struct sock *S > {
+	tcps_state = S ? S->__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..d59bb436 100644
--- a/dlibs/x86_64/6.10/ip.d
+++ b/dlibs/x86_64/6.10/ip.d
@@ -51,7 +51,6 @@ inline int TCP_MIN_HEADER_LENGTH =	20;
  * 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;
 
 /*
diff --git a/dlibs/x86_64/6.10/net.d b/dlibs/x86_64/6.10/net.d
index 6ac34287..45b5cba3 100644
--- a/dlibs/x86_64/6.10/net.d
+++ b/dlibs/x86_64/6.10/net.d
@@ -25,9 +25,13 @@ typedef struct conninfo {
 	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
 } conninfo_t;
 
+typedef __be32 ipaddr_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 in 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..d4beea87 100644
--- a/dlibs/x86_64/6.10/tcp.d
+++ b/dlibs/x86_64/6.10/tcp.d
@@ -8,7 +8,6 @@
 #pragma D depends_on module vmlinux
 #pragma D depends_on library net.d
 #pragma D depends_on provider ip
-#pragma D depends_on provider tcp
 
 inline int TH_FIN =	0x01;
 inline int TH_SYN =	0x02;
@@ -60,7 +59,7 @@ typedef struct tcpinfo {
 	uint32_t tcp_seq;		/* sequence number */
 	uint32_t tcp_ack;		/* acknowledgment number */
 	uint8_t tcp_offset;		/* data offset, in bytes */
-	uint8_t tcp_flags;		/* flags */
+	uint16_t tcp_flags;		/* flags */
 	uint16_t tcp_window;		/* window size */
 	uint16_t tcp_checksum;		/* checksum */
 	uint16_t tcp_urgent;		/* urgent data pointer */
@@ -111,13 +110,16 @@ 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;
 };
 
+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:
@@ -158,47 +160,45 @@ 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) :
-	    "<unknown>";
-	tcps_state = arg6;
+	    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 = arg7 == NET_PROBE_STATE ? arg6 :
+	    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;
@@ -229,3 +229,9 @@ translator tcpsinfo_t < struct tcp_sock *T > {
 translator tcplsinfo_t < int I > {
 	tcps_state = I;
 };
+
+/* For tracepoint, the last state is in the sock state, next passed as arg6 */
+#pragma D binding "1.6.3" translator
+translator tcplsinfo_t < struct sock *S > {
+	tcps_state = S ? S->__sk_common.skc_state : 0;
+};
-- 
2.39.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 1/4] dtrace: move get_member() to dt_cg.c
  2025-06-10 13:58 ` [PATCH v2 1/4] dtrace: move get_member() to dt_cg.c Alan Maguire
@ 2025-07-01 18:23   ` Eugene Loh
  0 siblings, 0 replies; 21+ messages in thread
From: Eugene Loh @ 2025-07-01 18:23 UTC (permalink / raw)
  To: Alan Maguire, dtrace; +Cc: dtrace-devel

Reviewed-by: Eugene Loh <eugene.loh@oracle.com>

btw...

On 6/10/25 09:58, Alan Maguire wrote:
> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> @@ -1901,6 +1901,45 @@ dt_cg_ctf_offsetof(const char *structname, const char *membername,
> +/*
> + * Retrieve the value of a member in a given struct.
> + *
> + * Entry:
> + *      reg = TYPE *ptr
> + *
> + * Return:
> + *      %r0 = ptr->member
> + * Clobbers:
> + *      %r1 .. %r5
> + */

The dominant (but admittedly not consistent) style of this file is to 
indent with a tab.  E.g., "^ *\treg = ...".  Clearly, we have not 
established a consistent style.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/4] dt_impl: bump number of TSLOTS to 8
  2025-06-10 13:58 ` [PATCH v2 2/4] dt_impl: bump number of TSLOTS to 8 Alan Maguire
@ 2025-07-01 18:31   ` Eugene Loh
  2025-07-02 14:52     ` Alan Maguire
  0 siblings, 1 reply; 21+ messages in thread
From: Eugene Loh @ 2025-07-01 18:31 UTC (permalink / raw)
  To: Alan Maguire, dtrace; +Cc: dtrace-devel

Very simple patch, but one key problem.  The changed line is preceded by 
a huge comment block that goes to excruciating pains to explain why the 
value should be 4.  So, I'm fine with the change itself and I think the 
"Because of the complexity..." sentence in the commit message can be 
dropped, but the comment block in the file needs to be updated to 
explain (with corresponding pains?) why we need 8 (not 9, not 7).

On 6/10/25 09:58, Alan Maguire wrote:
> Because of the complexity of the TCP translators, more tslots are
> needed.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
>   libdtrace/dt_impl.h | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
> index 68fb8ec5..10424f9c 100644
> --- a/libdtrace/dt_impl.h
> +++ b/libdtrace/dt_impl.h
> @@ -218,7 +218,7 @@ typedef struct dt_kern_path {
>    * - cleanpath() holds a prepended '/' char, a string, an appended '/' char,
>    *   and a terminating NUL char, or STRSZ + 3 chars altogether
>    */
> -#define DT_TSTRING_SLOTS	4
> +#define DT_TSTRING_SLOTS	8
>   #define DT_TSTRING_SIZE(dtp)	\
>   		MAX(P2ROUNDUP((dtp)->dt_options[DTRACEOPT_STRSIZE] + 3, 8), \
>   		    72)

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 0/4] DTrace TCP provider
  2025-06-10 13:58 [PATCH v2 0/4] DTrace TCP provider Alan Maguire
                   ` (3 preceding siblings ...)
  2025-06-10 13:58 ` [PATCH v2 4/4] dtrace: sync dlibs with tcp.d, ip.d and net.d changes Alan Maguire
@ 2025-07-01 19:08 ` Eugene Loh
  2025-07-01 19:27   ` Kris Van Hees
  4 siblings, 1 reply; 21+ messages in thread
From: Eugene Loh @ 2025-07-01 19:08 UTC (permalink / raw)
  To: Alan Maguire, dtrace; +Cc: dtrace-devel

Incidentally, looking at the subject lines for the patch series, I see:

         dtrace: move get_member() to dt_cg.c
         dt_impl: bump number of TSLOTS to 8
         dtrace: add tcp provider
         dtrace: sync dlibs with tcp.d, ip.d and net.d changes

That is, each line is prefixed with a component name.  We do that a lot 
for "test:", but otherwise the practice seems to depend on... the patch 
author?  Anyhow, I claim "dtrace:" is not very useful.  For patch 1, all 
the prefix is saying is "there is movement in the dtrace code base."  I 
think the subject would be more direct without the prefix.  Same for 
patch 2.  For patch 3, if one wanted to use a component prefix, I'd 
think one would use "tcp:".

Historically, it looks like we've added new providers with subject lines 
like this:

     rawfbt: new provider
     Implement the io provider
     Implement the ip provider
     Implement the lockstat provider
     Implement the sched provider (first part)
     Implement the proc provider
     provider: Implement a rawtp provider
     Add a CPC provider
     PID provider implementation
     Add a profile provider
     Implement BEGIN and END probes for the dtrace provider
     Added IO provider support for 4.14 kernels

Everyone has their own style.  Shrug.  No big deal.

Anyhow, personally, I think the prefixes don't add much, especially for 
"dtrace:".  My suggestion is to drop the prefixes, but... your call.

On 6/10/25 09:58, Alan Maguire wrote:

> This series is a first draft of TCP provider support, where the
> probes are implemented via underlying fbt and sdt probes.
>
> Due to the use of the sock/inet_sock_set_state tracepoint, intended
> for ~5.15 kernels and later.  Tried replacing this with
>
> fbt::tcp_set_state:entry
>
> but this misses a few state transitions, so stuck with using
> the tracepoint.
>
> All tests under test/unittest/tcp pass unmodified on an upstream
> (6.15) kernel and 5.15 UEK7U3 kernel.
>
> It implements all documented TCP provider probes:
>
> accept-established, accept-refused, connnect-request,
> connect-established, connect-refused, receive, send,
> state-change
>
> Changes since RFC:
>
> - fixed issues with test failures on UEK7 due to missing
>    SYN_RCV state change
> - moved get_member() to dt_cg.c (patch 1)
>
> Alan Maguire (4):
>    dtrace: move get_member() to dt_cg.c
>    dt_impl: bump number of TSLOTS to 8
>    dtrace: add tcp provider
>    dtrace: sync dlibs with tcp.d, ip.d and net.d changes
>
>   dlibs/aarch64/5.14/ip.d  |   1 -
>   dlibs/aarch64/5.14/net.d |   6 +-
>   dlibs/aarch64/5.14/tcp.d |  52 ++---
>   dlibs/aarch64/5.16/ip.d  |   1 -
>   dlibs/aarch64/5.16/net.d |   6 +-
>   dlibs/aarch64/5.16/tcp.d |  52 ++---
>   dlibs/aarch64/6.1/ip.d   |   1 -
>   dlibs/aarch64/6.1/net.d  |   6 +-
>   dlibs/aarch64/6.1/tcp.d  |  52 ++---
>   dlibs/aarch64/6.10/ip.d  |   1 -
>   dlibs/aarch64/6.10/net.d |   6 +-
>   dlibs/aarch64/6.10/tcp.d |  52 ++---
>   dlibs/x86_64/5.14/ip.d   |   1 -
>   dlibs/x86_64/5.14/net.d  |   6 +-
>   dlibs/x86_64/5.14/tcp.d  |  52 ++---
>   dlibs/x86_64/5.16/ip.d   |   1 -
>   dlibs/x86_64/5.16/net.d  |   6 +-
>   dlibs/x86_64/5.16/tcp.d  |  52 ++---
>   dlibs/x86_64/6.1/ip.d    |   1 -
>   dlibs/x86_64/6.1/net.d   |   6 +-
>   dlibs/x86_64/6.1/tcp.d   |  52 ++---
>   dlibs/x86_64/6.10/ip.d   |   1 -
>   dlibs/x86_64/6.10/net.d  |   6 +-
>   dlibs/x86_64/6.10/tcp.d  |  52 ++---
>   libdtrace/Build          |   2 +
>   libdtrace/dt_cg.c        |  39 ++++
>   libdtrace/dt_cg.h        |   2 +
>   libdtrace/dt_impl.h      |   2 +-
>   libdtrace/dt_prov_ip.c   |  45 +----
>   libdtrace/dt_prov_tcp.c  | 405 +++++++++++++++++++++++++++++++++++++++
>   libdtrace/dt_provider.c  |   1 +
>   libdtrace/dt_provider.h  |   1 +
>   libdtrace/ip.d           |   1 -
>   libdtrace/net.d          |   6 +-
>   libdtrace/tcp.d          |  52 ++---
>   35 files changed, 761 insertions(+), 267 deletions(-)
>   create mode 100644 libdtrace/dt_prov_tcp.c
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 0/4] DTrace TCP provider
  2025-07-01 19:08 ` [PATCH v2 0/4] DTrace TCP provider Eugene Loh
@ 2025-07-01 19:27   ` Kris Van Hees
  2025-07-02 14:52     ` Alan Maguire
  0 siblings, 1 reply; 21+ messages in thread
From: Kris Van Hees @ 2025-07-01 19:27 UTC (permalink / raw)
  To: Eugene Loh; +Cc: Alan Maguire, dtrace, dtrace-devel

On Tue, Jul 01, 2025 at 03:08:59PM -0400, Eugene Loh wrote:
> Incidentally, looking at the subject lines for the patch series, I see:
> 
>         dtrace: move get_member() to dt_cg.c
>         dt_impl: bump number of TSLOTS to 8
>         dtrace: add tcp provider
>         dtrace: sync dlibs with tcp.d, ip.d and net.d changes

I would suggest:

	cg: move get_member() to dt_cg.c
	cg: bump number of TSLOTS to 8
	tcp: new provider -or- Implement the tcp provider
	dlibs: sync dlibs with tcp.d, ip.d and net.d changes

> That is, each line is prefixed with a component name.  We do that a lot for
> "test:", but otherwise the practice seems to depend on... the patch author? 
> Anyhow, I claim "dtrace:" is not very useful.  For patch 1, all the prefix
> is saying is "there is movement in the dtrace code base."  I think the
> subject would be more direct without the prefix.  Same for patch 2.  For
> patch 3, if one wanted to use a component prefix, I'd think one would use
> "tcp:".
> 
> Historically, it looks like we've added new providers with subject lines
> like this:
> 
>     rawfbt: new provider
>     Implement the io provider
>     Implement the ip provider
>     Implement the lockstat provider
>     Implement the sched provider (first part)
>     Implement the proc provider
>     provider: Implement a rawtp provider
>     Add a CPC provider
>     PID provider implementation
>     Add a profile provider
>     Implement BEGIN and END probes for the dtrace provider
>     Added IO provider support for 4.14 kernels
> 
> Everyone has their own style.  Shrug.  No big deal.
> 
> Anyhow, personally, I think the prefixes don't add much, especially for
> "dtrace:".  My suggestion is to drop the prefixes, but... your call.
> 
> On 6/10/25 09:58, Alan Maguire wrote:
> 
> > This series is a first draft of TCP provider support, where the
> > probes are implemented via underlying fbt and sdt probes.
> > 
> > Due to the use of the sock/inet_sock_set_state tracepoint, intended
> > for ~5.15 kernels and later.  Tried replacing this with
> > 
> > fbt::tcp_set_state:entry
> > 
> > but this misses a few state transitions, so stuck with using
> > the tracepoint.
> > 
> > All tests under test/unittest/tcp pass unmodified on an upstream
> > (6.15) kernel and 5.15 UEK7U3 kernel.
> > 
> > It implements all documented TCP provider probes:
> > 
> > accept-established, accept-refused, connnect-request,
> > connect-established, connect-refused, receive, send,
> > state-change
> > 
> > Changes since RFC:
> > 
> > - fixed issues with test failures on UEK7 due to missing
> >    SYN_RCV state change
> > - moved get_member() to dt_cg.c (patch 1)
> > 
> > Alan Maguire (4):
> >    dtrace: move get_member() to dt_cg.c
> >    dt_impl: bump number of TSLOTS to 8
> >    dtrace: add tcp provider
> >    dtrace: sync dlibs with tcp.d, ip.d and net.d changes
> > 
> >   dlibs/aarch64/5.14/ip.d  |   1 -
> >   dlibs/aarch64/5.14/net.d |   6 +-
> >   dlibs/aarch64/5.14/tcp.d |  52 ++---
> >   dlibs/aarch64/5.16/ip.d  |   1 -
> >   dlibs/aarch64/5.16/net.d |   6 +-
> >   dlibs/aarch64/5.16/tcp.d |  52 ++---
> >   dlibs/aarch64/6.1/ip.d   |   1 -
> >   dlibs/aarch64/6.1/net.d  |   6 +-
> >   dlibs/aarch64/6.1/tcp.d  |  52 ++---
> >   dlibs/aarch64/6.10/ip.d  |   1 -
> >   dlibs/aarch64/6.10/net.d |   6 +-
> >   dlibs/aarch64/6.10/tcp.d |  52 ++---
> >   dlibs/x86_64/5.14/ip.d   |   1 -
> >   dlibs/x86_64/5.14/net.d  |   6 +-
> >   dlibs/x86_64/5.14/tcp.d  |  52 ++---
> >   dlibs/x86_64/5.16/ip.d   |   1 -
> >   dlibs/x86_64/5.16/net.d  |   6 +-
> >   dlibs/x86_64/5.16/tcp.d  |  52 ++---
> >   dlibs/x86_64/6.1/ip.d    |   1 -
> >   dlibs/x86_64/6.1/net.d   |   6 +-
> >   dlibs/x86_64/6.1/tcp.d   |  52 ++---
> >   dlibs/x86_64/6.10/ip.d   |   1 -
> >   dlibs/x86_64/6.10/net.d  |   6 +-
> >   dlibs/x86_64/6.10/tcp.d  |  52 ++---
> >   libdtrace/Build          |   2 +
> >   libdtrace/dt_cg.c        |  39 ++++
> >   libdtrace/dt_cg.h        |   2 +
> >   libdtrace/dt_impl.h      |   2 +-
> >   libdtrace/dt_prov_ip.c   |  45 +----
> >   libdtrace/dt_prov_tcp.c  | 405 +++++++++++++++++++++++++++++++++++++++
> >   libdtrace/dt_provider.c  |   1 +
> >   libdtrace/dt_provider.h  |   1 +
> >   libdtrace/ip.d           |   1 -
> >   libdtrace/net.d          |   6 +-
> >   libdtrace/tcp.d          |  52 ++---
> >   35 files changed, 761 insertions(+), 267 deletions(-)
> >   create mode 100644 libdtrace/dt_prov_tcp.c
> > 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/4] dtrace: add tcp provider
  2025-06-10 13:58 ` [PATCH v2 3/4] dtrace: add tcp provider Alan Maguire
@ 2025-07-01 23:16   ` Eugene Loh
  2025-07-02 15:06     ` Alan Maguire
  2025-07-03 19:55   ` Eugene Loh
  2025-07-07 18:44   ` Kris Van Hees
  2 siblings, 1 reply; 21+ messages in thread
From: Eugene Loh @ 2025-07-01 23:16 UTC (permalink / raw)
  To: Alan Maguire, dtrace; +Cc: dtrace-devel

I'll try reading this some more, but there is much I do not understand.  
For now:

I think tcp.d needs an updated Copyright year.  Also, there seems to be 
a missing ">";  check "<unknown" in the tcps_raddr assignment.

With this patch, should we also git rm test/unittest/tcp/test.x?

On most VMs,
     test/unittest/tcp/tst.ipv4remotetcp.sh
     test/unittest/tcp/tst.ipv4remotetcpstate.sh
xfail due to missing remote.  Are we okay with "shrugging our shoulders" 
like that?

Meanwhile, my one non-OCI VM ran those tests.  The first test passes.  
The second one consistently reports
     -tcp:::state-change to time-wait - yes
     +tcp:::state-change to time-wait - no
and occasionally reports stuff like
     dtrace: error in dt_clause_2 for probe ID 4976 (tcp:vmlinux::send): 
invalid address (0x1fc0c0000000000) at BPF pc 287
     dtrace: error in dt_clause_2 for probe ID 4976 (tcp:vmlinux::send): 
invalid address (0x225b80000000000) at BPF pc 287

The non-remote tests fail on OL8 UEK6 (x86 and arm).
     dtrace: failed to compile script /dev/stdin:
     ".../build/dlibs/5.2/tcp.d", line 177: failed to resolve type of 
inet_ntoa arg#1 (ipaddr_t *):
     Unknown type name

The probe names are
     tcp:ip:*:*        Solaris
     tcp:vmlinux:*:*   DTv1
     tcp:vmlinux::*    with this patch (that is, no more function)
I guess precedents have already been set for other SDT providers;  so, 
okay.  Just noting for my own sake.

Meanwhile, the typed args[] have changed in number and type from Solaris 
to DTv1 to this patch.  Does that merit discussion?

On 6/10/25 09:58, Alan Maguire wrote:
> 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 | 405 ++++++++++++++++++++++++++++++++++++++++
>   libdtrace/dt_provider.c |   1 +
>   libdtrace/dt_provider.h |   1 +
>   libdtrace/ip.d          |   1 -
>   libdtrace/net.d         |   6 +-
>   libdtrace/tcp.d         |  52 +++---
>   7 files changed, 443 insertions(+), 25 deletions(-)
>   create mode 100644 libdtrace/dt_prov_tcp.c
>
> diff --git a/libdtrace/Build b/libdtrace/Build
> index 7e6e8a38..a5439354 100644
> --- a/libdtrace/Build
> +++ b/libdtrace/Build
> @@ -59,6 +59,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 \
> @@ -117,6 +118,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..75e1e3a9
> --- /dev/null
> +++ b/libdtrace/dt_prov_tcp.c
> @@ -0,0 +1,405 @@
> +/*
> + * 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, "unsigned char", "int"} },
> +	{ "accept-established", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
> +	{ "accept-established", 7, { 7, 0, "int", "int" } },
> +
> +	{ "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, "unsigned char", "int"} },
> +	{ "accept-refused", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
> +	{ "accept-refused", 7, { 7, 0, "int", "int" } },
> +
> +	{ "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, "unsigned char", "int"} },
> +	{ "connect-established", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
> +	{ "connect-established", 7, { 7, 0, "int", "int" } },
> +
> +	{ "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, "unsigned char", "int"} },
> +	{ "connect-refused", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
> +	{ "connect-refused", 7, { 7, 0, "int", "int" } },
> +
> +	{ "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, "unsigned char", "int"} },
> +	{ "connect-request", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
> +	{ "connect-request", 7, { 7, 0, "int", "int" } },
> +
> +	{ "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, "unsigned char", "int"} },
> +	{ "receive", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
> +	{ "receive", 7, { 7, 0, "int", "int" } },
> +
> +	{ "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, "unsigned char", "int"} },
> +	{ "send", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
> +	{ "send", 7, { 7, 0, "int", "int" } },
> +
> +	{ "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, "void *", "void *" } },
> +	{ "state-change", 6, { 6, 0, "struct sock *", "tcplsinfo_t *" } },
> +	{ "state-change", 7, { 7, 0, "int", "int" } },
> +
> +	{ 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)
> +{
> +	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:
> +	 *      args[0] = skb
> +	 *      args[1] = sk
> +	 *      args[2] = ip_hdr(skb) [if available]
> +	 *      args[3] = sk [struct tcp_sock *]
> +	 *      args[4] = tcp_hdr(skb)
> +	 *      args[5] = sk->sk_state
> +	 *      args[6] = sk->sk_state
> +	 *      args[7] = NET_PROBE_INBOUND (0x1) | NET_PROBE_OUTBOUND (0x0)
> +	 */
> +
> +	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 explicitly 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.
> +		 */
> +		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_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(skarg)));
> +		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(6), 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));
> +		emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(5), 0));
> +
> +		/* NET_PROBE_STATE */
> +		emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(7),
> +					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) {
> +			/* NULL sk in arg1 not arg2 (we dont want ctl_sk) */
> +			skarg = 1;
> +			/* skb in arg2 not arg1 */
> +			skbarg = 2;
> +			have_iphdr = 1;
> +			/* tcp hdr in ip_reply_arg * */
> +			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(BPF_DW, BPF_REG_7, DMST_ARG(6), BPF_REG_0));
> +	}
> +	emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(7), 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 0c621197..798e67ee 100644
> --- a/libdtrace/dt_provider.c
> +++ b/libdtrace/dt_provider.c
> @@ -41,6 +41,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 59a8d62e..4db89b45 100644
> --- a/libdtrace/dt_provider.h
> +++ b/libdtrace/dt_provider.h
> @@ -87,6 +87,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 f8b77f12..d59bb436 100644
> --- a/libdtrace/ip.d
> +++ b/libdtrace/ip.d
> @@ -51,7 +51,6 @@ inline int TCP_MIN_HEADER_LENGTH =	20;
>    * 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;
>   
>   /*
> diff --git a/libdtrace/net.d b/libdtrace/net.d
> index 6ac34287..45b5cba3 100644
> --- a/libdtrace/net.d
> +++ b/libdtrace/net.d
> @@ -25,9 +25,13 @@ typedef struct conninfo {
>   	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>   } conninfo_t;
>   
> +typedef __be32 ipaddr_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 in 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..d4beea87 100644
> --- a/libdtrace/tcp.d
> +++ b/libdtrace/tcp.d
> @@ -8,7 +8,6 @@
>   #pragma D depends_on module vmlinux
>   #pragma D depends_on library net.d
>   #pragma D depends_on provider ip
> -#pragma D depends_on provider tcp
>   
>   inline int TH_FIN =	0x01;
>   inline int TH_SYN =	0x02;
> @@ -60,7 +59,7 @@ typedef struct tcpinfo {
>   	uint32_t tcp_seq;		/* sequence number */
>   	uint32_t tcp_ack;		/* acknowledgment number */
>   	uint8_t tcp_offset;		/* data offset, in bytes */
> -	uint8_t tcp_flags;		/* flags */
> +	uint16_t tcp_flags;		/* flags */
>   	uint16_t tcp_window;		/* window size */
>   	uint16_t tcp_checksum;		/* checksum */
>   	uint16_t tcp_urgent;		/* urgent data pointer */
> @@ -111,13 +110,16 @@ 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;
>   };
>   
> +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:
> @@ -158,47 +160,45 @@ 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) :
> -	    "<unknown>";
> -	tcps_state = arg6;
> +	    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 = arg7 == NET_PROBE_STATE ? arg6 :
> +	    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;
> @@ -229,3 +229,9 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>   translator tcplsinfo_t < int I > {
>   	tcps_state = I;
>   };
> +
> +/* For tracepoint, the last state is in the sock state, next passed as arg6 */
> +#pragma D binding "1.6.3" translator
> +translator tcplsinfo_t < struct sock *S > {
> +	tcps_state = S ? S->__sk_common.skc_state : 0;
> +};

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/4] dt_impl: bump number of TSLOTS to 8
  2025-07-01 18:31   ` Eugene Loh
@ 2025-07-02 14:52     ` Alan Maguire
  2025-07-02 20:22       ` Eugene Loh
  0 siblings, 1 reply; 21+ messages in thread
From: Alan Maguire @ 2025-07-02 14:52 UTC (permalink / raw)
  To: Eugene Loh, dtrace; +Cc: dtrace-devel

On 01/07/2025 19:31, Eugene Loh wrote:
> Very simple patch, but one key problem.  The changed line is preceded by
> a huge comment block that goes to excruciating pains to explain why the
> value should be 4.  So, I'm fine with the change itself and I think the
> "Because of the complexity..." sentence in the commit message can be
> dropped, but the comment block in the file needs to be updated to
> explain (with corresponding pains?) why we need 8 (not 9, not 7).
>

Ah good catch, forgot to do this. I actually think 6 is enough.
The additional seems to be a result of the various complex inet_ntoa*()
calls in the tcp.d translators; some have ternary operators with
multiple inet_ntoa*()s. The origin appears to be dt_cg_ternary_op() and
that has the comment:

        /*
         * Strings complicate things a bit because dn_left and dn_right
might
         * actually be temporary strings (tstring) *and* in different slots.
         * We need to allocate a new tstring to hold the result, and
copy the
         * value into the new tstring (and free any tstrings in dn_left and
         * dn_right).
         */

So if we have a left and right ternary and both allocate 3 tstring slots
for inet_ntoa*()s that means 3*2 = 6 tstring slots should actually be
enough. Additional testing suggests that is so; does the above make
sense or are there additional things to consider here? I'll update the
next version with a comment describing the above anyhow. Thanks for the
review!

Alan


> On 6/10/25 09:58, Alan Maguire wrote:
>> Because of the complexity of the TCP translators, more tslots are
>> needed.
>>
>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>> ---
>>   libdtrace/dt_impl.h | 2 +-
>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
>> index 68fb8ec5..10424f9c 100644
>> --- a/libdtrace/dt_impl.h
>> +++ b/libdtrace/dt_impl.h
>> @@ -218,7 +218,7 @@ typedef struct dt_kern_path {
>>    * - cleanpath() holds a prepended '/' char, a string, an appended
>> '/' char,
>>    *   and a terminating NUL char, or STRSZ + 3 chars altogether
>>    */
>> -#define DT_TSTRING_SLOTS    4
>> +#define DT_TSTRING_SLOTS    8
>>   #define DT_TSTRING_SIZE(dtp)    \
>>           MAX(P2ROUNDUP((dtp)->dt_options[DTRACEOPT_STRSIZE] + 3, 8), \
>>               72)


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 0/4] DTrace TCP provider
  2025-07-01 19:27   ` Kris Van Hees
@ 2025-07-02 14:52     ` Alan Maguire
  0 siblings, 0 replies; 21+ messages in thread
From: Alan Maguire @ 2025-07-02 14:52 UTC (permalink / raw)
  To: Kris Van Hees, Eugene Loh; +Cc: dtrace, dtrace-devel

On 01/07/2025 20:27, Kris Van Hees wrote:
> On Tue, Jul 01, 2025 at 03:08:59PM -0400, Eugene Loh wrote:
>> Incidentally, looking at the subject lines for the patch series, I see:
>>
>>         dtrace: move get_member() to dt_cg.c
>>         dt_impl: bump number of TSLOTS to 8
>>         dtrace: add tcp provider
>>         dtrace: sync dlibs with tcp.d, ip.d and net.d changes
> 
> I would suggest:
> 
> 	cg: move get_member() to dt_cg.c
> 	cg: bump number of TSLOTS to 8
> 	tcp: new provider -or- Implement the tcp provider
> 	dlibs: sync dlibs with tcp.d, ip.d and net.d changes
>

perfect, thanks!

>> That is, each line is prefixed with a component name.  We do that a lot for
>> "test:", but otherwise the practice seems to depend on... the patch author? 
>> Anyhow, I claim "dtrace:" is not very useful.  For patch 1, all the prefix
>> is saying is "there is movement in the dtrace code base."  I think the
>> subject would be more direct without the prefix.  Same for patch 2.  For
>> patch 3, if one wanted to use a component prefix, I'd think one would use
>> "tcp:".
>>
>> Historically, it looks like we've added new providers with subject lines
>> like this:
>>
>>     rawfbt: new provider
>>     Implement the io provider
>>     Implement the ip provider
>>     Implement the lockstat provider
>>     Implement the sched provider (first part)
>>     Implement the proc provider
>>     provider: Implement a rawtp provider
>>     Add a CPC provider
>>     PID provider implementation
>>     Add a profile provider
>>     Implement BEGIN and END probes for the dtrace provider
>>     Added IO provider support for 4.14 kernels
>>
>> Everyone has their own style.  Shrug.  No big deal.
>>
>> Anyhow, personally, I think the prefixes don't add much, especially for
>> "dtrace:".  My suggestion is to drop the prefixes, but... your call.
>>
>> On 6/10/25 09:58, Alan Maguire wrote:
>>
>>> This series is a first draft of TCP provider support, where the
>>> probes are implemented via underlying fbt and sdt probes.
>>>
>>> Due to the use of the sock/inet_sock_set_state tracepoint, intended
>>> for ~5.15 kernels and later.  Tried replacing this with
>>>
>>> fbt::tcp_set_state:entry
>>>
>>> but this misses a few state transitions, so stuck with using
>>> the tracepoint.
>>>
>>> All tests under test/unittest/tcp pass unmodified on an upstream
>>> (6.15) kernel and 5.15 UEK7U3 kernel.
>>>
>>> It implements all documented TCP provider probes:
>>>
>>> accept-established, accept-refused, connnect-request,
>>> connect-established, connect-refused, receive, send,
>>> state-change
>>>
>>> Changes since RFC:
>>>
>>> - fixed issues with test failures on UEK7 due to missing
>>>    SYN_RCV state change
>>> - moved get_member() to dt_cg.c (patch 1)
>>>
>>> Alan Maguire (4):
>>>    dtrace: move get_member() to dt_cg.c
>>>    dt_impl: bump number of TSLOTS to 8
>>>    dtrace: add tcp provider
>>>    dtrace: sync dlibs with tcp.d, ip.d and net.d changes
>>>
>>>   dlibs/aarch64/5.14/ip.d  |   1 -
>>>   dlibs/aarch64/5.14/net.d |   6 +-
>>>   dlibs/aarch64/5.14/tcp.d |  52 ++---
>>>   dlibs/aarch64/5.16/ip.d  |   1 -
>>>   dlibs/aarch64/5.16/net.d |   6 +-
>>>   dlibs/aarch64/5.16/tcp.d |  52 ++---
>>>   dlibs/aarch64/6.1/ip.d   |   1 -
>>>   dlibs/aarch64/6.1/net.d  |   6 +-
>>>   dlibs/aarch64/6.1/tcp.d  |  52 ++---
>>>   dlibs/aarch64/6.10/ip.d  |   1 -
>>>   dlibs/aarch64/6.10/net.d |   6 +-
>>>   dlibs/aarch64/6.10/tcp.d |  52 ++---
>>>   dlibs/x86_64/5.14/ip.d   |   1 -
>>>   dlibs/x86_64/5.14/net.d  |   6 +-
>>>   dlibs/x86_64/5.14/tcp.d  |  52 ++---
>>>   dlibs/x86_64/5.16/ip.d   |   1 -
>>>   dlibs/x86_64/5.16/net.d  |   6 +-
>>>   dlibs/x86_64/5.16/tcp.d  |  52 ++---
>>>   dlibs/x86_64/6.1/ip.d    |   1 -
>>>   dlibs/x86_64/6.1/net.d   |   6 +-
>>>   dlibs/x86_64/6.1/tcp.d   |  52 ++---
>>>   dlibs/x86_64/6.10/ip.d   |   1 -
>>>   dlibs/x86_64/6.10/net.d  |   6 +-
>>>   dlibs/x86_64/6.10/tcp.d  |  52 ++---
>>>   libdtrace/Build          |   2 +
>>>   libdtrace/dt_cg.c        |  39 ++++
>>>   libdtrace/dt_cg.h        |   2 +
>>>   libdtrace/dt_impl.h      |   2 +-
>>>   libdtrace/dt_prov_ip.c   |  45 +----
>>>   libdtrace/dt_prov_tcp.c  | 405 +++++++++++++++++++++++++++++++++++++++
>>>   libdtrace/dt_provider.c  |   1 +
>>>   libdtrace/dt_provider.h  |   1 +
>>>   libdtrace/ip.d           |   1 -
>>>   libdtrace/net.d          |   6 +-
>>>   libdtrace/tcp.d          |  52 ++---
>>>   35 files changed, 761 insertions(+), 267 deletions(-)
>>>   create mode 100644 libdtrace/dt_prov_tcp.c
>>>


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/4] dtrace: add tcp provider
  2025-07-01 23:16   ` Eugene Loh
@ 2025-07-02 15:06     ` Alan Maguire
  2025-07-03  0:02       ` Eugene Loh
  0 siblings, 1 reply; 21+ messages in thread
From: Alan Maguire @ 2025-07-02 15:06 UTC (permalink / raw)
  To: Eugene Loh, dtrace; +Cc: dtrace-devel

On 02/07/2025 00:16, Eugene Loh wrote:
> I'll try reading this some more, but there is much I do not understand. 
> For now:
> 
> I think tcp.d needs an updated Copyright year.  Also, there seems to be
> a missing ">";  check "<unknown" in the tcps_raddr assignment.
> 

will fix, thanks!

> With this patch, should we also git rm test/unittest/tcp/test.x?
>

true, will do.

> On most VMs,
>     test/unittest/tcp/tst.ipv4remotetcp.sh
>     test/unittest/tcp/tst.ipv4remotetcpstate.sh
> xfail due to missing remote.  Are we okay with "shrugging our shoulders"
> like that?
> 

Yeah, I don't think the remote test is robust enough. Specifically in
OCI it seems to always fail. I'd suggest we replace it with creating a
network namespace with IP addresses configured on top of veths to
simulate the remote case, the codepaths will be the same. I've done this
in other test suites and it works well.

> Meanwhile, my one non-OCI VM ran those tests.  The first test passes. 
> The second one consistently reports
>     -tcp:::state-change to time-wait - yes
>     +tcp:::state-change to time-wait - no

I hit some of these failure during development; adding the
fbt::tcp_time_wait:entry probe helped. Is that inlined or something
perhaps (grep tcp_time_wait /proc/kallsyms)?

> and occasionally reports stuff like
>     dtrace: error in dt_clause_2 for probe ID 4976 (tcp:vmlinux::send):
> invalid address (0x1fc0c0000000000) at BPF pc 287
>     dtrace: error in dt_clause_2 for probe ID 4976 (tcp:vmlinux::send):
> invalid address (0x225b80000000000) at BPF pc 287
> 

ah, ok there must be a null deref somewhere. Haven't seen this before;
what kernel version/arch is this?

> The non-remote tests fail on OL8 UEK6 (x86 and arm).
>     dtrace: failed to compile script /dev/stdin:
>     ".../build/dlibs/5.2/tcp.d", line 177: failed to resolve type of
> inet_ntoa arg#1 (ipaddr_t *):
>     Unknown type name
>

This is a weird failure; I see it on some systems but not on others.
In tcp.d we have

#pragma D depends_on library net.d

which contains the typedef for ipaddr_t ; it seems that's not enough to
pull in the typedef reliably. I suspect there is a timing element
involved here in when the net.d library is included. Perhaps there is a
better way to define ipaddr_t ; would using a builtin typedef in
_dtrace_typedefs_32/64 work better perhaps?


> The probe names are
>     tcp:ip:*:*        Solaris
>     tcp:vmlinux:*:*   DTv1
>     tcp:vmlinux::*    with this patch (that is, no more function)
> I guess precedents have already been set for other SDT providers;  so,
> okay.  Just noting for my own sake.
> 

> Meanwhile, the typed args[] have changed in number and type from Solaris> to DTv1 to this patch.  Does that merit discussion?
> 

Hmm, that's not intentional (aside from the additional INBOUND/OUTBOUND
etc which we use to help inform translation). Do you see other changes
aside from them? Thanks!

Alan

> On 6/10/25 09:58, Alan Maguire wrote:
>> 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 | 405 ++++++++++++++++++++++++++++++++++++++++
>>   libdtrace/dt_provider.c |   1 +
>>   libdtrace/dt_provider.h |   1 +
>>   libdtrace/ip.d          |   1 -
>>   libdtrace/net.d         |   6 +-
>>   libdtrace/tcp.d         |  52 +++---
>>   7 files changed, 443 insertions(+), 25 deletions(-)
>>   create mode 100644 libdtrace/dt_prov_tcp.c
>>
>> diff --git a/libdtrace/Build b/libdtrace/Build
>> index 7e6e8a38..a5439354 100644
>> --- a/libdtrace/Build
>> +++ b/libdtrace/Build
>> @@ -59,6 +59,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 \
>> @@ -117,6 +118,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..75e1e3a9
>> --- /dev/null
>> +++ b/libdtrace/dt_prov_tcp.c
>> @@ -0,0 +1,405 @@
>> +/*
>> + * 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, "unsigned char", "int"} },
>> +    { "accept-established", 6, { 6, 0, "unsigned char", "tcplsinfo_t
>> *" } },
>> +    { "accept-established", 7, { 7, 0, "int", "int" } },
>> +
>> +    { "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, "unsigned char", "int"} },
>> +    { "accept-refused", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
>> +    { "accept-refused", 7, { 7, 0, "int", "int" } },
>> +
>> +    { "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, "unsigned char", "int"} },
>> +    { "connect-established", 6, { 6, 0, "unsigned char", "tcplsinfo_t
>> *" } },
>> +    { "connect-established", 7, { 7, 0, "int", "int" } },
>> +
>> +    { "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, "unsigned char", "int"} },
>> +    { "connect-refused", 6, { 6, 0, "unsigned char", "tcplsinfo_t
>> *" } },
>> +    { "connect-refused", 7, { 7, 0, "int", "int" } },
>> +
>> +    { "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, "unsigned char", "int"} },
>> +    { "connect-request", 6, { 6, 0, "unsigned char", "tcplsinfo_t
>> *" } },
>> +    { "connect-request", 7, { 7, 0, "int", "int" } },
>> +
>> +    { "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, "unsigned char", "int"} },
>> +    { "receive", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
>> +    { "receive", 7, { 7, 0, "int", "int" } },
>> +
>> +    { "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, "unsigned char", "int"} },
>> +    { "send", 6, { 6, 0, "unsigned char", "tcplsinfo_t *" } },
>> +    { "send", 7, { 7, 0, "int", "int" } },
>> +
>> +    { "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, "void *", "void *" } },
>> +    { "state-change", 6, { 6, 0, "struct sock *", "tcplsinfo_t *" } },
>> +    { "state-change", 7, { 7, 0, "int", "int" } },
>> +
>> +    { 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)
>> +{
>> +    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:
>> +     *      args[0] = skb
>> +     *      args[1] = sk
>> +     *      args[2] = ip_hdr(skb) [if available]
>> +     *      args[3] = sk [struct tcp_sock *]
>> +     *      args[4] = tcp_hdr(skb)
>> +     *      args[5] = sk->sk_state
>> +     *      args[6] = sk->sk_state
>> +     *      args[7] = NET_PROBE_INBOUND (0x1) | NET_PROBE_OUTBOUND (0x0)
>> +     */
>> +
>> +    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 explicitly 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.
>> +         */
>> +        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_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7,
>> DMST_ARG(skarg)));
>> +        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(6), 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));
>> +        emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(5), 0));
>> +
>> +        /* NET_PROBE_STATE */
>> +        emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(7),
>> +                    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) {
>> +            /* NULL sk in arg1 not arg2 (we dont want ctl_sk) */
>> +            skarg = 1;
>> +            /* skb in arg2 not arg1 */
>> +            skbarg = 2;
>> +            have_iphdr = 1;
>> +            /* tcp hdr in ip_reply_arg * */
>> +            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(BPF_DW, BPF_REG_7, DMST_ARG(6), BPF_REG_0));
>> +    }
>> +    emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(7), 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 0c621197..798e67ee 100644
>> --- a/libdtrace/dt_provider.c
>> +++ b/libdtrace/dt_provider.c
>> @@ -41,6 +41,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 59a8d62e..4db89b45 100644
>> --- a/libdtrace/dt_provider.h
>> +++ b/libdtrace/dt_provider.h
>> @@ -87,6 +87,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 f8b77f12..d59bb436 100644
>> --- a/libdtrace/ip.d
>> +++ b/libdtrace/ip.d
>> @@ -51,7 +51,6 @@ inline int TCP_MIN_HEADER_LENGTH =    20;
>>    * 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;
>>     /*
>> diff --git a/libdtrace/net.d b/libdtrace/net.d
>> index 6ac34287..45b5cba3 100644
>> --- a/libdtrace/net.d
>> +++ b/libdtrace/net.d
>> @@ -25,9 +25,13 @@ typedef struct conninfo {
>>        string ci_protocol;    /* protocol (ipv4, ipv6, etc) */
>>   } conninfo_t;
>>   +typedef __be32 ipaddr_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 in 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..d4beea87 100644
>> --- a/libdtrace/tcp.d
>> +++ b/libdtrace/tcp.d
>> @@ -8,7 +8,6 @@
>>   #pragma D depends_on module vmlinux
>>   #pragma D depends_on library net.d
>>   #pragma D depends_on provider ip
>> -#pragma D depends_on provider tcp
>>     inline int TH_FIN =    0x01;
>>   inline int TH_SYN =    0x02;
>> @@ -60,7 +59,7 @@ typedef struct tcpinfo {
>>       uint32_t tcp_seq;        /* sequence number */
>>       uint32_t tcp_ack;        /* acknowledgment number */
>>       uint8_t tcp_offset;        /* data offset, in bytes */
>> -    uint8_t tcp_flags;        /* flags */
>> +    uint16_t tcp_flags;        /* flags */
>>       uint16_t tcp_window;        /* window size */
>>       uint16_t tcp_checksum;        /* checksum */
>>       uint16_t tcp_urgent;        /* urgent data pointer */
>> @@ -111,13 +110,16 @@ 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;
>>   };
>>   +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:
>> @@ -158,47 +160,45 @@ 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) :
>> -        "<unknown>";
>> -    tcps_state = arg6;
>> +        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 = arg7 == NET_PROBE_STATE ? arg6 :
>> +        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;
>> @@ -229,3 +229,9 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>>   translator tcplsinfo_t < int I > {
>>       tcps_state = I;
>>   };
>> +
>> +/* For tracepoint, the last state is in the sock state, next passed
>> as arg6 */
>> +#pragma D binding "1.6.3" translator
>> +translator tcplsinfo_t < struct sock *S > {
>> +    tcps_state = S ? S->__sk_common.skc_state : 0;
>> +};


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/4] dt_impl: bump number of TSLOTS to 8
  2025-07-02 14:52     ` Alan Maguire
@ 2025-07-02 20:22       ` Eugene Loh
  2025-07-03 15:18         ` Alan Maguire
  0 siblings, 1 reply; 21+ messages in thread
From: Eugene Loh @ 2025-07-02 20:22 UTC (permalink / raw)
  To: Alan Maguire, dtrace; +Cc: dtrace-devel

On 7/2/25 10:52, Alan Maguire wrote:

> On 01/07/2025 19:31, Eugene Loh wrote:
>> Very simple patch, but one key problem.  The changed line is preceded by
>> a huge comment block that goes to excruciating pains to explain why the
>> value should be 4.  So, I'm fine with the change itself and I think the
>> "Because of the complexity..." sentence in the commit message can be
>> dropped, but the comment block in the file needs to be updated to
>> explain (with corresponding pains?) why we need 8 (not 9, not 7).
>>
> Ah good catch, forgot to do this. I actually think 6 is enough.
> The additional seems to be a result of the various complex inet_ntoa*()
> calls in the tcp.d translators; some have ternary operators with
> multiple inet_ntoa*()s. The origin appears to be dt_cg_ternary_op() and
> that has the comment:
>
>          /*
>           * Strings complicate things a bit because dn_left and dn_right
> might
>           * actually be temporary strings (tstring) *and* in different slots.
>           * We need to allocate a new tstring to hold the result, and
> copy the
>           * value into the new tstring (and free any tstrings in dn_left and
>           * dn_right).
>           */
>
> So if we have a left and right ternary and both allocate 3 tstring slots
> for inet_ntoa*()s that means 3*2 = 6 tstring slots should actually be
> enough. Additional testing suggests that is so; does the above make
> sense or are there additional things to consider here?

Ha!  I do not know;  I prefer NOT to have a headache.  But maybe there 
should also be some tstring stress test using inet_ntoa*() and ternary ops.

> I'll update the
> next version with a comment describing the above anyhow. Thanks for the
> review!
> Alan
>
>
>> On 6/10/25 09:58, Alan Maguire wrote:
>>> Because of the complexity of the TCP translators, more tslots are
>>> needed.
>>>
>>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>>> ---
>>>    libdtrace/dt_impl.h | 2 +-
>>>    1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
>>> index 68fb8ec5..10424f9c 100644
>>> --- a/libdtrace/dt_impl.h
>>> +++ b/libdtrace/dt_impl.h
>>> @@ -218,7 +218,7 @@ typedef struct dt_kern_path {
>>>     * - cleanpath() holds a prepended '/' char, a string, an appended
>>> '/' char,
>>>     *   and a terminating NUL char, or STRSZ + 3 chars altogether
>>>     */
>>> -#define DT_TSTRING_SLOTS    4
>>> +#define DT_TSTRING_SLOTS    8
>>>    #define DT_TSTRING_SIZE(dtp)    \
>>>            MAX(P2ROUNDUP((dtp)->dt_options[DTRACEOPT_STRSIZE] + 3, 8), \
>>>                72)

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/4] dtrace: add tcp provider
  2025-07-02 15:06     ` Alan Maguire
@ 2025-07-03  0:02       ` Eugene Loh
  2025-07-03 15:03         ` Alan Maguire
  2025-07-03 15:29         ` Kris Van Hees
  0 siblings, 2 replies; 21+ messages in thread
From: Eugene Loh @ 2025-07-03  0:02 UTC (permalink / raw)
  To: Alan Maguire, dtrace; +Cc: dtrace-devel

On 7/2/25 11:06, Alan Maguire wrote:

> On 02/07/2025 00:16, Eugene Loh wrote:
>> On most VMs,
>>      test/unittest/tcp/tst.ipv4remotetcp.sh
>>      test/unittest/tcp/tst.ipv4remotetcpstate.sh
>> xfail due to missing remote.  Are we okay with "shrugging our shoulders"
>> like that?
> Yeah, I don't think the remote test is robust enough. Specifically in
> OCI it seems to always fail. I'd suggest we replace it with creating a
> network namespace with IP addresses configured on top of veths to
> simulate the remote case, the codepaths will be the same. I've done this
> in other test suites and it works well.

Sounds great (if "we" is "you", haha).

>> Meanwhile, my one non-OCI VM ran those tests.  The first test passes.
>> The second one consistently reports
>>      -tcp:::state-change to time-wait - yes
>>      +tcp:::state-change to time-wait - no
> I hit some of these failure during development; adding the
> fbt::tcp_time_wait:entry probe helped. Is that inlined or something
> perhaps (grep tcp_time_wait /proc/kallsyms)?

On the VM in question:

# grep -w tcp_time_wait /proc/kallsyms
ffffffff92ad25b0 T tcp_time_wait
# dtrace -lP fbt |& grep tcp_time_wait
49373        fbt           vmlinux                     tcp_time_wait return
49372        fbt           vmlinux                     tcp_time_wait entry
# dtrace -lP rawfbt |& grep tcp_time_wait
51079     rawfbt           vmlinux                     tcp_time_wait return
51078     rawfbt           vmlinux                     tcp_time_wait entry

>> and occasionally reports stuff like
>>      dtrace: error in dt_clause_2 for probe ID 4976 (tcp:vmlinux::send):
>> invalid address (0x1fc0c0000000000) at BPF pc 287
>>      dtrace: error in dt_clause_2 for probe ID 4976 (tcp:vmlinux::send):
>> invalid address (0x225b80000000000) at BPF pc 287
>>
> ah, ok there must be a null deref somewhere. Haven't seen this before;
> what kernel version/arch is this?

5.15.0-300.161.13.el9uek.x86_64

FWIW, I can comment out all probes in tcp other than:

         { "send", DTRACE_PROBESPEC_NAME, 
"rawfbt::ip_send_unicast_reply:entry" },

Then I run

dtrace -c "$testdir/client.ip.pl tcp $dest $tcpport" -qn 'tcp:::send 
/args[2]->ip_saddr == "'$source'"/ { tcpsend++; }'

The disassembly shows that I look up args[2] using dt_bvar_args() 
(including checking for a fault).  Then we try to dereference 
args[2]->ip_saddr.  We first check the pointer is non NULL.  Then we 
call dt_cg_load_scalar() to bpf_probe_read() from the desired location.  
This call is problematic.

>> The non-remote tests fail on OL8 UEK6 (x86 and arm).
>>      dtrace: failed to compile script /dev/stdin:
>>      ".../build/dlibs/5.2/tcp.d", line 177: failed to resolve type of
>> inet_ntoa arg#1 (ipaddr_t *):
>>      Unknown type name
>>
> This is a weird failure; I see it on some systems but not on others.
> In tcp.d we have
>
> #pragma D depends_on library net.d
>
> which contains the typedef for ipaddr_t ; it seems that's not enough to
> pull in the typedef reliably. I suspect there is a timing element
> involved here in when the net.d library is included. Perhaps there is a
> better way to define ipaddr_t ; would using a builtin typedef in
> _dtrace_typedefs_32/64 work better perhaps?

Don't know.

>> The probe names are
>>      tcp:ip:*:*        Solaris
>>      tcp:vmlinux:*:*   DTv1
>>      tcp:vmlinux::*    with this patch (that is, no more function)
>> I guess precedents have already been set for other SDT providers;  so,
>> okay.  Just noting for my own sake.
>> Meanwhile, the typed args[] have changed in number and type from Solaris> to DTv1 to this patch.  Does that merit discussion?
> Hmm, that's not intentional (aside from the additional INBOUND/OUTBOUND
> etc which we use to help inform translation).

Worth mentioning somewhere?

> Do you see other changes aside from them? Thanks!

This is what I have for typed args[] for tcp probes.

The typed probe arguments for probes
         accept-[refused|established]
         connect-[refused|established|request]
         receive
are the same as for send.

The typed probe arguments for state-change may be different.

So, the typed probe arguments are (wide screen, fixed-width font):

args[0]:      args[1]:      args[2]:      args[3]: args[4]:      
args[5]:      args[6]:      args[7]:

             send Solaris         pktinfo_t *   csinfo_t * ipinfo_t *    
tcpsinfo_t *  tcpinfo_t *
             send DTv1            (unknown)     (unknown) (unknown)     
(unknown)     (unknown)     (unknown) int           int
             send DTv2            pktinfo_t *   csinfo_t * ipinfo_t *    
tcpsinfo_t *  tcpinfo_t *   int tcplsinfo_t * int

             state-change Solaris void          csinfo_t * void          
tcpsinfo_t *  void          tcplsinfo_t *
             state-change DTv1    (unknown)     (unknown) (unknown)     
(unknown)     (unknown)     (unknown) int           int
             state-change DTv2    void      *   csinfo_t * void     *    
tcpsinfo_t *  void      *   void * tcplsinfo_t * int

Here, "DTv1" refers to legacy DTrace on Linux.  I guess we can ignore 
that.  By "DTv2" I mean your patch.  For state-change, Solaris calls 
some things "void" (not "void *") and tcplsinfo_t* moves from args[5] to 
args[6].

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/4] dtrace: add tcp provider
  2025-07-03  0:02       ` Eugene Loh
@ 2025-07-03 15:03         ` Alan Maguire
  2025-07-03 15:29         ` Kris Van Hees
  1 sibling, 0 replies; 21+ messages in thread
From: Alan Maguire @ 2025-07-03 15:03 UTC (permalink / raw)
  To: Eugene Loh, dtrace; +Cc: dtrace-devel

On 03/07/2025 01:02, Eugene Loh wrote:
> On 7/2/25 11:06, Alan Maguire wrote:
> 
>> On 02/07/2025 00:16, Eugene Loh wrote:
>>> On most VMs,
>>>      test/unittest/tcp/tst.ipv4remotetcp.sh
>>>      test/unittest/tcp/tst.ipv4remotetcpstate.sh
>>> xfail due to missing remote.  Are we okay with "shrugging our shoulders"
>>> like that?
>> Yeah, I don't think the remote test is robust enough. Specifically in
>> OCI it seems to always fail. I'd suggest we replace it with creating a
>> network namespace with IP addresses configured on top of veths to
>> simulate the remote case, the codepaths will be the same. I've done this
>> in other test suites and it works well.
> 
> Sounds great (if "we" is "you", haha).
>

I had a go; see

https://lore.kernel.org/dtrace/20250703113345.1273604-1-alan.maguire@oracle.com/


>>> Meanwhile, my one non-OCI VM ran those tests.  The first test passes.
>>> The second one consistently reports
>>>      -tcp:::state-change to time-wait - yes
>>>      +tcp:::state-change to time-wait - no
>> I hit some of these failure during development; adding the
>> fbt::tcp_time_wait:entry probe helped. Is that inlined or something
>> perhaps (grep tcp_time_wait /proc/kallsyms)?
> 
> On the VM in question:
> 
> # grep -w tcp_time_wait /proc/kallsyms
> ffffffff92ad25b0 T tcp_time_wait
> # dtrace -lP fbt |& grep tcp_time_wait
> 49373        fbt           vmlinux                     tcp_time_wait return
> 49372        fbt           vmlinux                     tcp_time_wait entry
> # dtrace -lP rawfbt |& grep tcp_time_wait
> 51079     rawfbt           vmlinux                     tcp_time_wait return
> 51078     rawfbt           vmlinux                     tcp_time_wait entry
>

I'm not sure if it's related, but in testing the IP provider with the
net namespace stuff I saw some weird behaviour with the IP sdt probes
that had multiple underlying probe definitions. If we had a program with
ip:::send and ip:::receive, we were often left one probe short (i.e. no
BPF prog created/attached) whatever the first probe point in the program
was.  So if I traced ip:::send then ip:::receive the ip6_finish_output
send probe was missing and the test failed. Reversing the order seemed
to transfer the problem to the receive probe. So maybe there's a general
bug around synthetic probes that's biting us here? Not sure but I'll
investigate further.

>>> and occasionally reports stuff like
>>>      dtrace: error in dt_clause_2 for probe ID 4976 (tcp:vmlinux::send):
>>> invalid address (0x1fc0c0000000000) at BPF pc 287
>>>      dtrace: error in dt_clause_2 for probe ID 4976 (tcp:vmlinux::send):
>>> invalid address (0x225b80000000000) at BPF pc 287
>>>
>> ah, ok there must be a null deref somewhere. Haven't seen this before;
>> what kernel version/arch is this?
> 
> 5.15.0-300.161.13.el9uek.x86_64
> 
> FWIW, I can comment out all probes in tcp other than:
> 
>         { "send", DTRACE_PROBESPEC_NAME,
> "rawfbt::ip_send_unicast_reply:entry" },
> 
> Then I run
> 
> dtrace -c "$testdir/client.ip.pl tcp $dest $tcpport" -qn 'tcp:::send /
> args[2]->ip_saddr == "'$source'"/ { tcpsend++; }'
> 
> The disassembly shows that I look up args[2] using dt_bvar_args()
> (including checking for a fault).  Then we try to dereference args[2]-
>>ip_saddr.  We first check the pointer is non NULL.  Then we call
> dt_cg_load_scalar() to bpf_probe_read() from the desired location.  This
> call is problematic.
> 

Great, thanks for narrowing this down!

>>> The non-remote tests fail on OL8 UEK6 (x86 and arm).
>>>      dtrace: failed to compile script /dev/stdin:
>>>      ".../build/dlibs/5.2/tcp.d", line 177: failed to resolve type of
>>> inet_ntoa arg#1 (ipaddr_t *):
>>>      Unknown type name
>>>
>> This is a weird failure; I see it on some systems but not on others.
>> In tcp.d we have
>>
>> #pragma D depends_on library net.d
>>
>> which contains the typedef for ipaddr_t ; it seems that's not enough to
>> pull in the typedef reliably. I suspect there is a timing element
>> involved here in when the net.d library is included. Perhaps there is a
>> better way to define ipaddr_t ; would using a builtin typedef in
>> _dtrace_typedefs_32/64 work better perhaps?
> 
> Don't know.
>

I'll dig into this further. If anyone has hints here it would be great.


>>> The probe names are
>>>      tcp:ip:*:*        Solaris
>>>      tcp:vmlinux:*:*   DTv1
>>>      tcp:vmlinux::*    with this patch (that is, no more function)
>>> I guess precedents have already been set for other SDT providers;  so,
>>> okay.  Just noting for my own sake.
>>> Meanwhile, the typed args[] have changed in number and type from
>>> Solaris> to DTv1 to this patch.  Does that merit discussion?
>> Hmm, that's not intentional (aside from the additional INBOUND/OUTBOUND
>> etc which we use to help inform translation).
> 
> Worth mentioning somewhere?
>

I guess though I hadn't really considered the fact that the argN values
become args[] values unless we intervene.

>> Do you see other changes aside from them? Thanks!
> 
> This is what I have for typed args[] for tcp probes.
> 
> The typed probe arguments for probes
>         accept-[refused|established]
>         connect-[refused|established|request]
>         receive
> are the same as for send.
> 
> The typed probe arguments for state-change may be different.
> 
> So, the typed probe arguments are (wide screen, fixed-width font):
> 
> args[0]:      args[1]:      args[2]:      args[3]: args[4]:     
> args[5]:      args[6]:      args[7]:
> 
>             send Solaris         pktinfo_t *   csinfo_t * ipinfo_t *   
> tcpsinfo_t *  tcpinfo_t *
>             send DTv1            (unknown)     (unknown) (unknown)    
> (unknown)     (unknown)     (unknown) int           int
>             send DTv2            pktinfo_t *   csinfo_t * ipinfo_t *   
> tcpsinfo_t *  tcpinfo_t *   int tcplsinfo_t * int
> 
>             state-change Solaris void          csinfo_t * void         
> tcpsinfo_t *  void          tcplsinfo_t *
>             state-change DTv1    (unknown)     (unknown) (unknown)    
> (unknown)     (unknown)     (unknown) int           int
>             state-change DTv2    void      *   csinfo_t * void     *   
> tcpsinfo_t *  void      *   void * tcplsinfo_t * int
> 
> Here, "DTv1" refers to legacy DTrace on Linux.  I guess we can ignore
> that.  By "DTv2" I mean your patch.  For state-change, Solaris calls
> some things "void" (not "void *") and tcplsinfo_t* moves from args[5] to
> args[6].

That latter one definitely needs fixing; I think in the other cases it's
just that we need to fix up the provider description as the fields
aren't set for Linux either.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/4] dt_impl: bump number of TSLOTS to 8
  2025-07-02 20:22       ` Eugene Loh
@ 2025-07-03 15:18         ` Alan Maguire
  0 siblings, 0 replies; 21+ messages in thread
From: Alan Maguire @ 2025-07-03 15:18 UTC (permalink / raw)
  To: Eugene Loh, dtrace; +Cc: dtrace-devel

On 02/07/2025 21:22, Eugene Loh wrote:
> On 7/2/25 10:52, Alan Maguire wrote:
> 
>> On 01/07/2025 19:31, Eugene Loh wrote:
>>> Very simple patch, but one key problem.  The changed line is preceded by
>>> a huge comment block that goes to excruciating pains to explain why the
>>> value should be 4.  So, I'm fine with the change itself and I think the
>>> "Because of the complexity..." sentence in the commit message can be
>>> dropped, but the comment block in the file needs to be updated to
>>> explain (with corresponding pains?) why we need 8 (not 9, not 7).
>>>
>> Ah good catch, forgot to do this. I actually think 6 is enough.
>> The additional seems to be a result of the various complex inet_ntoa*()
>> calls in the tcp.d translators; some have ternary operators with
>> multiple inet_ntoa*()s. The origin appears to be dt_cg_ternary_op() and
>> that has the comment:
>>
>>          /*
>>           * Strings complicate things a bit because dn_left and dn_right
>> might
>>           * actually be temporary strings (tstring) *and* in different
>> slots.
>>           * We need to allocate a new tstring to hold the result, and
>> copy the
>>           * value into the new tstring (and free any tstrings in
>> dn_left and
>>           * dn_right).
>>           */
>>
>> So if we have a left and right ternary and both allocate 3 tstring slots
>> for inet_ntoa*()s that means 3*2 = 6 tstring slots should actually be
>> enough. Additional testing suggests that is so; does the above make
>> sense or are there additional things to consider here?
> 
> Ha!  I do not know;  I prefer NOT to have a headache.  But maybe there
> should also be some tstring stress test using inet_ntoa*() and ternary ops.
>

sure, I can add some tests to operators/tst.ternary.d

>> I'll update the
>> next version with a comment describing the above anyhow. Thanks for the
>> review!
>> Alan
>>
>>
>>> On 6/10/25 09:58, Alan Maguire wrote:
>>>> Because of the complexity of the TCP translators, more tslots are
>>>> needed.
>>>>
>>>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>>>> ---
>>>>    libdtrace/dt_impl.h | 2 +-
>>>>    1 file changed, 1 insertion(+), 1 deletion(-)
>>>>
>>>> diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
>>>> index 68fb8ec5..10424f9c 100644
>>>> --- a/libdtrace/dt_impl.h
>>>> +++ b/libdtrace/dt_impl.h
>>>> @@ -218,7 +218,7 @@ typedef struct dt_kern_path {
>>>>     * - cleanpath() holds a prepended '/' char, a string, an appended
>>>> '/' char,
>>>>     *   and a terminating NUL char, or STRSZ + 3 chars altogether
>>>>     */
>>>> -#define DT_TSTRING_SLOTS    4
>>>> +#define DT_TSTRING_SLOTS    8
>>>>    #define DT_TSTRING_SIZE(dtp)    \
>>>>            MAX(P2ROUNDUP((dtp)->dt_options[DTRACEOPT_STRSIZE] + 3,
>>>> 8), \
>>>>                72)


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/4] dtrace: add tcp provider
  2025-07-03  0:02       ` Eugene Loh
  2025-07-03 15:03         ` Alan Maguire
@ 2025-07-03 15:29         ` Kris Van Hees
  2025-07-03 15:38           ` Kris Van Hees
  1 sibling, 1 reply; 21+ messages in thread
From: Kris Van Hees @ 2025-07-03 15:29 UTC (permalink / raw)
  To: Eugene Loh; +Cc: Alan Maguire, dtrace, dtrace-devel

Not sure if this would confuse things or help, but here is an early attempt I
did on implementing a tcp provider:

	kvh/2.0-branch-dev-tcp

From my recollection it was missing stuff, since it was WIP compared to what
Alan has worked on.

On Wed, Jul 02, 2025 at 08:02:00PM -0400, Eugene Loh wrote:
> On 7/2/25 11:06, Alan Maguire wrote:
> 
> > On 02/07/2025 00:16, Eugene Loh wrote:
> > > On most VMs,
> > >      test/unittest/tcp/tst.ipv4remotetcp.sh
> > >      test/unittest/tcp/tst.ipv4remotetcpstate.sh
> > > xfail due to missing remote.  Are we okay with "shrugging our shoulders"
> > > like that?
> > Yeah, I don't think the remote test is robust enough. Specifically in
> > OCI it seems to always fail. I'd suggest we replace it with creating a
> > network namespace with IP addresses configured on top of veths to
> > simulate the remote case, the codepaths will be the same. I've done this
> > in other test suites and it works well.
> 
> Sounds great (if "we" is "you", haha).
> 
> > > Meanwhile, my one non-OCI VM ran those tests.  The first test passes.
> > > The second one consistently reports
> > >      -tcp:::state-change to time-wait - yes
> > >      +tcp:::state-change to time-wait - no
> > I hit some of these failure during development; adding the
> > fbt::tcp_time_wait:entry probe helped. Is that inlined or something
> > perhaps (grep tcp_time_wait /proc/kallsyms)?
> 
> On the VM in question:
> 
> # grep -w tcp_time_wait /proc/kallsyms
> ffffffff92ad25b0 T tcp_time_wait
> # dtrace -lP fbt |& grep tcp_time_wait
> 49373        fbt           vmlinux                     tcp_time_wait return
> 49372        fbt           vmlinux                     tcp_time_wait entry
> # dtrace -lP rawfbt |& grep tcp_time_wait
> 51079     rawfbt           vmlinux                     tcp_time_wait return
> 51078     rawfbt           vmlinux                     tcp_time_wait entry
> 
> > > and occasionally reports stuff like
> > >      dtrace: error in dt_clause_2 for probe ID 4976 (tcp:vmlinux::send):
> > > invalid address (0x1fc0c0000000000) at BPF pc 287
> > >      dtrace: error in dt_clause_2 for probe ID 4976 (tcp:vmlinux::send):
> > > invalid address (0x225b80000000000) at BPF pc 287
> > > 
> > ah, ok there must be a null deref somewhere. Haven't seen this before;
> > what kernel version/arch is this?
> 
> 5.15.0-300.161.13.el9uek.x86_64
> 
> FWIW, I can comment out all probes in tcp other than:
> 
>         { "send", DTRACE_PROBESPEC_NAME,
> "rawfbt::ip_send_unicast_reply:entry" },
> 
> Then I run
> 
> dtrace -c "$testdir/client.ip.pl tcp $dest $tcpport" -qn 'tcp:::send
> /args[2]->ip_saddr == "'$source'"/ { tcpsend++; }'
> 
> The disassembly shows that I look up args[2] using dt_bvar_args() (including
> checking for a fault).  Then we try to dereference args[2]->ip_saddr.  We
> first check the pointer is non NULL.  Then we call dt_cg_load_scalar() to
> bpf_probe_read() from the desired location.  This call is problematic.
> 
> > > The non-remote tests fail on OL8 UEK6 (x86 and arm).
> > >      dtrace: failed to compile script /dev/stdin:
> > >      ".../build/dlibs/5.2/tcp.d", line 177: failed to resolve type of
> > > inet_ntoa arg#1 (ipaddr_t *):
> > >      Unknown type name
> > > 
> > This is a weird failure; I see it on some systems but not on others.
> > In tcp.d we have
> > 
> > #pragma D depends_on library net.d
> > 
> > which contains the typedef for ipaddr_t ; it seems that's not enough to
> > pull in the typedef reliably. I suspect there is a timing element
> > involved here in when the net.d library is included. Perhaps there is a
> > better way to define ipaddr_t ; would using a builtin typedef in
> > _dtrace_typedefs_32/64 work better perhaps?
> 
> Don't know.
> 
> > > The probe names are
> > >      tcp:ip:*:*        Solaris
> > >      tcp:vmlinux:*:*   DTv1
> > >      tcp:vmlinux::*    with this patch (that is, no more function)
> > > I guess precedents have already been set for other SDT providers;  so,
> > > okay.  Just noting for my own sake.
> > > Meanwhile, the typed args[] have changed in number and type from Solaris> to DTv1 to this patch.  Does that merit discussion?
> > Hmm, that's not intentional (aside from the additional INBOUND/OUTBOUND
> > etc which we use to help inform translation).
> 
> Worth mentioning somewhere?
> 
> > Do you see other changes aside from them? Thanks!
> 
> This is what I have for typed args[] for tcp probes.
> 
> The typed probe arguments for probes
>         accept-[refused|established]
>         connect-[refused|established|request]
>         receive
> are the same as for send.
> 
> The typed probe arguments for state-change may be different.
> 
> So, the typed probe arguments are (wide screen, fixed-width font):
> 
> args[0]:      args[1]:      args[2]:      args[3]: args[4]:     
> args[5]:      args[6]:      args[7]:
> 
>             send Solaris         pktinfo_t *   csinfo_t * ipinfo_t *   
> tcpsinfo_t *  tcpinfo_t *
>             send DTv1            (unknown)     (unknown) (unknown)    
> (unknown)     (unknown)     (unknown) int           int
>             send DTv2            pktinfo_t *   csinfo_t * ipinfo_t *   
> tcpsinfo_t *  tcpinfo_t *   int tcplsinfo_t * int
> 
>             state-change Solaris void          csinfo_t * void         
> tcpsinfo_t *  void          tcplsinfo_t *
>             state-change DTv1    (unknown)     (unknown) (unknown)    
> (unknown)     (unknown)     (unknown) int           int
>             state-change DTv2    void      *   csinfo_t * void     *   
> tcpsinfo_t *  void      *   void * tcplsinfo_t * int
> 
> Here, "DTv1" refers to legacy DTrace on Linux.  I guess we can ignore that. 
> By "DTv2" I mean your patch.  For state-change, Solaris calls some things
> "void" (not "void *") and tcplsinfo_t* moves from args[5] to args[6].

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/4] dtrace: add tcp provider
  2025-07-03 15:29         ` Kris Van Hees
@ 2025-07-03 15:38           ` Kris Van Hees
  0 siblings, 0 replies; 21+ messages in thread
From: Kris Van Hees @ 2025-07-03 15:38 UTC (permalink / raw)
  To: Kris Van Hees; +Cc: Eugene Loh, Alan Maguire, dtrace, dtrace-devel

On Thu, Jul 03, 2025 at 11:29:42AM -0400, Kris Van Hees wrote:
> Not sure if this would confuse things or help, but here is an early attempt I
> did on implementing a tcp provider:
> 
> 	kvh/2.0-branch-dev-tcp

But... it is only on an internal repo since I never finished it :)  But perhaps
it can help with review.  Probably not worth pushing to github because I do not
think what I did is useful anymore in view of Alan's work.

> >From my recollection it was missing stuff, since it was WIP compared to what
> Alan has worked on.
> 
> On Wed, Jul 02, 2025 at 08:02:00PM -0400, Eugene Loh wrote:
> > On 7/2/25 11:06, Alan Maguire wrote:
> > 
> > > On 02/07/2025 00:16, Eugene Loh wrote:
> > > > On most VMs,
> > > >      test/unittest/tcp/tst.ipv4remotetcp.sh
> > > >      test/unittest/tcp/tst.ipv4remotetcpstate.sh
> > > > xfail due to missing remote.  Are we okay with "shrugging our shoulders"
> > > > like that?
> > > Yeah, I don't think the remote test is robust enough. Specifically in
> > > OCI it seems to always fail. I'd suggest we replace it with creating a
> > > network namespace with IP addresses configured on top of veths to
> > > simulate the remote case, the codepaths will be the same. I've done this
> > > in other test suites and it works well.
> > 
> > Sounds great (if "we" is "you", haha).
> > 
> > > > Meanwhile, my one non-OCI VM ran those tests.  The first test passes.
> > > > The second one consistently reports
> > > >      -tcp:::state-change to time-wait - yes
> > > >      +tcp:::state-change to time-wait - no
> > > I hit some of these failure during development; adding the
> > > fbt::tcp_time_wait:entry probe helped. Is that inlined or something
> > > perhaps (grep tcp_time_wait /proc/kallsyms)?
> > 
> > On the VM in question:
> > 
> > # grep -w tcp_time_wait /proc/kallsyms
> > ffffffff92ad25b0 T tcp_time_wait
> > # dtrace -lP fbt |& grep tcp_time_wait
> > 49373        fbt           vmlinux                     tcp_time_wait return
> > 49372        fbt           vmlinux                     tcp_time_wait entry
> > # dtrace -lP rawfbt |& grep tcp_time_wait
> > 51079     rawfbt           vmlinux                     tcp_time_wait return
> > 51078     rawfbt           vmlinux                     tcp_time_wait entry
> > 
> > > > and occasionally reports stuff like
> > > >      dtrace: error in dt_clause_2 for probe ID 4976 (tcp:vmlinux::send):
> > > > invalid address (0x1fc0c0000000000) at BPF pc 287
> > > >      dtrace: error in dt_clause_2 for probe ID 4976 (tcp:vmlinux::send):
> > > > invalid address (0x225b80000000000) at BPF pc 287
> > > > 
> > > ah, ok there must be a null deref somewhere. Haven't seen this before;
> > > what kernel version/arch is this?
> > 
> > 5.15.0-300.161.13.el9uek.x86_64
> > 
> > FWIW, I can comment out all probes in tcp other than:
> > 
> >         { "send", DTRACE_PROBESPEC_NAME,
> > "rawfbt::ip_send_unicast_reply:entry" },
> > 
> > Then I run
> > 
> > dtrace -c "$testdir/client.ip.pl tcp $dest $tcpport" -qn 'tcp:::send
> > /args[2]->ip_saddr == "'$source'"/ { tcpsend++; }'
> > 
> > The disassembly shows that I look up args[2] using dt_bvar_args() (including
> > checking for a fault).  Then we try to dereference args[2]->ip_saddr.  We
> > first check the pointer is non NULL.  Then we call dt_cg_load_scalar() to
> > bpf_probe_read() from the desired location.  This call is problematic.
> > 
> > > > The non-remote tests fail on OL8 UEK6 (x86 and arm).
> > > >      dtrace: failed to compile script /dev/stdin:
> > > >      ".../build/dlibs/5.2/tcp.d", line 177: failed to resolve type of
> > > > inet_ntoa arg#1 (ipaddr_t *):
> > > >      Unknown type name
> > > > 
> > > This is a weird failure; I see it on some systems but not on others.
> > > In tcp.d we have
> > > 
> > > #pragma D depends_on library net.d
> > > 
> > > which contains the typedef for ipaddr_t ; it seems that's not enough to
> > > pull in the typedef reliably. I suspect there is a timing element
> > > involved here in when the net.d library is included. Perhaps there is a
> > > better way to define ipaddr_t ; would using a builtin typedef in
> > > _dtrace_typedefs_32/64 work better perhaps?
> > 
> > Don't know.
> > 
> > > > The probe names are
> > > >      tcp:ip:*:*        Solaris
> > > >      tcp:vmlinux:*:*   DTv1
> > > >      tcp:vmlinux::*    with this patch (that is, no more function)
> > > > I guess precedents have already been set for other SDT providers;  so,
> > > > okay.  Just noting for my own sake.
> > > > Meanwhile, the typed args[] have changed in number and type from Solaris> to DTv1 to this patch.  Does that merit discussion?
> > > Hmm, that's not intentional (aside from the additional INBOUND/OUTBOUND
> > > etc which we use to help inform translation).
> > 
> > Worth mentioning somewhere?
> > 
> > > Do you see other changes aside from them? Thanks!
> > 
> > This is what I have for typed args[] for tcp probes.
> > 
> > The typed probe arguments for probes
> >         accept-[refused|established]
> >         connect-[refused|established|request]
> >         receive
> > are the same as for send.
> > 
> > The typed probe arguments for state-change may be different.
> > 
> > So, the typed probe arguments are (wide screen, fixed-width font):
> > 
> > args[0]:      args[1]:      args[2]:      args[3]: args[4]:     
> > args[5]:      args[6]:      args[7]:
> > 
> >             send Solaris         pktinfo_t *   csinfo_t * ipinfo_t *   
> > tcpsinfo_t *  tcpinfo_t *
> >             send DTv1            (unknown)     (unknown) (unknown)    
> > (unknown)     (unknown)     (unknown) int           int
> >             send DTv2            pktinfo_t *   csinfo_t * ipinfo_t *   
> > tcpsinfo_t *  tcpinfo_t *   int tcplsinfo_t * int
> > 
> >             state-change Solaris void          csinfo_t * void         
> > tcpsinfo_t *  void          tcplsinfo_t *
> >             state-change DTv1    (unknown)     (unknown) (unknown)    
> > (unknown)     (unknown)     (unknown) int           int
> >             state-change DTv2    void      *   csinfo_t * void     *   
> > tcpsinfo_t *  void      *   void * tcplsinfo_t * int
> > 
> > Here, "DTv1" refers to legacy DTrace on Linux.  I guess we can ignore that. 
> > By "DTv2" I mean your patch.  For state-change, Solaris calls some things
> > "void" (not "void *") and tcplsinfo_t* moves from args[5] to args[6].

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/4] dtrace: add tcp provider
  2025-06-10 13:58 ` [PATCH v2 3/4] dtrace: add tcp provider Alan Maguire
  2025-07-01 23:16   ` Eugene Loh
@ 2025-07-03 19:55   ` Eugene Loh
  2025-07-07 18:44   ` Kris Van Hees
  2 siblings, 0 replies; 21+ messages in thread
From: Eugene Loh @ 2025-07-03 19:55 UTC (permalink / raw)
  To: Alan Maguire, dtrace; +Cc: dtrace-devel

In general, there are lots of code paths here.  Ideally, they would all 
get tested, but I know that's hard.

Also...

On 6/10/25 09:58, Alan Maguire wrote:

> diff --git a/libdtrace/dt_prov_tcp.c b/libdtrace/dt_prov_tcp.c
> +static int trampoline(dt_pcb_t *pcb, uint_t exitlbl)
> +{
> +	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:
> +	 *      args[0] = skb
> +	 *      args[1] = sk
> +	 *      args[2] = ip_hdr(skb) [if available]
> +	 *      args[3] = sk [struct tcp_sock *]
> +	 *      args[4] = tcp_hdr(skb)
> +	 *      args[5] = sk->sk_state
> +	 *      args[6] = sk->sk_state
> +	 *      args[7] = NET_PROBE_INBOUND (0x1) | NET_PROBE_OUTBOUND (0x0)
> +	 */
> +
> +	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 explicitly 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.
> +		 */
> +		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_LOAD(BPF_DW, BPF_REG_6, BPF_REG_7, DMST_ARG(skarg)));

BTW, is it actually necessary to reload %r6 here?

> +		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(6), 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));
> +		emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(5), 0));
> +
> +		/* NET_PROBE_STATE */
> +		emit(dlp, BPF_STORE_IMM(BPF_DW, BPF_REG_7, DMST_ARG(7),
> +					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) {
> +			/* NULL sk in arg1 not arg2 (we dont want ctl_sk) */
> +			skarg = 1;
> +			/* skb in arg2 not arg1 */
> +			skbarg = 2;
> +			have_iphdr = 1;
> +			/* tcp hdr in ip_reply_arg * */
> +			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;
> +		}
> +	}
> +

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/4] dtrace: add tcp provider
  2025-06-10 13:58 ` [PATCH v2 3/4] dtrace: add tcp provider Alan Maguire
  2025-07-01 23:16   ` Eugene Loh
  2025-07-03 19:55   ` Eugene Loh
@ 2025-07-07 18:44   ` Kris Van Hees
  2 siblings, 0 replies; 21+ messages in thread
From: Kris Van Hees @ 2025-07-07 18:44 UTC (permalink / raw)
  To: Alan Maguire; +Cc: dtrace, dtrace-devel

A few other observations:

On Tue, Jun 10, 2025 at 02:58:12PM +0100, Alan Maguire wrote:
<<skip>>
> diff --git a/libdtrace/tcp.d b/libdtrace/tcp.d
> index 54e310cb..d4beea87 100644
> --- a/libdtrace/tcp.d
> +++ b/libdtrace/tcp.d
> @@ -8,7 +8,6 @@
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
>  #pragma D depends_on provider ip
> -#pragma D depends_on provider tcp

Definitely should be put back (already mentioned earlier and replied to, so
will be fixed).

>  inline int TH_FIN =	0x01;
>  inline int TH_SYN =	0x02;
> @@ -60,7 +59,7 @@ typedef struct tcpinfo {
>  	uint32_t tcp_seq;		/* sequence number */
>  	uint32_t tcp_ack;		/* acknowledgment number */
>  	uint8_t tcp_offset;		/* data offset, in bytes */
> -	uint8_t tcp_flags;		/* flags */
> +	uint16_t tcp_flags;		/* flags */

Why is the type of tcp_flags being changed?  See the comment below where it
is being assigned.

>  	uint16_t tcp_window;		/* window size */
>  	uint16_t tcp_checksum;		/* checksum */
>  	uint16_t tcp_urgent;		/* urgent data pointer */
> @@ -111,13 +110,16 @@ 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;

This clearly shows that it is meant to be uint8_t anyway?

>  	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;
>  };
>  
> +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);

This could do with a comment I think to explain what it is and how it will be
used?

>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
> @@ -158,47 +160,45 @@ 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) :
> -	    "<unknown>";
> -	tcps_state = arg6;
> +	    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 = arg7 == NET_PROBE_STATE ? arg6 :
> +	    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;
> @@ -229,3 +229,9 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  translator tcplsinfo_t < int I > {
>  	tcps_state = I;
>  };
> +
> +/* For tracepoint, the last state is in the sock state, next passed as arg6 */
> +#pragma D binding "1.6.3" translator
> +translator tcplsinfo_t < struct sock *S > {
> +	tcps_state = S ? S->__sk_common.skc_state : 0;
> +};
> -- 
> 2.39.3
> 

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2025-07-07 18:44 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-10 13:58 [PATCH v2 0/4] DTrace TCP provider Alan Maguire
2025-06-10 13:58 ` [PATCH v2 1/4] dtrace: move get_member() to dt_cg.c Alan Maguire
2025-07-01 18:23   ` Eugene Loh
2025-06-10 13:58 ` [PATCH v2 2/4] dt_impl: bump number of TSLOTS to 8 Alan Maguire
2025-07-01 18:31   ` Eugene Loh
2025-07-02 14:52     ` Alan Maguire
2025-07-02 20:22       ` Eugene Loh
2025-07-03 15:18         ` Alan Maguire
2025-06-10 13:58 ` [PATCH v2 3/4] dtrace: add tcp provider Alan Maguire
2025-07-01 23:16   ` Eugene Loh
2025-07-02 15:06     ` Alan Maguire
2025-07-03  0:02       ` Eugene Loh
2025-07-03 15:03         ` Alan Maguire
2025-07-03 15:29         ` Kris Van Hees
2025-07-03 15:38           ` Kris Van Hees
2025-07-03 19:55   ` Eugene Loh
2025-07-07 18:44   ` Kris Van Hees
2025-06-10 13:58 ` [PATCH v2 4/4] dtrace: sync dlibs with tcp.d, ip.d and net.d changes Alan Maguire
2025-07-01 19:08 ` [PATCH v2 0/4] DTrace TCP provider Eugene Loh
2025-07-01 19:27   ` Kris Van Hees
2025-07-02 14:52     ` Alan Maguire

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox