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