1 /* 2 * Copyright (c) 2013 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 #include <sys/param.h> 35 #include <sys/queue.h> 36 #include <sys/tree.h> 37 #include <sys/socket.h> 38 #include <sys/socketvar.h> 39 #include <sys/protosw.h> 40 #include <sys/sysctl.h> 41 42 #include <netinet/in.h> 43 #include <arpa/inet.h> 44 #include <net/route.h> 45 #include <netinet/in_systm.h> 46 #include <netinet/ip.h> 47 #ifdef INET6 48 #include <netinet/ip6.h> 49 #endif 50 #include <netinet/in_pcb.h> 51 #include <netinet/ip_icmp.h> 52 #include <netinet/icmp_var.h> 53 #include <netinet/ip_var.h> 54 #include <netinet/tcp.h> 55 #include <netinet/tcpip.h> 56 #include <netinet/tcp_seq.h> 57 #include <netinet/tcp_fsm.h> 58 #include <netinet/tcp_timer.h> 59 #include <netinet/tcp_var.h> 60 #include <netinet/tcp_debug.h> 61 #include <netinet/udp.h> 62 #include <netinet/udp_var.h> 63 64 #include <err.h> 65 #include <errno.h> 66 #include <netdb.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <nlist.h> 70 #include <paths.h> 71 #include "systat.h" 72 #include "extern.h" 73 74 struct mytcpcb { 75 RB_ENTRY(mytcpcb) rb_node; 76 int seq; 77 struct xtcpcb xtcp; 78 struct xtcpcb last_xtcp; 79 }; 80 81 static int 82 mytcpcb_cmp(struct mytcpcb *tcp1, struct mytcpcb *tcp2) 83 { 84 int r; 85 86 /* 87 * Low local or foreign port comes first (local has priority). 88 */ 89 if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_lport) >= 1024 && 90 ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_lport) >= 1024) { 91 if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_fport) < 92 ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_fport)) 93 return(-1); 94 if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_fport) > 95 ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_fport)) 96 return(1); 97 } 98 99 if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_lport) < 100 ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_lport)) 101 return(-1); 102 if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_lport) > 103 ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_lport)) 104 return(1); 105 if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_fport) < 106 ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_fport)) 107 return(-1); 108 if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_fport) > 109 ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_fport)) 110 return(1); 111 112 /* 113 * Sort IPV4 vs IPV6 addresses 114 */ 115 if ((tcp1->xtcp.xt_inp.inp_vflag & (INP_IPV4|INP_IPV6)) < 116 (tcp2->xtcp.xt_inp.inp_vflag & (INP_IPV4|INP_IPV6))) 117 return(-1); 118 if ((tcp1->xtcp.xt_inp.inp_vflag & (INP_IPV4|INP_IPV6)) > 119 (tcp2->xtcp.xt_inp.inp_vflag & (INP_IPV4|INP_IPV6))) 120 return(1); 121 122 /* 123 * Local and foreign addresses 124 */ 125 if (tcp1->xtcp.xt_inp.inp_vflag & INP_IPV4) { 126 if (ntohl(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_laddr.s_addr) < 127 ntohl(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_laddr.s_addr)) 128 return(-1); 129 if (ntohl(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_laddr.s_addr) > 130 ntohl(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_laddr.s_addr)) 131 return(1); 132 if (ntohl(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_faddr.s_addr) < 133 ntohl(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_faddr.s_addr)) 134 return(-1); 135 if (ntohl(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_faddr.s_addr) > 136 ntohl(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_faddr.s_addr)) 137 return(1); 138 } else if (tcp1->xtcp.xt_inp.inp_vflag & INP_IPV6) { 139 r = bcmp(&tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr, 140 &tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr, 141 sizeof(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr)); 142 if (r) 143 return(r); 144 } else { 145 r = bcmp(&tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr, 146 &tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr, 147 sizeof(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr)); 148 if (r) 149 return(r); 150 } 151 return(0); 152 } 153 154 struct mytcpcb_tree; 155 RB_HEAD(mytcpcb_tree, mytcpcb); 156 RB_PROTOTYPE(mytcpcb_tree, mytcpcb, rb_node, mytcpcb_cmp); 157 RB_GENERATE(mytcpcb_tree, mytcpcb, rb_node, mytcpcb_cmp); 158 159 static struct mytcpcb_tree mytcp_tree; 160 static struct timeval tv_curr; 161 static struct timeval tv_last; 162 static struct tcp_stats tcp_curr; 163 static struct tcp_stats tcp_last; 164 static int tcp_pcb_seq; 165 166 static const char *numtok(double value); 167 static void netbwline(int row, struct mytcpcb *elm, double delta_time); 168 const char * netaddrstr(u_char vflags, union in_dependaddr *depaddr, 169 u_int16_t port); 170 static void updatepcb(struct xtcpcb *xtcp); 171 172 #define DELTARATE(field) \ 173 ((double)(tcp_curr.field - tcp_last.field) / delta_time) 174 175 #define DELTAELM(field) \ 176 ((double)(tcp_seq_diff_t)(elm->xtcp.field - \ 177 elm->last_xtcp.field) / \ 178 delta_time) 179 180 #define DELTAELMSCALE(field, scale) \ 181 ((double)((tcp_seq_diff_t)(elm->xtcp.field - \ 182 elm->last_xtcp.field) << scale) / \ 183 delta_time) 184 185 WINDOW * 186 opennetbw(void) 187 { 188 RB_INIT(&mytcp_tree); 189 return (subwin(stdscr, LINES-0-1, 0, 0, 0)); 190 } 191 192 void 193 closenetbw(WINDOW *w) 194 { 195 struct mytcpcb *mytcp; 196 197 while ((mytcp = RB_ROOT(&mytcp_tree)) != NULL) { 198 RB_REMOVE(mytcpcb_tree, &mytcp_tree, mytcp); 199 free(mytcp); 200 } 201 202 if (w != NULL) { 203 wclear(w); 204 wrefresh(w); 205 delwin(w); 206 } 207 } 208 209 int 210 initnetbw(void) 211 { 212 return(1); 213 } 214 215 void 216 fetchnetbw(void) 217 { 218 struct tcp_stats tcp_array[SMP_MAXCPU]; 219 struct xtcpcb *tcp_pcbs; 220 size_t npcbs; 221 size_t len; 222 size_t i; 223 size_t j; 224 size_t ncpus; 225 226 /* 227 * Extract PCB list 228 */ 229 len = 0; 230 if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) < 0) 231 return; 232 len += 128 * sizeof(tcp_pcbs[0]); 233 tcp_pcbs = malloc(len); 234 if (sysctlbyname("net.inet.tcp.pcblist", tcp_pcbs, &len, NULL, 0) < 0) { 235 free(tcp_pcbs); 236 return; 237 } 238 npcbs = len / sizeof(tcp_pcbs[0]); 239 ++tcp_pcb_seq; 240 241 for (i = 0; i < npcbs; ++i) { 242 if (tcp_pcbs[i].xt_len != sizeof(tcp_pcbs[0])) 243 break; 244 updatepcb(&tcp_pcbs[i]); 245 } 246 free(tcp_pcbs); 247 248 /* 249 * General stats 250 */ 251 len = sizeof(tcp_array); 252 if (sysctlbyname("net.inet.tcp.stats", tcp_array, &len, NULL, 0) < 0) 253 return; 254 ncpus = len / sizeof(tcp_array[0]); 255 tcp_last = tcp_curr; 256 tv_last = tv_curr; 257 bzero(&tcp_curr, sizeof(tcp_curr)); 258 gettimeofday(&tv_curr, NULL); 259 260 for (i = 0; i < ncpus; ++i) { 261 for (j = 0; j < sizeof(tcp_curr) / sizeof(u_long); ++j) { 262 ((u_long *)&tcp_curr)[j] += 263 ((u_long *)&tcp_array[i])[j]; 264 } 265 } 266 } 267 268 void 269 labelnetbw(void) 270 { 271 wmove(wnd, 0, 0); 272 wclrtobot(wnd); 273 #if 0 274 mvwaddstr(wnd, 0, LADDR, "Local Address"); 275 mvwaddstr(wnd, 0, FADDR, "Foreign Address"); 276 mvwaddstr(wnd, 0, PROTO, "Proto"); 277 mvwaddstr(wnd, 0, RCVCC, "Recv-Q"); 278 mvwaddstr(wnd, 0, SNDCC, "Send-Q"); 279 mvwaddstr(wnd, 0, STATE, "(state)"); 280 #endif 281 } 282 283 void 284 shownetbw(void) 285 { 286 double delta_time; 287 struct mytcpcb *elm; 288 struct mytcpcb *delm; 289 int row; 290 291 delta_time = (double)(tv_curr.tv_sec - tv_last.tv_sec) - 1.0 + 292 (tv_curr.tv_usec + 1000000 - tv_last.tv_usec) / 1e6; 293 if (delta_time < 0.1) 294 return; 295 296 mvwprintw(wnd, 0, 0, 297 "tcp accepts %s connects %s " 298 " rcv %s snd %s rexmit %s", 299 numtok(DELTARATE(tcps_accepts)), 300 numtok(DELTARATE(tcps_connects) - DELTARATE(tcps_accepts)), 301 numtok(DELTARATE(tcps_rcvbyte)), 302 numtok(DELTARATE(tcps_sndbyte)), 303 numtok(DELTARATE(tcps_sndrexmitbyte))); 304 305 row = 2; 306 delm = NULL; 307 RB_FOREACH(elm, mytcpcb_tree, &mytcp_tree) { 308 if (delm) { 309 RB_REMOVE(mytcpcb_tree, &mytcp_tree, delm); 310 free(delm); 311 delm = NULL; 312 } 313 if (elm->seq == tcp_pcb_seq && 314 (elm->xtcp.xt_socket.so_rcv.sb_cc || 315 elm->xtcp.xt_socket.so_snd.sb_cc || 316 DELTAELM(xt_tp.snd_max) || 317 DELTAELM(xt_tp.rcv_nxt) 318 )) { 319 if (row < LINES - 3) 320 netbwline(row, elm, delta_time); 321 ++row; 322 } else if (elm->seq != tcp_pcb_seq) { 323 delm = elm; 324 } 325 } 326 if (delm) { 327 RB_REMOVE(mytcpcb_tree, &mytcp_tree, delm); 328 free(delm); 329 delm = NULL; 330 } 331 wmove(wnd, row, 0); 332 wclrtobot(wnd); 333 mvwprintw(wnd, LINES-2, 0, 334 "Rate/sec, " 335 "R=rxpend T=txpend N=nodelay T=tstmp " 336 "S=sack X=winscale F=fastrec"); 337 } 338 339 static 340 void 341 netbwline(int row, struct mytcpcb *elm, double delta_time) 342 { 343 mvwprintw(wnd, row, 0, 344 "%s %s " 345 /*"rxb %s txb %s "*/ 346 "rcv %s snd %s " 347 "[%c%c%c%c%c%c%c]", 348 netaddrstr( 349 elm->xtcp.xt_inp.inp_vflag, 350 &elm->xtcp.xt_inp.inp_inc.inc_ie. 351 ie_dependladdr, 352 ntohs(elm->xtcp.xt_inp.inp_inc.inc_ie.ie_lport)), 353 netaddrstr( 354 elm->xtcp.xt_inp.inp_vflag, 355 &elm->xtcp.xt_inp.inp_inc.inc_ie. 356 ie_dependfaddr, 357 ntohs(elm->xtcp.xt_inp.inp_inc.inc_ie.ie_fport)), 358 /* 359 numtok(elm->xtcp.xt_socket.so_rcv.sb_cc), 360 numtok(elm->xtcp.xt_socket.so_snd.sb_cc), 361 */ 362 numtok(DELTAELM(xt_tp.rcv_nxt)), 363 numtok(DELTAELM(xt_tp.snd_max)), 364 (elm->xtcp.xt_socket.so_rcv.sb_cc > 15000 ? 365 'R' : ' '), 366 (elm->xtcp.xt_socket.so_snd.sb_cc > 15000 ? 367 'T' : ' '), 368 ((elm->xtcp.xt_tp.t_flags & TF_NODELAY) ? 369 'N' : ' '), 370 ((elm->xtcp.xt_tp.t_flags & TF_RCVD_TSTMP) ? 371 'T' : ' '), 372 ((elm->xtcp.xt_tp.t_flags & 373 TF_SACK_PERMITTED) ? 374 'S' : ' '), 375 ((elm->xtcp.xt_tp.t_flags & TF_RCVD_SCALE) ? 376 'X' : ' '), 377 ((elm->xtcp.xt_tp.t_flags & TF_FASTRECOVERY) ? 378 'F' : ' ') 379 ); 380 wclrtoeol(wnd); 381 } 382 383 #if 0 384 int 385 cmdnetbw(const char *cmd __unused, char *args __unused) 386 { 387 fetchnetbw(); 388 shownetbw(); 389 refresh(); 390 391 return (0); 392 } 393 #endif 394 395 #define MAXINDEXES 8 396 397 static 398 const char * 399 numtok(double value) 400 { 401 static char buf[MAXINDEXES][32]; 402 static int nexti; 403 static const char *suffixes[] = { " ", "K", "M", "G", "T", NULL }; 404 int suffix = 0; 405 const char *fmt; 406 407 while (value >= 1000.0 && suffixes[suffix+1]) { 408 value /= 1000.0; 409 ++suffix; 410 } 411 nexti = (nexti + 1) % MAXINDEXES; 412 if (value < 0.001) { 413 fmt = " "; 414 } else if (value < 1.0) { 415 fmt = "%5.3f%s"; 416 } else if (value < 10.0) { 417 fmt = "%5.3f%s"; 418 } else if (value < 100.0) { 419 fmt = "%5.2f%s"; 420 } else { 421 fmt = "%5.1f%s"; 422 } 423 snprintf(buf[nexti], sizeof(buf[nexti]), 424 fmt, value, suffixes[suffix]); 425 return (buf[nexti]); 426 } 427 428 const char * 429 netaddrstr(u_char vflags, union in_dependaddr *depaddr, u_int16_t port) 430 { 431 static char buf[MAXINDEXES][64]; 432 static int nexta; 433 char bufip[64]; 434 435 nexta = (nexta + 1) % MAXINDEXES; 436 437 if (vflags & INP_IPV4) { 438 snprintf(bufip, sizeof(bufip), 439 "%d.%d.%d.%d", 440 (ntohl(depaddr->id46_addr.ia46_addr4.s_addr) >> 24) & 441 255, 442 (ntohl(depaddr->id46_addr.ia46_addr4.s_addr) >> 16) & 443 255, 444 (ntohl(depaddr->id46_addr.ia46_addr4.s_addr) >> 8) & 445 255, 446 (ntohl(depaddr->id46_addr.ia46_addr4.s_addr) >> 0) & 447 255); 448 snprintf(buf[nexta], sizeof(buf[nexta]), 449 "%15s:%-5d", bufip, port); 450 } else if (vflags & INP_IPV6) { 451 snprintf(bufip, sizeof(bufip), 452 "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", 453 ntohs(depaddr->id6_addr.s6_addr16[0]), 454 ntohs(depaddr->id6_addr.s6_addr16[1]), 455 ntohs(depaddr->id6_addr.s6_addr16[2]), 456 ntohs(depaddr->id6_addr.s6_addr16[3]), 457 ntohs(depaddr->id6_addr.s6_addr16[4]), 458 ntohs(depaddr->id6_addr.s6_addr16[5]), 459 ntohs(depaddr->id6_addr.s6_addr16[6]), 460 ntohs(depaddr->id6_addr.s6_addr16[7])); 461 snprintf(buf[nexta], sizeof(buf[nexta]), 462 "%39s:%-5d", bufip, port); 463 } else { 464 snprintf(bufip, sizeof(bufip), "<unknown>"); 465 snprintf(buf[nexta], sizeof(buf[nexta]), 466 "%15s:%-5d", bufip, port); 467 } 468 return (buf[nexta]); 469 } 470 471 static 472 void 473 updatepcb(struct xtcpcb *xtcp) 474 { 475 struct mytcpcb dummy; 476 struct mytcpcb *elm; 477 478 dummy.xtcp = *xtcp; 479 if ((elm = RB_FIND(mytcpcb_tree, &mytcp_tree, &dummy)) == NULL) { 480 elm = malloc(sizeof(*elm)); 481 bzero(elm, sizeof(*elm)); 482 elm->xtcp = *xtcp; 483 elm->last_xtcp = *xtcp; 484 RB_INSERT(mytcpcb_tree, &mytcp_tree, elm); 485 } else { 486 elm->last_xtcp = elm->xtcp; 487 elm->xtcp = *xtcp; 488 } 489 elm->seq = tcp_pcb_seq; 490 } 491