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_t argument shows you the version-agnostic fields of the IP header
  • the tcpinfo_t argument shows you the TCP header
  • the tcpsinfo_t argument 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 from SYN-SENT to ESTABLISHED. 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 to 0.

πŸ“ 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 to 0.
  • uint64_t cs_cid β€” a pointer to the struct inpcb for this packet, or NULL.
  • pid_t cs_pid β€” always set to 0.

πŸ“ 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 a struct 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 the tcp provider.

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_local and tcps_active fields of tcpsinfo_t are 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