[DTrace-devel] [PATCH v4 6/7] dlibs: sync ip.d, net.d and tcp.d

Kris Van Hees kris.van.hees at oracle.com
Mon Jul 21 22:27:53 UTC 2025


On Wed, Jul 09, 2025 at 03:46:59PM +0100, Alan Maguire via DTrace-devel wrote:
> Sync dlibs with libdtrace versions.
> 
> Signed-off-by: Alan Maguire <alan.maguire at oracle.com>

Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>

> ---
>  dlibs/aarch64/5.11/ip.d  | 27 +---------------
>  dlibs/aarch64/5.11/net.d | 31 +++++++++++++++++--
>  dlibs/aarch64/5.11/tcp.d | 67 ++++++++++++++++++++++++----------------
>  dlibs/aarch64/5.12/ip.d  | 27 +---------------
>  dlibs/aarch64/5.12/net.d | 31 +++++++++++++++++--
>  dlibs/aarch64/5.12/tcp.d | 67 ++++++++++++++++++++++++----------------
>  dlibs/aarch64/5.14/ip.d  | 27 +---------------
>  dlibs/aarch64/5.14/net.d | 31 +++++++++++++++++--
>  dlibs/aarch64/5.14/tcp.d | 67 ++++++++++++++++++++++++----------------
>  dlibs/aarch64/5.16/ip.d  | 27 +---------------
>  dlibs/aarch64/5.16/net.d | 31 +++++++++++++++++--
>  dlibs/aarch64/5.16/tcp.d | 67 ++++++++++++++++++++++++----------------
>  dlibs/aarch64/5.2/ip.d   | 27 +---------------
>  dlibs/aarch64/5.2/net.d  | 31 +++++++++++++++++--
>  dlibs/aarch64/5.2/tcp.d  | 67 ++++++++++++++++++++++++----------------
>  dlibs/aarch64/5.6/ip.d   | 27 +---------------
>  dlibs/aarch64/5.6/net.d  | 31 +++++++++++++++++--
>  dlibs/aarch64/5.6/tcp.d  | 67 ++++++++++++++++++++++++----------------
>  dlibs/aarch64/6.1/ip.d   | 27 +---------------
>  dlibs/aarch64/6.1/net.d  | 31 +++++++++++++++++--
>  dlibs/aarch64/6.1/tcp.d  | 67 ++++++++++++++++++++++++----------------
>  dlibs/aarch64/6.10/ip.d  | 27 +---------------
>  dlibs/aarch64/6.10/net.d | 31 +++++++++++++++++--
>  dlibs/aarch64/6.10/tcp.d | 67 ++++++++++++++++++++++++----------------
>  dlibs/x86_64/5.11/ip.d   | 27 +---------------
>  dlibs/x86_64/5.11/net.d  | 31 +++++++++++++++++--
>  dlibs/x86_64/5.11/tcp.d  | 67 ++++++++++++++++++++++++----------------
>  dlibs/x86_64/5.12/ip.d   | 27 +---------------
>  dlibs/x86_64/5.12/net.d  | 31 +++++++++++++++++--
>  dlibs/x86_64/5.12/tcp.d  | 67 ++++++++++++++++++++++++----------------
>  dlibs/x86_64/5.14/ip.d   | 27 +---------------
>  dlibs/x86_64/5.14/net.d  | 31 +++++++++++++++++--
>  dlibs/x86_64/5.14/tcp.d  | 67 ++++++++++++++++++++++++----------------
>  dlibs/x86_64/5.16/ip.d   | 27 +---------------
>  dlibs/x86_64/5.16/net.d  | 31 +++++++++++++++++--
>  dlibs/x86_64/5.16/tcp.d  | 67 ++++++++++++++++++++++++----------------
>  dlibs/x86_64/5.2/ip.d    | 27 +---------------
>  dlibs/x86_64/5.2/net.d   | 31 +++++++++++++++++--
>  dlibs/x86_64/5.2/tcp.d   | 67 ++++++++++++++++++++++++----------------
>  dlibs/x86_64/5.6/ip.d    | 27 +---------------
>  dlibs/x86_64/5.6/net.d   | 31 +++++++++++++++++--
>  dlibs/x86_64/5.6/tcp.d   | 67 ++++++++++++++++++++++++----------------
>  dlibs/x86_64/6.1/ip.d    | 27 +---------------
>  dlibs/x86_64/6.1/net.d   | 31 +++++++++++++++++--
>  dlibs/x86_64/6.1/tcp.d   | 67 ++++++++++++++++++++++++----------------
>  dlibs/x86_64/6.10/ip.d   | 27 +---------------
>  dlibs/x86_64/6.10/net.d  | 31 +++++++++++++++++--
>  dlibs/x86_64/6.10/tcp.d  | 67 ++++++++++++++++++++++++----------------
>  48 files changed, 1120 insertions(+), 880 deletions(-)
> 
> diff --git a/dlibs/aarch64/5.11/ip.d b/dlibs/aarch64/5.11/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.11/ip.d
> +++ b/dlibs/aarch64/5.11/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/aarch64/5.11/net.d b/dlibs/aarch64/5.11/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.11/net.d
> +++ b/dlibs/aarch64/5.11/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/aarch64/5.11/tcp.d b/dlibs/aarch64/5.11/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.11/tcp.d
> +++ b/dlibs/aarch64/5.11/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/aarch64/5.12/ip.d b/dlibs/aarch64/5.12/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.12/ip.d
> +++ b/dlibs/aarch64/5.12/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/aarch64/5.12/net.d b/dlibs/aarch64/5.12/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.12/net.d
> +++ b/dlibs/aarch64/5.12/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/aarch64/5.12/tcp.d b/dlibs/aarch64/5.12/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.12/tcp.d
> +++ b/dlibs/aarch64/5.12/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/aarch64/5.14/ip.d b/dlibs/aarch64/5.14/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.14/ip.d
> +++ b/dlibs/aarch64/5.14/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/aarch64/5.14/net.d b/dlibs/aarch64/5.14/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.14/net.d
> +++ b/dlibs/aarch64/5.14/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/aarch64/5.14/tcp.d b/dlibs/aarch64/5.14/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.14/tcp.d
> +++ b/dlibs/aarch64/5.14/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/aarch64/5.16/ip.d b/dlibs/aarch64/5.16/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.16/ip.d
> +++ b/dlibs/aarch64/5.16/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/aarch64/5.16/net.d b/dlibs/aarch64/5.16/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.16/net.d
> +++ b/dlibs/aarch64/5.16/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/aarch64/5.16/tcp.d b/dlibs/aarch64/5.16/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.16/tcp.d
> +++ b/dlibs/aarch64/5.16/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/aarch64/5.2/ip.d b/dlibs/aarch64/5.2/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.2/ip.d
> +++ b/dlibs/aarch64/5.2/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/aarch64/5.2/net.d b/dlibs/aarch64/5.2/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.2/net.d
> +++ b/dlibs/aarch64/5.2/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/aarch64/5.2/tcp.d b/dlibs/aarch64/5.2/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.2/tcp.d
> +++ b/dlibs/aarch64/5.2/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/aarch64/5.6/ip.d b/dlibs/aarch64/5.6/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.6/ip.d
> +++ b/dlibs/aarch64/5.6/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/aarch64/5.6/net.d b/dlibs/aarch64/5.6/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.6/net.d
> +++ b/dlibs/aarch64/5.6/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/aarch64/5.6/tcp.d b/dlibs/aarch64/5.6/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.6/tcp.d
> +++ b/dlibs/aarch64/5.6/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/aarch64/6.1/ip.d b/dlibs/aarch64/6.1/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/6.1/ip.d
> +++ b/dlibs/aarch64/6.1/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/aarch64/6.1/net.d b/dlibs/aarch64/6.1/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/6.1/net.d
> +++ b/dlibs/aarch64/6.1/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/aarch64/6.1/tcp.d b/dlibs/aarch64/6.1/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/6.1/tcp.d
> +++ b/dlibs/aarch64/6.1/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/aarch64/6.10/ip.d b/dlibs/aarch64/6.10/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/6.10/ip.d
> +++ b/dlibs/aarch64/6.10/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/aarch64/6.10/net.d b/dlibs/aarch64/6.10/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/6.10/net.d
> +++ b/dlibs/aarch64/6.10/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/aarch64/6.10/tcp.d b/dlibs/aarch64/6.10/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/6.10/tcp.d
> +++ b/dlibs/aarch64/6.10/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/x86_64/5.11/ip.d b/dlibs/x86_64/5.11/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.11/ip.d
> +++ b/dlibs/x86_64/5.11/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/x86_64/5.11/net.d b/dlibs/x86_64/5.11/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.11/net.d
> +++ b/dlibs/x86_64/5.11/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/x86_64/5.11/tcp.d b/dlibs/x86_64/5.11/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.11/tcp.d
> +++ b/dlibs/x86_64/5.11/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/x86_64/5.12/ip.d b/dlibs/x86_64/5.12/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.12/ip.d
> +++ b/dlibs/x86_64/5.12/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/x86_64/5.12/net.d b/dlibs/x86_64/5.12/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.12/net.d
> +++ b/dlibs/x86_64/5.12/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/x86_64/5.12/tcp.d b/dlibs/x86_64/5.12/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.12/tcp.d
> +++ b/dlibs/x86_64/5.12/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/x86_64/5.14/ip.d b/dlibs/x86_64/5.14/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.14/ip.d
> +++ b/dlibs/x86_64/5.14/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/x86_64/5.14/net.d b/dlibs/x86_64/5.14/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.14/net.d
> +++ b/dlibs/x86_64/5.14/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/x86_64/5.14/tcp.d b/dlibs/x86_64/5.14/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.14/tcp.d
> +++ b/dlibs/x86_64/5.14/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/x86_64/5.16/ip.d b/dlibs/x86_64/5.16/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.16/ip.d
> +++ b/dlibs/x86_64/5.16/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/x86_64/5.16/net.d b/dlibs/x86_64/5.16/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.16/net.d
> +++ b/dlibs/x86_64/5.16/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/x86_64/5.16/tcp.d b/dlibs/x86_64/5.16/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.16/tcp.d
> +++ b/dlibs/x86_64/5.16/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/x86_64/5.2/ip.d b/dlibs/x86_64/5.2/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.2/ip.d
> +++ b/dlibs/x86_64/5.2/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/x86_64/5.2/net.d b/dlibs/x86_64/5.2/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.2/net.d
> +++ b/dlibs/x86_64/5.2/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/x86_64/5.2/tcp.d b/dlibs/x86_64/5.2/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.2/tcp.d
> +++ b/dlibs/x86_64/5.2/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/x86_64/5.6/ip.d b/dlibs/x86_64/5.6/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.6/ip.d
> +++ b/dlibs/x86_64/5.6/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/x86_64/5.6/net.d b/dlibs/x86_64/5.6/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.6/net.d
> +++ b/dlibs/x86_64/5.6/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/x86_64/5.6/tcp.d b/dlibs/x86_64/5.6/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.6/tcp.d
> +++ b/dlibs/x86_64/5.6/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/x86_64/6.1/ip.d b/dlibs/x86_64/6.1/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/6.1/ip.d
> +++ b/dlibs/x86_64/6.1/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/x86_64/6.1/net.d b/dlibs/x86_64/6.1/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/6.1/net.d
> +++ b/dlibs/x86_64/6.1/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/x86_64/6.1/tcp.d b/dlibs/x86_64/6.1/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/6.1/tcp.d
> +++ b/dlibs/x86_64/6.1/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> diff --git a/dlibs/x86_64/6.10/ip.d b/dlibs/x86_64/6.10/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/6.10/ip.d
> +++ b/dlibs/x86_64/6.10/ip.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH		=	135;
>  
>  inline int TCP_MIN_HEADER_LENGTH =	20;
>  
> -/*
> - * For compatibility with Solaris.  Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t	netstackid_t;
> -typedef __be32		ipaddr_t;
> -typedef struct in6_addr	in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> -	uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> -	uintptr_t cs_addr;
> -	uint64_t cs_cid;
> -} csinfo_t;
> -
>  /*
>   * ipinfo contains common IP info for both IPv4 and IPv6.
>   */
> diff --git a/dlibs/x86_64/6.10/net.d b/dlibs/x86_64/6.10/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/6.10/net.d
> +++ b/dlibs/x86_64/6.10/net.d
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
>  	 string ci_protocol;	/* protocol (ipv4, ipv6, etc) */
>  } conninfo_t;
>  
> +/*
> + * For compatibility with Solaris.  Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t	netstackid_t;
> +typedef __be32		ipaddr_t;
> +typedef struct in6_addr	in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> +	uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> +	uintptr_t cs_addr;
> +	uint64_t cs_cid;
> +} csinfo_t;
> +
>  /*
>   * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
>   */
>  inline int NET_PROBE_OUTBOUND =		0x00;
>  inline int NET_PROBE_INBOUND =		0x01;
> +inline int NET_PROBE_STATE =		0x02;
> diff --git a/dlibs/x86_64/6.10/tcp.d b/dlibs/x86_64/6.10/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/6.10/tcp.d
> +++ b/dlibs/x86_64/6.10/tcp.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  #pragma D depends_on module vmlinux
>  #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
>  #pragma D depends_on provider tcp
>  
>  inline int TH_FIN =	0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
>  	tcp_seq = T ? ntohl(T->seq) : 0;
>  	tcp_ack = T ? ntohl(T->ack_seq) : 0;
>  	tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> -	tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> +	tcp_flags = T ? *((uint8_t *)T + 13) : 0;
>  	tcp_window = T ? ntohs(T->window) : 0;
>  	tcp_checksum = T ? ntohs(T->check) : 0;
>  	tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
>  	tcp_hdr = (uintptr_t)T;
>  };
>  
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> +	(((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> +	 ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
>  /*
>   * In the main we simply translate from the "struct [tcp_]sock *" to
>   * a tcpsinfo_t *.  However there are a few exceptions:
>   *
> - * - tcps_state is always derived from arg6.  The reason is that in some
> + * - tcps_state for state-change is arg5.  The reason is that in some
>   * state transitions sock->sk_state does not reflect the actual TCP
>   * connection state.  For example the TIME_WAIT state is handled in
>   * Linux by creating a separate timewait socket and the state of the
>   * original socket is CLOSED.  In some other cases we also need to
> - * instrument state transition prior to the update of sk_state.  To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state.  To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
>   * - we sometimes need to retrieve local/remote port/address settings from
>   * TCP and IP headers directly, for example prior to the address/port
>   * being committed to the socket.  To do this effectively we need to know
>   * if the packet data is inbound (in which case the local IP/port are the
>   * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
>   * to reconstruct the address/port info where necessary.  arg2 used for IP
>   * information while arg4 contains the TCP header, so it is used for port data.
>   * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>              ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
>  	    ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
>  	    : 0;
> -	tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> +	tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> +	    tcp_fullsock[T] ?
>  	    ntohs(((struct inet_sock *)T)->inet_sport) :
>  	    (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> -	    ntohs(((struct sock *)T)->__sk_common.skc_num) :
> +	    ((struct sock *)T)->__sk_common.skc_num :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -	    ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> +		  ((struct tcphdr *)arg4)->dest :
> +		  ((struct tcphdr *)arg4)->source) :
>  	    0;
>  	tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
>  	    ntohs(((struct sock *)T)->__sk_common.skc_dport) :
>  	    arg4 != NULL ?
>  	    ntohs(arg7 == NET_PROBE_INBOUND ?
> -            ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> +		  ((struct tcphdr *)arg4)->source :
> +		  ((struct tcphdr *)arg4)->dest) :
>  	    0;
>  	tcps_laddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->daddr :
> -	    &((struct ipv6hdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
>  	    "<unknown>";
>  	tcps_raddr =
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
>  	    inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
>  	    T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
>  	    inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> -	    arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> -	    inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> -	    arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> -	    inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> -	    &((struct ipv6hdr *)arg2)->saddr :
> -	    &((struct ipv6hdr *)arg2)->daddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> +	    inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> +	    arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> +	    inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
>  	    "<unknown>";
> -	tcps_state = arg6;
> +	/* For state-change we probe right before state has changed, but
> +	 * provider definition wants new state in tcps_state; for
> +	 * state-change probes the trampoline stores it in arg5.
> +	 */
> +	tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> +	    T ? ((struct sock *)T)->__sk_common.skc_state : 0;
>  	tcps_iss = T ?
>  	    T->snd_una - (uint32_t)T->bytes_acked : 0;
>  	tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
>  	    T->rcv_nxt - (uint32_t)T->bytes_received : 0;
>  };
>  
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
>  #pragma D binding "1.6.3" translator
>  translator tcplsinfo_t < int I > {
> -	tcps_state = I;
> +	tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
>  };
> -- 
> 2.39.3
> 
> 
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel at oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel



More information about the DTrace-devel mailing list