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
sort_q(const void * pa,const void * pb)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
config_dummynet(int ac,char ** av)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
show_dummynet(int ac,char * av[])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
show_pipes(void * data,int nbytes,int ac,char * av[])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
show_queues(struct dn_ioc_flowset * fs,struct dn_ioc_flowqueue * q)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
show_flowset_parms(struct dn_ioc_flowset * fs,char * prefix)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
getbw(const char * str,u_short * flags,int kb)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
dummynet_flush(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
dummynet_main(int ac,char ** av)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