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_af < tcp2->xtcp.xt_inp.inp_af) 116 return(-1); 117 if (tcp1->xtcp.xt_inp.inp_af > tcp2->xtcp.xt_inp.inp_af) 118 return(1); 119 120 /* 121 * Local and foreign addresses 122 */ 123 if (INP_ISIPV4(&tcp1->xtcp.xt_inp)) { 124 if (ntohl(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_laddr.s_addr) < 125 ntohl(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_laddr.s_addr)) 126 return(-1); 127 if (ntohl(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_laddr.s_addr) > 128 ntohl(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_laddr.s_addr)) 129 return(1); 130 if (ntohl(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_faddr.s_addr) < 131 ntohl(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_faddr.s_addr)) 132 return(-1); 133 if (ntohl(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_faddr.s_addr) > 134 ntohl(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_faddr.s_addr)) 135 return(1); 136 } else if (INP_ISIPV6(&tcp1->xtcp.xt_inp)) { 137 r = bcmp(&tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr, 138 &tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr, 139 sizeof(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr)); 140 if (r) 141 return(r); 142 } else { 143 r = bcmp(&tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr, 144 &tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr, 145 sizeof(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr)); 146 if (r) 147 return(r); 148 } 149 return(0); 150 } 151 152 struct mytcpcb_tree; 153 RB_HEAD(mytcpcb_tree, mytcpcb); 154 RB_PROTOTYPE(mytcpcb_tree, mytcpcb, rb_node, mytcpcb_cmp); 155 RB_GENERATE(mytcpcb_tree, mytcpcb, rb_node, mytcpcb_cmp); 156 157 static struct mytcpcb_tree mytcp_tree; 158 static struct timeval tv_curr; 159 static struct timeval tv_last; 160 static struct tcp_stats tcp_curr; 161 static struct tcp_stats tcp_last; 162 static int tcp_pcb_seq; 163 164 static const char *numtok(double value); 165 static void netbwline(int row, struct mytcpcb *elm, double delta_time); 166 const char * netaddrstr(u_char vflags, union in_dependaddr *depaddr, 167 u_int16_t port); 168 static void updatepcb(struct xtcpcb *xtcp); 169 170 #define DELTARATE(field) \ 171 ((double)(tcp_curr.field - tcp_last.field) / delta_time) 172 173 #define DELTAELM(field) \ 174 ((double)(tcp_seq_diff_t)(elm->xtcp.field - \ 175 elm->last_xtcp.field) / \ 176 delta_time) 177 178 #define DELTAELMSCALE(field, scale) \ 179 ((double)((tcp_seq_diff_t)(elm->xtcp.field - \ 180 elm->last_xtcp.field) << scale) / \ 181 delta_time) 182 183 WINDOW * 184 opennetbw(void) 185 { 186 RB_INIT(&mytcp_tree); 187 return (subwin(stdscr, LINES-0-1, 0, 0, 0)); 188 } 189 190 void 191 closenetbw(WINDOW *w) 192 { 193 struct mytcpcb *mytcp; 194 195 while ((mytcp = RB_ROOT(&mytcp_tree)) != NULL) { 196 RB_REMOVE(mytcpcb_tree, &mytcp_tree, mytcp); 197 free(mytcp); 198 } 199 200 if (w != NULL) { 201 wclear(w); 202 wrefresh(w); 203 delwin(w); 204 } 205 } 206 207 int 208 initnetbw(void) 209 { 210 return(1); 211 } 212 213 void 214 fetchnetbw(void) 215 { 216 struct tcp_stats tcp_array[SMP_MAXCPU]; 217 struct xtcpcb *tcp_pcbs; 218 size_t npcbs; 219 size_t len; 220 size_t i; 221 size_t j; 222 size_t ncpus; 223 224 /* 225 * Extract PCB list 226 */ 227 len = 0; 228 if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) < 0) 229 return; 230 len += 128 * sizeof(tcp_pcbs[0]); 231 tcp_pcbs = malloc(len); 232 if (sysctlbyname("net.inet.tcp.pcblist", tcp_pcbs, &len, NULL, 0) < 0) { 233 free(tcp_pcbs); 234 return; 235 } 236 npcbs = len / sizeof(tcp_pcbs[0]); 237 ++tcp_pcb_seq; 238 239 for (i = 0; i < npcbs; ++i) { 240 if (tcp_pcbs[i].xt_len != sizeof(tcp_pcbs[0])) 241 break; 242 updatepcb(&tcp_pcbs[i]); 243 } 244 free(tcp_pcbs); 245 246 /* 247 * General stats 248 */ 249 len = sizeof(tcp_array); 250 if (sysctlbyname("net.inet.tcp.stats", tcp_array, &len, NULL, 0) < 0) 251 return; 252 ncpus = len / sizeof(tcp_array[0]); 253 tcp_last = tcp_curr; 254 tv_last = tv_curr; 255 bzero(&tcp_curr, sizeof(tcp_curr)); 256 gettimeofday(&tv_curr, NULL); 257 258 for (i = 0; i < ncpus; ++i) { 259 for (j = 0; j < sizeof(tcp_curr) / sizeof(u_long); ++j) { 260 ((u_long *)&tcp_curr)[j] += 261 ((u_long *)&tcp_array[i])[j]; 262 } 263 } 264 } 265 266 void 267 labelnetbw(void) 268 { 269 wmove(wnd, 0, 0); 270 wclrtobot(wnd); 271 #if 0 272 mvwaddstr(wnd, 0, LADDR, "Local Address"); 273 mvwaddstr(wnd, 0, FADDR, "Foreign Address"); 274 mvwaddstr(wnd, 0, PROTO, "Proto"); 275 mvwaddstr(wnd, 0, RCVCC, "Recv-Q"); 276 mvwaddstr(wnd, 0, SNDCC, "Send-Q"); 277 mvwaddstr(wnd, 0, STATE, "(state)"); 278 #endif 279 } 280 281 void 282 shownetbw(void) 283 { 284 double delta_time; 285 struct mytcpcb *elm; 286 struct mytcpcb *delm; 287 int row; 288 289 delta_time = (double)(tv_curr.tv_sec - tv_last.tv_sec) - 1.0 + 290 (tv_curr.tv_usec + 1000000 - tv_last.tv_usec) / 1e6; 291 if (delta_time < 0.1) 292 return; 293 294 mvwprintw(wnd, 0, 0, 295 "tcp accepts %s connects %s " 296 " rcv %s snd %s rexmit %s", 297 numtok(DELTARATE(tcps_accepts)), 298 numtok(DELTARATE(tcps_connects) - DELTARATE(tcps_accepts)), 299 numtok(DELTARATE(tcps_rcvbyte)), 300 numtok(DELTARATE(tcps_sndbyte)), 301 numtok(DELTARATE(tcps_sndrexmitbyte))); 302 303 row = 2; 304 delm = NULL; 305 RB_FOREACH(elm, mytcpcb_tree, &mytcp_tree) { 306 if (delm) { 307 RB_REMOVE(mytcpcb_tree, &mytcp_tree, delm); 308 free(delm); 309 delm = NULL; 310 } 311 if (elm->seq == tcp_pcb_seq && 312 (elm->xtcp.xt_socket.so_rcv.sb_cc || 313 elm->xtcp.xt_socket.so_snd.sb_cc || 314 DELTAELM(xt_tp.snd_max) || 315 DELTAELM(xt_tp.rcv_nxt) 316 )) { 317 if (row < LINES - 3) 318 netbwline(row, elm, delta_time); 319 ++row; 320 } else if (elm->seq != tcp_pcb_seq) { 321 delm = elm; 322 } 323 } 324 if (delm) { 325 RB_REMOVE(mytcpcb_tree, &mytcp_tree, delm); 326 free(delm); 327 delm = NULL; 328 } 329 wmove(wnd, row, 0); 330 wclrtobot(wnd); 331 mvwprintw(wnd, LINES-2, 0, 332 "Rate/sec, " 333 "R=rxpend T=txpend N=nodelay T=tstmp " 334 "S=sack X=winscale F=fastrec"); 335 } 336 337 static 338 void 339 netbwline(int row, struct mytcpcb *elm, double delta_time) 340 { 341 mvwprintw(wnd, row, 0, 342 "%s %s " 343 /*"rxb %s txb %s "*/ 344 "rcv %s snd %s " 345 "[%c%c%c%c%c%c%c]", 346 netaddrstr( 347 elm->xtcp.xt_inp.inp_af, 348 &elm->xtcp.xt_inp.inp_inc.inc_ie. 349 ie_dependladdr, 350 ntohs(elm->xtcp.xt_inp.inp_inc.inc_ie.ie_lport)), 351 netaddrstr( 352 elm->xtcp.xt_inp.inp_af, 353 &elm->xtcp.xt_inp.inp_inc.inc_ie. 354 ie_dependfaddr, 355 ntohs(elm->xtcp.xt_inp.inp_inc.inc_ie.ie_fport)), 356 /* 357 numtok(elm->xtcp.xt_socket.so_rcv.sb_cc), 358 numtok(elm->xtcp.xt_socket.so_snd.sb_cc), 359 */ 360 numtok(DELTAELM(xt_tp.rcv_nxt)), 361 numtok(DELTAELM(xt_tp.snd_max)), 362 (elm->xtcp.xt_socket.so_rcv.sb_cc > 15000 ? 363 'R' : ' '), 364 (elm->xtcp.xt_socket.so_snd.sb_cc > 15000 ? 365 'T' : ' '), 366 ((elm->xtcp.xt_tp.t_flags & TF_NODELAY) ? 367 'N' : ' '), 368 ((elm->xtcp.xt_tp.t_flags & TF_RCVD_TSTMP) ? 369 'T' : ' '), 370 ((elm->xtcp.xt_tp.t_flags & 371 TF_SACK_PERMITTED) ? 372 'S' : ' '), 373 ((elm->xtcp.xt_tp.t_flags & TF_RCVD_SCALE) ? 374 'X' : ' '), 375 ((elm->xtcp.xt_tp.t_flags & TF_FASTRECOVERY) ? 376 'F' : ' ') 377 ); 378 wclrtoeol(wnd); 379 } 380 381 #if 0 382 int 383 cmdnetbw(const char *cmd __unused, char *args __unused) 384 { 385 fetchnetbw(); 386 shownetbw(); 387 refresh(); 388 389 return (0); 390 } 391 #endif 392 393 #define MAXINDEXES 8 394 395 static 396 const char * 397 numtok(double value) 398 { 399 static char buf[MAXINDEXES][32]; 400 static int nexti; 401 static const char *suffixes[] = { " ", "K", "M", "G", "T", NULL }; 402 int suffix = 0; 403 const char *fmt; 404 405 while (value >= 1000.0 && suffixes[suffix+1]) { 406 value /= 1000.0; 407 ++suffix; 408 } 409 nexti = (nexti + 1) % MAXINDEXES; 410 if (value < 0.001) { 411 fmt = " "; 412 } else if (value < 1.0) { 413 fmt = "%5.3f%s"; 414 } else if (value < 10.0) { 415 fmt = "%5.3f%s"; 416 } else if (value < 100.0) { 417 fmt = "%5.2f%s"; 418 } else { 419 fmt = "%5.1f%s"; 420 } 421 snprintf(buf[nexti], sizeof(buf[nexti]), 422 fmt, value, suffixes[suffix]); 423 return (buf[nexti]); 424 } 425 426 const char * 427 netaddrstr(u_char af, union in_dependaddr *depaddr, u_int16_t port) 428 { 429 static char buf[MAXINDEXES][64]; 430 static int nexta; 431 char bufip[64]; 432 433 nexta = (nexta + 1) % MAXINDEXES; 434 435 if (af == AF_INET) { 436 snprintf(bufip, sizeof(bufip), 437 "%d.%d.%d.%d", 438 (ntohl(depaddr->id46_addr.ia46_addr4.s_addr) >> 24) & 439 255, 440 (ntohl(depaddr->id46_addr.ia46_addr4.s_addr) >> 16) & 441 255, 442 (ntohl(depaddr->id46_addr.ia46_addr4.s_addr) >> 8) & 443 255, 444 (ntohl(depaddr->id46_addr.ia46_addr4.s_addr) >> 0) & 445 255); 446 snprintf(buf[nexta], sizeof(buf[nexta]), 447 "%15s:%-5d", bufip, port); 448 } else if (af == AF_INET6) { 449 snprintf(bufip, sizeof(bufip), 450 "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", 451 ntohs(depaddr->id6_addr.s6_addr16[0]), 452 ntohs(depaddr->id6_addr.s6_addr16[1]), 453 ntohs(depaddr->id6_addr.s6_addr16[2]), 454 ntohs(depaddr->id6_addr.s6_addr16[3]), 455 ntohs(depaddr->id6_addr.s6_addr16[4]), 456 ntohs(depaddr->id6_addr.s6_addr16[5]), 457 ntohs(depaddr->id6_addr.s6_addr16[6]), 458 ntohs(depaddr->id6_addr.s6_addr16[7])); 459 snprintf(buf[nexta], sizeof(buf[nexta]), 460 "%39s:%-5d", bufip, port); 461 } else { 462 snprintf(bufip, sizeof(bufip), "<unknown>"); 463 snprintf(buf[nexta], sizeof(buf[nexta]), 464 "%15s:%-5d", bufip, port); 465 } 466 return (buf[nexta]); 467 } 468 469 static 470 void 471 updatepcb(struct xtcpcb *xtcp) 472 { 473 struct mytcpcb dummy; 474 struct mytcpcb *elm; 475 476 dummy.xtcp = *xtcp; 477 if ((elm = RB_FIND(mytcpcb_tree, &mytcp_tree, &dummy)) == NULL) { 478 elm = malloc(sizeof(*elm)); 479 bzero(elm, sizeof(*elm)); 480 elm->xtcp = *xtcp; 481 elm->last_xtcp = *xtcp; 482 RB_INSERT(mytcpcb_tree, &mytcp_tree, elm); 483 } else { 484 elm->last_xtcp = elm->xtcp; 485 elm->xtcp = *xtcp; 486 } 487 elm->seq = tcp_pcb_seq; 488 } 489