1 /* 2 * Copyright (c) 2014 - 2018 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Bill Yuan <bycn82@dragonflybsd.org> 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 35 #include <sys/param.h> 36 #include <sys/mbuf.h> 37 #include <sys/socket.h> 38 #include <sys/sockio.h> 39 #include <sys/sysctl.h> 40 #include <sys/time.h> 41 #include <sys/wait.h> 42 43 #include <arpa/inet.h> 44 #include <ctype.h> 45 #include <dlfcn.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <grp.h> 49 #include <limits.h> 50 #include <netdb.h> 51 #include <pwd.h> 52 #include <sysexits.h> 53 #include <signal.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <stdarg.h> 57 #include <string.h> 58 #include <timeconv.h> 59 #include <unistd.h> 60 61 #include <netinet/in.h> 62 #include <netinet/in_systm.h> 63 #include <netinet/ip.h> 64 #include <netinet/ip_icmp.h> 65 #include <netinet/tcp.h> 66 #include <net/if.h> 67 #include <net/if_dl.h> 68 #include <net/route.h> 69 #include <net/ethernet.h> 70 71 #include <net/ipfw3/ip_fw3.h> 72 #include <net/ipfw3_basic/ip_fw3_table.h> 73 #include <net/ipfw3_basic/ip_fw3_sync.h> 74 #include <net/ipfw3_basic/ip_fw3_basic.h> 75 #include <net/ipfw3_nat/ip_fw3_nat.h> 76 #include <net/dummynet3/ip_dummynet3.h> 77 78 #include "ipfw3.h" 79 #include "ipfw3dummynet.h" 80 81 82 extern int verbose; 83 extern int do_time; 84 extern int do_quiet; 85 extern int do_force; 86 extern int do_pipe; 87 extern int do_sort; 88 89 90 struct char_int_map dummynet_params[] = { 91 { "plr", TOK_PLR }, 92 { "noerror", TOK_NOERROR }, 93 { "buckets", TOK_BUCKETS }, 94 { "dst-ip", TOK_DSTIP }, 95 { "src-ip", TOK_SRCIP }, 96 { "dst-port", TOK_DSTPORT }, 97 { "src-port", TOK_SRCPORT }, 98 { "proto", TOK_PROTO }, 99 { "weight", TOK_WEIGHT }, 100 { "all", TOK_ALL }, 101 { "mask", TOK_MASK }, 102 { "droptail", TOK_DROPTAIL }, 103 { "red", TOK_RED }, 104 { "gred", TOK_GRED }, 105 { "bw", TOK_BW }, 106 { "bandwidth", TOK_BW }, 107 { "delay", TOK_DELAY }, 108 { "pipe", TOK_PIPE }, 109 { "queue", TOK_QUEUE }, 110 { "dummynet-params", TOK_NULL }, 111 { NULL, 0 } 112 }; 113 114 115 int 116 sort_q(const void *pa, const void *pb) 117 { 118 int rev = (do_sort < 0); 119 int field = rev ? -do_sort : do_sort; 120 long long res = 0; 121 const struct dn_ioc_flowqueue *a = pa; 122 const struct dn_ioc_flowqueue *b = pb; 123 124 switch(field) { 125 case 1: /* pkts */ 126 res = a->len - b->len; 127 break; 128 case 2: /* bytes */ 129 res = a->len_bytes - b->len_bytes; 130 break; 131 132 case 3: /* tot pkts */ 133 res = a->tot_pkts - b->tot_pkts; 134 break; 135 136 case 4: /* tot bytes */ 137 res = a->tot_bytes - b->tot_bytes; 138 break; 139 } 140 if (res < 0) 141 res = -1; 142 if (res > 0) 143 res = 1; 144 return (int)(rev ? res : -res); 145 } 146 147 148 149 /* 150 * config dummynet pipe/queue 151 */ 152 void 153 config_dummynet(int ac, char **av) 154 { 155 struct dn_ioc_pipe pipe; 156 u_int32_t a; 157 void *par = NULL; 158 int i; 159 char *end; 160 161 NEXT_ARG; 162 memset(&pipe, 0, sizeof pipe); 163 /* Pipe number */ 164 if (ac && isdigit(**av)) { 165 i = atoi(*av); 166 NEXT_ARG; 167 if (do_pipe == 1) 168 pipe.pipe_nr = i; 169 else 170 pipe.fs.fs_nr = i; 171 } 172 173 while (ac > 0) { 174 double d; 175 176 int tok = match_token(dummynet_params, *av); 177 NEXT_ARG; 178 179 switch(tok) { 180 case TOK_NOERROR: 181 pipe.fs.flags_fs |= DN_NOERROR; 182 break; 183 184 case TOK_PLR: 185 NEED1("plr needs argument 0..1\n"); 186 d = strtod(av[0], NULL); 187 if (d > 1) 188 d = 1; 189 else if (d < 0) 190 d = 0; 191 pipe.fs.plr = (int)(d*0x7fffffff); 192 NEXT_ARG; 193 break; 194 195 case TOK_QUEUE: 196 NEED1("queue needs queue size\n"); 197 end = NULL; 198 pipe.fs.qsize = getbw(av[0], &pipe.fs.flags_fs, 1024); 199 NEXT_ARG; 200 break; 201 202 case TOK_BUCKETS: 203 NEED1("buckets needs argument\n"); 204 pipe.fs.rq_size = strtoul(av[0], NULL, 0); 205 NEXT_ARG; 206 break; 207 208 case TOK_MASK: 209 NEED1("mask needs mask specifier\n"); 210 /* 211 * per-flow queue, mask is dst_ip, dst_port, 212 * src_ip, src_port, proto measured in bits 213 */ 214 par = NULL; 215 216 pipe.fs.flow_mask.type = ETHERTYPE_IP; 217 pipe.fs.flow_mask.u.ip.dst_ip = 0; 218 pipe.fs.flow_mask.u.ip.src_ip = 0; 219 pipe.fs.flow_mask.u.ip.dst_port = 0; 220 pipe.fs.flow_mask.u.ip.src_port = 0; 221 pipe.fs.flow_mask.u.ip.proto = 0; 222 end = NULL; 223 224 while (ac >= 1) { 225 u_int32_t *p32 = NULL; 226 u_int16_t *p16 = NULL; 227 228 tok = match_token(dummynet_params, *av); 229 NEXT_ARG; 230 switch(tok) { 231 case TOK_ALL: 232 /* 233 * special case, all bits significant 234 */ 235 pipe.fs.flow_mask.u.ip.dst_ip = ~0; 236 pipe.fs.flow_mask.u.ip.src_ip = ~0; 237 pipe.fs.flow_mask.u.ip.dst_port = ~0; 238 pipe.fs.flow_mask.u.ip.src_port = ~0; 239 pipe.fs.flow_mask.u.ip.proto = ~0; 240 pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK; 241 goto end_mask; 242 243 case TOK_DSTIP: 244 p32 = &pipe.fs.flow_mask.u.ip.dst_ip; 245 break; 246 247 case TOK_SRCIP: 248 p32 = &pipe.fs.flow_mask.u.ip.src_ip; 249 break; 250 251 case TOK_DSTPORT: 252 p16 = &pipe.fs.flow_mask.u.ip.dst_port; 253 break; 254 255 case TOK_SRCPORT: 256 p16 = &pipe.fs.flow_mask.u.ip.src_port; 257 break; 258 259 case TOK_PROTO: 260 break; 261 262 default: 263 NEXT_ARG; 264 goto end_mask; 265 } 266 if (ac < 1) 267 errx(EX_USAGE, "mask: value missing"); 268 if (*av[0] == '/') { 269 a = strtoul(av[0]+1, &end, 0); 270 a = (a == 32) ? ~0 : (1 << a) - 1; 271 } else 272 a = strtoul(av[0], &end, 0); 273 if (p32 != NULL) 274 *p32 = a; 275 else if (p16 != NULL) { 276 if (a > 65535) 277 errx(EX_DATAERR, 278 "mask: must be 16 bit"); 279 *p16 = (u_int16_t)a; 280 } else { 281 if (a > 255) 282 errx(EX_DATAERR, 283 "mask: must be 8 bit"); 284 pipe.fs.flow_mask.u.ip.proto = 285 (uint8_t)a; 286 } 287 if (a != 0) 288 pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK; 289 NEXT_ARG; 290 } /* end while, config masks */ 291 292 end_mask: 293 break; 294 295 case TOK_RED: 296 case TOK_GRED: 297 NEED1("red/gred needs w_q/min_th/max_th/max_p\n"); 298 pipe.fs.flags_fs |= DN_IS_RED; 299 if (tok == TOK_GRED) 300 pipe.fs.flags_fs |= DN_IS_GENTLE_RED; 301 /* 302 * the format for parameters is w_q/min_th/max_th/max_p 303 */ 304 if ((end = strsep(&av[0], "/"))) { 305 double w_q = strtod(end, NULL); 306 if (w_q > 1 || w_q <= 0) 307 errx(EX_DATAERR, "0 < w_q <= 1"); 308 pipe.fs.w_q = (int) (w_q * (1 << SCALE_RED)); 309 } 310 if ((end = strsep(&av[0], "/"))) { 311 pipe.fs.min_th = strtoul(end, &end, 0); 312 if (*end == 'K' || *end == 'k') 313 pipe.fs.min_th *= 1024; 314 } 315 if ((end = strsep(&av[0], "/"))) { 316 pipe.fs.max_th = strtoul(end, &end, 0); 317 if (*end == 'K' || *end == 'k') 318 pipe.fs.max_th *= 1024; 319 } 320 if ((end = strsep(&av[0], "/"))) { 321 double max_p = strtod(end, NULL); 322 if (max_p > 1 || max_p <= 0) 323 errx(EX_DATAERR, "0 < max_p <= 1"); 324 pipe.fs.max_p = (int)(max_p * (1 << SCALE_RED)); 325 } 326 NEXT_ARG; 327 break; 328 329 case TOK_DROPTAIL: 330 pipe.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED); 331 break; 332 333 case TOK_BW: 334 NEED1("bw needs bandwidth\n"); 335 if (do_pipe != 1) 336 errx(EX_DATAERR, 337 "bandwidth only valid for pipes"); 338 /* 339 * set bandwidth value 340 */ 341 pipe.bandwidth = getbw(av[0], NULL, 1000); 342 if (pipe.bandwidth < 0) 343 errx(EX_DATAERR, "bandwidth too large"); 344 NEXT_ARG; 345 break; 346 347 case TOK_DELAY: 348 if (do_pipe != 1) 349 errx(EX_DATAERR, "delay only valid for pipes"); 350 NEED1("delay needs argument 0..10000ms\n"); 351 pipe.delay = strtoul(av[0], NULL, 0); 352 NEXT_ARG; 353 break; 354 355 case TOK_WEIGHT: 356 if (do_pipe == 1) 357 errx(EX_DATAERR, 358 "weight only valid for queues"); 359 NEED1("weight needs argument 0..100\n"); 360 pipe.fs.weight = strtoul(av[0], &end, 0); 361 NEXT_ARG; 362 break; 363 364 case TOK_PIPE: 365 if (do_pipe == 1) 366 errx(EX_DATAERR, "pipe only valid for queues"); 367 NEED1("pipe needs pipe_number\n"); 368 pipe.fs.parent_nr = strtoul(av[0], &end, 0); 369 NEXT_ARG; 370 break; 371 372 default: 373 errx(EX_DATAERR, "unrecognised option ``%s''", *av); 374 } 375 } 376 if (do_pipe == 1) { 377 if (pipe.pipe_nr == 0) 378 errx(EX_DATAERR, "pipe_nr must be > 0"); 379 if (pipe.delay > 10000) 380 errx(EX_DATAERR, "delay must be < 10000"); 381 } else { /* do_pipe == 2, queue */ 382 if (pipe.fs.parent_nr == 0) 383 errx(EX_DATAERR, "pipe must be > 0"); 384 if (pipe.fs.weight >100) 385 errx(EX_DATAERR, "weight must be <= 100"); 386 } 387 if (pipe.fs.flags_fs & DN_QSIZE_IS_BYTES) { 388 if (pipe.fs.qsize > 1024*1024) 389 errx(EX_DATAERR, "queue size must be < 1MB"); 390 } else { 391 if (pipe.fs.qsize > 100) 392 errx(EX_DATAERR, "2 <= queue size <= 100"); 393 } 394 if (pipe.fs.flags_fs & DN_IS_RED) { 395 size_t len; 396 int lookup_depth, avg_pkt_size; 397 double s, idle, weight, w_q; 398 int clock_hz; 399 int t; 400 401 if (pipe.fs.min_th >= pipe.fs.max_th) 402 errx(EX_DATAERR, "min_th %d must be < than max_th %d", 403 pipe.fs.min_th, pipe.fs.max_th); 404 if (pipe.fs.max_th == 0) 405 errx(EX_DATAERR, "max_th must be > 0"); 406 407 len = sizeof(int); 408 if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", 409 &lookup_depth, &len, NULL, 0) == -1) 410 411 errx(1, "sysctlbyname(\"%s\")", 412 "net.inet.ip.dummynet.red_lookup_depth"); 413 if (lookup_depth == 0) 414 errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth" 415 " must be greater than zero"); 416 417 len = sizeof(int); 418 if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", 419 &avg_pkt_size, &len, NULL, 0) == -1) 420 421 errx(1, "sysctlbyname(\"%s\")", 422 "net.inet.ip.dummynet.red_avg_pkt_size"); 423 if (avg_pkt_size == 0) 424 errx(EX_DATAERR, 425 "net.inet.ip.dummynet.red_avg_pkt_size must" 426 " be greater than zero"); 427 428 len = sizeof(clock_hz); 429 if (sysctlbyname("net.inet.ip.dummynet.hz", &clock_hz, &len, 430 NULL, 0) == -1) { 431 errx(1, "sysctlbyname(\"%s\")", 432 "net.inet.ip.dummynet.hz"); 433 } 434 435 /* 436 * Ticks needed for sending a medium-sized packet. 437 * Unfortunately, when we are configuring a WF2Q+ queue, we 438 * do not have bandwidth information, because that is stored 439 * in the parent pipe, and also we have multiple queues 440 * competing for it. So we set s=0, which is not very 441 * correct. But on the other hand, why do we want RED with 442 * WF2Q+ ? 443 */ 444 if (pipe.bandwidth == 0) /* this is a WF2Q+ queue */ 445 s = 0; 446 else 447 s = clock_hz * avg_pkt_size * 8 / pipe.bandwidth; 448 449 /* 450 * max idle time (in ticks) before avg queue size becomes 0. 451 * NOTA: (3/w_q) is approx the value x so that 452 * (1-w_q)^x < 10^-3. 453 */ 454 w_q = ((double)pipe.fs.w_q) / (1 << SCALE_RED); 455 idle = s * 3. / w_q; 456 pipe.fs.lookup_step = (int)idle / lookup_depth; 457 if (!pipe.fs.lookup_step) 458 pipe.fs.lookup_step = 1; 459 weight = 1 - w_q; 460 for (t = pipe.fs.lookup_step; t > 0; --t) 461 weight *= weight; 462 pipe.fs.lookup_weight = (int)(weight * (1 << SCALE_RED)); 463 } 464 i = do_set_x(IP_DUMMYNET_CONFIGURE, &pipe, sizeof pipe); 465 if (i) 466 err(1, "do_set_x(%s)", "IP_DUMMYNET_CONFIGURE"); 467 } 468 469 470 void 471 show_dummynet(int ac, char *av[]) 472 { 473 void *data = NULL; 474 int nbytes; 475 int nalloc = 1024; /* start somewhere... */ 476 477 NEXT_ARG; 478 479 nbytes = nalloc; 480 while (nbytes >= nalloc) { 481 nalloc = nalloc * 2 + 200; 482 nbytes = nalloc; 483 if ((data = realloc(data, nbytes)) == NULL) 484 err(EX_OSERR, "realloc"); 485 if (do_get_x(IP_DUMMYNET_GET, data, &nbytes) < 0) { 486 err(EX_OSERR, "do_get_x(IP_%s_GET)", 487 do_pipe ? "DUMMYNET" : "FW"); 488 } 489 } 490 491 show_pipes(data, nbytes, ac, av); 492 free(data); 493 } 494 495 void 496 show_pipes(void *data, int nbytes, int ac, char *av[]) 497 { 498 u_long rulenum; 499 void *next = data; 500 struct dn_ioc_pipe *p = (struct dn_ioc_pipe *)data; 501 struct dn_ioc_flowset *fs; 502 struct dn_ioc_flowqueue *q; 503 int l; 504 505 if (ac > 0) 506 rulenum = strtoul(*av++, NULL, 10); 507 else 508 rulenum = 0; 509 for (; nbytes >= sizeof(*p); p = (struct dn_ioc_pipe *)next) { 510 double b = p->bandwidth; 511 char buf[30]; 512 char prefix[80]; 513 514 if (p->fs.fs_type != DN_IS_PIPE) 515 break; /* done with pipes, now queues */ 516 517 /* 518 * compute length, as pipe have variable size 519 */ 520 l = sizeof(*p) + p->fs.rq_elements * sizeof(*q); 521 next = (void *)p + l; 522 nbytes -= l; 523 524 if (rulenum != 0 && rulenum != p->pipe_nr) 525 continue; 526 527 /* 528 * Print rate 529 */ 530 if (b == 0) 531 sprintf(buf, "unlimited"); 532 else if (b >= 1000000) 533 sprintf(buf, "%7.3f Mbit/s", b/1000000); 534 else if (b >= 1000) 535 sprintf(buf, "%7.3f Kbit/s", b/1000); 536 else 537 sprintf(buf, "%7.3f bit/s ", b); 538 539 sprintf(prefix, "%05d: %s %4d ms ", 540 p->pipe_nr, buf, p->delay); 541 show_flowset_parms(&p->fs, prefix); 542 if (verbose) 543 printf(" V %20ju\n", (uintmax_t)p->V >> MY_M); 544 545 q = (struct dn_ioc_flowqueue *)(p+1); 546 show_queues(&p->fs, q); 547 } 548 549 for (fs = next; nbytes >= sizeof(*fs); fs = next) { 550 char prefix[80]; 551 552 if (fs->fs_type != DN_IS_QUEUE) 553 break; 554 l = sizeof(*fs) + fs->rq_elements * sizeof(*q); 555 next = (void *)fs + l; 556 nbytes -= l; 557 q = (struct dn_ioc_flowqueue *)(fs+1); 558 sprintf(prefix, "q%05d: weight %d pipe %d ", 559 fs->fs_nr, fs->weight, fs->parent_nr); 560 show_flowset_parms(fs, prefix); 561 show_queues(fs, q); 562 } 563 } 564 565 void 566 show_queues(struct dn_ioc_flowset *fs, struct dn_ioc_flowqueue *q) 567 { 568 int l; 569 570 printf("mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", 571 fs->flow_mask.u.ip.proto, 572 fs->flow_mask.u.ip.src_ip, fs->flow_mask.u.ip.src_port, 573 fs->flow_mask.u.ip.dst_ip, fs->flow_mask.u.ip.dst_port); 574 if (fs->rq_elements == 0) 575 return; 576 577 printf("BKT Prot ___Source IP/port____ " 578 "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n"); 579 if (do_sort != 0) 580 heapsort(q, fs->rq_elements, sizeof(*q), sort_q); 581 for (l = 0; l < fs->rq_elements; l++) { 582 struct in_addr ina; 583 struct protoent *pe; 584 585 ina.s_addr = htonl(q[l].id.u.ip.src_ip); 586 printf("%3d ", q[l].hash_slot); 587 pe = getprotobynumber(q[l].id.u.ip.proto); 588 if (pe) 589 printf("%-4s ", pe->p_name); 590 else 591 printf("%4u ", q[l].id.u.ip.proto); 592 printf("%15s/%-5d ", 593 inet_ntoa(ina), q[l].id.u.ip.src_port); 594 ina.s_addr = htonl(q[l].id.u.ip.dst_ip); 595 printf("%15s/%-5d ", 596 inet_ntoa(ina), q[l].id.u.ip.dst_port); 597 printf("%4ju %8ju %2u %4u %3u\n", 598 (uintmax_t)q[l].tot_pkts, (uintmax_t)q[l].tot_bytes, 599 q[l].len, q[l].len_bytes, q[l].drops); 600 if (verbose) 601 printf(" S %20ju F %20ju\n", 602 (uintmax_t)q[l].S, (uintmax_t)q[l].F); 603 } 604 } 605 606 void 607 show_flowset_parms(struct dn_ioc_flowset *fs, char *prefix) 608 { 609 char qs[30]; 610 char plr[30]; 611 char red[90]; /* Display RED parameters */ 612 int l; 613 614 l = fs->qsize; 615 if (fs->flags_fs & DN_QSIZE_IS_BYTES) { 616 if (l >= 8192) 617 sprintf(qs, "%d KB", l / 1024); 618 else 619 sprintf(qs, "%d B", l); 620 } else 621 sprintf(qs, "%3d sl.", l); 622 if (fs->plr) 623 sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); 624 else 625 plr[0] = '\0'; 626 if (fs->flags_fs & DN_IS_RED) /* RED parameters */ 627 sprintf(red, 628 "\n\t %cRED w_q %f min_th %d max_th %d max_p %f", 629 (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ', 630 1.0 * fs->w_q / (double)(1 << SCALE_RED), 631 SCALE_VAL(fs->min_th), 632 SCALE_VAL(fs->max_th), 633 1.0 * fs->max_p / (double)(1 << SCALE_RED)); 634 else 635 sprintf(red, "droptail"); 636 637 printf("%s %s%s %d queues (%d buckets) %s\n", 638 prefix, qs, plr, fs->rq_elements, fs->rq_size, red); 639 } 640 641 unsigned long 642 getbw(const char *str, u_short *flags, int kb) 643 { 644 unsigned long val; 645 int inbytes = 0; 646 char *end; 647 648 val = strtoul(str, &end, 0); 649 if (*end == 'k' || *end == 'K') { 650 ++end; 651 val *= kb; 652 } else if (*end == 'm' || *end == 'M') { 653 ++end; 654 val *= kb * kb; 655 } 656 657 /* 658 * Deal with bits or bytes or b(bits) or B(bytes). If there is no 659 * trailer assume bits. 660 */ 661 if (strncasecmp(end, "bit", 3) == 0) { 662 ; 663 } else if (strncasecmp(end, "byte", 4) == 0) { 664 inbytes = 1; 665 } else if (*end == 'b') { 666 ; 667 } else if (*end == 'B') { 668 inbytes = 1; 669 } 670 671 /* 672 * Return in bits if flags is NULL, else flag bits 673 * or bytes in flags and return the unconverted value. 674 */ 675 if (inbytes && flags) 676 *flags |= DN_QSIZE_IS_BYTES; 677 else if (inbytes && flags == NULL) 678 val *= 8; 679 680 return(val); 681 } 682 683 void 684 dummynet_flush(void) 685 { 686 int cmd = IP_FW_FLUSH; 687 if (do_pipe) { 688 cmd = IP_DUMMYNET_FLUSH; 689 } 690 if (!do_force) { 691 int c; 692 693 printf("Are you sure? [yn] "); 694 fflush(stdout); 695 do { 696 c = toupper(getc(stdin)); 697 while (c != '\n' && getc(stdin) != '\n') 698 if (feof(stdin)) 699 return; /* and do not flush */ 700 } while (c != 'Y' && c != 'N'); 701 if (c == 'N') /* user said no */ 702 return; 703 } 704 if (do_set_x(cmd, NULL, 0) < 0 ) { 705 if (do_pipe) 706 errx(EX_USAGE, "pipe/queue in use"); 707 else 708 errx(EX_USAGE, "do_set_x(IP_FW_FLUSH) failed"); 709 } 710 if (!do_quiet) { 711 printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules"); 712 } 713 } 714 715 void 716 dummynet_main(int ac, char **av) 717 { 718 if (!strncmp(*av, "config", strlen(*av))) { 719 config_dummynet(ac, av); 720 } else if (!strncmp(*av, "flush", strlen(*av))) { 721 dummynet_flush(); 722 } else if (!strncmp(*av, "show", strlen(*av))) { 723 show_dummynet(ac, av); 724 } else { 725 errx(EX_USAGE, "bad ipfw pipe command `%s'", *av); 726 } 727 } 728