summaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorHenning Brauer <henning@cvs.openbsd.org>2013-10-12 12:16:13 +0000
committerHenning Brauer <henning@cvs.openbsd.org>2013-10-12 12:16:13 +0000
commitcb341636ad8d53a27a17eb035feb2940219ef542 (patch)
tree0f9f6af76997e8091780e28a92ec1f596803d03c /sbin
parentcf075469382a1b2bbe60bc2cab89d834b6a892a8 (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/Makefile4
-rw-r--r--sbin/pfctl/parse.y356
-rw-r--r--sbin/pfctl/pfctl.c211
-rw-r--r--sbin/pfctl/pfctl.h4
-rw-r--r--sbin/pfctl/pfctl_altq.c4
-rw-r--r--sbin/pfctl/pfctl_parser.c56
-rw-r--r--sbin/pfctl/pfctl_parser.h15
-rw-r--r--sbin/pfctl/pfctl_qstats.c28
-rw-r--r--sbin/pfctl/pfctl_queue.c237
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++;
+}