tcpio.d Reports TCP Packet Details

The following DTrace script traces TCP packets and prints various details:

#!/usr/sbin/dtrace -s

#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);
}

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);
}

tcp:::send,
tcp:::receive
{
        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 & TH_ECE ? "ECE|" : "");
        printf("%s", args[4]->tcp_flags & TH_CWR ? "CWR|" : "");
        printf("%s", args[4]->tcp_flags == 0 ? "null " : "");
        printf("\b)\n");
}

This example output has captured a TCP handshake:

# ./tcpio.d
 CPU            LADDR:LPORT               RADDR:RPORT  BYTES  FLAGS
   1     192.0.2.8/27:22    ->    192.0.2.40/27:60337    464  (PUSH|ACK)
   1     192.0.2.8/27:22    ->    192.0.2.40/27:60337     48  (PUSH|ACK)
   2     192.0.2.8/27:22    ->    192.0.2.40/27:60337     20  (PUSH|ACK)
   3     192.0.2.8/27:22    <-    192.0.2.40/27:60337      0  (SYN)
   3     192.0.2.8/27:22    ->    192.0.2.40/27:60337      0  (SYN|ACK)
   3     192.0.2.8/27:22    <-    192.0.2.40/27:60337      0  (ACK)
   3     192.0.2.8/27:22    <-    192.0.2.40/27:60337      0  (ACK)
   3     192.0.2.8/27:22    <-    192.0.2.40/27:60337     20  (PUSH|ACK)
   3     192.0.2.8/27:22    ->    192.0.2.40/27:60337      0  (ACK)
   3     192.0.2.8/27:22    <-    192.0.2.40/27:60337      0  (ACK)
   3     192.0.2.8/27:22    <-    192.0.2.40/27:60337    376  (PUSH|ACK)
   3     192.0.2.8/27:22    ->    192.0.2.40/27:60337      0  (ACK)
   3     192.0.2.8/27:22    <-    192.0.2.40/27:60337     24  (PUSH|ACK)
   2     192.0.2.8/27:22    ->    192.0.2.40/27:60337    736  (PUSH|ACK)
   3     192.0.2.8/27:22    <-    192.0.2.40/27:60337      0  (ACK)

The following table describes the output fields.

Field Description

CPU

CPU id that event occurred on

LADDR

Local IP address

LPORT

Local TCP port

RADDR

Remote IP address

RPORT

Remote TCP port

BYTES

TCP payload bytes

FLAGS

TCP flags

Note:

The output may be shuffled slightly on multi-CPU servers due to DTrace per-CPU buffering, and events such as the TCP handshake can be printed out of order. Keep an eye on changes in the CPU column, or add a timestamp column to this script and post sort.