From: Kris Van Hees <kris.van.hees@oracle.com>
To: Alan Maguire <alan.maguire@oracle.com>
Cc: dtrace@lists.linux.dev, dtrace-devel@oss.oracle.com
Subject: Re: [DTrace-devel] [PATCH v4 6/7] dlibs: sync ip.d, net.d and tcp.d
Date: Mon, 21 Jul 2025 18:27:53 -0400 [thread overview]
Message-ID: <aH6+6f92+gobHoac@oracle.com> (raw)
In-Reply-To: <20250709144700.20591-7-alan.maguire@oracle.com>
On Wed, Jul 09, 2025 at 03:46:59PM +0100, Alan Maguire via DTrace-devel wrote:
> Sync dlibs with libdtrace versions.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
> ---
> dlibs/aarch64/5.11/ip.d | 27 +---------------
> dlibs/aarch64/5.11/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/5.11/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/5.12/ip.d | 27 +---------------
> dlibs/aarch64/5.12/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/5.12/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/5.14/ip.d | 27 +---------------
> dlibs/aarch64/5.14/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/5.14/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/5.16/ip.d | 27 +---------------
> dlibs/aarch64/5.16/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/5.16/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/5.2/ip.d | 27 +---------------
> dlibs/aarch64/5.2/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/5.2/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/5.6/ip.d | 27 +---------------
> dlibs/aarch64/5.6/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/5.6/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/6.1/ip.d | 27 +---------------
> dlibs/aarch64/6.1/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/6.1/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/aarch64/6.10/ip.d | 27 +---------------
> dlibs/aarch64/6.10/net.d | 31 +++++++++++++++++--
> dlibs/aarch64/6.10/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/5.11/ip.d | 27 +---------------
> dlibs/x86_64/5.11/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/5.11/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/5.12/ip.d | 27 +---------------
> dlibs/x86_64/5.12/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/5.12/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/5.14/ip.d | 27 +---------------
> dlibs/x86_64/5.14/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/5.14/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/5.16/ip.d | 27 +---------------
> dlibs/x86_64/5.16/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/5.16/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/5.2/ip.d | 27 +---------------
> dlibs/x86_64/5.2/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/5.2/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/5.6/ip.d | 27 +---------------
> dlibs/x86_64/5.6/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/5.6/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/6.1/ip.d | 27 +---------------
> dlibs/x86_64/6.1/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/6.1/tcp.d | 67 ++++++++++++++++++++++++----------------
> dlibs/x86_64/6.10/ip.d | 27 +---------------
> dlibs/x86_64/6.10/net.d | 31 +++++++++++++++++--
> dlibs/x86_64/6.10/tcp.d | 67 ++++++++++++++++++++++++----------------
> 48 files changed, 1120 insertions(+), 880 deletions(-)
>
> diff --git a/dlibs/aarch64/5.11/ip.d b/dlibs/aarch64/5.11/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.11/ip.d
> +++ b/dlibs/aarch64/5.11/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/5.11/net.d b/dlibs/aarch64/5.11/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.11/net.d
> +++ b/dlibs/aarch64/5.11/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/5.11/tcp.d b/dlibs/aarch64/5.11/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.11/tcp.d
> +++ b/dlibs/aarch64/5.11/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/5.12/ip.d b/dlibs/aarch64/5.12/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.12/ip.d
> +++ b/dlibs/aarch64/5.12/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/5.12/net.d b/dlibs/aarch64/5.12/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.12/net.d
> +++ b/dlibs/aarch64/5.12/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/5.12/tcp.d b/dlibs/aarch64/5.12/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.12/tcp.d
> +++ b/dlibs/aarch64/5.12/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/5.14/ip.d b/dlibs/aarch64/5.14/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.14/ip.d
> +++ b/dlibs/aarch64/5.14/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/5.14/net.d b/dlibs/aarch64/5.14/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.14/net.d
> +++ b/dlibs/aarch64/5.14/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/5.14/tcp.d b/dlibs/aarch64/5.14/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.14/tcp.d
> +++ b/dlibs/aarch64/5.14/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/5.16/ip.d b/dlibs/aarch64/5.16/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.16/ip.d
> +++ b/dlibs/aarch64/5.16/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/5.16/net.d b/dlibs/aarch64/5.16/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.16/net.d
> +++ b/dlibs/aarch64/5.16/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/5.16/tcp.d b/dlibs/aarch64/5.16/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.16/tcp.d
> +++ b/dlibs/aarch64/5.16/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/5.2/ip.d b/dlibs/aarch64/5.2/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.2/ip.d
> +++ b/dlibs/aarch64/5.2/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/5.2/net.d b/dlibs/aarch64/5.2/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.2/net.d
> +++ b/dlibs/aarch64/5.2/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/5.2/tcp.d b/dlibs/aarch64/5.2/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.2/tcp.d
> +++ b/dlibs/aarch64/5.2/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/5.6/ip.d b/dlibs/aarch64/5.6/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/5.6/ip.d
> +++ b/dlibs/aarch64/5.6/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/5.6/net.d b/dlibs/aarch64/5.6/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/5.6/net.d
> +++ b/dlibs/aarch64/5.6/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/5.6/tcp.d b/dlibs/aarch64/5.6/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/5.6/tcp.d
> +++ b/dlibs/aarch64/5.6/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/6.1/ip.d b/dlibs/aarch64/6.1/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/6.1/ip.d
> +++ b/dlibs/aarch64/6.1/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/6.1/net.d b/dlibs/aarch64/6.1/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/6.1/net.d
> +++ b/dlibs/aarch64/6.1/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/6.1/tcp.d b/dlibs/aarch64/6.1/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/6.1/tcp.d
> +++ b/dlibs/aarch64/6.1/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/aarch64/6.10/ip.d b/dlibs/aarch64/6.10/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/aarch64/6.10/ip.d
> +++ b/dlibs/aarch64/6.10/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/aarch64/6.10/net.d b/dlibs/aarch64/6.10/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/aarch64/6.10/net.d
> +++ b/dlibs/aarch64/6.10/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/aarch64/6.10/tcp.d b/dlibs/aarch64/6.10/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/aarch64/6.10/tcp.d
> +++ b/dlibs/aarch64/6.10/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/5.11/ip.d b/dlibs/x86_64/5.11/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.11/ip.d
> +++ b/dlibs/x86_64/5.11/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/5.11/net.d b/dlibs/x86_64/5.11/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.11/net.d
> +++ b/dlibs/x86_64/5.11/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/5.11/tcp.d b/dlibs/x86_64/5.11/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.11/tcp.d
> +++ b/dlibs/x86_64/5.11/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/5.12/ip.d b/dlibs/x86_64/5.12/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.12/ip.d
> +++ b/dlibs/x86_64/5.12/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/5.12/net.d b/dlibs/x86_64/5.12/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.12/net.d
> +++ b/dlibs/x86_64/5.12/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/5.12/tcp.d b/dlibs/x86_64/5.12/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.12/tcp.d
> +++ b/dlibs/x86_64/5.12/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/5.14/ip.d b/dlibs/x86_64/5.14/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.14/ip.d
> +++ b/dlibs/x86_64/5.14/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/5.14/net.d b/dlibs/x86_64/5.14/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.14/net.d
> +++ b/dlibs/x86_64/5.14/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/5.14/tcp.d b/dlibs/x86_64/5.14/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.14/tcp.d
> +++ b/dlibs/x86_64/5.14/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/5.16/ip.d b/dlibs/x86_64/5.16/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.16/ip.d
> +++ b/dlibs/x86_64/5.16/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/5.16/net.d b/dlibs/x86_64/5.16/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.16/net.d
> +++ b/dlibs/x86_64/5.16/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/5.16/tcp.d b/dlibs/x86_64/5.16/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.16/tcp.d
> +++ b/dlibs/x86_64/5.16/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/5.2/ip.d b/dlibs/x86_64/5.2/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.2/ip.d
> +++ b/dlibs/x86_64/5.2/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/5.2/net.d b/dlibs/x86_64/5.2/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.2/net.d
> +++ b/dlibs/x86_64/5.2/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/5.2/tcp.d b/dlibs/x86_64/5.2/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.2/tcp.d
> +++ b/dlibs/x86_64/5.2/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/5.6/ip.d b/dlibs/x86_64/5.6/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/5.6/ip.d
> +++ b/dlibs/x86_64/5.6/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/5.6/net.d b/dlibs/x86_64/5.6/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/5.6/net.d
> +++ b/dlibs/x86_64/5.6/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/5.6/tcp.d b/dlibs/x86_64/5.6/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/5.6/tcp.d
> +++ b/dlibs/x86_64/5.6/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/6.1/ip.d b/dlibs/x86_64/6.1/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/6.1/ip.d
> +++ b/dlibs/x86_64/6.1/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/6.1/net.d b/dlibs/x86_64/6.1/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/6.1/net.d
> +++ b/dlibs/x86_64/6.1/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/6.1/tcp.d b/dlibs/x86_64/6.1/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/6.1/tcp.d
> +++ b/dlibs/x86_64/6.1/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> diff --git a/dlibs/x86_64/6.10/ip.d b/dlibs/x86_64/6.10/ip.d
> index f8b77f12..493b75a0 100644
> --- a/dlibs/x86_64/6.10/ip.d
> +++ b/dlibs/x86_64/6.10/ip.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -46,31 +46,6 @@ inline int IPPROTO_MH = 135;
>
> inline int TCP_MIN_HEADER_LENGTH = 20;
>
> -/*
> - * For compatibility with Solaris. Here the netstackid will be the pointer
> - * to the net namespace (nd_net in struct net_device).
> - */
> -typedef uint64_t netstackid_t;
> -typedef __be32 ipaddr_t;
> -typedef struct in6_addr in6_addr_t;
> -
> -/*
> - * pktinfo is where packet ID info can be made available for deeper
> - * analysis if packet IDs become supported by the kernel in the future.
> - * The pkt_addr member is currently always NULL.
> - */
> -typedef struct pktinfo {
> - uintptr_t pkt_addr;
> -} pktinfo_t;
> -
> -/*
> - * csinfo is where connection state info is made available.
> - */
> -typedef struct csinfo {
> - uintptr_t cs_addr;
> - uint64_t cs_cid;
> -} csinfo_t;
> -
> /*
> * ipinfo contains common IP info for both IPv4 and IPv6.
> */
> diff --git a/dlibs/x86_64/6.10/net.d b/dlibs/x86_64/6.10/net.d
> index 6ac34287..f1291696 100644
> --- a/dlibs/x86_64/6.10/net.d
> +++ b/dlibs/x86_64/6.10/net.d
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -25,9 +25,36 @@ typedef struct conninfo {
> string ci_protocol; /* protocol (ipv4, ipv6, etc) */
> } conninfo_t;
>
> +/*
> + * For compatibility with Solaris. Here the netstackid will be the pointer
> + * to the net namespace (nd_net in struct net_device).
> + */
> +typedef uint64_t netstackid_t;
> +typedef __be32 ipaddr_t;
> +typedef struct in6_addr in6_addr_t;
> +
> +/*
> + * pktinfo is where packet ID info can be made available for deeper
> + * analysis if packet IDs become supported by the kernel in the future.
> + * The pkt_addr member is currently always NULL.
> + */
> +typedef struct pktinfo {
> + uintptr_t pkt_addr;
> +} pktinfo_t;
> +
> +/*
> + * csinfo is where connection state info is made available.
> + */
> +typedef struct csinfo {
> + uintptr_t cs_addr;
> + uint64_t cs_cid;
> +} csinfo_t;
> +
> /*
> * We use these values to determine if a probe point is associated
> - * with sending (outbound) or receiving (inbound).
> + * with sending (outbound) or receiving (inbound) or a state-related
> + * probe (i.e. neither inbound our outbound).
> */
> inline int NET_PROBE_OUTBOUND = 0x00;
> inline int NET_PROBE_INBOUND = 0x01;
> +inline int NET_PROBE_STATE = 0x02;
> diff --git a/dlibs/x86_64/6.10/tcp.d b/dlibs/x86_64/6.10/tcp.d
> index 54e310cb..48d9adb4 100644
> --- a/dlibs/x86_64/6.10/tcp.d
> +++ b/dlibs/x86_64/6.10/tcp.d
> @@ -1,13 +1,13 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
>
> #pragma D depends_on module vmlinux
> #pragma D depends_on library net.d
> -#pragma D depends_on provider ip
> +#pragma D depends_on library ip.d
> #pragma D depends_on provider tcp
>
> inline int TH_FIN = 0x01;
> @@ -111,30 +111,38 @@ translator tcpinfo_t < struct tcphdr *T > {
> tcp_seq = T ? ntohl(T->seq) : 0;
> tcp_ack = T ? ntohl(T->ack_seq) : 0;
> tcp_offset = T ? (*(uint8_t *)(T + 12) & 0xf0) >> 2 : 0;
> - tcp_flags = T ? *(uint8_t *)(T + 13) : 0;
> + tcp_flags = T ? *((uint8_t *)T + 13) : 0;
> tcp_window = T ? ntohs(T->window) : 0;
> tcp_checksum = T ? ntohs(T->check) : 0;
> tcp_urgent = T ? ntohs(T->urg_ptr) : 0;
> tcp_hdr = (uintptr_t)T;
> };
>
> +/* timewait sockets and inet connection sockets do not populate all fields
> + * and are not classified as full sockets; this inline helps translators
> + * spot them and act appropriately.
> + */
> +inline int tcp_fullsock[struct tcp_sock *sk] =
> + (((struct sock_common *)sk)->skc_state != TCP_STATE_SYN_RECEIVED &&
> + ((struct sock_common *)sk)->skc_state != TCP_STATE_TIME_WAIT);
> /*
> * In the main we simply translate from the "struct [tcp_]sock *" to
> * a tcpsinfo_t *. However there are a few exceptions:
> *
> - * - tcps_state is always derived from arg6. The reason is that in some
> + * - tcps_state for state-change is arg5. The reason is that in some
> * state transitions sock->sk_state does not reflect the actual TCP
> * connection state. For example the TIME_WAIT state is handled in
> * Linux by creating a separate timewait socket and the state of the
> * original socket is CLOSED. In some other cases we also need to
> - * instrument state transition prior to the update of sk_state. To do
> - * all of this we rely on arg6.
> + * instrument state transition _prior_ to the update of sk_state. To do
> + * all of this we rely on arg5 to hold the new state. arg6 is set to
> + * NET_PROBE_STATE to quickly identify state-change probes.
> * - we sometimes need to retrieve local/remote port/address settings from
> * TCP and IP headers directly, for example prior to the address/port
> * being committed to the socket. To do this effectively we need to know
> * if the packet data is inbound (in which case the local IP/port are the
> * destination) or outbound (in which case the local IP/port are the source).
> - * arg7 is set to 0 for outbound traffic and 1 for inbound so we use these
> + * arg6 is set to 0 for outbound traffic and 1 for inbound so we use these
> * to reconstruct the address/port info where necessary. arg2 used for IP
> * information while arg4 contains the TCP header, so it is used for port data.
> * NET_PROBE_INBOUND is defined as 1, NET_PROBE_OUTBOUND as 0 in net.d.
> @@ -158,47 +166,49 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_daddr)[2] &&
> ((uint32_t *)&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr)[3])
> : 0;
> - tcps_lport = (T && ((struct inet_sock *)T)->inet_sport != 0) ?
> + tcps_lport = T && ((struct inet_sock *)T)->inet_sport != 0 &&
> + tcp_fullsock[T] ?
> ntohs(((struct inet_sock *)T)->inet_sport) :
> (T && ((struct inet_sock *)T)->inet_sport == 0) ?
> - ntohs(((struct sock *)T)->__sk_common.skc_num) :
> + ((struct sock *)T)->__sk_common.skc_num :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->dest : ((struct tcphdr *)arg4)->source) :
> + ((struct tcphdr *)arg4)->dest :
> + ((struct tcphdr *)arg4)->source) :
> 0;
> tcps_rport = T && ((struct sock *)T)->__sk_common.skc_dport != 0 ?
> ntohs(((struct sock *)T)->__sk_common.skc_dport) :
> arg4 != NULL ?
> ntohs(arg7 == NET_PROBE_INBOUND ?
> - ((struct tcphdr *)arg4)->source : ((struct tcphdr *)arg4)->dest) :
> + ((struct tcphdr *)arg4)->source :
> + ((struct tcphdr *)arg4)->dest) :
> 0;
> tcps_laddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_rcv_saddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_rcv_saddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->daddr : &((struct iphdr *)arg2)->saddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->daddr :
> - &((struct ipv6hdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->daddr) :
> "<unknown>";
> tcps_raddr =
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET ?
> inet_ntoa(&((struct sock *)T)->__sk_common.skc_daddr) :
> T && ((struct sock *)T)->__sk_common.skc_family == AF_INET6 ?
> inet_ntoa6(&((struct sock *)T)->__sk_common.skc_v6_daddr) :
> - arg2 != NULL && (*(uint8_t *)arg2) >> 4 == 4 ?
> - inet_ntoa(arg7 == NET_PROBE_INBOUND ?
> - &((struct iphdr *)arg2)->saddr : &((struct iphdr *)arg2)->daddr) :
> - arg2 != NULL && *((uint8_t *)arg2) >> 4 == 6 ?
> - inet_ntoa6(arg7 == NET_PROBE_INBOUND ?
> - &((struct ipv6hdr *)arg2)->saddr :
> - &((struct ipv6hdr *)arg2)->daddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 4 ?
> + inet_ntoa(&((struct iphdr *)arg2)->saddr) :
> + arg2 != NULL && (*(uint8_t *)arg2 >> 4) == 6 ?
> + inet_ntoa6(&((struct ipv6hdr *)arg2)->saddr) :
> "<unknown>";
> - tcps_state = arg6;
> + /* For state-change we probe right before state has changed, but
> + * provider definition wants new state in tcps_state; for
> + * state-change probes the trampoline stores it in arg5.
> + */
> + tcps_state = arg6 == NET_PROBE_STATE ? arg5 :
> + T ? ((struct sock *)T)->__sk_common.skc_state : 0;
> tcps_iss = T ?
> T->snd_una - (uint32_t)T->bytes_acked : 0;
> tcps_suna = T ? T->snd_una : 0;
> @@ -225,7 +235,10 @@ translator tcpsinfo_t < struct tcp_sock *T > {
> T->rcv_nxt - (uint32_t)T->bytes_received : 0;
> };
>
> +/* state-change trampoline stores new state in arg5; at time of firing,
> + * state has not been updated, so last state is in tcp_sock state.
> + */
> #pragma D binding "1.6.3" translator
> translator tcplsinfo_t < int I > {
> - tcps_state = I;
> + tcps_state = arg3 ? ((struct sock *)arg3)->__sk_common.skc_state : 0;
> };
> --
> 2.39.3
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel@oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
next prev parent reply other threads:[~2025-07-21 22:28 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-09 14:46 [PATCH v4 0/7] DTrace TCP provider Alan Maguire
2025-07-09 14:46 ` [PATCH v4 1/7] cg: move get_member() to dt_cg.c Alan Maguire
2025-07-09 16:28 ` [DTrace-devel] " Kris Van Hees
2025-07-09 16:47 ` Kris Van Hees
2025-07-09 19:47 ` Alan Maguire
2025-07-09 14:46 ` [PATCH v4 2/7] cg: bump number of TSLOTS to 6 Alan Maguire
2025-07-10 4:46 ` [DTrace-devel] " Kris Van Hees
2025-07-09 14:46 ` [PATCH v4 3/7] test/operators: extend ternary tests to cover inet_ntoa*()s Alan Maguire
2025-07-21 19:59 ` [DTrace-devel] " Kris Van Hees
2025-07-09 14:46 ` [PATCH v4 4/7] providers: move network-generic definitions to net.d Alan Maguire
2025-07-21 20:24 ` [DTrace-devel] " Kris Van Hees
2025-07-09 14:46 ` [PATCH v4 5/7] tcp: new provider Alan Maguire
2025-07-09 14:46 ` [PATCH v4 6/7] dlibs: sync ip.d, net.d and tcp.d Alan Maguire
2025-07-21 22:27 ` Kris Van Hees [this message]
2025-07-09 14:47 ` [PATCH v4 7/7] unittest/tcp: update test.x Alan Maguire
2025-07-21 22:36 ` [DTrace-devel] " Kris Van Hees
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aH6+6f92+gobHoac@oracle.com \
--to=kris.van.hees@oracle.com \
--cc=alan.maguire@oracle.com \
--cc=dtrace-devel@oss.oracle.com \
--cc=dtrace@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox