xref: /dragonfly/sbin/ipfw3/ipfw3dummynet.c (revision 5e83d98b)
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