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