conntrack DTrace script: connections by process, user and port on Solaris/Opensolaris

I’ve created conntrack DTrace script for Solaris and Opensolaris to monitor all outgoing TCP and UDP connections by process, user and port.

It has some filtering capabilities allowing to filter traffic by port, process or user.

In a standard unix system it’s rather difficult to know what process are using the network and how. With this DTrace script you have the chance to know what user and process are using the network. You can also get what process is bound to a port.

Here are some examples:

Usage:

# ./conntrack -h
USAGE: ./conntrack [-h] [-p port] [-c command] [-u user]
   -p port         # filter by port (incompatible with user and command)
   -c command	   # filter by command (incompatible with port and user)
   -u user	   # filter by user (incompatible with port and command)
  eg,
       ./conntrack -p 22    # snoop connections to port 22

- What process is listening on port 23?:

# ./conntrack -p 23
             PROC   PID   UID      ZONE     LPORT     RPORT TYPE SOURCE          
            sched     0     0    global        23     36438  TCP 10.164.50.105
       in.telnetd  2084     0    global        23     36438  TCP 10.164.50.105
            sched     0     0    global        23     36438  TCP 10.164.50.105
            sched     0     0    global        23     36438  TCP 10.164.50.105
            sched     0     0    global        23     36438  TCP 10.164.50.105
       in.telnetd  2084     0    global        23     36438  TCP 10.164.50.105
       in.telnetd  2084     0    global        23     36438  TCP 10.164.50.105
            sched     0     0    global        23     36438  TCP 10.164.50.105
            sched     0     0    global        23     36438  TCP 10.164.50.105
       in.telnetd  2084     0    global        23     36438  TCP 10.164.50.105
       in.telnetd  2084     0    global        23     36438  TCP 10.164.50.105
            login  2086     0    global        23     36438  TCP 10.164.50.105

- Where is the firefox process connecting?

# ./conntrack -c firefox-bin
             PROC   PID   UID      ZONE     LPORT     RPORT TYPE SOURCE          
      firefox-bin  1305   100    global     63640        80  TCP 212.58.226.138
      firefox-bin  1305   100    global     60055        80  TCP 63.245.209.93
      firefox-bin  1305   100    global     38147        80  TCP 63.245.209.93
      firefox-bin  1305   100    global     61736        80  TCP 212.58.226.138

- Where and how is sending information a specific user?

# ./conntrack -u sergio
             PROC   PID   UID      ZONE     LPORT     RPORT TYPE SOURCE          
      firefox-bin  1305   100    global     57245        80  TCP 74.125.39.105
      firefox-bin  1305   100    global     57245        80  TCP 74.125.39.105
      firefox-bin  1305   100    global     57245        80  TCP 74.125.39.105
      firefox-bin  1305   100    global     57245        80  TCP 74.125.39.105
      firefox-bin  1305   100    global     64782        80  TCP 209.85.129.100
      firefox-bin  1305   100    global     57245        80  TCP 74.125.39.105
  thunderbird-bin  1323   100    global     45556       993  TCP 10.164.50.28
  thunderbird-bin  1323   100    global     45556       993  TCP 10.164.50.28
  thunderbird-bin  1323   100    global     45556       993  TCP 10.164.50.28
  thunderbird-bin  1323   100    global     45556       993  TCP 10.164.50.28

You can download the latest version here: conntrack
Here is the script:

#!/usr/bin/ksh
#
# conntrack - returns network, user and process information
#               Written using DTrace.
#
# 
# $Id: conntrack 1 2009-08-20 14:16:26Z sergio $
# 
# USAGE:       conntrack [-h] [-p port] [-c command] [-u user] 
#   -p port         # filter by port (incompatible with user and command)
#   -c command      # filter by command (incompatible with port and user)
#   -u user         # filter by user (incompatible with port and command)
#  eg,
#       conntrack -p 22          # snoop connections to port 22
#       conntrack -u sergio      # snoop connections for user sergio
#       conntrack -c firefox-bin # snoop connections for firefox-bin cmd
#
# Must be root or with DTrace role privilege
# 
# NOTES: This script uses dtrace so it should work on Solaris or OpenSolaris
# 
# THANKS:
# 
# COPYRIGHT: Copyright (c) 2008 Sergio Rodriguez de Guzman Martinez
# 
# CDDL HEADER START
# 
#  The contents of this file are subject to the terms of the
#  Common Development and Distribution License, Version 1.0 only
#  (the "License").  You may not use this file except in compliance
#  with the License.
#
#  You can obtain a copy of the license at Docs/cddl1.txt
#  or http://www.opensolaris.org/os/licensing.
#  See the License for the specific language governing permissions
#  and limitations under the License.
# 
# CDDL HEADER END
# 
# Author: Sergio Rodriguez de Guzman [Madrid, Spain]
# 
# 20-08-2009  Sergio Rodriguez de Guzman   Created this.
# 
#
 
 
#if [ `/usr/xpg4/bin/id -u` -ne 0 ]; then
#	echo "You must be root!"
#	exit 1
#fi
 
opt_port=0; opt_command=""; opt_user=0;
 
### Process options
while getopts hp:c:u: name
do
        case $name in
        p)      opt_port=$OPTARG; opt_command=""; opt_user=-1;;
        c)      opt_command=$OPTARG; opt_port=-1; opt_user=-1;;
        u)      opt_user=`/usr/xpg4/bin/id -u $OPTARG`
		if [[ $opt_user  == "" ]]; then
			print "User $OPTARG not found"
			exit 1
		fi
		opt_command=""
		opt_port=-1;;
        h|?)    cat <<-END >&2
USAGE: $0 [-h] [-p port] [-c command] [-u user]
   -p port         # filter by port (incompatible with user and command)
   -c command	   # filter by command (incompatible with port and user)
   -u user	   # filter by user (incompatible with port and command)
  eg,
       $0 -p 22    # snoop connections to port 22
END
                exit 1
        esac
done
 
 
#################################
# --- Main Program, DTrace ---
 
/usr/sbin/dtrace -C -s <( print -r '
 
#include <sys/file.h>
#include <sys/types.h>
#include <sys/byteorder.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
 
 #pragma D option quiet
 #pragma D option switchrate=10hz
 
 inline unsigned char OPT_port    = '$opt_port';
 inline unsigned char OPT_user    = '$opt_user';
 inline string OPT_command        = "'$opt_command'";
 
BEGIN
{
	printf("%17s%6s%6s%10s%10s%10s%5s %-16s\n", "PROC", "PID", "UID", "ZONE", "LPORT", "RPORT", "TYPE", "SOURCE");
}
 
::udp_send_data:entry
/ args[0]->udp_port == OPT_port && pid != 0 || OPT_port == 0 || OPT_command == execname || OPT_user == uid /
{
	/* Get source Address */
	self->octect[0] = (uint8_t) args[0]->udp_v6src._S6_un._S6_u8[12];
	self->octect[1] = (uint8_t) args[0]->udp_v6src._S6_un._S6_u8[13];
	self->octect[2] = (uint8_t) args[0]->udp_v6src._S6_un._S6_u8[14];
	self->octect[3] = (uint8_t) args[0]->udp_v6src._S6_un._S6_u8[15];
 	printf("%17s%6d%6d%10s%10d%10d%5s %d.%d.%d.%d\n", execname, pid, uid, zonename, args[0]->udp_port, 0, "UDP", self->octect[0], self->octect[1], self->octect[2], self->octect[3]);
	/* exit (0); */
}
 
::tcp_send_data:entry
{
	/* Get LPort details */
	dig1 = (unsigned int) args[0]->tcp_tcph->th_lport[0];
	dig2 = (unsigned int) args[0]->tcp_tcph->th_lport[1];
	dig1 = dig1<<8;
	self->lport = dig1 + dig2;
	/* Get RPort details */
	dig1 = (unsigned int) args[0]->tcp_tcph->th_fport[0];
	dig2 = (unsigned int) args[0]->tcp_tcph->th_fport[1];
	dig1 = dig1<<8;
	self->rport = dig1 + dig2;
	/* Get Source Address */
#if defined(_BIG_ENDIAN)
	ipAddress = (unsigned int) BSWAP_32(args[0]->tcp_ipha->ipha_dst);
#else
	ipAddress = (unsigned int) args[0]->tcp_ipha->ipha_dst;
#endif
	self->octect[0] = ipAddress >> 0*8 & 0xFF;
	self->octect[1] = ipAddress >> 1*8 & 0xFF;
	self->octect[2] = ipAddress >> 2*8 & 0xFF;
	self->octect[3] = ipAddress >> 3*8 & 0xFF;
	self->ok = 1;
}
 
 
::tcp_send_data:entry
/ self->ok && self->lport == OPT_port || self->rport == OPT_port && pid != 0 || OPT_port == 0 || OPT_command == execname || OPT_user == uid /
{
 	printf("%17s%6d%6d%10s%10d%10d%5s %d.%d.%d.%d\n", execname, pid, uid, zonename, self->lport, self->rport, "TCP", self->octect[0], self->octect[1], self->octect[2], self->octect[3]);
	self->ok = 0;
	/* exit (0); */
}
 
')

I hope you find it useful. It is very for me! ;)

There is a very good Sun course for DTrace development: Dynamic Performance Tuning and Troubleshooting With DTrace

/Sergio