diff options
author | Henning Brauer <henning@cvs.openbsd.org> | 2013-10-12 12:16:13 +0000 |
---|---|---|
committer | Henning Brauer <henning@cvs.openbsd.org> | 2013-10-12 12:16:13 +0000 |
commit | cb341636ad8d53a27a17eb035feb2940219ef542 (patch) | |
tree | 0f9f6af76997e8091780e28a92ec1f596803d03c /sbin | |
parent | cf075469382a1b2bbe60bc2cab89d834b6a892a8 (diff) |
config bits for the bandwidth shaping part of the new queueing subsystem
syntax worked out with many in ljubljana using a whiteboard, testing &
looking over by many, ok phessler sthen
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/pfctl/Makefile | 4 | ||||
-rw-r--r-- | sbin/pfctl/parse.y | 356 | ||||
-rw-r--r-- | sbin/pfctl/pfctl.c | 211 | ||||
-rw-r--r-- | sbin/pfctl/pfctl.h | 4 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_altq.c | 4 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.c | 56 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.h | 15 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_qstats.c | 28 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_queue.c | 237 |
9 files changed, 815 insertions, 100 deletions
diff --git a/sbin/pfctl/Makefile b/sbin/pfctl/Makefile index df74f88c63f..aee713b8afe 100644 --- a/sbin/pfctl/Makefile +++ b/sbin/pfctl/Makefile @@ -1,9 +1,9 @@ -# $OpenBSD: Makefile,v 1.19 2006/12/24 18:52:43 miod Exp $ +# $OpenBSD: Makefile,v 1.20 2013/10/12 12:16:10 henning Exp $ PROG= pfctl SRCS= pfctl.c parse.y pfctl_parser.c pf_print_state.c pfctl_altq.c SRCS+= pfctl_osfp.c pfctl_radix.c pfctl_table.c pfctl_qstats.c -SRCS+= pfctl_optimize.c pf_ruleset.c +SRCS+= pfctl_optimize.c pf_ruleset.c pfctl_queue.c CFLAGS+= -Wall -Wmissing-prototypes -Wno-uninitialized CFLAGS+= -Wstrict-prototypes -I${.CURDIR} YFLAGS= diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index dd8826655a5..8269af81963 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -1,10 +1,10 @@ -/* $OpenBSD: parse.y,v 1.624 2013/08/01 19:03:11 mikeb Exp $ */ +/* $OpenBSD: parse.y,v 1.625 2013/10/12 12:16:11 henning Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. * Copyright (c) 2001 Theo de Raadt. All rights reserved. - * Copyright (c) 2002,2003 Henning Brauer. All rights reserved. + * Copyright (c) 2002 - 2013 Henning Brauer <henning@openbsd.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,6 +37,7 @@ #include <netinet/ip_icmp.h> #include <netinet/icmp6.h> #include <net/pfvar.h> +#include <net/hfsc.h> #include <arpa/inet.h> #include <altq/altq.h> #include <altq/altq_cbq.h> @@ -185,7 +186,7 @@ struct node_queue { int scheduler; struct node_queue *next; struct node_queue *tail; -} *queues = NULL; +} *oqueues = NULL; struct node_qassign { char *qname; @@ -299,19 +300,39 @@ struct scrub_opts { int reassemble_tcp; } scrub_opts; +struct node_sc { + struct node_queue_bw m1; + u_int d; + struct node_queue_bw m2; +}; + struct queue_opts { + int marker; +#define QOM_BWSPEC 0x01 +#define QOM_PARENT 0x02 +#define QOM_DEFAULT 0x04 +#define QOM_QLIMIT 0x08 + struct node_sc realtime; + struct node_sc linkshare; + struct node_sc upperlimit; + char *parent; + int flags; + u_int qlimit; +} queue_opts; + +struct oldqueue_opts { int marker; -#define QOM_BWSPEC 0x01 -#define QOM_SCHEDULER 0x02 -#define QOM_PRIORITY 0x04 -#define QOM_TBRSIZE 0x08 -#define QOM_QLIMIT 0x10 +#define OQOM_BWSPEC 0x01 +#define OQOM_SCHEDULER 0x02 +#define OQOM_PRIORITY 0x04 +#define OQOM_TBRSIZE 0x08 +#define OQOM_QLIMIT 0x10 struct node_queue_bw queue_bwspec; struct node_queue_opt scheduler; int priority; int tbrsize; int qlimit; -} queue_opts; +} oldqueue_opts; struct table_opts { int flags; @@ -349,10 +370,11 @@ void expand_rule(struct pf_rule *, int, struct node_if *, struct node_host *, struct node_port *, struct node_uid *, struct node_gid *, struct node_if *, struct node_icmp *, const char *); +int expand_queue(char *, struct node_if *, struct queue_opts *); int expand_altq(struct pf_altq *, struct node_if *, struct node_queue *, struct node_queue_bw bwspec, struct node_queue_opt *); -int expand_queue(struct pf_altq *, struct node_if *, +int expand_oldqueue(struct pf_altq *, struct node_if *, struct node_queue *, struct node_queue_bw, struct node_queue_opt *); int expand_skip_interface(struct node_if *); @@ -424,9 +446,11 @@ typedef struct { struct node_queue_opt queue_options; struct node_queue_bw queue_bwspec; struct node_qassign qassign; + struct node_sc sc; struct filter_opts filter_opts; struct antispoof_opts antispoof_opts; struct queue_opts queue_opts; + struct oldqueue_opts oldqueue_opts; struct scrub_opts scrub_opts; struct table_opts table_opts; struct pool_opts pool_opts; @@ -457,8 +481,8 @@ int parseport(char *, struct range *r, int); %token BITMASK RANDOM SOURCEHASH ROUNDROBIN LEASTSTATES STATICPORT PROBABILITY %token WEIGHT %token ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT -%token QUEUE PRIORITY QLIMIT RTABLE RDOMAIN -%token LOAD RULESET_OPTIMIZATION RTABLE RDOMAIN PRIO ONCE +%token QUEUE OLDQUEUE PRIORITY QLIMIT RTABLE RDOMAIN MINIMUM BURST PARENT +%token LOAD RULESET_OPTIMIZATION RTABLE RDOMAIN PRIO ONCE DEFAULT %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW %token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE @@ -510,7 +534,9 @@ int parseport(char *, struct range *r, int); %type <v.filter_opts> filter_opts filter_opt filter_opts_l %type <v.filter_opts> filter_sets filter_set filter_sets_l %type <v.antispoof_opts> antispoof_opts antispoof_opt antispoof_opts_l -%type <v.queue_opts> queue_opts queue_opt queue_opts_l +%type <v.queue_opts> queue_opts queue_opt queue_opts_l optscs +%type <v.sc> scspec +%type <v.oldqueue_opts> oldqueue_opts oldqueue_opt oldqueue_opts_l %type <v.scrub_opts> scrub_opts scrub_opt scrub_opts_l %type <v.table_opts> table_opts table_opt table_opts_l %type <v.pool_opts> pool_opts pool_opt pool_opts_l @@ -523,8 +549,9 @@ ruleset : /* empty */ | ruleset pfrule '\n' | ruleset anchorrule '\n' | ruleset loadrule '\n' - | ruleset altqif '\n' | ruleset queuespec '\n' + | ruleset altqif '\n' + | ruleset oldqueuespec '\n' | ruleset varset '\n' | ruleset antispoof '\n' | ruleset tabledef '\n' @@ -1277,7 +1304,119 @@ table_host_list : tablespec optnl { $$ = $1; } } ; -altqif : ALTQ interface queue_opts QUEUE qassign { +queuespec : QUEUE STRING interface queue_opts { + if ($3 == NULL && $4.parent == NULL) { + yyerror("root queue without interface"); + YYERROR; + } + if ($2[0] == '_') { + yyerror("queue names must not start with _"); + YYERROR; + } + expand_queue($2, $3, &$4); + } + ; + +queue_opts : { + bzero(&queue_opts, sizeof queue_opts); + } + queue_opts_l + { $$ = queue_opts; } + ; + +queue_opts_l : queue_opts_l queue_opt + | queue_opt + ; + +queue_opt : BANDWIDTH scspec optscs { + if (queue_opts.marker & QOM_BWSPEC) { + yyerror("bandwidth cannot be respecified"); + YYERROR; + } + queue_opts.marker |= QOM_BWSPEC; + queue_opts.linkshare = $2; + queue_opts.realtime= $3.realtime; + queue_opts.upperlimit = $3.upperlimit; + } + | PARENT STRING { + if (queue_opts.marker & QOM_PARENT) { + yyerror("parent cannot be respecified"); + YYERROR; + } + queue_opts.marker |= QOM_PARENT; + queue_opts.parent = $2; + } + | DEFAULT { + if (queue_opts.marker & QOM_DEFAULT) { + yyerror("default cannot be respecified"); + YYERROR; + } + queue_opts.marker |= QOM_DEFAULT; + queue_opts.flags |= HFSC_DEFAULTCLASS; + } + | QLIMIT NUMBER { + if (queue_opts.marker & QOM_QLIMIT) { + yyerror("qlimit cannot be respecified"); + YYERROR; + } + if ($2 < 0 || $2 > 65535) { + yyerror("qlimit out of range: max 65535"); + YYERROR; + } + queue_opts.marker |= QOM_QLIMIT; + queue_opts.qlimit = $2; + } + ; + +optscs : /* nada */ { + + } + | comma MINIMUM scspec { + $$.realtime = $3; + } + | comma MAXIMUM scspec { + $$.upperlimit = $3; + } + | comma MINIMUM scspec comma MAXIMUM scspec { + $$.realtime = $3; + $$.upperlimit = $6; + } + | comma MAXIMUM scspec comma MINIMUM scspec { + $$.realtime = $6; + $$.upperlimit = $3; + } + ; + +scspec : bandwidth { + $$.m2 = $1; + $$.d = 0; + if ($$.m2.bw_percent) { + yyerror("no bandwidth in % yet"); + YYERROR; + } + } + | bandwidth BURST bandwidth FOR STRING { + u_long ul; + char *cp; + + ul = strtoul($5, &cp, 10); + if (cp == NULL || strcmp(cp, "ms")) { + yyerror("time in scspec must be in ms"); + YYERROR; + } + + $$.m1 = $3; + $$.d = ul; + $$.m2 = $1; + + if ($$.m1.bw_percent || $$.m2.bw_percent) { + yyerror("no bandwidth in % yet"); + YYERROR; + } + } + ; + +altqif : ALTQ interface oldqueue_opts QUEUE qassign { struct pf_altq a; memset(&a, 0, sizeof(a)); @@ -1298,7 +1437,7 @@ altqif : ALTQ interface queue_opts QUEUE qassign { } ; -queuespec : QUEUE STRING interface queue_opts qassign { +oldqueuespec : OLDQUEUE STRING interface oldqueue_opts qassign { struct pf_altq a; memset(&a, 0, sizeof(a)); @@ -1321,7 +1460,7 @@ queuespec : QUEUE STRING interface queue_opts qassign { a.priority = $4.priority; a.qlimit = $4.qlimit; a.scheduler = $4.scheduler.qtype; - if (expand_queue(&a, $3, $5, $4.queue_bwspec, + if (expand_oldqueue(&a, $3, $5, $4.queue_bwspec, &$4.scheduler)) { yyerror("errors in queue definition"); YYERROR; @@ -1329,39 +1468,39 @@ queuespec : QUEUE STRING interface queue_opts qassign { } ; -queue_opts : { - bzero(&queue_opts, sizeof queue_opts); - queue_opts.priority = DEFAULT_PRIORITY; - queue_opts.qlimit = DEFAULT_QLIMIT; - queue_opts.scheduler.qtype = ALTQT_NONE; - queue_opts.queue_bwspec.bw_percent = 100; +oldqueue_opts : { + bzero(&oldqueue_opts, sizeof oldqueue_opts); + oldqueue_opts.priority = DEFAULT_PRIORITY; + oldqueue_opts.qlimit = DEFAULT_QLIMIT; + oldqueue_opts.scheduler.qtype = ALTQT_NONE; + oldqueue_opts.queue_bwspec.bw_percent = 100; } - queue_opts_l - { $$ = queue_opts; } + oldqueue_opts_l + { $$ = oldqueue_opts; } | /* empty */ { - bzero(&queue_opts, sizeof queue_opts); - queue_opts.priority = DEFAULT_PRIORITY; - queue_opts.qlimit = DEFAULT_QLIMIT; - queue_opts.scheduler.qtype = ALTQT_NONE; - queue_opts.queue_bwspec.bw_percent = 100; - $$ = queue_opts; + bzero(&oldqueue_opts, sizeof oldqueue_opts); + oldqueue_opts.priority = DEFAULT_PRIORITY; + oldqueue_opts.qlimit = DEFAULT_QLIMIT; + oldqueue_opts.scheduler.qtype = ALTQT_NONE; + oldqueue_opts.queue_bwspec.bw_percent = 100; + $$ = oldqueue_opts; } ; -queue_opts_l : queue_opts_l queue_opt - | queue_opt +oldqueue_opts_l : oldqueue_opts_l oldqueue_opt + | oldqueue_opt ; -queue_opt : BANDWIDTH bandwidth { - if (queue_opts.marker & QOM_BWSPEC) { +oldqueue_opt : BANDWIDTH bandwidth { + if (oldqueue_opts.marker & OQOM_BWSPEC) { yyerror("bandwidth cannot be respecified"); YYERROR; } - queue_opts.marker |= QOM_BWSPEC; - queue_opts.queue_bwspec = $2; + oldqueue_opts.marker |= OQOM_BWSPEC; + oldqueue_opts.queue_bwspec = $2; } | PRIORITY NUMBER { - if (queue_opts.marker & QOM_PRIORITY) { + if (oldqueue_opts.marker & OQOM_PRIORITY) { yyerror("priority cannot be respecified"); YYERROR; } @@ -1369,11 +1508,11 @@ queue_opt : BANDWIDTH bandwidth { yyerror("priority out of range: max 255"); YYERROR; } - queue_opts.marker |= QOM_PRIORITY; - queue_opts.priority = $2; + oldqueue_opts.marker |= OQOM_PRIORITY; + oldqueue_opts.priority = $2; } | QLIMIT NUMBER { - if (queue_opts.marker & QOM_QLIMIT) { + if (oldqueue_opts.marker & OQOM_QLIMIT) { yyerror("qlimit cannot be respecified"); YYERROR; } @@ -1381,19 +1520,19 @@ queue_opt : BANDWIDTH bandwidth { yyerror("qlimit out of range: max 65535"); YYERROR; } - queue_opts.marker |= QOM_QLIMIT; - queue_opts.qlimit = $2; + oldqueue_opts.marker |= OQOM_QLIMIT; + oldqueue_opts.qlimit = $2; } | scheduler { - if (queue_opts.marker & QOM_SCHEDULER) { + if (oldqueue_opts.marker & OQOM_SCHEDULER) { yyerror("scheduler cannot be respecified"); YYERROR; } - queue_opts.marker |= QOM_SCHEDULER; - queue_opts.scheduler = $1; + oldqueue_opts.marker |= OQOM_SCHEDULER; + oldqueue_opts.scheduler = $1; } | TBRSIZE NUMBER { - if (queue_opts.marker & QOM_TBRSIZE) { + if (oldqueue_opts.marker & OQOM_TBRSIZE) { yyerror("tbrsize cannot be respecified"); YYERROR; } @@ -1401,8 +1540,8 @@ queue_opt : BANDWIDTH bandwidth { yyerror("tbrsize too big: max 65535"); YYERROR; } - queue_opts.marker |= QOM_TBRSIZE; - queue_opts.tbrsize = $2; + oldqueue_opts.marker |= OQOM_TBRSIZE; + oldqueue_opts.tbrsize = $2; } ; @@ -1414,13 +1553,22 @@ bandwidth : STRING { bps = strtod($1, &cp); if (cp != NULL) { + if (strlen(cp) > 1) { + char *cu = cp + 1; + if (!strcmp(cu, "Bit") || + !strcmp(cu, "B") || + !strcmp(cu, "bit") || + !strcmp(cu, "b")) { + *cu = 0; + } + } if (!strcmp(cp, "b")) ; /* nothing */ - else if (!strcmp(cp, "Kb")) + else if (!strcmp(cp, "K")) bps *= 1000; - else if (!strcmp(cp, "Mb")) + else if (!strcmp(cp, "M")) bps *= 1000 * 1000; - else if (!strcmp(cp, "Gb")) + else if (!strcmp(cp, "G")) bps *= 1000 * 1000 * 1000; else if (!strcmp(cp, "%")) { if (bps < 0 || bps > 100) { @@ -1432,7 +1580,7 @@ bandwidth : STRING { $$.bw_percent = bps; bps = 0; } else { - yyerror("unknown unit %s", cp); + yyerror("unknown unit \"%s\"", cp); free($1); YYERROR; } @@ -1481,10 +1629,11 @@ cbqflags_list : cbqflags_item { $$ |= $1; } | cbqflags_list comma cbqflags_item { $$ |= $3; } ; -cbqflags_item : STRING { - if (!strcmp($1, "default")) - $$ = CBQCLF_DEFCLASS; - else if (!strcmp($1, "borrow")) +cbqflags_item : DEFAULT { + $$ = CBQCLF_DEFCLASS; + } + | STRING { + if (!strcmp($1, "borrow")) $$ = CBQCLF_BORROW; else if (!strcmp($1, "red")) $$ = CBQCLF_RED; @@ -1503,10 +1652,11 @@ priqflags_list : priqflags_item { $$ |= $1; } | priqflags_list comma priqflags_item { $$ |= $3; } ; -priqflags_item : STRING { - if (!strcmp($1, "default")) - $$ = PRCF_DEFAULTCLASS; - else if (!strcmp($1, "red")) +priqflags_item : DEFAULT { + $$ = PRCF_DEFAULTCLASS; + } + | STRING { + if (!strcmp($1, "red")) $$ = PRCF_RED; else if (!strcmp($1, "ecn")) $$ = PRCF_RED|PRCF_ECN; @@ -1601,10 +1751,11 @@ hfscopts_item : LINKSHARE bandwidth { hfsc_opts.upperlimit.m2 = $7; hfsc_opts.upperlimit.used = 1; } + | DEFAULT { + hfsc_opts.flags |= HFCF_DEFAULTCLASS; + } | STRING { - if (!strcmp($1, "default")) - hfsc_opts.flags |= HFCF_DEFAULTCLASS; - else if (!strcmp($1, "red")) + if (!strcmp($1, "red")) hfsc_opts.flags |= HFCF_RED; else if (!strcmp($1, "ecn")) hfsc_opts.flags |= HFCF_RED|HFCF_ECN; @@ -4526,11 +4677,11 @@ expand_altq(struct pf_altq *a, struct node_if *interfaces, n->scheduler = pa.scheduler; n->next = NULL; n->tail = n; - if (queues == NULL) - queues = n; + if (oqueues == NULL) + oqueues = n; else { - queues->tail->next = n; - queues->tail = n; + oqueues->tail->next = n; + oqueues->tail = n; } ); } @@ -4542,7 +4693,57 @@ expand_altq(struct pf_altq *a, struct node_if *interfaces, } int -expand_queue(struct pf_altq *a, struct node_if *interfaces, +expand_queue(char *qname, struct node_if *interfaces, struct queue_opts *opts) +{ + struct pf_queuespec qspec; + + LOOP_THROUGH(struct node_if, interface, interfaces, + bzero(&qspec, sizeof(qspec)); + if (strlcpy(qspec.qname, qname, sizeof(qspec.qname)) >= + sizeof(qspec.qname)) { + yyerror("queuename too long"); + return (1); + } + if (opts->parent && strlcpy(qspec.parent, opts->parent, + sizeof(qspec.parent)) >= sizeof(qspec.parent)) { + yyerror("parent too long"); + return (1); + } + if (strlcpy(qspec.ifname, interface->ifname, + sizeof(qspec.ifname)) >= sizeof(qspec.ifname)) { + yyerror("interface too long"); + return (1); + } + qspec.realtime.m1.absolute = opts->realtime.m1.bw_absolute; + qspec.realtime.m1.percent = opts->realtime.m1.bw_percent; + qspec.realtime.m2.absolute = opts->realtime.m2.bw_absolute; + qspec.realtime.m2.percent = opts->realtime.m2.bw_percent; + qspec.realtime.d = opts->realtime.d; + + qspec.linkshare.m1.absolute = opts->linkshare.m1.bw_absolute; + qspec.linkshare.m1.percent = opts->linkshare.m1.bw_percent; + qspec.linkshare.m2.absolute = opts->linkshare.m2.bw_absolute; + qspec.linkshare.m2.percent = opts->linkshare.m2.bw_percent; + qspec.linkshare.d = opts->linkshare.d; + + qspec.upperlimit.m1.absolute = opts->upperlimit.m1.bw_absolute; + qspec.upperlimit.m1.percent = opts->upperlimit.m1.bw_percent; + qspec.upperlimit.m2.absolute = opts->upperlimit.m2.bw_absolute; + qspec.upperlimit.m2.percent = opts->upperlimit.m2.bw_percent; + qspec.upperlimit.d = opts->upperlimit.d; + + qspec.flags = opts->flags; + qspec.qlimit = opts->qlimit; + + pfctl_add_queue(pf, &qspec); + ); + + FREE_LIST(struct node_if, interfaces); + return (0); +} + +int +expand_oldqueue(struct pf_altq *a, struct node_if *interfaces, struct node_queue *nqueues, struct node_queue_bw bwspec, struct node_queue_opt *opts) { @@ -4551,14 +4752,14 @@ expand_queue(struct pf_altq *a, struct node_if *interfaces, u_int8_t found = 0; u_int8_t errs = 0; - if (queues == NULL) { + if (oqueues == NULL) { yyerror("queue %s has no parent", a->qname); FREE_LIST(struct node_queue, nqueues); return (1); } LOOP_THROUGH(struct node_if, interface, interfaces, - LOOP_THROUGH(struct node_queue, tqueue, queues, + LOOP_THROUGH(struct node_queue, tqueue, oqueues, if (!strncmp(a->qname, tqueue->queue, PF_QNAME_SIZE) && (interface->ifname[0] == 0 || (!interface->not && !strncmp(interface->ifname, @@ -4636,11 +4837,11 @@ expand_queue(struct pf_altq *a, struct node_if *interfaces, n->scheduler = tqueue->scheduler; n->next = NULL; n->tail = n; - if (queues == NULL) - queues = n; + if (oqueues == NULL) + oqueues = n; else { - queues->tail->next = n; - queues->tail = n; + oqueues->tail->next = n; + oqueues->tail = n; } } if ((pf->opts & PF_OPT_VERBOSE) && ( @@ -5199,9 +5400,11 @@ lookup(char *s) { "bitmask", BITMASK}, { "block", BLOCK}, { "block-policy", BLOCKPOLICY}, + { "burst", BURST}, { "cbq", CBQ}, { "code", CODE}, { "debug", DEBUG}, + { "default", DEFAULT}, { "divert-packet", DIVERTPACKET}, { "divert-reply", DIVERTREPLY}, { "divert-to", DIVERTTO}, @@ -5242,6 +5445,7 @@ lookup(char *s) { "max-src-conn-rate", MAXSRCCONNRATE}, { "max-src-nodes", MAXSRCNODES}, { "max-src-states", MAXSRCSTATES}, + { "min", MINIMUM}, { "min-ttl", MINTTL}, { "modulate", MODULATE}, { "nat-to", NATTO}, @@ -5249,12 +5453,14 @@ lookup(char *s) { "no-df", NODF}, { "no-route", NOROUTE}, { "no-sync", NOSYNC}, + { "oldqueue", OLDQUEUE}, { "on", ON}, { "once", ONCE}, { "optimization", OPTIMIZATION}, { "os", OS}, { "out", OUT}, { "overload", OVERLOAD}, + { "parent", PARENT}, { "pass", PASS}, { "pflow", PFLOW}, { "port", PORT}, diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c index 2fc6910b78a..9fb5cb58e16 100644 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -1,8 +1,8 @@ -/* $OpenBSD: pfctl.c,v 1.318 2013/10/09 02:59:27 lteo Exp $ */ +/* $OpenBSD: pfctl.c,v 1.319 2013/10/12 12:16:11 henning Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier - * Copyright (c) 2002,2003 Henning Brauer + * Copyright (c) 2002 - 2013 Henning Brauer <henning@openbsd.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -92,6 +92,11 @@ void pfctl_debug(int, u_int32_t, int); int pfctl_test_altqsupport(int, int); int pfctl_show_anchors(int, int, char *); int pfctl_ruleset_trans(struct pfctl *, char *, struct pf_anchor *); +u_int pfctl_find_childqs(struct pfctl_qsitem *); +void pfctl_load_queue(struct pfctl *, u_int32_t, struct pfctl_qsitem *); +int pfctl_load_queues(struct pfctl *); +u_int pfctl_leafqueue_check(char *); +u_int pfctl_check_qassignments(struct pf_ruleset *); int pfctl_load_ruleset(struct pfctl *, char *, struct pf_ruleset *, int); int pfctl_load_rule(struct pfctl *, char *, struct pf_rule *, int); const char *pfctl_lookup_option(char *, const char **); @@ -339,7 +344,6 @@ int pfctl_clear_altq(int dev, int opts) { struct pfr_buffer t; - if (!altqsupport) return (-1); memset(&t, 0, sizeof(t)); @@ -1096,6 +1100,196 @@ pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pf_anchor *a) return (0); } +TAILQ_HEAD(qspecs, pfctl_qsitem) qspecs = TAILQ_HEAD_INITIALIZER(qspecs); +TAILQ_HEAD(rootqs, pfctl_qsitem) rootqs = TAILQ_HEAD_INITIALIZER(rootqs); + +int +pfctl_add_queue(struct pfctl *pf, struct pf_queuespec *q) +{ + struct pfctl_qsitem *qi; + + if ((qi = calloc(1, sizeof(*qi))) == NULL) + err(1, "calloc"); + bcopy(q, &qi->qs, sizeof(qi->qs)); + TAILQ_INIT(&qi->children); + + if (qi->qs.parent[0]) + TAILQ_INSERT_TAIL(&qspecs, qi, entries); + else + TAILQ_INSERT_TAIL(&rootqs, qi, entries); + + return (0); +} + +u_int +pfctl_find_childqs(struct pfctl_qsitem *qi) +{ + struct pfctl_qsitem *n, *p, *q; + u_int flags = qi->qs.flags; + + TAILQ_FOREACH(p, &qspecs, entries) { + if (strcmp(p->qs.parent, qi->qs.qname)) + continue; + if (p->qs.ifname[0] && strcmp(p->qs.ifname, qi->qs.ifname)) + continue; + if (++p->matches > 10000) + errx(1, "pfctl_find_childqs: excessive matches, loop?"); + + /* check wether a children with that name is already there */ + TAILQ_FOREACH(q, &qi->children, entries) + if (!strcmp(q->qs.qname, p->qs.qname)) + break; + if (q == NULL) { + /* insert */ + if ((n = calloc(1, sizeof(*n))) == NULL) + err(1, "calloc"); + TAILQ_INIT(&n->children); + bcopy(&p->qs, &n->qs, sizeof(n->qs)); + TAILQ_INSERT_TAIL(&qi->children, n, entries); + } else { + if ((q->qs.ifname[0] && p->qs.ifname[0])) + errx(1, "queue %s on %s respecified", + q->qs.qname, q->qs.ifname); + if (!q->qs.ifname[0] && !p->qs.ifname[0]) + errx(1, "queue %s respecified", + q->qs.qname); + /* ifbound beats floating */ + if (!q->qs.ifname[0]) + bcopy(&p->qs, &q->qs, sizeof(q->qs)); + } + } + + TAILQ_FOREACH(p, &qi->children, entries) + flags |= pfctl_find_childqs(p); + + if (qi->qs.flags & HFSC_DEFAULTCLASS && !TAILQ_EMPTY(&qi->children)) + errx(1, "default queue %s is not a leaf queue", qi->qs.qname); + + return (flags); +} + +void +pfctl_load_queue(struct pfctl *pf, u_int32_t ticket, struct pfctl_qsitem *qi) +{ + struct pfioc_queue q; + struct pfctl_qsitem *p; + + q.ticket = ticket; + bcopy(&qi->qs, &q.queue, sizeof(q.queue)); + if ((pf->opts & PF_OPT_NOACTION) == 0) + if (ioctl(pf->dev, DIOCADDQUEUE, &q)) + err(1, "DIOCADDQUEUE"); + if (pf->opts & PF_OPT_VERBOSE) + print_queuespec(&qi->qs); + while ((p = TAILQ_FIRST(&qi->children)) != NULL) { + TAILQ_REMOVE(&qi->children, p, entries); + strlcpy(p->qs.ifname, qi->qs.ifname, IFNAMSIZ); + pfctl_load_queue(pf, ticket, p); + free(p); + } +} + +int +pfctl_load_queues(struct pfctl *pf) +{ + struct pfctl_qsitem *qi, rqi; + u_int32_t ticket; + + while ((qi = TAILQ_FIRST(&qspecs)) != NULL) { + if (qi->matches == 0) + errx(1, "queue %s: parent %s not found\n", qi->qs.qname, + qi->qs.parent); + if (qi->qs.realtime.m1.percent || qi->qs.realtime.m2.percent || + qi->qs.linkshare.m1.percent || + qi->qs.linkshare.m2.percent || + qi->qs.upperlimit.m1.percent || + qi->qs.upperlimit.m2.percent) + errx(1, "only absolute bandwidth specs for now"); + + TAILQ_REMOVE(&qspecs, qi, entries); + free(qi); + } + + if ((pf->opts & PF_OPT_NOACTION) == 0) + ticket = pfctl_get_ticket(pf->trans, PF_TRANS_RULESET, ""); + while ((qi = TAILQ_FIRST(&rootqs)) != NULL) { + TAILQ_REMOVE(&rootqs, qi, entries); + + /* + * We must have a hidden root queue below the user- + * specified/visible root queue, due to the way the + * dequeueing works far down there... don't ask. + * the _ namespace is reserved for these. + */ + bzero(&rqi, sizeof(rqi)); + TAILQ_INIT(&rqi.children); + TAILQ_INSERT_TAIL(&rqi.children, qi, entries); + snprintf(rqi.qs.qname, PF_QNAME_SIZE, "_root_%s", + qi->qs.ifname); + strlcpy(rqi.qs.ifname, qi->qs.ifname, sizeof(rqi.qs.ifname)); + strlcpy(qi->qs.parent, rqi.qs.qname, sizeof(qi->qs.parent)); + + pfctl_load_queue(pf, ticket, &rqi); + + } + + return (0); +} + +u_int +pfctl_leafqueue_check(char *qname) +{ + struct pfctl_qsitem *qi; + if (qname == NULL || qname[0] == 0) + return (0); + + TAILQ_FOREACH(qi, &rootqs, entries) { + if (strcmp(qname, qi->qs.qname)) + continue; + if (!TAILQ_EMPTY(&qi->children)) { + printf("queue %s: packets must be assigned to leaf " + "queues only\n", qname); + return (1); + } + } + TAILQ_FOREACH(qi, &qspecs, entries) { + if (strcmp(qname, qi->qs.qname)) + continue; + if (!TAILQ_EMPTY(&qi->children)) { + printf("queue %s: packets must be assigned to leaf " + "queues only\n", qname); + return (1); + } + } + return (0); +} + +u_int +pfctl_check_qassignments(struct pf_ruleset *rs) +{ + struct pf_rule *r; + struct pfctl_qsitem *qi; + u_int flags, errs = 0; + + /* main ruleset: need find_childqs to populate qi->children */ + if (rs->anchor->path[0] == 0) { + TAILQ_FOREACH(qi, &rootqs, entries) { + flags = pfctl_find_childqs(qi); + if (!(flags & HFSC_DEFAULTCLASS)) + errx(1, "no default queue specified"); + } + } + + TAILQ_FOREACH(r, rs->rules.active.ptr, entries) { + if (r->anchor) + errs += pfctl_check_qassignments(&r->anchor->ruleset); + if (pfctl_leafqueue_check(r->qname) || + pfctl_leafqueue_check(r->pqname)) + errs++; + } + return (errs); +} + int pfctl_load_ruleset(struct pfctl *pf, char *path, struct pf_ruleset *rs, int depth) @@ -1309,6 +1503,14 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, goto _error; } + if (pfctl_check_qassignments(&pf.anchor->ruleset) || + pfctl_load_queues(&pf)) { + if ((opts & PF_OPT_NOACTION) == 0) + ERRX("Unable to load queues into kernel"); + else + goto _error; + } + if (pfctl_load_ruleset(&pf, path, rs, 0)) { if ((opts & PF_OPT_NOACTION) == 0) ERRX("Unable to load rules into kernel"); @@ -1747,6 +1949,7 @@ pfctl_debug(int dev, u_int32_t level, int opts) memset(&t, 0, sizeof(t)); t.pfrb_type = PFRB_TRANS; +printf("pfctl_debug DIOCXCOMMIT\n"); if (pfctl_trans(dev, &t, DIOCXBEGIN, 0) || ioctl(dev, DIOCSETDEBUG, &level) || pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) @@ -2119,6 +2322,8 @@ main(int argc, char *argv[]) anchorname, 0, anchor_wildcard, shownr); break; case 'q': + pfctl_show_queues(dev, ifaceopt, opts, + opts & PF_OPT_VERBOSE2); pfctl_show_altq(dev, ifaceopt, opts, opts & PF_OPT_VERBOSE2); break; diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h index 0b99cb56b4f..828609face7 100644 --- a/sbin/pfctl/pfctl.h +++ b/sbin/pfctl/pfctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pfctl.h,v 1.50 2012/07/08 17:48:37 lteo Exp $ */ +/* $OpenBSD: pfctl.h,v 1.51 2013/10/12 12:16:11 henning Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -117,4 +117,6 @@ u_int32_t pfctl_get_ticket(struct pfr_buffer *, int, const char *); int pfctl_trans(int, struct pfr_buffer *, u_long, int); +int pfctl_show_queues(int, const char *, int, int); + #endif /* _PFCTL_H_ */ diff --git a/sbin/pfctl/pfctl_altq.c b/sbin/pfctl/pfctl_altq.c index ce3b860de50..e3d3f341fb1 100644 --- a/sbin/pfctl/pfctl_altq.c +++ b/sbin/pfctl/pfctl_altq.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfctl_altq.c,v 1.99 2013/03/20 00:18:00 deraadt Exp $ */ +/* $OpenBSD: pfctl_altq.c,v 1.100 2013/10/12 12:16:11 henning Exp $ */ /* * Copyright (c) 2002 @@ -181,7 +181,7 @@ print_queue(const struct pf_altq *a, unsigned int level, { unsigned int i; - printf("queue "); + printf("oldqueue "); for (i = 0; i < level; ++i) printf(" "); printf("%s ", a->qname); diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index 4972612677c..91267746272 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -1,8 +1,8 @@ -/* $OpenBSD: pfctl_parser.c,v 1.294 2013/08/01 19:03:11 mikeb Exp $ */ +/* $OpenBSD: pfctl_parser.c,v 1.295 2013/10/12 12:16:12 henning Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier - * Copyright (c) 2002,2003 Henning Brauer + * Copyright (c) 2002 - 2013 Henning Brauer <henning@openbsd.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,6 +42,7 @@ #include <netinet/ip_icmp.h> #include <netinet/icmp6.h> #include <net/pfvar.h> +#include <net/hfsc.h> #include <arpa/inet.h> #include <stdio.h> @@ -67,6 +68,8 @@ void print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned); void print_flags (u_int8_t); void print_fromto(struct pf_rule_addr *, pf_osfp_t, struct pf_rule_addr *, u_int8_t, u_int8_t, int); +void print_bwspec(const char *index, struct pf_queue_bwspec *); +void print_scspec(const char *, struct pf_queue_scspec *); int ifa_skip_if(const char *filter, struct node_host *p); struct node_host *ifa_grouplookup(const char *, int); @@ -1147,6 +1150,55 @@ print_tabledef(const char *name, int flags, int addrs, printf("\n"); } +void +print_bwspec(const char *prefix, struct pf_queue_bwspec *bw) +{ + u_int rate; + int i; + static const char unit[] = " KMG"; + + if (bw->percent) + printf("%s%u%%", prefix, bw->percent); + else if (bw->absolute) { + rate = bw->absolute; + for (i = 0; rate >= 1000 && i <= 3; i++) + rate /= 1000; + printf("%s%u%c", prefix, rate, unit[i]); + } +} + +void +print_scspec(const char *prefix, struct pf_queue_scspec *sc) +{ + print_bwspec(prefix, &sc->m2); + if (sc->d) { + printf(" burst "); + print_bwspec("", &sc->m1); + printf(" for %ums", sc->d); + } +} + +void +print_queuespec(struct pf_queuespec *q) +{ + /* hide the _root_ifname queues */ + if (q->qname[0] == '_') + return; + printf("queue %s", q->qname); + if (q->parent[0] && q->parent[0] != '_') + printf(" parent %s", q->parent); + if (q->ifname[0]) + printf(" on %s", q->ifname); + print_scspec(" bandwidth ", &q->linkshare); + print_scspec(", min ", &q->realtime); + print_scspec(", max ", &q->upperlimit); + if (q->flags & HFSC_DEFAULTCLASS) + printf(" default"); + if (q->qlimit) + printf(" qlimit %u", q->qlimit); + printf("\n"); +} + int parse_flags(char *s) { diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h index 7dff2e74ae0..1bd2a649e96 100644 --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -1,7 +1,8 @@ -/* $OpenBSD: pfctl_parser.h,v 1.100 2013/08/01 19:03:11 mikeb Exp $ */ +/* $OpenBSD: pfctl_parser.h,v 1.101 2013/10/12 12:16:12 henning Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier + * Copyright (c) 2002 - 2013 Henning Brauer <henning@openbsd.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -217,11 +218,15 @@ int parse_config(char *, struct pfctl *); int parse_flags(char *); int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *); +int pfctl_load_queues(struct pfctl *); +int pfctl_add_queue(struct pfctl *, struct pf_queuespec *); + void print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int, int); void print_src_node(struct pf_src_node *, int); void print_rule(struct pf_rule *, const char *, int); void print_tabledef(const char *, int, int, struct node_tinithead *); void print_status(struct pf_status *, int); +void print_queuespec(struct pf_queuespec *); int eval_pfaltq(struct pfctl *, struct pf_altq *, struct node_queue_bw *, struct node_queue_opt *); @@ -284,4 +289,12 @@ int append_addr(struct pfr_buffer *, char *, int); int append_addr_host(struct pfr_buffer *, struct node_host *, int, int); +TAILQ_HEAD(pf_qihead, pfctl_qsitem); +struct pfctl_qsitem { + TAILQ_ENTRY(pfctl_qsitem) entries; + struct pf_queuespec qs; + struct pf_qihead children; + int matches; +}; + #endif /* _PFCTL_PARSER_H_ */ diff --git a/sbin/pfctl/pfctl_qstats.c b/sbin/pfctl/pfctl_qstats.c index d4139b07d4b..c2cf2b6e868 100644 --- a/sbin/pfctl/pfctl_qstats.c +++ b/sbin/pfctl/pfctl_qstats.c @@ -1,7 +1,7 @@ -/* $OpenBSD: pfctl_qstats.c,v 1.32 2011/07/04 22:49:03 henning Exp $ */ +/* $OpenBSD: pfctl_qstats.c,v 1.33 2013/10/12 12:16:12 henning Exp $ */ /* - * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> + * Copyright (c) 2003 - 2013 Henning Brauer <henning@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -64,7 +64,7 @@ struct pf_altq_node { struct queue_stats qstats; }; -int pfctl_update_qstats(int, struct pf_altq_node **); +int pfctl_update_altqstats(int, struct pf_altq_node **); void pfctl_insert_altq_node(struct pf_altq_node **, const struct pf_altq, const struct queue_stats); struct pf_altq_node *pfctl_find_altq_node(struct pf_altq_node *, @@ -78,7 +78,7 @@ void pfctl_free_altq_node(struct pf_altq_node *); void pfctl_print_altq_nodestat(int, const struct pf_altq_node *); -void update_avg(struct pf_altq_node *); +void altq_update_avg(struct pf_altq_node *); int pfctl_show_altq(int dev, const char *iface, int opts, int verbose2) @@ -87,11 +87,11 @@ pfctl_show_altq(int dev, const char *iface, int opts, int verbose2) int nodes, dotitle = (opts & PF_OPT_SHOWALL); - if ((nodes = pfctl_update_qstats(dev, &root)) < 0) + if ((nodes = pfctl_update_altqstats(dev, &root)) < 0) return (-1); if (nodes == 0) - printf("No queue in use\n"); + printf("No altq queue in use\n"); for (node = root; node != NULL; node = node->next) { if (iface != NULL && strcmp(node->altq.ifname, iface)) continue; @@ -106,7 +106,7 @@ pfctl_show_altq(int dev, const char *iface, int opts, int verbose2) printf("\n"); fflush(stdout); sleep(STAT_INTERVAL); - if ((nodes = pfctl_update_qstats(dev, &root)) == -1) + if ((nodes = pfctl_update_altqstats(dev, &root)) == -1) return (-1); for (node = root; node != NULL; node = node->next) { if (iface != NULL && strcmp(node->altq.ifname, iface)) @@ -119,11 +119,11 @@ pfctl_show_altq(int dev, const char *iface, int opts, int verbose2) } int -pfctl_update_qstats(int dev, struct pf_altq_node **root) +pfctl_update_altqstats(int dev, struct pf_altq_node **root) { struct pf_altq_node *node; struct pfioc_altq pa; - struct pfioc_qstats pq; + struct pfioc_altqstats pq; u_int32_t mnr, nr; struct queue_stats qstats; static u_int32_t last_ticket; @@ -155,15 +155,15 @@ pfctl_update_qstats(int dev, struct pf_altq_node **root) pq.ticket = pa.ticket; pq.buf = &qstats.data; pq.nbytes = sizeof(qstats.data); - if (ioctl(dev, DIOCGETQSTATS, &pq)) { - warn("DIOCGETQSTATS"); + if (ioctl(dev, DIOCGETALTQSTATS, &pq)) { + warn("DIOCGETALTQSTATS"); return (-1); } if ((node = pfctl_find_altq_node(*root, pa.altq.qname, pa.altq.ifname)) != NULL) { memcpy(&node->qstats.data, &qstats.data, sizeof(qstats.data)); - update_avg(node); + altq_update_avg(node); } else { pfctl_insert_altq_node(root, pa.altq, qstats); } @@ -209,7 +209,7 @@ pfctl_insert_altq_node(struct pf_altq_node **root, prev->next = node; } } - update_avg(node); + altq_update_avg(node); } struct pf_altq_node * @@ -363,7 +363,7 @@ pfctl_free_altq_node(struct pf_altq_node *node) } void -update_avg(struct pf_altq_node *a) +altq_update_avg(struct pf_altq_node *a) { struct queue_stats *qs; u_int64_t b, p; diff --git a/sbin/pfctl/pfctl_queue.c b/sbin/pfctl/pfctl_queue.c new file mode 100644 index 00000000000..5d11e61a22d --- /dev/null +++ b/sbin/pfctl/pfctl_queue.c @@ -0,0 +1,237 @@ +/* $OpenBSD: pfctl_queue.c,v 1.1 2013/10/12 12:16:12 henning Exp $ */ + +/* + * Copyright (c) 2003 - 2013 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <net/pfvar.h> +#include <arpa/inet.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <net/hfsc.h> + +#include "pfctl.h" +#include "pfctl_parser.h" + +#define AVGN_MAX 8 +#define STAT_INTERVAL 5 + +struct queue_stats { + struct hfsc_class_stats data; + int avgn; + double avg_bytes; + double avg_packets; + u_int64_t prev_bytes; + u_int64_t prev_packets; +}; + +struct pfctl_queue_node { + TAILQ_ENTRY(pfctl_queue_node) entries; + struct pf_queuespec qs; + struct queue_stats qstats; +}; +TAILQ_HEAD(qnodes, pfctl_queue_node) qnodes = TAILQ_HEAD_INITIALIZER(qnodes); + +int pfctl_update_qstats(int); +void pfctl_insert_queue_node(const struct pf_queuespec, + const struct queue_stats); +struct pfctl_queue_node *pfctl_find_queue_node(const char *, const char *); +void pfctl_print_queue_node(int, struct pfctl_queue_node *, + int); +void print_qstats(struct queue_stats); +void pfctl_free_queue_node(struct pfctl_queue_node *); +void pfctl_print_queue_nodestat(int, + const struct pfctl_queue_node *); +void update_avg(struct queue_stats *); + +int +pfctl_show_queues(int dev, const char *iface, int opts, int verbose2) +{ + struct pfctl_queue_node *node; + int nodes, dotitle = (opts & PF_OPT_SHOWALL); + + + if ((nodes = pfctl_update_qstats(dev)) <= 0) + return (nodes); + + TAILQ_FOREACH(node, &qnodes, entries) { + if (iface != NULL && strcmp(node->qs.ifname, iface)) + continue; + if (dotitle) { + pfctl_print_title("QUEUES:"); + dotitle = 0; + } + pfctl_print_queue_node(dev, node, opts); + } + + while (verbose2 && nodes > 0) { + printf("\n"); + fflush(stdout); + sleep(STAT_INTERVAL); + if ((nodes = pfctl_update_qstats(dev)) == -1) + return (-1); + TAILQ_FOREACH(node, &qnodes, entries) { + if (iface != NULL && strcmp(node->qs.ifname, iface)) + continue; + pfctl_print_queue_node(dev, node, opts); + } + } + while ((node = TAILQ_FIRST(&qnodes)) != NULL) + TAILQ_REMOVE(&qnodes, node, entries); + return (0); +} + +int +pfctl_update_qstats(int dev) +{ + struct pfctl_queue_node *node; + struct pfioc_queue pq; + struct pfioc_qstats pqs; + u_int32_t mnr, nr; + struct queue_stats qstats; + static u_int32_t last_ticket; + + memset(&pq, 0, sizeof(pq)); + memset(&pqs, 0, sizeof(pqs)); + memset(&qstats, 0, sizeof(qstats)); + if (ioctl(dev, DIOCGETQUEUES, &pq)) { + warn("DIOCGETQUEUES"); + return (-1); + } + + /* if a new set is found, start over */ + if (pq.ticket != last_ticket) + while ((node = TAILQ_FIRST(&qnodes)) != NULL) + TAILQ_REMOVE(&qnodes, node, entries); + last_ticket = pq.ticket; + + mnr = pq.nr; + for (nr = 0; nr < mnr; ++nr) { + pqs.nr = nr; + pqs.ticket = pq.ticket; + pqs.buf = &qstats.data; + pqs.nbytes = sizeof(qstats.data); + if (ioctl(dev, DIOCGETQSTATS, &pqs)) { + warn("DIOCGETQSTATS"); + return (-1); + } +// if (pqs.queue.qname[0] != '_') { +// if (pqs.queue.parent[0] && pqs.queue.parent[0] == '_') +// pqs.queue.parent[0] = 0; + if ((node = pfctl_find_queue_node(pqs.queue.qname, + pqs.queue.ifname)) != NULL) { + memcpy(&node->qstats.data, &qstats.data, + sizeof(qstats.data)); + update_avg(&node->qstats); + } else { + pfctl_insert_queue_node(pqs.queue, qstats); + } +// } + } + return (mnr); +} + +void +pfctl_insert_queue_node(const struct pf_queuespec qs, + const struct queue_stats qstats) +{ + struct pfctl_queue_node *node; + + node = calloc(1, sizeof(struct pfctl_queue_node)); + if (node == NULL) + err(1, "pfctl_insert_queue_node: calloc"); + memcpy(&node->qs, &qs, sizeof(qs)); + memcpy(&node->qstats, &qstats, sizeof(qstats)); + TAILQ_INSERT_TAIL(&qnodes, node, entries); + update_avg(&node->qstats); +} + +struct pfctl_queue_node * +pfctl_find_queue_node(const char *qname, const char *ifname) +{ + struct pfctl_queue_node *node; + + TAILQ_FOREACH(node, &qnodes, entries) + if (!strcmp(node->qs.qname, qname) + && !(strcmp(node->qs.ifname, ifname))) + return (node); + return (NULL); +} + +void +pfctl_print_queue_node(int dev, struct pfctl_queue_node *node, int opts) +{ + if (node == NULL) + return; + + print_queuespec(&node->qs); + if (opts & PF_OPT_VERBOSE) + pfctl_print_queue_nodestat(dev, node); + + if (opts & PF_OPT_DEBUG) + printf(" [ qid=%u parent_qid=%u ifname=%s]\n", + node->qs.qid, node->qs.parent_qid, node->qs.ifname); +} + +void +pfctl_print_queue_nodestat(int dev, const struct pfctl_queue_node *node) +{ + printf(" [ pkts: %10llu bytes: %10llu " + "dropped pkts: %6llu bytes: %6llu ]\n", + (unsigned long long)node->qstats.data.xmit_cnt.packets, + (unsigned long long)node->qstats.data.xmit_cnt.bytes, + (unsigned long long)node->qstats.data.drop_cnt.packets, + (unsigned long long)node->qstats.data.drop_cnt.bytes); + printf(" [ qlength: %3d/%3d ]\n", node->qstats.data.qlength, + node->qstats.data.qlimit); + + if (node->qstats.avgn < 2) + return; + + printf(" [ measured: %7.1f packets/s, %s/s ]\n", + node->qstats.avg_packets / STAT_INTERVAL, + rate2str((8 * node->qstats.avg_bytes) / STAT_INTERVAL)); +} + +void +update_avg(struct queue_stats *s) +{ + if (s->avgn > 0) { + if (s->data.xmit_cnt.bytes >= s->prev_bytes) + s->avg_bytes = ((s->avg_bytes * (s->avgn - 1)) + + (s->data.xmit_cnt.bytes - s->prev_bytes)) / + s->avgn; + if (s->data.xmit_cnt.packets >= s->prev_packets) + s->avg_packets = ((s->avg_packets * (s->avgn - 1)) + + (s->data.xmit_cnt.packets - s->prev_packets)) / + s->avgn; + } + + s->prev_bytes = s->data.xmit_cnt.bytes; + s->prev_packets = s->data.xmit_cnt.packets; + if (s->avgn < AVGN_MAX) + s->avgn++; +} |