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