1 /* $NetBSD: vtw.c,v 1.12 2021/10/30 11:23:07 nia Exp $ */ 2 3 /* 4 * Copyright (c) 2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Coyote Point Systems, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 /* 32 * Copyright (c) 1983, 1988, 1993 33 * The Regents of the University of California. All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. Neither the name of the University nor the names of its contributors 44 * may be used to endorse or promote products derived from this software 45 * without specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 57 * SUCH DAMAGE. 58 */ 59 60 #include <sys/cdefs.h> 61 #ifndef lint 62 #if 0 63 static char sccsid[] = "from: @(#)inet.c 8.4 (Berkeley) 4/20/94"; 64 #else 65 __RCSID("$NetBSD: vtw.c,v 1.12 2021/10/30 11:23:07 nia Exp $"); 66 #endif 67 #endif /* not lint */ 68 69 #define _CALLOUT_PRIVATE /* for defs in sys/callout.h */ 70 71 #include <sys/param.h> 72 #include <sys/queue.h> 73 #include <sys/socket.h> 74 #include <sys/socketvar.h> 75 #include <sys/mbuf.h> 76 #include <sys/protosw.h> 77 #include <sys/sysctl.h> 78 79 #include <net/if_arp.h> 80 #include <net/route.h> 81 #include <netinet/in.h> 82 #include <netinet/in_systm.h> 83 #include <netinet/ip.h> 84 #include <netinet/in_pcb.h> 85 #include <netinet/ip_icmp.h> 86 87 #ifdef INET6 88 #include <netinet/ip6.h> 89 #endif 90 91 #include <netinet/icmp_var.h> 92 #include <netinet/igmp_var.h> 93 #include <netinet/ip_var.h> 94 #include <netinet/pim_var.h> 95 #include <netinet/tcp.h> 96 #include <netinet/tcp_seq.h> 97 #include <netinet/tcp_fsm.h> 98 #include <netinet/tcp_timer.h> 99 #include <netinet/tcp_var.h> 100 #include <netinet/tcp_debug.h> 101 #include <netinet/udp.h> 102 #include <netinet/ip_carp.h> 103 #include <netinet/udp_var.h> 104 #include <netinet/tcp_vtw.h> 105 106 #include <arpa/inet.h> 107 #include <kvm.h> 108 #include <netdb.h> 109 #include <stdio.h> 110 #include <string.h> 111 #include <unistd.h> 112 #include <stdlib.h> 113 #include <err.h> 114 #include "netstat.h" 115 #include "vtw.h" 116 #include "prog_ops.h" 117 118 static bool vtw_enabled(void); 119 static void snarf(const void *, void *, size_t); 120 static void *lookup(const char *); 121 static void process_vtw(const vtw_ctl_t *, void (*)(const vtw_t *)); 122 123 static bool 124 vtw_enabled(void) 125 { 126 127 if (use_sysctl) { 128 int enabled; 129 size_t size = sizeof(enabled); 130 131 if (prog_sysctlbyname("net.inet.tcp.vtw.enable", 132 &enabled, &size, NULL, 0) == -1) 133 return true; 134 return enabled ? true : false; 135 } else { 136 return true; 137 } 138 } 139 140 static void 141 snarf(const void *addr, void *buf, size_t len) 142 { 143 size_t cc; 144 145 memset(buf, 0, len); 146 147 cc = kvm_read(get_kvmd(), (unsigned long) addr, buf, len); 148 149 if (cc != len) { 150 warnx("%s: short read at %p, len %zx cc %zx", __func__, addr, 151 len, cc); 152 } 153 } 154 155 static void * 156 lookup(const char *name) 157 { 158 kvm_t *k; 159 struct nlist nl[2]; 160 161 nl[0].n_name = name; 162 nl[0].n_value = 0; 163 nl[1].n_name = NULL; 164 165 if ((k = get_kvmd()) == NULL) { 166 if (Vflag) 167 errx(EXIT_FAILURE, "kvm not available"); 168 return NULL; 169 } 170 switch (kvm_nlist(k, &nl[0])) { 171 case -1: 172 err(EXIT_FAILURE, "kvm_nlist"); 173 break; 174 175 case 0: 176 return (void *)nl[0].n_value; 177 178 default: 179 if (Vflag) 180 errx(EXIT_FAILURE, "%s missing in symbol table", name); 181 break; 182 } 183 184 return NULL; 185 } 186 187 void 188 timebase(struct timeval *tv) 189 { 190 void *p; 191 struct bintime timebasebin; 192 193 if (!vtw_enabled()) { 194 memset(tv, 0, sizeof(*tv)); 195 return; 196 } 197 198 p = lookup("timebasebin"); 199 if (!p) 200 return; 201 snarf(p, &timebasebin, sizeof(timebasebin)); 202 bintime2timeval(&timebasebin, tv); 203 } 204 205 static void 206 process_vtw(const vtw_ctl_t * ctl, void (*print)(const vtw_t *)) 207 { 208 vtw_t *vp; 209 210 for (vp = ctl->base.v; vp && vp <= ctl->lim.v;) { 211 212 (*print)(vp); 213 214 if (ctl->is_v4) { 215 vtw_v4_t *v4 = (vtw_v4_t *)vp; 216 217 vp = &(++v4)->common; 218 } else if (ctl->is_v6) { 219 vtw_v6_t *v6 = (vtw_v6_t *)vp; 220 221 vp = &(++v6)->common; 222 } 223 } 224 } 225 226 void 227 show_vtw_stats(void) 228 { 229 vtw_stats_t stats; 230 void *p; 231 232 if (!Vflag) 233 return; 234 235 if (!vtw_enabled()) 236 return; 237 238 if ((p = lookup("vtw_stats")) == NULL) 239 return; 240 snarf(p, &stats, sizeof(stats)); 241 242 printf("\t\t%" PRIu64 " inserts\n", stats.ins); 243 printf("\t\t%" PRIu64 " deletes\n", stats.del); 244 printf("\t\t%" PRIu64 " assassinations\n", stats.kill); 245 printf("\tvestigial time-wait lookup_connect\n"); 246 printf("\t\t%" PRIu64 " look\n", stats.look[0]); 247 printf("\t\t%" PRIu64 " hit\n", stats.hit[0]); 248 printf("\t\t%" PRIu64 " miss\n", stats.miss[0]); 249 printf("\t\t%" PRIu64 " probe\n", stats.probe[0]); 250 printf("\t\t%" PRIu64 " losing\n", stats.losing[0]); 251 printf("\t\t%" PRIu64 " max_chain\n", stats.max_chain[0]); 252 printf("\t\t%" PRIu64 " max_probe\n", stats.max_probe[0]); 253 printf("\t\t%" PRIu64 " max_loss\n", stats.max_loss[0]); 254 printf("\tvestigial time-wait lookup_port\n"); 255 printf("\t\t%" PRIu64 " look\n", stats.look[1]); 256 printf("\t\t%" PRIu64 " hit\n", stats.hit[1]); 257 printf("\t\t%" PRIu64 " miss\n", stats.miss[1]); 258 printf("\t\t%" PRIu64 " probe\n", stats.probe[1]); 259 printf("\t\t%" PRIu64 " losing\n", stats.losing[1]); 260 printf("\t\t%" PRIu64 " max_chain\n", stats.max_chain[1]); 261 printf("\t\t%" PRIu64 " max_probe\n", stats.max_probe[1]); 262 printf("\t\t%" PRIu64 " max_loss\n", stats.max_loss[1]); 263 } 264 265 void 266 show_vtw_v4(void (*print)(const vtw_t *)) 267 { 268 fatp_t *base, *lim; 269 fatp_t **hash, **port; 270 size_t n; 271 fatp_ctl_t fat_tcpv4; 272 vtw_ctl_t vtw_tcpv4[VTW_NCLASS]; 273 int i; 274 int mem = 0; 275 void *p; 276 277 if (!vtw_enabled()) 278 return; 279 280 if ((p = lookup("fat_tcpv4")) == NULL) 281 return; 282 snarf(p, &fat_tcpv4, sizeof(fat_tcpv4)); 283 284 if ((p = lookup("vtw_tcpv4")) == NULL) 285 return; 286 snarf(p, &vtw_tcpv4[0], sizeof(vtw_tcpv4)); 287 288 mem += sizeof(fat_tcpv4); 289 mem += sizeof(vtw_tcpv4); 290 291 /* snarf/adjust vtw_ctl */ 292 for (i = 0; i < VTW_NCLASS; ++i) { 293 vtw_v4_t *kbase, *klim; 294 vtw_v4_t *ubase; 295 ptrdiff_t delta; 296 297 kbase = vtw_tcpv4[i].base.v4; 298 klim = vtw_tcpv4[i].lim.v4; 299 300 if (!kbase || !klim) 301 continue; 302 303 n = (klim - kbase + 1); 304 305 if (!i) { 306 ubase = NULL; 307 if (reallocarr(&ubase, n, sizeof(*kbase)) != 0) 308 err(EXIT_FAILURE, "reallocarr"); 309 snarf(kbase, ubase, n * sizeof(*ubase)); 310 311 mem += n * sizeof(*ubase); 312 } else { 313 ubase = vtw_tcpv4[0].base.v4; 314 } 315 316 delta = ubase - kbase; 317 318 vtw_tcpv4[i].base.v4 += delta; 319 vtw_tcpv4[i].lim.v4 += delta; 320 vtw_tcpv4[i].alloc.v4 += delta; 321 vtw_tcpv4[i].fat = &fat_tcpv4; 322 323 if (vtw_tcpv4[i].oldest.v4) 324 vtw_tcpv4[i].oldest.v4 += delta; 325 } 326 327 /* snarf/adjust fat_ctl */ 328 329 base = fat_tcpv4.base; 330 lim = fat_tcpv4.lim; 331 332 if (!base || !lim) 333 goto end; 334 335 mem += (lim - base + 1) * sizeof(*base); 336 337 fat_tcpv4.base = NULL; 338 if (reallocarr(&fat_tcpv4.base, lim - base + 1, sizeof(*base)) != 0) 339 err(EXIT_FAILURE, "reallocarr"); 340 fat_tcpv4.lim = fat_tcpv4.base + (lim - base); 341 342 snarf(base, fat_tcpv4.base, sizeof(*base) * (lim - base + 1)); 343 344 fat_tcpv4.vtw = &vtw_tcpv4[0]; 345 fat_tcpv4.free = fat_tcpv4.base + (fat_tcpv4.free - base); 346 347 n = fat_tcpv4.mask + 1; 348 hash = fat_tcpv4.hash; 349 port = fat_tcpv4.port; 350 351 fat_tcpv4.hash = NULL; 352 if (reallocarr(&fat_tcpv4.hash, n, sizeof(*hash)) != 0) 353 err(EXIT_FAILURE, "reallocarr"); 354 355 fat_tcpv4.port = NULL; 356 if (reallocarr(&fat_tcpv4.port, n, sizeof(*port)) != 0) 357 err(EXIT_FAILURE, "reallocarr"); 358 359 snarf(hash, fat_tcpv4.hash, n * sizeof(*hash)); 360 snarf(port, fat_tcpv4.port, n * sizeof(*port)); 361 362 end: 363 process_vtw(&vtw_tcpv4[0], print); 364 365 #if 0 366 if (Vflag && vflag) { 367 printf("total memory for VTW in current config: %d bytes %f MB\n" 368 ,mem 369 ,mem / (1024.0 * 1024)); 370 } 371 #endif 372 } 373 374 void 375 show_vtw_v6(void (*print)(const vtw_t *)) 376 { 377 fatp_t *base, *lim; 378 fatp_t **hash, **port; 379 size_t n; 380 fatp_ctl_t fat_tcpv6; 381 vtw_ctl_t vtw_tcpv6[VTW_NCLASS]; 382 int i; 383 int mem = 0; 384 void *p; 385 386 if (!vtw_enabled()) 387 return; 388 389 if ((p = lookup("fat_tcpv6")) == NULL) 390 return; 391 snarf(p, &fat_tcpv6, sizeof(fat_tcpv6)); 392 if ((p = lookup("vtw_tcpv6")) == NULL) 393 return; 394 snarf(p, &vtw_tcpv6[0], sizeof(vtw_tcpv6)); 395 396 mem += sizeof(fat_tcpv6); 397 mem += sizeof(vtw_tcpv6); 398 399 for (i = 0; i < VTW_NCLASS; ++i) { 400 vtw_v6_t *kbase, *klim; 401 vtw_v6_t *ubase; 402 ptrdiff_t delta; 403 404 kbase = vtw_tcpv6[i].base.v6; 405 klim = vtw_tcpv6[i].lim.v6; 406 407 if (!kbase || !klim) 408 continue; 409 410 n = (klim - kbase + 1); 411 412 if (!i) { 413 ubase = NULL; 414 if (reallocarr(&ubase, n, sizeof(*kbase)) != 0) 415 err(EXIT_FAILURE, "reallocarr"); 416 417 snarf(kbase, ubase, n * sizeof(*ubase)); 418 419 mem += n * sizeof(*ubase); 420 } else { 421 ubase = vtw_tcpv6[0].base.v6; 422 } 423 424 delta = ubase - kbase; 425 426 vtw_tcpv6[i].base.v6 += delta; 427 vtw_tcpv6[i].lim.v6 += delta; 428 vtw_tcpv6[i].alloc.v6 += delta; 429 vtw_tcpv6[i].fat = &fat_tcpv6; 430 431 if (vtw_tcpv6[i].oldest.v6) 432 vtw_tcpv6[i].oldest.v6 += delta; 433 } 434 435 base = fat_tcpv6.base; 436 lim = fat_tcpv6.lim; 437 438 if (!base || !lim) 439 goto end; 440 441 mem += (lim - base + 1) * sizeof(*base); 442 443 fat_tcpv6.base = NULL; 444 if (reallocarr(&fat_tcpv6.base, lim - base + 1, sizeof(*base)) != 0) 445 err(EXIT_FAILURE, "reallocarr"); 446 447 fat_tcpv6.lim = fat_tcpv6.base + (lim - base); 448 449 snarf(base, fat_tcpv6.base, sizeof(*base) * (lim - base + 1)); 450 451 fat_tcpv6.vtw = &vtw_tcpv6[0]; 452 fat_tcpv6.free = fat_tcpv6.base + (fat_tcpv6.free - base); 453 454 n = fat_tcpv6.mask + 1; 455 hash = fat_tcpv6.hash; 456 port = fat_tcpv6.port; 457 458 fat_tcpv6.hash = NULL; 459 if (reallocarr(&fat_tcpv6.hash, n, sizeof(*hash)) != 0) 460 err(EXIT_FAILURE, "reallocarr"); 461 462 fat_tcpv6.port = NULL; 463 if (reallocarr(&fat_tcpv6.port, n, sizeof(*port)) != 0) 464 err(EXIT_FAILURE, "reallocarr"); 465 466 snarf(hash, fat_tcpv6.hash, n * sizeof(*hash)); 467 snarf(port, fat_tcpv6.port, n * sizeof(*port)); 468 469 end: 470 471 process_vtw(&vtw_tcpv6[0], print); 472 #if 0 473 if (Vflag && vflag) { 474 printf("total memory for VTW in current config: %d bytes %f MB\n" 475 ,mem 476 ,mem / (1024.0 * 1024)); 477 } 478 #endif 479 } 480