ipfw3_nat: highspeed lockless in-kernel NAT
[dragonfly.git] / sbin / ipfw3 / ipfw3dummynet.c
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 }