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