This repo is obsolete, please see git://git.code.sf.net/p/dummynet/code@master
[ipfw.git] / ipfw / dummynet.c
1 /*
2  * Copyright (c) 2002-2003,2010 Luigi Rizzo
3  *
4  * Redistribution and use in source forms, with and without modification,
5  * are permitted provided that this entire comment appears intact.
6  *
7  * Redistribution in binary form may occur without any restrictions.
8  * Obviously, it would be nice if you gave credit where credit is due
9  * but requiring it would be too onerous.
10  *
11  * This software is provided ``AS IS'' without any warranties of any kind.
12  *
13  * $FreeBSD: head/sbin/ipfw/dummynet.c 206843 2010-04-19 15:11:45Z luigi $
14  *
15  * dummynet support
16  */
17
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 /* XXX there are several sysctl leftover here */
21 #include <sys/sysctl.h>
22
23 #include "ipfw2.h"
24
25 #include <ctype.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <libutil.h>
29 #include <netdb.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sysexits.h>
34
35 #include <net/if.h>
36 #include <netinet/in.h>
37 #include <netinet/ip_fw.h>
38 #include <netinet/ip_dummynet.h>
39 #include <arpa/inet.h>  /* inet_ntoa */
40
41
42 static struct _s_x dummynet_params[] = {
43         { "plr",                TOK_PLR },
44         { "noerror",            TOK_NOERROR },
45         { "buckets",            TOK_BUCKETS },
46         { "dst-ip",             TOK_DSTIP },
47         { "src-ip",             TOK_SRCIP },
48         { "dst-port",           TOK_DSTPORT },
49         { "src-port",           TOK_SRCPORT },
50         { "proto",              TOK_PROTO },
51         { "weight",             TOK_WEIGHT },
52         { "lmax",               TOK_LMAX },
53         { "maxlen",             TOK_LMAX },
54         { "all",                TOK_ALL },
55         { "mask",               TOK_MASK }, /* alias for both */
56         { "sched_mask",         TOK_SCHED_MASK },
57         { "flow_mask",          TOK_FLOW_MASK },
58         { "droptail",           TOK_DROPTAIL },
59         { "red",                TOK_RED },
60         { "gred",               TOK_GRED },
61         { "bw",                 TOK_BW },
62         { "bandwidth",          TOK_BW },
63         { "delay",              TOK_DELAY },
64         { "link",               TOK_LINK },
65         { "pipe",               TOK_PIPE },
66         { "queue",              TOK_QUEUE },
67         { "flowset",            TOK_FLOWSET },
68         { "sched",              TOK_SCHED },
69         { "pri",                TOK_PRI },
70         { "priority",           TOK_PRI },
71         { "type",               TOK_TYPE },
72         { "flow-id",            TOK_FLOWID},
73         { "dst-ipv6",           TOK_DSTIP6},
74         { "dst-ip6",            TOK_DSTIP6},
75         { "src-ipv6",           TOK_SRCIP6},
76         { "src-ip6",            TOK_SRCIP6},
77         { "profile",            TOK_PROFILE},
78         { "burst",              TOK_BURST},
79         { "dummynet-params",    TOK_NULL },
80         { NULL, 0 }     /* terminator */
81 };
82
83 #define O_NEXT(p, len) ((void *)((char *)p + len))
84
85 static void
86 oid_fill(struct dn_id *oid, int len, int type, uintptr_t id)
87 {
88         oid->len = len;
89         oid->type = type;
90         oid->subtype = 0;
91         oid->id = id;
92 }
93
94 /* make room in the buffer and move the pointer forward */
95 static void *
96 o_next(struct dn_id **o, int len, int type)
97 {
98         struct dn_id *ret = *o;
99         oid_fill(ret, len, type, 0);
100         *o = O_NEXT(*o, len);
101         return ret;
102 }
103
104 /* handle variable lenght structures moving back the pointer and fixing lenght */
105 static void *
106 o_compact(struct dn_id **o, int len, int real_length, int type)
107 {
108         struct dn_id *ret = *o;
109
110         ret = O_NEXT(*o, -len);
111         oid_fill(ret, real_length, type, 0);
112         *o = O_NEXT(ret, real_length);
113         return ret;
114 }
115
116 #if 0
117 static int
118 sort_q(void *arg, const void *pa, const void *pb)
119 {
120         int rev = (co.do_sort < 0);
121         int field = rev ? -co.do_sort : co.do_sort;
122         long long res = 0;
123         const struct dn_flow_queue *a = pa;
124         const struct dn_flow_queue *b = pb;
125
126         switch (field) {
127         case 1: /* pkts */
128                 res = a->len - b->len;
129                 break;
130         case 2: /* bytes */
131                 res = a->len_bytes - b->len_bytes;
132                 break;
133
134         case 3: /* tot pkts */
135                 res = a->tot_pkts - b->tot_pkts;
136                 break;
137
138         case 4: /* tot bytes */
139                 res = a->tot_bytes - b->tot_bytes;
140                 break;
141         }
142         if (res < 0)
143                 res = -1;
144         if (res > 0)
145                 res = 1;
146         return (int)(rev ? res : -res);
147 }
148 #endif
149
150 /* print a mask and header for the subsequent list of flows */
151 static void
152 print_mask(struct ipfw_flow_id *id)
153 {
154         if (!IS_IP6_FLOW_ID(id)) {
155                 printf("    "
156                     "mask: %s 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
157                     id->extra ? "queue," : "",
158                     id->proto,
159                     id->src_ip, id->src_port,
160                     id->dst_ip, id->dst_port);
161         } else {
162                 char buf[255];
163                 printf("\n        mask: %sproto: 0x%02x, flow_id: 0x%08x,  ",
164                     id->extra ? "queue," : "",
165                     id->proto, id->flow_id6);
166                 inet_ntop(AF_INET6, &(id->src_ip6), buf, sizeof(buf));
167                 printf("%s/0x%04x -> ", buf, id->src_port);
168                 inet_ntop(AF_INET6, &(id->dst_ip6), buf, sizeof(buf));
169                 printf("%s/0x%04x\n", buf, id->dst_port);
170         }
171 }
172
173 static void
174 print_header(struct ipfw_flow_id *id)
175 {
176         if (!IS_IP6_FLOW_ID(id))
177                 printf("BKT Prot ___Source IP/port____ "
178                     "____Dest. IP/port____ "
179                     "Tot_pkt/bytes Pkt/Byte Drp\n");
180         else
181                 printf("BKT ___Prot___ _flow-id_ "
182                     "______________Source IPv6/port_______________ "
183                     "_______________Dest. IPv6/port_______________ "
184                     "Tot_pkt/bytes Pkt/Byte Drp\n");
185 }
186
187 static void
188 list_flow(struct dn_flow *ni, int *print)
189 {
190         char buff[255];
191         struct protoent *pe = NULL;
192         struct in_addr ina;
193         struct ipfw_flow_id *id = &ni->fid;
194
195         if (*print) {
196                 print_header(&ni->fid);
197                 *print = 0;
198         }
199         pe = getprotobynumber(id->proto);
200                 /* XXX: Should check for IPv4 flows */
201         printf("%3u%c", (ni->oid.id) & 0xff,
202                 id->extra ? '*' : ' ');
203         if (!IS_IP6_FLOW_ID(id)) {
204                 if (pe)
205                         printf("%-4s ", pe->p_name);
206                 else
207                         printf("%4u ", id->proto);
208                 ina.s_addr = htonl(id->src_ip);
209                 printf("%15s/%-5d ",
210                     inet_ntoa(ina), id->src_port);
211                 ina.s_addr = htonl(id->dst_ip);
212                 printf("%15s/%-5d ",
213                     inet_ntoa(ina), id->dst_port);
214         } else {
215                 /* Print IPv6 flows */
216                 if (pe != NULL)
217                         printf("%9s ", pe->p_name);
218                 else
219                         printf("%9u ", id->proto);
220                 printf("%7d  %39s/%-5d ", id->flow_id6,
221                     inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)),
222                     id->src_port);
223                 printf(" %39s/%-5d ",
224                     inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)),
225                     id->dst_port);
226         }
227         pr_u64(&ni->tot_pkts, 4);
228         pr_u64(&ni->tot_bytes, 8);
229         printf("%2u %4u %3u\n",
230             ni->length, ni->len_bytes, ni->drops);
231 }
232
233 static void
234 print_flowset_parms(struct dn_fs *fs, char *prefix)
235 {
236         int l;
237         char qs[30];
238         char plr[30];
239         char red[90];   /* Display RED parameters */
240
241         l = fs->qsize;
242         if (fs->flags & DN_QSIZE_BYTES) {
243                 if (l >= 8192)
244                         sprintf(qs, "%d KB", l / 1024);
245                 else
246                         sprintf(qs, "%d B", l);
247         } else
248                 sprintf(qs, "%3d sl.", l);
249         if (fs->plr)
250                 sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
251         else
252                 plr[0] = '\0';
253
254         if (fs->flags & DN_IS_RED)      /* RED parameters */
255                 sprintf(red,
256                     "\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
257                     (fs->flags & DN_IS_GENTLE_RED) ? 'G' : ' ',
258                     1.0 * fs->w_q / (double)(1 << SCALE_RED),
259                     fs->min_th,
260                     fs->max_th,
261                     1.0 * fs->max_p / (double)(1 << SCALE_RED));
262         else
263                 sprintf(red, "droptail");
264
265         if (prefix[0]) {
266             printf("%s %s%s %d queues (%d buckets) %s\n",
267                 prefix, qs, plr, fs->oid.id, fs->buckets, red);
268             prefix[0] = '\0';
269         } else {
270             printf("q%05d %s%s %d flows (%d buckets) sched %d "
271                         "weight %d lmax %d pri %d %s\n",
272                 fs->fs_nr, qs, plr, fs->oid.id, fs->buckets,
273                 fs->sched_nr, fs->par[0], fs->par[1], fs->par[2], red);
274             if (fs->flags & DN_HAVE_MASK)
275                 print_mask(&fs->flow_mask);
276         }
277 }
278
279 static void
280 print_extra_delay_parms(struct dn_profile *p)
281 {
282         double loss;
283         if (p->samples_no <= 0)
284                 return;
285
286         loss = p->loss_level;
287         loss /= p->samples_no;
288         printf("\t profile: name \"%s\" loss %f samples %d\n",
289                 p->name, loss, p->samples_no);
290 }
291
292 static void
293 flush_buf(char *buf)
294 {
295         if (buf[0])
296                 printf("%s\n", buf);
297         buf[0] = '\0';
298 }
299         
300 /*
301  * generic list routine. We expect objects in a specific order, i.e.
302  * PIPES AND SCHEDULERS:
303  *      link; scheduler; internal flowset if any; instances
304  * we can tell a pipe from the number.
305  *
306  * FLOWSETS:
307  *      flowset; queues;
308  * link i (int queue); scheduler i; si(i) { flowsets() : queues }
309  */
310 static void
311 list_pipes(struct dn_id *oid, struct dn_id *end)
312 {
313     char buf[160];      /* pending buffer */
314     int toPrint = 1;    /* print header */
315
316     buf[0] = '\0';
317     for (; oid != end; oid = O_NEXT(oid, oid->len)) {
318         if (oid->len < sizeof(*oid))
319                 errx(1, "invalid oid len %d\n", oid->len);
320
321         switch (oid->type) {
322         default:
323             flush_buf(buf);
324             printf("unrecognized object %d size %d\n", oid->type, oid->len);
325             break;
326         case DN_TEXT: /* list of attached flowsets */
327             {
328                 int i, l;
329                 struct {
330                         struct dn_id id;
331                         uint32_t p[0];
332                 } *d = (void *)oid;
333                 l = (oid->len - sizeof(*oid))/sizeof(d->p[0]);
334                 if (l == 0)
335                     break;
336                 printf("   Children flowsets: ");
337                 for (i = 0; i < l; i++)
338                         printf("%u ", d->p[i]);
339                 printf("\n");
340                 break;
341             }
342         case DN_CMD_GET:
343             if (co.verbose)
344                 printf("answer for cmd %d, len %d\n", oid->type, oid->id);
345             break;
346         case DN_SCH: {
347             struct dn_sch *s = (struct dn_sch *)oid;
348             flush_buf(buf);
349             printf(" sched %d type %s flags 0x%x %d buckets %d active\n",
350                         s->sched_nr,
351                         s->name, s->flags, s->buckets, s->oid.id);
352             if (s->flags & DN_HAVE_MASK)
353                 print_mask(&s->sched_mask);
354             }
355             break;
356
357         case DN_FLOW:
358             list_flow((struct dn_flow *)oid, &toPrint);
359             break;
360
361         case DN_LINK: {
362             struct dn_link *p = (struct dn_link *)oid;
363             double b = p->bandwidth;
364             char bwbuf[30];
365             char burst[5 + 7];
366
367             /* This starts a new object so flush buffer */
368             flush_buf(buf);
369             /* data rate */
370             if (b == 0)
371                 sprintf(bwbuf, "unlimited     ");
372             else if (b >= 1000000)
373                 sprintf(bwbuf, "%7.3f Mbit/s", b/1000000);
374             else if (b >= 1000)
375                 sprintf(bwbuf, "%7.3f Kbit/s", b/1000);
376             else
377                 sprintf(bwbuf, "%7.3f bit/s ", b);
378
379             if (humanize_number(burst, sizeof(burst), p->burst,
380                     "", HN_AUTOSCALE, 0) < 0 || co.verbose)
381                 sprintf(burst, "%d", (int)p->burst);
382             sprintf(buf, "%05d: %s %4d ms burst %s",
383                 p->link_nr % DN_MAX_ID, bwbuf, p->delay, burst);
384             }
385             break;
386
387         case DN_FS:
388             print_flowset_parms((struct dn_fs *)oid, buf);
389             break;
390         case DN_PROFILE:
391             flush_buf(buf);
392             print_extra_delay_parms((struct dn_profile *)oid);
393         }
394         flush_buf(buf); // XXX does it really go here ?
395     }
396 }
397
398 /*
399  * Delete pipe, queue or scheduler i
400  */
401 int
402 ipfw_delete_pipe(int do_pipe, int i)
403 {
404         struct {
405                 struct dn_id oid;
406                 uintptr_t a[1]; /* add more if we want a list */
407         } cmd;
408         oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION);
409         cmd.oid.subtype = (do_pipe == 1) ? DN_LINK :
410                 ( (do_pipe == 2) ? DN_FS : DN_SCH);
411         cmd.a[0] = i;
412         i = do_cmd(IP_DUMMYNET3, &cmd, cmd.oid.len);
413         if (i) {
414                 i = 1;
415                 warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", i);
416         }
417         return i;
418 }
419
420 /*
421  * Code to parse delay profiles.
422  *
423  * Some link types introduce extra delays in the transmission
424  * of a packet, e.g. because of MAC level framing, contention on
425  * the use of the channel, MAC level retransmissions and so on.
426  * From our point of view, the channel is effectively unavailable
427  * for this extra time, which is constant or variable depending
428  * on the link type. Additionally, packets may be dropped after this
429  * time (e.g. on a wireless link after too many retransmissions).
430  * We can model the additional delay with an empirical curve
431  * that represents its distribution.
432  *
433  *      cumulative probability
434  *      1.0 ^
435  *          |
436  *      L   +-- loss-level          x
437  *          |                 ******
438  *          |                *
439  *          |           *****
440  *          |          *
441  *          |        **
442  *          |       *                         
443  *          +-------*------------------->
444  *                      delay
445  *
446  * The empirical curve may have both vertical and horizontal lines.
447  * Vertical lines represent constant delay for a range of
448  * probabilities; horizontal lines correspond to a discontinuty
449  * in the delay distribution: the link will use the largest delay
450  * for a given probability.
451  * 
452  * To pass the curve to dummynet, we must store the parameters
453  * in a file as described below, and issue the command
454  *
455  *      ipfw pipe <n> config ... bw XXX profile <filename> ...
456  *
457  * The file format is the following, with whitespace acting as
458  * a separator and '#' indicating the beginning a comment:
459  *
460  *      samples N
461  *              the number of samples used in the internal
462  *              representation (2..1024; default 100);
463  *
464  *      loss-level L 
465  *              The probability above which packets are lost.
466  *               (0.0 <= L <= 1.0, default 1.0 i.e. no loss);
467  *
468  *      name identifier
469  *              Optional a name (listed by "ipfw pipe show")
470  *              to identify the distribution;
471  *
472  *      "delay prob" | "prob delay"
473  *              One of these two lines is mandatory and defines
474  *              the format of the following lines with data points.
475  *
476  *      XXX YYY
477  *              2 or more lines representing points in the curve,
478  *              with either delay or probability first, according
479  *              to the chosen format.
480  *              The unit for delay is milliseconds.
481  *
482  * Data points does not need to be ordered or equal to the number
483  * specified in the "samples" line. ipfw will sort and interpolate
484  * the curve as needed.
485  *
486  * Example of a profile file:
487  
488         name    bla_bla_bla
489         samples 100
490         loss-level    0.86
491         prob    delay
492         0       200     # minimum overhead is 200ms
493         0.5     200
494         0.5     300
495         0.8     1000
496         0.9     1300
497         1       1300
498  
499  * Internally, we will convert the curve to a fixed number of
500  * samples, and when it is time to transmit a packet we will
501  * model the extra delay as extra bits in the packet.
502  *
503  */
504
505 #define ED_MAX_LINE_LEN 256+ED_MAX_NAME_LEN
506 #define ED_TOK_SAMPLES  "samples"
507 #define ED_TOK_LOSS     "loss-level"
508 #define ED_TOK_NAME     "name"
509 #define ED_TOK_DELAY    "delay"
510 #define ED_TOK_PROB     "prob"
511 #define ED_TOK_BW       "bw"
512 #define ED_SEPARATORS   " \t\n"
513 #define ED_MIN_SAMPLES_NO       2
514
515 /*
516  * returns 1 if s is a non-negative number, with at least one '.'
517  */
518 static int
519 is_valid_number(const char *s)
520 {
521         int i, dots_found = 0;
522         int len = strlen(s);
523
524         for (i = 0; i<len; ++i)
525                 if (!isdigit(s[i]) && (s[i] !='.' || ++dots_found > 1))
526                         return 0;
527         return 1;
528 }
529
530 /*
531  * Take as input a string describing a bandwidth value
532  * and return the numeric bandwidth value.
533  * set clocking interface or bandwidth value
534  */
535 static void
536 read_bandwidth(char *arg, int *bandwidth, char *if_name, int namelen)
537 {
538         if (*bandwidth != -1)
539                 warnx("duplicate token, override bandwidth value!");
540
541         if (arg[0] >= 'a' && arg[0] <= 'z') {
542                 if (!if_name) {
543                         errx(1, "no if support");
544                 }
545                 if (namelen >= IFNAMSIZ)
546                         warn("interface name truncated");
547                 namelen--;
548                 /* interface name */
549                 strncpy(if_name, arg, namelen);
550                 if_name[namelen] = '\0';
551                 *bandwidth = 0;
552         } else {        /* read bandwidth value */
553                 int bw;
554                 char *end = NULL;
555
556                 bw = strtoul(arg, &end, 0);
557                 if (*end == 'K' || *end == 'k') {
558                         end++;
559                         bw *= 1000;
560                 } else if (*end == 'M' || *end == 'm') {
561                         end++;
562                         bw *= 1000000;
563                 }
564                 if ((*end == 'B' &&
565                         _substrcmp2(end, "Bi", "Bit/s") != 0) ||
566                     _substrcmp2(end, "by", "bytes") == 0)
567                         bw *= 8;
568
569                 if (bw < 0)
570                         errx(EX_DATAERR, "bandwidth too large");
571
572                 *bandwidth = bw;
573                 if (if_name)
574                         if_name[0] = '\0';
575         }
576 }
577
578 struct point {
579         double prob;
580         double delay;
581 };
582
583 static int
584 compare_points(const void *vp1, const void *vp2)
585 {
586         const struct point *p1 = vp1;
587         const struct point *p2 = vp2;
588         double res = 0;
589
590         res = p1->prob - p2->prob;
591         if (res == 0)
592                 res = p1->delay - p2->delay;
593         if (res < 0)
594                 return -1;
595         else if (res > 0)
596                 return 1;
597         else
598                 return 0;
599 }
600
601 #define ED_EFMT(s) EX_DATAERR,"error in %s at line %d: "#s,filename,lineno
602
603 /*
604  * Interpolate a set of proability-value tuples.
605  *
606  * This function takes as input a tuple of values <prob, value>
607  * and samples the interpolated curve described from the tuples.
608  *
609  * The user defined points are stored in the ponts structure.
610  * The number of points is stored in points_no.
611  * The user defined sampling value is stored in samples_no.
612  * The resulting samples are in the "samples" pointer.
613  *
614  *       We assume that The last point for the '1' value of the
615  *       probability should be defined. (XXX add checks for this)
616  *
617  * The input data are points and points_no.
618  * The output data are s (the array of s_no samples)
619  * and s_no (the number of samples)
620  *
621  */
622 static void
623 interpolate_samples(struct point *p, int points_no, 
624                 int *samples, int samples_no, const char *filename)
625 {
626         double dy;              /* delta on the y axis */
627         double y;               /* current value of y */
628         double x;               /* current value of x */
629         double m;               /* the y slope */
630         int i;                  /* samples index */
631         int curr;               /* points current index */
632
633         /* make sure that there are enough points. */
634         /* XXX Duplicated should be removed */
635         if (points_no < 3)
636             errx(EX_DATAERR, "%s too few samples, need at least %d",
637                 filename, 3);
638
639         qsort(p, points_no, sizeof(struct point), compare_points);
640
641         dy = 1.0/samples_no;
642         y = 0;
643
644         for (i=0, curr = 0; i < samples_no; i++, y+=dy) {
645                 /* This statment move the curr pointer to the next point
646                  * skipping the points with the same x value. We are
647                  * guaranteed to exit from the loop because the
648                  * last possible value of y is stricly less than 1
649                  * and the last possible value of the y points is 1 */
650                 while ( y >= p[curr+1].prob ) curr++;
651
652                 /* compute the slope of the curve */
653                 m = (p[curr+1].delay - p[curr].delay) / (p[curr+1].prob - p[curr].prob);
654                 /* compute the x value starting from the current point */
655                 x = p[curr].delay + (y - p[curr].prob) * m;
656                 samples[i] = x;
657         }
658
659         /* add the last sample */
660         samples[i] = p[curr+1].delay;
661 }
662
663 /*
664  * p is the link (old pipe)
665  * pf is the profile
666  */
667 static void
668 load_extra_delays(const char *filename, struct dn_profile *p,
669         struct dn_link *link)
670 {
671         char    line[ED_MAX_LINE_LEN];
672         FILE    *f;
673         int     lineno = 0;
674
675         int     samples = -1;
676         double  loss = -1.0;
677         char    profile_name[ED_MAX_NAME_LEN];
678         int     delay_first = -1;
679         int     do_points = 0;
680         struct point    points[ED_MAX_SAMPLES_NO];
681         int     points_no = 0;
682
683         /* XXX link never NULL? */
684         p->link_nr = link->link_nr;
685
686         profile_name[0] = '\0';
687
688         f = fopen(filename, "r");
689         if (f == NULL)
690                 err(EX_UNAVAILABLE, "fopen: %s", filename);
691
692         while (fgets(line, ED_MAX_LINE_LEN, f)) {         /* read commands */
693                 char *s, *cur = line, *name = NULL, *arg = NULL;
694
695                 ++lineno;
696
697                 /* parse the line */
698                 while (cur) {
699                         s = strsep(&cur, ED_SEPARATORS);
700                         if (s == NULL || *s == '#')
701                                 break;
702                         if (*s == '\0')
703                                 continue;
704                         if (arg)
705                                 errx(ED_EFMT("too many arguments"));
706                         if (name == NULL)
707                                 name = s;
708                         else
709                                 arg = s;
710                 }
711
712                 if ((name == NULL) || (*name == '#'))   /* empty line */
713                         continue;
714
715                 if (!strcasecmp(name, ED_TOK_SAMPLES)) {
716                     if (samples > 0)
717                         errx(ED_EFMT("duplicate ``samples'' line"));
718                     if (atoi(arg) <=0)
719                         errx(ED_EFMT("invalid number of samples"));
720                     samples = atoi(arg);
721                     if (samples>=ED_MAX_SAMPLES_NO-1)
722                             errx(ED_EFMT("too many samples, maximum is %d"),
723                                 ED_MAX_SAMPLES_NO-1);
724                     do_points = 0;
725                 } else if (!strcasecmp(name, ED_TOK_BW)) {
726                     char buf[IFNAMSIZ];
727                     read_bandwidth(arg, &link->bandwidth, buf, sizeof(buf));
728                     p->bandwidth = link->bandwidth;
729                 } else if (!strcasecmp(name, ED_TOK_LOSS)) {
730                     if (loss != -1.0)
731                         errx(ED_EFMT("duplicated token: %s"), name);
732                     if (!is_valid_number(arg))
733                         errx(ED_EFMT("invalid %s"), arg);
734                     loss = atof(arg);
735                     if (loss > 1)
736                         errx(ED_EFMT("%s greater than 1.0"), name);
737                     do_points = 0;
738                 } else if (!strcasecmp(name, ED_TOK_NAME)) {
739                     if (profile_name[0] != '\0')
740                         errx(ED_EFMT("duplicated token: %s"), name);
741                     strncpy(profile_name, arg, sizeof(profile_name) - 1);
742                     profile_name[sizeof(profile_name)-1] = '\0';
743                     do_points = 0;
744                 } else if (!strcasecmp(name, ED_TOK_DELAY)) {
745                     if (do_points)
746                         errx(ED_EFMT("duplicated token: %s"), name);
747                     delay_first = 1;
748                     do_points = 1;
749                 } else if (!strcasecmp(name, ED_TOK_PROB)) {
750                     if (do_points)
751                         errx(ED_EFMT("duplicated token: %s"), name);
752                     delay_first = 0;
753                     do_points = 1;
754                 } else if (do_points) {
755                     if (!is_valid_number(name) || !is_valid_number(arg))
756                         errx(ED_EFMT("invalid point found"));
757                     if (delay_first) {
758                         points[points_no].delay = atof(name);
759                         points[points_no].prob = atof(arg);
760                     } else {
761                         points[points_no].delay = atof(arg);
762                         points[points_no].prob = atof(name);
763                     }
764                     if (points[points_no].prob > 1.0)
765                         errx(ED_EFMT("probability greater than 1.0"));
766                     ++points_no;
767                 } else {
768                     errx(ED_EFMT("unrecognised command '%s'"), name);
769                 }
770         }
771
772         fclose (f);
773
774         if (samples == -1) {
775             warnx("'%s' not found, assuming 100", ED_TOK_SAMPLES);
776             samples = 100;
777         }
778
779         if (loss == -1.0) {
780             warnx("'%s' not found, assuming no loss", ED_TOK_LOSS);
781             loss = 1;
782         }
783
784         interpolate_samples(points, points_no, p->samples, samples, filename);
785
786         p->samples_no = samples++;
787         p->loss_level = loss * samples;
788         strncpy(p->name, profile_name, sizeof(p->name));
789 }
790
791 /*
792  * configuration of pipes, schedulers, flowsets.
793  * When we configure a new scheduler, an empty pipe is created, so:
794  * 
795  * do_pipe = 1 -> "pipe N config ..." only for backward compatibility
796  *      sched N+Delta type fifo sched_mask ...
797  *      pipe N+Delta <parameters>
798  *      flowset N+Delta pipe N+Delta (no parameters)
799  *      sched N type wf2q+ sched_mask ...
800  *      pipe N <parameters>
801  *
802  * do_pipe = 2 -> flowset N config
803  *      flowset N parameters
804  *
805  * do_pipe = 3 -> sched N config
806  *      sched N parameters (default no pipe)
807  *      optional Pipe N config ...
808  * pipe ==>
809  */
810 void
811 ipfw_config_pipe(int ac, char **av)
812 {
813         int i, j;
814         char *end;
815         void *par = NULL;
816         struct dn_id *buf, *base;
817         struct dn_sch *sch = NULL;
818         struct dn_link *p = NULL;
819         struct dn_fs *fs = NULL;
820         struct dn_profile *pf = NULL;
821         struct ipfw_flow_id *mask = NULL;
822         int lmax;
823         uint32_t _foo = 0, *flags = &_foo , *buckets = &_foo;
824         size_t max_pf_size = sizeof(struct dn_profile) + ED_MAX_SAMPLES_NO * sizeof(int);
825
826         /*
827          * allocate space for 1 header,
828          * 1 scheduler, 1 link, 1 flowset, 1 profile
829          */
830         lmax = sizeof(struct dn_id);    /* command header */
831         lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) +
832                 sizeof(struct dn_fs);
833         lmax += max_pf_size;
834
835         av++; ac--;
836         /* Pipe number */
837         if (ac && isdigit(**av)) {
838                 i = atoi(*av); av++; ac--;
839         } else
840                 i = -1;
841         if (i <= 0)
842                 errx(EX_USAGE, "need a pipe/flowset/sched number");
843         base = buf = safe_calloc(1, lmax);
844         /* all commands start with a 'CONFIGURE' and a version */
845         o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG);
846         base->id = DN_API_VERSION;
847
848         switch (co.do_pipe) {
849         case 1: /* "pipe N config ..." */
850                 /* Allocate space for the WF2Q+ scheduler, its link
851                  * and the FIFO flowset. Set the number, but leave
852                  * the scheduler subtype and other parameters to 0
853                  * so the kernel will use appropriate defaults.
854                  * XXX todo: add a flag to record if a parameter
855                  * is actually configured.
856                  * If we do a 'pipe config' mask -> sched_mask.
857                  * The FIFO scheduler and link are derived from the
858                  * WF2Q+ one in the kernel.
859                  */
860                 sch = o_next(&buf, sizeof(*sch), DN_SCH);
861                 p = o_next(&buf, sizeof(*p), DN_LINK);
862                 fs = o_next(&buf, sizeof(*fs), DN_FS);
863
864                 sch->sched_nr = i;
865                 sch->oid.subtype = 0;   /* defaults to WF2Q+ */
866                 mask = &sch->sched_mask;
867                 flags = &sch->flags;
868                 buckets = &sch->buckets;
869                 *flags |= DN_PIPE_CMD;
870
871                 p->link_nr = i;
872
873                 /* This flowset is only for the FIFO scheduler */
874                 fs->fs_nr = i + 2*DN_MAX_ID;
875                 fs->sched_nr = i + DN_MAX_ID;
876                 break;
877
878         case 2: /* "queue N config ... " */
879                 fs = o_next(&buf, sizeof(*fs), DN_FS);
880                 fs->fs_nr = i;
881                 mask = &fs->flow_mask;
882                 flags = &fs->flags;
883                 buckets = &fs->buckets;
884                 break;
885
886         case 3: /* "sched N config ..." */
887                 sch = o_next(&buf, sizeof(*sch), DN_SCH);
888                 fs = o_next(&buf, sizeof(*fs), DN_FS);
889                 sch->sched_nr = i;
890                 mask = &sch->sched_mask;
891                 flags = &sch->flags;
892                 buckets = &sch->buckets;
893                 /* fs is used only with !MULTIQUEUE schedulers */
894                 fs->fs_nr = i + DN_MAX_ID;
895                 fs->sched_nr = i;
896                 break;
897         }
898         /* set to -1 those fields for which we want to reuse existing
899          * values from the kernel.
900          * Also, *_nr and subtype = 0 mean reuse the value from the kernel.
901          * XXX todo: support reuse of the mask.
902          */
903         if (p)
904                 p->bandwidth = -1;
905         for (j = 0; j < sizeof(fs->par)/sizeof(fs->par[0]); j++)
906                 fs->par[j] = -1;
907         while (ac > 0) {
908                 double d;
909                 int tok = match_token(dummynet_params, *av);
910                 ac--; av++;
911
912                 switch(tok) {
913                 case TOK_NOERROR:
914                         NEED(fs, "noerror is only for pipes");
915                         fs->flags |= DN_NOERROR;
916                         break;
917
918                 case TOK_PLR:
919                         NEED(fs, "plr is only for pipes");
920                         NEED1("plr needs argument 0..1\n");
921                         d = strtod(av[0], NULL);
922                         if (d > 1)
923                                 d = 1;
924                         else if (d < 0)
925                                 d = 0;
926                         fs->plr = (int)(d*0x7fffffff);
927                         ac--; av++;
928                         break;
929
930                 case TOK_QUEUE:
931                         NEED(fs, "queue is only for pipes or flowsets");
932                         NEED1("queue needs queue size\n");
933                         end = NULL;
934                         fs->qsize = strtoul(av[0], &end, 0);
935                         if (*end == 'K' || *end == 'k') {
936                                 fs->flags |= DN_QSIZE_BYTES;
937                                 fs->qsize *= 1024;
938                         } else if (*end == 'B' ||
939                             _substrcmp2(end, "by", "bytes") == 0) {
940                                 fs->flags |= DN_QSIZE_BYTES;
941                         }
942                         ac--; av++;
943                         break;
944
945                 case TOK_BUCKETS:
946                         NEED(fs, "buckets is only for pipes or flowsets");
947                         NEED1("buckets needs argument\n");
948                         *buckets = strtoul(av[0], NULL, 0);
949                         ac--; av++;
950                         break;
951
952                 case TOK_FLOW_MASK:
953                 case TOK_SCHED_MASK:
954                 case TOK_MASK:
955                         NEED(mask, "tok_mask");
956                         NEED1("mask needs mask specifier\n");
957                         /*
958                          * per-flow queue, mask is dst_ip, dst_port,
959                          * src_ip, src_port, proto measured in bits
960                          */
961                         par = NULL;
962
963                         bzero(mask, sizeof(*mask));
964                         end = NULL;
965
966                         while (ac >= 1) {
967                             uint32_t *p32 = NULL;
968                             uint16_t *p16 = NULL;
969                             uint32_t *p20 = NULL;
970                             struct in6_addr *pa6 = NULL;
971                             uint32_t a;
972
973                             tok = match_token(dummynet_params, *av);
974                             ac--; av++;
975                             switch(tok) {
976                             case TOK_ALL:
977                                     /*
978                                      * special case, all bits significant
979                                      * except 'extra' (the queue number)
980                                      */
981                                     mask->dst_ip = ~0;
982                                     mask->src_ip = ~0;
983                                     mask->dst_port = ~0;
984                                     mask->src_port = ~0;
985                                     mask->proto = ~0;
986                                     n2mask(&mask->dst_ip6, 128);
987                                     n2mask(&mask->src_ip6, 128);
988                                     mask->flow_id6 = ~0;
989                                     *flags |= DN_HAVE_MASK;
990                                     goto end_mask;
991
992                             case TOK_QUEUE:
993                                     mask->extra = ~0;
994                                     *flags |= DN_HAVE_MASK;
995                                     goto end_mask;
996
997                             case TOK_DSTIP:
998                                     mask->addr_type = 4;
999                                     p32 = &mask->dst_ip;
1000                                     break;
1001
1002                             case TOK_SRCIP:
1003                                     mask->addr_type = 4;
1004                                     p32 = &mask->src_ip;
1005                                     break;
1006
1007                             case TOK_DSTIP6:
1008                                     mask->addr_type = 6;
1009                                     pa6 = &mask->dst_ip6;
1010                                     break;
1011                             
1012                             case TOK_SRCIP6:
1013                                     mask->addr_type = 6;
1014                                     pa6 = &mask->src_ip6;
1015                                     break;
1016
1017                             case TOK_FLOWID:
1018                                     mask->addr_type = 6;
1019                                     p20 = &mask->flow_id6;
1020                                     break;
1021
1022                             case TOK_DSTPORT:
1023                                     p16 = &mask->dst_port;
1024                                     break;
1025
1026                             case TOK_SRCPORT:
1027                                     p16 = &mask->src_port;
1028                                     break;
1029
1030                             case TOK_PROTO:
1031                                     break;
1032
1033                             default:
1034                                     ac++; av--; /* backtrack */
1035                                     goto end_mask;
1036                             }
1037                             if (ac < 1)
1038                                     errx(EX_USAGE, "mask: value missing");
1039                             if (*av[0] == '/') {
1040                                     a = strtoul(av[0]+1, &end, 0);
1041                                     if (pa6 == NULL)
1042                                             a = (a == 32) ? ~0 : (1 << a) - 1;
1043                             } else
1044                                     a = strtoul(av[0], &end, 0);
1045                             if (p32 != NULL)
1046                                     *p32 = a;
1047                             else if (p16 != NULL) {
1048                                     if (a > 0xFFFF)
1049                                             errx(EX_DATAERR,
1050                                                 "port mask must be 16 bit");
1051                                     *p16 = (uint16_t)a;
1052                             } else if (p20 != NULL) {
1053                                     if (a > 0xfffff)
1054                                         errx(EX_DATAERR,
1055                                             "flow_id mask must be 20 bit");
1056                                     *p20 = (uint32_t)a;
1057                             } else if (pa6 != NULL) {
1058                                     if (a > 128)
1059                                         errx(EX_DATAERR,
1060                                             "in6addr invalid mask len");
1061                                     else
1062                                         n2mask(pa6, a);
1063                             } else {
1064                                     if (a > 0xFF)
1065                                             errx(EX_DATAERR,
1066                                                 "proto mask must be 8 bit");
1067                                     mask->proto = (uint8_t)a;
1068                             }
1069                             if (a != 0)
1070                                     *flags |= DN_HAVE_MASK;
1071                             ac--; av++;
1072                         } /* end while, config masks */
1073 end_mask:
1074                         break;
1075
1076                 case TOK_RED:
1077                 case TOK_GRED:
1078                         NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
1079                         fs->flags |= DN_IS_RED;
1080                         if (tok == TOK_GRED)
1081                                 fs->flags |= DN_IS_GENTLE_RED;
1082                         /*
1083                          * the format for parameters is w_q/min_th/max_th/max_p
1084                          */
1085                         if ((end = strsep(&av[0], "/"))) {
1086                             double w_q = strtod(end, NULL);
1087                             if (w_q > 1 || w_q <= 0)
1088                                 errx(EX_DATAERR, "0 < w_q <= 1");
1089                             fs->w_q = (int) (w_q * (1 << SCALE_RED));
1090                         }
1091                         if ((end = strsep(&av[0], "/"))) {
1092                             fs->min_th = strtoul(end, &end, 0);
1093                             if (*end == 'K' || *end == 'k')
1094                                 fs->min_th *= 1024;
1095                         }
1096                         if ((end = strsep(&av[0], "/"))) {
1097                             fs->max_th = strtoul(end, &end, 0);
1098                             if (*end == 'K' || *end == 'k')
1099                                 fs->max_th *= 1024;
1100                         }
1101                         if ((end = strsep(&av[0], "/"))) {
1102                             double max_p = strtod(end, NULL);
1103                             if (max_p > 1 || max_p <= 0)
1104                                 errx(EX_DATAERR, "0 < max_p <= 1");
1105                             fs->max_p = (int)(max_p * (1 << SCALE_RED));
1106                         }
1107                         ac--; av++;
1108                         break;
1109
1110                 case TOK_DROPTAIL:
1111                         NEED(fs, "droptail is only for flowsets");
1112                         fs->flags &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
1113                         break;
1114
1115                 case TOK_BW:
1116                         NEED(p, "bw is only for links");
1117                         NEED1("bw needs bandwidth or interface\n");
1118                         read_bandwidth(av[0], &p->bandwidth, NULL, 0);
1119                         ac--; av++;
1120                         break;
1121
1122                 case TOK_DELAY:
1123                         NEED(p, "delay is only for links");
1124                         NEED1("delay needs argument 0..10000ms\n");
1125                         p->delay = strtoul(av[0], NULL, 0);
1126                         ac--; av++;
1127                         break;
1128
1129                 case TOK_TYPE: {
1130                         int l;
1131                         NEED(sch, "type is only for schedulers");
1132                         NEED1("type needs a string");
1133                         l = strlen(av[0]);
1134                         if (l == 0 || l > 15)
1135                                 errx(1, "type %s too long\n", av[0]);
1136                         strcpy(sch->name, av[0]);
1137                         sch->oid.subtype = 0; /* use string */
1138                         ac--; av++;
1139                         break;
1140                     }
1141
1142                 case TOK_WEIGHT:
1143                         NEED(fs, "weight is only for flowsets");
1144                         NEED1("weight needs argument\n");
1145                         fs->par[0] = strtol(av[0], &end, 0);
1146                         ac--; av++;
1147                         break;
1148
1149                 case TOK_LMAX:
1150                         NEED(fs, "lmax is only for flowsets");
1151                         NEED1("lmax needs argument\n");
1152                         fs->par[1] = strtol(av[0], &end, 0);
1153                         ac--; av++;
1154                         break;
1155
1156                 case TOK_PRI:
1157                         NEED(fs, "priority is only for flowsets");
1158                         NEED1("priority needs argument\n");
1159                         fs->par[2] = strtol(av[0], &end, 0);
1160                         ac--; av++;
1161                         break;
1162
1163                 case TOK_SCHED:
1164                 case TOK_PIPE:
1165                         NEED(fs, "pipe/sched");
1166                         NEED1("pipe/link/sched needs number\n");
1167                         fs->sched_nr = strtoul(av[0], &end, 0);
1168                         ac--; av++;
1169                         break;
1170
1171                 case TOK_PROFILE:
1172                     {
1173                         size_t real_length;
1174
1175                         NEED((!pf), "profile already set");
1176                         NEED(p, "profile");
1177                         NEED1("extra delay needs the file name\n");
1178
1179                         /* load the profile structure using the DN_API */
1180                         pf = o_next(&buf, max_pf_size, DN_PROFILE);
1181                         load_extra_delays(av[0], pf, p); //XXX can't fail?
1182
1183                         /* compact the dn_id structure */
1184                         real_length = sizeof(struct dn_profile) +
1185                                 pf->samples_no * sizeof(int);
1186                         o_compact(&buf, max_pf_size, real_length, DN_PROFILE);
1187                         --ac; ++av;
1188                     }
1189                         break;
1190
1191                 case TOK_BURST:
1192                         NEED(p, "burst");
1193                         NEED1("burst needs argument\n");
1194                         errno = 0;
1195                         if (expand_number(av[0], (int64_t *)&p->burst) < 0)
1196                                 if (errno != ERANGE)
1197                                         errx(EX_DATAERR,
1198                                             "burst: invalid argument");
1199                         if (errno || p->burst > (1ULL << 48) - 1)
1200                                 errx(EX_DATAERR,
1201                                     "burst: out of range (0..2^48-1)");
1202                         ac--; av++;
1203                         break;
1204
1205                 default:
1206                         errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
1207                 }
1208         }
1209
1210         /* check validity of parameters */
1211         if (p) {
1212                 if (p->delay > 10000)
1213                         errx(EX_DATAERR, "delay must be < 10000");
1214                 if (p->bandwidth == -1)
1215                         p->bandwidth = 0;
1216         }
1217         if (fs) {
1218                 /* XXX accept a 0 scheduler to keep the default */
1219             if (fs->flags & DN_QSIZE_BYTES) {
1220                 size_t len;
1221                 long limit;
1222
1223                 len = sizeof(limit);
1224                 if (sysctlbyname("net.inet.ip.dummynet.pipe_byte_limit",
1225                         &limit, &len, NULL, 0) == -1)
1226                         limit = 1024*1024;
1227                 if (fs->qsize > limit)
1228                         errx(EX_DATAERR, "queue size must be < %ldB", limit);
1229             } else {
1230                 size_t len;
1231                 long limit;
1232
1233                 len = sizeof(limit);
1234                 if (sysctlbyname("net.inet.ip.dummynet.pipe_slot_limit",
1235                         &limit, &len, NULL, 0) == -1)
1236                         limit = 100;
1237                 if (fs->qsize > limit)
1238                         errx(EX_DATAERR, "2 <= queue size <= %ld", limit);
1239             }
1240
1241             if (fs->flags & DN_IS_RED) {
1242                 size_t len;
1243                 int lookup_depth, avg_pkt_size;
1244                 double w_q;
1245
1246                 if (fs->min_th >= fs->max_th)
1247                     errx(EX_DATAERR, "min_th %d must be < than max_th %d",
1248                         fs->min_th, fs->max_th);
1249                 if (fs->max_th == 0)
1250                     errx(EX_DATAERR, "max_th must be > 0");
1251
1252                 len = sizeof(int);
1253                 if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
1254                         &lookup_depth, &len, NULL, 0) == -1)
1255                         lookup_depth = 256;
1256                 if (lookup_depth == 0)
1257                     errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
1258                         " must be greater than zero");
1259
1260                 len = sizeof(int);
1261                 if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
1262                         &avg_pkt_size, &len, NULL, 0) == -1)
1263                         avg_pkt_size = 512;
1264
1265                 if (avg_pkt_size == 0)
1266                         errx(EX_DATAERR,
1267                             "net.inet.ip.dummynet.red_avg_pkt_size must"
1268                             " be greater than zero");
1269
1270                 /*
1271                  * Ticks needed for sending a medium-sized packet.
1272                  * Unfortunately, when we are configuring a WF2Q+ queue, we
1273                  * do not have bandwidth information, because that is stored
1274                  * in the parent pipe, and also we have multiple queues
1275                  * competing for it. So we set s=0, which is not very
1276                  * correct. But on the other hand, why do we want RED with
1277                  * WF2Q+ ?
1278                  */
1279 #if 0
1280                 if (p.bandwidth==0) /* this is a WF2Q+ queue */
1281                         s = 0;
1282                 else
1283                         s = (double)ck.hz * avg_pkt_size * 8 / p.bandwidth;
1284 #endif
1285                 /*
1286                  * max idle time (in ticks) before avg queue size becomes 0.
1287                  * NOTA:  (3/w_q) is approx the value x so that
1288                  * (1-w_q)^x < 10^-3.
1289                  */
1290                 w_q = ((double)fs->w_q) / (1 << SCALE_RED);
1291 #if 0 // go in kernel
1292                 idle = s * 3. / w_q;
1293                 fs->lookup_step = (int)idle / lookup_depth;
1294                 if (!fs->lookup_step)
1295                         fs->lookup_step = 1;
1296                 weight = 1 - w_q;
1297                 for (t = fs->lookup_step; t > 1; --t)
1298                         weight *= 1 - w_q;
1299                 fs->lookup_weight = (int)(weight * (1 << SCALE_RED));
1300 #endif
1301             }
1302         }
1303
1304         i = do_cmd(IP_DUMMYNET3, base, (char *)buf - (char *)base);
1305
1306         if (i)
1307                 err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
1308 }
1309
1310 void
1311 dummynet_flush(void)
1312 {
1313         struct dn_id oid;
1314         oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION);
1315         do_cmd(IP_DUMMYNET3, &oid, oid.len);
1316 }
1317
1318 /* Parse input for 'ipfw [pipe|sched|queue] show [range list]'
1319  * Returns the number of ranges, and possibly stores them
1320  * in the array v of size len.
1321  */
1322 static int
1323 parse_range(int ac, char *av[], uint32_t *v, int len)
1324 {
1325         int n = 0;
1326         char *endptr, *s;
1327         uint32_t base[2];
1328
1329         if (v == NULL || len < 2) {
1330                 v = base;
1331                 len = 2;
1332         }
1333
1334         for (s = *av; s != NULL; av++, ac--) {
1335                 v[0] = strtoul(s, &endptr, 10);
1336                 v[1] = (*endptr != '-') ? v[0] :
1337                          strtoul(endptr+1, &endptr, 10);
1338                 if (*endptr == '\0') { /* prepare for next round */
1339                         s = (ac > 0) ? *(av+1) : NULL;
1340                 } else {
1341                         if (*endptr != ',') {
1342                                 warn("invalid number: %s", s);
1343                                 s = ++endptr;
1344                                 continue;
1345                         }
1346                         /* continue processing from here */
1347                         s = ++endptr;
1348                         ac++;
1349                         av--;
1350                 }
1351                 if (v[1] < v[0] ||
1352                         v[1] < 0 || v[1] >= DN_MAX_ID-1 ||
1353                         v[0] < 0 || v[1] >= DN_MAX_ID-1) {
1354                         continue; /* invalid entry */
1355                 }
1356                 n++;
1357                 /* translate if 'pipe list' */
1358                 if (co.do_pipe == 1) {
1359                         v[0] += DN_MAX_ID;
1360                         v[1] += DN_MAX_ID;
1361                 }
1362                 v = (n*2 < len) ? v + 2 : base;
1363         }
1364         return n;
1365 }
1366
1367 /* main entry point for dummynet list functions. co.do_pipe indicates
1368  * which function we want to support.
1369  * av may contain filtering arguments, either individual entries
1370  * or ranges, or lists (space or commas are valid separators).
1371  * Format for a range can be n1-n2 or n3 n4 n5 ...
1372  * In a range n1 must be <= n2, otherwise the range is ignored.
1373  * A number 'n4' is translate in a range 'n4-n4'
1374  * All number must be > 0 and < DN_MAX_ID-1
1375  */
1376 void
1377 dummynet_list(int ac, char *av[], int show_counters)
1378 {
1379         struct dn_id *oid, *x = NULL;
1380         int ret, i, l;
1381         int n;          /* # of ranges */
1382         int buflen;
1383         int max_size;   /* largest obj passed up */
1384
1385         ac--;
1386         av++;           /* skip 'list' | 'show' word */
1387
1388         n = parse_range(ac, av, NULL, 0);       /* Count # of ranges. */
1389
1390         /* Allocate space to store ranges */
1391         l = sizeof(*oid) + sizeof(uint32_t) * n * 2;
1392         oid = safe_calloc(1, l);
1393         oid_fill(oid, l, DN_CMD_GET, DN_API_VERSION);
1394
1395         if (n > 0)      /* store ranges in idx */
1396                 parse_range(ac, av, (uint32_t *)(oid + 1), n*2);
1397         /*
1398          * Compute the size of the largest object returned. If the
1399          * response leaves at least this much spare space in the
1400          * buffer, then surely the response is complete; otherwise
1401          * there might be a risk of truncation and we will need to
1402          * retry with a larger buffer.
1403          * XXX don't bother with smaller structs.
1404          */
1405         max_size = sizeof(struct dn_fs);
1406         if (max_size < sizeof(struct dn_sch))
1407                 max_size = sizeof(struct dn_sch);
1408         if (max_size < sizeof(struct dn_flow))
1409                 max_size = sizeof(struct dn_flow);
1410
1411         switch (co.do_pipe) {
1412         case 1:
1413                 oid->subtype = DN_LINK; /* list pipe */
1414                 break;
1415         case 2:
1416                 oid->subtype = DN_FS;   /* list queue */
1417                 break;
1418         case 3:
1419                 oid->subtype = DN_SCH;  /* list sched */
1420                 break;
1421         }
1422
1423         /*
1424          * Ask the kernel an estimate of the required space (result
1425          * in oid.id), unless we are requesting a subset of objects,
1426          * in which case the kernel does not give an exact answer.
1427          * In any case, space might grow in the meantime due to the
1428          * creation of new queues, so we must be prepared to retry.
1429          */
1430         if (n > 0) {
1431                 buflen = 4*1024;
1432         } else {
1433                 ret = do_cmd(-IP_DUMMYNET3, oid, (uintptr_t)&l);
1434                 if (ret != 0 || oid->id <= sizeof(*oid))
1435                         goto done;
1436                 buflen = oid->id + max_size;
1437                 oid->len = sizeof(*oid); /* restore */
1438         }
1439         /* Try a few times, until the buffer fits */
1440         for (i = 0; i < 20; i++) {
1441                 l = buflen;
1442                 x = safe_realloc(x, l);
1443                 bcopy(oid, x, oid->len);
1444                 ret = do_cmd(-IP_DUMMYNET3, x, (uintptr_t)&l);
1445                 if (ret != 0 || x->id <= sizeof(*oid))
1446                         goto done; /* no response */
1447                 if (l + max_size <= buflen)
1448                         break; /* ok */
1449                 buflen *= 2;     /* double for next attempt */
1450         }
1451         list_pipes(x, O_NEXT(x, l));
1452 done:
1453         if (x)
1454                 free(x);
1455         free(oid);
1456 }