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