]>
git.saurik.com Git - apple/network_cmds.git/blob - dnctl/dnctl.c
2 * Copyright (c) 2002-2012 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>
74 * Limit delay to avoid computation overflow
76 #define MAX_DELAY (INT_MAX / 1000)
80 do_quiet
, /* Be quiet in add and flush */
81 do_pipe
, /* this cmd refers to a pipe */
82 do_sort
, /* field to sort results (0 = no) */
83 test_only
, /* only check syntax */
86 #define IP_MASK_ALL 0xffffffff
89 * _s_x is a structure that stores a string <-> token pairs, used in
90 * various places in the parser. Entries are stored in arrays,
91 * with an entry with s=NULL as terminator.
92 * The search routines are match_token() and match_value().
93 * Often, an element with x=0 contains an error string.
130 struct _s_x dummynet_params
[] = {
132 { "noerror", TOK_NOERROR
},
133 { "buckets", TOK_BUCKETS
},
134 { "dst-ip", TOK_DSTIP
},
135 { "src-ip", TOK_SRCIP
},
136 { "dst-port", TOK_DSTPORT
},
137 { "src-port", TOK_SRCPORT
},
138 { "proto", TOK_PROTO
},
139 { "weight", TOK_WEIGHT
},
141 { "mask", TOK_MASK
},
142 { "droptail", TOK_DROPTAIL
},
144 { "gred", TOK_GRED
},
146 { "bandwidth", TOK_BW
},
147 { "delay", TOK_DELAY
},
148 { "pipe", TOK_PIPE
},
149 { "queue", TOK_QUEUE
},
150 { "dst-ipv6", TOK_DSTIP6
},
151 { "dst-ip6", TOK_DSTIP6
},
152 { "src-ipv6", TOK_SRCIP6
},
153 { "src-ip6", TOK_SRCIP6
},
154 { "dummynet-params", TOK_NULL
},
155 { NULL
, 0 } /* terminator */
158 static void show_usage(void);
161 void n2mask(struct in6_addr
*, int );
162 unsigned long long align_uint64(const uint64_t *);
164 /* n2mask sets n bits of the mask */
166 n2mask(struct in6_addr
*mask
, int n
)
168 static int minimask
[9] =
169 { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
172 memset(mask
, 0, sizeof(struct in6_addr
));
174 for (; n
> 0; p
++, n
-= 8) {
184 * The following is used to generate a printable argument for
185 * 64-bit numbers, irrespective of platform alignment and bit size.
186 * Because all the printf in this program use %llu as a format,
187 * we just return an unsigned long long, which is larger than
188 * we need in certain cases, but saves the hassle of using
189 * PRIu64 as a format specifier.
190 * We don't care about inlining, this is not performance critical code.
193 align_uint64(const uint64_t *pll
)
197 bcopy (pll
, &ret
, sizeof(ret
));
202 * conditionally runs the command.
205 do_cmd(int optname
, void *optval
, socklen_t
*optlen
)
207 static int s
= -1; /* the socket */
214 s
= socket(AF_INET
, SOCK_RAW
, IPPROTO_RAW
);
216 err(EX_UNAVAILABLE
, "socket");
218 if (optname
== IP_DUMMYNET_GET
)
219 i
= getsockopt(s
, IPPROTO_IP
, optname
, optval
, optlen
);
221 i
= setsockopt(s
, IPPROTO_IP
, optname
, optval
, optlen
? *optlen
: 0);
226 * match_token takes a table and a string, returns the value associated
227 * with the string (-1 in case of failure).
230 match_token(struct _s_x
*table
, char *string
)
233 size_t i
= strlen(string
);
235 for (pt
= table
; i
&& pt
->s
!= NULL
; pt
++)
236 if (strlen(pt
->s
) == i
&& !bcmp(string
, pt
->s
, i
))
242 sort_q(const void *pa
, const void *pb
)
244 int rev
= (do_sort
< 0);
245 int field
= rev
? -do_sort
: do_sort
;
247 const struct dn_flow_queue
*a
= pa
;
248 const struct dn_flow_queue
*b
= pb
;
252 res
= a
->len
- b
->len
;
255 res
= a
->len_bytes
- b
->len_bytes
;
258 case 3: /* tot pkts */
259 res
= a
->tot_pkts
- b
->tot_pkts
;
262 case 4: /* tot bytes */
263 res
= a
->tot_bytes
- b
->tot_bytes
;
270 return (int)(rev
? res
: -res
);
274 list_queues(struct dn_flow_set
*fs
, struct dn_flow_queue
*q
)
277 int index_printed
, indexes
= 0;
281 printf(" mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
283 fs
->flow_mask
.src_ip
, fs
->flow_mask
.src_port
,
284 fs
->flow_mask
.dst_ip
, fs
->flow_mask
.dst_port
);
285 if (fs
->rq_elements
== 0)
288 printf("BKT Prot ___Source IP/port____ "
289 "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n");
291 heapsort(q
, fs
->rq_elements
, sizeof(struct dn_flow_queue
), sort_q
);
293 /* Print IPv4 flows */
294 for (l
= 0; l
< fs
->rq_elements
; l
++) {
297 /* XXX: Should check for IPv4 flows */
298 if (IS_IP6_FLOW_ID(&(q
[l
].id
)))
301 if (!index_printed
) {
303 if (indexes
> 0) /* currently a no-op */
307 "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
309 fs
->flow_mask
.src_ip
, fs
->flow_mask
.src_port
,
310 fs
->flow_mask
.dst_ip
, fs
->flow_mask
.dst_port
);
312 printf("BKT Prot ___Source IP/port____ "
313 "____Dest. IP/port____ "
314 "Tot_pkt/bytes Pkt/Byte Drp\n");
317 printf("%3d ", q
[l
].hash_slot
);
318 pe
= getprotobynumber(q
[l
].id
.proto
);
320 printf("%-4s ", pe
->p_name
);
322 printf("%4u ", q
[l
].id
.proto
);
323 ina
.s_addr
= htonl(q
[l
].id
.src_ip
);
325 inet_ntoa(ina
), q
[l
].id
.src_port
);
326 ina
.s_addr
= htonl(q
[l
].id
.dst_ip
);
328 inet_ntoa(ina
), q
[l
].id
.dst_port
);
329 printf("%4llu %8llu %2u %4u %3u\n",
330 align_uint64(&q
[l
].tot_pkts
),
331 align_uint64(&q
[l
].tot_bytes
),
332 q
[l
].len
, q
[l
].len_bytes
, q
[l
].drops
);
334 printf(" S %20llu F %20llu\n",
335 align_uint64(&q
[l
].S
), align_uint64(&q
[l
].F
));
338 /* Print IPv6 flows */
340 for (l
= 0; l
< fs
->rq_elements
; l
++) {
341 if (!IS_IP6_FLOW_ID(&(q
[l
].id
)))
344 if (!index_printed
) {
349 printf("\n mask: proto: 0x%02x, flow_id: 0x%08x, ",
350 fs
->flow_mask
.proto
, fs
->flow_mask
.flow_id6
);
351 inet_ntop(AF_INET6
, &(fs
->flow_mask
.src_ip6
),
353 printf("%s/0x%04x -> ", buff
, fs
->flow_mask
.src_port
);
354 inet_ntop( AF_INET6
, &(fs
->flow_mask
.dst_ip6
),
355 buff
, sizeof(buff
) );
356 printf("%s/0x%04x\n", buff
, fs
->flow_mask
.dst_port
);
358 printf("BKT ___Prot___ _flow-id_ "
359 "______________Source IPv6/port_______________ "
360 "_______________Dest. IPv6/port_______________ "
361 "Tot_pkt/bytes Pkt/Byte Drp\n");
363 printf("%3d ", q
[l
].hash_slot
);
364 pe
= getprotobynumber(q
[l
].id
.proto
);
366 printf("%9s ", pe
->p_name
);
368 printf("%9u ", q
[l
].id
.proto
);
369 printf("%7d %39s/%-5d ", q
[l
].id
.flow_id6
,
370 inet_ntop(AF_INET6
, &(q
[l
].id
.src_ip6
), buff
, sizeof(buff
)),
372 printf(" %39s/%-5d ",
373 inet_ntop(AF_INET6
, &(q
[l
].id
.dst_ip6
), buff
, sizeof(buff
)),
375 printf(" %4llu %8llu %2u %4u %3u\n",
376 align_uint64(&q
[l
].tot_pkts
),
377 align_uint64(&q
[l
].tot_bytes
),
378 q
[l
].len
, q
[l
].len_bytes
, q
[l
].drops
);
380 printf(" S %20llu F %20llu\n",
381 align_uint64(&q
[l
].S
),
382 align_uint64(&q
[l
].F
));
387 print_flowset_parms(struct dn_flow_set
*fs
, char *prefix
)
392 char red
[90]; /* Display RED parameters */
395 if (fs
->flags_fs
& DN_QSIZE_IS_BYTES
) {
397 snprintf(qs
, sizeof(qs
), "%d KB", l
/ 1024);
399 snprintf(qs
, sizeof(qs
), "%d B", l
);
401 snprintf(qs
, sizeof(qs
), "%3d sl.", l
);
403 snprintf(plr
, sizeof(plr
), "plr %f", 1.0 * fs
->plr
/ (double)(0x7fffffff));
406 if (fs
->flags_fs
& DN_IS_RED
) /* RED parameters */
407 snprintf(red
, sizeof(red
),
408 "\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
409 (fs
->flags_fs
& DN_IS_GENTLE_RED
) ? 'G' : ' ',
410 1.0 * fs
->w_q
/ (double)(1 << SCALE_RED
),
411 SCALE_VAL(fs
->min_th
),
412 SCALE_VAL(fs
->max_th
),
413 1.0 * fs
->max_p
/ (double)(1 << SCALE_RED
));
415 snprintf(red
, sizeof(red
), "droptail");
417 printf("%s %s%s %d queues (%d buckets) %s\n",
418 prefix
, qs
, plr
, fs
->rq_elements
, fs
->rq_size
, red
);
422 list_pipes(void *data
, size_t nbytes
, int ac
, char *av
[])
424 unsigned int rulenum
;
426 struct dn_pipe
*p
= (struct dn_pipe
*) data
;
427 struct dn_flow_set
*fs
;
428 struct dn_flow_queue
*q
;
432 rulenum
= (unsigned int)strtoul(*av
++, NULL
, 10);
435 for (; nbytes
>= sizeof(struct dn_pipe
); p
= (struct dn_pipe
*)next
) {
436 double b
= p
->bandwidth
;
440 if (p
->next
.sle_next
!= (struct dn_pipe
*)DN_IS_PIPE
)
441 break; /* done with pipes, now queues */
444 * compute length, as pipe have variable size
446 l
= sizeof(struct dn_pipe
) + p
->fs
.rq_elements
* sizeof(struct dn_flow_queue
);
447 next
= (char *)p
+ l
;
450 if (rulenum
!= 0 && rulenum
!= p
->pipe_nr
)
454 * Print rate (or clocking interface)
456 if (p
->if_name
[0] != '\0')
457 snprintf(buf
, sizeof(buf
), "%s", p
->if_name
);
459 snprintf(buf
, sizeof(buf
), "unlimited");
460 else if (b
>= 1000000)
461 snprintf(buf
, sizeof(buf
), "%7.3f Mbit/s", b
/1000000);
463 snprintf(buf
, sizeof(buf
), "%7.3f Kbit/s", b
/1000);
465 snprintf(buf
, sizeof(buf
), "%7.3f bit/s ", b
);
467 snprintf(prefix
, sizeof(prefix
), "%05d: %s %4d ms ",
468 p
->pipe_nr
, buf
, p
->delay
);
469 print_flowset_parms(&(p
->fs
), prefix
);
471 printf(" V %20qd\n", p
->V
>> MY_M
);
473 q
= (struct dn_flow_queue
*)(p
+1);
474 list_queues(&(p
->fs
), q
);
476 for (fs
= next
; nbytes
>= sizeof *fs
; fs
= next
) {
479 if (fs
->next
.sle_next
!= (struct dn_flow_set
*)DN_IS_QUEUE
)
481 l
= sizeof(struct dn_flow_set
) + fs
->rq_elements
* sizeof(struct dn_flow_queue
);
482 next
= (char *)fs
+ l
;
484 q
= (struct dn_flow_queue
*)(fs
+1);
485 snprintf(prefix
, sizeof(prefix
), "q%05d: weight %d pipe %d ",
486 fs
->fs_nr
, fs
->weight
, fs
->parent_nr
);
487 print_flowset_parms(fs
, prefix
);
493 list(int ac
, char *av
[], int show_counters
)
499 int nalloc
= 1024; /* start somewhere... */
502 fprintf(stderr
, "Testing only, list disabled\n");
509 /* get rules or pipes from kernel, resizing array as necessary */
512 while (nbytes
>= nalloc
) {
513 nalloc
= nalloc
* 2 + 200;
515 if ((data
= realloc(data
, nbytes
)) == NULL
)
516 err(EX_OSERR
, "realloc");
518 if (do_cmd(IP_DUMMYNET_GET
, data
, &nbytes
) < 0) {
519 if (errno
== ENOBUFS
) {
523 err(EX_OSERR
, "getsockopt(IP_DUMMYNET_GET)");
528 list_pipes(data
, nbytes
, ac
, av
);
532 if (exitval
!= EX_OK
)
539 fprintf(stderr
, "usage: dnctl [options]\n"
540 "do \"dnctl -h\" or see dnctl manpage for details\n"
549 "dnclt [-acdeftTnNpqS] <command> where <command> is one of:\n"
550 "{pipe|queue} N config PIPE-BODY\n"
551 "[pipe|queue] {zero|delete|show} [N{,N}]\n"
557 delete(int ac
, char *av
[])
564 memset(&p
, 0, sizeof(struct dn_pipe
));
568 while (ac
&& isdigit(**av
)) {
569 i
= atoi(*av
); av
++; ac
--;
575 len
= sizeof(struct dn_pipe
);
576 i
= do_cmd(IP_DUMMYNET_DEL
, &p
, &len
);
579 warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
580 do_pipe
== 1 ? p
.pipe_nr
: p
.fs
.fs_nr
);
583 if (exitval
!= EX_OK
)
588 * the following macro returns an error message if we run out of
591 #define NEED1(msg) {if (!ac) errx(EX_USAGE, msg);}
592 #define NEED2(msg, arg) {if (!ac) errx(EX_USAGE, msg, arg);}
595 config_pipe(int ac
, char **av
)
603 memset(&p
, 0, sizeof(struct dn_pipe
));
607 if (ac
&& isdigit(**av
)) {
608 i
= atoi(*av
); av
++; ac
--;
616 int tok
= match_token(dummynet_params
, *av
);
621 p
.fs
.flags_fs
|= DN_NOERROR
;
625 NEED1("plr needs argument 0..1\n");
626 d
= strtod(av
[0], NULL
);
631 p
.fs
.plr
= (int)(d
*0x7fffffff);
636 NEED1("queue needs queue size\n");
638 p
.fs
.qsize
= (int)strtoul(av
[0], &end
, 0);
639 if (*end
== 'K' || *end
== 'k') {
640 p
.fs
.flags_fs
|= DN_QSIZE_IS_BYTES
;
642 } else if (*end
== 'B' || !strncmp(end
, "by", 2)) {
643 p
.fs
.flags_fs
|= DN_QSIZE_IS_BYTES
;
649 NEED1("buckets needs argument\n");
650 p
.fs
.rq_size
= (int)strtoul(av
[0], NULL
, 0);
655 NEED1("mask needs mask specifier\n");
657 * per-flow queue, mask is dst_ip, dst_port,
658 * src_ip, src_port, proto measured in bits
662 p
.fs
.flow_mask
.dst_ip
= 0;
663 p
.fs
.flow_mask
.src_ip
= 0;
664 p
.fs
.flow_mask
.dst_port
= 0;
665 p
.fs
.flow_mask
.src_port
= 0;
666 p
.fs
.flow_mask
.proto
= 0;
670 uint32_t *p32
= NULL
;
671 uint16_t *p16
= NULL
;
672 struct in6_addr
*pa6
= NULL
;
675 tok
= match_token(dummynet_params
, *av
);
680 * special case, all bits significant
682 p
.fs
.flow_mask
.dst_ip
= ~0;
683 p
.fs
.flow_mask
.src_ip
= ~0;
684 p
.fs
.flow_mask
.dst_port
= ~0;
685 p
.fs
.flow_mask
.src_port
= ~0;
686 p
.fs
.flow_mask
.proto
= ~0;
687 n2mask(&(p
.fs
.flow_mask
.dst_ip6
), 128);
688 n2mask(&(p
.fs
.flow_mask
.src_ip6
), 128);
689 p
.fs
.flags_fs
|= DN_HAVE_FLOW_MASK
;
693 p32
= &p
.fs
.flow_mask
.dst_ip
;
697 p32
= &p
.fs
.flow_mask
.src_ip
;
701 pa6
= &(p
.fs
.flow_mask
.dst_ip6
);
705 pa6
= &(p
.fs
.flow_mask
.src_ip6
);
709 p16
= &p
.fs
.flow_mask
.dst_port
;
713 p16
= &p
.fs
.flow_mask
.src_port
;
720 ac
++; av
--; /* backtrack */
724 errx(EX_USAGE
, "mask: value missing");
726 a
= (int)strtoul(av
[0]+1, &end
, 0);
728 a
= (a
== 32) ? ~0 : (1 << a
) - 1;
730 a
= (int)strtoul(av
[0], &end
, 0);
733 else if (p16
!= NULL
) {
736 "mask: must be 16 bit");
738 } else if (pa6
!= NULL
) {
741 "in6addr invalid mask len");
747 "mask: must be 8 bit");
748 p
.fs
.flow_mask
.proto
= (uint8_t)a
;
751 p
.fs
.flags_fs
|= DN_HAVE_FLOW_MASK
;
753 } /* end while, config masks */
759 NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
760 p
.fs
.flags_fs
|= DN_IS_RED
;
762 p
.fs
.flags_fs
|= DN_IS_GENTLE_RED
;
764 * the format for parameters is w_q/min_th/max_th/max_p
766 if ((end
= strsep(&av
[0], "/"))) {
767 double w_q
= strtod(end
, NULL
);
768 if (w_q
> 1 || w_q
<= 0)
769 errx(EX_DATAERR
, "0 < w_q <= 1");
770 p
.fs
.w_q
= (int) (w_q
* (1 << SCALE_RED
));
772 if ((end
= strsep(&av
[0], "/"))) {
773 p
.fs
.min_th
= (int)strtoul(end
, &end
, 0);
774 if (*end
== 'K' || *end
== 'k')
777 if ((end
= strsep(&av
[0], "/"))) {
778 p
.fs
.max_th
= (int)strtoul(end
, &end
, 0);
779 if (*end
== 'K' || *end
== 'k')
782 if ((end
= strsep(&av
[0], "/"))) {
783 double max_p
= strtod(end
, NULL
);
784 if (max_p
> 1 || max_p
<= 0)
785 errx(EX_DATAERR
, "0 < max_p <= 1");
786 p
.fs
.max_p
= (int)(max_p
* (1 << SCALE_RED
));
792 p
.fs
.flags_fs
&= ~(DN_IS_RED
|DN_IS_GENTLE_RED
);
796 NEED1("bw needs bandwidth or interface\n");
798 errx(EX_DATAERR
, "bandwidth only valid for pipes");
800 * set clocking interface or bandwidth value
802 if (av
[0][0] >= 'a' && av
[0][0] <= 'z') {
803 int l
= sizeof(p
.if_name
)-1;
805 strncpy(p
.if_name
, av
[0], l
);
810 p
.bandwidth
= (int)strtoul(av
[0], &end
, 0);
811 if (*end
== 'K' || *end
== 'k') {
814 } else if (*end
== 'M') {
816 p
.bandwidth
*= 1000000;
818 if (*end
== 'B' || !strncmp(end
, "by", 2))
821 errx(EX_DATAERR
, "bandwidth too large");
828 errx(EX_DATAERR
, "delay only valid for pipes");
829 NEED2("delay needs argument 0..%d\n", MAX_DELAY
);
830 p
.delay
= (int)strtoul(av
[0], NULL
, 0);
836 errx(EX_DATAERR
,"weight only valid for queues");
837 NEED1("weight needs argument 0..100\n");
838 p
.fs
.weight
= (int)strtoul(av
[0], &end
, 0);
844 errx(EX_DATAERR
,"pipe only valid for queues");
845 NEED1("pipe needs pipe_number\n");
846 p
.fs
.parent_nr
= strtoul(av
[0], &end
, 0);
851 errx(EX_DATAERR
, "unrecognised option ``%s''", *(--av
));
856 errx(EX_DATAERR
, "pipe_nr must be > 0");
857 if (p
.delay
> MAX_DELAY
)
858 errx(EX_DATAERR
, "delay must be < %d ms", MAX_DELAY
);
859 } else { /* do_pipe == 2, queue */
860 if (p
.fs
.parent_nr
== 0)
861 errx(EX_DATAERR
, "pipe must be > 0");
862 if (p
.fs
.weight
>100)
863 errx(EX_DATAERR
, "weight must be <= 100");
865 if (p
.fs
.flags_fs
& DN_QSIZE_IS_BYTES
) {
866 if (p
.fs
.qsize
> 1024*1024)
867 errx(EX_DATAERR
, "queue size must be < 1MB");
869 if (p
.fs
.qsize
> 100)
870 errx(EX_DATAERR
, "2 <= queue size <= 100");
872 if (p
.fs
.flags_fs
& DN_IS_RED
) {
874 int lookup_depth
, avg_pkt_size
;
875 double s
, idle
, weight
, w_q
;
879 if (p
.fs
.min_th
>= p
.fs
.max_th
)
880 errx(EX_DATAERR
, "min_th %d must be < than max_th %d",
881 p
.fs
.min_th
, p
.fs
.max_th
);
882 if (p
.fs
.max_th
== 0)
883 errx(EX_DATAERR
, "max_th must be > 0");
886 if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
887 &lookup_depth
, &len
, NULL
, 0) == -1)
889 errx(1, "sysctlbyname(\"%s\")",
890 "net.inet.ip.dummynet.red_lookup_depth");
891 if (lookup_depth
== 0)
892 errx(EX_DATAERR
, "net.inet.ip.dummynet.red_lookup_depth"
893 " must be greater than zero");
896 if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
897 &avg_pkt_size
, &len
, NULL
, 0) == -1)
899 errx(1, "sysctlbyname(\"%s\")",
900 "net.inet.ip.dummynet.red_avg_pkt_size");
901 if (avg_pkt_size
== 0)
903 "net.inet.ip.dummynet.red_avg_pkt_size must"
904 " be greater than zero");
906 len
= sizeof(struct clockinfo
);
907 if (sysctlbyname("kern.clockrate", &ck
, &len
, NULL
, 0) == -1)
908 errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
911 * Ticks needed for sending a medium-sized packet.
912 * Unfortunately, when we are configuring a WF2Q+ queue, we
913 * do not have bandwidth information, because that is stored
914 * in the parent pipe, and also we have multiple queues
915 * competing for it. So we set s=0, which is not very
916 * correct. But on the other hand, why do we want RED with
919 if (p
.bandwidth
==0) /* this is a WF2Q+ queue */
922 s
= ck
.hz
* avg_pkt_size
* 8 / p
.bandwidth
;
925 * max idle time (in ticks) before avg queue size becomes 0.
926 * NOTA: (3/w_q) is approx the value x so that
929 w_q
= ((double)p
.fs
.w_q
) / (1 << SCALE_RED
);
931 p
.fs
.lookup_step
= (int)idle
/ lookup_depth
;
932 if (!p
.fs
.lookup_step
)
933 p
.fs
.lookup_step
= 1;
935 for (t
= p
.fs
.lookup_step
; t
> 0; --t
)
937 p
.fs
.lookup_weight
= (int)(weight
* (1 << SCALE_RED
));
939 len
= sizeof(struct dn_pipe
);
940 i
= do_cmd(IP_DUMMYNET_CONFIGURE
, &p
, &len
);
942 err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
948 if (!force
&& !do_quiet
) { /* need to ask user */
951 printf("Are you sure? [yn] ");
954 c
= toupper(getc(stdin
));
955 while (c
!= '\n' && getc(stdin
) != '\n')
957 return; /* and do not flush */
958 } while (c
!= 'Y' && c
!= 'N');
960 if (c
== 'N') /* user said no */
964 if (do_cmd(IP_DUMMYNET_FLUSH
, NULL
, 0) < 0)
965 err(EX_UNAVAILABLE
, "setsockopt(IP_DUMMYNET_FLUSH)");
968 printf("Flushed all pipes.\n");
972 * Free a the (locally allocated) copy of command line arguments.
975 free_args(int ac
, char **av
)
979 for (i
=0; i
< ac
; i
++)
985 * Called with the arguments (excluding program name).
986 * Returns 0 if successful, 1 if empty command, errx() in case of errors.
989 parse_args(int oldac
, char **oldav
)
992 char **av
, **save_av
;
993 int do_acct
= 0; /* Show packet/byte count */
994 int do_force
= 0; /* Don't ask for confirmation */
996 #define WHITESP " \t\f\v\n\r"
999 else if (oldac
== 1) {
1001 * If we are called with a single string, try to split it into
1002 * arguments for subsequent parsing.
1003 * But first, remove spaces after a ',', by copying the string
1006 char *arg
= oldav
[0]; /* The string... */
1007 size_t l
= strlen(arg
);
1008 int copy
= 0; /* 1 if we need to copy, 0 otherwise */
1010 for (i
= j
= 0; i
< l
; i
++) {
1011 if (arg
[i
] == '#') /* comment marker */
1015 copy
= !index("," WHITESP
, arg
[i
]);
1017 copy
= !index(WHITESP
, arg
[i
]);
1022 if (!copy
&& j
> 0) /* last char was a 'blank', remove it */
1024 l
= j
; /* the new argument length */
1026 if (l
== 0) /* empty string! */
1030 * First, count number of arguments. Because of the previous
1031 * processing, this is just the number of blanks plus 1.
1033 for (i
= 0, ac
= 1; i
< l
; i
++)
1034 if (index(WHITESP
, arg
[i
]) != NULL
)
1037 av
= calloc(ac
, sizeof(char *));
1040 * Second, copy arguments from cmd[] to av[]. For each one,
1041 * j is the initial character, i is the one past the end.
1043 for (ac
= 0, i
= j
= 0; i
< l
; i
++)
1044 if (index(WHITESP
, arg
[i
]) != NULL
|| i
== l
-1) {
1047 av
[ac
] = calloc(i
-j
+1, 1);
1048 bcopy(arg
+j
, av
[ac
], i
-j
);
1054 * If an argument ends with ',' join with the next one.
1055 * Just add its length to 'l' and continue. When we have a string
1056 * without a ',' ending, we'll have the combined length in 'l'
1061 av
= calloc(oldac
, sizeof(char *));
1062 for (first
= i
= ac
= 0, l
= 0; i
< oldac
; i
++) {
1063 char *arg
= oldav
[i
];
1064 size_t k
= strlen(arg
);
1067 if (arg
[k
-1] != ',' || i
== oldac
-1) {
1068 size_t buflen
= l
+1;
1070 av
[ac
] = calloc(l
+1, 1);
1071 for (l
=0; first
<= i
; first
++) {
1072 strlcat(av
[ac
]+l
, oldav
[first
], buflen
-l
);
1073 l
+= strlen(oldav
[first
]);
1082 /* Set the force flag for non-interactive processes */
1083 do_force
= !isatty(STDIN_FILENO
);
1085 /* Save arguments for final freeing of memory. */
1089 optind
= optreset
= 0;
1090 while ((ch
= getopt(ac
, av
, "afhnqsv")) != -1)
1100 case 'h': /* help */
1101 free_args(save_ac
, save_av
);
1103 break; /* NOTREACHED */
1113 case 's': /* sort */
1114 do_sort
= atoi(optarg
);
1117 case 'v': /* verbose */
1122 free_args(save_ac
, save_av
);
1128 NEED1("bad arguments, for usage summary ``dnctl''");
1131 * An undocumented behaviour of dnctl1 was to allow rule numbers first,
1132 * e.g. "100 add allow ..." instead of "add 100 allow ...".
1133 * In case, swap first and second argument to get the normal form.
1135 if (ac
> 1 && isdigit(*av
[0])) {
1143 * optional: pipe or queue
1146 if (!strncmp(*av
, "pipe", strlen(*av
)))
1148 else if (!strncmp(*av
, "queue", strlen(*av
)))
1154 NEED1("missing command");
1157 * For pipes and queues we normally say 'pipe NN config'
1158 * but the code is easier to parse as 'pipe config NN'
1159 * so we swap the two arguments.
1161 if (do_pipe
> 0 && ac
> 1 && isdigit(*av
[0])) {
1168 if (do_pipe
&& !strncmp(*av
, "config", strlen(*av
)))
1169 config_pipe(ac
, av
);
1170 else if (!strncmp(*av
, "delete", strlen(*av
)))
1172 else if (!strncmp(*av
, "flush", strlen(*av
)))
1174 else if (!strncmp(*av
, "print", strlen(*av
)) ||
1175 !strncmp(*av
, "list", strlen(*av
)))
1176 list(ac
, av
, do_acct
);
1177 else if (!strncmp(*av
, "show", strlen(*av
)))
1178 list(ac
, av
, 1 /* show counters */);
1180 errx(EX_USAGE
, "bad command `%s'", *av
);
1182 /* Free memory allocated in the argument parsing. */
1183 free_args(save_ac
, save_av
);
1188 dnctl_readfile(int ac
, char *av
[])
1192 char *cmd
= NULL
, *filename
= av
[ac
-1];
1197 while ((c
= getopt(ac
, av
, "np:q")) != -1) {
1206 * Skip previous args and delete last one, so we
1207 * pass all but the last argument to the preprocessor
1213 fprintf(stderr
, "command is %s\n", av
[0]);
1221 errx(EX_USAGE
, "bad arguments, for usage"
1222 " summary ``dnctl''");
1229 if (cmd
== NULL
&& ac
!= optind
+ 1) {
1230 fprintf(stderr
, "ac %d, optind %d\n", ac
, optind
);
1231 errx(EX_USAGE
, "extraneous filename arguments");
1234 if ((f
= fopen(filename
, "r")) == NULL
)
1235 err(EX_UNAVAILABLE
, "fopen: %s", filename
);
1237 if (cmd
!= NULL
) { /* pipe through preprocessor */
1240 if (pipe(pipedes
) == -1)
1241 err(EX_OSERR
, "cannot create pipe");
1245 err(EX_OSERR
, "cannot fork");
1249 * Child, will run the preprocessor with the
1250 * file on stdin and the pipe on stdout.
1252 if (dup2(fileno(f
), 0) == -1
1253 || dup2(pipedes
[1], 1) == -1)
1254 err(EX_OSERR
, "dup2()");
1259 err(EX_OSERR
, "execvp(%s) failed", cmd
);
1260 } else { /* parent, will reopen f as the pipe */
1263 if ((f
= fdopen(pipedes
[0], "r")) == NULL
) {
1264 int savederrno
= errno
;
1266 (void)kill(preproc
, SIGTERM
);
1268 err(EX_OSERR
, "fdopen()");
1273 while (fgets(buf
, BUFSIZ
, f
)) { /* read commands */
1278 snprintf(linename
, sizeof(linename
), "Line %d", lineno
);
1279 setprogname(linename
); /* XXX */
1281 parse_args(1, args
);
1287 if (waitpid(preproc
, &status
, 0) == -1)
1288 errx(EX_OSERR
, "waitpid()");
1289 if (WIFEXITED(status
) && WEXITSTATUS(status
) != EX_OK
)
1290 errx(EX_UNAVAILABLE
,
1291 "preprocessor exited with status %d",
1292 WEXITSTATUS(status
));
1293 else if (WIFSIGNALED(status
))
1294 errx(EX_UNAVAILABLE
,
1295 "preprocessor exited with signal %d",
1301 main(int ac
, char *av
[])
1304 * If the last argument is an absolute pathname, interpret it
1305 * as a file to be preprocessed.
1308 if (ac
> 1 && av
[ac
- 1][0] == '/' && access(av
[ac
- 1], R_OK
) == 0)
1309 dnctl_readfile(ac
, av
);
1311 if (parse_args(ac
-1, av
+1))