DTRACE_TCP(4)
π DTRACE_TCP π β dtrace_tcp(4)
Kernel Interfaces Manual β¨ section 4 of the manual, bestie, and it's a whole vibe
πΈ NAME πΈ
dtrace_tcp β a DTrace provider for tracing events related to the tcp(4) protocol
Like. That's the whole name section. She's a DTrace provider. She traces TCP things. She's iconic and she knows it. π
π¦ SYNOPSIS π¦
okay so here's the lineup of probes, and yes every single one has that five-argument glow-up (except the last one, she's different, we love her for it):
tcp:::accept-established(pktinfo_t *, csinfo_t *, ipinfo_t *,
tcpsinfo_t *, tcpinfo_t *);
tcp:::accept-refused(pktinfo_t *, csinfo_t *, ipinfo_t *, tcpsinfo_t *,
tcpinfo_t *);
tcp:::connect-established(pktinfo_t *, csinfo_t *, ipinfo_t *,
tcpsinfo_t *, tcpinfo_t *);
tcp:::connect-refused(pktinfo_t *, csinfo_t *, ipinfo_t *, tcpsinfo_t *,
tcpinfo_t *);
tcp:::connect-request(pktinfo_t *, csinfo_t *, ipinfo_t *, tcpsinfo_t *,
tcpinfo_t *);
tcp:::receive(pktinfo_t *, csinfo_t *, ipinfo_t *, tcpsinfo_t *,
tcpinfo_t *);
tcp:::send(pktinfo_t *, csinfo_t *, ipinfo_t *, tcpsinfo_t *,
tcpinfo_t *);
tcp:::state-change(void *, csinfo_t *, void *, tcpsinfo_t *, void *,
tcplsinfo_t *);
π DESCRIPTION π
So the DTrace tcp provider lets you trace events happening inside the tcp(4) protocol implementation. She's lowkey cousins with the dtrace_ip(4) and dtrace_udp(4) providers, but she's got extra β she comes with probes that correspond to protocol events at a level higher than packet reception and transmission. Main character behavior honestly. π
Now, listen: all the tcp probes except tcp:::state-change() have the same number and type of arguments. And the last three args are the ones doing the actual storytelling β they describe a TCP segment:
- the
ipinfo_targument shows you the version-agnostic fields of the IP header - the
tcpinfo_targument shows you the TCP header - the
tcpsinfo_targument describes details of the corresponding TCP connection state, if any
Their fields are all spelled out down in ARGUMENTS, don't worry, we get there. π«Ά
The tcp:::accept-established() probe fires when a remotely-initiated active TCP open succeeds. At that moment the new connection is in the ESTABLISHED state, and the probe arguments expose the headers associated with the final ACK of the three-way handshake. Slay. The tcp:::accept-refused() probe fires when a SYN shows up on a port with no listening socket. The probe arguments there expose the headers associated with the RST that's about to be transmitted back to the remote host in response to that SYN segment. (Rude, but fair. π)
The tcp:::connect-established(), tcp:::connect-refused(), and tcp:::connect-request() probes are basically the βacceptβ probes' siblings, except they correspond to locally-initiated TCP connections. Break it down:
- The
tcp:::connect-established()probe fires when the SYN-ACK segment of a three-way handshake is received from the remote host and a final ACK is prepared for transmission. This happens immediately after the local connection state transitions fromSYN-SENTtoESTABLISHED. The probe arguments describe the headers associated with the received SYN-ACK segment. - The
tcp:::connect-refused()probe fires when the local host receives a RST segment in response to a SYN segment β which is the remote host saying it refuses to open a connection. The probe arguments describe the IP and TCP headers associated with the received RST segment. - The
tcp:::connect-request()probe fires as the kernel prepares to transmit the initial SYN segment of a three-way handshake.
The tcp:::send() and tcp:::receive() probes fire when the host sends or receives a TCP packet, respectively. And this is important bestie, fr fr: just like with the dtrace_udp(4) provider, tcp probes fire only for packets sent by or to the local host. Forwarded packets get handled in the IP layer and are only visible to the dtrace_ip(4) provider. Not our girl's problem. π
The tcp:::state-change() probe fires upon local TCP connection state transitions. Her first, third and fifth arguments are currently always NULL. Her last argument describes the from-state in the transition, and the to-state can be pulled from args[3]->tcps_state. Sneaky but efficient. β¨
π ARGUMENTS π
π The pktinfo_t argument
Currently unimplemented and included for compatibility with other implementations of this provider. Her fields:
uinptr_t pkt_addrβ always set to0.
π The csinfo_t argument
Also currently unimplemented, also included for compatibility with other implementations of this provider. Her fields:
uintptr_t cs_addrβ always set to0.uint64_t cs_cidβ a pointer to thestruct inpcbfor this packet, orNULL.pid_t cs_pidβ always set to0.
π The ipinfo_t type
A version-agnostic representation of fields from an IP header. Her fields are described over in the dtrace_ip(4) manual page, so go say hi to that one. π¦
π The tcpsinfo_t type
Used to provide a stable representation of TCP connection state. Heads up: some tcp probes β like tcp:::accept-refused() β fire in a context where there is no TCP connection, and in that case this argument is NULL. Her fields:
uintptr_t tcps_addrβ the address of the corresponding TCP control block. This is currently a pointer to astruct tcpcb.int tcps_localβ a boolean indicating whether the connection is local to the host. Currently unimplemented and always set to-1.int tcps_activeβ a boolean indicating whether the connection was initiated by the local host. Currently unimplemented and always set to-1.uint16_t tcps_lportβ local TCP port.uint16_t tcps_rportβ remote TCP port.string tcps_laddrβ local address.string tcps_raddrβ remote address.int32_t tcps_stateβ current TCP state. The valid TCP state values are given by the constants prefixed with βTCPS_β in/usr/lib/dtrace/tcp.d.uint32_t tcps_issβ initial send sequence number.uint32_t tcps_sunaβ initial sequence number of sent but unacknowledged data.uint32_t tcps_snxtβ next sequence number for send.uint32_t tcps_rackβ sequence number of received and acknowledged data.uint32_t tcps_rnxtβ next expected sequence number for receive.u_long tcps_swndβ TCP send window size.int32_t tcps_snd_wsβ window scaling factor for the TCP send window.u_long tcps_rwndβ TCP receive window size.int32_t tcps_rcv_wsβ window scaling factor for the TCP receive window.u_long tcps_cwndβ TCP congestion window size.u_long tcps_cwnd_ssthreshβ congestion window threshold at which slow start ends and congestion avoidance begins.uint32_t tcps_sack_fackβ last sequence number selectively acknowledged by the receiver.uint32_t tcps_sack_snxtβ next selectively acknowledge sequence number at which to begin retransmitting.uint32_t tcps_rtoβ round-trip timeout, in milliseconds.uint32_t tcps_mssβ maximum segment size.int tcps_retransmitβ a boolean indicating that the local sender is retransmitting data.int tcps_srttβ smoothed round-trip time.
π The tcpinfo_t type
Exposes the fields in a TCP segment header in host order. Her fields:
uint16_t tcp_sportβ source TCP port.uint16_t tcp_dportβ destination TCP port.uint32_t tcp_seqβ sequence number.uint32_t tcp_ackβ acknowledgement number.uint8_t tcp_offsetβ data offset, in bytes.uint8_t tcp_flagsβ TCP flags.uint16_t tcp_windowβ TCP window size.uint16_t tcp_checksumβ checksum.uint16_t tcp_urgentβ urgent data pointer.struct tcphdr *tcp_hdrβ a pointer to the raw TCP header.
π The tcplsinfo_t type
Used by the tcp:::state-change() probe to provide the from-state of a transition. Her fields:
int32_t tcps_stateβ a TCP state. The valid TCP state values are given by the constants prefixed with βTCPS_β in/usr/lib/dtrace/tcp.d.
π FILES π
/usr/lib/dtrace/tcp.dβ DTrace type and translator definitions for thetcpprovider.
to be continued, bestieβ¦ there's more and you're gonna wanna see it π
β¨ EXAMPLES β¨
okay bestie, this is the part where it gets fun π because we're actually writing D scripts now. First up: a script that logs TCP segments in real time. like, watching the packets go by in real time. it's giving surveillance but make it cute π
#pragma D option quiet
#pragma D option switchrate=10hz
dtrace:::BEGIN
{
printf(" %3s %15s:%-5s %15s:%-5s %6s %s\n", "CPU",
"LADDR", "LPORT", "RADDR", "RPORT", "BYTES", "FLAGS");
}
tcp:::send
{
this->length = args[2]->ip_plength - args[4]->tcp_offset;
printf(" %3d %16s:%-5d -> %16s:%-5d %6d (", cpu, args[2]->ip_saddr,
args[4]->tcp_sport, args[2]->ip_daddr, args[4]->tcp_dport,
this->length);
printf("%s", args[4]->tcp_flags & TH_FIN ? "FIN|" : "");
printf("%s", args[4]->tcp_flags & TH_SYN ? "SYN|" : "");
printf("%s", args[4]->tcp_flags & TH_RST ? "RST|" : "");
printf("%s", args[4]->tcp_flags & TH_PUSH ? "PUSH|" : "");
printf("%s", args[4]->tcp_flags & TH_ACK ? "ACK|" : "");
printf("%s", args[4]->tcp_flags & TH_URG ? "URG|" : "");
printf("\s", args[4]->tcp_flags == 0 ? "null " : "");
printf("n;
} "
tcp:::receive
{
this->length = args[2]->ip_plength - args[4]->tcp_offset;
printf(" %3d %16s:%-5d <- %16s:%-5d %6d (", cpu,
args[2]->ip_daddr, args[4]->tcp_dport, args[2]->ip_saddr,
args[4]->tcp_sport, this->length);
printf("%s", args[4]->tcp_flags & TH_FIN ? "FIN|" : "");
printf("%s", args[4]->tcp_flags & TH_SYN ? "SYN|" : "");
printf("%s", args[4]->tcp_flags & TH_RST ? "RST|" : "");
printf("%s", args[4]->tcp_flags & TH_PUSH ? "PUSH|" : "");
printf("%s", args[4]->tcp_flags & TH_ACK ? "ACK|" : "");
printf("%s", args[4]->tcp_flags & TH_URG ? "URG|" : "");
printf("\s", args[4]->tcp_flags == 0 ? "null " : "");
printf("n;
} "
and now for round two π¦ β this one logs TCP connection state changes as they happen. the absolute drama of a connection going through its little lifecycle stages. main character energy fr fr π
#pragma D option quiet
#pragma D option switchrate=25hz
int last[int];
dtrace:::BEGIN
{
printf(" %12s %-20s %-20s %s\n",
"DELTA(us)", "OLD", "NEW", "TIMESTAMP");
}
tcp:::state-change
{
this->elapsed = (timestamp - last[args[1]->cs_cid]) / 1000;
printf(" %12d %-20s -> %-20s %d\n", this->elapsed,
tcp_state_string[args[5]->tcps_state],
tcp_state_string[args[3]->tcps_state], timestamp);
last[args[1]->cs_cid] = timestamp;
}
tcp:::state-change
/last[args[1]->cs_cid] == 0/
{
printf(" %12s %-20s -> %-20s %d\n", "-",
tcp_state_string[args[5]->tcps_state],
tcp_state_string[args[3]->tcps_state], timestamp);
last[args[1]->cs_cid] = timestamp;
}
ngl bestie, these scripts are shown exactly as they appear in the page β copy them character for character, don't go "fixing" the pretty-printing on me π π
π« COMPATIBILITY π«
this provider is compatible with the tcp provider in Solaris. so if you already know that one, you already know this one. slay, honestly π
π SEE ALSO π
go read these next, bestie, they're all part of the extended universe π«Ά
π HISTORY π
the tcp provider first appeared in FreeBSD 10.0. a real one. an icon. a legend β¨
π AUTHORS π
this manual page was written by Mark Johnston <markj@FreeBSD.org>. thank you for your service, king π
π BUGS π
okay so there IS a little something, and I need you to hear me out:
- the
tcps_localandtcps_activefields oftcpsinfo_tare not filled in by the translator. so don't go trusting those two, bestie, they're just sitting there empty π
and that's the whole page, bestie!! πβ¨ this one came out of the Debian edition dated August 1, 2018, and it's filed under DTRACE_TCP(4). go forth and trace those segments, you gorgeous packet whisperer π¦πΈπ
π¦ where this page comes from
- debian bookworm: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- debian trixie: freebsd-manpages 12.2-2 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu jammy: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu noble: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu questing: freebsd-manpages 12.2-2
- ubuntu resolute: freebsd-manpages 12.2-2build1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu stonking: freebsd-manpages 12.2-2build1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- Copyright (c) 2015 Mark Johnston <markj@FreeBSD.org>
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms