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