]>
git.saurik.com Git - apple/network_cmds.git/blob - dnctl/dnctl.c
2 * Copyright (c) 2002-2011 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 * Copyright (c) 2002-2003 Luigi Rizzo
31 * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
32 * Copyright (c) 1994 Ugen J.S.Antsilevich
34 * Idea and grammar partially left from:
35 * Copyright (c) 1993 Daniel Boulet
37 * Redistribution and use in source forms, with and without modification,
38 * are permitted provided that this entire comment appears intact.
40 * Redistribution in binary form may occur without any restrictions.
41 * Obviously, it would be nice if you gave credit where credit is due
42 * but requiring it would be too onerous.
44 * This software is provided ``AS IS'' without any warranties of any kind.
51 #include <sys/param.h>
52 #include <sys/socket.h>
53 #include <sys/sysctl.h>
68 #include <netinet/in.h>
69 #include <netinet/ip.h>
70 #include <netinet/ip_dummynet.h>
71 #include <arpa/inet.h>
75 do_quiet
, /* Be quiet in add and flush */
76 do_pipe
, /* this cmd refers to a pipe */
77 do_sort
, /* field to sort results (0 = no) */
78 test_only
, /* only check syntax */
81 #define IP_MASK_ALL 0xffffffff
84 * _s_x is a structure that stores a string <-> token pairs, used in
85 * various places in the parser. Entries are stored in arrays,
86 * with an entry with s=NULL as terminator.
87 * The search routines are match_token() and match_value().
88 * Often, an element with x=0 contains an error string.
125 struct _s_x dummynet_params
[] = {
127 { "noerror", TOK_NOERROR
},
128 { "buckets", TOK_BUCKETS
},
129 { "dst-ip", TOK_DSTIP
},
130 { "src-ip", TOK_SRCIP
},
131 { "dst-port", TOK_DSTPORT
},
132 { "src-port", TOK_SRCPORT
},
133 { "proto", TOK_PROTO
},
134 { "weight", TOK_WEIGHT
},
136 { "mask", TOK_MASK
},
137 { "droptail", TOK_DROPTAIL
},
139 { "gred", TOK_GRED
},
141 { "bandwidth", TOK_BW
},
142 { "delay", TOK_DELAY
},
143 { "pipe", TOK_PIPE
},
144 { "queue", TOK_QUEUE
},
145 { "dst-ipv6", TOK_DSTIP6
},
146 { "dst-ip6", TOK_DSTIP6
},
147 { "src-ipv6", TOK_SRCIP6
},
148 { "src-ip6", TOK_SRCIP6
},
149 { "dummynet-params", TOK_NULL
},
150 { NULL
, 0 } /* terminator */
153 static void show_usage(void);
156 void n2mask(struct in6_addr
*, int );
157 unsigned long long align_uint64(const uint64_t *);
159 /* n2mask sets n bits of the mask */
161 n2mask(struct in6_addr
*mask
, int n
)
163 static int minimask
[9] =
164 { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
167 memset(mask
, 0, sizeof(struct in6_addr
));
169 for (; n
> 0; p
++, n
-= 8) {
179 * The following is used to generate a printable argument for
180 * 64-bit numbers, irrespective of platform alignment and bit size.
181 * Because all the printf in this program use %llu as a format,
182 * we just return an unsigned long long, which is larger than
183 * we need in certain cases, but saves the hassle of using
184 * PRIu64 as a format specifier.
185 * We don't care about inlining, this is not performance critical code.
188 align_uint64(const uint64_t *pll
)
192 bcopy (pll
, &ret
, sizeof(ret
));
197 * conditionally runs the command.
200 do_cmd(int optname
, void *optval
, socklen_t
*optlen
)
202 static int s
= -1; /* the socket */
209 s
= socket(AF_INET
, SOCK_RAW
, IPPROTO_RAW
);
211 err(EX_UNAVAILABLE
, "socket");
213 if (optname
== IP_DUMMYNET_GET
)
214 i
= getsockopt(s
, IPPROTO_IP
, optname
, optval
, optlen
);
216 i
= setsockopt(s
, IPPROTO_IP
, optname
, optval
, optlen
? *optlen
: 0);
221 * match_token takes a table and a string, returns the value associated
222 * with the string (-1 in case of failure).
225 match_token(struct _s_x
*table
, char *string
)
228 size_t i
= strlen(string
);
230 for (pt
= table
; i
&& pt
->s
!= NULL
; pt
++)
231 if (strlen(pt
->s
) == i
&& !bcmp(string
, pt
->s
, i
))
237 sort_q(const void *pa
, const void *pb
)
239 int rev
= (do_sort
< 0);
240 int field
= rev
? -do_sort
: do_sort
;
242 const struct dn_flow_queue
*a
= pa
;
243 const struct dn_flow_queue
*b
= pb
;
247 res
= a
->len
- b
->len
;
250 res
= a
->len_bytes
- b
->len_bytes
;
253 case 3: /* tot pkts */
254 res
= a
->tot_pkts
- b
->tot_pkts
;
257 case 4: /* tot bytes */
258 res
= a
->tot_bytes
- b
->tot_bytes
;
265 return (int)(rev
? res
: -res
);
269 list_queues(struct dn_flow_set
*fs
, struct dn_flow_queue
*q
)
272 int index_printed
, indexes
= 0;
276 printf(" mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
278 fs
->flow_mask
.src_ip
, fs
->flow_mask
.src_port
,
279 fs
->flow_mask
.dst_ip
, fs
->flow_mask
.dst_port
);
280 if (fs
->rq_elements
== 0)
283 printf("BKT Prot ___Source IP/port____ "
284 "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n");
286 heapsort(q
, fs
->rq_elements
, sizeof(struct dn_flow_queue
), sort_q
);
288 /* Print IPv4 flows */
289 for (l
= 0; l
< fs
->rq_elements
; l
++) {
292 /* XXX: Should check for IPv4 flows */
293 if (IS_IP6_FLOW_ID(&(q
[l
].id
)))
296 if (!index_printed
) {
298 if (indexes
> 0) /* currently a no-op */
302 "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
304 fs
->flow_mask
.src_ip
, fs
->flow_mask
.src_port
,
305 fs
->flow_mask
.dst_ip
, fs
->flow_mask
.dst_port
);
307 printf("BKT Prot ___Source IP/port____ "
308 "____Dest. IP/port____ "
309 "Tot_pkt/bytes Pkt/Byte Drp\n");
312 printf("%3d ", q
[l
].hash_slot
);
313 pe
= getprotobynumber(q
[l
].id
.proto
);
315 printf("%-4s ", pe
->p_name
);
317 printf("%4u ", q
[l
].id
.proto
);
318 ina
.s_addr
= htonl(q
[l
].id
.src_ip
);
320 inet_ntoa(ina
), q
[l
].id
.src_port
);
321 ina
.s_addr
= htonl(q
[l
].id
.dst_ip
);
323 inet_ntoa(ina
), q
[l
].id
.dst_port
);
324 printf("%4llu %8llu %2u %4u %3u\n",
325 align_uint64(&q
[l
].tot_pkts
),
326 align_uint64(&q
[l
].tot_bytes
),
327 q
[l
].len
, q
[l
].len_bytes
, q
[l
].drops
);
329 printf(" S %20llu F %20llu\n",
330 align_uint64(&q
[l
].S
), align_uint64(&q
[l
].F
));
333 /* Print IPv6 flows */
335 for (l
= 0; l
< fs
->rq_elements
; l
++) {
336 if (!IS_IP6_FLOW_ID(&(q
[l
].id
)))
339 if (!index_printed
) {
344 printf("\n mask: proto: 0x%02x, flow_id: 0x%08x, ",
345 fs
->flow_mask
.proto
, fs
->flow_mask
.flow_id6
);
346 inet_ntop(AF_INET6
, &(fs
->flow_mask
.src_ip6
),
348 printf("%s/0x%04x -> ", buff
, fs
->flow_mask
.src_port
);
349 inet_ntop( AF_INET6
, &(fs
->flow_mask
.dst_ip6
),
350 buff
, sizeof(buff
) );
351 printf("%s/0x%04x\n", buff
, fs
->flow_mask
.dst_port
);
353 printf("BKT ___Prot___ _flow-id_ "
354 "______________Source IPv6/port_______________ "
355 "_______________Dest. IPv6/port_______________ "
356 "Tot_pkt/bytes Pkt/Byte Drp\n");
358 printf("%3d ", q
[l
].hash_slot
);
359 pe
= getprotobynumber(q
[l
].id
.proto
);
361 printf("%9s ", pe
->p_name
);
363 printf("%9u ", q
[l
].id
.proto
);
364 printf("%7d %39s/%-5d ", q
[l
].id
.flow_id6
,
365 inet_ntop(AF_INET6
, &(q
[l
].id
.src_ip6
), buff
, sizeof(buff
)),
367 printf(" %39s/%-5d ",
368 inet_ntop(AF_INET6
, &(q
[l
].id
.dst_ip6
), buff
, sizeof(buff
)),
370 printf(" %4llu %8llu %2u %4u %3u\n",
371 align_uint64(&q
[l
].tot_pkts
),
372 align_uint64(&q
[l
].tot_bytes
),
373 q
[l
].len
, q
[l
].len_bytes
, q
[l
].drops
);
375 printf(" S %20llu F %20llu\n",
376 align_uint64(&q
[l
].S
),
377 align_uint64(&q
[l
].F
));
382 print_flowset_parms(struct dn_flow_set
*fs
, char *prefix
)
387 char red
[90]; /* Display RED parameters */
390 if (fs
->flags_fs
& DN_QSIZE_IS_BYTES
) {
392 snprintf(qs
, sizeof(qs
), "%d KB", l
/ 1024);
394 snprintf(qs
, sizeof(qs
), "%d B", l
);
396 snprintf(qs
, sizeof(qs
), "%3d sl.", l
);
398 snprintf(plr
, sizeof(plr
), "plr %f", 1.0 * fs
->plr
/ (double)(0x7fffffff));
401 if (fs
->flags_fs
& DN_IS_RED
) /* RED parameters */
402 snprintf(red
, sizeof(red
),
403 "\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
404 (fs
->flags_fs
& DN_IS_GENTLE_RED
) ? 'G' : ' ',
405 1.0 * fs
->w_q
/ (double)(1 << SCALE_RED
),
406 SCALE_VAL(fs
->min_th
),
407 SCALE_VAL(fs
->max_th
),
408 1.0 * fs
->max_p
/ (double)(1 << SCALE_RED
));
410 snprintf(red
, sizeof(red
), "droptail");
412 printf("%s %s%s %d queues (%d buckets) %s\n",
413 prefix
, qs
, plr
, fs
->rq_elements
, fs
->rq_size
, red
);
417 list_pipes(void *data
, size_t nbytes
, int ac
, char *av
[])
419 unsigned int rulenum
;
421 struct dn_pipe
*p
= (struct dn_pipe
*) data
;
422 struct dn_flow_set
*fs
;
423 struct dn_flow_queue
*q
;
427 rulenum
= (unsigned int)strtoul(*av
++, NULL
, 10);
430 for (; nbytes
>= sizeof(struct dn_pipe
); p
= (struct dn_pipe
*)next
) {
431 double b
= p
->bandwidth
;
435 if (p
->next
.sle_next
!= (struct dn_pipe
*)DN_IS_PIPE
)
436 break; /* done with pipes, now queues */
439 * compute length, as pipe have variable size
441 l
= sizeof(struct dn_pipe
) + p
->fs
.rq_elements
* sizeof(struct dn_flow_queue
);
442 next
= (char *)p
+ l
;
445 if (rulenum
!= 0 && rulenum
!= p
->pipe_nr
)
449 * Print rate (or clocking interface)
451 if (p
->if_name
[0] != '\0')
452 snprintf(buf
, sizeof(buf
), "%s", p
->if_name
);
454 snprintf(buf
, sizeof(buf
), "unlimited");
455 else if (b
>= 1000000)
456 snprintf(buf
, sizeof(buf
), "%7.3f Mbit/s", b
/1000000);
458 snprintf(buf
, sizeof(buf
), "%7.3f Kbit/s", b
/1000);
460 snprintf(buf
, sizeof(buf
), "%7.3f bit/s ", b
);
462 snprintf(prefix
, sizeof(prefix
), "%05d: %s %4d ms ",
463 p
->pipe_nr
, buf
, p
->delay
);
464 print_flowset_parms(&(p
->fs
), prefix
);
466 printf(" V %20qd\n", p
->V
>> MY_M
);
468 q
= (struct dn_flow_queue
*)(p
+1);
469 list_queues(&(p
->fs
), q
);
471 for (fs
= next
; nbytes
>= sizeof *fs
; fs
= next
) {
474 if (fs
->next
.sle_next
!= (struct dn_flow_set
*)DN_IS_QUEUE
)
476 l
= sizeof(struct dn_flow_set
) + fs
->rq_elements
* sizeof(struct dn_flow_queue
);
477 next
= (char *)fs
+ l
;
479 q
= (struct dn_flow_queue
*)(fs
+1);
480 snprintf(prefix
, sizeof(prefix
), "q%05d: weight %d pipe %d ",
481 fs
->fs_nr
, fs
->weight
, fs
->parent_nr
);
482 print_flowset_parms(fs
, prefix
);
488 list(int ac
, char *av
[], int show_counters
)
494 int nalloc
= 1024; /* start somewhere... */
497 fprintf(stderr
, "Testing only, list disabled\n");
504 /* get rules or pipes from kernel, resizing array as necessary */
507 while (nbytes
>= nalloc
) {
508 nalloc
= nalloc
* 2 + 200;
510 if ((data
= realloc(data
, nbytes
)) == NULL
)
511 err(EX_OSERR
, "realloc");
513 if (do_cmd(IP_DUMMYNET_GET
, data
, &nbytes
) < 0) {
514 if (errno
== ENOBUFS
) {
518 err(EX_OSERR
, "getsockopt(IP_DUMMYNET_GET)");
523 list_pipes(data
, nbytes
, ac
, av
);
527 if (exitval
!= EX_OK
)
534 fprintf(stderr
, "usage: dnctl [options]\n"
535 "do \"dnctl -h\" or see dnctl manpage for details\n"
544 "dnclt [-acdeftTnNpqS] <command> where <command> is one of:\n"
545 "{pipe|queue} N config PIPE-BODY\n"
546 "[pipe|queue] {zero|delete|show} [N{,N}]\n"
552 delete(int ac
, char *av
[])
559 memset(&p
, 0, sizeof(struct dn_pipe
));
563 while (ac
&& isdigit(**av
)) {
564 i
= atoi(*av
); av
++; ac
--;
570 len
= sizeof(struct dn_pipe
);
571 i
= do_cmd(IP_DUMMYNET_DEL
, &p
, &len
);
574 warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
575 do_pipe
== 1 ? p
.pipe_nr
: p
.fs
.fs_nr
);
578 if (exitval
!= EX_OK
)
583 * the following macro returns an error message if we run out of
586 #define NEED1(msg) {if (!ac) errx(EX_USAGE, msg);}
589 config_pipe(int ac
, char **av
)
597 memset(&p
, 0, sizeof(struct dn_pipe
));
601 if (ac
&& isdigit(**av
)) {
602 i
= atoi(*av
); av
++; ac
--;
610 int tok
= match_token(dummynet_params
, *av
);
615 p
.fs
.flags_fs
|= DN_NOERROR
;
619 NEED1("plr needs argument 0..1\n");
620 d
= strtod(av
[0], NULL
);
625 p
.fs
.plr
= (int)(d
*0x7fffffff);
630 NEED1("queue needs queue size\n");
632 p
.fs
.qsize
= (int)strtoul(av
[0], &end
, 0);
633 if (*end
== 'K' || *end
== 'k') {
634 p
.fs
.flags_fs
|= DN_QSIZE_IS_BYTES
;
636 } else if (*end
== 'B' || !strncmp(end
, "by", 2)) {
637 p
.fs
.flags_fs
|= DN_QSIZE_IS_BYTES
;
643 NEED1("buckets needs argument\n");
644 p
.fs
.rq_size
= (int)strtoul(av
[0], NULL
, 0);
649 NEED1("mask needs mask specifier\n");
651 * per-flow queue, mask is dst_ip, dst_port,
652 * src_ip, src_port, proto measured in bits
656 p
.fs
.flow_mask
.dst_ip
= 0;
657 p
.fs
.flow_mask
.src_ip
= 0;
658 p
.fs
.flow_mask
.dst_port
= 0;
659 p
.fs
.flow_mask
.src_port
= 0;
660 p
.fs
.flow_mask
.proto
= 0;
664 uint32_t *p32
= NULL
;
665 uint16_t *p16
= NULL
;
666 struct in6_addr
*pa6
= NULL
;
669 tok
= match_token(dummynet_params
, *av
);
674 * special case, all bits significant
676 p
.fs
.flow_mask
.dst_ip
= ~0;
677 p
.fs
.flow_mask
.src_ip
= ~0;
678 p
.fs
.flow_mask
.dst_port
= ~0;
679 p
.fs
.flow_mask
.src_port
= ~0;
680 p
.fs
.flow_mask
.proto
= ~0;
681 n2mask(&(p
.fs
.flow_mask
.dst_ip6
), 128);
682 n2mask(&(p
.fs
.flow_mask
.src_ip6
), 128);
683 p
.fs
.flags_fs
|= DN_HAVE_FLOW_MASK
;
687 p32
= &p
.fs
.flow_mask
.dst_ip
;
691 p32
= &p
.fs
.flow_mask
.src_ip
;
695 pa6
= &(p
.fs
.flow_mask
.dst_ip6
);
699 pa6
= &(p
.fs
.flow_mask
.src_ip6
);
703 p16
= &p
.fs
.flow_mask
.dst_port
;
707 p16
= &p
.fs
.flow_mask
.src_port
;
714 ac
++; av
--; /* backtrack */
718 errx(EX_USAGE
, "mask: value missing");
720 a
= (int)strtoul(av
[0]+1, &end
, 0);
722 a
= (a
== 32) ? ~0 : (1 << a
) - 1;
724 a
= (int)strtoul(av
[0], &end
, 0);
727 else if (p16
!= NULL
) {
730 "mask: must be 16 bit");
732 } else if (pa6
!= NULL
) {
735 "in6addr invalid mask len");
741 "mask: must be 8 bit");
742 p
.fs
.flow_mask
.proto
= (uint8_t)a
;
745 p
.fs
.flags_fs
|= DN_HAVE_FLOW_MASK
;
747 } /* end while, config masks */
753 NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
754 p
.fs
.flags_fs
|= DN_IS_RED
;
756 p
.fs
.flags_fs
|= DN_IS_GENTLE_RED
;
758 * the format for parameters is w_q/min_th/max_th/max_p
760 if ((end
= strsep(&av
[0], "/"))) {
761 double w_q
= strtod(end
, NULL
);
762 if (w_q
> 1 || w_q
<= 0)
763 errx(EX_DATAERR
, "0 < w_q <= 1");
764 p
.fs
.w_q
= (int) (w_q
* (1 << SCALE_RED
));
766 if ((end
= strsep(&av
[0], "/"))) {
767 p
.fs
.min_th
= (int)strtoul(end
, &end
, 0);
768 if (*end
== 'K' || *end
== 'k')
771 if ((end
= strsep(&av
[0], "/"))) {
772 p
.fs
.max_th
= (int)strtoul(end
, &end
, 0);
773 if (*end
== 'K' || *end
== 'k')
776 if ((end
= strsep(&av
[0], "/"))) {
777 double max_p
= strtod(end
, NULL
);
778 if (max_p
> 1 || max_p
<= 0)
779 errx(EX_DATAERR
, "0 < max_p <= 1");
780 p
.fs
.max_p
= (int)(max_p
* (1 << SCALE_RED
));
786 p
.fs
.flags_fs
&= ~(DN_IS_RED
|DN_IS_GENTLE_RED
);
790 NEED1("bw needs bandwidth or interface\n");
792 errx(EX_DATAERR
, "bandwidth only valid for pipes");
794 * set clocking interface or bandwidth value
796 if (av
[0][0] >= 'a' && av
[0][0] <= 'z') {
797 int l
= sizeof(p
.if_name
)-1;
799 strncpy(p
.if_name
, av
[0], l
);
804 p
.bandwidth
= (int)strtoul(av
[0], &end
, 0);
805 if (*end
== 'K' || *end
== 'k') {
808 } else if (*end
== 'M') {
810 p
.bandwidth
*= 1000000;
812 if (*end
== 'B' || !strncmp(end
, "by", 2))
815 errx(EX_DATAERR
, "bandwidth too large");
822 errx(EX_DATAERR
, "delay only valid for pipes");
823 NEED1("delay needs argument 0..10000ms\n");
824 p
.delay
= (int)strtoul(av
[0], NULL
, 0);
830 errx(EX_DATAERR
,"weight only valid for queues");
831 NEED1("weight needs argument 0..100\n");
832 p
.fs
.weight
= (int)strtoul(av
[0], &end
, 0);
838 errx(EX_DATAERR
,"pipe only valid for queues");
839 NEED1("pipe needs pipe_number\n");
840 p
.fs
.parent_nr
= strtoul(av
[0], &end
, 0);
845 errx(EX_DATAERR
, "unrecognised option ``%s''", *(--av
));
850 errx(EX_DATAERR
, "pipe_nr must be > 0");
852 errx(EX_DATAERR
, "delay must be < 10000");
853 } else { /* do_pipe == 2, queue */
854 if (p
.fs
.parent_nr
== 0)
855 errx(EX_DATAERR
, "pipe must be > 0");
856 if (p
.fs
.weight
>100)
857 errx(EX_DATAERR
, "weight must be <= 100");
859 if (p
.fs
.flags_fs
& DN_QSIZE_IS_BYTES
) {
860 if (p
.fs
.qsize
> 1024*1024)
861 errx(EX_DATAERR
, "queue size must be < 1MB");
863 if (p
.fs
.qsize
> 100)
864 errx(EX_DATAERR
, "2 <= queue size <= 100");
866 if (p
.fs
.flags_fs
& DN_IS_RED
) {
868 int lookup_depth
, avg_pkt_size
;
869 double s
, idle
, weight
, w_q
;
873 if (p
.fs
.min_th
>= p
.fs
.max_th
)
874 errx(EX_DATAERR
, "min_th %d must be < than max_th %d",
875 p
.fs
.min_th
, p
.fs
.max_th
);
876 if (p
.fs
.max_th
== 0)
877 errx(EX_DATAERR
, "max_th must be > 0");
880 if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
881 &lookup_depth
, &len
, NULL
, 0) == -1)
883 errx(1, "sysctlbyname(\"%s\")",
884 "net.inet.ip.dummynet.red_lookup_depth");
885 if (lookup_depth
== 0)
886 errx(EX_DATAERR
, "net.inet.ip.dummynet.red_lookup_depth"
887 " must be greater than zero");
890 if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
891 &avg_pkt_size
, &len
, NULL
, 0) == -1)
893 errx(1, "sysctlbyname(\"%s\")",
894 "net.inet.ip.dummynet.red_avg_pkt_size");
895 if (avg_pkt_size
== 0)
897 "net.inet.ip.dummynet.red_avg_pkt_size must"
898 " be greater than zero");
900 len
= sizeof(struct clockinfo
);
901 if (sysctlbyname("kern.clockrate", &ck
, &len
, NULL
, 0) == -1)
902 errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
905 * Ticks needed for sending a medium-sized packet.
906 * Unfortunately, when we are configuring a WF2Q+ queue, we
907 * do not have bandwidth information, because that is stored
908 * in the parent pipe, and also we have multiple queues
909 * competing for it. So we set s=0, which is not very
910 * correct. But on the other hand, why do we want RED with
913 if (p
.bandwidth
==0) /* this is a WF2Q+ queue */
916 s
= ck
.hz
* avg_pkt_size
* 8 / p
.bandwidth
;
919 * max idle time (in ticks) before avg queue size becomes 0.
920 * NOTA: (3/w_q) is approx the value x so that
923 w_q
= ((double)p
.fs
.w_q
) / (1 << SCALE_RED
);
925 p
.fs
.lookup_step
= (int)idle
/ lookup_depth
;
926 if (!p
.fs
.lookup_step
)
927 p
.fs
.lookup_step
= 1;
929 for (t
= p
.fs
.lookup_step
; t
> 0; --t
)
931 p
.fs
.lookup_weight
= (int)(weight
* (1 << SCALE_RED
));
933 len
= sizeof(struct dn_pipe
);
934 i
= do_cmd(IP_DUMMYNET_CONFIGURE
, &p
, &len
);
936 err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
942 if (!force
&& !do_quiet
) { /* need to ask user */
945 printf("Are you sure? [yn] ");
948 c
= toupper(getc(stdin
));
949 while (c
!= '\n' && getc(stdin
) != '\n')
951 return; /* and do not flush */
952 } while (c
!= 'Y' && c
!= 'N');
954 if (c
== 'N') /* user said no */
958 if (do_cmd(IP_DUMMYNET_FLUSH
, NULL
, 0) < 0)
959 err(EX_UNAVAILABLE
, "setsockopt(IP_DUMMYNET_FLUSH)");
962 printf("Flushed all pipes.\n");
966 * Free a the (locally allocated) copy of command line arguments.
969 free_args(int ac
, char **av
)
973 for (i
=0; i
< ac
; i
++)
979 * Called with the arguments (excluding program name).
980 * Returns 0 if successful, 1 if empty command, errx() in case of errors.
983 parse_args(int oldac
, char **oldav
)
986 char **av
, **save_av
;
987 int do_acct
= 0; /* Show packet/byte count */
988 int do_force
= 0; /* Don't ask for confirmation */
990 #define WHITESP " \t\f\v\n\r"
993 else if (oldac
== 1) {
995 * If we are called with a single string, try to split it into
996 * arguments for subsequent parsing.
997 * But first, remove spaces after a ',', by copying the string
1000 char *arg
= oldav
[0]; /* The string... */
1001 size_t l
= strlen(arg
);
1002 int copy
= 0; /* 1 if we need to copy, 0 otherwise */
1004 for (i
= j
= 0; i
< l
; i
++) {
1005 if (arg
[i
] == '#') /* comment marker */
1009 copy
= !index("," WHITESP
, arg
[i
]);
1011 copy
= !index(WHITESP
, arg
[i
]);
1016 if (!copy
&& j
> 0) /* last char was a 'blank', remove it */
1018 l
= j
; /* the new argument length */
1020 if (l
== 0) /* empty string! */
1024 * First, count number of arguments. Because of the previous
1025 * processing, this is just the number of blanks plus 1.
1027 for (i
= 0, ac
= 1; i
< l
; i
++)
1028 if (index(WHITESP
, arg
[i
]) != NULL
)
1031 av
= calloc(ac
, sizeof(char *));
1034 * Second, copy arguments from cmd[] to av[]. For each one,
1035 * j is the initial character, i is the one past the end.
1037 for (ac
= 0, i
= j
= 0; i
< l
; i
++)
1038 if (index(WHITESP
, arg
[i
]) != NULL
|| i
== l
-1) {
1041 av
[ac
] = calloc(i
-j
+1, 1);
1042 bcopy(arg
+j
, av
[ac
], i
-j
);
1048 * If an argument ends with ',' join with the next one.
1049 * Just add its length to 'l' and continue. When we have a string
1050 * without a ',' ending, we'll have the combined length in 'l'
1055 av
= calloc(oldac
, sizeof(char *));
1056 for (first
= i
= ac
= 0, l
= 0; i
< oldac
; i
++) {
1057 char *arg
= oldav
[i
];
1058 size_t k
= strlen(arg
);
1061 if (arg
[k
-1] != ',' || i
== oldac
-1) {
1062 size_t buflen
= l
+1;
1064 av
[ac
] = calloc(l
+1, 1);
1065 for (l
=0; first
<= i
; first
++) {
1066 strlcat(av
[ac
]+l
, oldav
[first
], buflen
-l
);
1067 l
+= strlen(oldav
[first
]);
1076 /* Set the force flag for non-interactive processes */
1077 do_force
= !isatty(STDIN_FILENO
);
1079 /* Save arguments for final freeing of memory. */
1083 optind
= optreset
= 0;
1084 while ((ch
= getopt(ac
, av
, "afhnqsv")) != -1)
1094 case 'h': /* help */
1095 free_args(save_ac
, save_av
);
1097 break; /* NOTREACHED */
1107 case 's': /* sort */
1108 do_sort
= atoi(optarg
);
1111 case 'v': /* verbose */
1116 free_args(save_ac
, save_av
);
1122 NEED1("bad arguments, for usage summary ``dnctl''");
1125 * An undocumented behaviour of dnctl1 was to allow rule numbers first,
1126 * e.g. "100 add allow ..." instead of "add 100 allow ...".
1127 * In case, swap first and second argument to get the normal form.
1129 if (ac
> 1 && isdigit(*av
[0])) {
1137 * optional: pipe or queue
1140 if (!strncmp(*av
, "pipe", strlen(*av
)))
1142 else if (!strncmp(*av
, "queue", strlen(*av
)))
1148 NEED1("missing command");
1151 * For pipes and queues we normally say 'pipe NN config'
1152 * but the code is easier to parse as 'pipe config NN'
1153 * so we swap the two arguments.
1155 if (do_pipe
> 0 && ac
> 1 && isdigit(*av
[0])) {
1162 if (do_pipe
&& !strncmp(*av
, "config", strlen(*av
)))
1163 config_pipe(ac
, av
);
1164 else if (!strncmp(*av
, "delete", strlen(*av
)))
1166 else if (!strncmp(*av
, "flush", strlen(*av
)))
1168 else if (!strncmp(*av
, "print", strlen(*av
)) ||
1169 !strncmp(*av
, "list", strlen(*av
)))
1170 list(ac
, av
, do_acct
);
1171 else if (!strncmp(*av
, "show", strlen(*av
)))
1172 list(ac
, av
, 1 /* show counters */);
1174 errx(EX_USAGE
, "bad command `%s'", *av
);
1176 /* Free memory allocated in the argument parsing. */
1177 free_args(save_ac
, save_av
);
1182 dnctl_readfile(int ac
, char *av
[])
1186 char *cmd
= NULL
, *filename
= av
[ac
-1];
1191 while ((c
= getopt(ac
, av
, "np:q")) != -1) {
1200 * Skip previous args and delete last one, so we
1201 * pass all but the last argument to the preprocessor
1207 fprintf(stderr
, "command is %s\n", av
[0]);
1215 errx(EX_USAGE
, "bad arguments, for usage"
1216 " summary ``dnctl''");
1223 if (cmd
== NULL
&& ac
!= optind
+ 1) {
1224 fprintf(stderr
, "ac %d, optind %d\n", ac
, optind
);
1225 errx(EX_USAGE
, "extraneous filename arguments");
1228 if ((f
= fopen(filename
, "r")) == NULL
)
1229 err(EX_UNAVAILABLE
, "fopen: %s", filename
);
1231 if (cmd
!= NULL
) { /* pipe through preprocessor */
1234 if (pipe(pipedes
) == -1)
1235 err(EX_OSERR
, "cannot create pipe");
1239 err(EX_OSERR
, "cannot fork");
1243 * Child, will run the preprocessor with the
1244 * file on stdin and the pipe on stdout.
1246 if (dup2(fileno(f
), 0) == -1
1247 || dup2(pipedes
[1], 1) == -1)
1248 err(EX_OSERR
, "dup2()");
1253 err(EX_OSERR
, "execvp(%s) failed", cmd
);
1254 } else { /* parent, will reopen f as the pipe */
1257 if ((f
= fdopen(pipedes
[0], "r")) == NULL
) {
1258 int savederrno
= errno
;
1260 (void)kill(preproc
, SIGTERM
);
1262 err(EX_OSERR
, "fdopen()");
1267 while (fgets(buf
, BUFSIZ
, f
)) { /* read commands */
1272 snprintf(linename
, sizeof(linename
), "Line %d", lineno
);
1273 setprogname(linename
); /* XXX */
1275 parse_args(1, args
);
1281 if (waitpid(preproc
, &status
, 0) == -1)
1282 errx(EX_OSERR
, "waitpid()");
1283 if (WIFEXITED(status
) && WEXITSTATUS(status
) != EX_OK
)
1284 errx(EX_UNAVAILABLE
,
1285 "preprocessor exited with status %d",
1286 WEXITSTATUS(status
));
1287 else if (WIFSIGNALED(status
))
1288 errx(EX_UNAVAILABLE
,
1289 "preprocessor exited with signal %d",
1295 main(int ac
, char *av
[])
1298 * If the last argument is an absolute pathname, interpret it
1299 * as a file to be preprocessed.
1302 if (ac
> 1 && av
[ac
- 1][0] == '/' && access(av
[ac
- 1], R_OK
) == 0)
1303 dnctl_readfile(ac
, av
);
1305 if (parse_args(ac
-1, av
+1))