Home | History | Annotate | Line # | Download | only in Bin
tcptop_snv revision 1.1
      1 #!/usr/bin/ksh
      2 #
      3 # tcptop_snv - display top TCP network packets by process. 
      4 #              Written using DTrace (Solaris Nevada)
      5 #
      6 # This analyses TCP network packets and prints the responsible PID and UID,
      7 # plus standard details such as IP address and port. This captures traffic
      8 # of newly created TCP connections that were established while this program
      9 # was running. It can help identify which processes is causing TCP traffic.
     10 #
     11 # WARNING: This script may only work on Solaris Nevada and OpenSolaris
     12 # of the late 2007 vintage, since it uses the fbt provider to trace the raw
     13 # operation of a specific version of the kernel. In the future, a 'stable'
     14 # network provider should exist which will allow this to be written for that
     15 # and subsequent versions of the kernel. In the meantime, check for other
     16 # versions of this script in the /Net directory, and read the
     17 # Notes/ALLfbt_notes.txt for more background on fbt.
     18 #
     19 # $Id: tcptop_snv,v 1.1 2015/09/30 22:01:07 christos Exp $
     20 #
     21 # USAGE:	tcptop [-Ch] [-j|-Z] [interval [count]]
     22 #
     23 #		-C		# don't clear the screen
     24 #		-j		# print project IDs
     25 #		-Z		# print zone IDs
     26 #
     27 # FIELDS:
     28 #		UID     	user ID
     29 #		PID     	process ID
     30 #		CMD     	command
     31 #		LADDR		local IP address
     32 #		RADDR		remote IP address
     33 #		LPORT		local port number
     34 #		RPORT		remote port number
     35 #		SIZE    	packet size, bytes
     36 #		load		1 min load average
     37 #		TCPin		TCP inbound payload data
     38 #		TCPout		TCP outbound payload data
     39 #		ZONE    	zone ID
     40 #		PROJ    	project ID
     41 #
     42 # SEE ALSO:	tcpsnoop
     43 #
     44 # COPYRIGHT: Copyright (c) 2005, 2006 Brendan Gregg.
     45 #
     46 # CDDL HEADER START
     47 #
     48 #  The contents of this file are subject to the terms of the
     49 #  Common Development and Distribution License, Version 1.0 only
     50 #  (the "License").  You may not use this file except in compliance
     51 #  with the License.
     52 #
     53 #  You can obtain a copy of the license at Docs/cddl1.txt
     54 #  or http://www.opensolaris.org/os/licensing.
     55 #  See the License for the specific language governing permissions
     56 #  and limitations under the License.
     57 #
     58 # CDDL HEADER END
     59 #
     60 # Author: Brendan Gregg  [Sydney, Australia]
     61 #
     62 # ToDo: IPv6
     63 #
     64 # 05-Jul-2005  Brendan Gregg	Created this.
     65 # 03-Dec-2005	  "	 "	Fixed tcp_accept_finish bug, now 100% correct
     66 #				execname. Thanks Kias Belgaied for expertise.
     67 # 20-Apr-2006     "      "      Fixed SS_TCP_FAST_ACCEPT bug in build 31+.
     68 # 20-Apr-2006     "      "      Last update.
     69 # 30-Sep-2007	   "	  "	Bumped this for recent OpenSolaris/Nevada.
     70 #
     71 
     72 ##############################
     73 # --- Process Arguments ---
     74 #
     75 
     76 ### default variables
     77 opt_def=1; opt_clear=1; opt_zone=0; opt_proj=0; interval=5; count=-1
     78 
     79 ### process options
     80 while getopts ChjZ name
     81 do
     82 	case $name in
     83 	C)      opt_clear=0 ;;
     84 	j)      opt_proj=1; opt_def=0 ;;
     85 	Z)      opt_zone=1; opt_def=0 ;;
     86 	h|?)    cat <<-END >&2
     87 		USAGE: tcptop [-h] [-j|-Z] [interval [count]]
     88 		       tcptop                  # default output
     89 		                -C             # don't clear the screen
     90 		                -j             # print project ID
     91 		                -Z             # print zonename
     92 		  eg,
     93 		      tcptop                   # default is 5 sec interval
     94 		      tcptop 2                 # 2 second interval
     95 		      tcptop -C 1 10           # 10 x 1 sec samples, no clear
     96 		END
     97 		exit 1
     98 	esac
     99 done
    100 shift $(( $OPTIND - 1 ))
    101 
    102 ### option logic
    103 if [[ "$1" > 0 ]]; then
    104         interval=$1; shift
    105 fi
    106 if [[ "$1" > 0 ]]; then
    107         count=$1; shift
    108 fi
    109 if (( opt_proj && opt_zone )); then
    110 	opt_proj=0
    111 fi
    112 if (( opt_clear )); then
    113 	clearstr=`clear`
    114 else
    115 	clearstr=.
    116 fi
    117 
    118 #################################
    119 # --- Main Program, DTrace ---
    120 #
    121 /usr/sbin/dtrace -Cs <( print -r '
    122  /*
    123   * Command line arguments
    124   */
    125  inline int OPT_def   = '$opt_def';
    126  inline int OPT_zone  = '$opt_zone';
    127  inline int OPT_proj  = '$opt_proj';
    128  inline int OPT_clear = '$opt_clear';
    129  inline int INTERVAL  = '$interval';
    130  inline int COUNTER   = '$count';
    131  inline string CLEAR  = "'$clearstr'";
    132 
    133 #pragma D option quiet
    134 #pragma D option switchrate=10hz
    135 
    136 #include <sys/file.h>
    137 #include <inet/common.h>
    138 #include <sys/byteorder.h>
    139 #include <sys/socket.h>
    140 #include <sys/socketvar.h>
    141 
    142 /*
    143  * Print header
    144  */
    145 dtrace:::BEGIN
    146 {
    147 	/* starting values */
    148         counts = COUNTER;
    149         secs = INTERVAL;
    150 	TCP_out = 0;
    151 	TCP_in = 0;
    152 
    153 	printf("Tracing... Please wait.\n");
    154 }
    155 
    156 /*
    157  * TCP Process inbound connections
    158  *
    159  * 0x00200000 has been hardcoded. It was SS_TCP_FAST_ACCEPT, but was
    160  * renamed to SS_DIRECT around build 31.
    161  */
    162 fbt:sockfs:sotpi_accept:entry
    163 /(arg1 & FREAD) && (arg1 & FWRITE) && (args[0]->so_state & 0x00200000)/
    164 {
    165 	self->sop = args[0];
    166 }
    167 
    168 fbt:sockfs:sotpi_create:return
    169 /self->sop/
    170 {
    171 	self->nsop = (struct sonode *)arg1;
    172 }
    173 
    174 fbt:sockfs:sotpi_accept:return
    175 /self->nsop/
    176 {
    177 	this->tcpp = (tcp_t *)self->nsop->so_priv;
    178 	self->connp = (conn_t *)this->tcpp->tcp_connp;
    179 	tname[(int)self->connp] = execname;
    180 	tpid[(int)self->connp] = pid;
    181 	tuid[(int)self->connp] = uid;
    182 }
    183 
    184 fbt:sockfs:sotpi_accept:return
    185 {
    186 	self->nsop = 0;
    187 	self->sop = 0;
    188 }
    189 
    190 /*
    191  * TCP Process outbound connections
    192  */
    193 fbt:ip:tcp_connect:entry
    194 {
    195 	this->tcpp = (tcp_t *)arg0;
    196 	self->connp = (conn_t *)this->tcpp->tcp_connp;
    197 	tname[(int)self->connp] = execname;
    198 	tpid[(int)self->connp] = pid;
    199 	tuid[(int)self->connp] = uid;
    200 	OPT_proj ? tproj[(int)self->connp] = curpsinfo->pr_projid : 1;
    201 }
    202 
    203 /*
    204  * TCP Data translations
    205  */
    206 fbt:sockfs:sotpi_accept:return,
    207 fbt:ip:tcp_connect:return
    208 /self->connp/
    209 {
    210 	/* fetch ports */
    211 #if defined(_BIG_ENDIAN)
    212 	self->lport = self->connp->u_port.tcpu_ports.tcpu_lport;
    213 	self->fport = self->connp->u_port.tcpu_ports.tcpu_fport;
    214 #else
    215 	self->lport = BSWAP_16(self->connp->u_port.tcpu_ports.tcpu_lport);
    216 	self->fport = BSWAP_16(self->connp->u_port.tcpu_ports.tcpu_fport);
    217 #endif
    218 
    219 	/* fetch IPv4 addresses */
    220 	this->fad12 =
    221 	    (int)self->connp->connua_v6addr.connua_faddr._S6_un._S6_u8[12];
    222 	this->fad13 =
    223 	    (int)self->connp->connua_v6addr.connua_faddr._S6_un._S6_u8[13];
    224 	this->fad14 =
    225 	    (int)self->connp->connua_v6addr.connua_faddr._S6_un._S6_u8[14];
    226 	this->fad15 =
    227 	    (int)self->connp->connua_v6addr.connua_faddr._S6_un._S6_u8[15];
    228 	this->lad12 =
    229 	    (int)self->connp->connua_v6addr.connua_laddr._S6_un._S6_u8[12];
    230 	this->lad13 =
    231 	    (int)self->connp->connua_v6addr.connua_laddr._S6_un._S6_u8[13];
    232 	this->lad14 =
    233 	    (int)self->connp->connua_v6addr.connua_laddr._S6_un._S6_u8[14];
    234 	this->lad15 =
    235 	    (int)self->connp->connua_v6addr.connua_laddr._S6_un._S6_u8[15];
    236 
    237 	/* convert type for use with lltostr() */
    238 	this->fad12 = this->fad12 < 0 ? 256 + this->fad12 : this->fad12;
    239 	this->fad13 = this->fad13 < 0 ? 256 + this->fad13 : this->fad13;
    240 	this->fad14 = this->fad14 < 0 ? 256 + this->fad14 : this->fad14;
    241 	this->fad15 = this->fad15 < 0 ? 256 + this->fad15 : this->fad15;
    242 	this->lad12 = this->lad12 < 0 ? 256 + this->lad12 : this->lad12;
    243 	this->lad13 = this->lad13 < 0 ? 256 + this->lad13 : this->lad13;
    244 	this->lad14 = this->lad14 < 0 ? 256 + this->lad14 : this->lad14;
    245 	this->lad15 = this->lad15 < 0 ? 256 + this->lad15 : this->lad15;
    246 
    247 	/* stringify addresses */
    248 	self->faddr = strjoin(lltostr(this->fad12), ".");
    249 	self->faddr = strjoin(self->faddr, strjoin(lltostr(this->fad13), "."));
    250 	self->faddr = strjoin(self->faddr, strjoin(lltostr(this->fad14), "."));
    251 	self->faddr = strjoin(self->faddr, lltostr(this->fad15 + 0));
    252 	self->laddr = strjoin(lltostr(this->lad12), ".");
    253 	self->laddr = strjoin(self->laddr, strjoin(lltostr(this->lad13), "."));
    254 	self->laddr = strjoin(self->laddr, strjoin(lltostr(this->lad14), "."));
    255 	self->laddr = strjoin(self->laddr, lltostr(this->lad15 + 0));
    256 
    257 	/* fix direction and save values */
    258 	tladdr[(int)self->connp] = self->laddr;
    259 	tfaddr[(int)self->connp] = self->faddr;
    260 	tlport[(int)self->connp] = self->lport;
    261 	tfport[(int)self->connp] = self->fport;
    262 
    263 	/* all systems go */
    264 	tok[(int)self->connp] = 1;
    265 }
    266 
    267 /*
    268  * TCP Clear connp
    269  */
    270 fbt:ip:tcp_get_conn:return
    271 {
    272 	/* Q_TO_CONN */
    273 	this->connp = (conn_t *)arg1;
    274 	tok[(int)this->connp] = 0;
    275 	tpid[(int)this->connp] = 0;
    276 	tuid[(int)this->connp] = 0;
    277 	tname[(int)this->connp] = 0;
    278 	tproj[(int)this->connp] = 0;
    279 }
    280 
    281 /*
    282  * TCP Process "port closed"
    283  */
    284 fbt:ip:tcp_xmit_early_reset:entry
    285 {
    286 	this->queuep = args[7]->tcps_g_q;
    287 	this->connp = (conn_t *)this->queuep->q_ptr;
    288 	this->tcpp = (tcp_t *)this->connp->conn_tcp;
    289 	self->zoneid = this->connp->conn_zoneid;
    290 
    291 	/* split addresses */
    292 	this->ipha = (ipha_t *)args[1]->b_rptr;
    293 	this->fad15 = (this->ipha->ipha_src & 0xff000000) >> 24;
    294 	this->fad14 = (this->ipha->ipha_src & 0x00ff0000) >> 16;
    295 	this->fad13 = (this->ipha->ipha_src & 0x0000ff00) >> 8;
    296 	this->fad12 = (this->ipha->ipha_src & 0x000000ff);
    297 	this->lad15 = (this->ipha->ipha_dst & 0xff000000) >> 24;
    298 	this->lad14 = (this->ipha->ipha_dst & 0x00ff0000) >> 16;
    299 	this->lad13 = (this->ipha->ipha_dst & 0x0000ff00) >> 8;
    300 	this->lad12 = (this->ipha->ipha_dst & 0x000000ff);
    301 
    302 	/* stringify addresses */
    303 	self->faddr = strjoin(lltostr(this->fad12), ".");
    304 	self->faddr = strjoin(self->faddr, strjoin(lltostr(this->fad13), "."));
    305 	self->faddr = strjoin(self->faddr, strjoin(lltostr(this->fad14), "."));
    306 	self->faddr = strjoin(self->faddr, lltostr(this->fad15 + 0));
    307 	self->laddr = strjoin(lltostr(this->lad12), ".");
    308 	self->laddr = strjoin(self->laddr, strjoin(lltostr(this->lad13), "."));
    309 	self->laddr = strjoin(self->laddr, strjoin(lltostr(this->lad14), "."));
    310 	self->laddr = strjoin(self->laddr, lltostr(this->lad15 + 0));
    311 
    312 	self->reset = 1;
    313 }
    314 
    315 /*
    316  * TCP Fetch "port closed" ports
    317  */
    318 fbt:ip:tcp_xchg:entry
    319 /self->reset/
    320 {
    321 #if defined(_BIG_ENDIAN)
    322 	self->lport = (uint16_t)arg0;
    323 	self->fport = (uint16_t)arg1;
    324 #else
    325 	self->lport = BSWAP_16((uint16_t)arg0);
    326 	self->fport = BSWAP_16((uint16_t)arg1);
    327 #endif
    328 	self->lport = BE16_TO_U16(arg0);
    329 	self->fport = BE16_TO_U16(arg1);
    330 }
    331 
    332 /*
    333  * TCP Print "port closed"
    334  */
    335 fbt:ip:tcp_xmit_early_reset:return
    336 {
    337 	self->name = "<closed>";
    338 	self->pid = 0;
    339 	self->uid = 0;
    340 	self->proj = 0;
    341 	self->size = 54 * 2;	/* should check trailers */
    342 	OPT_def ? @out[self->uid, self->pid, self->laddr, self->lport,
    343 	    self->faddr, self->fport, self->name] = sum(self->size) : 1;
    344 	OPT_zone ? @out[self->zoneid, self->pid, self->laddr, self->lport,
    345 	    self->faddr, self->fport, self->name] = sum(self->size) : 1;
    346 	OPT_proj ? @out[self->proj, self->pid, self->laddr, self->lport,
    347 	    self->faddr, self->fport, self->name] = sum(self->size) : 1;
    348 	self->reset = 0;
    349 	self->size = 0;
    350 	self->name = 0;
    351 }
    352 
    353 /*
    354  * TCP Process Write
    355  */
    356 fbt:ip:tcp_send_data:entry
    357 {
    358 	self->conn_p = (conn_t *)args[0]->tcp_connp;
    359 }
    360 
    361 fbt:ip:tcp_send_data:entry
    362 /tok[(int)self->conn_p]/
    363 {
    364         self->size = msgdsize(args[2]) + 14;	/* should check trailers */
    365 	self->uid = tuid[(int)self->conn_p];
    366 	self->laddr = tladdr[(int)self->conn_p];
    367 	self->faddr = tfaddr[(int)self->conn_p];
    368 	self->lport = tlport[(int)self->conn_p];
    369 	self->fport = tfport[(int)self->conn_p];
    370 	OPT_proj ? self->proj = tproj[(int)self->conn_p] : 1;
    371 	self->zoneid = self->conn_p->conn_zoneid;
    372         self->ok = 2;
    373 
    374 	/* follow inetd -> in.* transitions */
    375 	self->name = pid && (tname[(int)self->conn_p] == "inetd") ?
    376 	    execname : tname[(int)self->conn_p];
    377 	self->pid = pid && (tname[(int)self->conn_p] == "inetd") ?
    378 	    pid : tpid[(int)self->conn_p];
    379 	tname[(int)self->conn_p] = self->name;
    380 	tpid[(int)self->conn_p] = self->pid;
    381 }
    382 
    383 /*
    384  * TCP Process Read
    385  */
    386 fbt:ip:tcp_rput_data:entry
    387 {
    388 	self->conn_p = (conn_t *)arg0;
    389         self->size = msgdsize(args[1]) + 14;	/* should check trailers */
    390 }
    391 
    392 fbt:ip:tcp_rput_data:entry
    393 /tok[(int)self->conn_p]/
    394 {
    395 	self->uid = tuid[(int)self->conn_p];
    396 	self->laddr = tladdr[(int)self->conn_p];
    397 	self->faddr = tfaddr[(int)self->conn_p];
    398 	self->lport = tlport[(int)self->conn_p];
    399 	self->fport = tfport[(int)self->conn_p];
    400 	OPT_proj ? self->proj = tproj[(int)self->conn_p] : 1;
    401 	self->zoneid = self->conn_p->conn_zoneid;
    402 	self->ok = 2;
    403 
    404 	/* follow inetd -> in.* transitions */
    405 	self->name = pid && (tname[(int)self->conn_p] == "inetd") ?
    406 	    execname : tname[(int)self->conn_p];
    407 	self->pid = pid && (tname[(int)self->conn_p] == "inetd") ?
    408 	    pid : tpid[(int)self->conn_p];
    409 	tname[(int)self->conn_p] = self->name;
    410 	tpid[(int)self->conn_p] = self->pid;
    411 }
    412 
    413 /*
    414  * TCP Complete printing outbound handshake
    415  */
    416 fbt:ip:tcp_connect:return
    417 /self->connp/
    418 {
    419 	self->name = tname[(int)self->connp];
    420 	self->pid = tpid[(int)self->connp];
    421 	self->uid = tuid[(int)self->connp];
    422 	self->zoneid = self->connp->conn_zoneid;
    423 	OPT_proj ? self->proj = tproj[(int)self->connp] : 1;
    424 	self->size = 54;	/* should check trailers */
    425 
    426 	/* this packet occured before connp was fully established */
    427 	OPT_def ? @out[self->uid, self->pid, self->laddr, self->lport,
    428 	    self->faddr, self->fport, self->name] = sum(self->size) : 1;
    429 	OPT_zone ? @out[self->zoneid, self->pid, self->laddr, self->lport,
    430 	    self->faddr, self->fport, self->name] = sum(self->size) : 1;
    431 	OPT_proj ? @out[self->proj, self->pid, self->laddr, self->lport,
    432 	    self->faddr, self->fport, self->name] = sum(self->size) : 1;
    433 }
    434 
    435 /*
    436  * TCP Complete printing inbound handshake
    437  */
    438 fbt:sockfs:sotpi_accept:return
    439 /self->connp/
    440 {
    441 	self->name = tname[(int)self->connp];
    442 	self->pid = tpid[(int)self->connp];
    443 	self->uid = tuid[(int)self->connp];
    444 	self->zoneid = self->connp->conn_zoneid;
    445 	OPT_proj ? self->proj = tproj[(int)self->connp] : 1;
    446 	self->size = 54 * 3;	/* should check trailers */
    447 
    448 	/* these packets occured before connp was fully established */
    449 	OPT_def ? @out[self->uid, self->pid, self->laddr, self->lport,
    450 	    self->faddr, self->fport, self->name] = sum(self->size) : 1;
    451 	OPT_zone ? @out[self->zoneid, self->pid, self->laddr, self->lport,
    452 	    self->faddr, self->fport, self->name] = sum(self->size) : 1;
    453 	OPT_proj ? @out[self->proj, self->pid, self->laddr, self->lport,
    454 	    self->faddr, self->fport, self->name] = sum(self->size) : 1;
    455 }
    456 
    457 /*
    458  * TCP Save data
    459  */
    460 fbt:ip:tcp_send_data:entry,
    461 fbt:ip:tcp_rput_data:entry
    462 /self->ok == 2/ 
    463 {
    464 	/* save r+w data*/
    465 	OPT_def ? @out[self->uid, self->pid, self->laddr, self->lport,
    466 	    self->faddr, self->fport, self->name] = sum(self->size) : 1;
    467 	OPT_zone ? @out[self->zoneid, self->pid, self->laddr, self->lport,
    468 	    self->faddr, self->fport, self->name] = sum(self->size) : 1;
    469 	OPT_proj ? @out[self->proj, self->pid, self->laddr, self->lport,
    470 	    self->faddr, self->fport, self->name] = sum(self->size) : 1;
    471 }
    472 
    473 /* 
    474  * TCP Clear connect variables
    475  */
    476 fbt:sockfs:sotpi_accept:return,
    477 fbt:ip:tcp_connect:return
    478 /self->connp/
    479 {
    480 	self->faddr = 0;
    481 	self->laddr = 0;
    482 	self->fport = 0;
    483 	self->lport = 0;
    484 	self->connp = 0;
    485 	self->name = 0;
    486 	self->pid = 0;
    487 	self->uid = 0;
    488 }
    489 
    490 /* 
    491  * TCP Clear r/w variables
    492  */
    493 fbt:ip:tcp_send_data:entry,
    494 fbt:ip:tcp_rput_data:entry
    495 {
    496 	self->ok = 0;
    497 	self->uid = 0;
    498 	self->pid = 0;
    499 	self->size = 0;
    500 	self->name = 0;
    501 	self->lport = 0;
    502 	self->fport = 0;
    503 	self->laddr = 0;
    504 	self->faddr = 0;
    505 	self->conn_p = 0;
    506 	self->zoneid = 0;
    507 	self->proj = 0;
    508 }
    509 
    510 /*
    511  * TCP Systemwide Stats
    512  */
    513 mib:::tcpOutDataBytes       { TCP_out += args[0]; }
    514 mib:::tcpRetransBytes       { TCP_out += args[0]; }
    515 mib:::tcpInDataInorderBytes { TCP_in  += args[0]; }
    516 mib:::tcpInDataDupBytes     { TCP_in  += args[0]; }
    517 mib:::tcpInDataUnorderBytes { TCP_in  += args[0]; }
    518 
    519 /*
    520  * Timer
    521  */
    522 profile:::tick-1sec
    523 {
    524         secs--;
    525 }
    526 
    527 /*
    528  * Print Report
    529  */
    530 profile:::tick-1sec
    531 /secs == 0/
    532 {
    533         /* fetch 1 min load average */
    534         this->load1a  = `hp_avenrun[0] / 65536;
    535         this->load1b  = ((`hp_avenrun[0] % 65536) * 100) / 65536;
    536 
    537 	/* convert TCP counters to Kbytes */
    538 	TCP_out /= 1024;
    539 	TCP_in  /= 1024;
    540 
    541 	/* print status */
    542 	OPT_clear ? printf("%s", CLEAR) : 1;
    543         printf("%Y,  load: %d.%02d,  TCPin: %6d KB,  TCPout: %6d KB\n\n",
    544             walltimestamp, this->load1a, this->load1b, TCP_in, TCP_out);
    545 
    546 	/* print headers */
    547 	OPT_def  ? printf(" UID ") : 1;
    548 	OPT_proj ? printf("PROJ ") : 1;
    549 	OPT_zone ? printf("ZONE ") : 1;
    550         printf("%6s %-15s %5s %-15s %5s %9s %s\n",
    551 	    "PID", "LADDR", "LPORT", "RADDR", "RPORT", "SIZE", "NAME");
    552 
    553 	/* print data */
    554         printa("%4d %6d %-15s %5d %-15s %5d %@9d %s\n", @out);
    555 	printf("\n");
    556 
    557 	/* clear data */
    558         trunc(@out);
    559 	TCP_in = 0;
    560 	TCP_out = 0;
    561         secs = INTERVAL;
    562         counts--;
    563 }
    564 
    565 /*
    566  * End of program
    567  */
    568 profile:::tick-1sec
    569 /counts == 0/
    570 {
    571         exit(0);
    572 }
    573 
    574 /*
    575  * Cleanup for Ctrl-C
    576  */
    577 dtrace:::END
    578 {
    579         trunc(@out);
    580 }
    581 ')
    582