summaryrefslogtreecommitdiff
path: root/usr.sbin/altq
diff options
context:
space:
mode:
authorKenjiro Cho <kjc@cvs.openbsd.org>2001-06-27 18:23:37 +0000
committerKenjiro Cho <kjc@cvs.openbsd.org>2001-06-27 18:23:37 +0000
commit77495dfc56dcd9fa3f83095f0c6e02d31e3e62ab (patch)
treea54e11370c1ed886374557f82f27156196a46ba8 /usr.sbin/altq
parent54fed91c5c2a712e95e1cd420ef821213ea00f3f (diff)
import ALTQ userland tools from KAME.
Diffstat (limited to 'usr.sbin/altq')
-rw-r--r--usr.sbin/altq/Makefile6
-rw-r--r--usr.sbin/altq/Makefile.inc6
-rw-r--r--usr.sbin/altq/altqd/Makefile24
-rw-r--r--usr.sbin/altq/altqd/altq.conf.51173
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/cbq.bandwidthtest17
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/cbq.diffedge57
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/cbq.lo012
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/cbq.sample057
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/cbq.sample118
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/cbq.sample213
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/cbq.sharing36
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/cbq.v668
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/cbq.widecamp94
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/cdnr.sample36
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/hfsc.diffedge59
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/hfsc.grate23
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/hfsc.lo023
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/hfsc.pvc014
-rw-r--r--usr.sbin/altq/altqd/altq.conf.samples/hfsc.share34
-rw-r--r--usr.sbin/altq/altqd/altqd.8103
-rw-r--r--usr.sbin/altq/altqd/altqd.c338
-rw-r--r--usr.sbin/altq/altqd/libaltq2.c70
-rw-r--r--usr.sbin/altq/altqstat/Makefile16
-rw-r--r--usr.sbin/altq/altqstat/altqstat.198
-rw-r--r--usr.sbin/altq/altqstat/altqstat.c226
-rw-r--r--usr.sbin/altq/altqstat/altqstat.h67
-rw-r--r--usr.sbin/altq/altqstat/qdisc_blue.c90
-rw-r--r--usr.sbin/altq/altqstat/qdisc_cbq.c160
-rw-r--r--usr.sbin/altq/altqstat/qdisc_cdnr.c153
-rw-r--r--usr.sbin/altq/altqstat/qdisc_conf.c107
-rw-r--r--usr.sbin/altq/altqstat/qdisc_fifoq.c85
-rw-r--r--usr.sbin/altq/altqstat/qdisc_hfsc.c130
-rw-r--r--usr.sbin/altq/altqstat/qdisc_priq.c117
-rw-r--r--usr.sbin/altq/altqstat/qdisc_red.c114
-rw-r--r--usr.sbin/altq/altqstat/qdisc_rio.c147
-rw-r--r--usr.sbin/altq/altqstat/qdisc_wfq.c167
-rw-r--r--usr.sbin/altq/altqstat/quip_client.c492
-rw-r--r--usr.sbin/altq/altqstat/quip_client.h49
-rw-r--r--usr.sbin/altq/libaltq/Makefile18
-rw-r--r--usr.sbin/altq/libaltq/altq_qop.h255
-rw-r--r--usr.sbin/altq/libaltq/parser.c1353
-rw-r--r--usr.sbin/altq/libaltq/qop.c1438
-rw-r--r--usr.sbin/altq/libaltq/qop_blue.c278
-rw-r--r--usr.sbin/altq/libaltq/qop_blue.h45
-rw-r--r--usr.sbin/altq/libaltq/qop_cbq.c1082
-rw-r--r--usr.sbin/altq/libaltq/qop_cbq.h102
-rw-r--r--usr.sbin/altq/libaltq/qop_cdnr.c933
-rw-r--r--usr.sbin/altq/libaltq/qop_cdnr.h119
-rw-r--r--usr.sbin/altq/libaltq/qop_conf.c67
-rw-r--r--usr.sbin/altq/libaltq/qop_dummy.c188
-rw-r--r--usr.sbin/altq/libaltq/qop_errlist.c49
-rw-r--r--usr.sbin/altq/libaltq/qop_fifoq.c246
-rw-r--r--usr.sbin/altq/libaltq/qop_fifoq.h39
-rw-r--r--usr.sbin/altq/libaltq/qop_hfsc.c1097
-rw-r--r--usr.sbin/altq/libaltq/qop_hfsc.h79
-rw-r--r--usr.sbin/altq/libaltq/qop_priq.c524
-rw-r--r--usr.sbin/altq/libaltq/qop_priq.h62
-rw-r--r--usr.sbin/altq/libaltq/qop_red.c308
-rw-r--r--usr.sbin/altq/libaltq/qop_red.h48
-rw-r--r--usr.sbin/altq/libaltq/qop_rio.c352
-rw-r--r--usr.sbin/altq/libaltq/qop_rio.h48
-rw-r--r--usr.sbin/altq/libaltq/qop_wfq.c283
-rw-r--r--usr.sbin/altq/libaltq/qop_wfq.h42
-rw-r--r--usr.sbin/altq/libaltq/quip_server.c483
-rw-r--r--usr.sbin/altq/libaltq/quip_server.h37
-rw-r--r--usr.sbin/altq/tbrconfig/Makefile11
-rw-r--r--usr.sbin/altq/tbrconfig/tbrconfig.8170
-rw-r--r--usr.sbin/altq/tbrconfig/tbrconfig.c322
68 files changed, 14577 insertions, 0 deletions
diff --git a/usr.sbin/altq/Makefile b/usr.sbin/altq/Makefile
new file mode 100644
index 00000000000..b683a7a01d6
--- /dev/null
+++ b/usr.sbin/altq/Makefile
@@ -0,0 +1,6 @@
+# $OpenBSD: Makefile,v 1.1 2001/06/27 18:23:16 kjc Exp $
+# $NetBSD: Makefile,v 1.2 2001/01/09 03:13:41 lukem Exp $
+
+SUBDIR= libaltq altqd altqstat tbrconfig
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/altq/Makefile.inc b/usr.sbin/altq/Makefile.inc
new file mode 100644
index 00000000000..1934f915415
--- /dev/null
+++ b/usr.sbin/altq/Makefile.inc
@@ -0,0 +1,6 @@
+# $OpenBSD: Makefile.inc,v 1.1 2001/06/27 18:23:16 kjc Exp $
+# $NetBSD: Makefile.inc,v 1.1 2000/12/16 18:57:34 thorpej Exp $
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
diff --git a/usr.sbin/altq/altqd/Makefile b/usr.sbin/altq/altqd/Makefile
new file mode 100644
index 00000000000..03524847471
--- /dev/null
+++ b/usr.sbin/altq/altqd/Makefile
@@ -0,0 +1,24 @@
+# $OpenBSD: Makefile,v 1.1 2001/06/27 18:23:16 kjc Exp $
+# $NetBSD: Makefile,v 1.3 2001/01/11 13:15:06 enami Exp $
+
+.include <bsd.own.mk>
+
+PROG= altqd
+MAN= altqd.8 altq.conf.5
+
+SRCS= altqd.c libaltq2.c
+
+CPPFLAGS+= -DALTQ -I${.CURDIR}/../libaltq
+
+.if exists(${.CURDIR}/../libaltq/${__objdir})
+LDADD+= -L${.CURDIR}/../libaltq/${__objdir} -laltq
+DPADD+= ${.CURDIR}/../libaltq/${__objdir}/libaltq.a
+.else
+LDADD+= -L${.CURDIR}/../libaltq -laltq
+DPADD+= ${.CURDIR}/../libaltq/libaltq.a
+.endif
+
+LDADD+= -lm
+DPADD+= ${LIBM}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/altq/altqd/altq.conf.5 b/usr.sbin/altq/altqd/altq.conf.5
new file mode 100644
index 00000000000..b5c39747287
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.5
@@ -0,0 +1,1173 @@
+.\" $OpenBSD: altq.conf.5,v 1.1 2001/06/27 18:23:17 kjc Exp $
+.\" $KAME: altq.conf.5,v 1.8 2001/04/09 16:26:28 thorpej Exp $
+.\"
+.\" Copyright (C) 2000
+.\" Sony Computer Science Laboratories Inc. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd September 28, 1999
+.Dt ALTQ.CONF 5
+.Os KAME
+.\"
+.Sh NAME
+.Nm altq.conf
+.Nd ALTQ configuration file
+.\"
+.Sh DESCRIPTION
+The
+.Nm altq.conf
+file contains a number of lines specifying the behavior of queueing
+disciplines. Comments start with a # and extend to the end of the line.
+.Pp
+The
+.Xr altqd 8
+program reads
+.Pa /etc/altq.conf
+at startup and sets up queueing disciplines.
+BLUE, CBQ (Class-Based Queueing), FIFOQ (First-In First-Out Queue),
+HFSC (Hierarchical Fair Service Curve), PRIQ (Priority Queueing),
+RED (Random Early Detection), RIO (RED with IN/OUT),
+WFQ (Weighted Fair Queueing),
+and CDNR (Diffserv Traffic Conditioner) can be configured in this file.
+.Pp
+.Sh Interface Commands
+.Bl -tag -width interface -offset indent
+.It Nm interface
+.Ar if_name
+.Op Cm bandwidth Ar bps
+.Op Cm tbrsize Ar bytes
+.Op Ar sched_type
+.Op Ar discipline-specific-options
+.El
+.Pp
+The
+.Sy interface
+command specifies a network interface to be under control of ALTQ.
+One interface specification is provided for each network interface
+under control of ALTQ. A system configured as a router may have
+multiple interface specifications.
+.\"
+.Bl -tag -width 8n -offset indent
+.It Em if_name
+specifies the name of a network interface (e.g., fxp0).
+.It Sy bandwidth
+specifies the interface bandwidth in bits per second.
+This is the maximum rate that the queueing discipline will allow on this
+interface.
+.It Sy tbrsize
+specifies the bucket size of a token bucket regulator in bytes.
+When
+.Sy tbrsize
+is omitted, the system automatically sets the bucket size
+using heuristics.
+The token rate is set to the interface bandwidth specified by the
+.Sy interface
+command.
+.It Em sched_type
+Type of a queueing discipline. It must be either
+.Sy blue ,
+.Sy cbq ,
+.Sy fifoq ,
+.Sy hfsc ,
+.Sy priq ,
+.Sy red ,
+.Sy rio ,
+or
+.Sy wfq .
+If the interface has only traffic conditioners and no queueing
+discipline, sched_type can be omitted.
+.El
+.Pp
+.Sh Class Command
+.Bl -tag -width class -offset indent
+.It Nm class
+.Ar sched_type
+.Ar if_name
+.Ar class_name
+.Ar parent_name
+.Op Cm red|rio
+.Op Cm ecn
+.Op Cm cleardscp
+.Op Ar discipline-specific-options
+.El
+.Pp
+The
+.Sy class
+command specifies a packet scheduling class for CBQ, HFSC, or PRIQ.
+A class specifier must be provided for each packet scheduling class.
+.Bl -tag -width 8n -offset indent
+.It Em sched_type
+Type of queueing discipline. Must correspond to the discipline name
+in interface specification.
+.It Em if_name
+Interface name. Must correspond to name in interface specification.
+.It Em class_name
+Arbitrary name for this class. Must be unique for this interface.
+.It Em parent_name
+The name of the parent class for this class (for CBQ or HFSC).
+Parent class must have been previously defined.
+PRIQ does not have class hierarchy and parent_name must be ``NULL''
+for PRIQ classes.
+.It Sy red
+Use RED (Random Early Detection) on this class queue.
+RED drops packets with the probability propotional to the average
+queue length.
+.It Sy rio
+Use RIO (RED with In/Out bit) on this class queue.
+RIO runs triple RED algorithms at the same time.
+.It Sy ecn
+Use RED/ECN (Explicit Congestion Notification) on this
+class queue (experimental implementation). ECN implies RED.
+.It Sy cleardscp
+Clear diffserv codepoint in the IP header.
+.El
+.Pp
+.Sh Filter Commands
+.Bl -tag -width filter -offset indent
+.It Nm filter
+.Ar if_name
+.Ar class_name
+.Op Cm name Ar fltr_name
+.Op Cm ruleno Ar num
+.Ar filter_values
+.El
+.Pp
+The
+.Sy filter
+command specifies a filter to classify packets into
+a scheduling class.
+A filter specifier determines any statically-defined packet
+classification rules.
+.Bl -tag -width 10n -offset indent
+.It Em if_name
+Name of a network interface (e.g., fxp0).
+.It Em class_name
+Name of a class or a conditioner to which matching packets are directed.
+.It Sy name
+Add an arbitrary name to the filter for a future refenece.
+.It Sy ruleno
+Specifies explicit order of filter matching. Filter matching is performed
+from a filter with a larger ruleno. Default is 0.
+.El
+.Pp
+.Em filter_value
+should be in the following format:
+.Bl -tag -width filter -offset indent
+.It filter_values :
+.Ad dst_addr Op Cm netmask Ar mask
+.Ar dport
+.Ad src_addr Op Cm netmask Ar mask
+.Ar sport
+.Ar proto
+.Oo
+.Sy tos
+.Ar value
+.Op Cm tosmask Ar value
+.Oc
+.Op Cm gpi Ar value
+.El
+.Pp
+Here
+.Ad dst_addr
+and
+.Ad src_addr
+are dotted-decimal addresses of
+the destination and the source respectively. An address may be
+followed by
+.Sy netmask
+keyword.
+.Em dport
+and
+.Em sport
+are port numbers of the destination and the source respectively.
+.Em proto
+is a protocol number defined for IP packets (e.g. 6 for TCP).
+.Sy tos
+keyword can be used to specify the type of service field value.
+.Sy gpi
+keyword can be used to specify the Security Parameter Index value for
+IPSec.
+.Pp
+When filter value 0 is used, it is taken as a wildcard.
+.Bl -tag -width filter6 -offset indent
+.It Nm filter6
+.Ar if_name
+.Ar class_name
+.Op Cm name Ar fltr_name
+.Op Cm ruleno Ar num
+.Ar filter6_values
+.El
+.Pp
+The
+.Sy filter6
+command is for IPv6.
+.Em filter6_value
+should be in the following format:
+.Bl -tag -width filter6 -offset indent
+.It filter6_values :
+.Ad dst_addr[/prefix_len]
+.Ar dport
+.Ad src_addr[/prefix_len]
+.Ar sport
+.Ar proto
+.Op Cm flowlabel Ar value
+.Oo
+.Sy tclass
+.Ar value
+.Op Cm tclassmask Ar value
+.Oc
+.Op Cm gpi Ar value
+.El
+.Pp
+Here
+.Ad dst_addr
+and
+.Ad src_addr
+are IPv6 addresses of the destination and the source respectively.
+An address may be followed by an optional
+.Sy address prefix length .
+.Em dport
+and
+.Em sport
+are port numbers of the destination and the source respectively.
+.Em proto
+is a protocol number defined for IPv6 packets (e.g. 6 for TCP).
+.Sy flowlabel
+keyword can be used to specify the flowlabel field value.
+.Sy tclass
+keyword can be used to specify the traffic class field value.
+.Sy gpi
+keyword can be used to specify the Security Parameter Index value for
+IPSec.
+.Pp
+When filter value 0 is used, it is taken as a wildcard.
+.Pp
+.Sh CBQ Commands
+CBQ (Class Based Queueing) achieves both partitioning and sharing of
+link bandwidth by hierarchically structured classes.
+Each class has its own queue and is assigned its share of bandwidth.
+A child class can borrow bandwidth from its parent class as long as
+excess bandwidth is available.
+.Bl -tag -width interface -offset indent
+.It Nm interface
+.Ar if_name
+.Op Cm bandwidth Ar bps
+.Op Cm tbrsize Ar bytes
+.Op Ar sched_type
+.Op Cm efficient
+.El
+.Pp
+.Bl -tag -width 8n -offset indent
+.It Em if_name
+specifies the name of a network interface (e.g., fxp0).
+.It Sy bandwidth
+specifies the interface bandwidth in bits per second.
+.It Sy tbrsize
+specifies the bucket size of a token bucket regulator in bytes.
+.It Em sched_type
+must be either
+.Sy cbq ,
+.Sy cbq-wrr
+(weighted-round robin) or
+.Sy cbq-prr
+(packet-by-packet round robin).
+.Sy cbq
+is equivalent to
+.Sy cbq-wrr .
+.It Sy efficient
+Enables CBQ's link efficiency mode. This means that
+the scheduler will send a packet from the first overlimit
+class it encounters of all classes of the link-sharing
+structure when all classes are overlimit.
+This will also cause the scheduler to use greater than it's assigned
+bandwidth, if the link is capable of more than the assigned bandwidth.
+By default, this mode is turned off. By adding the keyword
+.Sy efficient
+to the interface specification line, enables this mode.
+.El
+.Pp
+.Bl -tag -width class -offset indent
+.It Nm class
+.Ar sched_type
+.Ar if_name
+.Ar class_name
+.Ar parent_name
+.Op Cm admission cntlload|none
+.Op Cm priority Ar pri
+.Op Cm pbandwidth Ar percent
+.Op Cm exactbandwidth Ar bps
+.Op Cm borrow
+.Op Cm default
+.Op Cm control
+.Op Cm maxburst Ar count
+.Op Cm minburst Ar count
+.Bk -words
+.Op Cm maxdelay Ar msec
+.Ek
+.Op Cm packetsize Ar bytes
+.Op Cm maxpacketsize Ar bytes
+.Op Cm red|rio
+.Op Cm ecn
+.Op Cm flowvalve
+.Op Cm cleardscp
+.El
+.Pp
+The
+.Sy class
+command specifies a CBQ class.
+The classes are organized as a hierarchy, and every class, except
+for the root class, has a parent.
+.Bl -tag -width 8n -offset indent
+.It Em sched_type
+must be
+.Sy cbq
+for a CBQ class.
+.It Em if_name
+Interface name. Must correspond to name in interface specification.
+.It Em class_name
+Arbitrary name for this class. Must be unique within the class
+hierarchy for this interface. The name
+.Sy ctl_class
+is a reserved class name.
+.It Em parent_name
+The name of the parent class for this class or
+.Sy NULL
+if this is the root class. Parent class must have been previously defined.
+.It Sy admission
+The type of admission control and QoS type.
+.Sy cntlload
+is controlled load service for
+.Em RSVP ,
+otherwise, it should be
+.Sy none .
+The default is
+.Sy none .
+.It Sy priority
+High numbers are higher priority. Max value is 7 and Min value is 0.
+Default is 1.
+.It Sy pbandwidth
+The percentage of the interface bandwidth allocated to this class.
+Generally should add up to 100 percent at each level of the class
+hierarchy, although other amounts can be specified for purposes of
+experimentation.
+.It Sy exactbandwidth
+Specify the bandwidth in bits-per-second instead of
+.Sy pbandwidth .
+Note that the bandwidth allocation of CBQ is not so precise but this
+is just a way to pass a parameter to CBQ; the user is supposed to know
+the detailed internals of CBQ.
+.Sy pbandwidth
+is a preferred way to specify the bandwidth of a class.
+.It Sy borrow
+The class can borrow bandwidth from its parent class when this class
+is overlimit.
+If this keyword is not present, then no borrowing is done, and the
+packet is delayed or dropped when the class is overlimit.
+.It Sy default
+Specify the default class. When this keyword is present, all packets
+that do not match some classification criteria are assigned to this
+class. Must be exactly one class on each interface defined as the
+default class.
+.It Sy control
+Specify the control class. When this keyword is present, the predefined
+control class packets (RSVP, IGMP, and ICMP) are assigned to this
+class.
+Note that when the control class is not specified by the time the
+default class is created, one is automatically created with default
+parameters. Thus, if the control class is specified, it must be
+listed before the default class.
+Must be exactly one class on each interface defined as the
+control class.
+.It Sy maxburst
+The maximum burst of back-to-back packets allowed in this class.
+Default is 16 but the default value is automatically reduced to 4 when
+the class bandwidth is small (about less than 1Mbps).
+.It Sy minburst
+The minimum burst is used to obtain the steady state burst size. It's
+the parameter to help compute offtime for the class. Offtime is the
+amount of time a class is to wait between packets. Default is 2.
+.It Sy maxdelay
+The maxdelay is specified in millisecond and used to obtain the max
+queue size of the class.
+If not specified, the default max queue size (30 packets) is used.
+.It Sy packetsize
+The average packet size in bytes to be used in CBQ over-/under-limit
+computations. Default value is MTU of the interface.
+.It Sy maxpacketsize
+The maxium packet size in bytes for the class.
+Default value is MTU of the interface.
+.It Sy red
+enables RED on this class queue.
+.It Sy rio
+enables RIO on this class queue.
+.It Sy ecn
+enables RED/ECN on this class queue.
+.It Sy flowvalve
+enables RED/flow-valve (a.k.a. red-penalty-box) on this class queue.
+.It Sy cleardscp
+clears diffserv codepoint in the IP header.
+.El
+.Pp
+.Sh HFSC Commands
+HFSC (Hierarchical Fair Service Curve) supports both link-sharing and
+guaranteed real-time services. H-FSC employs a service curve based QoS
+model, and its unique feature is an ability to decouple delay and
+bandwidth allocation.
+HFSC has 2 independent scheduling mechanisms.
+Real-time scheduling is used to guarantee the delay and the
+bandwidth allocation at the same time.
+Hierarchical link-sharing is used to distribute the excess
+bandwidth.
+When dequeueing a packet, HFSC always tries real-time scheduling
+first. If no packet is eligible for real-time scheduling,
+link-sharing scheduling is performed.
+HFSC does not use class hierarchy for real-time scheduling.
+.Bl -tag -width interface -offset indent
+.It Nm interface
+.Ar if_name
+.Op Cm bandwidth Ar bps
+.Op Cm tbrsize Ar bytes
+.Op Ar sched_type
+.El
+.Pp
+.Bl -tag -width 8n -offset indent
+.It Em if_name
+specifies the name of a network interface (e.g., fxp0).
+.It Sy bandwidth
+specifies the interface bandwidth in bits per second.
+.It Sy tbrsize
+specifies the bucket size of a token bucket regulator in bytes.
+.It Em sched_type
+must be
+.Sy hfsc
+for HFSC.
+.El
+.Pp
+.Bl -tag -width class -offset indent
+.It Nm class
+.Ar sched_type
+.Ar if_name
+.Ar class_name
+.Ar parent_name
+.Op Cm admission cntlload|none
+.Op Bq Cm sc Em m1 d m2
+.Op Bq Cm rt Em m1 d m2
+.Op Bq Cm ls Em m1 d m2
+.Op Cm pshare Ar percent
+.Op Cm grate Ar bps
+.Op Cm default
+.Op Cm qlimit Ar count
+.Op Cm red|rio
+.Op Cm ecn
+.Op Cm cleardscp
+.El
+.Pp
+The
+.Sy class
+command specifies a HFSC class.
+The classes are organized as a hierarchy, and every class, except
+for the root class, has a parent.
+.Pp
+Each HFSC class has 2 service curves, the real-time service curve and
+the link-sharing service curve. Service curves are specified by
+.Bq Em type m1 d m2
+\&.
+.Em type
+should be either
+.Sy sc, rt
+or
+.Sy ls .
+.Sy sc
+(service curve) is used to set the same values to both service curves.
+.Sy rt
+(real-time) is used to specify the real-time service curve.
+.Sy ls
+(link-sharing) is used to specify the link-sharing service curve.
+.Em m1
+is the slope of the first segment specified in bits-per-second.
+.Em d
+is the x-projection of the intersection point of the 2 segments
+specified in milli-second.
+.Em m2
+is the slope of the second segment specified in bits-per-second.
+.Bl -tag -width 8n -offset indent
+.It Em sched_type
+must be
+.Sy hfsc
+for a HFSC class.
+.It Em if_name
+Interface name. Must correspond to name in interface specification.
+.It Em class_name
+Arbitrary name for this class. Must be unique within the class
+hierarchy for this interface. The name
+.Sy root
+is a reserved class name for the root class. The root class for the
+interface is automatically created by the
+.Sy interface
+command.
+.It Em parent_name
+The name of the parent class for this class. Keyword
+.Sy root
+is used when the parent is the root class. Parent class must
+have been previously defined.
+.It Sy admission
+The type of admission control and QoS type.
+.Sy cntlload
+is controlled load service for
+.Em RSVP ,
+otherwise, it should be
+.Sy none .
+The default is
+.Sy none .
+.It Sy pshare
+Percent of the link share. This specifies a linear link-sharing
+service curve as a fraction of the link bandwidth.
+It is a short hand of [ls 0 0 (link-bandwidth * percent / 100)].
+.It Sy grate
+Guaranteed rate. This specifies a linear real-time service curve.
+It is a short hand of [rt 0 0 bps].
+.It Sy default
+Specify the default class. When this keyword is present, all packets
+that do not match some classification criteria are assigned to this
+class. Must be exactly one class on each interface defined as the
+default class.
+.It Sy qlimit
+The maxium queue size in number of packets.
+Default value is 50.
+.It Sy red
+enables RED on this class queue.
+.It Sy rio
+enables RIO on this class queue.
+.It Sy ecn
+enables RED/ECN on this class queue.
+.It Sy cleardscp
+clears diffserv codepoint in the IP header.
+.El
+.Pp
+.Sh PRIQ Commands
+PRIQ (Priority Queueing) implements a simple priority-based queueing.
+A higher priority class is always served first.
+Up to 16 priorities can be used with PRIQ.
+.Bl -tag -width interface -offset indent
+.It Nm interface
+.Ar if_name
+.Op Cm bandwidth Ar bps
+.Op Cm tbrsize Ar bytes
+.Op Ar sched_type
+.El
+.Pp
+.Bl -tag -width 8n -offset indent
+.It Em if_name
+specifies the name of a network interface (e.g., fxp0).
+.It Sy bandwidth
+specifies the interface bandwidth in bits per second.
+.It Sy tbrsize
+specifies the bucket size of a token bucket regulator in bytes.
+.It Em sched_type
+must be
+.Sy priq
+for PRIQ.
+.El
+.Pp
+.Bl -tag -width class -offset indent
+.It Nm class
+.Ar sched_type
+.Ar if_name
+.Ar class_name
+.Ar parent_name
+.Op Cm priority Ar pri
+.Op Cm default
+.Op Cm qlimit Ar count
+.Op Cm red|rio
+.Op Cm ecn
+.Op Cm cleardscp
+.El
+.Pp
+.Bl -tag -width 8n -offset indent
+.It Em sched_type
+must be
+.Sy priq
+for a PRIQ class.
+.It Em if_name
+Interface name. Must correspond to name in interface specification.
+.It Em class_name
+Arbitrary name for this class. Must be unique for this interface.
+.It Em parent_name
+Parent class must be ``NULL'' for PRIQ.
+.It Sy priority
+High numbers are higher priority.
+Max value is 15 and Min value is 0. Default is 0.
+A higher priority class is always served first in PRIQ.
+Priority must be unique for the interface.
+.It Sy default
+Specify the default class. When this keyword is present, all packets
+that do not match some classification criteria are assigned to this
+class. Must be exactly one class on each interface defined as the
+default class.
+.It Sy qlimit
+The maxium queue size in number of packets.
+Default value is 50.
+.It Sy red
+enables RED on this class queue.
+.It Sy rio
+enables RIO on this class queue.
+.It Sy ecn
+enables RED/ECN on this class queue.
+.It Sy cleardscp
+clears diffserv codepoint in the IP header.
+.El
+.Pp
+.Sh WFQ Commands
+WFQ (Weighted Fair Queueing) implements a weighted-round robin
+scheduler for a set of queue.
+A weight can be assigned to each queue to give a
+different proportion of the link capacity.
+A hash function is used to map a flow to one of a set of queues, and
+thus, it is possible for two different flows to be mapped into the same
+queue.
+.Bl -tag -width interface -offset indent
+.It Nm interface
+.Ar if_name
+.Op Cm bandwidth Ar bps
+.Op Cm tbrsize Ar bytes
+.Op Ar sched_type
+.Op Cm nqueues Ar count
+.Op Cm qsize Ar bytes
+.Op Cm hash Ar policy
+.El
+.Pp
+.Bl -tag -width 8n -offset indent
+.It Em if_name
+specifies the name of a network interface (e.g., fxp0).
+.It Sy bandwidth
+specifies the interface bandwidth in bits per second.
+.It Sy tbrsize
+specifies the bucket size of a token bucket regulator in bytes.
+.It Em sched_type
+must be
+.Sy wfq
+for WFQ.
+.It Sy nqueues
+The number of queues in WFQ. Default value is 256.
+.It Sy qsize
+The size of each queue in number of bytes. Default value is 64K bytes.
+.It Sy hash
+Type of hash policy to select a queue.
+.Sy dstaddr
+specifies a hashing policy by IP destination address.
+.Sy full
+specifies a hashing policy by IP addresses and ports.
+.Sy srcport
+specifies a hashing policy by IP source port number. Default is
+.Sy dstaddr
+.El
+.Pp
+.Sh FIFOQ Commands
+FIFOQ (First-In First-Out Queueing) is a simple tail-drop FIFO queue.
+FIFOQ is the simplest possible implementation of a queueing discipline
+in ALTQ, and can be used to compare with other queueing disciplines.
+FIFOQ can be also used as a template for those who want to write their
+own queueing disciplines.
+.Bl -tag -width interface -offset indent
+.It Nm interface
+.Ar if_name
+.Op Cm bandwidth Ar bps
+.Op Cm tbrsize Ar bytes
+.Op Ar sched_type
+.Op Cm qlimit Ar count
+.El
+.Pp
+.Bl -tag -width 8n -offset indent
+.It Em if_name
+specifies the name of a network interface (e.g., fxp0).
+.It Sy bandwidth
+specifies the interface bandwidth in bits per second.
+.It Sy tbrsize
+specifies the bucket size of a token bucket regulator in bytes.
+.It Em sched_type
+must be
+.Sy fifoq
+for FIFOQ.
+.It Sy qlimit
+The maxium queue size in number of packets.
+Default value is 50.
+.El
+.Pp
+.Sh RED Commands
+RED (Random Early Detection) is an implicit congestion notification
+mechanism that exercises packet dropping or packet marking
+stochastically according to the average queue length.
+RED can be viewed as a buffer management mechanism
+and can be integrated into other packet scheduling schemes.
+.Bl -tag -width red -offset indent
+.It Nm red
+.Ar min_th
+.Ar max_th
+.Ar inv_pmax
+.El
+.Pp
+The
+.Sy red
+command sets the default RED paramters.
+.Em min_th
+and
+.Em max_th
+are the minimum and the maximum threshold values.
+.Em inv_pmax
+is the inverse (reciprocal) of the maximum drop probability.
+For example, 10 means the maximum drop probability of 1/10.
+.Bl -tag -width interface -offset indent
+.It Nm interface
+.Ar if_name
+.Op Cm bandwidth Ar bps
+.Op Cm tbrsize Ar bytes
+.Op Ar sched_type
+.Op Cm qlimit Ar count
+.Op Cm packetsize Ar bytes
+.Op Cm weight Ar n
+.Op Cm thmin Ar n
+.Op Cm thmax Ar n
+.Op Cm invpmax Ar n
+.Op Cm ecn
+.Op Cm flowvalve
+.El
+.Pp
+.Bl -tag -width 8n -offset indent
+.It Em if_name
+specifies the name of a network interface (e.g., fxp0).
+.It Sy bandwidth
+specifies the interface bandwidth in bits per second.
+.It Sy tbrsize
+specifies the bucket size of a token bucket regulator in bytes.
+.It Em sched_type
+must be
+.Sy red
+for RED.
+.It Sy qlimit
+The maxium queue size in number of packets.
+Default value is 60.
+.It Sy packetsize
+The average packet size in number of bytes. This parameter is used to
+calibrate the idle period. Default value is 1000.
+.It Sy weight
+The inverse of the weight of EWMA (exponentially weighted moving average).
+.It Sy thmin
+The minimum threshold.
+.It Sy thmax
+The maximum threshold.
+.It Sy invpmax
+The inverse of the maximum drop probability.
+.It Sy ecn
+enables ECN.
+.It Sy flowvalve
+enables flowvalve.
+.El
+.Pp
+.Sh RIO Commands
+ALTQ/RIO has 3 drop precedence levels defined for the Assured
+Forwarding of DiffServ (RFC2597).
+Since adaptive flows are likely to stay under
+the medium drop precedence level under congestion, the medium drop
+precedence would protect adaptive flows from unadaptive flows.
+.Pp
+The original RIO has 2 sets of RED parameters; one for in-profile
+packets and the other for out-of-profile packets.
+At the ingress of the network, profile meters tag packets as IN
+or OUT based on contracted profiles for customers.
+Inside the network, IN packets receive preferential treatment by
+the RIO dropper.
+It is possible to provision the network not to drop IN packets
+at all by providing enough capacity for the total volume of IN
+packets.
+Thus, RIO can be used to provide a service that statistically assures
+capacity allocated for users.
+This mechanism can be extended to support an arbitrary number of drop
+precedence levels. ALTQ supports 3 drop precedence levels.
+.Bl -tag -width rio -offset indent
+.It Nm rio
+.Ar low_min_th
+.Ar low_max_th
+.Ar low_inv_pmax
+.Ar medium_min_th
+.Ar medium_max_th
+.Ar medium_inv_pmax
+.Ar high_min_th
+.Ar high_max_th
+.Ar high_inv_pmax
+.El
+.Pp
+The
+.Sy rio
+command sets the default RIO paramters. The parameters are
+RED parameters for 3 (low, medium, high) drop precedence.
+.Bl -tag -width interface -offset indent
+.It Nm interface
+.Ar if_name
+.Op Cm bandwidth Ar bps
+.Op Cm tbrsize Ar bytes
+.Op Ar sched_type
+.Op Cm qlimit Ar count
+.Op Cm packetsize Ar bytes
+.Op Cm weight Ar n
+.Op Cm lo_thmin Ar n
+.Op Cm lo_thmax Ar n
+.Op Cm lo_invpmax Ar n
+.Op Cm med_thmin Ar n
+.Op Cm med_thmax Ar n
+.Op Cm med_invpmax Ar n
+.Op Cm hi_thmin Ar n
+.Op Cm hi_thmax Ar n
+.Op Cm hi_invpmax Ar n
+.Op Cm ecn
+.El
+.Pp
+.Bl -tag -width 8n -offset indent
+.It Em if_name
+specifies the name of a network interface (e.g., fxp0).
+.It Sy bandwidth
+specifies the interface bandwidth in bits per second.
+.It Sy tbrsize
+specifies the bucket size of a token bucket regulator in bytes.
+.It Em sched_type
+must be
+.Sy rio
+for RIO.
+.It Sy qlimit
+The maxium queue size in number of packets.
+Default value is 60.
+.It Sy packetsize
+The average packet size in number of bytes. This parameter is used to
+calibrate the idle period. Default value is 1000.
+.It Sy weight
+The inverse of the weight of EWMA (exponentially weighted moving average).
+.It Sy lo_thmin
+The minimum threshold for low drop precedence.
+.It Sy lo_thmax
+The maximum threshold for low drop precedence.
+.It Sy lo_invpmax
+The inverse of the maximum drop probability for low drop precedence.
+.It Sy med_thmin
+The minimum threshold for medium drop precedence.
+.It Sy med_thmax
+The maximum threshold for medium drop precedence.
+.It Sy med_invpmax
+The inverse of the maximum drop probability for medium drop precedence.
+.It Sy hi_thmin
+The minimum threshold for high drop precedence.
+.It Sy hi_thmax
+The maximum threshold for high drop precedence.
+.It Sy hi_invpmax
+The inverse of the maximum drop probability for high drop precedence.
+.It Sy ecn
+enables ECN.
+.El
+.Pp
+.Sh BLUE Commands
+.Bl -tag -width interface -offset indent
+.It Nm interface
+.Ar if_name
+.Op Cm bandwidth Ar bps
+.Op Cm tbrsize Ar bytes
+.Op Ar sched_type
+.Op Cm qlimit Ar count
+.Op Cm packetsize Ar bytes
+.Op Cm maxpmark Ar n
+.Op Cm holdtime Ar usec
+.Op Cm ecn
+.El
+.Pp
+.Bl -tag -width 8n -offset indent
+.It Em if_name
+specifies the name of a network interface (e.g., fxp0).
+.It Sy bandwidth
+specifies the interface bandwidth in bits per second.
+.It Sy tbrsize
+specifies the bucket size of a token bucket regulator in bytes.
+.It Em sched_type
+must be
+.Sy blue
+for BLUE.
+.It Sy qlimit
+The maxium queue size in number of packets.
+Default value is 60.
+.It Sy packetsize
+The average packet size in number of bytes. Default value is 1000.
+.It Sy maxpmark
+specifies the precision of marking probability.
+.It Sy holdtime
+specifies the hold time in usec.
+.It Sy ecn
+enables ECN.
+.El
+.Pp
+.Sh CDNR Commands
+The
+.Sy conditioner
+command specifies a diffserv traffic conditioner. A traffic
+conditioner is not a queueing discipline but a component to meter,
+mark or drop incoming packets according to some rules.
+.Pp
+As opposed to a queueing discipline, a traffic conditioner handles
+incoming packets at an input interface.
+If no queueing discipline (e.g., CBQ) is used for the interface,
+a null interface command should be used to specify an input network
+interface.
+.Bl -tag -width interface -offset indent
+.It Nm interface
+.Ar if_name
+.Op Cm bandwidth Ar bps
+.Op Cm tbrsize Ar bytes
+.El
+.Pp
+The
+.Sy conditioner
+command has the following syntax.
+.Bl -tag -width conditioner -offset indent
+.It Nm conditioner
+.Ar if_name
+.Ar cdnr_name
+.Aq action
+.El
+.Pp
+.Bl -tag -width 10n -offset indent
+.It Em if_name
+Interface name. Must correspond to name in interface specification.
+.It Em cdnr_name
+Arbitrary name for this conditioner. Must be unique for this
+interface.
+.It Em action
+Action of the conditioner.
+.El
+.Pp
+An action can be a recursively defined action. The following actions
+are defined.
+.Bl -tag -width pass -offset indent
+.It Nm pass
+.Bl -inset -offset indent
+.It Sy pass
+allows the packet to go through without any modification to the packet.
+.El
+.El
+.Pp
+.Bl -tag -width drop -offset indent
+.It Nm drop
+.Bl -inset -offset indent
+.It Sy drop
+rejects the packet. The packet is immediately discarded.
+.El
+.El
+.Pp
+.Bl -tag -width mark -offset indent
+.It Nm mark
+.Ar value
+.Bl -inset -offset indent
+.It Sy mark
+sets the specified value to the ds field in the IP header. Then, the
+packet is allowed to go through.
+.El
+.El
+.Pp
+.Bl -tag -width tbmeter -offset indent
+.It Nm tbmeter
+.Ar rate depth
+.Aq in_action
+.Aq out_action
+.Pp
+.Bl -inset -offset indent
+.It Sy tbmeter
+is a token bucket meter configured with rate and depth parameters.
+Rate is token rate in bits-per-second. Depth is bucket depth in KB.
+When an incoming packet is in profile (available token is more than
+the packet size), tbmeter takes in_action.
+Otherwise, tbmeter takes out_action.
+.El
+.El
+.Pp
+.Bl -tag -width trtcm -offset indent
+.It Nm trtcm
+.Ar cmtd_rate cmtd_depth peak_rate peak_depth
+.Aq green_action
+.Aq yellow_action
+.Aq red_action
+.Op Cm coloraware|colorblind
+.Bl -inset -offset indent
+.It Sy trtcm
+is a 2-rate 3 color marker for Assured Forwarding.
+A trtcm consists of 2 token buckets, one for a committed rate and the
+other for a peak rate.
+When an incoming packet is in the committed profile, trtcm takes
+green_action. When the packet is out of the committed profile but in
+the peak profile, trtcm takes yellow_action. Otherwise, tbtcm takes
+red_action.
+A trtcm is either color-aware or color-blind. A color-aware trtcm do
+not raise the color (ds field value), that is, a yellow packet can be
+yellow or red but can not be blue. Default is color-blind.
+.El
+.El
+.Pp
+.Bl -tag -width tswtcm -offset indent
+.It Nm tswtcm
+.Ar cmtd_rate peak_rate avg_interval
+.Aq green_action
+.Aq yellow_action
+.Aq red_action
+.Bl -inset -offset indent
+.It Sy tswtcm
+is a time sliding window 3 color marker for Assured Forwarding.
+A tswtcm differs from trtcm in that a tswtcm probabilistically marks
+packets.
+A tswtcm consists of 2 rates, one for a committed rate and the
+other for a peak rate.
+When an incoming packet is in the committed profile, tswtcm takes
+green_action. When the packet is out of the committed profile but in
+the peak profile, tswtcm takes yellow_action. Otherwise, tswtcm takes
+red_action.
+cmtd_rate and peak_rate are specified in bits per seccond.
+avg_interval provides the size of time window for averaging incoming
+rate, and is specified in milli-second. 500 msec is ok for normal
+settings.
+.El
+.El
+.Pp
+.Sh EXAMPLES
+.nf
+
+CBQ Example:
+
+#
+# cbq configuration for vx0 (10Mbps ether)
+# give at least 40% to TCP
+# limit HTTP from network 133.138.1.0 up to 10%, use RED.
+# other traffic goes into default class
+#
+interface vx0 bandwidth 10M cbq
+#
+class cbq vx0 root_class NULL priority 0 pbandwidth 100
+class cbq vx0 def_class root_class borrow pbandwidth 95 default
+class cbq vx0 tcp_class def_class borrow pbandwidth 40
+ filter vx0 tcp_class 0 0 0 0 6
+class cbq vx0 csl_class tcp_class pbandwidth 10 red
+ filter vx0 csl_class 0 0 133.138.1.0 netmask 0xffffff00 80 6
+ filter vx0 csl_class 133.138.1.0 netmask 0xffffff00 0 0 80 6
+#
+# sample filter6 command
+#
+ filter6 vx0 csl_class ::0 0 d000:a:0:123::/64 80 6
+
+HFSC Example:
+
+#
+# hfsc configuration for hierachical sharing
+#
+interface pvc0 bandwidth 45M hfsc
+#
+# (10% of the bandwidth share goes to the default class)
+class hfsc pvc0 def_class root pshare 10 default
+#
+# bandwidth share guaranteed rate
+# CMU: 45% 15Mbps
+# PITT: 45% 15Mbps
+#
+class hfsc pvc0 cmu root pshare 45 grate 15M
+class hfsc pvc0 pitt root pshare 45 grate 15M
+#
+# CMU bandwidth share guaranteed rate
+# CS: 20% 10Mbps
+# other: 20% 5Mbps
+#
+class hfsc pvc0 cmu_other cmu pshare 20 grate 10M
+ filter pvc0 cmu_other 0 0 128.2.0.0 netmask 0xffff0000 0 0
+class hfsc pvc0 cmu_cs cmu pshare 20 grate 5M
+ filter pvc0 cmu_cs 0 0 128.2.242.0 netmask 0xffffff00 0 0
+#
+# PITT bandwidth share guaranteed rate
+# CS: 20% 10Mbps
+# other: 20% 5Mbps
+#
+class hfsc pvc0 pitt_other pitt pshare 20 grate 10M
+ filter pvc0 pitt_other 0 0 136.142.0.0 netmask 0xffff0000 0 0
+class hfsc pvc0 pitt_cs pitt pshare 20 grate 5M
+ filter pvc0 pitt_cs 0 0 136.142.79.0 netmask 0xffffff00 0 0
+
+
+PRIQ Example:
+
+#
+# priq configuration for fxp0 (100Mbps ether)
+# icmp: high priority
+# tcp: medium priority
+# others: low priority
+#
+interface fxp0 bandwidth 100M priq
+#
+class priq fxp0 high_class NULL priority 2
+ filter fxp0 high_class 0 0 0 0 1
+class priq fxp0 med_class NULL priority 1
+ filter fxp0 high_class 0 0 0 0 6
+class priq fxp0 low_class NULL priority 0 default
+
+
+WFQ Example:
+
+interface pvc0 bandwidth 134000000 wfq
+
+
+FIFOQ Example:
+
+interface rl0 bandwidth 10M fifoq
+
+
+Conditioner Example:
+
+#
+interface fxp0
+#
+# a simple dropper
+# discard all packets from 192.168.0.83
+#
+conditioner fxp0 dropper <drop>
+ filter fxp0 dropper 0 0 192.168.0.83 0 0
+
+#
+# EF conditioner
+# mark EF to all packets from 192.168.0.117
+#
+conditioner pvc1 ef_cdnr <tbmeter 6M 64K <mark 0xb8><drop>>
+ filter fxp0 ef_cdnr 0 0 192.168.0.117 0 0
+
+#
+# AF1x conditioner
+# mark AF1x to packets from 192.168.0.178
+# AF11 (low drop precedence): less than 3Mbps
+# AF12 (medium drop precedence): more than 3Mbps and less than 10Mbps
+# AF13 (high drop precedence): more than 10Mbps
+#
+conditioner fxp0 af1x_cdnr <trtcm 3M 32K 10M 64K <mark 0x28><mark 0x30><mark 0x38>>
+ filter fxp0 af1x_cdnr 0 0 192.168.0.178 0 0
+
+.fi
+.Pp
+.Sh SEE ALSO
+.Xr altqd 8
+.Sh BUGS
+This man page is incomplete. For more information read the source.
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/cbq.bandwidthtest b/usr.sbin/altq/altqd/altq.conf.samples/cbq.bandwidthtest
new file mode 100644
index 00000000000..678de54b830
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/cbq.bandwidthtest
@@ -0,0 +1,17 @@
+#
+# this is the setting used for the bandwidth guarantee test (Fig. 7)
+# in the ALTQ paper
+#
+interface en0 bandwidth 134M cbq
+class cbq en0 root_class NULL priority 0 admission none pbandwidth 100
+class cbq en0 def_class root_class borrow priority 2 pbandwidth 95 default
+#
+class cbq en0 tcp_class0 def_class priority 3 pbandwidth 8
+filter en0 tcp_class0 0 0 0 6790 6
+class cbq en0 tcp_class1 def_class priority 3 pbandwidth 16
+filter en0 tcp_class1 0 0 0 6791 6
+class cbq en0 tcp_class2 def_class priority 3 pbandwidth 24
+filter en0 tcp_class2 0 0 0 6792 6
+class cbq en0 tcp_class3 def_class priority 3 pbandwidth 32
+filter en0 tcp_class3 0 0 0 6793 6
+
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/cbq.diffedge b/usr.sbin/altq/altqd/altq.conf.samples/cbq.diffedge
new file mode 100644
index 00000000000..adea91ccb49
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/cbq.diffedge
@@ -0,0 +1,57 @@
+#
+# sample diffedge configuration using CBQ
+#
+
+#
+# input interface
+#
+interface pvc1
+#
+# EF conditioner
+#
+conditioner pvc1 ef_cdnr <tbmeter 6M 64K <mark 0xb8><drop>>
+ filter pvc1 ef_cdnr 0 0 172.16.4.174 0 0
+#
+# AF conditioners
+#
+conditioner pvc1 af1x_cdnr \
+ <trtcm 3M 32K 10M 64K <mark 0x28><mark 0x30><mark 0x38> colorblind>
+conditioner pvc1 af2x_cdnr \
+ <trtcm 3M 32K 10M 64K <mark 0x48><mark 0x50><mark 0x58> colorblind>
+conditioner pvc1 af3x_cdnr \
+ <trtcm 3M 32K 10M 64K <mark 0x68><mark 0x70><mark 0x78> colorblind>
+# tswtcm
+conditioner pvc1 af4x_cdnr \
+ <tswtcm 3M 10M 500 <mark 0x88><mark 0x90><mark 0x98>>
+
+ filter pvc1 af1x_cdnr 0 0 172.16.4.176 0 0
+ filter pvc1 af2x_cdnr 0 0 172.16.4.177 0 0
+ filter pvc1 af3x_cdnr 0 0 172.16.4.178 0 0
+ filter pvc1 af4x_cdnr 0 0 172.16.4.179 0 0
+
+#
+# output interface
+#
+interface pvc0 bandwidth 45M cbq
+class cbq pvc0 root_class NULL pbandwidth 100
+class cbq pvc0 def_class root_class borrow pbandwidth 86 default
+#
+# EF class
+#
+class cbq pvc0 ef_class root_class pbandwidth 14 priority 5
+ filter pvc0 ef_class 0 0 0 0 0 tos 0xb8 tosmask 0xfc
+#
+# AF classes
+#
+# rio threshold values
+rio 40 50 10 20 30 10 5 15 10
+#
+class cbq pvc0 af1x_class def_class borrow pbandwidth 20 rio
+class cbq pvc0 af2x_class def_class borrow pbandwidth 20 rio
+class cbq pvc0 af3x_class def_class borrow pbandwidth 20 rio cleardscp
+class cbq pvc0 af4x_class def_class borrow pbandwidth 20 rio
+
+ filter pvc0 af1x_class 0 0 0 0 0 tos 0x20 tosmask 0xe4
+ filter pvc0 af2x_class 0 0 0 0 0 tos 0x40 tosmask 0xe4
+ filter pvc0 af3x_class 0 0 0 0 0 tos 0x60 tosmask 0xe4
+ filter pvc0 af4x_class 0 0 0 0 0 tos 0x80 tosmask 0xe4
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/cbq.lo0 b/usr.sbin/altq/altqd/altq.conf.samples/cbq.lo0
new file mode 100644
index 00000000000..ba519ee1fa9
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/cbq.lo0
@@ -0,0 +1,12 @@
+interface lo0 bandwidth 300M cbq
+class cbq lo0 root_class NULL pbandwidth 100
+class cbq lo0 def_class root_class borrow pbandwidth 95 default
+class cbq lo0 tcp_class def_class pbandwidth 10
+ filter lo0 tcp_class 0 0 0 0 6
+class cbq lo0 udp_class def_class pbandwidth 10
+ filter lo0 udp_class 0 0 0 0 17
+
+# filters for ipv6
+# filter6 lo0 tcp_class ::0 0 ::0 0 6
+# filter6 lo0 udp_class ::0 0 ::0 0 17
+# \ No newline at end of file
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/cbq.sample0 b/usr.sbin/altq/altqd/altq.conf.samples/cbq.sample0
new file mode 100644
index 00000000000..ec662265aa3
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/cbq.sample0
@@ -0,0 +1,57 @@
+#
+# sample configuration file for 1Mbps link
+#
+interface sr0 bandwidth 1M cbq
+class cbq sr0 root NULL pbandwidth 100
+#
+# meta classes
+#
+class cbq sr0 ctl_class root pbandwidth 4 control
+class cbq sr0 def_class root borrow pbandwidth 95 default
+#
+class cbq sr0 bulk def_class borrow pbandwidth 30
+class cbq sr0 misc def_class borrow pbandwidth 30
+class cbq sr0 intr def_class borrow pbandwidth 30
+
+#
+# leaf classes
+#
+
+#
+# bulk data classes
+#
+class cbq sr0 tcp bulk borrow pbandwidth 5 red
+ filter sr0 tcp 0 0 0 0 6 # other tcp
+class cbq sr0 ftp bulk borrow pbandwidth 5 red
+ filter sr0 ftp 0 0 0 20 6 # ftp-data
+ filter sr0 ftp 0 20 0 0 6 # ftp-data
+class cbq sr0 http bulk borrow pbandwidth 5 red
+ filter sr0 http 0 0 0 80 6 # http
+ filter sr0 http 0 80 0 0 6 # http
+class cbq sr0 pop3 bulk borrow pbandwidth 5 red
+ filter sr0 pop3 0 0 0 110 6 # pop3
+ filter sr0 pop3 0 110 0 0 6 # pop3
+class cbq sr0 imap bulk borrow pbandwidth 5 red
+ filter sr0 imap 0 0 0 143 6 # imap
+ filter sr0 imap 0 143 0 0 6 # imap
+#
+# misc (udp) classes
+#
+class cbq sr0 udp misc borrow pbandwidth 5 red
+ filter sr0 udp 0 0 0 0 17 # other udp
+class cbq sr0 rip misc borrow pbandwidth 5 red
+ filter sr0 rip 0 520 0 0 17 # rip
+#
+# interactive classes
+#
+class cbq sr0 dns intr borrow pbandwidth 5 red
+ filter sr0 dns 0 0 0 53 17
+ filter sr0 dns 0 0 0 53 6
+class cbq sr0 telnet intr borrow pbandwidth 5 red
+ filter sr0 telnet 0 0 0 23 6 # telnet
+ filter sr0 telnet 0 23 0 0 6 # telnet
+ filter sr0 telnet 0 0 0 513 6 # rlogin
+ filter sr0 telnet 0 513 0 0 6 # rlogin
+class cbq sr0 ssh intr borrow pbandwidth 5 red
+ filter sr0 ssh 0 0 0 22 6 # ssh
+ filter sr0 ssh 0 22 0 0 6 # ssh
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/cbq.sample1 b/usr.sbin/altq/altqd/altq.conf.samples/cbq.sample1
new file mode 100644
index 00000000000..a1a2c61e17c
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/cbq.sample1
@@ -0,0 +1,18 @@
+interface en0 bandwidth 135M cbq
+class cbq en0 root_class NULL pbandwidth 100
+class cbq en0 def_class root_class borrow pbandwidth 95 default
+# tcp, udp, mcast
+class cbq en0 tcp_class def_class borrow pbandwidth 40
+ filter en0 tcp_class 0 0 0 0 6
+class cbq en0 udp_class def_class borrow pbandwidth 40
+ filter en0 udp_class 0 0 0 0 17
+class cbq en0 mcast_class def_class pbandwidth 10
+ filter en0 mcast_class 224.0.0.0 netmask 0xf0000000 0 0 0 17
+# http ftp
+class cbq en0 http_class tcp_class borrow pbandwidth 10
+ filter en0 http_class 0 0 0 80 6
+class cbq en0 ftp_class tcp_class borrow pbandwidth 10
+ filter en0 ftp_class 0 0 0 20 6
+ filter en0 ftp_class 0 20 0 0 6
+
+
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/cbq.sample2 b/usr.sbin/altq/altqd/altq.conf.samples/cbq.sample2
new file mode 100644
index 00000000000..a7c913257e7
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/cbq.sample2
@@ -0,0 +1,13 @@
+# cbq configuration for vx0 (10Mbps ether)
+# give at least 40% to TCP
+# limit HTTP from network 133.138.1.0 up to 10%
+# other traffic goes into default class
+interface vx0 bandwidth 10M cbq
+class cbq vx0 root_class NULL pbandwidth 100
+class cbq vx0 def_class root_class borrow pbandwidth 95 default
+class cbq vx0 tcp_class def_class borrow pbandwidth 40
+ filter vx0 tcp_class 0 0 0 0 6
+class cbq vx0 csl_class tcp_class pbandwidth 10
+ filter vx0 csl_class 0 0 133.138.1.0 netmask 0xffffff00 80 6
+ filter vx0 csl_class 133.138.1.0 netmask 0xffffff00 0 0 80 6
+
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/cbq.sharing b/usr.sbin/altq/altqd/altq.conf.samples/cbq.sharing
new file mode 100644
index 00000000000..adaf008d757
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/cbq.sharing
@@ -0,0 +1,36 @@
+#
+# this is the setting used for the link-sharing test (Fig. 10, 11)
+# in the ALTQ paper
+#
+#
+# WRR for cbq two Agency link sharing test
+#
+interface pvc0 bandwidth 134M cbq
+class cbq pvc0 root_class NULL priority 0 pbandwidth 100
+class cbq pvc0 ctl_class root_class pbandwidth 0
+#
+# Create Agency A classes
+#
+class cbq pvc0 agencyA root_class priority 2 pbandwidth 70 borrow default
+class cbq pvc0 dataA_class agencyA priority 3 pbandwidth 40 borrow
+class cbq pvc0 vidA_class agencyA priority 4 pbandwidth 30 borrow
+#
+# Create Agency B class
+#
+class cbq pvc0 agencyB root_class priority 2 pbandwidth 30 borrow
+class cbq pvc0 dataB_class agencyB priority 3 pbandwidth 20 borrow
+class cbq pvc0 vidB_class agencyB priority 4 pbandwidth 10 borrow
+#
+# Filters
+#
+# dataA - 172.16.4.176/6760
+# vidA - 172.16.4.176/6761
+#
+filter pvc0 dataA_class 0 0 172.16.3.178 6760 6
+filter pvc0 vidA_class 0 0 172.16.3.178 6761 6
+#
+# data B - 172.16.3.178/6762
+# vid B - 172.16.3.178/6763
+#
+filter pvc0 dataB_class 0 0 172.16.4.176 6760 6
+filter pvc0 vidB_class 0 0 172.16.4.176 6761 6
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/cbq.v6 b/usr.sbin/altq/altqd/altq.conf.samples/cbq.v6
new file mode 100644
index 00000000000..894a26984a5
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/cbq.v6
@@ -0,0 +1,68 @@
+#
+# CBQ (for IPv6) configuration for the WIDE Camp'98 Fall
+#
+interface sr0 bandwidth 128K cbq
+class cbq sr0 root NULL pbandwidth 100
+#
+# meta classes
+#
+class cbq sr0 ctl_class root pbandwidth 4 control
+class cbq sr0 def_class root borrow pbandwidth 95 default
+#
+class cbq sr0 bulk def_class borrow pbandwidth 40
+class cbq sr0 misc def_class borrow pbandwidth 25
+class cbq sr0 intr def_class borrow pbandwidth 25
+
+#
+# leaf classes
+#
+
+#
+# bulk data classes
+#
+class cbq sr0 tcp bulk borrow pbandwidth 10 red
+ filter6 sr0 tcp ::0 0 ::0 0 6 # other tcp
+class cbq sr0 ftp bulk borrow pbandwidth 10 red
+ filter6 sr0 ftp ::0 0 ::0 20 6 # ftp-data
+ filter6 sr0 ftp ::0 20 ::0 0 6 # ftp-data
+class cbq sr0 http bulk borrow pbandwidth 10 red
+ filter6 sr0 http ::0 0 ::0 80 6 # http
+ filter6 sr0 http ::0 80 ::0 0 6 # http
+ filter6 sr0 http ::0 0 ::0 8080 6 # http proxy
+ filter6 sr0 http ::0 8080 ::0 0 6 # http proxy
+ filter6 sr0 http ::0 0 ::0 3128 6 # squid
+ filter6 sr0 http ::0 3128 ::0 0 6 # squid
+class cbq sr0 pop3 bulk borrow pbandwidth 10 red
+ filter6 sr0 pop3 ::0 0 ::0 110 6 # pop3
+ filter6 sr0 pop3 ::0 110 ::0 0 6 # pop3
+
+#
+# misc (udp, ipv4) classes
+#
+class cbq sr0 udp misc borrow pbandwidth 5 red
+ filter6 sr0 udp ::0 0 ::0 0 17 # other udp
+class cbq sr0 cbq misc borrow pbandwidth 5 red
+ filter6 sr0 cbq ::0 7289 ::0 0 17 # cbq monitor
+ filter6 sr0 cbq ::0 7291 ::0 0 17 # cbq monitor
+class cbq sr0 rip misc borrow pbandwidth 5 red
+ filter6 sr0 rip ::0 520 ::0 0 17 # rip
+ filter6 sr0 rip ::0 0 ::0 520 17 # rip
+class cbq sr0 ipv4 misc borrow pbandwidth 10 red
+ filter sr0 ipv4 0 0 0 0 0 # ipv4
+
+#
+# interactive classes
+#
+class cbq sr0 dns intr borrow pbandwidth 5 red
+ filter6 sr0 dns ::0 0 ::0 53 17 # dns (udp)
+ filter6 sr0 dns ::0 53 ::0 0 17 # dns (udp)
+ filter6 sr0 dns ::0 0 ::0 53 6 # dns (tcp)
+ filter6 sr0 dns ::0 53 ::0 0 6 # dns (tcp)
+class cbq sr0 telnet intr borrow pbandwidth 10 red
+ filter6 sr0 telnet ::0 0 ::0 23 6 # telnet
+ filter6 sr0 telnet ::0 23 ::0 0 6 # telnet
+ filter6 sr0 telnet ::0 0 ::0 513 6 # rlogin
+ filter6 sr0 telnet ::0 513 ::0 0 6 # rlogin
+class cbq sr0 ssh intr borrow pbandwidth 10 red
+ filter6 sr0 ssh ::0 0 ::0 22 6 # ssh
+ filter6 sr0 ssh ::0 22 ::0 0 6 # ssh
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/cbq.widecamp b/usr.sbin/altq/altqd/altq.conf.samples/cbq.widecamp
new file mode 100644
index 00000000000..206cd58b1fe
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/cbq.widecamp
@@ -0,0 +1,94 @@
+#
+# cbq config for wide-camp (98 spring)
+# 384kbps line for more than 250 people
+#
+interface sr0 bandwidth 384000 cbq
+class cbq sr0 root NULL priority 0 pbandwidth 100
+#
+# meta classes
+#
+class cbq sr0 ctl_class root priority 3 pbandwidth 4 maxburst 4 control
+class cbq sr0 def_class root borrow priority 2 pbandwidth 60 default
+#
+class cbq sr0 bulk def_class borrow priority 0 pbandwidth 20
+class cbq sr0 misc def_class borrow priority 0 pbandwidth 12
+class cbq sr0 intr def_class borrow priority 0 pbandwidth 24
+
+#
+# leaf classes
+#
+
+#
+# bulk data classes
+#
+class cbq sr0 tcp bulk borrow priority 3 pbandwidth 4 maxburst 8 red
+ filter sr0 tcp 0 0 0 0 6 # other tcp
+class cbq sr0 ftp bulk borrow priority 3 pbandwidth 4 maxburst 8 red
+ filter sr0 ftp 0 0 0 20 6 # ftp-data
+ filter sr0 ftp 0 20 0 0 6 # ftp-data
+class cbq sr0 http bulk borrow priority 3 pbandwidth 4 maxburst 8 red
+ filter sr0 http 0 0 0 80 6 # http
+ filter sr0 http 0 80 0 0 6 # http
+ filter sr0 http 0 0 0 8080 6 # cache
+ filter sr0 http 0 8080 0 0 6 # cache
+ filter sr0 http 0 0 0 443 6 # https
+ filter sr0 http 0 443 0 0 6 # https
+class cbq sr0 pop3 bulk borrow priority 3 pbandwidth 4 maxburst 8 red
+ filter sr0 pop3 0 0 0 110 6 # pop3
+ filter sr0 pop3 0 110 0 0 6 # pop3
+class cbq sr0 imap bulk borrow priority 3 pbandwidth 4 maxburst 8 red
+ filter sr0 imap 0 0 0 143 6 # imap
+ filter sr0 imap 0 143 0 0 6 # imap
+#
+# misc (udp) classes
+#
+class cbq sr0 udp misc borrow priority 3 pbandwidth 4 maxburst 8 red
+ filter sr0 udp 0 0 0 0 17 # other udp
+class cbq sr0 cbq misc borrow priority 3 pbandwidth 4 maxburst 8 red
+ filter sr0 cbq 224.8.8.0 0 0 0 17 # cbq traffic monitor
+class cbq sr0 rip misc borrow priority 3 pbandwidth 4 maxburst 8 red
+ filter sr0 rip 0 520 0 0 17 # rip
+#
+# interactive classes
+#
+class cbq sr0 dns intr borrow priority 5 pbandwidth 4 maxburst 8 red
+ filter sr0 dns 0 0 0 53 17
+ filter sr0 dns 0 0 0 53 6
+class cbq sr0 telnet intr borrow priority 4 pbandwidth 4 maxburst 8 red ecn
+ filter sr0 telnet 0 0 0 23 6 # telnet
+ filter sr0 telnet 0 23 0 0 6 # telnet
+ filter sr0 telnet 0 0 0 513 6 # rlogin
+ filter sr0 telnet 0 513 0 0 6 # rlogin
+class cbq sr0 ssh intr borrow priority 4 pbandwidth 4 maxburst 8 red ecn
+ filter sr0 ssh 0 0 0 22 6 # ssh
+ filter sr0 ssh 0 22 0 0 6 # ssh
+class cbq sr0 ipv6 intr borrow priority 4 pbandwidth 4 maxburst 8 red ecn
+ filter sr0 ipv6 0 0 0 0 41 # ipv6
+class cbq sr0 ipsec intr borrow priority 4 pbandwidth 4 maxburst 8 red ecn
+ filter sr0 ipsec 0 0 0 0 50 # ipsec (ah)
+ filter sr0 ipsec 0 0 0 0 51 # ipsec (esp)
+
+## irc for soi staff
+class cbq sr0 irc intr borrow priority 4 pbandwidth 4 maxburst 8 red
+ filter sr0 irc 0 6667 0 0 6 # soi irc
+ filter sr0 irc 0 0 0 6667 6 # soi irc
+#
+# fec
+#
+class cbq sr0 fec root borrow priority 0 pbandwidth 0 maxburst 8 # red
+ filter sr0 fec 0 0 0 0 200 # fec
+#
+# soi video live
+#
+class cbq sr0 soi root priority 0 pbandwidth 32
+#
+class cbq sr0 video soi borrow priority 3 pbandwidth 16 maxburst 8 red
+ filter sr0 video 0 7070 0 0 17 # soi video
+class cbq sr0 voice soi borrow priority 3 pbandwidth 16 maxburst 8 red
+ filter sr0 voice 0 40034 0 0 17 # soi i-phone
+
+#
+# protection against tunnel (including mbone tunnel)
+#
+class cbq sr0 ipip root borrow priority 0 pbandwidth 0
+ filter sr0 ipip 0 0 0 0 4 # ip-ip
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/cdnr.sample b/usr.sbin/altq/altqd/altq.conf.samples/cdnr.sample
new file mode 100644
index 00000000000..fe8f8e4b448
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/cdnr.sample
@@ -0,0 +1,36 @@
+#
+# null interface command
+#
+interface pvc1
+
+#
+# simple dropper
+#
+conditioner pvc1 dropper <drop>
+ filter pvc1 dropper 0 0 172.16.4.173 0 0
+
+#
+# simple marker to clear dscp
+#
+conditioner pvc1 clear_marker <mark 0x0>
+ filter pvc1 clear_marker 0 0 172.16.4.174 0 0
+
+#
+# EF style conditioner (a simple token bucket)
+#
+conditioner pvc1 ef_cdnr <tbmeter 6M 64K <pass><drop>>
+ filter pvc1 ef_cdnr 0 0 172.16.4.176 0 0
+
+#
+# AF style conditioners (trTCM)
+#
+conditioner pvc1 af1x_cdnr \
+ <trtcm 3M 32K 10M 64K <mark 0x28><mark 0x30><mark 0x38> colorblind>
+ filter pvc1 af1x_cdnr 0 0 172.16.4.177 0 0
+
+#
+# color-blind trTCM is equivalent to a dual tokenbucket meter
+#
+conditioner pvc1 dual_tb \
+ <tbmeter 10M 64K <tbmeter 3M 32K <mark 0x28><mark 0x30>><mark 0x38>>
+ filter pvc1 dual_tb 0 0 172.16.4.178 0 0
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/hfsc.diffedge b/usr.sbin/altq/altqd/altq.conf.samples/hfsc.diffedge
new file mode 100644
index 00000000000..cdebb8bfef9
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/hfsc.diffedge
@@ -0,0 +1,59 @@
+#
+# sample diffedge configuration using HFSC
+#
+
+# input interface
+#
+interface pvc1
+#
+# EF conditioner
+#
+conditioner pvc1 ef_cdnr <tbmeter 6M 64K <mark 0xb8><drop>>
+ filter pvc1 ef_cdnr 0 0 172.16.4.174 0 0
+#
+# AF conditioners
+#
+conditioner pvc1 af1x_cdnr \
+ <trtcm 3M 32K 10M 64K <mark 0x28><mark 0x30><mark 0x38> colorblind>
+conditioner pvc1 af2x_cdnr \
+ <trtcm 3M 32K 10M 64K <mark 0x48><mark 0x50><mark 0x58> colorblind>
+conditioner pvc1 af3x_cdnr \
+ <trtcm 3M 32K 10M 64K <mark 0x68><mark 0x70><mark 0x78> colorblind>
+# tswtcm
+conditioner pvc1 af4x_cdnr \
+ <tswtcm 3M 10M 500 <mark 0x88><mark 0x90><mark 0x98>>
+
+ filter pvc1 af1x_cdnr 0 0 172.16.4.176 0 0
+ filter pvc1 af2x_cdnr 0 0 172.16.4.177 0 0
+ filter pvc1 af3x_cdnr 0 0 172.16.4.178 0 0
+ filter pvc1 af4x_cdnr 0 0 172.16.4.179 0 0
+
+#
+# output interface
+#
+interface pvc0 bandwidth 45M hfsc
+class hfsc pvc0 def_class root pshare 10 default
+#
+# EF class
+# real-time: 6Mbps
+# link-sharing: 0%
+#
+class hfsc pvc0 ef_class root grate 6M
+ filter pvc0 ef_class 0 0 0 0 0 tos 0xb8 tosmask 0xfc
+#
+# AF classes
+# real-time: 3Mbps
+# link-sharing: 10% (4.5Mbps)
+#
+# rio threshold values
+rio 40 50 10 20 30 10 5 15 10
+#
+class hfsc pvc0 af1x_class root grate 3M pshare 10 rio
+class hfsc pvc0 af2x_class root grate 3M pshare 10 rio
+class hfsc pvc0 af3x_class root grate 3M pshare 10 rio cleardscp
+class hfsc pvc0 af4x_class root grate 3M pshare 10 rio
+
+ filter pvc0 af1x_class 0 0 0 0 0 tos 0x20 tosmask 0xe4
+ filter pvc0 af2x_class 0 0 0 0 0 tos 0x40 tosmask 0xe4
+ filter pvc0 af3x_class 0 0 0 0 0 tos 0x60 tosmask 0xe4
+ filter pvc0 af4x_class 0 0 0 0 0 tos 0x80 tosmask 0xe4
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/hfsc.grate b/usr.sbin/altq/altqd/altq.conf.samples/hfsc.grate
new file mode 100644
index 00000000000..67f3ebef2f5
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/hfsc.grate
@@ -0,0 +1,23 @@
+#
+# a simple test config for loopback
+#
+
+#
+# interface bandwidth 45Mbps is arbitrary (it doesn't matter as long as
+# it is bigger than the total guaranteed bandwidth)
+#
+interface pvc0 bandwidth 45M hfsc
+#
+# 50% of the excess bandwidth goes to the default class
+#
+class hfsc pvc0 def_class root pshare 50 default
+#
+# TCP 10Mbps guaranteed rate (no excess bandwidth assigned)
+#
+class hfsc pvc0 tcp_class root grate 10M
+ filter pvc0 tcp_class 0 0 0 0 6
+#
+# UDP 5Mbps guaranteed rate (no excess bandwidth assigned)
+#
+class hfsc pvc0 udp_class root grate 5M
+ filter pvc0 udp_class 0 0 0 0 17
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/hfsc.lo0 b/usr.sbin/altq/altqd/altq.conf.samples/hfsc.lo0
new file mode 100644
index 00000000000..789b3baadb8
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/hfsc.lo0
@@ -0,0 +1,23 @@
+#
+# a simple test config for loopback
+#
+
+#
+# interface bandwidth 300Mbps is arbitrary (it doesn't matter as long as
+# it is bigger than the total guaranteed bandwidth)
+#
+interface lo0 bandwidth 300M hfsc
+#
+# 50% of the excess bandwidth goes to the default class
+#
+class hfsc lo0 def_class root pshare 50 default
+#
+# TCP 10Mbps guaranteed rate (no excess bandwidth assigned)
+#
+class hfsc lo0 tcp_class root grate 10M
+ filter lo0 tcp_class 0 0 0 0 6
+#
+# UDP 5Mbps guaranteed rate (no excess bandwidth assigned)
+#
+class hfsc lo0 udp_class root grate 5M
+ filter lo0 udp_class 0 0 0 0 17
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/hfsc.pvc0 b/usr.sbin/altq/altqd/altq.conf.samples/hfsc.pvc0
new file mode 100644
index 00000000000..05c73333781
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/hfsc.pvc0
@@ -0,0 +1,14 @@
+#
+# simple test config for pvc0
+#
+
+#
+interface pvc0 bandwidth 45M hfsc
+#
+class hfsc pvc0 def_class root pshare 60 default
+class hfsc pvc0 shared_class root pshare 40
+class hfsc pvc0 tcp_class shared_class pshare 30
+ filter pvc0 tcp_class 0 0 0 0 6
+class hfsc pvc0 csl_class shared_class pshare 10
+ filter pvc0 csl_class 0 0 172.16.4.176 0 6
+
diff --git a/usr.sbin/altq/altqd/altq.conf.samples/hfsc.share b/usr.sbin/altq/altqd/altq.conf.samples/hfsc.share
new file mode 100644
index 00000000000..2086553f9c0
--- /dev/null
+++ b/usr.sbin/altq/altqd/altq.conf.samples/hfsc.share
@@ -0,0 +1,34 @@
+#
+# a config for hierachical sharing
+#
+
+interface pvc0 bandwidth 45M hfsc
+#
+# 10% of the bandwidth share goes to the default class
+#
+class hfsc pvc0 def_class root pshare 10 default
+#
+# bandwidth share guaranteed rate
+# CMU: 45% 15Mbps
+# MIT: 45% 15Mbps
+#
+class hfsc pvc0 cmu root grate 15M pshare 45
+class hfsc pvc0 mit root grate 15M pshare 45
+#
+# CMU bandwidth share guaranteed rate
+# CS: 20% 10Mbps
+# other: 20% 5Mbps
+#
+class hfsc pvc0 cmu_other cmu grate 10M pshare 20
+ filter pvc0 cmu_other 0 0 0 0 6
+class hfsc pvc0 cmu_cs cmu grate 5M pshare 20
+ filter pvc0 cmu_cs 0 0 172.16.4.176 0 6
+#
+# MIT bandwidth share guaranteed rate
+# CS: 20% 10Mbps
+# other: 20% 5Mbps
+#
+class hfsc pvc0 mit_other mit grate 10M pshare 20
+ filter pvc0 mit_other 0 0 0 0 17
+class hfsc pvc0 mit_cs mit grate 5M pshare 20
+ filter pvc0 mit_cs 0 0 172.16.4.176 0 17
diff --git a/usr.sbin/altq/altqd/altqd.8 b/usr.sbin/altq/altqd/altqd.8
new file mode 100644
index 00000000000..722572cd5df
--- /dev/null
+++ b/usr.sbin/altq/altqd/altqd.8
@@ -0,0 +1,103 @@
+.\" $OpenBSD: altqd.8,v 1.1 2001/06/27 18:23:17 kjc Exp $
+.\" $KAME: altqd.8,v 1.4 2001/04/09 16:26:28 thorpej Exp $
+.\"
+.\" Copyright (C) 2000
+.\" Sony Computer Science Laboratories Inc. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd September 28, 1999
+.Dt ALTQD 8
+.Sh NAME
+.Nm altqd
+.Nd ALTQ daemon
+.Sh SYNOPSIS
+.Nm altqd
+.Op Fl f Ar conf_file
+.Op Fl dvD
+.Sh DESCRIPTION
+.Nm
+is a daemon program that reads a configuration file and then sets up
+the ALTQ state of network interfaces. After configuring the ALTQ
+state,
+.Nm
+will detach and become a daemon.
+
+The signal SIGINT or SIGTERM will shutdown
+.Nm
+, and the signal SIGHUP will restart
+.Nm
+\&.
+.Pp
+The following options are available:
+.Bl -tag -width Fl -offset indent
+.It Fl f
+Specify a configuration file to read instead of the default. The
+default file is
+.Pa /etc/altq.conf
+\&.
+.It Fl d
+Debug mode.
+.Nm
+does not detach and goes into the command mode.
+.It Fl v
+Print debugging information. This option implies
+.Fl d
+\&.
+.It Fl D
+Dummy mode.
+.Nm
+does not issue altq system calls.
+.El
+
+.Sh COMMANDS
+When
+.Fl d
+option is provided,
+.Nm
+goes into the command mode after reading the configuration file and
+setting up the ALTQ state. Each command is a single line, starting
+with the command verb.
+.Pp
+The basic commands are as follows:
+.Bl -tag -width help -offset indent
+.It Sy help Em or Sy ?
+Display a complete list of commands and their syntax.
+.It Sy quit
+Exit.
+.It Sy altq reload
+reload the configuration file and reinitialize ALTQ.
+.It Sy altq Em iface_name Op enable|disable
+Enables or disables ALTQ on the interface named
+.Em iface_name .
+When
+.Nm
+enters the command mode, ALTQ is enabled on all the
+interfaces listed in the configuration file.
+.El
+
+.Sh FILES
+.Pa /etc/altq.conf ,
+.Pa /var/run/altqd.pid
+.Pa /var/run/altq_quip
+.Sh SEE ALSO
+.Xr altq.conf 5
diff --git a/usr.sbin/altq/altqd/altqd.c b/usr.sbin/altq/altqd/altqd.c
new file mode 100644
index 00000000000..2510b064628
--- /dev/null
+++ b/usr.sbin/altq/altqd/altqd.c
@@ -0,0 +1,338 @@
+/* $OpenBSD: altqd.c,v 1.1 2001/06/27 18:23:17 kjc Exp $ */
+/* $KAME: altqd.c,v 1.2 2000/10/18 09:15:15 kjc Exp $ */
+/*
+ * Copyright (C) 1997-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*******************************************************************
+
+ Copyright (c) 1996 by the University of Southern California
+ All rights reserved.
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation in source and binary forms for any purpose and without
+ fee is hereby granted, provided that both the above copyright notice
+ and this permission notice appear in all copies. and that any
+ documentation, advertising materials, and other materials related to
+ such distribution and use acknowledge that the software was developed
+ in part by the University of Southern California, Information
+ Sciences Institute. The name of the University may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+ the suitability of this software for any purpose. THIS SOFTWARE IS
+ PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ Other copyrights might apply to parts of this software and are so
+ noted when applicable.
+
+********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <err.h>
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <altq/altq.h>
+#include "altq_qop.h"
+#include "quip_server.h"
+
+#define ALTQD_PID_FILE "/var/run/altqd.pid"
+
+static int altqd_socket = -1;
+#define MAX_CLIENT 10
+static FILE *client[MAX_CLIENT];
+
+/* for command mode */
+int T; /* Current Thread number */
+FILE *infp; /* Input file pointer */
+char *infile = NULL; /* command input file. stdin if NULL. */
+fd_set fds, t_fds;
+
+#define DEFAULT_DEBUG_MASK 0
+#define DEFAULT_LOGGING_LEVEL LOG_INFO
+
+static void usage(void)
+{
+ fprintf(stderr, "usage: altqd [options]\n");
+ fprintf(stderr, " options:\n");
+ fprintf(stderr, " -f config_file : set config file\n");
+ fprintf(stderr, " -v : verbose (no daemonize)\n");
+ fprintf(stderr, " -d : debug (no daemonize)\n");
+}
+
+static void
+sig_handler(int sig)
+{
+ if (sig == SIGPIPE) {
+ /*
+ * we have lost an API connection.
+ * a subsequent output operation will catch EPIPE.
+ */
+ return;
+ }
+
+ qcmd_destroyall();
+
+ if (sig == SIGHUP) {
+ printf("reinitializing altqd...\n");
+ qcmd_init();
+ return;
+ }
+
+ fprintf(stderr, "Exiting on signal %d\n", sig);
+
+ /* if we have a pid file, remove it */
+ if (daemonize) {
+ unlink(ALTQD_PID_FILE);
+ closelog();
+ }
+
+ if (altqd_socket >= 0) {
+ close(altqd_socket);
+ altqd_socket = -1;
+ }
+
+ exit(0);
+}
+
+int main(int argc, char **argv)
+{
+ int c;
+ int i, maxfd;
+ extern char *optarg;
+
+ m_debug = DEFAULT_DEBUG_MASK;
+ l_debug = DEFAULT_LOGGING_LEVEL;
+
+ while ((c = getopt(argc, argv, "f:vDdl:")) != -1) {
+ switch (c) {
+ case 'f':
+ altqconfigfile = optarg;
+ break;
+
+ case 'D': /* -D => dummy mode */
+ Debug_mode = 1;
+ printf("Debug mode set.\n");
+ break;
+
+ case 'v':
+ l_debug = LOG_DEBUG;
+ m_debug |= DEBUG_ALTQ;
+ daemonize = 0;
+ break;
+
+ case 'd':
+ daemonize = 0;
+ break;
+
+ case 'l':
+ l_debug = atoi(optarg);
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ signal(SIGINT, sig_handler);
+ signal(SIGTERM, sig_handler);
+ signal(SIGHUP, sig_handler);
+ signal(SIGPIPE, sig_handler);
+
+ if (daemonize)
+ openlog("altqd", LOG_PID, LOG_DAEMON);
+
+ if (qcmd_init() != 0) {
+ if (daemonize)
+ closelog();
+ exit(1);
+ }
+
+ /*
+ * open a unix domain socket for altqd clients
+ */
+ for (i = 0; i < MAX_CLIENT; i++)
+ client[i] = NULL;
+ if ((altqd_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ LOG(LOG_ERR, errno, "can't open unix domain socket\n");
+ else {
+ struct sockaddr_un addr;
+
+ unlink(QUIP_PATH);
+ bzero(&addr, sizeof(addr));
+ addr.sun_family = AF_LOCAL;
+ strcpy(addr.sun_path, QUIP_PATH);
+ if (bind(altqd_socket, (struct sockaddr *)&addr,
+ sizeof(addr)) < 0) {
+ LOG(LOG_ERR, errno, "can't bind to %s\n",
+ QUIP_PATH);
+ altqd_socket = -1;
+ }
+ chmod(QUIP_PATH, 0666);
+ if (listen(altqd_socket, SOMAXCONN) < 0) {
+ LOG(LOG_ERR, errno, "can't listen to %s\n",
+ QUIP_PATH);
+ altqd_socket = -1;
+ }
+ }
+
+ if (daemonize) {
+ FILE *fp;
+
+ daemon(0, 0);
+
+ /* save pid to the pid file (/var/tmp/altqd.pid) */
+ if ((fp = fopen(ALTQD_PID_FILE, "w")) != NULL) {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+ else
+ warn("can't open pid file: %s: %s",
+ ALTQD_PID_FILE, strerror(errno));
+ } else {
+ /* interactive mode */
+ if (infile) {
+ if (NULL == (infp = fopen(infile, "r"))) {
+ perror("Cannot open input file");
+ exit(1);
+ }
+ } else {
+ infp = stdin;
+ printf("\nEnter ? or command:\n");
+ printf("altqd %s> ", cur_ifname());
+ }
+ fflush(stdout);
+ }
+
+ /*
+ * go into the command mode.
+ * the code below is taken from rtap of rsvpd.
+ */
+ FD_ZERO(&fds);
+ maxfd = 0;
+ if (infp != NULL) {
+ FD_SET(fileno(infp), &fds);
+ maxfd = MAX(maxfd, fileno(infp) + 1);
+ }
+ if (altqd_socket >= 0) {
+ FD_SET(altqd_socket, &fds);
+ maxfd = MAX(maxfd, altqd_socket + 1);
+ }
+ while (1) {
+ int rc;
+
+ FD_COPY(&fds, &t_fds);
+ rc = select(maxfd, &t_fds, NULL, NULL, NULL);
+ if (rc < 0) {
+ if (errno != EINTR) {
+ perror("select");
+ exit(1);
+ }
+ continue;
+ }
+
+ /*
+ * If there is control input, read the input line,
+ * parse it, and execute.
+ */
+ if (infp != NULL && FD_ISSET(fileno(infp), &t_fds)) {
+ rc = DoCommand(infile, infp);
+ if (rc == 0) {
+ /*
+ * EOF on input. If reading from file,
+ * go to stdin; else exit.
+ */
+ if (infile) {
+ infp = stdin;
+ infile = NULL;
+ printf("\nEnter ? or command:\n");
+ FD_SET(fileno(infp), &fds);
+ } else {
+ LOG(LOG_INFO, 0, "Exiting.\n");
+ (void) qcmd_destroyall();
+ exit(0);
+ }
+ } else if (infp == stdin)
+ printf("altqd %s> ", cur_ifname());
+ fflush(stdout);
+ } else if (altqd_socket >= 0 && FD_ISSET(altqd_socket, &t_fds)) {
+ /*
+ * quip connection request from client via unix
+ * domain socket; get a new socket for this
+ * connection and add it to the select list.
+ */
+ int newsock = accept(altqd_socket, NULL, NULL);
+ if (newsock == -1) {
+ LOG(LOG_ERR, errno, "accept error\n");
+ continue;
+ }
+ FD_SET(newsock, &fds);
+ for (i = 0; i < MAX_CLIENT; i++)
+ if (client[i] == NULL) {
+ client[i] = fdopen(newsock, "r+");
+ break;
+ }
+ maxfd = MAX(maxfd, newsock + 1);
+ } else {
+ /*
+ * check input from a client via unix domain socket
+ */
+ for (i = 0; i <= MAX_CLIENT; i++) {
+ int fd;
+
+ if (client[i] == NULL)
+ continue;
+ fd = fileno(client[i]);
+ if (FD_ISSET(fd, &t_fds)) {
+ if (quip_input(client[i]) != 0 ||
+ fflush(client[i]) != 0) {
+ /* connection closed */
+ fclose(client[i]);
+ client[i] = NULL;
+ FD_CLR(fd, &fds);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/usr.sbin/altq/altqd/libaltq2.c b/usr.sbin/altq/altqd/libaltq2.c
new file mode 100644
index 00000000000..19bdaf33ced
--- /dev/null
+++ b/usr.sbin/altq/altqd/libaltq2.c
@@ -0,0 +1,70 @@
+/* $OpenBSD: libaltq2.c,v 1.1 2001/06/27 18:23:17 kjc Exp $ */
+/*
+ * this file contains functions and variables needed to use libaltq.
+ * since these are defined in rsvpd, they should be separated in order
+ * to link libaltq to rsvpd.
+ */
+#include <sys/param.h>
+
+#include <altq/altq.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <syslog.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "altq_qop.h"
+
+/* from rsvp_main.c */
+char *altqconfigfile = "/etc/altq.conf";
+
+/* from rsvp_global.h */
+int if_num; /* number of phyints */
+int m_debug; /* Debug output control bits */
+int l_debug; /* Logging severity level */
+
+int daemonize = 1;
+
+/* taken from rsvp_debug.c and modified. */
+void
+log_write(int severity, int syserr, const char *format, ...)
+{
+ va_list ap;
+
+#ifdef __STDC__
+ va_start(ap, format);
+#else
+ va_start(ap);
+#endif
+
+ if (severity <= l_debug) {
+ if (!daemonize)
+ vfprintf(stderr, format, ap);
+ else
+ vsyslog(severity, format, ap);
+ }
+
+ va_end(ap);
+
+ if (syserr == 0) {
+ /* Do nothing for now */
+ } else if (syserr < sys_nerr) {
+ if (severity <= l_debug) {
+ if (!daemonize)
+ fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+ else
+ syslog(severity, ": %s", sys_errlist[syserr]);
+ }
+ } else {
+ if (severity <= l_debug) {
+ if (!daemonize)
+ fprintf(stderr, ": errno %d\n", syserr);
+ else
+ syslog(severity, ": errno %d", syserr);
+ }
+ }
+}
diff --git a/usr.sbin/altq/altqstat/Makefile b/usr.sbin/altq/altqstat/Makefile
new file mode 100644
index 00000000000..0d571b3515a
--- /dev/null
+++ b/usr.sbin/altq/altqstat/Makefile
@@ -0,0 +1,16 @@
+# $OpenBSD: Makefile,v 1.1 2001/06/27 18:23:19 kjc Exp $
+# $NetBSD: Makefile,v 1.2 2000/12/16 18:57:35 thorpej Exp $
+
+BINDIR=/usr/bin
+
+PROG= altqstat
+SRCS= altqstat.c quip_client.c qdisc_conf.c qdisc_cbq.c qdisc_hfsc.c \
+ qdisc_cdnr.c qdisc_wfq.c qdisc_fifoq.c qdisc_red.c qdisc_rio.c \
+ qdisc_blue.c qdisc_priq.c
+
+CPPFLAGS+= -DALTQ -I${.CURDIR}/../libaltq
+
+DPADD+= ${LIBCURSES}
+LDADD+= -lcurses
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/altq/altqstat/altqstat.1 b/usr.sbin/altq/altqstat/altqstat.1
new file mode 100644
index 00000000000..f982a1bf153
--- /dev/null
+++ b/usr.sbin/altq/altqstat/altqstat.1
@@ -0,0 +1,98 @@
+.\" $OpenBSD: altqstat.1,v 1.1 2001/06/27 18:23:19 kjc Exp $
+.\" $KAME: altqstat.1,v 1.3 2001/04/09 16:26:29 thorpej Exp $
+.\"
+.\" Copyright (C) 2000
+.\" Sony Computer Science Laboratories Inc. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd December 24, 1999
+.Dt ALTQSTAT 1
+.Os BSD
+.Sh NAME
+.Nm altqstat
+.Nd show altq status
+.Sh SYNOPSIS
+.Nm altqstat
+.Op Fl enrs
+.Op Fl c Ar count
+.Op Fl w Ar wait
+.Op Fl i Ar interface
+.Op Fl I Ar input_interface
+.Sh DESCRIPTION
+The
+.Nm altqstat
+command displays the status of a queueing discipline. The contents
+displayed by
+.Nm
+is specific to each queueing discipline.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl e
+Echo communication with
+.Xr altqd 8
+to standard output. This option is for debugging.
+.It Fl n
+Disable communication with
+.Xr altqd 8 .
+The interface should be explicitly specified.
+.It Fl r
+Enter the raw console mode to talk to
+.Xr altqd 8 .
+This option is for debugging queue information exchange between
+.Nm
+and
+.Xr altqd 8 .
+.It Fl s
+List all interfaces, classes and filters currently installed.
+.It Fl c Ar count
+.Nm
+exits after displaying
+.Ar count
+times. If no repeat
+.Ar count
+is specified, the default is infinity.
+.It Fl w Ar wait
+Pause
+.Ar wait
+seconds between each display. If no repeat
+.Ar wait
+interval is specified, the default is 5 seconds.
+.It Fl i Ar interface
+Show information about the specified interface. If no
+.Ar interface
+is specified, the default interface is the first interface returned
+from
+.Xr altqd 8 .
+.It Fl I Ar input_interface
+Show information about the specified input interface. This option is
+used to specify
+.Em traffic conditioner
+at an input interface.
+.El
+
+.Sh FILES
+.Pa /var/run/altq_quip
+.Sh SEE ALSO
+.Xr altqd 8 ,
+.Xr altq.conf 5
diff --git a/usr.sbin/altq/altqstat/altqstat.c b/usr.sbin/altq/altqstat/altqstat.c
new file mode 100644
index 00000000000..5c66b740d79
--- /dev/null
+++ b/usr.sbin/altq/altqstat/altqstat.c
@@ -0,0 +1,226 @@
+/* $OpenBSD: altqstat.c,v 1.1 2001/06/27 18:23:19 kjc Exp $ */
+/* $KAME: altqstat.c,v 1.4 2000/12/03 05:44:19 kawa Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/fcntl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <err.h>
+#ifndef NO_CURSES
+#include <curses.h>
+#endif
+
+#include "quip_client.h"
+#include "altqstat.h"
+
+#define DEV_PATH "/dev/altq"
+
+int qdiscfd = -1;
+int show_config = 0;
+int interval = 5;
+int no_server = 0;
+char *interface = NULL;
+char *qdisc_name = NULL;
+
+stat_loop_t *stat_loop;
+
+static void sig_handler(int sig);
+static void usage(void);
+
+static void
+sig_handler(int sig)
+{
+ fprintf(stderr, "Exiting on signal %d\n", sig);
+
+ close(qdiscfd); /* close altq device */
+ quip_closeserver(); /* clocse socket to altqd */
+#ifndef NO_CURSES
+ if (qdisc_name != NULL && strcmp(qdisc_name, "wfq") == 0)
+ endwin(); /* wfqstat uses curses */
+#endif
+ exit(0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: altqstat [-enrs] [-c count] [-w wait] [-i interface|-I input_interface]\n");
+ exit(1);
+}
+
+int
+main (int argc, char **argv)
+{
+ int ch, raw_mode = 0;
+ int qtype;
+ int count = 0;
+ char device[64], qname[64], input[32];
+
+ while ((ch = getopt(argc, argv, "I:c:ei:nrsw:")) != -1) {
+ switch (ch) {
+ case 'I':
+ sprintf(input, "_%s", optarg);
+ interface = input;
+ break;
+ case 'c':
+ count = atoi(optarg);
+ break;
+ case 'e':
+ quip_echo = 1;
+ break;
+ case 'i':
+ interface = optarg;
+ break;
+ case 'n':
+ no_server = 1;
+ break;
+ case 'r':
+ raw_mode = 1;
+ quip_echo = 1;
+ break;
+ case 's':
+ show_config = 1;
+ break;
+ case 'w':
+ interval = atoi(optarg);
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ signal(SIGINT, sig_handler);
+ signal(SIGTERM, sig_handler);
+ signal(SIGPIPE, sig_handler);
+
+ if (no_server == 0) {
+ if (quip_openserver() < 0 && interface == NULL)
+ errx(1, "you have to specify interface!");
+ }
+
+ if (raw_mode == 1) {
+ quip_rawmode();
+ quip_closeserver();
+ exit(0);
+ }
+
+ if (show_config) {
+ if (no_server)
+ errx(1, "no server (-n) can't be set for show config (-s)!");
+ quip_printconfig();
+ quip_closeserver();
+ exit(0);
+ }
+
+ interface = quip_selectinterface(interface);
+ if (interface == NULL)
+ errx(1, "no interface found!");
+
+ qtype = ifname2qdisc(interface, qname);
+ if (qtype == 0)
+ errx(1, "altq is not attached on %s!", interface);
+
+ qdisc_name = qname;
+
+ stat_loop = qdisc2stat_loop(qdisc_name);
+ if (stat_loop == NULL)
+ errx(1, "qdisc %s is not supported!", qdisc_name);
+
+ printf("%s: %s on interface %s\n",
+ argv[0], qdisc_name, interface);
+
+ sprintf(device, "%s/%s", DEV_PATH, qdisc_name);
+ if ((qdiscfd = open(device, O_RDONLY)) < 0)
+ err(1, "can't open %s", device);
+
+ (*stat_loop)(qdiscfd, interface, count, interval);
+ /* never returns */
+
+ exit(0);
+}
+
+/* calculate interval in sec */
+double
+calc_interval(struct timeval *cur_time, struct timeval *last_time)
+{
+ double sec;
+
+ sec = (double)(cur_time->tv_sec - last_time->tv_sec) +
+ (double)(cur_time->tv_usec - last_time->tv_usec) / 1000000;
+ return (sec);
+}
+
+
+/* calculate rate in bps */
+double
+calc_rate(u_int64_t new_bytes, u_int64_t last_bytes, double interval)
+{
+ double rate;
+
+ rate = (double)(new_bytes - last_bytes) * 8 / interval;
+ return (rate);
+}
+
+/* calculate packets in second */
+double
+calc_pps(u_int64_t new_pkts, u_int64_t last_pkts, double interval)
+{
+ double pps;
+
+ pps = (double)(new_pkts - last_pkts) / interval;
+ return (pps);
+}
+
+#define R2S_BUFS 8
+
+char *
+rate2str(double rate)
+{
+ char *buf;
+ static char r2sbuf[R2S_BUFS][16]; /* ring bufer for up to R2S_BUFS */
+ static int idx = 0;
+
+ buf = r2sbuf[idx++];
+ if (idx == R2S_BUFS)
+ idx = 0;
+
+ if (rate == 0.0)
+ sprintf(buf, "0");
+ else if (rate >= 1000000.0)
+ sprintf(buf, "%.2fM", rate / 1000000.0);
+ else
+ sprintf(buf, "%.2fK", rate / 1000.0);
+ return (buf);
+}
diff --git a/usr.sbin/altq/altqstat/altqstat.h b/usr.sbin/altq/altqstat/altqstat.h
new file mode 100644
index 00000000000..077d15ddac3
--- /dev/null
+++ b/usr.sbin/altq/altqstat/altqstat.h
@@ -0,0 +1,67 @@
+/* $OpenBSD: altqstat.h,v 1.1 2001/06/27 18:23:19 kjc Exp $ */
+/* $KAME: altqstat.h,v 1.2 2000/10/18 09:15:16 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+typedef void (stat_loop_t)(int fd, const char *ifname,
+ int count, int interval);
+
+struct qdisc_conf {
+ const char *qdisc_name; /* e.g., cbq */
+ int altqtype; /* e.g., ALTQT_CBQ */
+ stat_loop_t *stat_loop;
+};
+
+/*
+ * cast u_int64_t to ull for printf, since type of u_int64_t
+ * is architecture dependent
+ */
+typedef unsigned long long ull;
+
+stat_loop_t cbq_stat_loop;
+stat_loop_t hfsc_stat_loop;
+stat_loop_t cdnr_stat_loop;
+stat_loop_t wfq_stat_loop;
+stat_loop_t fifoq_stat_loop;
+stat_loop_t red_stat_loop;
+stat_loop_t rio_stat_loop;
+stat_loop_t blue_stat_loop;
+stat_loop_t priq_stat_loop;
+
+struct redstats;
+
+void chandle2name(const char *ifname, u_long handle, char *name);
+stat_loop_t *qdisc2stat_loop(const char *qdisc_name);
+int ifname2qdisc(const char *ifname, char *qname);
+double calc_interval(struct timeval *cur_time, struct timeval *last_time);
+double calc_rate(u_int64_t new_bytes, u_int64_t last_bytes, double interval);
+double calc_pps(u_int64_t new_pkts, u_int64_t last_pkts, double interval);
+char *rate2str(double rate);
+int print_redstats(struct redstats *rp);
+int print_riostats(struct redstats *rp);
+
+
+
diff --git a/usr.sbin/altq/altqstat/qdisc_blue.c b/usr.sbin/altq/altqstat/qdisc_blue.c
new file mode 100644
index 00000000000..f540f8b2ddf
--- /dev/null
+++ b/usr.sbin/altq/altqstat/qdisc_blue.c
@@ -0,0 +1,90 @@
+/* $OpenBSD: qdisc_blue.c,v 1.1 2001/06/27 18:23:19 kjc Exp $ */
+/* $KAME: qdisc_blue.c,v 1.2 2000/10/18 09:15:16 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <altq/altq.h>
+#include <altq/altq_blue.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <err.h>
+
+#include "altqstat.h"
+
+void
+blue_stat_loop(int fd, const char *ifname, int count, int interval)
+{
+ struct blue_stats blue_stats;
+ struct timeval cur_time, last_time;
+ u_int64_t last_bytes;
+ double sec;
+ int cnt = count;
+
+ strcpy(blue_stats.iface.blue_ifname, ifname);
+
+ gettimeofday(&last_time, NULL);
+ last_time.tv_sec -= interval;
+ last_bytes = 0;
+
+ while (count == 0 || cnt-- > 0) {
+
+ if (ioctl(fd, BLUE_GETSTATS, &blue_stats) < 0)
+ err(1, "ioctl BLUE_GETSTATS");
+
+ gettimeofday(&cur_time, NULL);
+ sec = calc_interval(&cur_time, &last_time);
+
+ printf(" q_len:%d , q_limit:%d, q_pmark: %d\n",
+ blue_stats.q_len, blue_stats.q_limit,
+ blue_stats.q_pmark);
+ printf(" xmit: %llu pkts, drop: %llu pkts (forced: %llu, early: %llu)\n",
+ (ull)blue_stats.xmit_packets,
+ (ull)blue_stats.drop_packets,
+ (ull)blue_stats.drop_forced,
+ (ull)blue_stats.drop_unforced);
+ if (blue_stats.marked_packets != 0)
+ printf(" marked: %llu\n",
+ (ull)blue_stats.marked_packets);
+ printf(" throughput: %sbps\n",
+ rate2str(calc_rate(blue_stats.xmit_bytes,
+ last_bytes, sec)));
+
+ last_bytes = blue_stats.xmit_bytes;
+ last_time = cur_time;
+ sleep(interval);
+ }
+}
diff --git a/usr.sbin/altq/altqstat/qdisc_cbq.c b/usr.sbin/altq/altqstat/qdisc_cbq.c
new file mode 100644
index 00000000000..7bf6b72d90a
--- /dev/null
+++ b/usr.sbin/altq/altqstat/qdisc_cbq.c
@@ -0,0 +1,160 @@
+/* $OpenBSD: qdisc_cbq.c,v 1.1 2001/06/27 18:23:20 kjc Exp $ */
+/* $KAME: qdisc_cbq.c,v 1.3 2000/12/29 06:39:27 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <altq/altq.h>
+#include <altq/altq_cbq.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <err.h>
+
+#include "quip_client.h"
+#include "altqstat.h"
+
+#define NCLASSES 64
+
+#ifndef RM_FILTER_GAIN
+#define RM_FILTER_GAIN 5 /* log2 of gain, e.g., 5 => 31/32 */
+#endif
+#ifndef RM_POWER
+#define RM_POWER (1 << RM_FILTER_GAIN)
+#endif
+
+void
+cbq_stat_loop(int fd, const char *ifname, int count, int interval)
+{
+ class_stats_t stats1[NCLASSES], stats2[NCLASSES];
+ char clnames[NCLASSES][128];
+ u_long clhandles[NCLASSES];
+ struct cbq_getstats get_stats;
+ class_stats_t *sp, *lp, *new, *last, *tmp;
+ struct timeval cur_time, last_time;
+ int i;
+ double flow_bps, sec;
+ int cnt = count;
+
+ strcpy(get_stats.iface.cbq_ifacename, ifname);
+ new = &stats1[0];
+ last = &stats2[0];
+
+ for (i = 0; i < NCLASSES; i++)
+ clhandles[i] = NULL_CLASS_HANDLE;
+
+ while (count == 0 || cnt-- > 0) {
+ get_stats.nclasses = NCLASSES;
+ get_stats.stats = new;
+ if (ioctl(fd, CBQ_GETSTATS, &get_stats) < 0)
+ err(1, "ioctl CBQ_GETSTATS");
+
+ gettimeofday(&cur_time, NULL);
+ sec = calc_interval(&cur_time, &last_time);
+
+ for (i=0; i<get_stats.nclasses; i++) {
+ sp = &new[i];
+ lp = &last[i];
+
+ if (sp->handle != clhandles[i]) {
+ quip_chandle2name(ifname, sp->handle,
+ clnames[i]);
+ clhandles[i] = sp->handle;
+ continue;
+ }
+
+ switch (sp->handle) {
+ case ROOT_CLASS_HANDLE:
+ printf("Root Class for Interface %s: %s\n",
+ ifname, clnames[i]);
+ break;
+ case DEFAULT_CLASS_HANDLE:
+ printf("Default Class for Interface %s: %s\n",
+ ifname, clnames[i]);
+ break;
+ case CTL_CLASS_HANDLE:
+ printf("Ctl Class for Interface %s: %s\n",
+ ifname, clnames[i]);
+ break;
+ default:
+ printf("Class %d on Interface %s: %s\n",
+ sp->handle, ifname, clnames[i]);
+ break;
+ }
+
+ flow_bps = 8.0 / (double)sp->ns_per_byte
+ * 1000*1000*1000;
+
+ printf("\tpriority: %d depth: %d",
+ sp->priority, sp->depth);
+ printf(" offtime: %d [us] wrr_allot: %d bytes\n",
+ sp->offtime, sp->wrr_allot);
+ printf("\tnsPerByte: %d", sp->ns_per_byte);
+ printf("\t(%sbps),", rate2str(flow_bps));
+ printf("\tMeasured: %s [bps]\n",
+ rate2str(calc_rate(sp->xmit_cnt.bytes,
+ lp->xmit_cnt.bytes, sec)));
+ printf("\tpkts: %llu,\tbytes: %llu\n",
+ (ull)sp->xmit_cnt.packets,
+ (ull)sp->xmit_cnt.bytes);
+ printf("\tovers: %u,\toveractions: %u\n",
+ sp->over, sp->overactions);
+ printf("\tborrows: %u,\tdelays: %u\n",
+ sp->borrows, sp->delays);
+ printf("\tdrops: %llu,\tdrop_bytes: %llu\n",
+ (ull)sp->drop_cnt.bytes,
+ (ull)sp->drop_cnt.bytes);
+ if (sp->qtype == Q_RED)
+ print_redstats(sp->red);
+ else if (sp->qtype == Q_RIO)
+ print_riostats(sp->red);
+
+ printf("\tQCount: %d,\t(qmax: %d)\n",
+ sp->qcnt, sp->qmax);
+ printf("\tAvgIdle: %d [us],\t(maxidle: %d minidle: %d [us])\n",
+ sp->avgidle >> RM_FILTER_GAIN,
+ sp->maxidle >> RM_FILTER_GAIN,
+ sp->minidle / RM_POWER);
+ }
+
+ /* swap the buffer pointers */
+ tmp = last;
+ last = new;
+ new = tmp;
+
+ last_time = cur_time;
+ sleep(interval);
+ }
+}
diff --git a/usr.sbin/altq/altqstat/qdisc_cdnr.c b/usr.sbin/altq/altqstat/qdisc_cdnr.c
new file mode 100644
index 00000000000..8b5e525ae26
--- /dev/null
+++ b/usr.sbin/altq/altqstat/qdisc_cdnr.c
@@ -0,0 +1,153 @@
+/* $OpenBSD: qdisc_cdnr.c,v 1.1 2001/06/27 18:23:20 kjc Exp $ */
+/* $KAME: qdisc_cdnr.c,v 1.3 2000/10/18 09:15:16 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <altq/altq.h>
+#include <altq/altq_cdnr.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <err.h>
+
+#include "quip_client.h"
+#include "altqstat.h"
+
+#define NELEMENTS 64
+#define MAX_PROB (128*1024)
+
+static char *element_names[] = { "none", "top", "element", "tbmeter", "trtcm",
+ "tswtcm" };
+static char *tbmprof_names[] = { "in: ", "out: " };
+static char *tcmprof_names[] = { "green: ", "yellow:", "red: " };
+
+void
+cdnr_stat_loop(int fd, const char *ifname, int count, int interval)
+{
+ struct tce_stats stats1[NELEMENTS], stats2[NELEMENTS];
+ char cdnrnames[NELEMENTS][128];
+ struct cdnr_get_stats get_stats;
+ struct tce_stats *sp, *lp, *new, *last, *tmp;
+ struct timeval cur_time, last_time;
+ double sec;
+ char **profile_names, _ifname[32];
+ int i, j, nprofile;
+ int cnt = count;
+
+ if (ifname[0] == '_')
+ ifname++;
+ sprintf(_ifname, "_%s", ifname);
+
+ strcpy(get_stats.iface.cdnr_ifname, ifname);
+ new = &stats1[0];
+ last = &stats2[0];
+
+ for (i = 0; i < NELEMENTS; i++)
+ stats1[i].tce_handle = stats2[i].tce_handle = CDNR_NULL_HANDLE;
+
+ while (count == 0 || cnt-- > 0) {
+ get_stats.nskip = 0;
+ get_stats.nelements = NELEMENTS;
+ get_stats.tce_stats = new;
+
+ if (ioctl(fd, CDNR_GETSTATS, &get_stats) < 0)
+ err(1, "ioctl CDNR_GETSTATS");
+
+ gettimeofday(&cur_time, NULL);
+ sec = calc_interval(&cur_time, &last_time);
+
+ printf("actions:\n");
+ printf(" pass:%llu drop:%llu mark:%llu next:%llu return:%llu none:%llu\n",
+ (ull)get_stats.cnts[TCACODE_PASS].packets,
+ (ull)get_stats.cnts[TCACODE_DROP].packets,
+ (ull)get_stats.cnts[TCACODE_MARK].packets,
+ (ull)get_stats.cnts[TCACODE_NEXT].packets,
+ (ull)get_stats.cnts[TCACODE_RETURN].packets,
+ (ull)get_stats.cnts[TCACODE_NONE].packets);
+
+ for (i = 0; i < get_stats.nelements; i++) {
+ sp = &new[i];
+ lp = &last[i];
+
+ if (sp->tce_handle != lp->tce_handle) {
+ quip_chandle2name(_ifname, sp->tce_handle,
+ cdnrnames[i]);
+ continue;
+ }
+
+ switch (sp->tce_type) {
+ case TCETYPE_TBMETER:
+ nprofile = 2;
+ profile_names = tbmprof_names;
+ break;
+ case TCETYPE_TRTCM:
+ case TCETYPE_TSWTCM:
+ nprofile = 3;
+ profile_names = tcmprof_names;
+ break;
+ default:
+ profile_names = tbmprof_names; /* silence cc */
+ nprofile = 0;
+ }
+
+ if (nprofile == 0)
+ continue;
+
+ printf("[%s: %s] handle:%#lx\n",
+ element_names[sp->tce_type], cdnrnames[i],
+ sp->tce_handle);
+ for (j = 0; j < nprofile; j++) {
+ printf(" %s %10llu pkts %16llu bytes (%sbps)\n",
+ profile_names[j],
+ (ull)sp->tce_cnts[j].packets,
+ (ull)sp->tce_cnts[j].bytes,
+ rate2str(
+ calc_rate(sp->tce_cnts[j].bytes,
+ lp->tce_cnts[j].bytes,
+ sec)));
+ }
+ }
+ printf("\n");
+
+ /* swap the buffer pointers */
+ tmp = last;
+ last = new;
+ new = tmp;
+
+ last_time = cur_time;
+ sleep(interval);
+ }
+}
diff --git a/usr.sbin/altq/altqstat/qdisc_conf.c b/usr.sbin/altq/altqstat/qdisc_conf.c
new file mode 100644
index 00000000000..e574d7a1af1
--- /dev/null
+++ b/usr.sbin/altq/altqstat/qdisc_conf.c
@@ -0,0 +1,107 @@
+/* $KAME: qdisc_conf.c,v 1.3 2000/10/18 09:15:16 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/ioctl.h>
+#endif
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <altq/altq.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <err.h>
+
+#include "altqstat.h"
+
+#define ALTQ_DEVICE "/dev/altq/altq"
+
+struct qdisc_conf qdisc_table[] = {
+ {"cbq", ALTQT_CBQ, cbq_stat_loop},
+ {"hfsc", ALTQT_HFSC, hfsc_stat_loop},
+ {"cdnr", ALTQT_CDNR, cdnr_stat_loop},
+ {"wfq", ALTQT_WFQ, wfq_stat_loop},
+ {"fifoq", ALTQT_FIFOQ, fifoq_stat_loop},
+ {"red", ALTQT_RED, red_stat_loop},
+ {"rio", ALTQT_RIO, rio_stat_loop},
+ {"blue", ALTQT_BLUE, blue_stat_loop},
+ {"priq", ALTQT_PRIQ, priq_stat_loop},
+ {NULL, 0, NULL}
+};
+
+stat_loop_t *
+qdisc2stat_loop(const char *qdisc_name)
+{
+ struct qdisc_conf *stat;
+
+ for (stat = qdisc_table; stat->qdisc_name != NULL; stat++)
+ if (strcmp(stat->qdisc_name, qdisc_name) == 0)
+ return (stat->stat_loop);
+ return (NULL);
+}
+
+int
+ifname2qdisc(const char *ifname, char *qname)
+{
+ struct altqreq qtypereq;
+ int fd, qtype = 0;
+
+ if (ifname[0] == '_') {
+ /* input interface */
+ if (qname != NULL)
+ strcpy(qname, "cdnr");
+ return (ALTQT_CDNR);
+ }
+
+ strcpy(qtypereq.ifname, ifname);
+ if ((fd = open(ALTQ_DEVICE, O_RDONLY)) < 0) {
+ warn("can't open %s", ALTQ_DEVICE);
+ return (0);
+ }
+ if (ioctl(fd, ALTQGTYPE, &qtypereq) < 0) {
+ warn("ALTQGQTYPE");
+ return (0);
+ }
+ close(fd);
+
+ if (qname != NULL) {
+ struct qdisc_conf *stat;
+
+ qtype = qtypereq.arg;
+ for (stat = qdisc_table; stat->qdisc_name != NULL; stat++)
+ if (stat->altqtype == qtype)
+ strcpy(qname, stat->qdisc_name);
+ }
+
+ return (qtype);
+}
+
diff --git a/usr.sbin/altq/altqstat/qdisc_fifoq.c b/usr.sbin/altq/altqstat/qdisc_fifoq.c
new file mode 100644
index 00000000000..bea260c40b7
--- /dev/null
+++ b/usr.sbin/altq/altqstat/qdisc_fifoq.c
@@ -0,0 +1,85 @@
+/* $KAME: qdisc_fifoq.c,v 1.3 2000/10/18 09:15:16 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <altq/altq.h>
+#include <altq/altq_fifoq.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <err.h>
+
+#include "altqstat.h"
+
+void
+fifoq_stat_loop(int fd, const char *ifname, int count, int interval)
+{
+ struct fifoq_getstats get_stats;
+ struct timeval cur_time, last_time;
+ u_int64_t last_bytes;
+ double sec;
+ int cnt = count;
+
+ strcpy(get_stats.iface.fifoq_ifname, ifname);
+
+ gettimeofday(&last_time, NULL);
+ last_time.tv_sec -= interval;
+ last_bytes = 0;
+
+ while (count == 0 || cnt-- > 0) {
+
+ if (ioctl(fd, FIFOQ_GETSTATS, &get_stats) < 0)
+ err(1, "ioctl FIFOQ_GETSTATS");
+
+ gettimeofday(&cur_time, NULL);
+ sec = calc_interval(&cur_time, &last_time);
+
+ printf(" q_len:%d q_limit:%d period:%u\n",
+ get_stats.q_len, get_stats.q_limit, get_stats.period);
+ printf(" xmit:%llu pkts (%llu bytes) drop:%llu pkts (%llu bytes)\n",
+ (ull)get_stats.xmit_cnt.packets,
+ (ull)get_stats.xmit_cnt.bytes,
+ (ull)get_stats.drop_cnt.packets,
+ (ull)get_stats.drop_cnt.bytes);
+ printf(" throughput: %sbps\n",
+ rate2str(calc_rate(get_stats.xmit_cnt.bytes,
+ last_bytes, sec)));
+
+ last_bytes = get_stats.xmit_cnt.bytes;
+ last_time = cur_time;
+ sleep(interval);
+ }
+}
diff --git a/usr.sbin/altq/altqstat/qdisc_hfsc.c b/usr.sbin/altq/altqstat/qdisc_hfsc.c
new file mode 100644
index 00000000000..79c6213b662
--- /dev/null
+++ b/usr.sbin/altq/altqstat/qdisc_hfsc.c
@@ -0,0 +1,130 @@
+/* $OpenBSD: qdisc_hfsc.c,v 1.1 2001/06/27 18:23:21 kjc Exp $ */
+/* $KAME: qdisc_hfsc.c,v 1.3 2000/10/18 09:15:17 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <altq/altq.h>
+#include <altq/altq_hfsc.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <err.h>
+
+#include "quip_client.h"
+#include "altqstat.h"
+
+#define NCLASSES 64
+
+void
+hfsc_stat_loop(int fd, const char *ifname, int count, int interval)
+{
+ struct class_stats stats1[NCLASSES], stats2[NCLASSES];
+ char clnames[NCLASSES][128];
+ struct hfsc_class_stats get_stats;
+ struct class_stats *sp, *lp, *new, *last, *tmp;
+ struct timeval cur_time, last_time;
+ int i;
+ double sec;
+ int cnt = count;
+
+ strcpy(get_stats.iface.hfsc_ifname, ifname);
+ new = &stats1[0];
+ last = &stats2[0];
+
+ /* invalidate class ids */
+ for (i=0; i<NCLASSES; i++)
+ last[i].class_id = 999999; /* XXX */
+
+ while (count == 0 || cnt-- > 0) {
+ get_stats.nskip = 0;
+ get_stats.nclasses = NCLASSES;
+ get_stats.stats = new;
+
+ if (ioctl(fd, HFSC_GETSTATS, &get_stats) < 0)
+ err(1, "ioctl HFSC_GETSTATS");
+
+ gettimeofday(&cur_time, NULL);
+ sec = calc_interval(&cur_time, &last_time);
+
+ printf("\ncur_time:%#llx %u classes %u packets in the tree\n",
+ (ull)get_stats.cur_time,
+ get_stats.hif_classes, get_stats.hif_packets);
+
+ for (i=0; i<get_stats.nclasses; i++) {
+ sp = &new[i];
+ lp = &last[i];
+
+ if (sp->class_id != lp->class_id) {
+ quip_chandle2name(ifname, sp->class_handle,
+ clnames[i]);
+ continue;
+ }
+
+ printf("[%2d %s] handle:%#lx [rt %s %ums %s][ls %s %ums %s]\n",
+ sp->class_id, clnames[i], sp->class_handle,
+ rate2str((double)sp->rsc.m1), sp->rsc.d,
+ rate2str((double)sp->rsc.m2),
+ rate2str((double)sp->fsc.m1), sp->fsc.d,
+ rate2str((double)sp->fsc.m2));
+ printf(" measured: %sbps [rt:%s ls:%s] qlen:%2d period:%u\n",
+ rate2str(calc_rate(sp->total, lp->total, sec)),
+ rate2str(calc_rate(sp->cumul, lp->cumul, sec)),
+ rate2str(calc_rate(sp->total - sp->cumul,
+ lp->total - lp->cumul, sec)),
+ sp->qlength, sp->period);
+ printf(" packets:%llu (%llu bytes) drops:%llu\n",
+ (ull)sp->xmit_cnt.packets,
+ (ull)sp->xmit_cnt.bytes,
+ (ull)sp->drop_cnt.packets);
+ printf(" cumul:%#llx total:%#llx\n",
+ (ull)sp->cumul, (ull)sp->total);
+ printf(" vt:%#llx d:%#llx e:%#llx\n",
+ (ull)sp->vt, (ull)sp->d, (ull)sp->e);
+ if (sp->qtype == Q_RED)
+ print_redstats(sp->red);
+ else if (sp->qtype == Q_RIO)
+ print_riostats(sp->red);
+ }
+
+ /* swap the buffer pointers */
+ tmp = last;
+ last = new;
+ new = tmp;
+
+ last_time = cur_time;
+ sleep(interval);
+ }
+}
diff --git a/usr.sbin/altq/altqstat/qdisc_priq.c b/usr.sbin/altq/altqstat/qdisc_priq.c
new file mode 100644
index 00000000000..868407eb836
--- /dev/null
+++ b/usr.sbin/altq/altqstat/qdisc_priq.c
@@ -0,0 +1,117 @@
+/* $OpenBSD: qdisc_priq.c,v 1.1 2001/06/27 18:23:21 kjc Exp $ */
+/* $KAME: qdisc_priq.c,v 1.1 2000/10/18 09:15:17 kjc Exp $ */
+/*
+ * Copyright (C) 2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <altq/altq.h>
+#include <altq/altq_priq.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <err.h>
+
+#include "quip_client.h"
+#include "altqstat.h"
+
+void
+priq_stat_loop(int fd, const char *ifname, int count, int interval)
+{
+ struct class_stats stats1[PRIQ_MAXPRI], stats2[PRIQ_MAXPRI];
+ char clnames[PRIQ_MAXPRI][128];
+ struct priq_class_stats get_stats;
+ struct class_stats *sp, *lp, *new, *last, *tmp;
+ struct timeval cur_time, last_time;
+ int i;
+ double sec;
+ int cnt = count;
+
+ strcpy(get_stats.iface.ifname, ifname);
+ new = &stats1[0];
+ last = &stats2[0];
+
+ /* invalidate class handles */
+ for (i=0; i<PRIQ_MAXPRI; i++)
+ last[i].class_handle = PRIQ_NULLCLASS_HANDLE;
+
+ while (count == 0 || cnt-- > 0) {
+ get_stats.stats = new;
+ get_stats.maxpri = PRIQ_MAXPRI;
+ if (ioctl(fd, PRIQ_GETSTATS, &get_stats) < 0)
+ err(1, "ioctl PRIQ_GETSTATS");
+
+ gettimeofday(&cur_time, NULL);
+ sec = calc_interval(&cur_time, &last_time);
+
+ printf("\n%s:\n", ifname);
+
+ for (i = get_stats.maxpri; i >= 0; i--) {
+ sp = &new[i];
+ lp = &last[i];
+
+ if (sp->class_handle == PRIQ_NULLCLASS_HANDLE)
+ continue;
+
+ if (sp->class_handle != lp->class_handle) {
+ quip_chandle2name(ifname, sp->class_handle,
+ clnames[i]);
+ continue;
+ }
+
+ printf("[%s] handle:%#lx pri:%d\n",
+ clnames[i], sp->class_handle, i);
+ printf(" measured: %sbps qlen:%2d period:%u\n",
+ rate2str(calc_rate(sp->xmitcnt.bytes,
+ lp->xmitcnt.bytes, sec)),
+ sp->qlength, sp->period);
+ printf(" packets:%llu (%llu bytes) drops:%llu\n",
+ (ull)sp->xmitcnt.packets,
+ (ull)sp->xmitcnt.bytes,
+ (ull)sp->dropcnt.packets);
+ if (sp->qtype == Q_RED)
+ print_redstats(sp->red);
+ else if (sp->qtype == Q_RIO)
+ print_riostats(sp->red);
+ }
+
+ /* swap the buffer pointers */
+ tmp = last;
+ last = new;
+ new = tmp;
+
+ last_time = cur_time;
+ sleep(interval);
+ }
+}
diff --git a/usr.sbin/altq/altqstat/qdisc_red.c b/usr.sbin/altq/altqstat/qdisc_red.c
new file mode 100644
index 00000000000..75550bb4e14
--- /dev/null
+++ b/usr.sbin/altq/altqstat/qdisc_red.c
@@ -0,0 +1,114 @@
+/* $OpenBSD: qdisc_red.c,v 1.1 2001/06/27 18:23:21 kjc Exp $ */
+/* $KAME: qdisc_red.c,v 1.2 2000/10/18 09:15:17 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <altq/altq.h>
+#include <altq/altq_red.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <err.h>
+
+#include "altqstat.h"
+
+static int avg_scale = 4096; /* default fixed-point scale */
+
+void
+red_stat_loop(int fd, const char *ifname, int count, int interval)
+{
+ struct red_stats red_stats;
+ struct timeval cur_time, last_time;
+ u_int64_t last_bytes;
+ double sec;
+ int cnt = count;
+
+ strcpy(red_stats.iface.red_ifname, ifname);
+
+ gettimeofday(&last_time, NULL);
+ last_time.tv_sec -= interval;
+ last_bytes = 0;
+
+ while (count == 0 || cnt-- > 0) {
+
+ if (ioctl(fd, RED_GETSTATS, &red_stats) < 0)
+ err(1, "ioctl RED_GETSTATS");
+
+ gettimeofday(&cur_time, NULL);
+ sec = calc_interval(&cur_time, &last_time);
+
+ printf(" weight:%d inv_pmax:%d qthresh:(%d,%d)\n",
+ red_stats.weight, red_stats.inv_pmax,
+ red_stats.th_min, red_stats.th_max);
+ printf(" q_len:%d (avg: %.2f), q_limit:%d\n",
+ red_stats.q_len,
+ ((double)red_stats.q_avg)/(double)avg_scale,
+ red_stats.q_limit);
+ printf(" xmit:%llu pkts, drop:%llu pkts (forced: %u, early: %u)\n",
+ (ull)red_stats.xmit_cnt.packets,
+ (ull)red_stats.drop_cnt.packets,
+ red_stats.drop_forced, red_stats.drop_unforced);
+ if (red_stats.marked_packets != 0)
+ printf(" marked: %u\n", red_stats.marked_packets);
+ printf(" throughput: %sbps\n",
+ rate2str(calc_rate(red_stats.xmit_cnt.bytes,
+ last_bytes, sec)));
+ if (red_stats.fv_alloc > 0) {
+ printf(" flowvalve: alloc:%u flows:%u\n",
+ red_stats.fv_alloc, red_stats.fv_flows);
+ printf(" predrop:%u pass:%u escape:%u\n",
+ red_stats.fv_predrop, red_stats.fv_pass,
+ red_stats.fv_escape);
+ }
+ printf("\n");
+
+ last_bytes = red_stats.xmit_cnt.bytes;
+ last_time = cur_time;
+ sleep(interval);
+ }
+}
+
+int
+print_redstats(struct redstats *rp)
+{
+ printf(" RED q_avg:%.2f xmit:%llu (forced:%u early:%u marked:%u)\n",
+ ((double)rp->q_avg)/(double)avg_scale,
+ (ull)rp->xmit_cnt.packets,
+ rp->drop_forced,
+ rp->drop_unforced,
+ rp->marked_packets);
+ return 0;
+}
diff --git a/usr.sbin/altq/altqstat/qdisc_rio.c b/usr.sbin/altq/altqstat/qdisc_rio.c
new file mode 100644
index 00000000000..a7193ff8aec
--- /dev/null
+++ b/usr.sbin/altq/altqstat/qdisc_rio.c
@@ -0,0 +1,147 @@
+/* $OpenBSD: qdisc_rio.c,v 1.1 2001/06/27 18:23:21 kjc Exp $ */
+/* $KAME: qdisc_rio.c,v 1.3 2001/05/17 08:01:47 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <altq/altq.h>
+#include <altq/altq_red.h>
+#include <altq/altq_rio.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <err.h>
+
+#include "altqstat.h"
+
+static int avg_scale = 4096; /* default fixed-point scale */
+
+void
+rio_stat_loop(int fd, const char *ifname, int count, int interval)
+{
+ struct rio_stats rio_stats;
+ struct timeval cur_time, last_time;
+ u_int64_t last_bytes[3];
+ double sec;
+ int cnt = count;
+
+ bzero(&rio_stats, sizeof(rio_stats));
+ strcpy(rio_stats.iface.rio_ifname, ifname);
+
+ gettimeofday(&last_time, NULL);
+ last_time.tv_sec -= interval;
+
+ while (count == 0 || cnt-- > 0) {
+
+ if (ioctl(fd, RIO_GETSTATS, &rio_stats) < 0)
+ err(1, "ioctl RIO_GETSTATS");
+
+ gettimeofday(&cur_time, NULL);
+ sec = calc_interval(&cur_time, &last_time);
+
+ printf("weight:%d q_limit:%d\n",
+ rio_stats.weight, rio_stats.q_limit);
+
+ printf("\t\t\tLOW DP\t\tMEDIUM DP\t\tHIGH DP\n");
+
+ printf("thresh (prob):\t\t[%d,%d](1/%d)\t[%d,%d](1/%d)\t\t[%d,%d](%d)\n",
+ rio_stats.q_params[0].th_min,
+ rio_stats.q_params[0].th_max,
+ rio_stats.q_params[0].inv_pmax,
+ rio_stats.q_params[1].th_min,
+ rio_stats.q_params[1].th_max,
+ rio_stats.q_params[1].inv_pmax,
+ rio_stats.q_params[2].th_min,
+ rio_stats.q_params[2].th_max,
+ rio_stats.q_params[2].inv_pmax);
+ printf("qlen (avg):\t\t%d (%.2f)\t%d (%.2f)\t\t%d (%.2f)\n",
+ rio_stats.q_len[0],
+ ((double)rio_stats.q_stats[0].q_avg)/(double)avg_scale,
+ rio_stats.q_len[1],
+ ((double)rio_stats.q_stats[1].q_avg)/(double)avg_scale,
+ rio_stats.q_len[2],
+ ((double)rio_stats.q_stats[2].q_avg)/(double)avg_scale);
+ printf("xmit (drop) pkts:\t%llu (%llu)\t\t%llu (%llu)\t\t\t%llu (%llu)\n",
+ (ull)rio_stats.q_stats[0].xmit_cnt.packets,
+ (ull)rio_stats.q_stats[0].drop_cnt.packets,
+ (ull)rio_stats.q_stats[1].xmit_cnt.packets,
+ (ull)rio_stats.q_stats[1].drop_cnt.packets,
+ (ull)rio_stats.q_stats[2].xmit_cnt.packets,
+ (ull)rio_stats.q_stats[2].drop_cnt.packets);
+ printf("(forced:early):\t\t(%u:%u)\t\t(%u:%u)\t\t\t(%u:%u)\n",
+ rio_stats.q_stats[0].drop_forced,
+ rio_stats.q_stats[0].drop_unforced,
+ rio_stats.q_stats[1].drop_forced,
+ rio_stats.q_stats[1].drop_unforced,
+ rio_stats.q_stats[2].drop_forced,
+ rio_stats.q_stats[2].drop_unforced);
+ if (rio_stats.q_stats[0].marked_packets != 0
+ || rio_stats.q_stats[1].marked_packets != 0
+ || rio_stats.q_stats[2].marked_packets != 0)
+ printf("marked:\t\t\t%u\t\t%u\t\t\t%u\n",
+ rio_stats.q_stats[0].marked_packets,
+ rio_stats.q_stats[1].marked_packets,
+ rio_stats.q_stats[2].marked_packets);
+ printf("throughput:\t\t%sbps\t%sbps\t\t%sbps\n\n",
+ rate2str(calc_rate(rio_stats.q_stats[0].xmit_cnt.bytes,
+ last_bytes[0], sec)),
+ rate2str(calc_rate(rio_stats.q_stats[1].xmit_cnt.bytes,
+ last_bytes[1], sec)),
+ rate2str(calc_rate(rio_stats.q_stats[2].xmit_cnt.bytes,
+ last_bytes[2], sec)));
+
+ last_bytes[0] = rio_stats.q_stats[0].xmit_cnt.bytes;
+ last_bytes[1] = rio_stats.q_stats[1].xmit_cnt.bytes;
+ last_bytes[2] = rio_stats.q_stats[2].xmit_cnt.bytes;
+ last_time = cur_time;
+ sleep(interval);
+ }
+}
+
+int
+print_riostats(struct redstats *rp)
+{
+ int dp;
+
+ for (dp = 0; dp < RIO_NDROPPREC; dp++)
+ printf(" RIO[%d] q_avg:%.2f xmit:%llu (forced: %u early:%u marked:%u)\n",
+ dp,
+ ((double)rp[dp].q_avg)/(double)avg_scale,
+ (ull)rp[dp].xmit_cnt.packets,
+ rp[dp].drop_forced,
+ rp[dp].drop_unforced,
+ rp[dp].marked_packets);
+ return 0;
+}
diff --git a/usr.sbin/altq/altqstat/qdisc_wfq.c b/usr.sbin/altq/altqstat/qdisc_wfq.c
new file mode 100644
index 00000000000..f829b27e194
--- /dev/null
+++ b/usr.sbin/altq/altqstat/qdisc_wfq.c
@@ -0,0 +1,167 @@
+/* $KAME: qdisc_wfq.c,v 1.2 2000/10/18 09:15:17 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <altq/altq.h>
+#include <altq/altq_wfq.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <err.h>
+#ifndef NO_CURSES
+#include <curses.h>
+#endif
+
+#include "altqstat.h"
+
+struct wfqinfo {
+ int qid;
+ queue_stats stats;
+ u_quad_t last_bytes;
+ double bps;
+};
+
+#define NTOP 10
+static int ntop = NTOP;
+
+void
+wfq_stat_loop(int fd, const char *ifname, int count, int interval)
+{
+ struct wfq_getstats wfq_stats;
+ struct timeval cur_time, last_time;
+ int i, j, k, nqueues;
+ double sec;
+ struct wfqinfo *qinfo, **top;
+ int cnt = count;
+
+ strcpy(wfq_stats.iface.wfq_ifacename, ifname);
+
+ /*
+ * first, find out how many queues are available
+ */
+ for (i = 0; i < MAX_QSIZE; i++) {
+ wfq_stats.qid = i;
+ if (ioctl(fd, WFQ_GET_STATS, &wfq_stats) < 0)
+ break;
+ }
+ nqueues = i;
+ printf("wfq on %s: %d queues are used\n", ifname, nqueues);
+
+ if ((qinfo = malloc(nqueues * sizeof(struct wfqinfo))) == NULL)
+ err(1, "malloc failed!");
+ if ((top = malloc(ntop * sizeof(struct wfqinfo *))) == NULL)
+ err(1, "malloc failed!");
+
+#ifndef NO_CURSES
+ sleep(2); /* wait a bit before clearing the screen */
+
+ initscr();
+#endif
+
+ gettimeofday(&last_time, NULL);
+ last_time.tv_sec -= interval;
+
+ while (count == 0 || cnt-- > 0) {
+
+ for (j = 0; j < ntop; j++)
+ top[j] = NULL;
+
+ for (i = 0; i < nqueues; i++) {
+ wfq_stats.qid = i;
+ if (ioctl(fd, WFQ_GET_STATS, &wfq_stats) < 0)
+ err(1, "ioctl WFQ_GET_STATS");
+
+ qinfo[i].qid = i;
+ qinfo[i].stats = wfq_stats.stats;
+ }
+
+ gettimeofday(&cur_time, NULL);
+ sec = calc_interval(&cur_time, &last_time);
+
+ /*
+ * calculate the throughput of each queue
+ */
+ for (i = 0; i < nqueues; i++) {
+ qinfo[i].bps = calc_rate(qinfo[i].stats.xmit_cnt.bytes,
+ qinfo[i].last_bytes, sec);
+ qinfo[i].last_bytes = qinfo[i].stats.xmit_cnt.bytes;
+
+ for (j = 0; j < ntop; j++) {
+ if (top[j] == NULL) {
+ top[j] = &qinfo[i];
+ break;
+ }
+ if (top[j]->bps < qinfo[i].bps ||
+ (top[j]->bps == qinfo[i].bps &&
+ top[j]->stats.xmit_cnt.packets <
+ qinfo[i].stats.xmit_cnt.packets)) {
+ for (k = ntop-1; k > j; k--)
+ top[k] = top[k-1];
+ top[j] = &qinfo[i];
+ break;
+ }
+ }
+ }
+
+ /*
+ * display top
+ */
+ printf("[QID] WEIGHT QSIZE(KB) SENT(pkts) (KB) DROP(pkts) (KB) bps\n\r");
+
+ for (j = 0; j < ntop; j++) {
+ if (top[j] != NULL)
+ printf("[%4d] %4d %4d %10llu %14llu %10llu %14llu %9s\n\r",
+ top[j]->qid,
+ top[j]->stats.weight,
+ top[j]->stats.bytes / 1024,
+ (ull)top[j]->stats.xmit_cnt.packets,
+ (ull)top[j]->stats.xmit_cnt.bytes /1024,
+ (ull)top[j]->stats.drop_cnt.packets,
+ (ull)top[j]->stats.drop_cnt.bytes /1024,
+ rate2str(top[j]->bps));
+ else
+ printf("\n");
+ }
+#ifndef NO_CURSES
+ refresh();
+ mvcur(ntop+1, 0, 0, 0);
+#endif
+
+ last_time = cur_time;
+ sleep(interval);
+ }
+
+}
diff --git a/usr.sbin/altq/altqstat/quip_client.c b/usr.sbin/altq/altqstat/quip_client.c
new file mode 100644
index 00000000000..10d4cd16271
--- /dev/null
+++ b/usr.sbin/altq/altqstat/quip_client.c
@@ -0,0 +1,492 @@
+/* $OpenBSD: quip_client.c,v 1.1 2001/06/27 18:23:22 kjc Exp $ */
+/* $KAME: quip_client.c,v 1.2 2000/10/18 09:15:17 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <err.h>
+
+#include "quip_client.h"
+#include "altqstat.h"
+
+/*
+ * quip (queue information protocol) is a http-like protocol
+ * in order to retrieve information from the server.
+ * a unix domain TCP socket "/var/run/altq_quip" is used for
+ * clinet-server style communication.
+ *
+ * there are 2 quip message types: request and response.
+ * request format: (only single-line request message is used at this moment)
+ * request-line
+ *
+ * request-line = <method> <operation>[?<query>] <quip-version>
+ * <method> = GET (only GET is defined at this moment)
+ * <operation> = list | handle-to-name | qdisc | filter
+ * query format is operation dependent but most query takes
+ * <interface> or <class> or <filter>.
+ * <interface> = <if_name>
+ * <class> = <if_name>:<class_path>/<class_name>
+ * <filter> = <if_name>:<class_path>/<class_name>:<filter_name>
+ * "list" operation accepts "*" as a wildcard.
+ *
+ * response format:
+ * status-line
+ * response-headers (0 or more)
+ * <blank line>
+ * body
+ *
+ * status-line = <quip-version> <status-code> <reason phrase>
+ * response-header = Content-Length:<value>
+ *
+ * "Content-Length" specifies the length of the message body.
+ *
+ * example:
+ * to retrieve a list of classes (handle and name) on interface "fxp0":
+ * a request message looks like,
+ * GET list?fxp0:* QUIP/1.0<cr>
+ * a response message looks like,
+ * QUIP/1.0 200 OK<cr>
+ * Content-Length:86<cr>
+ * <cr>
+ * 0000000000 fxp0:/root<cr>
+ * 0xc0d1be00 fxp0:/root/parent<cr>
+ * 0xc0d1ba00 fxp0:/root/parent/child<cr>
+ *
+ * other examples:
+ * list all interfaces, classes, and filters:
+ * GET list QUIP/1.0<cr>
+ * list all interfaces:
+ * GET list?* QUIP/1.0<cr>
+ * list all classes:
+ * GET list?*:* QUIP/1.0<cr>
+ * list all filters:
+ * GET list?*:*:* QUIP/1.0<cr>
+ * convert class handle to class name:
+ * GET handle-to-name?fxp0:0xc0d1be00 QUIP/1.0<cr>
+ * convert filter handle to filter name:
+ * GET handle-to-name?fxp0::0x1000000a QUIP/1.0<cr>
+ */
+
+enum nametype { INTERFACE, CLASS, FILTER, CONDITIONER };
+
+static FILE *server = NULL;
+int quip_echo = 0;
+
+static char *extract_ifname(const char *name);
+
+int
+quip_openserver(void)
+{
+ struct sockaddr_un addr;
+ int fd;
+
+ if ((fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ err(1, "can't open socket");
+
+ bzero(&addr, sizeof(addr));
+ addr.sun_family = AF_LOCAL;
+ strcpy(addr.sun_path, QUIP_PATH);
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "can't talk to altqd!\n"
+ "probably, altqd is not running\n");
+ return (-1);
+ }
+
+ if ((server = fdopen(fd, "r+")) == NULL) {
+ warn("fdopen: can't open stream to the quip server");
+ return (-1);
+ }
+ return (0);
+}
+
+int
+quip_closeserver(void)
+{
+ if (server != NULL)
+ return fclose(server);
+ return (0);
+}
+
+void
+quip_sendrequest(FILE *fp, const char *request)
+{
+ char buf[1024], *cp;
+ int n;
+
+ if ((cp = strstr(request, "QUIP")) == NULL) {
+ cp = strchr(request, '\n');
+ n = cp - request;
+ strncpy(buf, request, n);
+ n += sprintf(buf + n, " QUIP/1.0");
+ strcpy(buf + n, cp);
+ }
+ else
+ strcpy(buf, request);
+
+ if (fputs(buf, fp) != 0)
+ err(1, "fputs");
+ if (fflush(fp) != 0)
+ err(1, "fflush");
+ if (quip_echo) {
+ fputs("<< ", stdout);
+ fputs(buf, stdout);
+ }
+}
+
+/*
+ * recv_response receives a response message from the server
+ * and returns status_code.
+ */
+int
+quip_recvresponse(FILE *fp, char *header, char *body, int *blen)
+{
+ char buf[1024], version[64];
+ int code, resid;
+ int end_of_header = 0;
+
+ if (blen != NULL)
+ *blen = 0;
+ code = 0;
+ resid = 0;
+ while (fgets(buf, 1024, fp) != 0) {
+ if (quip_echo) {
+ fputs("> ", stdout);
+ fputs(buf, stdout);
+ }
+
+ if (!end_of_header) {
+ /* process message header */
+ if (header != NULL)
+ header += sprintf(header, "%s", buf);
+
+ if (code == 0) {
+ /* status line expected */
+ if (buf[0] == '\n') {
+ /* ignore blank lines */
+ }
+ else if (sscanf(buf, "%s %d",
+ version, &code) != 2) {
+ /* can't get result code */
+ fpurge(fp);
+ return (-1);
+ }
+ }
+ else {
+ /* entity header expected */
+ char *field, *cp;
+
+ if (buf[0] == '\n') {
+ /* end of header */
+ end_of_header = 1;
+ if (resid == 0)
+ /* no message body */
+ return (code);
+ }
+
+ cp = buf;
+ field = strsep(&cp, ":");
+ if (strcmp(field, "Content-Length") == 0) {
+ sscanf(cp, "%d", &resid);
+ if (blen != NULL)
+ *blen = resid;
+ }
+ }
+ }
+ else {
+ /* process message body */
+ int len;
+
+ if (body != NULL) {
+ len = sprintf(body, "%s", buf);
+ body += len;
+ }
+ else
+ len = strlen(buf);
+ resid -= len;
+ if (resid <= 0)
+ return (code);
+ }
+ }
+ return (-1);
+}
+
+void
+quip_rawmode(void)
+{
+ char line[1024];
+ int result_code;
+
+ printf(">>>Entering the raw interactive mode to the server:\n\n");
+ if (server == NULL) {
+ printf("No server available!\n");
+ return;
+ }
+
+ while (1) {
+ printf("%% "); fflush(stdout);
+ /* read a line from stdin */
+ if (fgets(line, 1024, stdin) == NULL)
+ break;
+
+ if (line[0] == '\n') {
+ /* if a blank line, echo locally */
+ fputs(line, stdout);
+ continue;
+ }
+ if (line[0] == 'q') {
+ printf("Exit\n");
+ break;
+ }
+
+ /* send the input line to the server */
+ quip_sendrequest(server, line);
+
+ /* get a response message from the server */
+ result_code = quip_recvresponse(server, NULL, NULL, NULL);
+ }
+}
+
+char *
+quip_selectinterface(char *ifname)
+{
+ char buf[8192], *cp;
+ int result_code, len;
+ u_int if_index;
+ static char interface[64];
+
+ if (server == NULL)
+ return (ifname);
+
+ /* get an inferface list from the server */
+ quip_sendrequest(server, "GET list?*\n");
+
+ result_code = quip_recvresponse(server, NULL, buf, &len);
+ if (result_code != 200)
+ errx(1, "can't get interface list");
+
+ cp = buf;
+ while (1) {
+ if (sscanf(cp, "%x %s", &if_index, interface) != 2)
+ break;
+ if (ifname == NULL) {
+ /* if name isn't specified, return the 1st entry */
+ return (interface);
+ }
+ if (strcmp(ifname, interface) == 0)
+ /* found the matching entry */
+
+ return (interface);
+ if ((cp = strchr(cp+1, '\n')) == NULL)
+ break;
+ }
+ errx(1, "can't get interface");
+ return (NULL);
+}
+
+char *
+quip_selectqdisc(char *ifname, char *qdisc_name)
+{
+ char buf[8192], req[256];
+ int result_code, len;
+ static char qdisc[64];
+
+ if (server == NULL) {
+ if (ifname == NULL || qdisc_name == NULL)
+ errx(1, "when disabling server communication,\n"
+ "specify both interface (-i) and qdisc (-q)!");
+ return (qdisc_name);
+ }
+
+ /* get qdisc info from the server */
+ sprintf(req, "GET qdisc?%s\n", ifname);
+ quip_sendrequest(server, req);
+
+ result_code = quip_recvresponse(server, NULL, buf, &len);
+ if (result_code != 200)
+ errx(1, "can't get qdisc info");
+
+ if (sscanf(buf, "%s", qdisc) != 1)
+ errx(1, "can't get qdisc name");
+
+ if (qdisc_name != NULL && strcmp(qdisc, qdisc_name) != 0)
+ errx(1, "qdisc %s on %s doesn't match specified qdisc %s",
+ qdisc, ifname, qdisc_name);
+
+ return (qdisc);
+}
+
+void
+quip_chandle2name(const char *ifname, u_long handle, char *name)
+{
+ char buf[8192], req[256], *cp;
+ int result_code, len;
+
+ name[0] = '\0';
+ if (server == NULL)
+ return;
+
+ /* get class name from the server */
+ sprintf(req, "GET handle-to-name?%s:%#lx\n", ifname, handle);
+ quip_sendrequest(server, req);
+
+ result_code = quip_recvresponse(server, NULL, buf, &len);
+ if (result_code != 200)
+ errx(1, "can't get class name");
+
+ if ((cp = strchr(buf, '\n')) != NULL)
+ *cp = '\0';
+ if ((cp = strrchr(buf, '/')) != NULL)
+ strcpy(name, cp+1);
+}
+
+void
+quip_printqdisc(const char *ifname)
+{
+ char buf[8192], req[256], *cp;
+ int result_code, len;
+
+ if (server == NULL) {
+ printf("No server available!\n");
+ return;
+ }
+
+ /* get qdisc info from the server */
+ sprintf(req, "GET qdisc?%s\n", ifname);
+ quip_sendrequest(server, req);
+
+ result_code = quip_recvresponse(server, NULL, buf, &len);
+ if (result_code != 200)
+ errx(1, "can't get qdisc info");
+
+ /* replace newline by space */
+ cp = buf;
+ while ((cp = strchr(cp, '\n')) != NULL)
+ *cp = ' ';
+
+ printf(" qdisc:%s\n", buf);
+}
+
+void
+quip_printfilter(const char *ifname, const u_long handle)
+{
+ char buf[8192], req[256], *cp;
+ int result_code, len;
+
+ /* get qdisc info from the server */
+ sprintf(req, "GET filter?%s::%#lx\n", ifname, handle);
+ quip_sendrequest(server, req);
+
+ result_code = quip_recvresponse(server, NULL, buf, &len);
+ if (result_code != 200)
+ errx(1, "can't get filter info");
+
+ if ((cp = strchr(buf, '\n')) != NULL)
+ *cp = '\0';
+ printf("%s", buf);
+}
+
+static char *
+extract_ifname(const char *name)
+{
+ char *cp;
+ int len;
+ static char ifname[64];
+
+ if ((cp = strchr(name, ':')) != NULL)
+ len = cp - name;
+ else
+ len = strlen(name);
+ len = MIN(len, 63);
+ strncpy(ifname, name, len);
+ ifname[len] = '\0';
+ return (ifname);
+}
+
+void
+quip_printconfig(void)
+{
+ char buf[8192], name[256], *cp, *p, *flname;
+ int result_code, len;
+ enum nametype type;
+ u_long handle;
+
+ /* get a total list from the server */
+ quip_sendrequest(server, "GET list\n");
+
+ result_code = quip_recvresponse(server, NULL, buf, &len);
+ if (result_code != 200)
+ errx(1, "can't get total list");
+
+ printf("------------ current configuration ------------");
+
+ cp = buf;
+ while (1) {
+ if (sscanf(cp, "%lx %s", &handle, name) != 2)
+ break;
+
+ if ((p = strchr(name, ':')) == NULL)
+ type = INTERFACE;
+ else if (strchr(p+1, ':') == NULL)
+ type = CLASS;
+ else
+ type = FILTER;
+
+ switch (type) {
+ case INTERFACE:
+ printf("\ninterface: %s (index:%lu)\n",
+ name, handle);
+ quip_printqdisc(name);
+ break;
+ case CLASS:
+ printf("class: %s (handle:%#lx)\n",
+ name, handle);
+ break;
+ case FILTER:
+ flname = strrchr(name, ':') + 1;
+ printf(" filter: name:%s [", flname);
+ quip_printfilter(extract_ifname(name), handle);
+ printf("] (handle:%#lx)\n", handle);
+ break;
+ case CONDITIONER:
+ break;
+ }
+
+ if ((cp = strchr(cp+1, '\n')) == NULL)
+ break;
+ }
+ printf("-----------------------------------------------\n\n");
+}
+
diff --git a/usr.sbin/altq/altqstat/quip_client.h b/usr.sbin/altq/altqstat/quip_client.h
new file mode 100644
index 00000000000..7a107b999af
--- /dev/null
+++ b/usr.sbin/altq/altqstat/quip_client.h
@@ -0,0 +1,49 @@
+/* $OpenBSD: quip_client.h,v 1.1 2001/06/27 18:23:22 kjc Exp $ */
+/* $KAME: quip_client.h,v 1.2 2000/10/18 09:15:17 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _QUIP_CLIENT_H_
+#define _QUIP_CLIENT_H_
+
+/* unix domain socket for quip */
+#define QUIP_PATH "/var/run/altq_quip"
+
+extern int quip_echo;
+
+int quip_openserver(void);
+int quip_closeserver(void);
+void quip_sendrequest(FILE *fp, const char *request);
+int quip_recvresponse(FILE *fp, char *header, char *body, int *blen);
+void quip_rawmode(void);
+char *quip_selectinterface(char *ifname);
+char *quip_selectqdisc(char *ifname, char *qdisc_name);
+void quip_chandle2name(const char *ifname, u_long handle, char *name);
+void quip_printqdisc(const char *ifname);
+void quip_printfilter(const char *ifname, const u_long handle);
+void quip_printconfig(void);
+
+#endif /* _QUIP_CLIENT_H_ */
diff --git a/usr.sbin/altq/libaltq/Makefile b/usr.sbin/altq/libaltq/Makefile
new file mode 100644
index 00000000000..4c6c1559059
--- /dev/null
+++ b/usr.sbin/altq/libaltq/Makefile
@@ -0,0 +1,18 @@
+# $OpenBSD: Makefile,v 1.1 2001/06/27 18:23:22 kjc Exp $
+# $NetBSD: Makefile,v 1.2 2000/12/16 18:57:35 thorpej Exp $
+
+LIB= altq
+SRCS= parser.c qop.c qop_blue.c qop_cbq.c qop_cdnr.c qop_conf.c \
+ qop_dummy.c qop_errlist.c qop_fifoq.c qop_hfsc.c qop_priq.c \
+ qop_red.c qop_rio.c qop_wfq.c quip_server.c
+
+CPPFLAGS+= -DALTQ
+
+# we don't build the other copies of this library
+NOPIC= yes
+NOPROFILE= yes
+
+# only needed during build
+install:
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/altq/libaltq/altq_qop.h b/usr.sbin/altq/libaltq/altq_qop.h
new file mode 100644
index 00000000000..222bba6e0be
--- /dev/null
+++ b/usr.sbin/altq/libaltq/altq_qop.h
@@ -0,0 +1,255 @@
+/* $OpenBSD: altq_qop.h,v 1.1 2001/06/27 18:23:23 kjc Exp $ */
+/* $KAME: altq_qop.h,v 1.4 2000/10/18 09:15:18 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef _ALTQ_QOP_H_
+#define _ALTQ_QOP_H_
+
+#include <sys/queue.h>
+#include <altq/altq.h>
+#include <altq/altq_red.h>
+
+struct ifinfo;
+struct classinfo;
+struct fltrinfo;
+
+/* queueing discipline specific command parsers */
+struct qdisc_parser {
+ char *qname;
+ int (*interface_parser)(const char *ifname, int argc, char **argv);
+ int (*class_parser)(const char *ifname, const char *clname,
+ const char *parent, int argc, char **argv);
+};
+
+/* queueing discipline specific operations */
+struct qdisc_ops {
+ int qdisc_type; /* discipline type (e.g., ALTQT_CBQ) */
+ char *qname; /* discipline name (e.g., cbq) */
+
+ /* interface operations */
+ int (*attach)(struct ifinfo *);
+ int (*detach)(struct ifinfo *);
+ int (*clear)(struct ifinfo *);
+ int (*enable)(struct ifinfo *);
+ int (*disable)(struct ifinfo *);
+
+ /* class operations (optional) */
+ int (*add_class)(struct classinfo *);
+ int (*modify_class)(struct classinfo *, void *);
+ int (*delete_class)(struct classinfo *);
+
+ /* filter operations (optional) */
+ int (*add_filter)(struct fltrinfo *);
+ int (*delete_filter)(struct fltrinfo *);
+};
+
+/*
+ * interface info
+ */
+struct ifinfo {
+ LIST_ENTRY(ifinfo) next; /* next entry on iflist */
+ char *ifname; /* interface name */
+ u_int bandwidth; /* bandwidth in bps */
+ u_int ifmtu; /* mtu of the interface */
+ u_int ifindex; /* interface index */
+ int enabled; /* hfsc on/off state */
+ LIST_HEAD(, classinfo) cllist; /* class list */
+ LIST_HEAD(, fltrinfo) fltr_rules; /* filter rule list */
+
+ struct classinfo *resv_class; /* special class for rsvp */
+
+ /* discipline info */
+ struct qdisc_ops *qdisc; /* qdisc system interface */
+ void *private; /* discipline specific data */
+ int (*enable_hook)(struct ifinfo *);
+ int (*delete_hook)(struct ifinfo *);
+};
+
+/*
+ * class info
+ */
+struct classinfo {
+ LIST_ENTRY(classinfo) next; /* next entry on cllist
+ of ifinfo */
+ u_long handle; /* class handle */
+ char *clname; /* class name */
+ struct ifinfo *ifinfo; /* back pointer to ifinfo */
+ struct classinfo *parent; /* parent class */
+ struct classinfo *sibling; /* sibling class */
+ struct classinfo *child; /* child class */
+ LIST_HEAD(, fltrinfo) fltrlist; /* filters for this class */
+
+ void *private; /* discipline specific data */
+ int (*delete_hook)(struct classinfo *);
+};
+
+/*
+ * filter info
+ */
+struct fltrinfo {
+ LIST_ENTRY(fltrinfo) next; /* next entry on fltrlist
+ of classinfo */
+ LIST_ENTRY(fltrinfo) nextrule; /* next entry on fltr_rules
+ of ifinfo */
+ u_long handle; /* filter handle */
+ char *flname; /* filter name, if specified */
+ struct flow_filter fltr; /* filter value */
+ struct classinfo *clinfo; /* back pointer to classinfo */
+
+ /* for consistency check */
+ int line_no; /* config file line number */
+ int dontwarn; /* supress warning msg */
+};
+
+int DoCommand(char *infile, FILE *infp);
+
+int qcmd_enable(const char *ifname);
+int qcmd_disable(const char *ifname);
+int qcmd_delete_if(const char *ifname);
+int qcmd_clear_hierarchy(const char *ifname);
+int qcmd_enableall(void);
+int qcmd_disableall(void);
+int qcmd_config(void);
+int qcmd_init(void);
+int qcmd_clear(const char *ifname);
+int qcmd_destroyall(void);
+int qcmd_restart(void);
+int qcmd_delete_class(const char *ifname, const char *clname);
+int qcmd_add_filter(const char *ifname, const char *clname, const char *flname,
+ const struct flow_filter *fltr);
+int qcmd_delete_filter(const char *ifname, const char *clname,
+ const char *flname);
+int qcmd_tbr_register(const char *ifname, u_int rate, u_int size);
+int qop_enable(struct ifinfo *ifinfo);
+int qop_disable(struct ifinfo *ifinfo);
+int qop_delete_if(struct ifinfo *ifinfo);
+int qop_clear(struct ifinfo *ifinfo);
+
+int qop_add_if(struct ifinfo **rp, const char *ifname, u_int bandwidth,
+ struct qdisc_ops *qdisc_ops, void *if_private);
+int qop_delete_if(struct ifinfo *ifinfo);
+
+int qop_add_class(struct classinfo **rp, const char *clname,
+ struct ifinfo *ifinfo, struct classinfo *parent,
+ void *class_private);
+int qop_modify_class(struct classinfo *clinfo, void *arg);
+int qop_delete_class(struct classinfo *clinfo);
+
+int qop_add_filter(struct fltrinfo **rp,
+ struct classinfo *clinfo,
+ const char *flname,
+ const struct flow_filter *fltr,
+ struct fltrinfo **conflict);
+int qop_delete_filter(struct fltrinfo *fltr);
+
+int is_q_enabled(const char *ifname);
+struct ifinfo *ifname2ifinfo(const char *ifname);
+struct ifinfo *input_ifname2ifinfo(const char *ifname);
+struct classinfo *clname2clinfo(const struct ifinfo *ifinfo,
+ const char *clname);
+struct classinfo * clhandle2clinfo(struct ifinfo *ifinfo, u_long handle);
+struct fltrinfo *flname2flinfo(const struct classinfo *clinfo,
+ const char *flname);
+struct fltrinfo *flhandle2fltrinfo(struct ifinfo *ifinfo, u_long handle);
+void print_filter(const struct flow_filter *filt);
+const char *qoperror(int qoperrno);
+u_int get_ifindex(const char *ifname);
+struct classinfo *get_rootclass(struct ifinfo *ifinfo);
+struct classinfo *get_nextclass(struct classinfo *clinfo);
+u_long atobps(const char *s);
+u_long atobytes(const char *s);
+int qop_red_set_defaults(int th_min, int th_max, int inv_pmax);
+int qop_rio_set_defaults(struct redparams *params);
+int open_module(const char *devname, int flags);
+int client_input(FILE *fp);
+
+/* misc system errors */
+#define QOPERR_OK 0 /* no error */
+#define QOPERR_SYSCALL 1 /* syscall err; see errno */
+#define QOPERR_NOMEM 2 /* not enough memory */
+#define QOPERR_INVAL 3 /* invalid parameter */
+#define QOPERR_RANGE 4 /* out of range */
+#define QOPERR_BADIF 5 /* bad interface name */
+#define QOPERR_BADCLASS 6 /* bad class name */
+#define QOPERR_BADFILTER 7 /* bad filter name */
+
+/* class errors */
+#define QOPERR_CLASS 8 /* class failure */
+#define QOPERR_CLASS_INVAL 9 /* bad class value */
+#define QOPERR_CLASS_PERM 10 /* class operation not permitted */
+
+/* filter errors */
+#define QOPERR_FILTER 11 /* filter failure */
+#define QOPERR_FILTER_INVAL 12 /* bad filter value */
+#define QOPERR_FILTER_SHADOW 13 /* shadows an existing filter */
+
+/* addmission errors */
+#define QOPERR_ADMISSION 14 /* admission control failure */
+#define QOPERR_ADMISSION_NOBW 15 /* insufficient bandwidth */
+#define QOPERR_ADMISSION_DELAY 16 /* cannot meet delay bound req */
+#define QOPERR_ADMISSION_NOSVC 17 /* no service available */
+
+/* policy errors */
+#define QOPERR_POLICY 18 /* policy control failure */
+
+#define QOPERR_MAX 18
+
+extern int filter_dontwarn;/* supress warning for the current filter */
+extern char *altqconfigfile; /* config file name */
+extern const char *qop_errlist[]; /* error string list */
+extern struct qdisc_ops nop_qdisc;
+extern char *cur_ifname(void);
+extern struct qdisc_parser qdisc_parser[];
+extern int Debug_mode;
+
+#ifndef RSVPD
+/* rename LOG() to log_write() */
+#define LOG log_write
+void log_write(int, int, const char *, ...);
+
+/* stuff defined in rsvp headers */
+#define IsDebug(type) (l_debug >= LOG_DEBUG && (m_debug & (type)))
+#define DEBUG_ALTQ 0x40
+
+#define ntoh16(x) ((u_int16_t)ntohs((u_int16_t)(x)))
+#define ntoh32(x) ((u_int32_t)ntohl((u_int32_t)(x)))
+#define hton16(x) ((u_int16_t)htons((u_int16_t)(x)))
+#define hton32(x) ((u_int32_t)htonl((u_int32_t)(x)))
+
+extern int if_num; /* number of phyints */
+extern int m_debug; /* Debug output control bits */
+extern int l_debug; /* Logging severity level */
+extern int line_no; /* current line number in config file */
+extern int daemonize; /* log_write uses stderr if daemonize is 0 */
+
+#endif /* !RSVPD */
+
+#ifdef INET6
+/* a macro to handle v6 address in 32-bit fields */
+#define IN6ADDR32(a, i) (*(u_int32_t *)(&(a)->s6_addr[(i)<<2]))
+#endif
+
+#endif /* _ALTQ_QOP_H_ */
diff --git a/usr.sbin/altq/libaltq/parser.c b/usr.sbin/altq/libaltq/parser.c
new file mode 100644
index 00000000000..0a960c549a7
--- /dev/null
+++ b/usr.sbin/altq/libaltq/parser.c
@@ -0,0 +1,1353 @@
+/* $OpenBSD: parser.c,v 1.1 2001/06/27 18:23:24 kjc Exp $ */
+/* $KAME: parser.c,v 1.6 2001/05/30 10:30:44 kjc Exp $ */
+/*******************************************************************
+
+ Copyright (c) 1996 by the University of Southern California
+ All rights reserved.
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation in source and binary forms for any purpose and without
+ fee is hereby granted, provided that both the above copyright notice
+ and this permission notice appear in all copies. and that any
+ documentation, advertising materials, and other materials related to
+ such distribution and use acknowledge that the software was developed
+ in part by the University of Southern California, Information
+ Sciences Institute. The name of the University may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+ the suitability of this software for any purpose. THIS SOFTWARE IS
+ PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ Other copyrights might apply to parts of this software and are so
+ noted when applicable.
+
+********************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <altq/altq.h>
+#include <altq/altq_cdnr.h>
+#include <altq/altq_red.h>
+#include <altq/altq_rio.h>
+#include "altq_qop.h"
+#include "qop_cdnr.h"
+
+#define show_help(op) printf(cmd_tab[op].cmd_help)
+
+/*
+ * Forward & External Declarations
+ */
+static int is_qdisc_name(const char *qname);
+static int qdisc_interface_parser(const char * qname, const char *ifname,
+ int argc, char **argv);
+static int qdisc_class_parser(const char *qname, const char *ifname,
+ const char *class_name, const char *parent_name,
+ int argc, char **argv);
+
+static int pfxcmp(const char *s1, const char *s2);
+static int next_word(char **cpp, char *b);
+
+static int do_cmd(int op, char *cmdbuf);
+static int get_ifname(char **cpp, char **ifnamep);
+static int get_addr(char **cpp, struct in_addr *addr, struct in_addr *mask);
+static int get_port(const char *name, u_int16_t *port_no);
+static int get_proto(const char *name, int *proto_no);
+static int get_fltr_opts(char **cpp, char *fltr_name, int *ruleno);
+static int interface_parser(char *cmdbuf);
+static int class_parser(char *cmdbuf) ;
+static int filter_parser(char *cmdbuf);
+#ifdef INET6
+static int filter6_parser(char *cmdbuf);
+static int get_ip6addr(char **cpp, struct in6_addr *addr,
+ struct in6_addr *mask);
+#endif
+static int ctl_parser(char *cmdbuf);
+static int delete_parser(char *cmdbuf);
+static int red_parser(char *cmdbuf);
+static int rio_parser(char *cmdbuf);
+static int conditioner_parser(char *cmdbuf);
+static int tc_action_parser(char *ifname, char **cpp,
+ struct tc_action *action);
+
+/*
+ * Globals
+ */
+#define MAX_NFLWDS 64
+#define MAX_T 64
+
+int TNO = 1; /* Current Thread number */
+int line_no = 0;
+int filter_dontwarn;
+
+static char if_names[MAX_T][IFNAMSIZ];
+static struct if_nameindex *if_namelist = NULL;
+
+#ifndef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+enum op_codes {
+ /* order must be same as entries cmd_tab[].cmd_op below!! */
+ OP_HELP = 1, OP_QUIT,
+ OP_IFACE, OP_CLASS, OP_FILTER,
+ OP_ALTQ, OP_DEL,
+#ifdef INET6
+ OP_FILTER6,
+#endif
+ OP_RED, OP_RIO,
+ OP_CDNR,
+ OP_NULL, OP_BUG
+};
+
+/* Following table MUST match enum order of op_codes !
+ */
+struct cmds {
+ char *cmd_verb;
+ int cmd_op;
+ char *cmd_help;
+} cmd_tab[] = {
+
+ { "?", OP_HELP, "Commands are:\n" },
+ { "help", OP_HELP, " help | ?\n" },
+ { "quit", OP_QUIT, " quit\n" },
+ { "interface", OP_IFACE, " interface if_name [bandwidth bps] [cbq|hfsc]\n" },
+ { "class", OP_CLASS, " class discipline if_name class_name [parent]\n" },
+ { "filter", OP_FILTER, " filter if_name class_name [name filt_name] dst [netmask #] dport src [netmask #] sport proto [tos # [tosmask #] [gpi #] [dontwarn]\n" },
+ { "altq", OP_ALTQ, " disc if_name {enable|disable}\n" },
+ { "delete", OP_DEL, " delete if_name class_name\n" },
+#ifdef INET6
+ { "filter6", OP_FILTER6, " filter6 if_name class_name [name filt_name] dst[/prefix] dport src[/prefix] sport proto [flowlabel #][tclass # [tclassmask #]][gpi #] [dontwarn]\n" },
+#endif
+ { "red", OP_RED, " red th_min th_max inv_pmax\n" },
+ { "rio", OP_RIO, " rio low_th_min low_th_max low_inv_pmax med_th_min med_th_max med_inv_pmax high_th_min high_th_max high_inv_pmax\n" },
+ { "conditioner", OP_CDNR, " conditioner if_name cdnr_name <tc_action>\n" },
+ { "bug", OP_BUG, " bug (On/Off)\n" },
+ { "", OP_NULL, "" } /* MUST BE LAST IN CMD TABLE */
+};
+
+static int
+is_qdisc_name(const char *qname)
+{
+ struct qdisc_parser *qp;
+
+ for (qp = qdisc_parser; qp->qname != NULL; qp++)
+ if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0)
+ return (1);
+ return (0);
+}
+
+static int
+qdisc_interface_parser(const char * qname, const char *ifname,
+ int argc, char **argv)
+{
+ struct qdisc_parser *qp;
+
+ for (qp = qdisc_parser; qp->qname != NULL; qp++)
+ if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0)
+ return (*qp->interface_parser)(ifname, argc, argv);
+ return (0);
+}
+
+static int
+qdisc_class_parser(const char *qname, const char *ifname,
+ const char *class_name, const char *parent_name,
+ int argc, char **argv)
+{
+ struct qdisc_parser *qp;
+ struct ifinfo *ifinfo;
+
+ for (qp = qdisc_parser; qp->qname != NULL; qp++)
+ if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0) {
+ if (qp->class_parser == NULL) {
+ LOG(LOG_ERR, 0,
+ "class can't be specified for %s", qp->qname);
+ return (0);
+ }
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL) {
+ LOG(LOG_ERR, 0,
+ "no such interface, line %d\n", line_no);
+ return (0);
+ }
+ if (strncmp(ifinfo->qdisc->qname, qname,
+ strlen(ifinfo->qdisc->qname)) != 0) {
+ LOG(LOG_ERR, 0,
+ "qname doesn't match the interface, line %d\n",
+ line_no);
+ return (0);
+ }
+ return (*qp->class_parser)(ifname, class_name,
+ parent_name, argc, argv);
+ }
+ return (0);
+}
+
+
+/*
+ * Read the config file to learn about tunnel vifs and non-default phyint
+ * parameters.
+ */
+int
+qcmd_config(void)
+{
+ FILE *f;
+ int i, rc = 1;
+
+ if (if_namelist != NULL)
+ if_freenameindex(if_namelist);
+ if_namelist = if_nameindex();
+
+ for (i = 0; i < MAX_T; i++)
+ if_names[i][0] = '\0';
+
+ LOG(LOG_INFO, 0, "ALTQ config file is %s\n", altqconfigfile);
+
+ f = fopen(altqconfigfile, "r");
+ if (f == NULL) {
+ LOG(LOG_ERR, errno, "Can't open %s", altqconfigfile, 0);
+ return (QOPERR_INVAL);
+ }
+ line_no = 0;
+ while (rc)
+ rc = DoCommand(altqconfigfile, f);
+
+ (void) fclose(f);
+ line_no = 0;
+ return (0);
+}
+
+/*
+ * Do_Command(): Top-level routine to read the next line from a given
+ * file and execute the command it contains.
+ * returns 1 if OK, 0 if EOF.
+ */
+int
+DoCommand(char *infile, FILE *infp)
+{
+ char cmd_line[256], cmd_op[80];
+ struct cmds *cmdp;
+ char *cp;
+ int rc;
+
+ if (fgets(cmd_line, sizeof(cmd_line), infp) == NULL)
+ /* EOF */
+ return(0);
+ line_no++;
+
+ /* check escaped newline */
+ while ((cp = strrchr(cmd_line, '\\')) != NULL && cp[1] == '\n') {
+ if (fgets(cp, &cmd_line[256] - cp, infp) != NULL)
+ line_no++;
+ }
+
+ /* remove trailing NL */
+ cp = cmd_line + strlen(cmd_line) - 1;
+ if (*cp == '\n')
+ *cp = '\0';
+ else if (!feof(infp)) {
+ printf("LINE %d > 255 CHARS: %s.\n", line_no, cmd_line);
+ exit(1);
+ }
+ /*** printf("DoCommand: %s\n", cmd_line); ***/
+
+ if (cmd_line[0] == '#') { /* Comment, skip this line */
+ return(1);
+ }
+ cp = cmd_line;
+ if (!next_word(&cp, cmd_op))
+ return(1);
+ if (cmd_op[0] == 'T') {
+ TNO = atoi(&cmd_op[1]);
+ if (!next_word(&cp, cmd_op))
+ return(1);
+ }
+ cmdp = cmd_tab;
+ while ((cmdp->cmd_op != OP_NULL) && pfxcmp(cmd_op, cmdp->cmd_verb))
+ cmdp++;
+
+ if (cmdp->cmd_op == OP_NULL) {
+ if (cmd_op[0])
+ printf(" ?? %s\n", cmd_op);
+ return(1);
+ }
+ rc = do_cmd(cmdp->cmd_op, cp);
+ if (rc == 0) {
+ if (infile) {
+ /* error in the config file. cleanup and exit. */
+ LOG(LOG_ERR, 0, "Config failed. Exiting.\n");
+ (void) qcmd_destroyall();
+ (void) fclose(infp);
+ exit(1);
+ } else {
+ /* interactive mode */
+ printf("error: usage :");
+ show_help(cmdp->cmd_op);
+ }
+ }
+ return(1);
+}
+
+
+/*
+ * Prefix string comparison: Return 0 if s1 string is prefix of s2 string, 1
+ * otherwise.
+ */
+static int
+pfxcmp(const char *s1, const char *s2)
+{
+ while (*s1)
+ if (*s1++ != *s2++)
+ return (1);
+ return (0);
+}
+
+/*
+ * Skip leading blanks, then copy next word (delimited by blank or zero, but
+ * no longer than 63 bytes) into buffer b, set scan pointer to following
+ * non-blank (or end of string), and return 1. If there is no non-blank text,
+ * set scan ptr to point to 0 byte and return 0.
+ */
+static int
+next_word(char **cpp, char *b)
+{
+ char *tp;
+ size_t L;
+
+ *cpp += strspn(*cpp, " \t");
+ if (**cpp == '\0' || **cpp == '\n' || **cpp == '#')
+ return(0);
+
+ tp = strpbrk(*cpp, " \t\n#");
+ L = MIN((tp)?(tp-*cpp):strlen(*cpp), 63);
+ strncpy(b, *cpp, L);
+ *(b + L) = '\0';
+ *cpp += L;
+ *cpp += strspn(*cpp, " \t");
+ return (1);
+}
+
+/*
+ * do_cmd executes a command input.
+ * returns 1 if OK, 0 if an error occurs.
+ */
+static int
+do_cmd(int op, char *cmdbuf)
+{
+ int i, rval = 0;
+
+ switch (op) {
+ case OP_HELP:
+ for (i = 0; i < OP_NULL; i++)
+ show_help(i);
+ rval = 1;
+ break;
+ case OP_QUIT:
+ qcmd_destroyall();
+ exit(0);
+ break;
+ case OP_IFACE:
+ rval = interface_parser(cmdbuf);
+ break;
+ case OP_CLASS:
+ rval = class_parser(cmdbuf);
+ break;
+ case OP_FILTER:
+ rval = filter_parser(cmdbuf);
+ break;
+ case OP_ALTQ:
+ rval = ctl_parser(cmdbuf);
+ break;
+ case OP_DEL:
+ rval = delete_parser(cmdbuf);
+ break;
+#ifdef INET6
+ case OP_FILTER6:
+ rval = filter6_parser(cmdbuf);
+ break;
+#endif
+ case OP_RED:
+ rval = red_parser(cmdbuf);
+ break;
+ case OP_RIO:
+ rval = rio_parser(cmdbuf);
+ break;
+ case OP_CDNR:
+ rval = conditioner_parser(cmdbuf);
+ break;
+ case OP_BUG:
+ if (m_debug & DEBUG_ALTQ) {
+ /* turn off verbose */
+ l_debug = LOG_INFO;
+ m_debug &= ~DEBUG_ALTQ;
+ } else {
+ /* turn on verbose */
+ l_debug = LOG_DEBUG;
+ m_debug |= DEBUG_ALTQ;
+ }
+ break;
+ default:
+ printf("command %d not supported\n", op);
+ rval = 0;
+ break;
+ }
+ return(rval);
+}
+
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+char *cur_ifname(void)
+{
+ return (if_names[TNO]);
+}
+
+u_int
+get_ifindex(const char *ifname)
+{
+ struct if_nameindex *ifnp;
+
+ for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++)
+ if (strcmp(ifname, ifnp->if_name) == 0)
+ return (ifnp->if_index);
+ return (0);
+}
+
+static int
+get_ifname(char **cpp, char **ifnamep)
+{
+ char w[128], *ocp;
+ struct if_nameindex *ifnp;
+
+ ocp = *cpp;
+ if (next_word(&ocp, w) && if_namelist != NULL)
+ for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++)
+ if (strcmp(w, ifnp->if_name) == 0) {
+ /* if_name found. advance the word pointer */
+ *cpp = ocp;
+ strcpy(if_names[TNO], w);
+ *ifnamep = if_names[TNO];
+ return (1);
+ }
+
+ /* this is not interface name. use one in the context. */
+ if (if_names[TNO][0] == 0)
+ return (0);
+ *ifnamep = if_names[TNO];
+ return (1);
+}
+
+/* set address and netmask in network byte order */
+static int
+get_addr(char **cpp, struct in_addr *addr, struct in_addr *mask)
+{
+ char w[128], *ocp;
+ u_long tmp;
+
+ addr->s_addr = 0;
+ mask->s_addr = 0xffffffff;
+
+ if (!next_word(cpp, w))
+ return (0);
+
+ if ((tmp = inet_addr((char *)w)) == INADDR_NONE) {
+ /* try gethostbyname */
+ struct hostent *h;
+
+ if ((h = gethostbyname(w)) == NULL
+ || h->h_addrtype != AF_INET || h->h_length != 4)
+ return (0);
+
+ bcopy(h->h_addr, &tmp, (size_t)h->h_length);
+ }
+
+ addr->s_addr = tmp;
+
+ /* check if netmask option is present */
+ ocp = *cpp;
+ if (next_word(&ocp, w) && EQUAL(w, "netmask")) {
+ if (!next_word(&ocp, w))
+ return (0);
+
+ if (inet_aton((char *)w, (struct in_addr *)&tmp) == 0)
+ return (0);
+
+ mask->s_addr = tmp;
+ *cpp = ocp;
+ return (1);
+ }
+ /* no netmask option */
+ return (1);
+}
+
+/* returns service number in network byte order */
+static int
+get_port(const char *name, u_int16_t *port_no)
+{
+ struct servent *s;
+ u_int16_t num;
+
+ if (isdigit(name[0])) {
+ num = (u_int16_t)strtol(name, NULL, 0);
+ *port_no = htons(num);
+ return (1);
+ }
+
+ if ((s = getservbyname(name, 0)) == NULL)
+ return (0);
+
+ *port_no = (u_int16_t)s->s_port;
+ return (1);
+}
+
+static int
+get_proto(const char *name, int *proto_no)
+{
+ struct protoent *p;
+
+ if (isdigit(name[0])) {
+ *proto_no = (int)strtol(name, NULL, 0);
+ return (1);
+ }
+
+ if ((p = getprotobyname(name)) == NULL)
+ return (0);
+
+ *proto_no = p->p_proto;
+ return (1);
+}
+
+static int
+get_fltr_opts(char **cpp, char *fltr_name, int *ruleno)
+{
+ char w[128], *ocp;
+
+ ocp = *cpp;
+ while (next_word(&ocp, w)) {
+ if (EQUAL(w, "name")) {
+ if (!next_word(&ocp, w))
+ return (0);
+ strcpy(fltr_name, w);
+ *cpp = ocp;
+ } else if (EQUAL(w, "ruleno")) {
+ if (!next_word(&ocp, w))
+ return (0);
+ *ruleno = (int)strtol(w, NULL, 0);
+ *cpp = ocp;
+ } else
+ break;
+ }
+ return (1);
+}
+
+
+#define DISCIPLINE_NONE 0
+
+static int
+interface_parser(char *cmdbuf)
+{
+ char w[256], *ap, *cp = cmdbuf;
+ char *ifname, *argv[64], qdisc_name[64];
+ int argc, rval;
+
+ if (!get_ifname(&cp, &ifname)) {
+ LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /*
+ * Create argment list & look for scheduling discipline options.
+ */
+ sprintf(qdisc_name, "null");
+ argc = 0;
+ ap = w;
+ while (next_word(&cp, ap)) {
+ if (is_qdisc_name(ap))
+ strcpy(qdisc_name, ap);
+
+ argv[argc] = ap;
+ ap += strlen(ap) + 1;
+ argc++;
+ }
+
+ rval = qdisc_interface_parser(qdisc_name, ifname, argc, argv);
+ if (rval == 0) {
+ LOG(LOG_ERR, 0, "Error in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ return (1);
+}
+
+static int
+class_parser(char *cmdbuf)
+{
+ char w[256], *cp = cmdbuf;
+ char *ifname, qdisc_name[128], class_name[128], parent_name[128];
+ char *clname = class_name;
+ char *parent = NULL;
+ char *argv[64], *ap;
+ int argc, rval;
+
+ /* get scheduling class */
+ if (!next_word(&cp, qdisc_name)) {
+ LOG(LOG_ERR, 0, "missing scheduling discipline in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ if (!is_qdisc_name(qdisc_name)) {
+ LOG(LOG_ERR, 0,
+ "unknown scheduling discipline '%s' in %s, line %d\n",
+ qdisc_name, altqconfigfile, line_no);
+ return (0);
+ }
+
+ /* get interface name */
+ if (!get_ifname(&cp, &ifname)) {
+ LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /* get class name */
+ if (!next_word(&cp, class_name)) {
+ LOG(LOG_ERR, 0, "missing class name in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /* get parent name */
+ if (!next_word(&cp, parent_name)) {
+ LOG(LOG_ERR, 0, "missing parent class in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ if (!EQUAL(parent_name, "null") && !EQUAL(parent_name, "NULL")) {
+ parent = parent_name;
+ } else {
+ parent = NULL;
+ }
+
+ ap = w;
+ argc = 0;
+ while (next_word(&cp, ap)) {
+ argv[argc] = ap;
+ ap += strlen(ap) + 1;
+ argc++;
+ }
+
+ rval = qdisc_class_parser(qdisc_name, ifname, clname, parent,
+ argc, argv);
+ if (rval == 0) {
+ LOG(LOG_ERR, 0, "can't add class '%s' on interface '%s'\n",
+ clname, ifname);
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+filter_parser(char *cmdbuf)
+{
+ char w[128], *cp = cmdbuf;
+ char *ifname, class_name[64], fltr_name[64], *flname = NULL;
+ struct flow_filter sfilt;
+ int protocol;
+ u_char tos, tosmask;
+ int ruleno;
+ int dontwarn = 0;
+ int error;
+
+ memset(&sfilt, 0, sizeof(sfilt));
+ sfilt.ff_flow.fi_family = AF_INET;
+
+ if (!get_ifname(&cp, &ifname)) {
+ LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ if (!next_word(&cp, class_name)) {
+ LOG(LOG_ERR, 0,
+ "missing class name in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ fltr_name[0] = '\0';
+ ruleno = 0;
+ if (!get_fltr_opts(&cp, &fltr_name[0], &ruleno)) {
+ LOG(LOG_ERR, 0,
+ "bad filter option in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ if (fltr_name[0] != '\0')
+ flname = fltr_name;
+ sfilt.ff_ruleno = ruleno;
+
+ /* get filter destination Address */
+ if (!get_addr(&cp, &sfilt.ff_flow.fi_dst, &sfilt.ff_mask.mask_dst)) {
+ LOG(LOG_ERR, 0,
+ "bad filter destination address in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /* get filter destination port */
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0,
+ "missing filter destination port in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ if (!get_port(w, &sfilt.ff_flow.fi_dport)) {
+ LOG(LOG_ERR, 0, "bad filter destination port in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /* get filter source address */
+ if (!get_addr(&cp, &sfilt.ff_flow.fi_src, &sfilt.ff_mask.mask_src)) {
+ LOG(LOG_ERR, 0, "bad filter source address in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /* get filter source port */
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0, "missing filter source port in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ if (!get_port(w, &sfilt.ff_flow.fi_sport)) {
+ LOG(LOG_ERR, 0, "bad filter source port in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /* get filter protocol id */
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0, "missing filter protocol id in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ if (!get_proto(w, &protocol)) {
+ LOG(LOG_ERR, 0, "bad protocol in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ sfilt.ff_flow.fi_proto = protocol;
+
+ while (next_word(&cp, w)) {
+ if (EQUAL(w, "tos")) {
+ tos = 0;
+ tosmask = 0xff;
+
+ if (next_word(&cp, w)) {
+ tos = (u_char)strtol(w, NULL, 0);
+ if (next_word(&cp, w)) {
+ if (EQUAL(w, "tosmask")) {
+ next_word(&cp, w);
+ tosmask = (u_char)strtol(w, NULL, 0);
+ }
+ }
+ }
+ sfilt.ff_flow.fi_tos = tos;
+ sfilt.ff_mask.mask_tos = tosmask;
+ } else if (EQUAL(w, "gpi")) {
+ if (next_word(&cp, w)) {
+ sfilt.ff_flow.fi_gpi =
+ (u_int32_t)strtoul(w, NULL, 0);
+ sfilt.ff_flow.fi_gpi =
+ htonl(sfilt.ff_flow.fi_gpi);
+ }
+ } else if (EQUAL(w, "dontwarn"))
+ dontwarn = 1;
+ }
+
+ /*
+ * Add the filter.
+ */
+ filter_dontwarn = dontwarn; /* XXX */
+ error = qcmd_add_filter(ifname, class_name, flname, &sfilt);
+ filter_dontwarn = 0; /* XXX */
+ if (error) {
+ LOG(LOG_ERR, 0,
+ "can't add filter to class '%s' on interface '%s'\n",
+ class_name, ifname);
+ return (0);
+ }
+
+ return (1);
+}
+
+#ifdef INET6
+static int
+filter6_parser(char *cmdbuf)
+{
+ char w[128], *cp = cmdbuf;
+ char *ifname, class_name[128], fltr_name[64], *flname = NULL;
+ struct flow_filter6 sfilt;
+ int protocol;
+ u_char tclass, tclassmask;
+ int ruleno;
+ int dontwarn = 0;
+ int ret;
+
+ memset(&sfilt, 0, sizeof(sfilt));
+ sfilt.ff_flow6.fi6_family = AF_INET6;
+
+ if (!get_ifname(&cp, &ifname)) {
+ LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ if (!next_word(&cp, class_name)) {
+ LOG(LOG_ERR, 0, "missing class name in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ fltr_name[0] = '\0';
+ ruleno = 0;
+ if (!get_fltr_opts(&cp, &fltr_name[0], &ruleno)) {
+ LOG(LOG_ERR, 0,
+ "bad filter option in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ if (fltr_name[0] != '\0')
+ flname = fltr_name;
+ sfilt.ff_ruleno = ruleno;
+
+ /* get filter destination address */
+ if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_dst,
+ &sfilt.ff_mask6.mask6_dst)) {
+ LOG(LOG_ERR, 0, "bad destination address in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /* get filter destination port */
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0,
+ "missing filter destination port in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ if (!get_port(w, &sfilt.ff_flow6.fi6_dport)) {
+ LOG(LOG_ERR, 0, "bad filter destination port in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /* get filter source address */
+ if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_src,
+ &sfilt.ff_mask6.mask6_src)) {
+ LOG(LOG_ERR, 0, "bad source address in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /* get filter source port */
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0, "missing filter source port in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ if (!get_port(w, &sfilt.ff_flow6.fi6_sport)) {
+ LOG(LOG_ERR, 0, "bad filter source port in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /* get filter protocol id */
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0, "missing filter protocol id in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ if (!get_proto(w, &protocol)) {
+ LOG(LOG_ERR, 0, "bad protocol in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ sfilt.ff_flow6.fi6_proto = protocol;
+
+ while (next_word(&cp, w)) {
+ if (EQUAL(w, "tclass")) {
+ tclass = 0;
+ tclassmask = 0xff;
+
+ if (next_word(&cp, w)) {
+ tclass = (u_char)strtol(w, NULL, 0);
+ if (next_word(&cp, w)) {
+ if (EQUAL(w, "tclassmask")) {
+ next_word(&cp, w);
+ tclassmask =
+ (u_char)strtol(w, NULL, 0);
+ }
+ }
+ }
+ sfilt.ff_flow6.fi6_tclass = tclass;
+ sfilt.ff_mask6.mask6_tclass = tclassmask;
+ } else if (EQUAL(w, "gpi")) {
+ if (next_word(&cp, w)) {
+ sfilt.ff_flow6.fi6_gpi =
+ (u_int32_t)strtoul(w, NULL, 0);
+ sfilt.ff_flow6.fi6_gpi =
+ htonl(sfilt.ff_flow6.fi6_gpi);
+ }
+ } else if (EQUAL(w, "flowlabel")) {
+ if (next_word(&cp, w)) {
+ sfilt.ff_flow6.fi6_flowlabel =
+ (u_int32_t)strtoul(w, NULL, 0) & 0x000fffff;
+ sfilt.ff_flow6.fi6_flowlabel =
+ htonl(sfilt.ff_flow6.fi6_flowlabel);
+ }
+ } else if (EQUAL(w, "dontwarn"))
+ dontwarn = 1;
+ }
+
+ /*
+ * Add the filter.
+ */
+ filter_dontwarn = dontwarn; /* XXX */
+ ret = qcmd_add_filter(ifname, class_name, flname,
+ (struct flow_filter *)&sfilt);
+ filter_dontwarn = 0; /* XXX */
+ if (ret) {
+ LOG(LOG_ERR, 0,
+ "can't add filter to class '%s' on interface '%s'\n",
+ class_name, ifname);
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+get_ip6addr(char **cpp, struct in6_addr *addr, struct in6_addr *mask)
+{
+ char w[128], *prefix;
+ u_char *cp;
+ int len;
+
+ *addr = in6addr_any; /* set all 0 */
+ *mask = in6addr_any; /* set all 0 */
+
+ if (!next_word(cpp, w))
+ return (0);
+
+ if (EQUAL(w, "0"))
+ /* abbreviation of a wildcard (::0) */
+ return (1);
+
+ if ((prefix = strchr(w, '/')) != NULL) {
+ /* address has prefix length */
+ *prefix++ = '\0';
+ }
+
+ if (inet_pton(AF_INET6, w, addr) <= 0)
+ return (0);
+
+ if (IN6_IS_ADDR_UNSPECIFIED(addr) && prefix == NULL)
+ /* wildcard */
+ return (1);
+
+ /* convert address prefix length to address mask */
+ if (prefix != NULL) {
+ len = (int)strtol(prefix, NULL, 0);
+ if ((len < 0) || (len > 128))
+ return (0);
+ for (cp = (u_char *)mask; len > 7; len -= 8)
+ *cp++ = 0xff;
+ if (len > 0)
+ *cp = (0xff << (8 - len)) & 0xff;
+
+ IN6ADDR32(addr, 0) &= IN6ADDR32(mask, 0);
+ IN6ADDR32(addr, 1) &= IN6ADDR32(mask, 1);
+ IN6ADDR32(addr, 2) &= IN6ADDR32(mask, 2);
+ IN6ADDR32(addr, 3) &= IN6ADDR32(mask, 3);
+ } else
+ /* full mask */
+ memset(mask, 0xff, sizeof(struct in6_addr));
+
+ return (1);
+}
+
+#endif /* INET6 */
+
+static int
+ctl_parser(char *cmdbuf)
+{
+ char w[128], *cp = cmdbuf;
+ char *ifname;
+ int state;
+ int rval;
+
+ if (!get_ifname(&cp, &ifname)) {
+ printf("missing interface name in %s, line %d",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ if (!next_word(&cp, w)) {
+ state = is_q_enabled(ifname);
+ printf("altq %s on %s\n",
+ state ? "enabled" : "disabled", ifname);
+ return (1);
+ }
+
+ if (EQUAL(w, "enable")) {
+ rval = qcmd_enable(ifname);
+ printf("altq %s on %s\n",
+ (rval == 0) ? "enabled" : "enable failed!", ifname);
+ } else if (EQUAL(w, "disable")) {
+ rval = qcmd_disable(ifname);
+ printf("altq %s on %s\n",
+ (rval == 0) ? "disabled" : "disable failed!", ifname);
+ } else if (EQUAL(w, "reload")) {
+ printf("reinitializing altq...\n");
+ qcmd_destroyall();
+ qcmd_init();
+ } else
+ return (0);
+ return (1);
+}
+
+
+static int
+delete_parser(char *cmdbuf)
+{
+ char *cp = cmdbuf;
+ char *ifname, class_name[128];
+ int ret;
+
+ if (!get_ifname(&cp, &ifname)) {
+ printf("missing interface name in %s, line %d",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ if (!next_word(&cp, class_name)) {
+ LOG(LOG_ERR, 0,
+ "missing class name in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ ret = qcmd_delete_class(ifname, class_name);
+ if (ret) {
+ LOG(LOG_ERR, 0,
+ "can't delete class '%s' on interface '%s'\n",
+ class_name, ifname);
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+red_parser(char *cmdbuf)
+{
+ char w[128], *cp = cmdbuf;
+ int th_min, th_max, inv_pmax;
+
+ if (!next_word(&cp, w))
+ goto bad;
+ th_min = (int)strtol(w, NULL, 0);
+
+ if (!next_word(&cp, w))
+ goto bad;
+ th_max = (int)strtol(w, NULL, 0);
+
+ if (!next_word(&cp, w))
+ goto bad;
+ inv_pmax = (int)strtol(w, NULL, 0);
+
+ if (qop_red_set_defaults(th_min, th_max, inv_pmax) != 0) {
+ LOG(LOG_ERR, 0, "can't set red default parameters\n");
+ return (0);
+ }
+
+ return (1);
+
+ bad:
+ LOG(LOG_ERR, 0, "bad red parameter in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+}
+
+static int
+rio_parser(char *cmdbuf)
+{
+ char w[128], *cp = cmdbuf;
+ int i;
+ struct redparams params[RIO_NDROPPREC];
+
+ for (i = 0; i < RIO_NDROPPREC; i++) {
+ if (!next_word(&cp, w))
+ goto bad;
+ params[i].th_min = (int)strtol(w, NULL, 0);
+
+ if (!next_word(&cp, w))
+ goto bad;
+ params[i].th_max = (int)strtol(w, NULL, 0);
+
+ if (!next_word(&cp, w))
+ goto bad;
+ params[i].inv_pmax = (int)strtol(w, NULL, 0);
+ }
+
+ if (qop_rio_set_defaults(&params[0]) != 0) {
+ LOG(LOG_ERR, 0, "can't set rio default parameters\n");
+ return (0);
+ }
+
+ return (1);
+
+ bad:
+ LOG(LOG_ERR, 0, "bad rio parameter in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+}
+
+static int
+conditioner_parser(char *cmdbuf)
+{
+ char cdnr_name[128], *cp = cmdbuf;
+ char *ifname;
+ struct tc_action action[64];
+
+ if (!get_ifname(&cp, &ifname)) {
+ LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /* get conditioner name */
+ if (!next_word(&cp, cdnr_name)) {
+ LOG(LOG_ERR, 0, "missing cdnr name in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ if (tc_action_parser(ifname, &cp, &action[0]) == 0)
+ return (0);
+
+ if (qcmd_cdnr_add_element(NULL, ifname, cdnr_name, &action[0]) != 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * recursively parse '<'tc_action'>'
+ * note that array "action" grows during recursive parse.
+ */
+static int
+tc_action_parser(char *ifname, char **cpp, struct tc_action *action)
+{
+ char *cp, *start, *end;
+ char type[128], w[128];
+ int depth, i;
+ struct tb_profile profile[2];
+
+ /*
+ * find a possibly nested pair of '<' and '>',
+ * make them pointed by 'start' and 'end'.
+ */
+ start = strchr(*cpp, '<');
+ if (start == NULL) {
+ LOG(LOG_ERR, 0, "conditioner action missing in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ depth = 1;
+ cp = start + 1;
+ do {
+ end = strpbrk(cp, "<>");
+ if (end == NULL) {
+ LOG(LOG_ERR, 0,
+ "conditioner action delimiter mismatch in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ if (*end == '<')
+ depth++;
+ else if (*end == '>')
+ depth--;
+ cp = end + 1;
+ } while (depth > 0);
+ *end = '\0';
+ *cpp = end + 1;
+ cp = start + 1;
+
+ if (IsDebug(DEBUG_ALTQ)) {
+ printf("tc_action_parser: [%s]\n", cp);
+ }
+
+ if (!next_word(&cp, type)) {
+ LOG(LOG_ERR, 0,
+ "missing conditioner action type in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ /*
+ * action type specific process
+ */
+ if (EQUAL(type, "conditioner")) {
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0,
+ "missing conditioner name in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ action->tca_code = TCACODE_HANDLE;
+ action->tca_handle = cdnr_name2handle(ifname, w);
+ if (action->tca_handle == CDNR_NULL_HANDLE) {
+ LOG(LOG_ERR, 0,
+ "wrong conditioner name %s in %s, line %d\n",
+ w, altqconfigfile, line_no);
+ return (0);
+ }
+ } else if (EQUAL(type, "pass")) {
+ action->tca_code = TCACODE_PASS;
+ } else if (EQUAL(type, "drop")) {
+ action->tca_code = TCACODE_DROP;
+ } else if (EQUAL(type, "mark")) {
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0, "missing dscp in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ action->tca_code = TCACODE_MARK;
+ action->tca_dscp = (u_int8_t)strtol(w, NULL, 0);
+ } else if (EQUAL(type, "tbmeter")) {
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0, "missing tb profile in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ profile[0].rate = atobps(w);
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0, "missing tb profile in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ profile[0].depth = atobytes(w);
+ if (tc_action_parser(ifname, &cp, &action[1]) == 0)
+ return (0);
+ if (tc_action_parser(ifname, &cp, &action[2]) == 0)
+ return (0);
+
+ if (qcmd_cdnr_add_tbmeter(action, ifname, NULL, &profile[0],
+ &action[1], &action[2]) != 0)
+ return (0);
+ } else if (EQUAL(type, "trtcm")) {
+ int coloraware = 0; /* default is color-blind */
+
+ for (i=0; i<2; i++) {
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0,
+ "missing tb profile in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ profile[i].rate = atobps(w);
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0,
+ "missing tb profile in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ profile[i].depth = atobytes(w);
+ }
+ if (tc_action_parser(ifname, &cp, &action[1]) == 0)
+ return (0);
+ if (tc_action_parser(ifname, &cp, &action[2]) == 0)
+ return (0);
+ if (tc_action_parser(ifname, &cp, &action[3]) == 0)
+ return (0);
+ if (next_word(&cp, w)) {
+ if (EQUAL(w, "coloraware"))
+ coloraware = 1;
+ else if (EQUAL(w, "colorblind"))
+ coloraware = 0;
+ }
+
+ if (qcmd_cdnr_add_trtcm(action, ifname, NULL,
+ &profile[0], &profile[1],
+ &action[1], &action[2], &action[3],
+ coloraware) != 0)
+ return (0);
+ } else if (EQUAL(type, "tswtcm")) {
+ u_int32_t cmtd_rate, peak_rate, avg_interval;
+
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0, "missing cmtd rate in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ cmtd_rate = atobps(w);
+
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0, "missing peak rate in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ peak_rate = atobps(w);
+
+ if (!next_word(&cp, w)) {
+ LOG(LOG_ERR, 0, "missing avg interval in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ avg_interval = (u_int32_t)strtoul(w, NULL, 0);
+
+ if (tc_action_parser(ifname, &cp, &action[1]) == 0)
+ return (0);
+ if (tc_action_parser(ifname, &cp, &action[2]) == 0)
+ return (0);
+ if (tc_action_parser(ifname, &cp, &action[3]) == 0)
+ return (0);
+
+ if (qcmd_cdnr_add_tswtcm(action, ifname, NULL,
+ cmtd_rate, peak_rate, avg_interval,
+ &action[1], &action[2], &action[3])
+ != 0)
+ return (0);
+ } else {
+ LOG(LOG_ERR, 0,
+ "Unkown action type %s in %s, line %d\n",
+ type, altqconfigfile, line_no);
+ return (0);
+ }
+
+ *end = '>'; /* restore the end delimiter */
+
+ return (1);
+}
+
diff --git a/usr.sbin/altq/libaltq/qop.c b/usr.sbin/altq/libaltq/qop.c
new file mode 100644
index 00000000000..361e60eae9f
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop.c
@@ -0,0 +1,1438 @@
+/* $OpenBSD: qop.c,v 1.1 2001/06/27 18:23:26 kjc Exp $ */
+/* $KAME: qop.c,v 1.6 2000/10/18 09:15:18 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#if defined(__FreeBSD__) && (__FreeBSD_version > 300000)
+#include <sys/linker.h>
+#endif
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <err.h>
+#include <syslog.h>
+
+#include <altq/altq.h>
+#include <altq/altq_red.h>
+#include <altq/altq_rio.h>
+#include <altq/altq_cdnr.h>
+#include "altq_qop.h"
+#include "qop_cdnr.h"
+
+#define ALTQ_DEVICE "/dev/altq/altq"
+#define RED_DEVICE "/dev/altq/red"
+#define RIO_DEVICE "/dev/altq/rio"
+#define CDNR_DEVICE "/dev/altq/cdnr"
+
+#ifndef LIST_HEAD_INITIALIZER
+#define LIST_HEAD_INITIALIZER(head) { NULL }
+#endif
+
+/*
+ * token bucket regulator information
+ */
+struct tbrinfo {
+ LIST_ENTRY(tbrinfo) link;
+ char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */
+ struct tb_profile tb_prof, otb_prof;
+ int installed;
+};
+
+/*
+ * Static globals
+ */
+/* a list of configured interfaces */
+LIST_HEAD(qop_iflist, ifinfo) qop_iflist = LIST_HEAD_INITIALIZER(&iflist);
+/* a list of configured token bucket regulators */
+LIST_HEAD(tbr_list, tbrinfo) tbr_list = LIST_HEAD_INITIALIZER(&tbr_list);
+int Debug_mode = 0; /* nosched (dummy mode) */
+
+/*
+ * internal functions
+ */
+static int get_ifmtu(const char *ifname);
+static void tbr_install(const char *ifname);
+static void tbr_deinstall(const char *ifname);
+static int add_filter_rule(struct ifinfo *ifinfo, struct fltrinfo *fltrinfo,
+ struct fltrinfo **conflict);
+static int remove_filter_rule(struct ifinfo *ifinfo,
+ struct fltrinfo *fltrinfo);
+static int filt_check_relation(struct flow_filter *front,
+ struct flow_filter *back);
+static int filt_disjoint(struct flow_filter *front, struct flow_filter *back);
+static int filt_subset(struct flow_filter *front, struct flow_filter *back);
+
+/*
+ * QCMD (Queue Command) API
+ */
+int
+qcmd_init(void)
+{
+ int error;
+
+ /* read config file and execute commands */
+ error = qcmd_config();
+
+ if (error == 0)
+ error = qcmd_enableall();
+
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: qcmd_init failed.\n",
+ qoperror(error));
+ return (error);
+}
+
+int
+qcmd_enable(const char *ifname)
+{
+ struct ifinfo *ifinfo;
+ int error = 0;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ error = QOPERR_BADIF;
+
+ if (error == 0)
+ error = qop_enable(ifinfo);
+
+ if (error == 0) {
+ LOG(LOG_INFO, 0, "%s enabled on interface %s (mtu:%d)\n",
+ ifinfo->qdisc->qname, ifname, ifinfo->ifmtu);
+ } else
+ LOG(LOG_ERR, errno, "%s: enable failed!\n", qoperror(error));
+ return (error);
+}
+
+int
+qcmd_disable(const char *ifname)
+{
+ struct ifinfo *ifinfo;
+ int error = 0;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ error = QOPERR_BADIF;
+
+ if (error == 0)
+ error = qop_disable(ifinfo);
+
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: disable failed!\n", qoperror(error));
+ return (error);
+}
+
+int
+qcmd_enableall()
+{
+ struct ifinfo *ifinfo;
+ int error;
+
+ LIST_FOREACH(ifinfo, &qop_iflist, next) {
+ if ((error = qop_enable(ifinfo)) != 0)
+ return (error);
+ LOG(LOG_INFO, 0, "%s enabled on interface %s (mtu:%d)\n",
+ ifinfo->qdisc->qname, ifinfo->ifname, ifinfo->ifmtu);
+ }
+ return (0);
+}
+
+int
+qcmd_disableall()
+{
+ struct ifinfo *ifinfo;
+ int err, error = 0;
+
+ LIST_FOREACH(ifinfo, &qop_iflist, next)
+ if ((err = qop_disable(ifinfo)) != 0)
+ if (error == 0)
+ error = err;
+ return (error);
+}
+
+int
+qcmd_clear(const char *ifname)
+{
+ struct ifinfo *ifinfo;
+ int error = 0;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ error = QOPERR_BADIF;
+
+ if (error == 0)
+ error = qop_clear(ifinfo);
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: clear failed!\n", qoperror(error));
+ return (error);
+}
+
+int
+qcmd_destroyall(void)
+{
+ while (!LIST_EMPTY(&qop_iflist))
+ (void)qop_delete_if(LIST_FIRST(&qop_iflist));
+ return (0);
+}
+
+int
+qcmd_restart(void)
+{
+ qcmd_destroyall();
+ return qcmd_init();
+}
+
+int
+qcmd_delete_class(const char *ifname, const char *clname)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+ int error = 0;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ error = QOPERR_BADIF;
+
+ if (error == 0 &&
+ (clinfo = clname2clinfo(ifinfo, clname)) == NULL)
+ error = QOPERR_BADCLASS;
+
+ if (error == 0)
+ error = qop_delete_class(clinfo);
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: delete_class failed\n",
+ qoperror(error));
+ return (error);
+}
+
+int
+qcmd_add_filter(const char *ifname, const char *clname, const char *flname,
+ const struct flow_filter *fltr)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+ int error = 0;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ error = QOPERR_BADIF;
+
+ if (error == 0 &&
+ (clinfo = clname2clinfo(ifinfo, clname)) == NULL) {
+ /*
+ * there is no matching class.
+ * check if it is for a traffic conditioner
+ */
+ if ((ifinfo = input_ifname2ifinfo(ifname)) == NULL ||
+ (clinfo = clname2clinfo(ifinfo, clname)) == NULL)
+ error = QOPERR_BADCLASS;
+ }
+
+ if (error == 0)
+ error = qop_add_filter(NULL, clinfo, flname, fltr, NULL);
+
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: add filter failed!\n",
+ qoperror(error));
+ else if (IsDebug(DEBUG_ALTQ)) {
+ LOG(LOG_DEBUG, 0, "%s: add a filter %s to class %s\n",
+ ifname, flname ? flname : "(null)",
+ clname ? clname : "(null)");
+ print_filter(fltr);
+ }
+ return (error);
+}
+
+int
+qcmd_delete_filter(const char *ifname, const char *clname, const char *flname)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+ struct fltrinfo *fltrinfo;
+ int error = 0;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ error = QOPERR_BADIF;
+
+ if (error == 0 &&
+ (clinfo = clname2clinfo(ifinfo, clname)) == NULL) {
+ /*
+ * there is no matching class.
+ * check if it is for a traffic conditioner
+ */
+ if ((ifinfo = input_ifname2ifinfo(ifname)) == NULL ||
+ (clinfo = clname2clinfo(ifinfo, clname)) == NULL)
+ error = QOPERR_BADCLASS;
+ }
+
+ if (error == 0 &&
+ (fltrinfo = flname2flinfo(clinfo, flname)) == NULL)
+ error = QOPERR_BADFILTER;
+
+ if (error == 0)
+ error = qop_delete_filter(fltrinfo);
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: delete filter failed!\n",
+ qoperror(error));
+ return (error);
+}
+
+int
+qcmd_tbr_register(const char *ifname, u_int rate, u_int size)
+{
+ struct tbrinfo *info;
+
+ if ((info = calloc(1, sizeof(struct tbrinfo))) == NULL)
+ return (QOPERR_NOMEM);
+
+ strcpy(info->ifname, ifname);
+ info->tb_prof.rate = rate;
+ info->tb_prof.depth = size;
+ info->installed = 0;
+ LIST_INSERT_HEAD(&tbr_list, info, link);
+ return (0);
+}
+
+/*
+ * QOP (Queue Operation) API
+ */
+
+int
+qop_add_if(struct ifinfo **rp, const char *ifname, u_int bandwidth,
+ struct qdisc_ops *qdisc_ops, void *if_private)
+{
+ struct ifinfo *ifinfo;
+ int error;
+
+ if (ifname2ifinfo(ifname) != NULL) {
+ LOG(LOG_ERR, 0, "qop_add_if: %s already exists!\n", ifname);
+ return (QOPERR_BADIF);
+ }
+
+ if ((ifinfo = calloc(1, sizeof(struct ifinfo))) == NULL)
+ return (QOPERR_NOMEM);
+ ifinfo->ifname = strdup(ifname);
+ ifinfo->bandwidth = bandwidth;
+ ifinfo->enabled = 0;
+ if (ifname[0] == '_')
+ /* input interface */
+ ifname += 1;
+ ifinfo->ifindex = get_ifindex(ifname);
+ ifinfo->ifmtu = get_ifmtu(ifname);
+ if (qdisc_ops == NULL || Debug_mode)
+ ifinfo->qdisc = &nop_qdisc; /* replace syscalls by nops */
+ else
+ ifinfo->qdisc = qdisc_ops;
+ ifinfo->private = if_private;
+ LIST_INIT(&ifinfo->cllist);
+ LIST_INIT(&ifinfo->fltr_rules);
+
+ /* Link the interface info structure */
+ LIST_INSERT_HEAD(&qop_iflist, ifinfo, next);
+
+ /* install token bucket regulator, if necessary */
+ tbr_install(ifname);
+
+ /* attach the discipline to the interface */
+ if ((error = (*ifinfo->qdisc->attach)(ifinfo)) != 0)
+ goto err_ret;
+
+ /* disable and clear the interface */
+ if (ifinfo->qdisc->disable != NULL)
+ if ((error = (*ifinfo->qdisc->disable)(ifinfo)) != 0)
+ goto err_ret;
+ if (ifinfo->qdisc->clear != NULL)
+ if ((error = (*ifinfo->qdisc->clear)(ifinfo)) != 0)
+ goto err_ret;
+
+ if (rp != NULL)
+ *rp = ifinfo;
+ return (0);
+
+err_ret:
+ if (ifinfo != NULL) {
+ LIST_REMOVE(ifinfo, next);
+ if (ifinfo->ifname != NULL)
+ free(ifinfo->ifname);
+ free(ifinfo);
+ }
+ return (error);
+}
+
+int
+qop_delete_if(struct ifinfo *ifinfo)
+{
+ (void)qop_disable(ifinfo);
+ (void)qop_clear(ifinfo);
+
+ if (ifinfo->delete_hook != NULL)
+ (*ifinfo->delete_hook)(ifinfo);
+
+ /* remove this entry from qop_iflist */
+ LIST_REMOVE(ifinfo, next);
+
+ (void)(*ifinfo->qdisc->detach)(ifinfo);
+
+ /* deinstall token bucket regulator, if necessary */
+ tbr_deinstall(ifinfo->ifname);
+
+ if (ifinfo->private != NULL)
+ free(ifinfo->private);
+ if (ifinfo->ifname != NULL)
+ free(ifinfo->ifname);
+ free(ifinfo);
+ return (0);
+}
+
+int
+qop_enable(struct ifinfo *ifinfo)
+{
+ int error;
+
+ if (ifinfo->enable_hook != NULL)
+ if ((error = (*ifinfo->enable_hook)(ifinfo)) != 0)
+ return (error);
+
+ if (ifinfo->qdisc->enable != NULL)
+ if ((error = (*ifinfo->qdisc->enable)(ifinfo)) != 0)
+ return (error);
+ ifinfo->enabled = 1;
+ return (0);
+}
+
+int
+qop_disable(struct ifinfo *ifinfo)
+{
+ int error;
+
+ if (ifinfo->qdisc->disable != NULL)
+ if ((error = (*ifinfo->qdisc->disable)(ifinfo)) != 0)
+ return (error);
+ ifinfo->enabled = 0;
+ return (0);
+}
+
+int
+qop_clear(struct ifinfo *ifinfo)
+{
+ struct classinfo *clinfo;
+
+ /* free all classes and filters */
+ if (ifinfo->ifname[0] != '_') {
+ /* output interface. delete from leaf classes */
+ while (!LIST_EMPTY(&ifinfo->cllist)) {
+ LIST_FOREACH(clinfo, &ifinfo->cllist, next) {
+ if (clinfo->child != NULL)
+ continue;
+ qop_delete_class(clinfo);
+ /*
+ * the list has been changed,
+ * restart from the head
+ */
+ break;
+ }
+ }
+ } else {
+ /* input interface. delete from parents */
+ struct classinfo *root = get_rootclass(ifinfo);
+
+ while (!LIST_EMPTY(&ifinfo->cllist)) {
+ LIST_FOREACH(clinfo, &ifinfo->cllist, next)
+ if (clinfo->parent == root) {
+ qop_delete_cdnr(clinfo);
+ break;
+ }
+ if (root->child == NULL)
+ qop_delete_class(root);
+ }
+ }
+
+ /* clear the interface */
+ if (ifinfo->qdisc->clear != NULL)
+ return (*ifinfo->qdisc->clear)(ifinfo);
+ return (0);
+}
+
+int
+qop_add_class(struct classinfo **rp, const char *clname,
+ struct ifinfo *ifinfo, struct classinfo *parent,
+ void *class_private)
+{
+ struct classinfo *clinfo;
+ int error;
+
+ if ((clinfo = calloc(1, sizeof(*clinfo))) == NULL)
+ return (QOPERR_NOMEM);
+
+ if (clname != NULL)
+ clinfo->clname = strdup(clname);
+ else
+ clinfo->clname = strdup("(null)"); /* dummy name */
+ clinfo->ifinfo = ifinfo;
+ clinfo->private = class_private;
+ clinfo->parent = parent;
+ clinfo->child = NULL;
+ LIST_INIT(&clinfo->fltrlist);
+
+ if ((error = (*ifinfo->qdisc->add_class)(clinfo)) != 0)
+ goto err_ret;
+
+ /* link classinfo in lists */
+ LIST_INSERT_HEAD(&ifinfo->cllist, clinfo, next);
+
+ if (parent != NULL) {
+ clinfo->sibling = parent->child;
+ clinfo->parent->child = clinfo;
+ }
+
+ if (rp != NULL)
+ *rp = clinfo;
+ return (0);
+
+err_ret:
+ if (clinfo != NULL) {
+ if (clinfo->clname != NULL)
+ free(clinfo->clname);
+ free(clinfo);
+ }
+ return (error);
+}
+
+int
+qop_modify_class(struct classinfo *clinfo, void *arg)
+{
+ return (*clinfo->ifinfo->qdisc->modify_class)(clinfo, arg);
+}
+
+int
+qop_delete_class(struct classinfo *clinfo)
+{
+ struct ifinfo *ifinfo = clinfo->ifinfo;
+ struct classinfo *prev;
+ int error;
+
+ /* a class to be removed should not have a child */
+ if (clinfo->child != NULL)
+ return (QOPERR_CLASS_PERM);
+
+ /* remove filters associated to this class */
+ while (!LIST_EMPTY(&clinfo->fltrlist))
+ (void)qop_delete_filter(LIST_FIRST(&clinfo->fltrlist));
+
+ if (clinfo->delete_hook != NULL)
+ (*clinfo->delete_hook)(clinfo);
+
+ /* remove class info from the interface */
+ LIST_REMOVE(clinfo, next);
+
+ /* remove this class from the child list */
+ if (clinfo->parent != NULL) {
+ if (clinfo->parent->child == clinfo)
+ clinfo->parent->child = clinfo->sibling;
+ else for (prev = clinfo->parent->child; prev->sibling != NULL;
+ prev = prev->sibling)
+ if (prev->sibling == clinfo) {
+ prev->sibling = clinfo->sibling;
+ break;
+ }
+ }
+
+ /* delete class from kernel */
+ if ((error = (*ifinfo->qdisc->delete_class)(clinfo)) != 0)
+ return (error);
+
+ if (clinfo->private != NULL)
+ free(clinfo->private);
+ if (clinfo->clname != NULL)
+ free(clinfo->clname);
+ free(clinfo);
+ return (0);
+}
+
+int
+qop_add_filter(struct fltrinfo **rp, struct classinfo *clinfo,
+ const char *flname, const struct flow_filter *fltr,
+ struct fltrinfo **conflict)
+{
+ struct ifinfo *ifinfo;
+ struct fltrinfo *fltrinfo;
+ int error;
+
+ if ((fltrinfo = calloc(1, sizeof(*fltrinfo))) == NULL)
+ return (QOPERR_NOMEM);
+
+ fltrinfo->clinfo = clinfo;
+ fltrinfo->fltr = *fltr;
+#if 1
+ /* fix this */
+ fltrinfo->line_no = line_no; /* XXX */
+ fltrinfo->dontwarn = filter_dontwarn; /* XXX */
+#endif
+ if (flname != NULL)
+ fltrinfo->flname = strdup(flname);
+ else
+ fltrinfo->flname = strdup("(null)"); /* dummy name */
+
+ /* check and save the filter */
+ ifinfo = clinfo->ifinfo;
+ if ((error = add_filter_rule(ifinfo, fltrinfo, conflict)) != 0)
+ goto err_ret;
+
+ /* install the filter to the kernel */
+ if ((error = (*ifinfo->qdisc->add_filter)(fltrinfo)) != 0) {
+ remove_filter_rule(ifinfo, fltrinfo);
+ goto err_ret;
+ }
+
+ /* link fltrinfo onto fltrlist of the class */
+ LIST_INSERT_HEAD(&clinfo->fltrlist, fltrinfo, next);
+
+ if (rp != NULL)
+ *rp = fltrinfo;
+ return (0);
+
+err_ret:
+ if (fltrinfo != NULL) {
+ if (fltrinfo->flname != NULL)
+ free(fltrinfo->flname);
+ free(fltrinfo);
+ }
+ return (error);
+}
+
+int
+qop_delete_filter(struct fltrinfo *fltrinfo)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+ int error;
+
+ /* remove filter info from the class */
+ clinfo = fltrinfo->clinfo;
+ ifinfo = clinfo->ifinfo;
+
+
+ /* remove the entry from fltrlist of the class */
+ LIST_REMOVE(fltrinfo, next);
+
+ remove_filter_rule(ifinfo, fltrinfo);
+
+ /* delete filter from kernel */
+ if ((error = (*ifinfo->qdisc->delete_filter)(fltrinfo)) != 0)
+ return (error);
+
+ if (fltrinfo->flname)
+ free(fltrinfo->flname);
+ free(fltrinfo);
+ return (0);
+}
+
+const char *
+qoperror(int qoperrno)
+{
+ static char buf[64];
+
+ if (qoperrno <= QOPERR_MAX)
+ return (qop_errlist[qoperrno]);
+ sprintf(buf, "unknown error %d", qoperrno);
+ return (buf);
+}
+
+/*
+ * misc functions
+ */
+struct ifinfo *
+ifname2ifinfo(const char *ifname)
+{
+ struct ifinfo *ifinfo;
+
+ LIST_FOREACH(ifinfo, &qop_iflist, next)
+ if (ifinfo->ifname != NULL &&
+ strcmp(ifinfo->ifname, ifname) == 0)
+ return (ifinfo);
+ return (NULL);
+}
+
+struct ifinfo *
+input_ifname2ifinfo(const char *ifname)
+{
+ struct ifinfo *ifinfo;
+
+ LIST_FOREACH(ifinfo, &qop_iflist, next)
+ if (ifinfo->ifname[0] == '_' &&
+ strcmp(ifinfo->ifname+1, ifname) == 0)
+ return (ifinfo);
+ return (NULL);
+}
+
+struct classinfo *
+clname2clinfo(const struct ifinfo *ifinfo, const char *clname)
+{
+ struct classinfo *clinfo;
+
+ LIST_FOREACH(clinfo, &ifinfo->cllist, next)
+ if (clinfo->clname != NULL &&
+ strcmp(clinfo->clname, clname) == 0)
+ return (clinfo);
+ return (NULL);
+}
+
+struct classinfo *
+clhandle2clinfo(struct ifinfo *ifinfo, u_long handle)
+{
+ struct classinfo *clinfo;
+
+ LIST_FOREACH(clinfo, &ifinfo->cllist, next)
+ if (clinfo->handle == handle)
+ return (clinfo);
+ return (NULL);
+}
+
+struct fltrinfo *
+flname2flinfo(const struct classinfo *clinfo, const char *flname)
+{
+ struct fltrinfo *fltrinfo;
+
+ LIST_FOREACH(fltrinfo, &clinfo->fltrlist, next)
+ if (fltrinfo->flname != NULL &&
+ strcmp(fltrinfo->flname, flname) == 0)
+ return (fltrinfo);
+ return (NULL);
+}
+
+struct fltrinfo *
+flhandle2fltrinfo(struct ifinfo *ifinfo, u_long handle)
+{
+ struct fltrinfo *fltrinfo;
+
+ LIST_FOREACH(fltrinfo, &ifinfo->fltr_rules, nextrule)
+ if (fltrinfo->handle == handle)
+ return (fltrinfo);
+ return (NULL);
+}
+
+int
+is_q_enabled(const char *ifname)
+{
+ struct ifinfo *ifinfo;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ return (0);
+ return (ifinfo->enabled);
+}
+
+/*
+ * functions to walk through a class tree:
+ *
+ * for (clinfo = get_rootclass(ifinfo);
+ * clinfo != NULL; clinfo = get_nextclass(clinfo)) {
+ * do_something;
+ * }
+ */
+struct classinfo *get_rootclass(struct ifinfo *ifinfo)
+{
+ struct classinfo *clinfo;
+
+ /* find a class without parent */
+ LIST_FOREACH(clinfo, &ifinfo->cllist, next)
+ if (clinfo->parent == NULL)
+ return (clinfo);
+ return (NULL);
+}
+
+/* return next class in the tree */
+struct classinfo *get_nextclass(struct classinfo *clinfo)
+{
+ struct classinfo *next;
+
+ if (clinfo->child != NULL)
+ next = clinfo->child;
+ else if (clinfo->sibling != NULL)
+ next = clinfo->sibling;
+ else {
+ next = clinfo;
+ while ((next = next->parent) != NULL)
+ if (next->sibling) {
+ next = next->sibling;
+ break;
+ }
+ }
+ return (next);
+}
+
+u_long
+atobps(const char *s)
+{
+ u_long bandwidth;
+ char *cp;
+
+ bandwidth = strtoul(s, &cp, 0);
+ if (cp != NULL) {
+ if (*cp == 'K' || *cp == 'k')
+ bandwidth *= 1000;
+ else if (*cp == 'M' || *cp == 'm')
+ bandwidth *= 1000000;
+ else if (*cp == 'G' || *cp == 'g')
+ bandwidth *= 1000000000;
+ }
+ return (bandwidth);
+}
+
+u_long
+atobytes(const char *s)
+{
+ u_long bytes;
+ char *cp;
+
+ bytes = strtoul(s, &cp, 0);
+ if (cp != NULL) {
+ if (*cp == 'K' || *cp == 'k')
+ bytes *= 1024;
+ else if (*cp == 'M' || *cp == 'm')
+ bytes *= 1024 * 1024;
+ else if (*cp == 'G' || *cp == 'g')
+ bytes *= 1024 * 1024 * 1024;
+ }
+ return (bytes);
+}
+
+static int
+get_ifmtu(const char *ifname)
+{
+ int s, mtu;
+ struct ifreq ifr;
+#ifdef __OpenBSD__
+ struct if_data ifdata;
+#endif
+
+ mtu = 512; /* default MTU */
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ return (mtu);
+ strncpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name);
+#ifdef __OpenBSD__
+ ifr.ifr_data = (caddr_t)&ifdata;
+ if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == 0)
+ mtu = ifdata.ifi_mtu;
+#else
+ if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == 0)
+ mtu = ifr.ifr_mtu;
+#endif
+ close(s);
+ return (mtu);
+}
+
+static void
+tbr_install(const char *ifname)
+{
+ struct tbrinfo *info;
+ struct tbrreq req;
+ int fd;
+
+ LIST_FOREACH(info, &tbr_list, link)
+ if (strcmp(info->ifname, ifname) == 0)
+ break;
+ if (info == NULL)
+ return;
+ if (info->tb_prof.rate == 0 || info->installed)
+ return;
+
+ /* get the current token bucket regulator */
+ if ((fd = open(ALTQ_DEVICE, O_RDWR)) < 0)
+ err(1, "can't open altq device");
+ strncpy(req.ifname, ifname, IFNAMSIZ-1);
+ if (ioctl(fd, ALTQTBRGET, &req) < 0)
+ err(1, "ALTQTBRGET for interface %s", req.ifname);
+
+ /* save the current values */
+ info->otb_prof.rate = req.tb_prof.rate;
+ info->otb_prof.depth = req.tb_prof.depth;
+
+ /*
+ * if tbr is not specified in the config file and tbr is already
+ * configured, do not change.
+ */
+ if (req.tb_prof.rate != 0) {
+ LOG(LOG_INFO, 0,
+ "tbr is already installed on %s,\n"
+ " using the current setting (rate:%.2fM size:%.2fK).\n",
+ info->ifname,
+ (double)req.tb_prof.rate/1000000.0,
+ (double)req.tb_prof.depth/1024.0);
+ close (fd);
+ return;
+ }
+
+ /* if the new size is not specified, use heuristics */
+ if (info->tb_prof.depth == 0) {
+ u_int rate, size;
+
+ rate = info->tb_prof.rate;
+ if (rate <= 1*1000*1000)
+ size = 1;
+ else if (rate <= 10*1000*1000)
+ size = 4;
+ else if (rate <= 200*1000*1000)
+ size = 8;
+ else
+ size = 24;
+ size = size * 1500; /* assume the default mtu is 1500 */
+ info->tb_prof.depth = size;
+ }
+
+ /* install the new tbr */
+ strncpy(req.ifname, ifname, IFNAMSIZ-1);
+ req.tb_prof.rate = info->tb_prof.rate;
+ req.tb_prof.depth = info->tb_prof.depth;
+ if (ioctl(fd, ALTQTBRSET, &req) < 0)
+ err(1, "ALTQTBRSET for interface %s", req.ifname);
+ LOG(LOG_INFO, 0,
+ "tbr installed on %s (rate:%.2fM size:%.2fK)\n",
+ info->ifname,
+ (double)info->tb_prof.rate/1000000.0,
+ (double)info->tb_prof.depth/1024.0);
+ close(fd);
+ info->installed = 1;
+}
+
+static void
+tbr_deinstall(const char *ifname)
+{
+ struct tbrinfo *info;
+ struct tbrreq req;
+ int fd;
+
+ LIST_FOREACH(info, &tbr_list, link)
+ if (strcmp(info->ifname, ifname) == 0)
+ break;
+ if (info == NULL)
+ return;
+
+ /* if we installed tbr, restore the old values */
+ if (info->installed != 0) {
+ strncpy(req.ifname, ifname, IFNAMSIZ-1);
+ req.tb_prof.rate = info->otb_prof.rate;
+ req.tb_prof.depth = info->otb_prof.depth;
+ if ((fd = open(ALTQ_DEVICE, O_RDWR)) < 0)
+ err(1, "can't open altq device");
+ if (ioctl(fd, ALTQTBRSET, &req) < 0)
+ err(1, "ALTQTBRSET for interface %s", req.ifname);
+ close(fd);
+ }
+ LIST_REMOVE(info, link);
+ free(info);
+}
+
+void
+print_filter(const struct flow_filter *filt)
+{
+ if (filt->ff_flow.fi_family == AF_INET) {
+ struct in_addr in_addr;
+
+ in_addr.s_addr = filt->ff_flow.fi_dst.s_addr;
+ LOG(LOG_DEBUG, 0,
+ " Filter Dest Addr: %s (mask %#x) Port: %d\n",
+ inet_ntoa(in_addr), ntoh32(filt->ff_mask.mask_dst.s_addr),
+ ntoh16(filt->ff_flow.fi_dport));
+ in_addr.s_addr = filt->ff_flow.fi_src.s_addr;
+ LOG(LOG_DEBUG, 0,
+ " Src Addr: %s (mask %#x) Port: %d\n",
+ inet_ntoa(in_addr), ntoh32(filt->ff_mask.mask_src.s_addr),
+ ntoh16(filt->ff_flow.fi_sport));
+ LOG(LOG_DEBUG, 0, " Protocol: %d TOS %#x (mask %#x)\n",
+ filt->ff_flow.fi_proto, filt->ff_flow.fi_tos,
+ filt->ff_mask.mask_tos);
+ }
+#ifdef INET6
+ else if (filt->ff_flow.fi_family == AF_INET6) {
+ char str1[INET6_ADDRSTRLEN], str2[INET6_ADDRSTRLEN];
+ const struct flow_filter6 *sfilt6;
+
+ sfilt6 = (const struct flow_filter6 *)filt;
+ LOG(LOG_DEBUG, 0, "Filter6 Dest Addr: %s (mask %s) Port: %d",
+ inet_ntop(AF_INET6, &sfilt6->ff_flow6.fi6_dst,
+ str1, sizeof(str1)),
+ inet_ntop(AF_INET6, &sfilt6->ff_mask6.mask6_dst,
+ str2, sizeof(str2)),
+ ntoh16(sfilt6->ff_flow6.fi6_dport));
+ LOG(LOG_DEBUG, 0, " Src Addr: %s (mask %s) Port: %d",
+ inet_ntop(AF_INET6, &sfilt6->ff_flow6.fi6_src,
+ str1, sizeof(str1)),
+ inet_ntop(AF_INET6, &sfilt6->ff_mask6.mask6_src,
+ str2, sizeof(str2)),
+ ntoh16(sfilt6->ff_flow6.fi6_sport));
+ LOG(LOG_DEBUG, 0, " Protocol: %d TCLASS %#x (mask %#x)\n",
+ sfilt6->ff_flow6.fi6_proto, sfilt6->ff_flow6.fi6_tclass,
+ sfilt6->ff_mask6.mask6_tclass);
+ }
+#endif /* INET6 */
+}
+
+/*
+ * functions to check the filter-rules.
+ * when a new filter is added, we check the relation to the existing filters
+ * and if some inconsistency is found, produce an error or a warning message.
+ *
+ * filter matching is performed from the head of the list.
+ * let
+ * S: a set of packets that filter s matches
+ * T: a set of packets that filter t matches
+ * filter relations are:
+ * disjoint: S ^ T = empty
+ * subset: S <= T
+ * intersect: S ^ T = not empty
+ *
+ * a new filter is disjoint or subset of the existing filters --> ok
+ * a new filter is superset of an existing filter --> order problem
+ * a new filter intersect an existing filter --> warning
+ *
+ * port-intersect: a special case we don't make warning
+ * - intersection is only port numbers
+ * - one specifies src port and the other specifies dst port
+ * there must be no packet with well-known port numbers in
+ * both src and dst ports. so this is ok.
+ */
+
+#define FILT_DISJOINT 1
+#define FILT_SUBSET 2
+#define FILT_SUPERSET 3
+#define FILT_INTERSECT 4
+#define FILT_PORTINTERSECT 5
+
+static int
+add_filter_rule(struct ifinfo *ifinfo, struct fltrinfo *fltrinfo,
+ struct fltrinfo **conflict)
+{
+ struct fltrinfo *fp, *front, *back, *prev = NULL;
+ int relation;
+
+ LIST_FOREACH(fp, &ifinfo->fltr_rules, nextrule) {
+ if (fp->fltr.ff_ruleno > fltrinfo->fltr.ff_ruleno) {
+ front = fp;
+ back = fltrinfo;
+ prev = fp;
+ } else {
+ front = fltrinfo;
+ back = fp;
+ }
+
+ relation = filt_check_relation(&front->fltr, &back->fltr);
+
+ switch (relation) {
+ case FILT_SUBSET:
+ case FILT_DISJOINT:
+ /* OK */
+ break;
+ case FILT_SUPERSET:
+ if (front->dontwarn == 0 && back->dontwarn == 0)
+ LOG(LOG_ERR, 0,
+ "filters for \"%s\" at line %d and for \"%s\" at line %d has an order problem!\n",
+ front->clinfo->clname, front->line_no,
+ back->clinfo->clname, back->line_no);
+
+ if (conflict != NULL)
+ *conflict = fp;
+ return (QOPERR_FILTER_SHADOW);
+ case FILT_PORTINTERSECT:
+ break;
+ case FILT_INTERSECT:
+ /*
+ * if the intersecting two filters beloging to the
+ * same class, it's ok.
+ */
+ if (front->clinfo == back->clinfo)
+ break;
+ if (front->dontwarn == 0 && back->dontwarn == 0)
+ LOG(LOG_WARNING, 0,
+ "warning: filter for \"%s\" at line %d could override filter for \"%s\" at line %d\n",
+ front->clinfo->clname, front->line_no,
+ back->clinfo->clname, back->line_no);
+ break;
+ }
+ }
+
+ if (prev == NULL)
+ LIST_INSERT_HEAD(&ifinfo->fltr_rules, fltrinfo, nextrule);
+ else
+ LIST_INSERT_AFTER(prev, fltrinfo, nextrule);
+ return (0);
+}
+
+static int
+remove_filter_rule(struct ifinfo *ifinfo, struct fltrinfo *fltrinfo)
+{
+ LIST_REMOVE(fltrinfo, nextrule);
+ return (0);
+}
+
+static int
+filt_check_relation(struct flow_filter *front, struct flow_filter *back)
+{
+ int rval;
+
+ if (front->ff_flow.fi_family != back->ff_flow.fi_family)
+ return (FILT_DISJOINT);
+
+ if (filt_disjoint(front, back))
+ return (FILT_DISJOINT);
+
+ if ((rval = filt_subset(front, back)) == 1)
+ return (FILT_SUBSET);
+
+ if (filt_subset(back, front) == 1)
+ return (FILT_SUPERSET);
+
+ if (rval == 2)
+ return (FILT_PORTINTERSECT);
+
+ return (FILT_INTERSECT);
+}
+
+static int
+filt_disjoint(struct flow_filter *front, struct flow_filter *back)
+{
+ u_int32_t mask;
+ u_int8_t tosmask;
+
+ if (front->ff_flow.fi_family == AF_INET) {
+ if (front->ff_flow.fi_proto != 0 && back->ff_flow.fi_proto != 0
+ && front->ff_flow.fi_proto != back->ff_flow.fi_proto)
+ return (1);
+ if (front->ff_flow.fi_sport != 0 && back->ff_flow.fi_sport != 0
+ && front->ff_flow.fi_sport != back->ff_flow.fi_sport)
+ return (1);
+ if (front->ff_flow.fi_dport != 0 && back->ff_flow.fi_dport != 0
+ && front->ff_flow.fi_dport != back->ff_flow.fi_dport)
+ return (1);
+ if (front->ff_flow.fi_gpi != 0 && back->ff_flow.fi_gpi != 0
+ && front->ff_flow.fi_gpi != back->ff_flow.fi_gpi)
+ return (1);
+ if (front->ff_flow.fi_src.s_addr != 0 &&
+ back->ff_flow.fi_src.s_addr != 0) {
+ mask = front->ff_mask.mask_src.s_addr &
+ back->ff_mask.mask_src.s_addr;
+ if ((front->ff_flow.fi_src.s_addr & mask) !=
+ (back->ff_flow.fi_src.s_addr & mask))
+ return (1);
+ }
+ if (front->ff_flow.fi_dst.s_addr != 0 &&
+ back->ff_flow.fi_dst.s_addr != 0) {
+ mask = front->ff_mask.mask_dst.s_addr &
+ back->ff_mask.mask_dst.s_addr;
+ if ((front->ff_flow.fi_dst.s_addr & mask) !=
+ (back->ff_flow.fi_dst.s_addr & mask))
+ return (1);
+ }
+ if (front->ff_flow.fi_tos != 0 && back->ff_flow.fi_tos != 0) {
+ tosmask = front->ff_mask.mask_tos &
+ back->ff_mask.mask_tos;
+ if ((front->ff_flow.fi_tos & tosmask) !=
+ (back->ff_flow.fi_tos & tosmask))
+ return (1);
+ }
+ return (0);
+ }
+#ifdef INET6
+ else if (front->ff_flow.fi_family == AF_INET6) {
+ struct flow_filter6 *front6, *back6;
+ int i;
+
+ front6 = (struct flow_filter6 *)front;
+ back6 = (struct flow_filter6 *)back;
+
+ if (front6->ff_flow6.fi6_proto != 0 &&
+ back6->ff_flow6.fi6_proto != 0 &&
+ front6->ff_flow6.fi6_proto != back6->ff_flow6.fi6_proto)
+ return (1);
+ if (front6->ff_flow6.fi6_flowlabel != 0 &&
+ back6->ff_flow6.fi6_flowlabel != 0 &&
+ front6->ff_flow6.fi6_flowlabel !=
+ back6->ff_flow6.fi6_flowlabel)
+ return (1);
+ if (front6->ff_flow6.fi6_sport != 0 &&
+ back6->ff_flow6.fi6_sport != 0 &&
+ front6->ff_flow6.fi6_sport != back6->ff_flow6.fi6_sport)
+ return (1);
+ if (front6->ff_flow6.fi6_dport != 0 &&
+ back6->ff_flow6.fi6_dport != 0 &&
+ front6->ff_flow6.fi6_dport != back6->ff_flow6.fi6_dport)
+ return (1);
+ if (front6->ff_flow6.fi6_gpi != 0 &&
+ back6->ff_flow6.fi6_gpi != 0 &&
+ front6->ff_flow6.fi6_gpi != back6->ff_flow6.fi6_gpi)
+ return (1);
+ if (!IN6_IS_ADDR_UNSPECIFIED(&front6->ff_flow6.fi6_src) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&back6->ff_flow6.fi6_src)) {
+ for (i=0; i<4; i++) {
+ mask = IN6ADDR32(&front6->ff_mask6.mask6_src, i)
+ & IN6ADDR32(&back6->ff_mask6.mask6_src, i);
+ if ((IN6ADDR32(&front6->ff_flow6.fi6_src, i) & mask) !=
+ (IN6ADDR32(&back6->ff_flow6.fi6_src, i) & mask))
+ return (1);
+ }
+ }
+ if (!IN6_IS_ADDR_UNSPECIFIED(&front6->ff_flow6.fi6_dst) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&back6->ff_flow6.fi6_dst)) {
+ for (i=0; i<4; i++) {
+ mask = IN6ADDR32(&front6->ff_mask6.mask6_dst, i)
+ & IN6ADDR32(&back6->ff_mask6.mask6_dst, i);
+ if ((IN6ADDR32(&front6->ff_flow6.fi6_dst, i) & mask) !=
+ (IN6ADDR32(&back6->ff_flow6.fi6_dst, i) & mask))
+ return (1);
+ }
+ }
+ if (front6->ff_flow6.fi6_tclass != 0 &&
+ back6->ff_flow6.fi6_tclass != 0) {
+ tosmask = front6->ff_mask6.mask6_tclass &
+ back6->ff_mask6.mask6_tclass;
+ if ((front6->ff_flow6.fi6_tclass & tosmask) !=
+ (back6->ff_flow6.fi6_tclass & tosmask))
+ return (1);
+ }
+ return (0);
+ }
+#endif /* INET6 */
+ return (0);
+}
+
+/*
+ * check if "front" is a subset of "back". assumes they are not disjoint
+ * return value 0: not a subset
+ * 1: subset
+ * 2: subset except src & dst ports
+ * (possible port-intersect)
+ */
+static int
+filt_subset(struct flow_filter *front, struct flow_filter *back)
+{
+ u_int16_t srcport, dstport;
+
+ if (front->ff_flow.fi_family == AF_INET) {
+ if (front->ff_flow.fi_proto == 0 &&
+ back->ff_flow.fi_proto != 0)
+ return (0);
+ if (front->ff_flow.fi_gpi == 0 && back->ff_flow.fi_gpi != 0)
+ return (0);
+ if (front->ff_flow.fi_src.s_addr == 0) {
+ if (back->ff_flow.fi_src.s_addr != 0)
+ return (0);
+ } else if (back->ff_flow.fi_src.s_addr != 0 &&
+ (~front->ff_mask.mask_src.s_addr &
+ back->ff_mask.mask_src.s_addr))
+ return (0);
+ if (front->ff_flow.fi_dst.s_addr == 0) {
+ if (back->ff_flow.fi_dst.s_addr != 0)
+ return (0);
+ } else if (back->ff_flow.fi_dst.s_addr != 0 &&
+ (~front->ff_mask.mask_dst.s_addr &
+ back->ff_mask.mask_dst.s_addr))
+ return (0);
+ if (~front->ff_mask.mask_tos & back->ff_mask.mask_tos)
+ return (0);
+
+ if (front->ff_flow.fi_sport == 0 &&
+ back->ff_flow.fi_sport != 0) {
+ srcport = ntohs(back->ff_flow.fi_sport);
+ dstport = ntohs(front->ff_flow.fi_dport);
+ if (dstport > 0 /* && dstport < 1024 */ &&
+ srcport > 0 /* && srcport < 1024 */)
+ return (2);
+ return (0);
+ }
+ if (front->ff_flow.fi_dport == 0 &&
+ back->ff_flow.fi_dport != 0) {
+ dstport = ntohs(back->ff_flow.fi_dport);
+ srcport = ntohs(front->ff_flow.fi_sport);
+ if (srcport > 0 /* && srcport < 1024 */ &&
+ dstport > 0 /* && dstport < 1024 */)
+ return (2);
+ return (0);
+ }
+
+ return (1);
+ }
+#ifdef INET6
+ else if (front->ff_flow.fi_family == AF_INET6) {
+ struct flow_filter6 *front6, *back6;
+ int i;
+
+ front6 = (struct flow_filter6 *)front;
+ back6 = (struct flow_filter6 *)back;
+
+ if (front6->ff_flow6.fi6_proto == 0 &&
+ back6->ff_flow6.fi6_proto != 0)
+ return (0);
+ if (front6->ff_flow6.fi6_flowlabel == 0 &&
+ back6->ff_flow6.fi6_flowlabel != 0)
+ return (0);
+ if (front6->ff_flow6.fi6_gpi == 0 &&
+ back6->ff_flow6.fi6_gpi != 0)
+ return (0);
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&front6->ff_flow6.fi6_src)) {
+ if (!IN6_IS_ADDR_UNSPECIFIED(&back6->ff_flow6.fi6_src))
+ return (0);
+ } else if (!IN6_IS_ADDR_UNSPECIFIED(&back6->ff_flow6.fi6_src))
+ for (i=0; i<4; i++)
+ if (~IN6ADDR32(&front6->ff_mask6.mask6_src, i) &
+ IN6ADDR32(&back6->ff_mask6.mask6_src, i))
+ return (0);
+ if (IN6_IS_ADDR_UNSPECIFIED(&front6->ff_flow6.fi6_dst)) {
+ if (!IN6_IS_ADDR_UNSPECIFIED(&back6->ff_flow6.fi6_dst))
+ return (0);
+ } else if (!IN6_IS_ADDR_UNSPECIFIED(&back6->ff_flow6.fi6_dst))
+ for (i=0; i<4; i++)
+ if (~IN6ADDR32(&front6->ff_mask6.mask6_dst, i) &
+ IN6ADDR32(&back6->ff_mask6.mask6_dst, i))
+ return (0);
+
+ if (~front6->ff_mask6.mask6_tclass &
+ back6->ff_mask6.mask6_tclass)
+ return (0);
+
+ if (front6->ff_flow6.fi6_sport == 0 &&
+ back6->ff_flow6.fi6_sport != 0) {
+ srcport = ntohs(back6->ff_flow6.fi6_sport);
+ dstport = ntohs(front6->ff_flow6.fi6_dport);
+ if (dstport > 0 /* && dstport < 1024 */ &&
+ srcport > 0 /* && srcport < 1024 */)
+ return (2);
+ return (0);
+ }
+ if (front6->ff_flow6.fi6_dport == 0 &&
+ back6->ff_flow6.fi6_dport != 0) {
+ dstport = ntohs(back6->ff_flow6.fi6_dport);
+ srcport = ntohs(front6->ff_flow6.fi6_sport);
+ if (srcport > 0 /* && srcport < 1024 */ &&
+ dstport > 0 /* && dstport < 1024 */)
+ return (2);
+ return (0);
+ }
+ }
+#endif /* INET6 */
+ return (1);
+}
+
+
+/*
+ * setting RED or RIO default parameters
+ */
+int
+qop_red_set_defaults(int th_min, int th_max, int inv_pmax)
+{
+ struct redparams params;
+ int fd;
+
+ if ((fd = open(RED_DEVICE, O_RDWR)) < 0) {
+ LOG(LOG_ERR, errno, "RED open\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ params.th_min = th_min;
+ params.th_max = th_max;
+ params.inv_pmax = inv_pmax;
+
+ if (ioctl(fd, RED_SETDEFAULTS, &params) < 0) {
+ LOG(LOG_ERR, errno, "RED_SETDEFAULTS\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ (void)close(fd);
+ return (0);
+}
+
+int
+qop_rio_set_defaults(struct redparams *params)
+{
+ int i, fd;
+
+ /* sanity check */
+ for (i = 1; i < RIO_NDROPPREC; i++) {
+ if (params[i].th_max > params[i-1].th_min)
+ LOG(LOG_WARNING, 0,
+ "warning: overlap found in RIO thresholds\n");
+ }
+
+ if ((fd = open(RIO_DEVICE, O_RDWR)) < 0) {
+ LOG(LOG_ERR, errno, "RIO open\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ if (ioctl(fd, RIO_SETDEFAULTS, params) < 0) {
+ LOG(LOG_ERR, errno, "RIO_SETDEFAULTS\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ (void)close(fd);
+ return (0);
+}
+
+/*
+ * try to load and open KLD module
+ */
+int
+open_module(const char *devname, int flags)
+{
+#if defined(__FreeBSD__) && (__FreeBSD_version > 300000)
+ char modname[64], filename[256], *cp;
+ int fd;
+ struct stat sbuf;
+
+ /* turn discipline name into module name */
+ strcpy(modname, "altq_");
+ if ((cp = strrchr(devname, '/')) == NULL)
+ return (-1);
+ strcat(modname, cp+1);
+
+ /* check if the kld module exists */
+ sprintf(filename, "/modules/%s.ko", modname);
+ if (stat(filename, &sbuf) < 0) {
+ /* module file doesn't exist */
+ return (-1);
+ }
+
+ if (kldload(modname) < 0) {
+ LOG(LOG_ERR, errno, "kldload %s failed!\n", modname);
+ return (-1);
+ }
+
+ /* successfully loaded, open the device */
+ LOG(LOG_INFO, 0, "kld module %s loaded\n", modname);
+ fd = open(devname, flags);
+ return (fd);
+#else
+ return (-1);
+#endif
+}
+
diff --git a/usr.sbin/altq/libaltq/qop_blue.c b/usr.sbin/altq/libaltq/qop_blue.c
new file mode 100644
index 00000000000..a9dd8269a57
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_blue.c
@@ -0,0 +1,278 @@
+/* $OpenBSD: qop_blue.c,v 1.1 2001/06/27 18:23:27 kjc Exp $ */
+/* $KAME: qop_blue.c,v 1.3 2000/10/18 09:15:18 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <altq/altq.h>
+#include <altq/altq_blue.h>
+#include "altq_qop.h"
+#include "qop_blue.h"
+
+static int blue_attach(struct ifinfo *ifinfo);
+static int blue_detach(struct ifinfo *ifinfo);
+static int blue_enable(struct ifinfo *ifinfo);
+static int blue_disable(struct ifinfo *ifinfo);
+
+#define BLUE_DEVICE "/dev/altq/blue"
+
+static int blue_fd = -1;
+static int blue_refcount = 0;
+
+static struct qdisc_ops blue_qdisc = {
+ ALTQT_BLUE,
+ "blue",
+ blue_attach,
+ blue_detach,
+ NULL, /* clear */
+ blue_enable,
+ blue_disable,
+ NULL, /* add class */
+ NULL, /* modify class */
+ NULL, /* delete class */
+ NULL, /* add filter */
+ NULL /* delete filter */
+};
+
+/*
+ * parser interface
+ */
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+int
+blue_interface_parser(const char *ifname, int argc, char **argv)
+{
+ u_int bandwidth = 100000000; /* 100Mbps */
+ u_int tbrsize = 0;
+ int max_pmark = 4000;
+ int hold_time = 1000;
+ int qlimit = 60;
+ int pkttime = 0;
+ int flags = 0;
+ int packet_size = 1000;
+
+ /*
+ * process options
+ */
+ while (argc > 0) {
+ if (EQUAL(*argv, "bandwidth")) {
+ argc--; argv++;
+ if (argc > 0)
+ bandwidth = atobps(*argv);
+ } else if (EQUAL(*argv, "tbrsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ tbrsize = atobytes(*argv);
+ } else if (EQUAL(*argv, "packetsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ packet_size = atobytes(*argv);
+ } else if (EQUAL(*argv, "qlimit")) {
+ argc--; argv++;
+ if (argc > 0)
+ qlimit = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "maxpmark")) {
+ argc--; argv++;
+ if (argc > 0)
+ max_pmark = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "holdtime")) {
+ argc--; argv++;
+ if (argc > 0)
+ hold_time = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "blue")) {
+ /* just skip */
+ } else if (EQUAL(*argv, "ecn")) {
+ flags |= BLUEF_ECN;
+ } else {
+ LOG(LOG_ERR, 0, "Unknown keyword '%s'\n", argv);
+ return (0);
+ }
+ argc--; argv++;
+ }
+
+ if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0)
+ return (0);
+
+ pkttime = packet_size * 8 * 1000 / (bandwidth / 1000);
+
+ if (qcmd_blue_add_if(ifname, bandwidth, max_pmark, hold_time,
+ qlimit, pkttime, flags) != 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * qcmd api
+ */
+int
+qcmd_blue_add_if(const char *ifname, u_int bandwidth, int max_pmark,
+ int hold_time, int qlimit, int pkttime, int flags)
+{
+ int error;
+
+ error = qop_blue_add_if(NULL, ifname, bandwidth, max_pmark, hold_time,
+ qlimit, pkttime, flags);
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: can't add blue on interface '%s'\n",
+ qoperror(error), ifname);
+ return (error);
+}
+
+/*
+ * qop api
+ */
+int
+qop_blue_add_if(struct ifinfo **rp, const char *ifname, u_int bandwidth,
+ int max_pmark, int hold_time, int qlimit,
+ int pkttime, int flags)
+{
+ struct ifinfo *ifinfo = NULL;
+ struct blue_ifinfo *blue_ifinfo;
+ int error;
+
+ if ((blue_ifinfo = calloc(1, sizeof(*blue_ifinfo))) == NULL)
+ return (QOPERR_NOMEM);
+ blue_ifinfo->max_pmark = max_pmark;
+ blue_ifinfo->hold_time = hold_time;
+ blue_ifinfo->qlimit = qlimit;
+ blue_ifinfo->pkttime = pkttime;
+ blue_ifinfo->flags = flags;
+
+ error = qop_add_if(&ifinfo, ifname, bandwidth,
+ &blue_qdisc, blue_ifinfo);
+ if (error != 0) {
+ free(blue_ifinfo);
+ return (error);
+ }
+
+ if (rp != NULL)
+ *rp = ifinfo;
+ return (0);
+}
+
+/*
+ * system call interfaces for qdisc_ops
+ */
+static int
+blue_attach(struct ifinfo *ifinfo)
+{
+ struct blue_interface iface;
+ struct blue_ifinfo *blue_ifinfo;
+ struct blue_conf conf;
+
+ if (blue_fd < 0 &&
+ (blue_fd = open(BLUE_DEVICE, O_RDWR)) < 0 &&
+ (blue_fd = open_module(BLUE_DEVICE, O_RDWR)) < 0) {
+ LOG(LOG_ERR, errno, "BLUE open\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ blue_refcount++;
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.blue_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(blue_fd, BLUE_IF_ATTACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ /* set blue parameters */
+ blue_ifinfo = (struct blue_ifinfo *)ifinfo->private;
+ memset(&conf, 0, sizeof(conf));
+ strncpy(conf.iface.blue_ifname, ifinfo->ifname, IFNAMSIZ);
+ conf.blue_max_pmark = blue_ifinfo->max_pmark;
+ conf.blue_hold_time = blue_ifinfo->hold_time;
+ conf.blue_limit = blue_ifinfo->qlimit;
+ conf.blue_pkttime = blue_ifinfo->pkttime;
+ conf.blue_flags = blue_ifinfo->flags;
+ if (ioctl(blue_fd, BLUE_CONFIG, &conf) < 0)
+ return (QOPERR_SYSCALL);
+
+#if 1
+ LOG(LOG_INFO, 0, "blue attached to %s\n", iface.blue_ifname);
+#endif
+ return (0);
+}
+
+static int
+blue_detach(struct ifinfo *ifinfo)
+{
+ struct blue_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.blue_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(blue_fd, BLUE_IF_DETACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ if (--blue_refcount == 0) {
+ close(blue_fd);
+ blue_fd = -1;
+ }
+ return (0);
+}
+
+static int
+blue_enable(struct ifinfo *ifinfo)
+{
+ struct blue_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.blue_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(blue_fd, BLUE_ENABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+blue_disable(struct ifinfo *ifinfo)
+{
+ struct blue_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.blue_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(blue_fd, BLUE_DISABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
diff --git a/usr.sbin/altq/libaltq/qop_blue.h b/usr.sbin/altq/libaltq/qop_blue.h
new file mode 100644
index 00000000000..8e1c67fce9a
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_blue.h
@@ -0,0 +1,45 @@
+/* $OpenBSD: qop_blue.h,v 1.1 2001/06/27 18:23:27 kjc Exp $ */
+/* $KAME: qop_blue.h,v 1.2 2000/10/18 09:15:18 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * blue private ifinfo structure
+ */
+struct blue_ifinfo {
+ int max_pmark; /* precision of marking probability */
+ int hold_time; /* hold time in usec */
+ int qlimit; /* max queue length */
+ int pkttime; /* average packet time in usec */
+ int flags; /* see below */
+};
+
+int blue_interface_parser(const char *ifname, int argc, char **argv);
+int qcmd_blue_add_if(const char *ifname, u_int bandwidth, int max_pmark,
+ int hold_time, int qlimit, int pkttime, int flags);
+int qop_blue_add_if(struct ifinfo **rp, const char *ifname, u_int bandwidth,
+ int max_pmark, int hold_time, int qlimit,
+ int pkttime, int flags);
diff --git a/usr.sbin/altq/libaltq/qop_cbq.c b/usr.sbin/altq/libaltq/qop_cbq.c
new file mode 100644
index 00000000000..2e627090dde
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_cbq.c
@@ -0,0 +1,1082 @@
+/* $OpenBSD: qop_cbq.c,v 1.1 2001/06/27 18:23:29 kjc Exp $ */
+/* $KAME: qop_cbq.c,v 1.3 2000/10/18 09:15:18 kjc Exp $ */
+/*
+ * Copyright (c) Sun Microsystems, Inc. 1993-1998 All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the SMCC Technology
+ * Development Group at Sun Microsystems, Inc.
+ *
+ * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
+ * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is
+ * provided "as is" without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this software.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <math.h>
+
+#include <altq/altq.h>
+#include <altq/altq_cbq.h>
+#include "altq_qop.h"
+#include "qop_cbq.h"
+
+static int qcmd_cbq_add_ctl_filters(const char *ifname, const char *clname);
+
+static int qop_cbq_enable_hook(struct ifinfo *ifinfo);
+static int qop_cbq_delete_class_hook(struct classinfo *clinfo);
+
+static int cbq_class_spec(struct ifinfo *ifinfo, u_long parent_class,
+ u_long borrow_class, u_int pri, int flags,
+ u_int bandwidth, u_int maxdelay, u_int maxburst,
+ u_int minburst, u_int av_pkt_size,
+ u_int max_pkt_size, cbq_class_spec_t *cl_spec);
+
+static int cbq_attach(struct ifinfo *ifinfo);
+static int cbq_detach(struct ifinfo *ifinfo);
+static int cbq_clear(struct ifinfo *ifinfo);
+static int cbq_enable(struct ifinfo *ifinfo);
+static int cbq_disable(struct ifinfo *ifinfo);
+static int cbq_add_class(struct classinfo *clinfo);
+static int cbq_modify_class(struct classinfo *clinfo, void *arg);
+static int cbq_delete_class(struct classinfo *clinfo);
+static int cbq_add_filter(struct fltrinfo *fltrinfo);
+static int cbq_delete_filter(struct fltrinfo *fltrinfo);
+
+#define CTL_PBANDWIDTH 2
+#define NS_PER_MS (1000000.0)
+#define NS_PER_SEC (NS_PER_MS*1000.0)
+#define RM_FILTER_GAIN 5
+
+#define CBQ_DEVICE "/dev/altq/cbq"
+
+static int cbq_fd = -1;
+static int cbq_refcount = 0;
+
+static struct qdisc_ops cbq_qdisc = {
+ ALTQT_CBQ,
+ "cbq",
+ cbq_attach,
+ cbq_detach,
+ cbq_clear,
+ cbq_enable,
+ cbq_disable,
+ cbq_add_class,
+ cbq_modify_class,
+ cbq_delete_class,
+ cbq_add_filter,
+ cbq_delete_filter,
+};
+
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+/*
+ * parser interface
+ */
+int
+cbq_interface_parser(const char *ifname, int argc, char **argv)
+{
+ u_int bandwidth = 100000000; /* 100Mbps */
+ u_int tbrsize = 0;
+ u_int is_efficient = 0;
+ u_int is_wrr = 1; /* weighted round-robin is default */
+
+ /*
+ * process options
+ */
+ while (argc > 0) {
+ if (EQUAL(*argv, "bandwidth")) {
+ argc--; argv++;
+ if (argc > 0)
+ bandwidth = atobps(*argv);
+ } else if (EQUAL(*argv, "tbrsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ tbrsize = atobytes(*argv);
+ } else if (EQUAL(*argv, "efficient")) {
+ is_efficient = 1;
+ } else if (EQUAL(*argv, "cbq")) {
+ /* just skip */
+ } else if (EQUAL(*argv, "cbq-wrr")) {
+ is_wrr = 1;
+ } else if (EQUAL(*argv, "cbq-prr")) {
+ is_wrr = 0;
+ } else {
+ LOG(LOG_ERR, 0, "Unknown keyword '%s'\n", argv);
+ return (0);
+ }
+ argc--; argv++;
+ }
+
+ if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0)
+ return (0);
+
+ if (qcmd_cbq_add_if(ifname, bandwidth,
+ is_wrr, is_efficient) != 0)
+ return (0);
+ return (1);
+}
+
+int
+cbq_class_parser(const char *ifname, const char *class_name,
+ const char *parent_name, int argc, char **argv)
+{
+ const char *borrow = NULL;
+ u_int pri = 1;
+ u_int pbandwidth = 0;
+ u_int bandwidth = 0;
+ u_int maxdelay = 0; /* 0 means default */
+ u_int maxburst = 0; /* 0 means default */
+ u_int minburst = 0; /* 0 means default */
+ u_int av_pkt_size = 0; /* 0 means use if mtu as default */
+ u_int max_pkt_size = 0; /* 0 means use if mtu as default */
+ int flags = 0;
+ cbq_tos_t admission_type = CBQ_QOS_NONE;
+ int error;
+
+ if (parent_name == NULL)
+ flags |= CBQCLF_ROOTCLASS;
+
+ while (argc > 0) {
+ if (EQUAL(*argv, "priority")) {
+ argc--; argv++;
+ if (argc > 0)
+ pri = strtoul(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "default")) {
+ flags |= CBQCLF_DEFCLASS;
+ } else if (EQUAL(*argv, "control")) {
+ flags |= CBQCLF_CTLCLASS;
+ } else if (EQUAL(*argv, "admission")) {
+ argc--; argv++;
+ if (argc > 0) {
+ if (EQUAL(*argv, "guaranteed"))
+ admission_type = CBQ_QOS_GUARANTEED;
+ else if (EQUAL(*argv, "predictive"))
+ admission_type = CBQ_QOS_PREDICTIVE;
+ else if (EQUAL(*argv, "cntlload"))
+ admission_type = CBQ_QOS_CNTR_LOAD;
+ else if (EQUAL(*argv, "cntldelay"))
+ admission_type = CBQ_QOS_CNTR_DELAY;
+ else if (EQUAL(*argv, "none"))
+ admission_type = CBQ_QOS_NONE;
+ else {
+ LOG(LOG_ERR, 0,
+ "unknown admission type - %s, line %d\n",
+ *argv, line_no);
+ return (0);
+ }
+ }
+ } else if (EQUAL(*argv, "maxdelay")) {
+ argc--; argv++;
+ if (argc > 0)
+ maxdelay = strtoul(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "borrow")) {
+ borrow = parent_name;
+#if 1
+ /* support old style "borrow [parent]" */
+ if (argc > 1 &&
+ EQUAL(*(argv + 1), parent_name)) {
+ /* old style, skip borrow_name */
+ argc--; argv++;
+ }
+#endif
+ } else if (EQUAL(*argv, "pbandwidth")) {
+ argc--; argv++;
+ if (argc > 0)
+ pbandwidth = strtoul(*argv, NULL, 0);
+ if (pbandwidth > 100) {
+ LOG(LOG_ERR, 0,
+ "bad pbandwidth %d for %s!\n",
+ pbandwidth, class_name);
+ return (0);
+ }
+ } else if (EQUAL(*argv, "exactbandwidth")) {
+ argc--; argv++;
+ if (argc > 0)
+ bandwidth = atobps(*argv);
+ } else if (EQUAL(*argv, "maxburst")) {
+ argc--; argv++;
+ if (argc > 0)
+ maxburst = strtoul(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "minburst")) {
+ argc--; argv++;
+ if (argc > 0)
+ minburst = strtoul(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "packetsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ av_pkt_size = atobytes(*argv);
+ } else if (EQUAL(*argv, "maxpacketsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ max_pkt_size = atobytes(*argv);
+ } else if (EQUAL(*argv, "red")) {
+ flags |= CBQCLF_RED;
+ } else if (EQUAL(*argv, "ecn")) {
+ flags |= CBQCLF_ECN;
+ } else if (EQUAL(*argv, "flowvalve")) {
+ flags |= CBQCLF_FLOWVALVE;
+ } else if (EQUAL(*argv, "rio")) {
+ flags |= CBQCLF_RIO;
+ } else if (EQUAL(*argv, "cleardscp")) {
+ flags |= CBQCLF_CLEARDSCP;
+ } else {
+ LOG(LOG_ERR, 0,
+ "Unknown keyword '%s' in %s, line %d\n",
+ *argv, altqconfigfile, line_no);
+ return (0);
+ }
+
+ argc--; argv++;
+ }
+
+ if ((flags & (CBQCLF_RED|CBQCLF_RIO)) == (CBQCLF_RED|CBQCLF_RIO)) {
+ LOG(LOG_ERR, 0,
+ "both red and rio defined on interface '%s'\n",
+ ifname);
+ return (0);
+ }
+ if ((flags & (CBQCLF_ECN|CBQCLF_FLOWVALVE))
+ && (flags & (CBQCLF_RED|CBQCLF_RIO)) == 0)
+ flags |= CBQCLF_RED;
+
+ if (strcmp("ctl_class", class_name) == 0)
+ flags |= CBQCLF_CTLCLASS;
+
+ if (bandwidth == 0 && pbandwidth != 0) {
+ struct ifinfo *ifinfo;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) != NULL)
+ bandwidth = ifinfo->bandwidth / 100 * pbandwidth;
+ }
+
+ error = qcmd_cbq_add_class(ifname, class_name, parent_name, borrow,
+ pri, bandwidth,
+ maxdelay, maxburst, minburst,
+ av_pkt_size, max_pkt_size,
+ admission_type, flags);
+ if (error)
+ return (0);
+ return (1);
+}
+
+/*
+ * qcmd api
+ */
+int
+qcmd_cbq_add_if(const char *ifname, u_int bandwidth, int is_wrr, int efficient)
+{
+ int error;
+
+ error = qop_cbq_add_if(NULL, ifname, bandwidth, is_wrr, efficient);
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: can't add cbq on interface '%s'\n",
+ qoperror(error), ifname);
+ return (error);
+}
+
+int
+qcmd_cbq_add_class(const char *ifname, const char *class_name,
+ const char *parent_name, const char *borrow_name,
+ u_int pri, u_int bandwidth,
+ u_int maxdelay, u_int maxburst, u_int minburst,
+ u_int av_pkt_size, u_int max_pkt_size,
+ int admission_type, int flags)
+{
+ struct ifinfo *ifinfo;
+ struct cbq_ifinfo *cbq_ifinfo;
+ struct classinfo *parent = NULL, *borrow = NULL;
+ u_int ctl_bandwidth = 0;
+ int error = 0;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ error = QOPERR_BADIF;
+ cbq_ifinfo = ifinfo->private;
+
+ if (error == 0 && parent_name != NULL &&
+ (parent = clname2clinfo(ifinfo, parent_name)) == NULL)
+ error = QOPERR_BADCLASS;
+
+ if (error == 0 && borrow_name != NULL &&
+ (borrow = clname2clinfo(ifinfo, borrow_name)) == NULL)
+ error = QOPERR_BADCLASS;
+
+ if (flags & CBQCLF_DEFCLASS) {
+ /*
+ * if this is a default class and no ctl_class is defined,
+ * we will create a ctl_class.
+ */
+ if (cbq_ifinfo->ctl_class == NULL) {
+ /* reserve bandwidth for ctl_class */
+ ctl_bandwidth =
+ ifinfo->bandwidth / 100 * CTL_PBANDWIDTH;
+ bandwidth -= ctl_bandwidth;
+ }
+ }
+
+ if (error == 0)
+ error = qop_cbq_add_class(NULL, class_name, ifinfo, parent,
+ borrow, pri, bandwidth,
+ maxdelay, maxburst, minburst,
+ av_pkt_size, max_pkt_size,
+ admission_type, flags);
+ if (error != 0)
+ LOG(LOG_ERR, errno,
+ "cbq: %s: can't add class '%s' on interface '%s'\n",
+ qoperror(error), class_name, ifname);
+
+ if (ctl_bandwidth != 0) {
+ /*
+ * If were adding the default traffic class and
+ * no ctl_class is defined, also add the ctl traffic class.
+ * This is for RSVP and IGMP packets.
+ */
+ if (qcmd_cbq_add_class(ifname, "ctl_class", parent_name,
+ borrow_name, 6, ctl_bandwidth,
+ maxdelay, maxburst, minburst, av_pkt_size,
+ max_pkt_size, admission_type, CBQCLF_CTLCLASS) != 0) {
+ LOG(LOG_ERR, errno, "can't create ctl_class!");
+ return (QOPERR_CLASS);
+ }
+ }
+
+ /*
+ * if this is a ctl class, add the default filters for backward
+ * compatibility
+ */
+ if (flags & CBQCLF_CTLCLASS)
+ qcmd_cbq_add_ctl_filters(ifname, class_name);
+
+ return (error);
+}
+
+int
+qcmd_cbq_modify_class(const char *ifname, const char *class_name,
+ u_int pri, u_int bandwidth,
+ u_int maxdelay, u_int maxburst, u_int minburst,
+ u_int av_pkt_size, u_int max_pkt_size, int flags)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ return (QOPERR_BADIF);
+
+ if ((clinfo = clname2clinfo(ifinfo, class_name)) == NULL)
+ return (QOPERR_BADCLASS);
+
+ return qop_cbq_modify_class(clinfo, pri, bandwidth,
+ maxdelay, maxburst, minburst,
+ av_pkt_size, max_pkt_size, flags);
+}
+
+/*
+ * add the default filters for ctl_class (for backward compatibility).
+ */
+#ifndef IPPROTO_RSVP
+#define IPPROTO_RSVP 46
+#endif
+
+static int
+qcmd_cbq_add_ctl_filters(const char *ifname, const char *clname)
+{
+ struct flow_filter sfilt;
+ u_int8_t ctl_protos[3] = {IPPROTO_ICMP, IPPROTO_IGMP, IPPROTO_RSVP};
+#ifdef INET6
+ struct flow_filter6 sfilt6;
+ u_int8_t ctl6_protos[3] = {IPPROTO_ICMPV6, IPPROTO_IGMP, IPPROTO_RSVP};
+#endif
+ int error, i;
+
+ for (i = 0; i < (int)sizeof(ctl_protos); i++) {
+ memset(&sfilt, 0, sizeof(sfilt));
+ sfilt.ff_flow.fi_family = AF_INET;
+ sfilt.ff_flow.fi_proto = ctl_protos[i];
+
+ filter_dontwarn = 1; /* XXX */
+ error = qcmd_add_filter(ifname, clname, NULL, &sfilt);
+ filter_dontwarn = 0; /* XXX */
+ if (error) {
+ LOG(LOG_ERR, 0,
+ "can't add ctl class filter on interface '%s'\n",
+ ifname);
+ return (error);
+ }
+ }
+
+#ifdef INET6
+ for (i = 0; i < sizeof(ctl6_protos); i++) {
+ memset(&sfilt6, 0, sizeof(sfilt6));
+ sfilt6.ff_flow6.fi6_family = AF_INET6;
+ sfilt6.ff_flow6.fi6_proto = ctl6_protos[i];
+
+ error = qcmd_add_filter(ifname, clname, NULL,
+ (struct flow_filter *)&sfilt6);
+ if (error) {
+ LOG(LOG_WARNING, 0,
+ "can't add ctl class IPv6 filter on interface '%s'\n",
+ ifname);
+ return (error);
+ }
+ }
+#endif
+ return (0);
+}
+
+/*
+ * qop api
+ */
+int
+qop_cbq_add_if(struct ifinfo **rp, const char *ifname,
+ u_int bandwidth, int is_wrr, int efficient)
+{
+ struct ifinfo *ifinfo = NULL;
+ struct cbq_ifinfo *cbq_ifinfo = NULL;
+ int error;
+
+ if ((cbq_ifinfo = calloc(1, sizeof(*cbq_ifinfo))) == NULL)
+ return (QOPERR_NOMEM);
+
+ cbq_ifinfo->nsPerByte =
+ (1.0 / (double)bandwidth) * NS_PER_SEC * 8;
+ cbq_ifinfo->is_wrr = is_wrr;
+ cbq_ifinfo->is_efficient = efficient;
+
+ error = qop_add_if(&ifinfo, ifname, bandwidth,
+ &cbq_qdisc, cbq_ifinfo);
+ if (error != 0)
+ goto err_ret;
+
+ /* set enable hook */
+ ifinfo->enable_hook = qop_cbq_enable_hook;
+
+ if (rp != NULL)
+ *rp = ifinfo;
+ return (0);
+
+ err_ret:
+ if (cbq_ifinfo != NULL) {
+ free(cbq_ifinfo);
+ if (ifinfo != NULL)
+ ifinfo->private = NULL;
+ }
+ return (error);
+}
+
+#define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0))
+
+int
+qop_cbq_add_class(struct classinfo **rp, const char *class_name,
+ struct ifinfo *ifinfo, struct classinfo *parent,
+ struct classinfo *borrow, u_int pri, u_int bandwidth,
+ u_int maxdelay, u_int maxburst, u_int minburst,
+ u_int av_pkt_size, u_int max_pkt_size,
+ int admission_type, int flags)
+{
+ struct classinfo *clinfo;
+ struct cbq_ifinfo *cbq_ifinfo;
+ struct cbq_classinfo *cbq_clinfo, *parent_clinfo;
+ u_int parent_handle, borrow_handle;
+ int error;
+
+ cbq_ifinfo = ifinfo->private;
+
+ if (parent == NULL) {
+ if (cbq_ifinfo->root_class != NULL)
+ return (QOPERR_CLASS_INVAL);
+ flags |= CBQCLF_ROOTCLASS;
+ }
+ if ((flags & CBQCLF_DEFCLASS) && cbq_ifinfo->default_class != NULL)
+ return (QOPERR_CLASS_INVAL);
+ if ((flags & CBQCLF_CTLCLASS) && cbq_ifinfo->ctl_class != NULL)
+ return (QOPERR_CLASS_INVAL);
+
+ /* admission control */
+ if (parent != NULL) {
+ parent_clinfo = parent->private;
+ if (bandwidth >
+ parent_clinfo->bandwidth - parent_clinfo->allocated) {
+#ifdef ALLOW_OVERCOMMIT
+ LOG(LOG_WARNING, 0,
+ "bandwidth overcommitted %uK requested but only %dK available (%uK already allocated)\n",
+ bandwidth / 1000,
+ ((int)parent_clinfo->bandwidth -
+ parent_clinfo->allocated) / 1000,
+ parent_clinfo->allocated / 1000);
+#else /* !ALLOW_OVERCOMMIT */
+ LOG(LOG_ERR, 0,
+ "cbq admission failed! %uK requested but only %uK available (%uK already allocated)\n",
+ bandwidth / 1000,
+ (parent_clinfo->bandwidth -
+ parent_clinfo->allocated) / 1000,
+ parent_clinfo->allocated / 1000);
+ return (QOPERR_ADMISSION_NOBW);
+#endif /* !ALLOW_OVERCOMMIT */
+ }
+ }
+
+ if ((cbq_clinfo = calloc(1, sizeof(*cbq_clinfo))) == NULL)
+ return (QOPERR_NOMEM);
+
+ cbq_clinfo->bandwidth = bandwidth;
+ cbq_clinfo->allocated = 0;
+
+ /* if average paket size isn't specified, set if mtu. */
+ if (av_pkt_size == 0) { /* use default */
+ av_pkt_size = ifinfo->ifmtu;
+ if (av_pkt_size > MCLBYTES) /* do what TCP does */
+ av_pkt_size &= ~MCLBYTES;
+ } else if (av_pkt_size > ifinfo->ifmtu)
+ av_pkt_size = ifinfo->ifmtu;
+
+ if (max_pkt_size == 0) /* use default */
+ max_pkt_size = ifinfo->ifmtu;
+ else if (max_pkt_size > ifinfo->ifmtu)
+ max_pkt_size = ifinfo->ifmtu;
+
+ cbq_clinfo->maxdelay = maxdelay;
+ cbq_clinfo->maxburst = maxburst;
+ cbq_clinfo->minburst = minburst;
+ cbq_clinfo->av_pkt_size = av_pkt_size;
+ cbq_clinfo->max_pkt_size = max_pkt_size;
+
+ parent_handle = parent != NULL ? parent->handle : NULL_CLASS_HANDLE;
+ borrow_handle = borrow != NULL ? borrow->handle : NULL_CLASS_HANDLE;
+
+ if (cbq_class_spec(ifinfo, parent_handle, borrow_handle, pri, flags,
+ bandwidth, maxdelay, maxburst, minburst,
+ av_pkt_size, max_pkt_size,
+ &cbq_clinfo->class_spec) < 0) {
+ error = QOPERR_INVAL;
+ goto err_ret;
+ }
+
+ clinfo = NULL;
+ error = qop_add_class(&clinfo, class_name, ifinfo, parent, cbq_clinfo);
+ if (error != 0)
+ goto err_ret;
+
+ /* set delete hook */
+ clinfo->delete_hook = qop_cbq_delete_class_hook;
+
+ if (parent == NULL)
+ cbq_ifinfo->root_class = clinfo;
+ else {
+ parent_clinfo = parent->private;
+ parent_clinfo->allocated += bandwidth;
+ }
+ if (flags & CBQCLF_DEFCLASS)
+ cbq_ifinfo->default_class = clinfo;
+ if (flags & CBQCLF_CTLCLASS)
+ cbq_ifinfo->ctl_class = clinfo;
+
+ switch (admission_type) {
+ case CBQ_QOS_CNTR_LOAD:
+ case CBQ_QOS_GUARANTEED:
+ case CBQ_QOS_PREDICTIVE:
+ case CBQ_QOS_CNTR_DELAY:
+ if (ifinfo->resv_class != NULL) {
+ LOG(LOG_ERR, 0,
+ "%s: duplicate resv meta class\n", class_name);
+ return (QOPERR_CLASS);
+ }
+ ifinfo->resv_class = clinfo;
+ }
+
+ if (rp != NULL)
+ *rp = clinfo;
+ return (0);
+
+ err_ret:
+ if (cbq_clinfo != NULL) {
+ free(cbq_clinfo);
+ if (clinfo != NULL)
+ clinfo->private = NULL;
+ }
+ return (error);
+}
+
+/*
+ * this is called from qop_delete_class() before a class is destroyed
+ * for discipline specific cleanup.
+ */
+static int
+qop_cbq_delete_class_hook(struct classinfo *clinfo)
+{
+ struct cbq_classinfo *cbq_clinfo, *parent_clinfo;
+
+ /* cancel admission control */
+ if (clinfo->parent != NULL) {
+ cbq_clinfo = clinfo->private;
+ parent_clinfo = clinfo->parent->private;
+
+ parent_clinfo->allocated -= cbq_clinfo->bandwidth;
+ }
+ return (0);
+}
+
+int
+qop_cbq_modify_class(struct classinfo *clinfo, u_int pri, u_int bandwidth,
+ u_int maxdelay, u_int maxburst, u_int minburst,
+ u_int av_pkt_size, u_int max_pkt_size, int flags)
+{
+ struct ifinfo *ifinfo;
+ struct cbq_classinfo *cbq_clinfo, *parent_clinfo;
+ u_int parent_handle, borrow_handle;
+ u_int old_bandwidth;
+ int error;
+
+ ifinfo = clinfo->ifinfo;
+ cbq_clinfo = clinfo->private;
+
+ /* admission control */
+ old_bandwidth = cbq_clinfo->bandwidth;
+ if (clinfo->parent != NULL) {
+ parent_clinfo = clinfo->parent->private;
+ if (bandwidth > old_bandwidth) {
+ /* increase bandwidth */
+ if (bandwidth - old_bandwidth >
+ parent_clinfo->bandwidth
+ - parent_clinfo->allocated)
+ return (QOPERR_ADMISSION_NOBW);
+ } else if (bandwidth < old_bandwidth) {
+ /* decrease bandwidth */
+ if (bandwidth < cbq_clinfo->allocated)
+ return (QOPERR_ADMISSION);
+ }
+ }
+
+ /* if average paket size isn't specified, set if mtu. */
+ if (av_pkt_size == 0) { /* use default */
+ av_pkt_size = ifinfo->ifmtu;
+ if (av_pkt_size > MCLBYTES) /* do what TCP does */
+ av_pkt_size &= ~MCLBYTES;
+ } else if (av_pkt_size > ifinfo->ifmtu)
+ av_pkt_size = ifinfo->ifmtu;
+
+ if (max_pkt_size == 0) /* use default */
+ max_pkt_size = ifinfo->ifmtu;
+ else if (max_pkt_size > ifinfo->ifmtu)
+ max_pkt_size = ifinfo->ifmtu;
+
+ cbq_clinfo->maxdelay = maxdelay;
+ cbq_clinfo->maxburst = maxburst;
+ cbq_clinfo->minburst = minburst;
+ cbq_clinfo->av_pkt_size = av_pkt_size;
+ cbq_clinfo->max_pkt_size = max_pkt_size;
+
+ parent_handle = cbq_clinfo->class_spec.parent_class_handle;
+ borrow_handle = cbq_clinfo->class_spec.borrow_class_handle;
+
+ if (cbq_class_spec(ifinfo, parent_handle, borrow_handle, pri, flags,
+ bandwidth, maxdelay, maxburst, minburst,
+ av_pkt_size, max_pkt_size,
+ &cbq_clinfo->class_spec) < 0) {
+ return (QOPERR_INVAL);
+ }
+
+ error = qop_modify_class(clinfo, NULL);
+
+ if (error == 0) {
+ if (clinfo->parent != NULL) {
+ parent_clinfo = clinfo->parent->private;
+ parent_clinfo->allocated -= old_bandwidth;
+ parent_clinfo->allocated += bandwidth;
+ }
+ cbq_clinfo->bandwidth = bandwidth;
+ }
+ return (error);
+}
+
+/*
+ * sanity check at enabling cbq:
+ * there must one root class and one default class for an interface
+ */
+static int
+qop_cbq_enable_hook(struct ifinfo *ifinfo)
+{
+ struct cbq_ifinfo *cbq_ifinfo;
+
+ cbq_ifinfo = ifinfo->private;
+ if (cbq_ifinfo->root_class == NULL) {
+ LOG(LOG_ERR, 0, "cbq: no root class on interface %s!\n",
+ ifinfo->ifname);
+ return (QOPERR_CLASS);
+ }
+ if (cbq_ifinfo->default_class == NULL) {
+ LOG(LOG_ERR, 0, "cbq: no default class on interface %s!\n",
+ ifinfo->ifname);
+ return (QOPERR_CLASS);
+ }
+ return (0);
+}
+
+static int
+cbq_class_spec(struct ifinfo *ifinfo, u_long parent_class,
+ u_long borrow_class, u_int pri, int flags,
+ u_int bandwidth, u_int maxdelay, u_int maxburst,
+ u_int minburst, u_int av_pkt_size, u_int max_pkt_size,
+ cbq_class_spec_t *cl_spec)
+{
+ struct cbq_ifinfo *cbq_ifinfo = ifinfo->private;
+ double maxq, maxidle_s, maxidle, minidle,
+ offtime, nsPerByte, ptime, cptime;
+ double z = (double)(1 << RM_FILTER_GAIN);
+ double g = (1.0 - 1.0 / z);
+ double f;
+ double gton;
+ double gtom;
+ double maxrate;
+
+ /* Compute other class parameters */
+ if (bandwidth == 0)
+ f = 0.0001; /* small enough? */
+ else
+ f = ((double) bandwidth / (double) ifinfo->bandwidth);
+
+ if (av_pkt_size == 0) { /* use default */
+ av_pkt_size = ifinfo->ifmtu;
+ if (av_pkt_size > MCLBYTES) /* do what TCP does */
+ av_pkt_size &= ~MCLBYTES;
+ } else if (av_pkt_size > ifinfo->ifmtu)
+ av_pkt_size = ifinfo->ifmtu;
+ if (max_pkt_size == 0) /* use default */
+ max_pkt_size = ifinfo->ifmtu;
+ else if (max_pkt_size > ifinfo->ifmtu)
+ max_pkt_size = ifinfo->ifmtu;
+
+ nsPerByte = cbq_ifinfo->nsPerByte / f;
+ ptime = (double) av_pkt_size * (double)cbq_ifinfo->nsPerByte;
+ maxrate = f * ((double)ifinfo->bandwidth / 8.0);
+ cptime = ptime * (1.0 - f) / f;
+#if 1 /* ALTQ */
+ if (nsPerByte * (double)max_pkt_size > (double)INT_MAX) {
+ /*
+ * this causes integer overflow in kernel!
+ * (bandwidth < 6Kbps when max_pkt_size=1500)
+ */
+ if (bandwidth != 0)
+ LOG(LOG_WARNING, 0, "warning: class is too slow!!\n");
+ nsPerByte = (double)(INT_MAX / max_pkt_size);
+ }
+#endif
+ if (maxburst == 0) { /* use default */
+ if (cptime > 10.0 * NS_PER_MS)
+ maxburst = 4;
+ else
+ maxburst = 16;
+ }
+ if (minburst == 0) /* use default */
+ minburst = 2;
+ if (minburst > maxburst)
+ minburst = maxburst;
+
+ if (IsDebug(DEBUG_ALTQ)) {
+ int packet_time;
+ LOG(LOG_DEBUG, 0,
+ "cbq_flowspec: maxburst=%d,minburst=%d,pkt_size=%d\n",
+ maxburst, minburst, av_pkt_size);
+ LOG(LOG_DEBUG, 0,
+ " nsPerByte=%.2f ns, link's nsPerByte=%.2f, f=%.3f\n",
+ nsPerByte, cbq_ifinfo->nsPerByte, f);
+ packet_time = av_pkt_size * (int)nsPerByte / 1000;
+ LOG(LOG_DEBUG, 0,
+ " packet time=%d [us]\n", packet_time);
+ if (maxburst * packet_time < 20000) {
+ LOG(LOG_WARNING, 0,
+ "warning: maxburst smaller than timer granularity!\n");
+ LOG(LOG_WARNING, 0,
+ " maxburst=%d, packet_time=%d [us]\n",
+ maxburst, packet_time);
+ }
+ }
+ gton = pow(g, (double)maxburst);
+ gtom = pow(g, (double)(minburst-1));
+ maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton));
+ maxidle_s = (1.0 - g);
+ if (maxidle > maxidle_s)
+ maxidle = ptime * maxidle;
+ else
+ maxidle = ptime * maxidle_s;
+ if (IsDebug(DEBUG_ALTQ))
+ LOG(LOG_DEBUG, 0, " maxidle=%.2f us\n", maxidle/1000.0);
+ if (minburst)
+ offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom);
+ else
+ offtime = cptime;
+ minidle = -((double)max_pkt_size * (double)nsPerByte);
+ if (IsDebug(DEBUG_ALTQ))
+ LOG(LOG_DEBUG, 0, " offtime=%.2f us minidle=%.2f us\n",
+ offtime/1000.0, minidle/1000.0);
+
+ maxidle = ((maxidle * 8.0) / nsPerByte) * pow(2, RM_FILTER_GAIN);
+#if 1 /* ALTQ */
+ /* also scale offtime and minidle */
+ offtime = (offtime * 8.0) / nsPerByte * pow(2, RM_FILTER_GAIN);
+ minidle = ((minidle * 8.0) / nsPerByte) * pow(2, RM_FILTER_GAIN);
+#endif
+ maxidle = maxidle / 1000.0;
+ offtime = offtime / 1000.0;
+ minidle = minidle / 1000.0;
+ /* adjust queue size when maxdelay is specified.
+ queue size should be relative to its share */
+ if (maxdelay == 0) {
+ if (flags & (CBQCLF_RED|CBQCLF_RIO))
+ maxq = 60.0;
+ else
+ maxq = 30.0;
+ } else {
+ maxq = ((double) maxdelay * NS_PER_MS) / (nsPerByte * av_pkt_size);
+ if (maxq < 4) {
+ LOG(LOG_WARNING, 0,
+ "warning: maxq (%d) is too small. set to %d\n",
+ (int)maxq, 4);
+ maxq = 4;
+ }
+ }
+ if (bandwidth == 0 && borrow_class == NULL_CLASS_HANDLE)
+ /* filter out this class by setting queue size to zero */
+ maxq = 0;
+ if (IsDebug(DEBUG_ALTQ)) {
+ if ((u_int)maxq < maxburst)
+ LOG(LOG_WARNING, 0,
+ "warning: maxq (%d) is smaller than maxburst(%d)\n",
+ (int)maxq, maxburst);
+ else if (maxq > 100.0)
+ LOG(LOG_WARNING, 0,
+ "warning: maxq %d too large\n", (int)maxq);
+ LOG(LOG_DEBUG, 0, " maxq=%d\n", (int)maxq);
+ }
+
+ if (parent_class == NULL_CLASS_HANDLE) {
+ if ((flags & CBQCLF_ROOTCLASS) == 0)
+ flags |= CBQCLF_ROOTCLASS;
+ if (cbq_ifinfo->is_wrr)
+ flags |= CBQCLF_WRR;
+ if (cbq_ifinfo->is_efficient)
+ flags |= CBQCLF_EFFICIENT;
+ }
+
+ memset((void *)cl_spec, 0, sizeof(cbq_class_spec_t));
+ cl_spec->priority = pri;
+ cl_spec->nano_sec_per_byte = (u_int) nsPerByte;
+ cl_spec->maxq = (u_int) maxq;
+ cl_spec->maxidle = (u_int) fabs(maxidle);
+ cl_spec->minidle = (int)minidle;
+ cl_spec->offtime = (u_int) fabs(offtime);
+
+ cl_spec->parent_class_handle = parent_class;
+ cl_spec->borrow_class_handle = borrow_class;
+
+ cl_spec->pktsize = av_pkt_size;
+ cl_spec->flags = flags;
+
+ return (0);
+}
+
+
+/*
+ * system call interfaces for qdisc_ops
+ */
+static int
+cbq_attach(struct ifinfo *ifinfo)
+{
+ struct cbq_interface iface;
+
+ if (cbq_fd < 0 &&
+ (cbq_fd = open(CBQ_DEVICE, O_RDWR)) < 0 &&
+ (cbq_fd = open_module(CBQ_DEVICE, O_RDWR)) < 0) {
+ LOG(LOG_ERR, errno, "CBQ open\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ cbq_refcount++;
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(cbq_fd, CBQ_IF_ATTACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+cbq_detach(struct ifinfo *ifinfo)
+{
+ struct cbq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(cbq_fd, CBQ_IF_DETACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ if (--cbq_refcount == 0) {
+ close(cbq_fd);
+ cbq_fd = -1;
+ }
+ return (0);
+}
+
+static int
+cbq_clear(struct ifinfo *ifinfo)
+{
+ struct cbq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(cbq_fd, CBQ_CLEAR_HIERARCHY, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+cbq_enable(struct ifinfo *ifinfo)
+{
+ struct cbq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(cbq_fd, CBQ_ENABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+cbq_disable(struct ifinfo *ifinfo)
+{
+ struct cbq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(cbq_fd, CBQ_DISABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+cbq_add_class(struct classinfo *clinfo)
+{
+ struct cbq_add_class class_add;
+ struct cbq_classinfo *cbq_clinfo;
+ struct cbq_ifinfo *cbq_ifinfo;
+
+ cbq_ifinfo = clinfo->ifinfo->private;
+ cbq_clinfo = clinfo->private;
+
+ memset(&class_add, 0, sizeof(class_add));
+ strncpy(class_add.cbq_iface.cbq_ifacename,
+ clinfo->ifinfo->ifname, IFNAMSIZ);
+
+ class_add.cbq_class = cbq_clinfo->class_spec;
+
+ if (ioctl(cbq_fd, CBQ_ADD_CLASS, &class_add) < 0)
+ return (QOPERR_SYSCALL);
+
+ clinfo->handle = class_add.cbq_class_handle;
+ return (0);
+}
+
+static int
+cbq_modify_class(struct classinfo *clinfo, void *arg)
+{
+ struct cbq_modify_class class_mod;
+ struct cbq_classinfo *cbq_clinfo;
+
+ cbq_clinfo = clinfo->private;
+
+ memset(&class_mod, 0, sizeof(class_mod));
+ strncpy(class_mod.cbq_iface.cbq_ifacename,
+ clinfo->ifinfo->ifname, IFNAMSIZ);
+ class_mod.cbq_class_handle = clinfo->handle;
+ class_mod.cbq_class = cbq_clinfo->class_spec;
+
+ if (ioctl(cbq_fd, CBQ_MODIFY_CLASS, &class_mod) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+cbq_delete_class(struct classinfo *clinfo)
+{
+ struct cbq_delete_class class_delete;
+
+ memset(&class_delete, 0, sizeof(class_delete));
+ strncpy(class_delete.cbq_iface.cbq_ifacename,
+ clinfo->ifinfo->ifname, IFNAMSIZ);
+ class_delete.cbq_class_handle = clinfo->handle;
+
+ if (ioctl(cbq_fd, CBQ_DEL_CLASS, &class_delete) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+cbq_add_filter(struct fltrinfo *fltrinfo)
+{
+ struct cbq_add_filter fltr_add;
+
+ memset(&fltr_add, 0, sizeof(fltr_add));
+ strncpy(fltr_add.cbq_iface.cbq_ifacename,
+ fltrinfo->clinfo->ifinfo->ifname, IFNAMSIZ);
+ fltr_add.cbq_class_handle = fltrinfo->clinfo->handle;
+ fltr_add.cbq_filter = fltrinfo->fltr;
+
+ if (ioctl(cbq_fd, CBQ_ADD_FILTER, &fltr_add) < 0)
+ return (QOPERR_SYSCALL);
+ fltrinfo->handle = fltr_add.cbq_filter_handle;
+ return (0);
+}
+
+static int
+cbq_delete_filter(struct fltrinfo *fltrinfo)
+{
+ struct cbq_delete_filter fltr_del;
+
+ memset(&fltr_del, 0, sizeof(fltr_del));
+ strncpy(fltr_del.cbq_iface.cbq_ifacename,
+ fltrinfo->clinfo->ifinfo->ifname, IFNAMSIZ);
+ fltr_del.cbq_filter_handle = fltrinfo->handle;
+
+ if (ioctl(cbq_fd, CBQ_DEL_FILTER, &fltr_del) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+
diff --git a/usr.sbin/altq/libaltq/qop_cbq.h b/usr.sbin/altq/libaltq/qop_cbq.h
new file mode 100644
index 00000000000..374859d966d
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_cbq.h
@@ -0,0 +1,102 @@
+/* $OpenBSD: qop_cbq.h,v 1.1 2001/06/27 18:23:29 kjc Exp $ */
+/* $KAME: qop_cbq.h,v 1.2 2000/10/18 09:15:18 kjc Exp $ */
+/*
+ * Copyright (c) Sun Microsystems, Inc. 1993-1998 All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the SMCC Technology
+ * Development Group at Sun Microsystems, Inc.
+ *
+ * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
+ * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is
+ * provided "as is" without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this software.
+ */
+
+#include <altq/altq_rmclass.h>
+#include <altq/altq_cbq.h>
+
+/* cbq admission types */
+typedef enum {
+ CBQ_QOS_NONE,
+ CBQ_QOS_GUARANTEED,
+ CBQ_QOS_PREDICTIVE,
+ CBQ_QOS_CNTR_DELAY,
+ CBQ_QOS_CNTR_LOAD
+} cbq_tos_t;
+
+/*
+ * cbq private ifinfo structure
+ */
+struct cbq_ifinfo {
+ struct classinfo *root_class; /* root class */
+ struct classinfo *default_class; /* default class */
+ struct classinfo *ctl_class; /* control class */
+
+ double nsPerByte; /* bandwidth in ns per sec */
+ int is_wrr; /* use weighted-round robin */
+ int is_efficient; /* use work-conserving */
+};
+
+/*
+ * cbq private classinfo structure
+ */
+struct cbq_classinfo {
+ u_int bandwidth; /* bandwidth in bps */
+ u_int allocated; /* bandwidth used by children */
+
+ u_int maxdelay;
+ u_int maxburst;
+ u_int minburst;
+ u_int av_pkt_size;
+ u_int max_pkt_size;
+
+ cbq_class_spec_t class_spec; /* class parameters */
+};
+
+int cbq_interface_parser(const char *ifname, int argc, char **argv);
+int cbq_class_parser(const char *ifname, const char *class_name,
+ const char *parent_name, int argc, char **argv);
+
+int qcmd_cbq_add_if(const char *ifname, u_int bandwidth,
+ int is_wrr, int efficient);
+int qcmd_cbq_add_class(const char *ifname, const char *class_name,
+ const char *parent_name, const char *borrow_name,
+ u_int pri, u_int bandwidth,
+ u_int maxdelay, u_int maxburst, u_int minburst,
+ u_int av_pkt_size, u_int max_pkt_size,
+ int admission_type, int flags);
+int qcmd_cbq_modify_class(const char *ifname, const char *class_name,
+ u_int pri, u_int bandwidth,
+ u_int maxdelay, u_int maxburst, u_int minburst,
+ u_int av_pkt_size, u_int max_pkt_size, int flags);
+
+int qop_cbq_add_if(struct ifinfo **rp, const char *ifname,
+ u_int bandwidth, int is_wrr, int efficient);
+int qop_cbq_add_class(struct classinfo **rp, const char *class_name,
+ struct ifinfo *ifinfo, struct classinfo *parent,
+ struct classinfo *borrow, u_int pri, u_int bandwidth,
+ u_int maxdelay, u_int maxburst, u_int minburst,
+ u_int av_pkt_size, u_int max_pkt_size,
+ int admission_type, int flags);
+int qop_cbq_modify_class(struct classinfo *clinfo, u_int pri, u_int bandwidth,
+ u_int maxdelay, u_int maxburst, u_int minburst,
+ u_int av_pkt_size, u_int max_pkt_size, int flags);
+
diff --git a/usr.sbin/altq/libaltq/qop_cdnr.c b/usr.sbin/altq/libaltq/qop_cdnr.c
new file mode 100644
index 00000000000..8c490c01581
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_cdnr.c
@@ -0,0 +1,933 @@
+/* $OpenBSD: qop_cdnr.c,v 1.1 2001/06/27 18:23:30 kjc Exp $ */
+/* $KAME: qop_cdnr.c,v 1.6 2000/10/18 09:15:19 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <altq/altq.h>
+#include <altq/altq_cdnr.h>
+#include "altq_qop.h"
+#include "qop_cdnr.h"
+/*
+ * diffserve traffic conditioner support
+ *
+ * we use the existing qop interface to support conditioner.
+ */
+
+static struct ifinfo *cdnr_ifname2ifinfo(const char *ifname);
+static int cdnr_attach(struct ifinfo *ifinfo);
+static int cdnr_detach(struct ifinfo *ifinfo);
+static int cdnr_enable(struct ifinfo *ifinfo);
+static int cdnr_disable(struct ifinfo *ifinfo);
+static int cdnr_add_class(struct classinfo *clinfo);
+static int cdnr_modify_class(struct classinfo *clinfo, void *arg);
+static int cdnr_delete_class(struct classinfo *clinfo);
+static int cdnr_add_filter(struct fltrinfo *fltrinfo);
+static int cdnr_delete_filter(struct fltrinfo *fltrinfo);
+static int verify_tbprofile(struct tb_profile *profile, const char *cdnr_name);
+
+#define CDNR_DEVICE "/dev/altq/cdnr"
+
+static int cdnr_fd = -1;
+static int cdnr_refcount = 0;
+
+static struct qdisc_ops cdnr_qdisc = {
+ ALTQT_CDNR,
+ "cdnr",
+ cdnr_attach,
+ cdnr_detach,
+ NULL, /* clear */
+ cdnr_enable,
+ cdnr_disable,
+ cdnr_add_class,
+ cdnr_modify_class,
+ cdnr_delete_class,
+ cdnr_add_filter,
+ cdnr_delete_filter,
+};
+
+u_long
+cdnr_name2handle(const char *ifname, const char *cdnr_name)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+
+ if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
+ return (CDNR_NULL_HANDLE);
+
+ if ((clinfo = clname2clinfo(ifinfo, cdnr_name)) == NULL)
+ return (CDNR_NULL_HANDLE);
+
+ return (clinfo->handle);
+}
+
+static struct ifinfo *
+cdnr_ifname2ifinfo(const char *ifname)
+{
+ struct ifinfo *ifinfo;
+ char input_ifname[64];
+
+ /*
+ * search for an existing input interface
+ */
+ if ((ifinfo = input_ifname2ifinfo(ifname)) != NULL)
+ return (ifinfo);
+
+ /*
+ * if there is a corresponding output interface,
+ * create an input interface by prepending "_" to
+ * its name.
+ */
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ return (NULL);
+
+ input_ifname[0] = '_';
+ strcpy(input_ifname+1, ifname);
+ if (qop_add_if(&ifinfo, input_ifname, 0, &cdnr_qdisc, NULL) != 0) {
+ LOG(LOG_ERR, errno,
+ "cdnr_ifname2ifinfo: can't add a input interface %s\n",
+ ifname);
+ return (NULL);
+ }
+ return (ifinfo);
+}
+
+int
+qcmd_cdnr_add_element(struct tc_action *rp, const char *ifname,
+ const char *cdnr_name, struct tc_action *action)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+ int error;
+
+ if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
+ return (QOPERR_BADIF);
+
+ if ((error = qop_cdnr_add_element(&clinfo, cdnr_name, ifinfo,
+ action)) != 0) {
+ LOG(LOG_ERR, errno, "%s: add element failed!\n",
+ qoperror(error));
+ return (error);
+ }
+
+ if (rp != NULL) {
+ rp->tca_code = TCACODE_HANDLE;
+ rp->tca_handle = clinfo->handle;
+ }
+ return (0);
+}
+
+int
+qcmd_cdnr_add_tbmeter(struct tc_action *rp, const char *ifname,
+ const char *cdnr_name,
+ struct tb_profile *profile,
+ struct tc_action *in_action,
+ struct tc_action *out_action)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+ int error;
+
+ if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
+ return (QOPERR_BADIF);
+
+ verify_tbprofile(profile, cdnr_name);
+
+ if ((error = qop_cdnr_add_tbmeter(&clinfo, cdnr_name, ifinfo,
+ profile, in_action, out_action)) != 0) {
+ LOG(LOG_ERR, errno, "%s: add tbmeter failed!\n",
+ qoperror(error));
+ return (error);
+ }
+
+ if (rp != NULL) {
+ rp->tca_code = TCACODE_HANDLE;
+ rp->tca_handle = clinfo->handle;
+ }
+ return (0);
+}
+
+int
+qcmd_cdnr_add_trtcm(struct tc_action *rp, const char *ifname,
+ const char *cdnr_name,
+ struct tb_profile *cmtd_profile,
+ struct tb_profile *peak_profile,
+ struct tc_action *green_action,
+ struct tc_action *yellow_action,
+ struct tc_action *red_action, int coloraware)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+ int error;
+
+ if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
+ return (QOPERR_BADIF);
+
+ verify_tbprofile(cmtd_profile, cdnr_name);
+ verify_tbprofile(peak_profile, cdnr_name);
+
+ if ((error = qop_cdnr_add_trtcm(&clinfo, cdnr_name, ifinfo,
+ cmtd_profile, peak_profile,
+ green_action, yellow_action, red_action,
+ coloraware)) != 0) {
+ LOG(LOG_ERR, errno, "%s: add trtcm failed!\n",
+ qoperror(error));
+ return (error);
+ }
+
+ if (rp != NULL) {
+ rp->tca_code = TCACODE_HANDLE;
+ rp->tca_handle = clinfo->handle;
+ }
+ return (0);
+}
+
+int
+qcmd_cdnr_add_tswtcm(struct tc_action *rp, const char *ifname,
+ const char *cdnr_name, const u_int32_t cmtd_rate,
+ const u_int32_t peak_rate, const u_int32_t avg_interval,
+ struct tc_action *green_action,
+ struct tc_action *yellow_action,
+ struct tc_action *red_action)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+ int error;
+
+ if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
+ return (QOPERR_BADIF);
+
+ if (cmtd_rate > peak_rate) {
+ LOG(LOG_ERR, 0,
+ "add tswtcm: cmtd_rate larger than peak_rate!\n");
+ return (QOPERR_INVAL);
+ }
+
+ if ((error = qop_cdnr_add_tswtcm(&clinfo, cdnr_name, ifinfo,
+ cmtd_rate, peak_rate, avg_interval,
+ green_action, yellow_action,
+ red_action)) != 0) {
+ LOG(LOG_ERR, errno, "%s: add tswtcm failed!\n",
+ qoperror(error));
+ return (error);
+ }
+
+ if (rp != NULL) {
+ rp->tca_code = TCACODE_HANDLE;
+ rp->tca_handle = clinfo->handle;
+ }
+ return (0);
+}
+
+int
+qcmd_cdnr_delete(const char *ifname, const char *cdnr_name)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+
+ if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
+ return (QOPERR_BADIF);
+
+ if ((clinfo = clname2clinfo(ifinfo, cdnr_name)) == NULL)
+ return (QOPERR_BADCLASS);
+
+ return qop_delete_cdnr(clinfo);
+}
+
+/*
+ * class operations:
+ * class structure is used to hold conditioners.
+ * XXX
+ * conditioners has dependencies in the reverse order; parent nodes
+ * refere to child nodes, and thus, a child is created first and
+ * parents should be removed first.
+ * qop_add_cdnr() and qop_delete_cdnr() are wrapper functions
+ * of qop_add_class() and qop_delete_class(), and takes care
+ * of dependencies.
+ * 1. when adding a conditioner, it is created as a child of a
+ * dummy root class. then, the child conditioners are made
+ * as its children.
+ * 2. when deleting a conditioner, its child conditioners are made
+ * as children of the dummy root class. then, the conditioner
+ * is deleted.
+ */
+
+int
+qop_add_cdnr(struct classinfo **rp, const char *cdnr_name,
+ struct ifinfo *ifinfo, struct classinfo **childlist,
+ void *cdnr_private)
+{
+ struct classinfo *clinfo, *root, *cl, *prev;
+ int error;
+
+ /*
+ * if there is no root cdnr, create one.
+ */
+ if ((root = get_rootclass(ifinfo)) == NULL) {
+ if ((error = qop_add_class(&root, "cdnr_root",
+ ifinfo, NULL, NULL)) != 0) {
+ LOG(LOG_ERR, errno,
+ "cdnr: %s: can't create dummy root cdnr on %s!\n",
+ qoperror(error), ifinfo->ifname);
+ return (QOPERR_CLASS);
+ }
+ }
+
+ /*
+ * create a class as a child of a root class.
+ */
+ if ((error = qop_add_class(&clinfo, cdnr_name,
+ ifinfo, root, cdnr_private)) != 0)
+ return (error);
+ /*
+ * move child nodes
+ */
+ for (cl = *childlist; cl != NULL; cl = *++childlist) {
+ if (cl->parent != root) {
+ /*
+ * this conditioner already has a non-root parent.
+ * we can't track down a multi-parent node by a
+ * tree structure; leave it as it is.
+ * (we need a mechanism similar to a symbolic link
+ * in a file system)
+ */
+ continue;
+ }
+ /* remove this child from the root */
+ if (root->child == cl)
+ root->child = cl->sibling;
+ else for (prev = root->child;
+ prev->sibling != NULL; prev = prev->sibling)
+ if (prev->sibling == cl) {
+ prev->sibling = cl->sibling;
+ break;
+ }
+
+ /* add as a child */
+ cl->sibling = clinfo->child;
+ clinfo->child = cl;
+ cl->parent = clinfo;
+ }
+
+ if (rp != NULL)
+ *rp = clinfo;
+ return (0);
+}
+
+int
+qop_delete_cdnr(struct classinfo *clinfo)
+{
+ struct classinfo *cl, *root;
+ int error;
+
+ if ((root = get_rootclass(clinfo->ifinfo)) == NULL) {
+ LOG(LOG_ERR, 0, "qop_delete_cdnr: no root cdnr!\n");
+ return (QOPERR_CLASS);
+ }
+
+ if (clinfo->parent != root)
+ return (QOPERR_CLASS_PERM);
+
+ if ((cl = clinfo->child) != NULL) {
+ /* change child's parent to root, find the last child */
+ while (cl->sibling != NULL) {
+ cl->parent = root;
+ cl = cl->sibling;
+ }
+ cl->parent = root;
+
+ /* move children to siblings */
+ cl->sibling = clinfo->sibling;
+ clinfo->sibling = cl;
+ clinfo->child = NULL;
+ }
+
+ error = qop_delete_class(clinfo);
+
+ if (error) {
+ /* ick! restore the class tree */
+ if (cl != NULL) {
+ clinfo->child = clinfo->sibling;
+ clinfo->sibling = cl->sibling;
+ cl->sibling = NULL;
+ /* restore parent field */
+ for (cl = clinfo->child; cl != NULL; cl = cl->sibling)
+ cl->parent = clinfo;
+ }
+ }
+ return (error);
+}
+
+int
+qop_cdnr_add_element(struct classinfo **rp, const char *cdnr_name,
+ struct ifinfo *ifinfo, struct tc_action *action)
+{
+ struct classinfo *clinfo, *clist[2];
+ struct cdnrinfo *cdnrinfo = NULL;
+ int error;
+
+ if (action->tca_code == TCACODE_HANDLE) {
+ clinfo = clhandle2clinfo(ifinfo, action->tca_handle);
+ if (clinfo == NULL)
+ return (QOPERR_BADCLASS);
+ clist[0] = clinfo;
+ clist[1] = NULL;
+#if 1
+ /*
+ * if the conditioner referred to doesn't have a name,
+ * this is called just to add a name to it.
+ * we can simply add the name to the existing conditioner
+ * and return it.
+ */
+ if (cdnr_name != NULL &&
+ strcmp(clinfo->clname, "(null)") == 0) {
+ free(clinfo->clname);
+ clinfo->clname = strdup(cdnr_name);
+ if (rp != NULL)
+ *rp = clinfo;
+ return (0);
+ }
+#endif
+ } else
+ clist[0] = NULL;
+
+ if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
+ return (QOPERR_NOMEM);
+
+ cdnrinfo->tce_type = TCETYPE_ELEMENT;
+ cdnrinfo->tce_un.element.action = *action;
+
+ if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
+ cdnrinfo)) != 0)
+ goto err_ret;
+
+ if (rp != NULL)
+ *rp = clinfo;
+ return (0);
+
+ err_ret:
+ if (cdnrinfo != NULL)
+ free(cdnrinfo);
+ return (error);
+}
+
+int
+qop_cdnr_add_tbmeter(struct classinfo **rp, const char *cdnr_name,
+ struct ifinfo *ifinfo,
+ struct tb_profile *profile,
+ struct tc_action *in_action,
+ struct tc_action *out_action)
+{
+ struct classinfo *clinfo, *clist[3];
+ struct cdnrinfo *cdnrinfo = NULL;
+ int n, error;
+
+ n = 0;
+ if (in_action->tca_code == TCACODE_HANDLE) {
+ clist[n] = clhandle2clinfo(ifinfo, in_action->tca_handle);
+ if (clist[n] == NULL)
+ return (QOPERR_BADCLASS);
+ n++;
+ }
+ if (out_action->tca_code == TCACODE_HANDLE) {
+ clist[n] = clhandle2clinfo(ifinfo, out_action->tca_handle);
+ if (clist[n] == NULL)
+ return (QOPERR_BADCLASS);
+ n++;
+ }
+ clist[n] = NULL;
+
+ if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
+ return (QOPERR_NOMEM);
+
+ cdnrinfo->tce_type = TCETYPE_TBMETER;
+ cdnrinfo->tce_un.tbmeter.profile = *profile;
+ cdnrinfo->tce_un.tbmeter.in_action = *in_action;
+ cdnrinfo->tce_un.tbmeter.out_action = *out_action;
+
+ if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
+ cdnrinfo)) != 0)
+ goto err_ret;
+
+ if (rp != NULL)
+ *rp = clinfo;
+ return (0);
+
+ err_ret:
+ if (cdnrinfo != NULL)
+ free(cdnrinfo);
+ return (error);
+}
+
+int
+qop_cdnr_modify_tbmeter(struct classinfo *clinfo, struct tb_profile *profile)
+{
+ struct cdnrinfo *cdnrinfo = clinfo->private;
+
+ if (cdnrinfo->tce_type != TCETYPE_TBMETER)
+ return (QOPERR_CLASS_INVAL);
+ cdnrinfo->tce_un.tbmeter.profile = *profile;
+
+ return qop_modify_class(clinfo, NULL);
+}
+
+int
+qop_cdnr_add_trtcm(struct classinfo **rp, const char *cdnr_name,
+ struct ifinfo *ifinfo,
+ struct tb_profile *cmtd_profile,
+ struct tb_profile *peak_profile,
+ struct tc_action *green_action,
+ struct tc_action *yellow_action,
+ struct tc_action *red_action, int coloraware)
+{
+ struct classinfo *clinfo, *clist[4];
+ struct cdnrinfo *cdnrinfo = NULL;
+ int n, error;
+
+ n = 0;
+ if (green_action->tca_code == TCACODE_HANDLE) {
+ clist[n] = clhandle2clinfo(ifinfo, green_action->tca_handle);
+ if (clist[n] == NULL)
+ return (QOPERR_BADCLASS);
+ n++;
+ }
+ if (yellow_action->tca_code == TCACODE_HANDLE) {
+ clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
+ if (clist[n] == NULL)
+ return (QOPERR_BADCLASS);
+ n++;
+ }
+ if (red_action->tca_code == TCACODE_HANDLE) {
+ clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
+ if (clist[n] == NULL)
+ return (QOPERR_BADCLASS);
+ n++;
+ }
+ clist[n] = NULL;
+
+ if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
+ return (QOPERR_NOMEM);
+
+ cdnrinfo->tce_type = TCETYPE_TRTCM;
+ cdnrinfo->tce_un.trtcm.cmtd_profile = *cmtd_profile;
+ cdnrinfo->tce_un.trtcm.peak_profile = *peak_profile;
+ cdnrinfo->tce_un.trtcm.green_action = *green_action;
+ cdnrinfo->tce_un.trtcm.yellow_action = *yellow_action;
+ cdnrinfo->tce_un.trtcm.red_action = *red_action;
+ cdnrinfo->tce_un.trtcm.coloraware = coloraware;
+
+ if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
+ cdnrinfo)) != 0)
+ goto err_ret;
+
+ if (rp != NULL)
+ *rp = clinfo;
+ return (0);
+
+ err_ret:
+ if (cdnrinfo != NULL)
+ free(cdnrinfo);
+ return (error);
+}
+
+int
+qop_cdnr_modify_trtcm(struct classinfo *clinfo,
+ struct tb_profile *cmtd_profile,
+ struct tb_profile *peak_profile, int coloraware)
+{
+ struct cdnrinfo *cdnrinfo = clinfo->private;
+
+ if (cdnrinfo->tce_type != TCETYPE_TRTCM)
+ return (QOPERR_CLASS_INVAL);
+ cdnrinfo->tce_un.trtcm.cmtd_profile = *cmtd_profile;
+ cdnrinfo->tce_un.trtcm.peak_profile = *peak_profile;
+ cdnrinfo->tce_un.trtcm.coloraware = coloraware;
+
+ return qop_modify_class(clinfo, NULL);
+}
+
+int
+qop_cdnr_add_tswtcm(struct classinfo **rp, const char *cdnr_name,
+ struct ifinfo *ifinfo, const u_int32_t cmtd_rate,
+ const u_int32_t peak_rate, const u_int32_t avg_interval,
+ struct tc_action *green_action,
+ struct tc_action *yellow_action,
+ struct tc_action *red_action)
+{
+ struct classinfo *clinfo, *clist[4];
+ struct cdnrinfo *cdnrinfo = NULL;
+ int n, error;
+
+ n = 0;
+ if (green_action->tca_code == TCACODE_HANDLE) {
+ clist[n] = clhandle2clinfo(ifinfo, green_action->tca_handle);
+ if (clist[n] == NULL)
+ return (QOPERR_BADCLASS);
+ n++;
+ }
+ if (yellow_action->tca_code == TCACODE_HANDLE) {
+ clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
+ if (clist[n] == NULL)
+ return (QOPERR_BADCLASS);
+ n++;
+ }
+ if (red_action->tca_code == TCACODE_HANDLE) {
+ clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
+ if (clist[n] == NULL)
+ return (QOPERR_BADCLASS);
+ n++;
+ }
+ clist[n] = NULL;
+
+ if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
+ return (QOPERR_NOMEM);
+
+ cdnrinfo->tce_type = TCETYPE_TSWTCM;
+ cdnrinfo->tce_un.tswtcm.cmtd_rate = cmtd_rate;
+ cdnrinfo->tce_un.tswtcm.peak_rate = peak_rate;
+ cdnrinfo->tce_un.tswtcm.avg_interval = avg_interval;
+ cdnrinfo->tce_un.tswtcm.green_action = *green_action;
+ cdnrinfo->tce_un.tswtcm.yellow_action = *yellow_action;
+ cdnrinfo->tce_un.tswtcm.red_action = *red_action;
+
+ if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
+ cdnrinfo)) != 0)
+ goto err_ret;
+
+ if (rp != NULL)
+ *rp = clinfo;
+ return (0);
+
+ err_ret:
+ if (cdnrinfo != NULL)
+ free(cdnrinfo);
+ return (error);
+}
+
+int
+qop_cdnr_modify_tswtcm(struct classinfo *clinfo, const u_int32_t cmtd_rate,
+ const u_int32_t peak_rate, const u_int32_t avg_interval)
+{
+ struct cdnrinfo *cdnrinfo = clinfo->private;
+
+ if (cdnrinfo->tce_type != TCETYPE_TSWTCM)
+ return (QOPERR_CLASS_INVAL);
+ cdnrinfo->tce_un.tswtcm.cmtd_rate = cmtd_rate;
+ cdnrinfo->tce_un.tswtcm.peak_rate = peak_rate;
+ cdnrinfo->tce_un.tswtcm.avg_interval = avg_interval;
+
+ return qop_modify_class(clinfo, NULL);
+}
+
+/*
+ * system call interfaces for qdisc_ops
+ */
+static int
+cdnr_attach(struct ifinfo *ifinfo)
+{
+ struct cdnr_interface iface;
+
+ if (cdnr_fd < 0 &&
+ (cdnr_fd = open(CDNR_DEVICE, O_RDWR)) < 0 &&
+ (cdnr_fd = open_module(CDNR_DEVICE, O_RDWR)) < 0) {
+ LOG(LOG_ERR, errno, "CDNR open\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ cdnr_refcount++;
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
+
+ if (ioctl(cdnr_fd, CDNR_IF_ATTACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+#if 1
+ LOG(LOG_INFO, 0, "conditioner attached to %s\n", iface.cdnr_ifname);
+#endif
+ return (0);
+}
+
+static int
+cdnr_detach(struct ifinfo *ifinfo)
+{
+ struct cdnr_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
+
+ if (ioctl(cdnr_fd, CDNR_IF_DETACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ if (--cdnr_refcount == 0) {
+ close(cdnr_fd);
+ cdnr_fd = -1;
+ }
+ return (0);
+}
+
+static int
+cdnr_enable(struct ifinfo *ifinfo)
+{
+ struct cdnr_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
+
+ if (ioctl(cdnr_fd, CDNR_ENABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+cdnr_disable(struct ifinfo *ifinfo)
+{
+ struct cdnr_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
+
+ if (ioctl(cdnr_fd, CDNR_DISABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+cdnr_add_class(struct classinfo *clinfo)
+{
+ struct cdnr_add_element element_add;
+ struct cdnr_add_tbmeter tbmeter_add;
+ struct cdnr_add_trtcm trtcm_add;
+ struct cdnr_add_tswtcm tswtcm_add;
+ struct cdnrinfo *cdnrinfo;
+
+ cdnrinfo = clinfo->private;
+
+ /* root class is a dummy class */
+ if (clinfo->parent == NULL) {
+ clinfo->handle = 0;
+ return (0);
+ }
+
+ switch (cdnrinfo->tce_type) {
+ case TCETYPE_ELEMENT:
+ memset(&element_add, 0, sizeof(element_add));
+ strncpy(element_add.iface.cdnr_ifname,
+ clinfo->ifinfo->ifname+1, IFNAMSIZ);
+ element_add.action = cdnrinfo->tce_un.element.action;
+ if (ioctl(cdnr_fd, CDNR_ADD_ELEM, &element_add) < 0) {
+ clinfo->handle = CDNR_NULL_HANDLE;
+ return (QOPERR_SYSCALL);
+ }
+ clinfo->handle = element_add.cdnr_handle;
+ break;
+
+ case TCETYPE_TBMETER:
+ memset(&tbmeter_add, 0, sizeof(tbmeter_add));
+ strncpy(tbmeter_add.iface.cdnr_ifname,
+ clinfo->ifinfo->ifname+1, IFNAMSIZ);
+ tbmeter_add.profile = cdnrinfo->tce_un.tbmeter.profile;
+ tbmeter_add.in_action = cdnrinfo->tce_un.tbmeter.in_action;
+ tbmeter_add.out_action = cdnrinfo->tce_un.tbmeter.out_action;
+ if (ioctl(cdnr_fd, CDNR_ADD_TBM, &tbmeter_add) < 0) {
+ clinfo->handle = CDNR_NULL_HANDLE;
+ return (QOPERR_SYSCALL);
+ }
+ clinfo->handle = tbmeter_add.cdnr_handle;
+ break;
+
+ case TCETYPE_TRTCM:
+ memset(&trtcm_add, 0, sizeof(trtcm_add));
+ strncpy(trtcm_add.iface.cdnr_ifname,
+ clinfo->ifinfo->ifname+1, IFNAMSIZ);
+ trtcm_add.cmtd_profile = cdnrinfo->tce_un.trtcm.cmtd_profile;
+ trtcm_add.peak_profile = cdnrinfo->tce_un.trtcm.peak_profile;
+ trtcm_add.green_action = cdnrinfo->tce_un.trtcm.green_action;
+ trtcm_add.yellow_action = cdnrinfo->tce_un.trtcm.yellow_action;
+ trtcm_add.red_action = cdnrinfo->tce_un.trtcm.red_action;
+ trtcm_add.coloraware = cdnrinfo->tce_un.trtcm.coloraware;
+ if (ioctl(cdnr_fd, CDNR_ADD_TCM, &trtcm_add) < 0) {
+ clinfo->handle = CDNR_NULL_HANDLE;
+ return (QOPERR_SYSCALL);
+ }
+ clinfo->handle = trtcm_add.cdnr_handle;
+ break;
+
+ case TCETYPE_TSWTCM:
+ memset(&tswtcm_add, 0, sizeof(tswtcm_add));
+ strncpy(tswtcm_add.iface.cdnr_ifname,
+ clinfo->ifinfo->ifname+1, IFNAMSIZ);
+ tswtcm_add.cmtd_rate = cdnrinfo->tce_un.tswtcm.cmtd_rate;
+ tswtcm_add.peak_rate = cdnrinfo->tce_un.tswtcm.peak_rate;
+ tswtcm_add.avg_interval = cdnrinfo->tce_un.tswtcm.avg_interval;
+ tswtcm_add.green_action = cdnrinfo->tce_un.tswtcm.green_action;
+ tswtcm_add.yellow_action = cdnrinfo->tce_un.tswtcm.yellow_action;
+ tswtcm_add.red_action = cdnrinfo->tce_un.tswtcm.red_action;
+ if (ioctl(cdnr_fd, CDNR_ADD_TSW, &tswtcm_add) < 0) {
+ clinfo->handle = CDNR_NULL_HANDLE;
+ return (QOPERR_SYSCALL);
+ }
+ clinfo->handle = tswtcm_add.cdnr_handle;
+ break;
+
+ default:
+ return (QOPERR_CLASS_INVAL);
+ }
+ return (0);
+}
+
+static int
+cdnr_modify_class(struct classinfo *clinfo, void *arg)
+{
+ struct cdnr_modify_tbmeter tbmeter_modify;
+ struct cdnr_modify_trtcm trtcm_modify;
+ struct cdnr_modify_tswtcm tswtcm_modify;
+ struct cdnrinfo *cdnrinfo;
+
+ cdnrinfo = clinfo->private;
+
+ switch (cdnrinfo->tce_type) {
+ case TCETYPE_TBMETER:
+ memset(&tbmeter_modify, 0, sizeof(tbmeter_modify));
+ strncpy(tbmeter_modify.iface.cdnr_ifname,
+ clinfo->ifinfo->ifname+1, IFNAMSIZ);
+ tbmeter_modify.cdnr_handle = clinfo->handle;
+ tbmeter_modify.profile = cdnrinfo->tce_un.tbmeter.profile;
+ if (ioctl(cdnr_fd, CDNR_MOD_TBM, &tbmeter_modify) < 0)
+ return (QOPERR_SYSCALL);
+ break;
+
+ case TCETYPE_TRTCM:
+ memset(&trtcm_modify, 0, sizeof(trtcm_modify));
+ strncpy(trtcm_modify.iface.cdnr_ifname,
+ clinfo->ifinfo->ifname+1, IFNAMSIZ);
+ trtcm_modify.cdnr_handle = clinfo->handle;
+ trtcm_modify.cmtd_profile =
+ cdnrinfo->tce_un.trtcm.cmtd_profile;
+ trtcm_modify.peak_profile =
+ cdnrinfo->tce_un.trtcm.peak_profile;
+ trtcm_modify.coloraware = cdnrinfo->tce_un.trtcm.coloraware;
+ if (ioctl(cdnr_fd, CDNR_MOD_TCM, &trtcm_modify) < 0)
+ return (QOPERR_SYSCALL);
+ break;
+
+ case TCETYPE_TSWTCM:
+ memset(&tswtcm_modify, 0, sizeof(tswtcm_modify));
+ strncpy(tswtcm_modify.iface.cdnr_ifname,
+ clinfo->ifinfo->ifname+1, IFNAMSIZ);
+ tswtcm_modify.cdnr_handle = clinfo->handle;
+ tswtcm_modify.cmtd_rate = cdnrinfo->tce_un.tswtcm.cmtd_rate;
+ tswtcm_modify.peak_rate = cdnrinfo->tce_un.tswtcm.peak_rate;
+ tswtcm_modify.avg_interval = cdnrinfo->tce_un.tswtcm.avg_interval;
+ if (ioctl(cdnr_fd, CDNR_MOD_TSW, &tswtcm_modify) < 0)
+ return (QOPERR_SYSCALL);
+ break;
+
+ default:
+ return (QOPERR_CLASS_INVAL);
+ }
+ return (0);
+}
+
+static int
+cdnr_delete_class(struct classinfo *clinfo)
+{
+ struct cdnr_delete_element element_delete;
+
+ if (clinfo->handle == CDNR_NULL_HANDLE)
+ return (0);
+
+ memset(&element_delete, 0, sizeof(element_delete));
+ strncpy(element_delete.iface.cdnr_ifname, clinfo->ifinfo->ifname+1,
+ IFNAMSIZ);
+ element_delete.cdnr_handle = clinfo->handle;
+
+ if (ioctl(cdnr_fd, CDNR_DEL_ELEM, &element_delete) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+cdnr_add_filter(struct fltrinfo *fltrinfo)
+{
+ struct cdnr_add_filter fltr_add;
+
+ memset(&fltr_add, 0, sizeof(fltr_add));
+ strncpy(fltr_add.iface.cdnr_ifname,
+ fltrinfo->clinfo->ifinfo->ifname+1, IFNAMSIZ);
+ fltr_add.cdnr_handle = fltrinfo->clinfo->handle;
+ fltr_add.filter = fltrinfo->fltr;
+
+ if (ioctl(cdnr_fd, CDNR_ADD_FILTER, &fltr_add) < 0)
+ return (QOPERR_SYSCALL);
+ fltrinfo->handle = fltr_add.filter_handle;
+ return (0);
+}
+
+static int
+cdnr_delete_filter(struct fltrinfo *fltrinfo)
+{
+ struct cdnr_delete_filter fltr_del;
+
+ memset(&fltr_del, 0, sizeof(fltr_del));
+ strncpy(fltr_del.iface.cdnr_ifname,
+ fltrinfo->clinfo->ifinfo->ifname+1, IFNAMSIZ);
+ fltr_del.filter_handle = fltrinfo->handle;
+
+ if (ioctl(cdnr_fd, CDNR_DEL_FILTER, &fltr_del) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+
+static int
+verify_tbprofile(struct tb_profile *profile, const char *cdnr_name)
+{
+ if (profile->depth < 1500) {
+ LOG(LOG_WARNING, 0,
+ "warning: token bucket depth for %s is too small (%d)\n",
+ cdnr_name, profile->depth);
+ return (-1);
+ }
+ return (0);
+}
+
diff --git a/usr.sbin/altq/libaltq/qop_cdnr.h b/usr.sbin/altq/libaltq/qop_cdnr.h
new file mode 100644
index 00000000000..127387f3fc7
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_cdnr.h
@@ -0,0 +1,119 @@
+/* $OpenBSD: qop_cdnr.h,v 1.1 2001/06/27 18:23:30 kjc Exp $ */
+/* $KAME: qop_cdnr.h,v 1.4 2000/10/18 09:15:19 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * struct classinfo is used also for traffic conditioners
+ */
+
+/* discipline specific class info */
+struct cdnrinfo {
+ int tce_type;
+ union {
+ struct {
+ struct tc_action action;
+ } element;
+ struct {
+ struct tb_profile profile;
+ struct tc_action in_action;
+ struct tc_action out_action;
+ } tbmeter;
+ struct {
+ struct tb_profile cmtd_profile;
+ struct tb_profile peak_profile;
+ struct tc_action green_action;
+ struct tc_action yellow_action;
+ struct tc_action red_action;
+ int coloraware;
+ } trtcm;
+ struct {
+ u_int32_t cmtd_rate;
+ u_int32_t peak_rate;
+ u_int32_t avg_interval;
+ struct tc_action green_action;
+ struct tc_action yellow_action;
+ struct tc_action red_action;
+ } tswtcm;
+ } tce_un;
+};
+
+u_long cdnr_name2handle(const char *ifname, const char *cdnr_name);
+
+int qcmd_cdnr_add_element(struct tc_action *rp, const char *ifname,
+ const char *cdnr_name, struct tc_action *action);
+int qcmd_cdnr_add_tbmeter(struct tc_action *rp, const char *ifname,
+ const char *cdnr_name,
+ struct tb_profile *profile,
+ struct tc_action *in_action,
+ struct tc_action *out_action);
+int qcmd_cdnr_add_trtcm(struct tc_action *rp, const char *ifname,
+ const char *cdnr_name,
+ struct tb_profile *cmtd_profile,
+ struct tb_profile *peak_profile,
+ struct tc_action *green_action,
+ struct tc_action *yellow_action,
+ struct tc_action *red_action, int coloraware);
+int qcmd_cdnr_add_tswtcm(struct tc_action *rp, const char *ifname,
+ const char *cdnr_name, const u_int32_t cmtd_rate,
+ const u_int32_t peak_rate,
+ const u_int32_t avg_interval,
+ struct tc_action *green_action,
+ struct tc_action *yellow_action,
+ struct tc_action *red_action);
+int qcmd_cdnr_delete(const char *ifname, const char *cdnr_name);
+int qcmd_nop_add_if(const char *ifname);
+
+int qop_add_cdnr(struct classinfo **rp, const char *cdnr_name,
+ struct ifinfo *ifinfo, struct classinfo **childlist,
+ void *cdnr_private);
+int qop_delete_cdnr(struct classinfo *clinfo);
+int qop_cdnr_add_element(struct classinfo **rp, const char *cdnr_name,
+ struct ifinfo *ifinfo, struct tc_action *action);
+int qop_cdnr_add_tbmeter(struct classinfo **rp, const char *cdnr_name,
+ struct ifinfo *ifinfo, struct tb_profile *profile,
+ struct tc_action *in_action, struct tc_action *out_action);
+int qop_cdnr_add_trtcm(struct classinfo **rp, const char *cdnr_name,
+ struct ifinfo *ifinfo,
+ struct tb_profile *cmtd_profile, struct tb_profile *peak_profile,
+ struct tc_action *green_action, struct tc_action *yellow_action,
+ struct tc_action *red_action, int colorware);
+int qop_cdnr_add_tswtcm(struct classinfo **rp, const char *cdnr_name,
+ struct ifinfo *ifinfo, const u_int32_t cmtd_rate,
+ const u_int32_t peak_rate,
+ const u_int32_t avg_interval,
+ struct tc_action *green_action,
+ struct tc_action *yellow_action,
+ struct tc_action *red_action);
+int qop_cdnr_modify_tbmeter(struct classinfo *clinfo,
+ struct tb_profile *profile);
+int qop_cdnr_modify_trtcm(struct classinfo *clinfo,
+ struct tb_profile *cmtd_profile,
+ struct tb_profile *peak_profile, int coloraware);
+int qop_cdnr_modify_tswtcm(struct classinfo *clinfo,
+ const u_int32_t cmtd_rate,
+ const u_int32_t peak_rate,
+ const u_int32_t avg_interval);
diff --git a/usr.sbin/altq/libaltq/qop_conf.c b/usr.sbin/altq/libaltq/qop_conf.c
new file mode 100644
index 00000000000..35d2abf0e5b
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_conf.c
@@ -0,0 +1,67 @@
+/* $OpenBSD: qop_conf.c,v 1.1 2001/06/27 18:23:30 kjc Exp $ */
+/* $KAME: qop_conf.c,v 1.2 2000/10/18 09:15:19 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <stdio.h>
+
+#include <altq/altq.h>
+#include "altq_qop.h"
+
+typedef int (interface_parser_t)(const char *, int, char **);
+typedef int (class_parser_t)(const char *, const char *, const char *,
+ int, char **);
+
+extern interface_parser_t null_interface_parser;
+extern class_parser_t null_class_parser;
+extern interface_parser_t cbq_interface_parser;
+extern class_parser_t cbq_class_parser;
+extern interface_parser_t hfsc_interface_parser;
+extern class_parser_t hfsc_class_parser;
+extern interface_parser_t red_interface_parser;
+extern interface_parser_t rio_interface_parser;
+extern interface_parser_t blue_interface_parser;
+extern interface_parser_t wfq_interface_parser;
+extern interface_parser_t fifoq_interface_parser;
+extern interface_parser_t priq_interface_parser;
+extern class_parser_t priq_class_parser;
+
+struct qdisc_parser qdisc_parser[] = {
+ {"null", null_interface_parser, null_class_parser},
+ {"cbq", cbq_interface_parser, cbq_class_parser},
+ {"hfsc", hfsc_interface_parser, hfsc_class_parser},
+ {"red", red_interface_parser, NULL},
+ {"rio", rio_interface_parser, NULL},
+ {"blue", blue_interface_parser, NULL},
+ {"wfq", wfq_interface_parser, NULL},
+ {"fifoq", fifoq_interface_parser, NULL},
+ {"priq", priq_interface_parser, priq_class_parser},
+ {NULL, NULL, NULL}
+};
+
diff --git a/usr.sbin/altq/libaltq/qop_dummy.c b/usr.sbin/altq/libaltq/qop_dummy.c
new file mode 100644
index 00000000000..b46f0f69814
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_dummy.c
@@ -0,0 +1,188 @@
+/* $OpenBSD: qop_dummy.c,v 1.1 2001/06/27 18:23:30 kjc Exp $ */
+/* $KAME: qop_dummy.c,v 1.2 2000/10/18 09:15:19 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <altq/altq.h>
+#include "altq_qop.h"
+
+int null_interface_parser(const char *ifname, int argc, char **argv);
+int null_class_parser(const char *ifname, const char *class_name,
+ const char *parent_name, int argc, char **argv);
+int qcmd_nop_add_if(const char *ifname);
+static int nop_attach(struct ifinfo *ifinfo);
+static int nop_detach(struct ifinfo *ifinfo);
+static int nop_clear(struct ifinfo *ifinfo);
+static int nop_enable(struct ifinfo *ifinfo);
+static int nop_disable(struct ifinfo *ifinfo);
+static int nop_add_class(struct classinfo *clinfo);
+static int nop_modify_class(struct classinfo *clinfo, void *arg);
+static int nop_delete_class(struct classinfo *clinfo);
+static int nop_add_filter(struct fltrinfo *fltrinfo);
+static int nop_delete_filter(struct fltrinfo *fltrinfo);
+
+struct qdisc_ops nop_qdisc = {
+ ALTQT_NONE,
+ "nop",
+ nop_attach,
+ nop_detach,
+ nop_clear,
+ nop_enable,
+ nop_disable,
+ nop_add_class,
+ nop_modify_class,
+ nop_delete_class,
+ nop_add_filter,
+ nop_delete_filter,
+};
+
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+/*
+ * parser interface for null interface
+ */
+int
+null_interface_parser(const char *ifname, int argc, char **argv)
+{
+ u_int bandwidth = 0;
+ u_int tbrsize = 0;
+
+ /*
+ * process options
+ */
+ while (argc > 0) {
+ if (EQUAL(*argv, "bandwidth")) {
+ argc--; argv++;
+ if (argc > 0)
+ bandwidth = atobps(*argv);
+ } else if (EQUAL(*argv, "tbrsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ tbrsize = atobytes(*argv);
+ } else {
+ LOG(LOG_ERR, 0, "Unknown keyword '%s'\n", argv);
+ return (0);
+ }
+ argc--; argv++;
+ }
+
+ if (bandwidth != 0)
+ if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0)
+ return (0);
+
+ /*
+ * add a dummy interface since traffic conditioner might need it.
+ */
+ if (qcmd_nop_add_if(ifname) != 0)
+ return (0);
+ return (1);
+}
+
+int
+null_class_parser(const char *ifname, const char *class_name,
+ const char *parent_name, int argc, char **argv)
+{
+ LOG(LOG_ERR, 0,
+ "class cannot be defined without a queueing discipline in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+}
+
+/*
+ * qcmd api
+ */
+int
+qcmd_nop_add_if(const char *ifname)
+{
+ int error;
+
+ error = qop_add_if(NULL, ifname, 0, &nop_qdisc, NULL);
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: can't add nop on interface '%s'\n",
+ qoperror(error), ifname);
+ return (error);
+}
+
+/*
+ * qop api
+ */
+static int nop_attach(struct ifinfo *ifinfo)
+{
+ return (0);
+}
+
+static int nop_detach(struct ifinfo *ifinfo)
+{
+ return (0);
+}
+
+static int nop_clear(struct ifinfo *ifinfo)
+{
+ return (0);
+}
+
+static int nop_enable(struct ifinfo *ifinfo)
+{
+ return (0);
+}
+
+static int nop_disable(struct ifinfo *ifinfo)
+{
+ return (0);
+}
+
+static int nop_add_class(struct classinfo *clinfo)
+{
+ return (0);
+}
+
+static int nop_modify_class(struct classinfo *clinfo, void *arg)
+{
+ return (0);
+}
+
+static int nop_delete_class(struct classinfo *clinfo)
+{
+ return (0);
+}
+
+static int nop_add_filter(struct fltrinfo *fltrinfo)
+{
+ return (0);
+}
+
+static int nop_delete_filter(struct fltrinfo *fltrinfo)
+{
+ return (0);
+}
diff --git a/usr.sbin/altq/libaltq/qop_errlist.c b/usr.sbin/altq/libaltq/qop_errlist.c
new file mode 100644
index 00000000000..2dcb0621a99
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_errlist.c
@@ -0,0 +1,49 @@
+/* $OpenBSD: qop_errlist.c,v 1.1 2001/06/27 18:23:22 kjc Exp $ */
+/* $KAME: qop_errlist.c,v 1.2 2000/10/18 09:15:19 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+const char *qop_errlist[] = {
+ "no error", /* 0 */
+ "syscall error", /* 1 */
+ "no memory", /* 2 */
+ "invalid parameter", /* 3 */
+ "out of range", /* 4 */
+ "bad interface", /* 5 */
+ "bad class", /* 6 */
+ "bad filter", /* 7 */
+ "class error", /* 8 */
+ "bad class value", /* 9 */
+ "class operation not permitted", /* 10 */
+ "filter error", /* 11 */
+ "bad filter value", /* 12 */
+ "filter shadows an existing filter", /* 13 */
+ "admission failure", /* 14 */
+ "admission failure (no bandwidth)", /* 15 */
+ "admission failure (delay)", /* 16 */
+ "admission failure (no service)", /* 17 */
+ "policy error", /* 18 */
+};
diff --git a/usr.sbin/altq/libaltq/qop_fifoq.c b/usr.sbin/altq/libaltq/qop_fifoq.c
new file mode 100644
index 00000000000..cedf6a46eb9
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_fifoq.c
@@ -0,0 +1,246 @@
+/* $OpenBSD: qop_fifoq.c,v 1.1 2001/06/27 18:23:31 kjc Exp $ */
+/* $KAME: qop_fifoq.c,v 1.3 2000/10/18 09:15:19 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <altq/altq.h>
+#include <altq/altq_fifoq.h>
+#include "altq_qop.h"
+#include "qop_fifoq.h"
+
+static int fifoq_attach(struct ifinfo *ifinfo);
+static int fifoq_detach(struct ifinfo *ifinfo);
+static int fifoq_enable(struct ifinfo *ifinfo);
+static int fifoq_disable(struct ifinfo *ifinfo);
+
+#define FIFOQ_DEVICE "/dev/altq/fifoq"
+
+static int fifoq_fd = -1;
+static int fifoq_refcount = 0;
+
+static struct qdisc_ops fifoq_qdisc = {
+ ALTQT_FIFOQ,
+ "fifoq",
+ fifoq_attach,
+ fifoq_detach,
+ NULL, /* clear */
+ fifoq_enable,
+ fifoq_disable,
+ NULL, /* add class */
+ NULL, /* modify class */
+ NULL, /* delete class */
+ NULL, /* add filter */
+ NULL /* delete filter */
+};
+
+/*
+ * parser interface
+ */
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+int
+fifoq_interface_parser(const char *ifname, int argc, char **argv)
+{
+ u_int bandwidth = 100000000; /* 100Mbps */
+ u_int tbrsize = 0;
+ int qlimit = 50;
+
+ /*
+ * process options
+ */
+ while (argc > 0) {
+ if (EQUAL(*argv, "bandwidth")) {
+ argc--; argv++;
+ if (argc > 0)
+ bandwidth = atobps(*argv);
+ } else if (EQUAL(*argv, "tbrsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ tbrsize = atobytes(*argv);
+ } else if (EQUAL(*argv, "qlimit")) {
+ argc--; argv++;
+ if (argc > 0)
+ qlimit = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "fifoq")) {
+ /* just skip */
+ } else {
+ LOG(LOG_ERR, 0, "Unknown keyword '%s'\n", argv);
+ return (0);
+ }
+ argc--; argv++;
+ }
+
+ if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0)
+ return (0);
+
+ if (qcmd_fifoq_add_if(ifname, bandwidth, qlimit) != 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * qcmd api
+ */
+int
+qcmd_fifoq_add_if(const char *ifname, u_int bandwidth, int qlimit)
+{
+ int error;
+
+ error = qop_fifoq_add_if(NULL, ifname, bandwidth, qlimit);
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: can't add fifoq on interface '%s'\n",
+ qoperror(error), ifname);
+ return (error);
+}
+
+/*
+ * qop api
+ */
+int
+qop_fifoq_add_if(struct ifinfo **rp, const char *ifname,
+ u_int bandwidth, int qlimit)
+{
+ struct ifinfo *ifinfo = NULL;
+ struct fifoq_ifinfo *fifoq_ifinfo;
+ int error;
+
+ if ((fifoq_ifinfo = calloc(1, sizeof(*fifoq_ifinfo))) == NULL)
+ return (QOPERR_NOMEM);
+ fifoq_ifinfo->qlimit = qlimit;
+
+ error = qop_add_if(&ifinfo, ifname, bandwidth,
+ &fifoq_qdisc, fifoq_ifinfo);
+ if (error != 0) {
+ free(fifoq_ifinfo);
+ return (error);
+ }
+
+ if (rp != NULL)
+ *rp = ifinfo;
+ return (0);
+}
+
+/*
+ * system call interfaces for qdisc_ops
+ */
+static int
+fifoq_attach(struct ifinfo *ifinfo)
+{
+ struct fifoq_interface iface;
+ struct fifoq_ifinfo *fifoq_ifinfo;
+ struct fifoq_conf conf;
+
+ if (fifoq_fd < 0 &&
+ (fifoq_fd = open(FIFOQ_DEVICE, O_RDWR)) < 0 &&
+ (fifoq_fd = open_module(FIFOQ_DEVICE, O_RDWR)) < 0) {
+ LOG(LOG_ERR, errno, "FIFOQ open\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ fifoq_refcount++;
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.fifoq_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(fifoq_fd, FIFOQ_IF_ATTACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ /* set fifoq parameters */
+ fifoq_ifinfo = (struct fifoq_ifinfo *)ifinfo->private;
+ if (fifoq_ifinfo->qlimit > 0) {
+ memset(&conf, 0, sizeof(conf));
+ strncpy(conf.iface.fifoq_ifname, ifinfo->ifname, IFNAMSIZ);
+ conf.fifoq_limit = fifoq_ifinfo->qlimit;
+ if (ioctl(fifoq_fd, FIFOQ_CONFIG, &conf) < 0)
+ return (QOPERR_SYSCALL);
+ }
+#if 1
+ LOG(LOG_INFO, 0, "fifoq attached to %s\n", iface.fifoq_ifname);
+#endif
+ return (0);
+}
+
+static int
+fifoq_detach(struct ifinfo *ifinfo)
+{
+ struct fifoq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.fifoq_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(fifoq_fd, FIFOQ_IF_DETACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ if (--fifoq_refcount == 0) {
+ close(fifoq_fd);
+ fifoq_fd = -1;
+ }
+ return (0);
+}
+
+static int
+fifoq_enable(struct ifinfo *ifinfo)
+{
+ struct fifoq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.fifoq_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(fifoq_fd, FIFOQ_ENABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+fifoq_disable(struct ifinfo *ifinfo)
+{
+ struct fifoq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.fifoq_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(fifoq_fd, FIFOQ_DISABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
diff --git a/usr.sbin/altq/libaltq/qop_fifoq.h b/usr.sbin/altq/libaltq/qop_fifoq.h
new file mode 100644
index 00000000000..b32c1570f6d
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_fifoq.h
@@ -0,0 +1,39 @@
+/* $OpenBSD: qop_fifoq.h,v 1.1 2001/06/27 18:23:31 kjc Exp $ */
+/* $KAME: qop_fifoq.h,v 1.2 2000/10/18 09:15:19 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * fifoq private ifinfo structure
+ */
+struct fifoq_ifinfo {
+ int qlimit; /* max queue length */
+};
+
+int fifoq_interface_parser(const char *ifname, int argc, char **argv);
+int qcmd_fifoq_add_if(const char *ifname, u_int bandwidth, int qlimit);
+int qop_fifoq_add_if(struct ifinfo **rp, const char *ifname,
+ u_int bandwidth, int qlimit);
diff --git a/usr.sbin/altq/libaltq/qop_hfsc.c b/usr.sbin/altq/libaltq/qop_hfsc.c
new file mode 100644
index 00000000000..b9e9d40a532
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_hfsc.c
@@ -0,0 +1,1097 @@
+/* $OpenBSD: qop_hfsc.c,v 1.1 2001/06/27 18:23:33 kjc Exp $ */
+/* $KAME: qop_hfsc.c,v 1.4 2000/10/18 09:15:19 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <altq/altq.h>
+#include <altq/altq_hfsc.h>
+#include "altq_qop.h"
+#include "qop_hfsc.h"
+
+static int read_sc(int *argcp, char ***argvp,
+ int *type, u_int *m1, u_int *d, u_int *m2);
+static int qop_hfsc_enable_hook(struct ifinfo *ifinfo);
+static int qop_hfsc_delete_class_hook(struct classinfo *clinfo);
+static int validate_sc(struct service_curve *sc);
+
+static void gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc);
+static void gsc_sub_sc(struct gen_sc *gsc, struct service_curve *sc);
+static int is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc);
+static void gsc_destroy(struct gen_sc *gsc);
+static struct segment *gsc_getentry(struct gen_sc *gsc, double x);
+static int gsc_add_seg(struct gen_sc *gsc,
+ double x, double y, double d, double m);
+static int gsc_sub_seg(struct gen_sc *gsc,
+ double x, double y, double d, double m);
+static void gsc_compress(struct gen_sc *gsc);
+static double sc_x2y(struct service_curve *sc, double x);
+
+static int hfsc_attach(struct ifinfo *ifinfo);
+static int hfsc_detach(struct ifinfo *ifinfo);
+static int hfsc_clear(struct ifinfo *ifinfo);
+static int hfsc_enable(struct ifinfo *ifinfo);
+static int hfsc_disable(struct ifinfo *ifinfo);
+static int hfsc_add_class(struct classinfo *clinfo);
+static int hfsc_modify_class(struct classinfo *clinfo, void *arg);
+static int hfsc_delete_class(struct classinfo *clinfo);
+static int hfsc_add_filter(struct fltrinfo *fltrinfo);
+static int hfsc_delete_filter(struct fltrinfo *fltrinfo);
+
+#define HFSC_DEVICE "/dev/altq/hfsc"
+
+static int hfsc_fd = -1;
+static int hfsc_refcount = 0;
+
+static struct qdisc_ops hfsc_qdisc = {
+ ALTQT_HFSC,
+ "hfsc",
+ hfsc_attach,
+ hfsc_detach,
+ hfsc_clear,
+ hfsc_enable,
+ hfsc_disable,
+ hfsc_add_class,
+ hfsc_modify_class,
+ hfsc_delete_class,
+ hfsc_add_filter,
+ hfsc_delete_filter,
+};
+
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+/*
+ * parser interface
+ */
+int
+hfsc_interface_parser(const char *ifname, int argc, char **argv)
+{
+ u_int bandwidth = 100000000; /* 100Mbps */
+ u_int tbrsize = 0;
+ int flags = 0;
+
+ /*
+ * process options
+ */
+ while (argc > 0) {
+ if (EQUAL(*argv, "bandwidth")) {
+ argc--; argv++;
+ if (argc > 0)
+ bandwidth = atobps(*argv);
+ } else if (EQUAL(*argv, "tbrsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ tbrsize = atobytes(*argv);
+ } else if (EQUAL(*argv, "hfsc")) {
+ /* just skip */
+ } else {
+ LOG(LOG_ERR, 0, "Unknown keyword '%s'\n", argv);
+ return (0);
+ }
+ argc--; argv++;
+ }
+
+ if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0)
+ return (0);
+
+ if (qcmd_hfsc_add_if(ifname, bandwidth, flags) != 0)
+ return (0);
+ return (1);
+}
+
+int
+hfsc_class_parser(const char *ifname, const char *class_name,
+ const char *parent_name, int argc, char **argv)
+{
+ u_int m1, d, m2, rm1, rd, rm2, fm1, fd, fm2;
+ int qlimit = 50;
+ int flags = 0, admission = 0;
+ int type = 0, error;
+
+ rm1 = rd = rm2 = fm1 = fd = fm2 = 0;
+ while (argc > 0) {
+ if (*argv[0] == '[') {
+ if (read_sc(&argc, &argv, &type, &m1, &d, &m2) != 0) {
+ LOG(LOG_ERR, 0,
+ "Bad service curve in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+ if (type & HFSC_REALTIMESC) {
+ rm1 = m1; rd = d; rm2 = m2;
+ }
+ if (type & HFSC_LINKSHARINGSC) {
+ fm1 = m1; fd = d; fm2 = m2;
+ }
+ } else if (EQUAL(*argv, "pshare")) {
+ argc--; argv++;
+ if (argc > 0) {
+ struct ifinfo *ifinfo;
+ u_int pshare;
+
+ pshare = (u_int)strtoul(*argv, NULL, 0);
+ if ((ifinfo = ifname2ifinfo(ifname)) != NULL) {
+ fm2 = ifinfo->bandwidth / 100 * pshare;
+ type |= HFSC_LINKSHARINGSC;
+ }
+ }
+ } else if (EQUAL(*argv, "grate")) {
+ argc--; argv++;
+ if (argc > 0) {
+ rm2 = atobps(*argv);
+ type |= HFSC_REALTIMESC;
+ }
+ } else if (EQUAL(*argv, "qlimit")) {
+ argc--; argv++;
+ if (argc > 0)
+ qlimit = strtoul(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "default")) {
+ flags |= HFCF_DEFAULTCLASS;
+ } else if (EQUAL(*argv, "admission")) {
+ argc--; argv++;
+ if (argc > 0) {
+ if (EQUAL(*argv, "guaranteed")
+ || EQUAL(*argv, "cntlload"))
+ admission = 1;
+ else if (EQUAL(*argv, "none")) {
+ /* nothing */
+ } else {
+ LOG(LOG_ERR, 0,
+ "unknown admission type - %s, line %d\n",
+ *argv, line_no);
+ return (0);
+ }
+ }
+ } else if (EQUAL(*argv, "red")) {
+ flags |= HFCF_RED;
+ } else if (EQUAL(*argv, "ecn")) {
+ flags |= HFCF_ECN;
+ } else if (EQUAL(*argv, "rio")) {
+ flags |= HFCF_RIO;
+ } else if (EQUAL(*argv, "cleardscp")) {
+ flags |= HFCF_CLEARDSCP;
+ } else {
+ LOG(LOG_ERR, 0,
+ "Unknown keyword '%s' in %s, line %d\n",
+ *argv, altqconfigfile, line_no);
+ return (0);
+ }
+
+ argc--; argv++;
+ }
+
+ if (type == 0) {
+ LOG(LOG_ERR, 0,
+ "hfsc: service curve not specified in %s, line %d\n",
+ altqconfigfile, line_no);
+ return (0);
+ }
+
+ if ((flags & HFCF_ECN) && (flags & (HFCF_RED|HFCF_RIO)) == 0)
+ flags |= HFCF_RED;
+
+ /*
+ * if the link-sharing service curve is diffrent from
+ * the real-time service curve, we first create a class with the
+ * smaller service curve and then modify the other service curve.
+ */
+ if (rm2 <= fm2) {
+ m1 = rm1; d = rd; m2 = rm2;
+ } else {
+ m1 = fm1; d = fd; m2 = fm2;
+ }
+ error = qcmd_hfsc_add_class(ifname, class_name, parent_name,
+ m1, d, m2, qlimit, flags);
+
+ if (error == 0 && (rm1 != fm1 || rd != fd || rm2 != fm2)) {
+ if (rm2 <= fm2) {
+ m1 = fm1; d = fd; m2 = fm2; type = HFSC_LINKSHARINGSC;
+ } else {
+ m1 = rm1; d = rd; m2 = rm2; type = HFSC_REALTIMESC;
+ }
+ error = qcmd_hfsc_modify_class(ifname, class_name,
+ m1, d, m2, type);
+ }
+
+ if (error == 0 && admission) {
+ /* this is a special class for rsvp */
+ struct ifinfo *ifinfo = ifname2ifinfo(ifname);
+ struct classinfo *clinfo = clname2clinfo(ifinfo, class_name);
+
+ if (ifinfo->resv_class != NULL) {
+ LOG(LOG_ERR, 0,
+ "more than one admission class specified: %s\n",
+ class_name);
+ return (0);
+ }
+ ifinfo->resv_class = clinfo;
+ }
+
+ if (error) {
+ LOG(LOG_ERR, errno, "hfsc_class_parser: %s\n",
+ qoperror(error));
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * read service curve parameters
+ * '[' <type> <m1> <d> <m2> ']'
+ * type := "sc", "rt", or "ls"
+ */
+static int
+read_sc(int *argcp, char ***argvp, int *type, u_int *m1, u_int *d, u_int *m2)
+{
+ int argc = *argcp;
+ char **argv = *argvp;
+ char *cp;
+
+ cp = *argv;
+ if (*cp++ != '[')
+ return (-1);
+ if (*cp == '\0') {
+ cp = *++argv; --argc;
+ }
+ if (*cp == 's' || *cp == 'S')
+ *type = HFSC_DEFAULTSC;
+ else if (*cp == 'r' || *cp == 'R')
+ *type = HFSC_REALTIMESC;
+ else if (*cp == 'l' || *cp == 'L')
+ *type = HFSC_LINKSHARINGSC;
+ else
+ return (-1);
+ cp = *++argv; --argc;
+ *m1 = atobps(cp);
+ cp = *++argv; --argc;
+ *d = (u_int)strtoul(cp, NULL, 0);
+ cp = *++argv; --argc;
+ *m2 = atobps(cp);
+ if (strchr(cp, ']') == NULL) {
+ cp = *++argv; --argc;
+ if (*cp != ']')
+ return (-1);
+ }
+ *argcp = argc;
+ *argvp = argv;
+ return (0);
+}
+
+/*
+ * qcmd api
+ */
+int
+qcmd_hfsc_add_if(const char *ifname, u_int bandwidth, int flags)
+{
+ int error;
+
+ error = qop_hfsc_add_if(NULL, ifname, bandwidth, flags);
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: can't add hfsc on interface '%s'\n",
+ qoperror(error), ifname);
+ return (error);
+}
+
+int
+qcmd_hfsc_add_class(const char *ifname, const char *class_name,
+ const char *parent_name, u_int m1, u_int d, u_int m2,
+ int qlimit, int flags)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *parent = NULL;
+ struct service_curve sc;
+ int error = 0;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ error = QOPERR_BADIF;
+
+ if (error == 0 &&
+ (parent = clname2clinfo(ifinfo, parent_name)) == NULL)
+ error = QOPERR_BADCLASS;
+
+ sc.m1 = m1;
+ sc.d = d;
+ sc.m2 = m2;
+
+ if (error == 0)
+ error = qop_hfsc_add_class(NULL, class_name, ifinfo, parent,
+ &sc, qlimit, flags);
+ if (error != 0)
+ LOG(LOG_ERR, errno,
+ "hfsc: %s: can't add class '%s' on interface '%s'\n",
+ qoperror(error), class_name, ifname);
+ return (error);
+}
+
+int
+qcmd_hfsc_modify_class(const char *ifname, const char *class_name,
+ u_int m1, u_int d, u_int m2, int sctype)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+ struct service_curve sc;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ return (QOPERR_BADIF);
+
+ if ((clinfo = clname2clinfo(ifinfo, class_name)) == NULL)
+ return (QOPERR_BADCLASS);
+
+ sc.m1 = m1;
+ sc.d = d;
+ sc.m2 = m2;
+
+ return qop_hfsc_modify_class(clinfo, &sc, sctype);
+}
+
+/*
+ * qop api
+ */
+int
+qop_hfsc_add_if(struct ifinfo **rp, const char *ifname,
+ u_int bandwidth, int flags)
+{
+ struct ifinfo *ifinfo = NULL;
+ struct hfsc_ifinfo *hfsc_ifinfo = NULL;
+ struct service_curve sc;
+ int error;
+
+ if ((hfsc_ifinfo = calloc(1, sizeof(*hfsc_ifinfo))) == NULL)
+ return (QOPERR_NOMEM);
+
+ error = qop_add_if(&ifinfo, ifname, bandwidth,
+ &hfsc_qdisc, hfsc_ifinfo);
+ if (error != 0)
+ goto err_ret;
+
+ /* set enable hook */
+ ifinfo->enable_hook = qop_hfsc_enable_hook;
+
+ /* create a dummy root class */
+ sc.m1 = bandwidth;
+ sc.d = 0;
+ sc.m2 = bandwidth;
+ if ((error = qop_hfsc_add_class(&hfsc_ifinfo->root_class, "root",
+ ifinfo, NULL, &sc, 0, 0)) != 0) {
+ LOG(LOG_ERR, errno,
+ "hfsc: %s: can't create dummy root class on %s!\n",
+ qoperror(error), ifname);
+ (void)qop_delete_if(ifinfo);
+ return (QOPERR_CLASS);
+ }
+
+ if (rp != NULL)
+ *rp = ifinfo;
+ return (0);
+
+ err_ret:
+ if (hfsc_ifinfo != NULL) {
+ free(hfsc_ifinfo);
+ if (ifinfo != NULL)
+ ifinfo->private = NULL;
+ }
+ return (error);
+}
+
+#define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0))
+
+int
+qop_hfsc_add_class(struct classinfo **rp, const char *class_name,
+ struct ifinfo *ifinfo, struct classinfo *parent,
+ struct service_curve *sc, int qlimit, int flags)
+{
+ struct classinfo *clinfo;
+ struct hfsc_ifinfo *hfsc_ifinfo;
+ struct hfsc_classinfo *hfsc_clinfo = NULL, *parent_clinfo = NULL;
+ int error;
+
+ hfsc_ifinfo = ifinfo->private;
+ if ((flags & HFCF_DEFAULTCLASS) && hfsc_ifinfo->default_class != NULL)
+ return (QOPERR_CLASS_INVAL);
+
+ if (validate_sc(sc) != 0)
+ return (QOPERR_INVAL);
+
+ /* admission control */
+ if (parent != NULL && !is_sc_null(sc)) {
+ parent_clinfo = parent->private;
+ gsc_add_sc(&parent_clinfo->gen_rsc, sc);
+ gsc_add_sc(&parent_clinfo->gen_fsc, sc);
+ if (!is_gsc_under_sc(&parent_clinfo->gen_rsc,
+ &parent_clinfo->rsc) ||
+ !is_gsc_under_sc(&parent_clinfo->gen_fsc,
+ &parent_clinfo->fsc)) {
+ /* admission control failure */
+ error = QOPERR_ADMISSION_NOBW;
+ goto err_ret;
+ }
+ }
+
+ if ((hfsc_clinfo = calloc(1, sizeof(*hfsc_clinfo))) == NULL) {
+ error = QOPERR_NOMEM;
+ goto err_ret;
+ }
+
+ hfsc_clinfo->rsc = *sc;
+ hfsc_clinfo->fsc = *sc;
+ LIST_INIT(&hfsc_clinfo->gen_rsc);
+ LIST_INIT(&hfsc_clinfo->gen_fsc);
+ hfsc_clinfo->qlimit = qlimit;
+ hfsc_clinfo->flags = flags;
+
+ if ((error = qop_add_class(&clinfo, class_name, ifinfo, parent,
+ hfsc_clinfo)) != 0)
+ goto err_ret;
+
+ /* set delete hook */
+ clinfo->delete_hook = qop_hfsc_delete_class_hook;
+
+ if (flags & HFCF_DEFAULTCLASS)
+ hfsc_ifinfo->default_class = clinfo;
+
+ if (parent == NULL) {
+ /*
+ * if this is a root class, reserve 20% of the real-time
+ * bandwidth for safety.
+ * many network cards are not able to saturate the wire,
+ * and if we allocate real-time traffic more than the
+ * maximum sending rate of the card, hfsc is no longer
+ * able to meet the delay bound requirements.
+ */
+ hfsc_clinfo->rsc.m1 = hfsc_clinfo->rsc.m1 / 10 * 8;
+ hfsc_clinfo->rsc.m2 = hfsc_clinfo->rsc.m2 / 10 * 8;
+ }
+
+ if (rp != NULL)
+ *rp = clinfo;
+ return (0);
+
+ err_ret:
+ /* cancel admission control */
+ if (parent != NULL && !is_sc_null(sc)) {
+ gsc_sub_sc(&parent_clinfo->gen_rsc, sc);
+ gsc_sub_sc(&parent_clinfo->gen_fsc, sc);
+ }
+
+ if (hfsc_clinfo != NULL) {
+ free(hfsc_clinfo);
+ clinfo->private = NULL;
+ }
+
+ return (error);
+}
+
+/*
+ * this is called from qop_delete_class() before a class is destroyed
+ * for discipline specific cleanup.
+ */
+static int
+qop_hfsc_delete_class_hook(struct classinfo *clinfo)
+{
+ struct hfsc_classinfo *hfsc_clinfo, *parent_clinfo;
+
+ hfsc_clinfo = clinfo->private;
+
+ /* cancel admission control */
+ if (clinfo->parent != NULL) {
+ parent_clinfo = clinfo->parent->private;
+
+ gsc_sub_sc(&parent_clinfo->gen_rsc, &hfsc_clinfo->rsc);
+ gsc_sub_sc(&parent_clinfo->gen_fsc, &hfsc_clinfo->fsc);
+ }
+
+ gsc_destroy(&hfsc_clinfo->gen_rsc);
+ gsc_destroy(&hfsc_clinfo->gen_fsc);
+ return (0);
+}
+
+int
+qop_hfsc_modify_class(struct classinfo *clinfo,
+ struct service_curve *sc, int sctype)
+{
+ struct hfsc_classinfo *hfsc_clinfo, *parent_clinfo;
+ struct service_curve rsc, fsc;
+ int error;
+
+ if (validate_sc(sc) != 0)
+ return (QOPERR_INVAL);
+
+ hfsc_clinfo = clinfo->private;
+ if (clinfo->parent == NULL)
+ return (QOPERR_CLASS_INVAL);
+ parent_clinfo = clinfo->parent->private;
+
+ /* save old service curves */
+ rsc = hfsc_clinfo->rsc;
+ fsc = hfsc_clinfo->fsc;
+
+ /* admission control */
+ if (sctype & HFSC_REALTIMESC) {
+ if (!is_gsc_under_sc(&hfsc_clinfo->gen_rsc, sc)) {
+ /* admission control failure */
+ return (QOPERR_ADMISSION);
+ }
+
+ gsc_sub_sc(&parent_clinfo->gen_rsc, &hfsc_clinfo->rsc);
+ gsc_add_sc(&parent_clinfo->gen_rsc, sc);
+ if (!is_gsc_under_sc(&parent_clinfo->gen_rsc,
+ &parent_clinfo->rsc)) {
+ /* admission control failure */
+ gsc_sub_sc(&parent_clinfo->gen_rsc, sc);
+ gsc_add_sc(&parent_clinfo->gen_rsc, &hfsc_clinfo->rsc);
+ return (QOPERR_ADMISSION_NOBW);
+ }
+ hfsc_clinfo->rsc = *sc;
+ }
+ if (sctype & HFSC_LINKSHARINGSC) {
+ if (!is_gsc_under_sc(&hfsc_clinfo->gen_fsc, sc)) {
+ /* admission control failure */
+ return (QOPERR_ADMISSION);
+ }
+
+ gsc_sub_sc(&parent_clinfo->gen_fsc, &hfsc_clinfo->fsc);
+ gsc_add_sc(&parent_clinfo->gen_fsc, sc);
+ if (!is_gsc_under_sc(&parent_clinfo->gen_fsc,
+ &parent_clinfo->fsc)) {
+ /* admission control failure */
+ gsc_sub_sc(&parent_clinfo->gen_fsc, sc);
+ gsc_add_sc(&parent_clinfo->gen_fsc, &hfsc_clinfo->fsc);
+ return (QOPERR_ADMISSION_NOBW);
+ }
+ hfsc_clinfo->fsc = *sc;
+ }
+
+ error = qop_modify_class(clinfo, (void *)((long)sctype));
+ if (error == 0)
+ return (0);
+
+ /* modify failed!, restore the old service curves */
+ if (sctype & HFSC_REALTIMESC) {
+ gsc_sub_sc(&parent_clinfo->gen_rsc, sc);
+ gsc_add_sc(&parent_clinfo->gen_rsc, &rsc);
+ hfsc_clinfo->rsc = rsc;
+ }
+ if (sctype & HFSC_LINKSHARINGSC) {
+ gsc_sub_sc(&parent_clinfo->gen_fsc, sc);
+ gsc_add_sc(&parent_clinfo->gen_fsc, &fsc);
+ hfsc_clinfo->fsc = fsc;
+ }
+ return (error);
+}
+
+/*
+ * sanity check at enabling hfsc:
+ * 1. there must one default class for an interface
+ * 2. the default class must be a leaf class
+ * 3. an internal class should not have filters
+ * (rule 2 and 3 are due to the fact that the hfsc link-sharing algorithm
+ * do not schedule internal classes.)
+ */
+static int
+qop_hfsc_enable_hook(struct ifinfo *ifinfo)
+{
+ struct hfsc_ifinfo *hfsc_ifinfo;
+ struct classinfo *clinfo;
+
+ hfsc_ifinfo = ifinfo->private;
+ if (hfsc_ifinfo->default_class == NULL) {
+ LOG(LOG_ERR, 0, "hfsc: no default class on interface %s!\n",
+ ifinfo->ifname);
+ return (QOPERR_CLASS);
+ } else if (hfsc_ifinfo->default_class->child != NULL) {
+ LOG(LOG_ERR, 0, "hfsc: default class on %s must be a leaf!\n",
+ ifinfo->ifname);
+ return (QOPERR_CLASS);
+ }
+
+ LIST_FOREACH(clinfo, &ifinfo->cllist, next) {
+ if (clinfo->child != NULL && !LIST_EMPTY(&clinfo->fltrlist)) {
+ LOG(LOG_ERR, 0, "hfsc: internal class \"%s\" should not have a filter!\n",
+ clinfo->clname);
+ return (QOPERR_CLASS);
+ }
+ }
+
+ return (0);
+}
+
+static int
+validate_sc(struct service_curve *sc)
+{
+ /* the 1st segment of a concave curve must be zero */
+ if (sc->m1 < sc->m2 && sc->m1 != 0) {
+ LOG(LOG_ERR, 0, "m1 must be 0 for convex!\n");
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * admission control using generalized service curve
+ */
+#define INFINITY 1e500 /* IEEE: positive infinity */
+
+/* add a new service curve to a generilized service curve */
+static void
+gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc)
+{
+ if (is_sc_null(sc))
+ return;
+ if (sc->d != 0)
+ gsc_add_seg(gsc, 0, 0, (double)sc->d, (double)sc->m1);
+ gsc_add_seg(gsc, (double)sc->d, 0, INFINITY, (double)sc->m2);
+}
+
+/* subtract a service curve from a generilized service curve */
+static void
+gsc_sub_sc(struct gen_sc *gsc, struct service_curve *sc)
+{
+ if (is_sc_null(sc))
+ return;
+ if (sc->d != 0)
+ gsc_sub_seg(gsc, 0, 0, (double)sc->d, (double)sc->m1);
+ gsc_sub_seg(gsc, (double)sc->d, 0, INFINITY, (double)sc->m2);
+}
+
+/*
+ * check whether all points of a generalized service curve have
+ * their y-coordinates no larger than a given two-piece linear
+ * service curve.
+ */
+static int
+is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc)
+{
+ struct segment *s, *last, *end;
+ double y;
+
+ if (is_sc_null(sc)) {
+ if (LIST_EMPTY(gsc))
+ return (1);
+ LIST_FOREACH(s, gsc, _next) {
+ if (s->m != 0)
+ return (0);
+ }
+ return (1);
+ }
+ /*
+ * gsc has a dummy entry at the end with x = INFINITY.
+ * loop through up to this dummy entry.
+ */
+ end = gsc_getentry(gsc, INFINITY);
+ if (end == NULL)
+ return (1);
+ last = NULL;
+ for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) {
+ if (s->y > sc_x2y(sc, s->x))
+ return (0);
+ last = s;
+ }
+ /* last now holds the real last segment */
+ if (last == NULL)
+ return (1);
+ if (last->m > sc->m2)
+ return (0);
+ if (last->x < sc->d && last->m > sc->m1) {
+ y = last->y + (sc->d - last->x) * last->m;
+ if (y > sc_x2y(sc, sc->d))
+ return (0);
+ }
+ return (1);
+}
+
+static void
+gsc_destroy(struct gen_sc *gsc)
+{
+ struct segment *s;
+
+ while ((s = LIST_FIRST(gsc)) != NULL) {
+ LIST_REMOVE(s, _next);
+ free(s);
+ }
+}
+
+/*
+ * return a segment entry starting at x.
+ * if gsc has no entry starting at x, a new entry is created at x.
+ */
+static struct segment *
+gsc_getentry(struct gen_sc *gsc, double x)
+{
+ struct segment *new, *prev, *s;
+
+ prev = NULL;
+ LIST_FOREACH(s, gsc, _next) {
+ if (s->x == x)
+ return (s); /* matching entry found */
+ else if (s->x < x)
+ prev = s;
+ else
+ break;
+ }
+
+ /* we have to create a new entry */
+ if ((new = calloc(1, sizeof(struct segment))) == NULL)
+ return (NULL);
+
+ new->x = x;
+ if (x == INFINITY || s == NULL)
+ new->d = 0;
+ else if (s->x == INFINITY)
+ new->d = INFINITY;
+ else
+ new->d = s->x - x;
+ if (prev == NULL) {
+ /* insert the new entry at the head of the list */
+ new->y = 0;
+ new->m = 0;
+ LIST_INSERT_HEAD(gsc, new, _next);
+ } else {
+ /*
+ * the start point intersects with the segment pointed by
+ * prev. divide prev into 2 segments
+ */
+ if (x == INFINITY) {
+ prev->d = INFINITY;
+ if (prev->m == 0)
+ new->y = prev->y;
+ else
+ new->y = INFINITY;
+ } else {
+ prev->d = x - prev->x;
+ new->y = prev->d * prev->m + prev->y;
+ }
+ new->m = prev->m;
+ LIST_INSERT_AFTER(prev, new, _next);
+ }
+ return (new);
+}
+
+/* add a segment to a generalized service curve */
+static int
+gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m)
+{
+ struct segment *start, *end, *s;
+ double x2;
+
+ if (d == INFINITY)
+ x2 = INFINITY;
+ else
+ x2 = x + d;
+ start = gsc_getentry(gsc, x);
+ end = gsc_getentry(gsc, x2);
+ if (start == NULL || end == NULL)
+ return (-1);
+
+ for (s = start; s != end; s = LIST_NEXT(s, _next)) {
+ s->m += m;
+ s->y += y + (s->x - x) * m;
+ }
+
+ end = gsc_getentry(gsc, INFINITY);
+ for (; s != end; s = LIST_NEXT(s, _next)) {
+ s->y += m * d;
+ }
+
+ return (0);
+}
+
+/* subtract a segment from a generalized service curve */
+static int
+gsc_sub_seg(struct gen_sc *gsc, double x, double y, double d, double m)
+{
+ if (gsc_add_seg(gsc, x, y, d, -m) < 0)
+ return (-1);
+ gsc_compress(gsc);
+ return (0);
+}
+
+/*
+ * collapse adjacent segments with the same slope
+ */
+static void
+gsc_compress(struct gen_sc *gsc)
+{
+ struct segment *s, *next;
+
+ again:
+ LIST_FOREACH(s, gsc, _next) {
+
+ if ((next = LIST_NEXT(s, _next)) == NULL) {
+ if (LIST_FIRST(gsc) == s && s->m == 0) {
+ /*
+ * if this is the only entry and its
+ * slope is 0, it's a remaining dummy
+ * entry. we can discard it.
+ */
+ LIST_REMOVE(s, _next);
+ free(s);
+ }
+ break;
+ }
+
+ if (s->x == next->x) {
+ /* discard this entry */
+ LIST_REMOVE(s, _next);
+ free(s);
+ goto again;
+ } else if (s->m == next->m) {
+ /* join the two entries */
+ if (s->d != INFINITY && next->d != INFINITY)
+ s->d += next->d;
+ LIST_REMOVE(next, _next);
+ free(next);
+ goto again;
+ }
+ }
+}
+
+/* get y-projection of a service curve */
+static double
+sc_x2y(struct service_curve *sc, double x)
+{
+ double y;
+
+ if (x <= (double)sc->d)
+ /* y belongs to the 1st segment */
+ y = x * (double)sc->m1;
+ else
+ /* y belongs to the 2nd segment */
+ y = (double)sc->d * (double)sc->m1
+ + (x - (double)sc->d) * (double)sc->m2;
+ return (y);
+}
+
+/*
+ * system call interfaces for qdisc_ops
+ */
+static int
+hfsc_attach(struct ifinfo *ifinfo)
+{
+ struct hfsc_attach attach;
+
+ if (hfsc_fd < 0 &&
+ (hfsc_fd = open(HFSC_DEVICE, O_RDWR)) < 0 &&
+ (hfsc_fd = open_module(HFSC_DEVICE, O_RDWR)) < 0) {
+ LOG(LOG_ERR, errno, "HFSC open\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ hfsc_refcount++;
+ memset(&attach, 0, sizeof(attach));
+ strncpy(attach.iface.hfsc_ifname, ifinfo->ifname, IFNAMSIZ);
+ attach.bandwidth = ifinfo->bandwidth;
+
+ if (ioctl(hfsc_fd, HFSC_IF_ATTACH, &attach) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+hfsc_detach(struct ifinfo *ifinfo)
+{
+ struct hfsc_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.hfsc_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(hfsc_fd, HFSC_IF_DETACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ if (--hfsc_refcount == 0) {
+ close(hfsc_fd);
+ hfsc_fd = -1;
+ }
+ return (0);
+}
+
+static int
+hfsc_clear(struct ifinfo *ifinfo)
+{
+ struct hfsc_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.hfsc_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(hfsc_fd, HFSC_CLEAR_HIERARCHY, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+hfsc_enable(struct ifinfo *ifinfo)
+{
+ struct hfsc_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.hfsc_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(hfsc_fd, HFSC_ENABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+hfsc_disable(struct ifinfo *ifinfo)
+{
+ struct hfsc_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.hfsc_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(hfsc_fd, HFSC_DISABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+hfsc_add_class(struct classinfo *clinfo)
+{
+ struct hfsc_add_class class_add;
+ struct hfsc_classinfo *hfsc_clinfo;
+ struct hfsc_ifinfo *hfsc_ifinfo;
+
+ /* root class is a dummy class */
+ if (clinfo->parent == NULL) {
+ clinfo->handle = HFSC_ROOTCLASS_HANDLE;
+ return (0);
+ }
+
+ hfsc_ifinfo = clinfo->ifinfo->private;
+ hfsc_clinfo = clinfo->private;
+
+ memset(&class_add, 0, sizeof(class_add));
+ strncpy(class_add.iface.hfsc_ifname, clinfo->ifinfo->ifname, IFNAMSIZ);
+ if (clinfo->parent == hfsc_ifinfo->root_class)
+ class_add.parent_handle = HFSC_ROOTCLASS_HANDLE;
+ else
+ class_add.parent_handle = clinfo->parent->handle;
+ class_add.service_curve = hfsc_clinfo->rsc;
+ class_add.qlimit = hfsc_clinfo->qlimit;
+ class_add.flags = hfsc_clinfo->flags;
+ if (ioctl(hfsc_fd, HFSC_ADD_CLASS, &class_add) < 0) {
+ clinfo->handle = HFSC_NULLCLASS_HANDLE;
+ return (QOPERR_SYSCALL);
+ }
+ clinfo->handle = class_add.class_handle;
+ return (0);
+}
+
+static int
+hfsc_modify_class(struct classinfo *clinfo, void *arg)
+{
+ struct hfsc_modify_class class_mod;
+ struct hfsc_classinfo *hfsc_clinfo;
+ long sctype;
+
+ sctype = (long)arg;
+ hfsc_clinfo = clinfo->private;
+
+ memset(&class_mod, 0, sizeof(class_mod));
+ strncpy(class_mod.iface.hfsc_ifname, clinfo->ifinfo->ifname, IFNAMSIZ);
+ class_mod.class_handle = clinfo->handle;
+ if (sctype & HFSC_REALTIMESC)
+ class_mod.service_curve = hfsc_clinfo->rsc;
+ else if (sctype & HFSC_LINKSHARINGSC)
+ class_mod.service_curve = hfsc_clinfo->fsc;
+ else
+ return (QOPERR_INVAL);
+ class_mod.sctype = sctype;
+
+ if (ioctl(hfsc_fd, HFSC_MOD_CLASS, &class_mod) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+hfsc_delete_class(struct classinfo *clinfo)
+{
+ struct hfsc_delete_class class_delete;
+
+ if (clinfo->handle == HFSC_NULLCLASS_HANDLE ||
+ clinfo->handle == HFSC_ROOTCLASS_HANDLE)
+ return (0);
+
+ memset(&class_delete, 0, sizeof(class_delete));
+ strncpy(class_delete.iface.hfsc_ifname, clinfo->ifinfo->ifname,
+ IFNAMSIZ);
+ class_delete.class_handle = clinfo->handle;
+
+ if (ioctl(hfsc_fd, HFSC_DEL_CLASS, &class_delete) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+hfsc_add_filter(struct fltrinfo *fltrinfo)
+{
+ struct hfsc_add_filter fltr_add;
+
+ memset(&fltr_add, 0, sizeof(fltr_add));
+ strncpy(fltr_add.iface.hfsc_ifname, fltrinfo->clinfo->ifinfo->ifname,
+ IFNAMSIZ);
+ fltr_add.class_handle = fltrinfo->clinfo->handle;
+ fltr_add.filter = fltrinfo->fltr;
+
+ if (ioctl(hfsc_fd, HFSC_ADD_FILTER, &fltr_add) < 0)
+ return (QOPERR_SYSCALL);
+ fltrinfo->handle = fltr_add.filter_handle;
+ return (0);
+}
+
+static int
+hfsc_delete_filter(struct fltrinfo *fltrinfo)
+{
+ struct hfsc_delete_filter fltr_del;
+
+ memset(&fltr_del, 0, sizeof(fltr_del));
+ strncpy(fltr_del.iface.hfsc_ifname, fltrinfo->clinfo->ifinfo->ifname,
+ IFNAMSIZ);
+ fltr_del.filter_handle = fltrinfo->handle;
+
+ if (ioctl(hfsc_fd, HFSC_DEL_FILTER, &fltr_del) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+
diff --git a/usr.sbin/altq/libaltq/qop_hfsc.h b/usr.sbin/altq/libaltq/qop_hfsc.h
new file mode 100644
index 00000000000..54162df084b
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_hfsc.h
@@ -0,0 +1,79 @@
+/* $OpenBSD: qop_hfsc.h,v 1.1 2001/06/27 18:23:33 kjc Exp $ */
+/* $KAME: qop_hfsc.h,v 1.2 2000/10/18 09:15:19 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <altq/altq_hfsc.h>
+
+/*
+ * generalized service curve used for admission control
+ */
+struct segment {
+ LIST_ENTRY(segment) _next;
+ double x, y, d, m;
+};
+
+typedef LIST_HEAD(gen_sc, segment) gsc_head_t;
+
+/*
+ * hfsc private ifinfo structure
+ */
+struct hfsc_ifinfo {
+ struct classinfo *root_class; /* root class */
+ struct classinfo *default_class; /* default class */
+};
+
+/*
+ * hfsc private classinfo structure
+ */
+struct hfsc_classinfo {
+ struct service_curve rsc; /* real-time service curve */
+ struct service_curve fsc; /* fair service curve */
+ gsc_head_t gen_rsc; /* generalized real-time sc */
+ gsc_head_t gen_fsc; /* generalized fsc */
+ int qlimit;
+ int flags;
+};
+
+int hfsc_interface_parser(const char *ifname, int argc, char **argv);
+int hfsc_class_parser(const char *ifname, const char *class_name,
+ const char *parent_name, int argc, char **argv);
+
+int qcmd_hfsc_add_if(const char *ifname, u_int bandwidth, int flags);
+int qcmd_hfsc_add_class(const char *ifname, const char *class_name,
+ const char *parent_name, u_int m1, u_int d, u_int m2,
+ int qlimit, int flags);
+int qcmd_hfsc_modify_class(const char *ifname, const char *class_name,
+ u_int m1, u_int d, u_int m2, int sctype);
+
+int qop_hfsc_add_if(struct ifinfo **rp, const char *ifname,
+ u_int bandwidth, int flags);
+int qop_hfsc_add_class(struct classinfo **rp, const char *class_name,
+ struct ifinfo *ifinfo, struct classinfo *parent,
+ struct service_curve *sc, int qlimit, int flags);
+int qop_hfsc_modify_class(struct classinfo *clinfo,
+ struct service_curve *sc, int sctype);
+
diff --git a/usr.sbin/altq/libaltq/qop_priq.c b/usr.sbin/altq/libaltq/qop_priq.c
new file mode 100644
index 00000000000..71bbb947248
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_priq.c
@@ -0,0 +1,524 @@
+/* $OpenBSD: qop_priq.c,v 1.1 2001/06/27 18:23:34 kjc Exp $ */
+/* $KAME: qop_priq.c,v 1.1 2000/10/18 09:15:19 kjc Exp $ */
+/*
+ * Copyright (C) 2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <altq/altq.h>
+#include <altq/altq_priq.h>
+#include "altq_qop.h"
+#include "qop_priq.h"
+
+static int qop_priq_enable_hook(struct ifinfo *ifinfo);
+
+static int priq_attach(struct ifinfo *ifinfo);
+static int priq_detach(struct ifinfo *ifinfo);
+static int priq_clear(struct ifinfo *ifinfo);
+static int priq_enable(struct ifinfo *ifinfo);
+static int priq_disable(struct ifinfo *ifinfo);
+static int priq_add_class(struct classinfo *clinfo);
+static int priq_modify_class(struct classinfo *clinfo, void *arg);
+static int priq_delete_class(struct classinfo *clinfo);
+static int priq_add_filter(struct fltrinfo *fltrinfo);
+static int priq_delete_filter(struct fltrinfo *fltrinfo);
+
+#define PRIQ_DEVICE "/dev/altq/priq"
+
+static int priq_fd = -1;
+static int priq_refcount = 0;
+
+static struct qdisc_ops priq_qdisc = {
+ ALTQT_PRIQ,
+ "priq",
+ priq_attach,
+ priq_detach,
+ priq_clear,
+ priq_enable,
+ priq_disable,
+ priq_add_class,
+ priq_modify_class,
+ priq_delete_class,
+ priq_add_filter,
+ priq_delete_filter,
+};
+
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+/*
+ * parser interface
+ */
+int
+priq_interface_parser(const char *ifname, int argc, char **argv)
+{
+ u_int bandwidth = 100000000; /* 100Mbps */
+ u_int tbrsize = 0;
+ int flags = 0;
+
+ /*
+ * process options
+ */
+ while (argc > 0) {
+ if (EQUAL(*argv, "bandwidth")) {
+ argc--; argv++;
+ if (argc > 0)
+ bandwidth = atobps(*argv);
+ } else if (EQUAL(*argv, "tbrsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ tbrsize = atobytes(*argv);
+ } else if (EQUAL(*argv, "priq")) {
+ /* just skip */
+ } else {
+ LOG(LOG_ERR, 0, "Unknown keyword '%s'\n", argv);
+ return (0);
+ }
+ argc--; argv++;
+ }
+
+ if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0)
+ return (0);
+
+ if (qcmd_priq_add_if(ifname, bandwidth, flags) != 0)
+ return (0);
+ return (1);
+}
+
+int
+priq_class_parser(const char *ifname, const char *class_name,
+ const char *parent_name, int argc, char **argv)
+{
+ int pri = 0, qlimit = 50;
+ int flags = 0, error;
+
+ while (argc > 0) {
+ if (EQUAL(*argv, "priority")) {
+ argc--; argv++;
+ if (argc > 0)
+ pri = strtoul(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "qlimit")) {
+ argc--; argv++;
+ if (argc > 0)
+ qlimit = strtoul(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "default")) {
+ flags |= PRCF_DEFAULTCLASS;
+ } else if (EQUAL(*argv, "red")) {
+ flags |= PRCF_RED;
+ } else if (EQUAL(*argv, "ecn")) {
+ flags |= PRCF_ECN;
+ } else if (EQUAL(*argv, "rio")) {
+ flags |= PRCF_RIO;
+ } else if (EQUAL(*argv, "cleardscp")) {
+ flags |= PRCF_CLEARDSCP;
+ } else {
+ LOG(LOG_ERR, 0,
+ "Unknown keyword '%s' in %s, line %d\n",
+ *argv, altqconfigfile, line_no);
+ return (0);
+ }
+
+ argc--; argv++;
+ }
+
+ if ((flags & PRCF_ECN) && (flags & (PRCF_RED|PRCF_RIO)) == 0)
+ flags |= PRCF_RED;
+
+ error = qcmd_priq_add_class(ifname, class_name, pri, qlimit, flags);
+
+ if (error) {
+ LOG(LOG_ERR, errno, "priq_class_parser: %s\n",
+ qoperror(error));
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * qcmd api
+ */
+int
+qcmd_priq_add_if(const char *ifname, u_int bandwidth, int flags)
+{
+ int error;
+
+ error = qop_priq_add_if(NULL, ifname, bandwidth, flags);
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: can't add priq on interface '%s'\n",
+ qoperror(error), ifname);
+ return (error);
+}
+
+int
+qcmd_priq_add_class(const char *ifname, const char *class_name,
+ int pri, int qlimit, int flags)
+{
+ struct ifinfo *ifinfo;
+ int error = 0;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ error = QOPERR_BADIF;
+
+ if (error == 0)
+ error = qop_priq_add_class(NULL, class_name, ifinfo,
+ pri, qlimit, flags);
+ if (error != 0)
+ LOG(LOG_ERR, errno,
+ "priq: %s: can't add class '%s' on interface '%s'\n",
+ qoperror(error), class_name, ifname);
+ return (error);
+}
+
+int
+qcmd_priq_modify_class(const char *ifname, const char *class_name,
+ int pri, int qlimit, int flags)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ return (QOPERR_BADIF);
+
+ if ((clinfo = clname2clinfo(ifinfo, class_name)) == NULL)
+ return (QOPERR_BADCLASS);
+
+ return qop_priq_modify_class(clinfo, pri, qlimit, flags);
+}
+
+/*
+ * qop api
+ */
+int
+qop_priq_add_if(struct ifinfo **rp, const char *ifname,
+ u_int bandwidth, int flags)
+{
+ struct ifinfo *ifinfo = NULL;
+ struct priq_ifinfo *priq_ifinfo = NULL;
+ int error;
+
+ if ((priq_ifinfo = calloc(1, sizeof(*priq_ifinfo))) == NULL)
+ return (QOPERR_NOMEM);
+
+ error = qop_add_if(&ifinfo, ifname, bandwidth,
+ &priq_qdisc, priq_ifinfo);
+ if (error != 0)
+ goto err_ret;
+
+ /* set enable hook */
+ ifinfo->enable_hook = qop_priq_enable_hook;
+
+ if (rp != NULL)
+ *rp = ifinfo;
+ return (0);
+
+ err_ret:
+ if (priq_ifinfo != NULL) {
+ free(priq_ifinfo);
+ if (ifinfo != NULL)
+ ifinfo->private = NULL;
+ }
+ return (error);
+}
+
+int
+qop_priq_add_class(struct classinfo **rp, const char *class_name,
+ struct ifinfo *ifinfo, int pri, int qlimit, int flags)
+{
+ struct classinfo *clinfo;
+ struct priq_ifinfo *priq_ifinfo;
+ struct priq_classinfo *priq_clinfo = NULL;
+ int error;
+
+ priq_ifinfo = ifinfo->private;
+ if ((flags & PRCF_DEFAULTCLASS) && priq_ifinfo->default_class != NULL)
+ return (QOPERR_CLASS_INVAL);
+
+ if ((priq_clinfo = calloc(1, sizeof(*priq_clinfo))) == NULL) {
+ error = QOPERR_NOMEM;
+ goto err_ret;
+ }
+
+ priq_clinfo->pri = pri;
+ priq_clinfo->qlimit = qlimit;
+ priq_clinfo->flags = flags;
+
+ if ((error = qop_add_class(&clinfo, class_name, ifinfo, NULL,
+ priq_clinfo)) != 0)
+ goto err_ret;
+
+ if (flags & PRCF_DEFAULTCLASS)
+ priq_ifinfo->default_class = clinfo;
+
+ if (rp != NULL)
+ *rp = clinfo;
+ return (0);
+
+ err_ret:
+ if (priq_clinfo != NULL) {
+ free(priq_clinfo);
+ clinfo->private = NULL;
+ }
+
+ return (error);
+}
+
+int
+qop_priq_modify_class(struct classinfo *clinfo,
+ int pri, int qlimit, int flags)
+{
+ struct priq_classinfo *priq_clinfo, *parent_clinfo;
+ int error;
+
+ priq_clinfo = clinfo->private;
+ if (clinfo->parent == NULL)
+ return (QOPERR_CLASS_INVAL);
+ parent_clinfo = clinfo->parent->private;
+
+ priq_clinfo->pri = pri;
+ priq_clinfo->qlimit = qlimit;
+ priq_clinfo->flags = flags;
+
+ error = qop_modify_class(clinfo, NULL);
+ if (error == 0)
+ return (0);
+ return (error);
+}
+
+/*
+ * sanity check at enabling priq:
+ * 1. there must one default class for an interface
+ */
+static int
+qop_priq_enable_hook(struct ifinfo *ifinfo)
+{
+ struct priq_ifinfo *priq_ifinfo;
+
+ priq_ifinfo = ifinfo->private;
+ if (priq_ifinfo->default_class == NULL) {
+ LOG(LOG_ERR, 0, "priq: no default class on interface %s!\n",
+ ifinfo->ifname);
+ return (QOPERR_CLASS);
+ }
+ return (0);
+}
+
+/*
+ * system call interfaces for qdisc_ops
+ */
+static int
+priq_attach(struct ifinfo *ifinfo)
+{
+ struct priq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (priq_fd < 0 &&
+ (priq_fd = open(PRIQ_DEVICE, O_RDWR)) < 0 &&
+ (priq_fd = open_module(PRIQ_DEVICE, O_RDWR)) < 0) {
+ LOG(LOG_ERR, errno, "PRIQ open\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ priq_refcount++;
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ);
+ iface.arg = ifinfo->bandwidth;
+
+ if (ioctl(priq_fd, PRIQ_IF_ATTACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+priq_detach(struct ifinfo *ifinfo)
+{
+ struct priq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(priq_fd, PRIQ_IF_DETACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ if (--priq_refcount == 0) {
+ close(priq_fd);
+ priq_fd = -1;
+ }
+ return (0);
+}
+
+static int
+priq_clear(struct ifinfo *ifinfo)
+{
+ struct priq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(priq_fd, PRIQ_CLEAR, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+priq_enable(struct ifinfo *ifinfo)
+{
+ struct priq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(priq_fd, PRIQ_ENABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+priq_disable(struct ifinfo *ifinfo)
+{
+ struct priq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(priq_fd, PRIQ_DISABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+priq_add_class(struct classinfo *clinfo)
+{
+ struct priq_add_class class_add;
+ struct priq_classinfo *priq_clinfo;
+ struct priq_ifinfo *priq_ifinfo;
+
+ priq_ifinfo = clinfo->ifinfo->private;
+ priq_clinfo = clinfo->private;
+
+ memset(&class_add, 0, sizeof(class_add));
+ strncpy(class_add.iface.ifname, clinfo->ifinfo->ifname, IFNAMSIZ);
+
+ class_add.pri = priq_clinfo->pri;
+ class_add.qlimit = priq_clinfo->qlimit;
+ class_add.flags = priq_clinfo->flags;
+ if (ioctl(priq_fd, PRIQ_ADD_CLASS, &class_add) < 0) {
+ clinfo->handle = PRIQ_NULLCLASS_HANDLE;
+ return (QOPERR_SYSCALL);
+ }
+ clinfo->handle = class_add.class_handle;
+ return (0);
+}
+
+static int
+priq_modify_class(struct classinfo *clinfo, void *arg)
+{
+ struct priq_modify_class class_mod;
+ struct priq_classinfo *priq_clinfo;
+
+ priq_clinfo = clinfo->private;
+
+ memset(&class_mod, 0, sizeof(class_mod));
+ strncpy(class_mod.iface.ifname, clinfo->ifinfo->ifname, IFNAMSIZ);
+ class_mod.class_handle = clinfo->handle;
+
+ class_mod.pri = priq_clinfo->pri;
+ class_mod.qlimit = priq_clinfo->qlimit;
+ class_mod.flags = priq_clinfo->flags;
+
+ if (ioctl(priq_fd, PRIQ_MOD_CLASS, &class_mod) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+priq_delete_class(struct classinfo *clinfo)
+{
+ struct priq_delete_class class_delete;
+
+ if (clinfo->handle == PRIQ_NULLCLASS_HANDLE)
+ return (0);
+
+ memset(&class_delete, 0, sizeof(class_delete));
+ strncpy(class_delete.iface.ifname, clinfo->ifinfo->ifname,
+ IFNAMSIZ);
+ class_delete.class_handle = clinfo->handle;
+
+ if (ioctl(priq_fd, PRIQ_DEL_CLASS, &class_delete) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+priq_add_filter(struct fltrinfo *fltrinfo)
+{
+ struct priq_add_filter fltr_add;
+
+ memset(&fltr_add, 0, sizeof(fltr_add));
+ strncpy(fltr_add.iface.ifname, fltrinfo->clinfo->ifinfo->ifname,
+ IFNAMSIZ);
+ fltr_add.class_handle = fltrinfo->clinfo->handle;
+ fltr_add.filter = fltrinfo->fltr;
+
+ if (ioctl(priq_fd, PRIQ_ADD_FILTER, &fltr_add) < 0)
+ return (QOPERR_SYSCALL);
+ fltrinfo->handle = fltr_add.filter_handle;
+ return (0);
+}
+
+static int
+priq_delete_filter(struct fltrinfo *fltrinfo)
+{
+ struct priq_delete_filter fltr_del;
+
+ memset(&fltr_del, 0, sizeof(fltr_del));
+ strncpy(fltr_del.iface.ifname, fltrinfo->clinfo->ifinfo->ifname,
+ IFNAMSIZ);
+ fltr_del.filter_handle = fltrinfo->handle;
+
+ if (ioctl(priq_fd, PRIQ_DEL_FILTER, &fltr_del) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+
diff --git a/usr.sbin/altq/libaltq/qop_priq.h b/usr.sbin/altq/libaltq/qop_priq.h
new file mode 100644
index 00000000000..142d852cb09
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_priq.h
@@ -0,0 +1,62 @@
+/* $OpenBSD: qop_priq.h,v 1.1 2001/06/27 18:23:34 kjc Exp $ */
+/* $KAME: qop_priq.h,v 1.1 2000/10/18 09:15:19 kjc Exp $ */
+/*
+ * Copyright (C) 2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <altq/altq_priq.h>
+
+/*
+ * priq private ifinfo structure
+ */
+struct priq_ifinfo {
+ struct classinfo *default_class; /* default class */
+};
+
+/*
+ * priq private classinfo structure
+ */
+struct priq_classinfo {
+ int pri;
+ int qlimit;
+ int flags;
+};
+
+int priq_interface_parser(const char *ifname, int argc, char **argv);
+int priq_class_parser(const char *ifname, const char *class_name,
+ const char *parent_name, int argc, char **argv);
+
+int qcmd_priq_add_if(const char *ifname, u_int bandwidth, int flags);
+int qcmd_priq_add_class(const char *ifname, const char *class_name,
+ int pri, int qlimit, int flags);
+int qcmd_priq_modify_class(const char *ifname, const char *class_name,
+ int pri, int qlimit, int flags);
+int qop_priq_add_if(struct ifinfo **rp, const char *ifname,
+ u_int bandwidth, int flags);
+int qop_priq_add_class(struct classinfo **rp, const char *class_name,
+ struct ifinfo *ifinfo,
+ int pri, int qlimit, int flags);
+int qop_priq_modify_class(struct classinfo *clinfo,
+ int pri, int qlimit, int flags);
diff --git a/usr.sbin/altq/libaltq/qop_red.c b/usr.sbin/altq/libaltq/qop_red.c
new file mode 100644
index 00000000000..516d55a2e43
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_red.c
@@ -0,0 +1,308 @@
+/* $OpenBSD: qop_red.c,v 1.1 2001/06/27 18:23:35 kjc Exp $ */
+/* $KAME: qop_red.c,v 1.3 2000/10/18 09:15:19 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <altq/altq.h>
+#include <altq/altq_red.h>
+#include "altq_qop.h"
+#include "qop_red.h"
+
+static int red_attach(struct ifinfo *ifinfo);
+static int red_detach(struct ifinfo *ifinfo);
+static int red_enable(struct ifinfo *ifinfo);
+static int red_disable(struct ifinfo *ifinfo);
+
+#define RED_DEVICE "/dev/altq/red"
+
+static int red_fd = -1;
+static int red_refcount = 0;
+
+static struct qdisc_ops red_qdisc = {
+ ALTQT_RED,
+ "red",
+ red_attach,
+ red_detach,
+ NULL, /* clear */
+ red_enable,
+ red_disable,
+ NULL, /* add class */
+ NULL, /* modify class */
+ NULL, /* delete class */
+ NULL, /* add filter */
+ NULL /* delete filter */
+};
+
+/*
+ * parser interface
+ */
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+int
+red_interface_parser(const char *ifname, int argc, char **argv)
+{
+ u_int bandwidth = 100000000; /* 100Mbps */
+ u_int tbrsize = 0;
+ int weight = 0; /* 0: use default */
+ int inv_pmax = 0; /* 0: use default */
+ int th_min = 0; /* 0: use default */
+ int th_max = 0; /* 0: use default */
+ int qlimit = 60;
+ int pkttime = 0;
+ int flags = 0;
+ int packet_size = 1000;
+
+ /*
+ * process options
+ */
+ while (argc > 0) {
+ if (EQUAL(*argv, "bandwidth")) {
+ argc--; argv++;
+ if (argc > 0)
+ bandwidth = atobps(*argv);
+ } else if (EQUAL(*argv, "tbrsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ tbrsize = atobytes(*argv);
+ } else if (EQUAL(*argv, "packetsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ packet_size = atobytes(*argv);
+ } else if (EQUAL(*argv, "weight")) {
+ argc--; argv++;
+ if (argc > 0)
+ weight = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "qlimit")) {
+ argc--; argv++;
+ if (argc > 0)
+ qlimit = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "thmin")) {
+ argc--; argv++;
+ if (argc > 0)
+ th_min = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "thmax")) {
+ argc--; argv++;
+ if (argc > 0)
+ th_max = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "invpmax")) {
+ argc--; argv++;
+ if (argc > 0)
+ inv_pmax = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "red")) {
+ /* just skip */
+ } else if (EQUAL(*argv, "ecn")) {
+ flags |= REDF_ECN;
+ } else if (EQUAL(*argv, "flowvalve")) {
+ flags |= REDF_FLOWVALVE;
+ } else {
+ LOG(LOG_ERR, 0, "Unknown keyword '%s'\n", argv);
+ return (0);
+ }
+ argc--; argv++;
+ }
+
+ if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0)
+ return (0);
+
+ pkttime = packet_size * 8 * 1000 / (bandwidth / 1000);
+ if (weight != 0) {
+ /* check if weight is power of 2 */
+ int i, w;
+
+ w = weight;
+ for (i = 0; w > 1; i++)
+ w = w >> 1;
+ w = 1 << i;
+ if (weight != w) {
+ LOG(LOG_ERR, 0, "weight %d: should be power of 2",
+ weight);
+ return (0);
+ }
+ }
+
+ if (qcmd_red_add_if(ifname, bandwidth, weight, inv_pmax,
+ th_min, th_max, qlimit, pkttime, flags) != 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * qcmd api
+ */
+int
+qcmd_red_add_if(const char *ifname, u_int bandwidth, int weight,
+ int inv_pmax, int th_min, int th_max, int qlimit,
+ int pkttime, int flags)
+{
+ int error;
+
+ error = qop_red_add_if(NULL, ifname, bandwidth, weight, inv_pmax,
+ th_min, th_max, qlimit, pkttime, flags);
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: can't add red on interface '%s'\n",
+ qoperror(error), ifname);
+ return (error);
+}
+
+/*
+ * qop api
+ */
+int
+qop_red_add_if(struct ifinfo **rp, const char *ifname,
+ u_int bandwidth, int weight, int inv_pmax, int th_min,
+ int th_max, int qlimit, int pkttime, int flags)
+{
+ struct ifinfo *ifinfo = NULL;
+ struct red_ifinfo *red_ifinfo;
+ int error;
+
+ if ((red_ifinfo = calloc(1, sizeof(*red_ifinfo))) == NULL)
+ return (QOPERR_NOMEM);
+ red_ifinfo->weight = weight;
+ red_ifinfo->inv_pmax = inv_pmax;
+ red_ifinfo->th_min = th_min;
+ red_ifinfo->th_max = th_max;
+ red_ifinfo->qlimit = qlimit;
+ red_ifinfo->pkttime = pkttime;
+ red_ifinfo->flags = flags;
+
+ error = qop_add_if(&ifinfo, ifname, bandwidth,
+ &red_qdisc, red_ifinfo);
+ if (error != 0) {
+ free(red_ifinfo);
+ return (error);
+ }
+
+ if (rp != NULL)
+ *rp = ifinfo;
+ return (0);
+}
+
+/*
+ * system call interfaces for qdisc_ops
+ */
+static int
+red_attach(struct ifinfo *ifinfo)
+{
+ struct red_interface iface;
+ struct red_ifinfo *red_ifinfo;
+ struct red_conf conf;
+
+ if (red_fd < 0 &&
+ (red_fd = open(RED_DEVICE, O_RDWR)) < 0 &&
+ (red_fd = open_module(RED_DEVICE, O_RDWR)) < 0) {
+ LOG(LOG_ERR, errno, "RED open\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ red_refcount++;
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.red_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(red_fd, RED_IF_ATTACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ /* set red parameters */
+ red_ifinfo = (struct red_ifinfo *)ifinfo->private;
+ memset(&conf, 0, sizeof(conf));
+ strncpy(conf.iface.red_ifname, ifinfo->ifname, IFNAMSIZ);
+ conf.red_weight = red_ifinfo->weight;
+ conf.red_inv_pmax = red_ifinfo->inv_pmax;
+ conf.red_thmin = red_ifinfo->th_min;
+ conf.red_thmax = red_ifinfo->th_max;
+ conf.red_limit = red_ifinfo->qlimit;
+ conf.red_flags = red_ifinfo->flags;
+ if (ioctl(red_fd, RED_CONFIG, &conf) < 0)
+ return (QOPERR_SYSCALL);
+
+#if 1
+ LOG(LOG_INFO, 0, "red attached to %s\n", iface.red_ifname);
+#endif
+ return (0);
+}
+
+static int
+red_detach(struct ifinfo *ifinfo)
+{
+ struct red_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.red_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(red_fd, RED_IF_DETACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ if (--red_refcount == 0) {
+ close(red_fd);
+ red_fd = -1;
+ }
+ return (0);
+}
+
+static int
+red_enable(struct ifinfo *ifinfo)
+{
+ struct red_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.red_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(red_fd, RED_ENABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+red_disable(struct ifinfo *ifinfo)
+{
+ struct red_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.red_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(red_fd, RED_DISABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
diff --git a/usr.sbin/altq/libaltq/qop_red.h b/usr.sbin/altq/libaltq/qop_red.h
new file mode 100644
index 00000000000..7b1b94f0206
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_red.h
@@ -0,0 +1,48 @@
+/* $OpenBSD: qop_red.h,v 1.1 2001/06/27 18:23:35 kjc Exp $ */
+/* $KAME: qop_red.h,v 1.2 2000/10/18 09:15:20 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * red private ifinfo structure
+ */
+struct red_ifinfo {
+ int weight; /* weight for EWMA */
+ int inv_pmax; /* inverse of max drop probability */
+ int th_min; /* red min threshold */
+ int th_max; /* red max threshold */
+ int qlimit; /* max queue length */
+ int pkttime; /* average packet time in usec */
+ int flags; /* see below */
+};
+
+int red_interface_parser(const char *ifname, int argc, char **argv);
+int qcmd_red_add_if(const char *ifname, u_int bandwidth, int weight,
+ int inv_pmax, int th_min, int th_max, int qlimit,
+ int pkttime, int flags);
+int qop_red_add_if(struct ifinfo **rp, const char *ifname,
+ u_int bandwidth, int weight, int inv_pmax, int th_min,
+ int th_max, int qlimit, int pkttime, int flags);
diff --git a/usr.sbin/altq/libaltq/qop_rio.c b/usr.sbin/altq/libaltq/qop_rio.c
new file mode 100644
index 00000000000..792d792a6b5
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_rio.c
@@ -0,0 +1,352 @@
+/* $OpenBSD: qop_rio.c,v 1.1 2001/06/27 18:23:35 kjc Exp $ */
+/* $KAME: qop_rio.c,v 1.3 2000/10/18 09:15:20 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <altq/altq.h>
+#include <altq/altq_red.h>
+#include <altq/altq_rio.h>
+#include "altq_qop.h"
+#include "qop_rio.h"
+
+static int rio_attach(struct ifinfo *ifinfo);
+static int rio_detach(struct ifinfo *ifinfo);
+static int rio_enable(struct ifinfo *ifinfo);
+static int rio_disable(struct ifinfo *ifinfo);
+
+#define RIO_DEVICE "/dev/altq/rio"
+
+static int rio_fd = -1;
+static int rio_refcount = 0;
+
+static struct qdisc_ops rio_qdisc = {
+ ALTQT_RIO,
+ "rio",
+ rio_attach,
+ rio_detach,
+ NULL, /* clear */
+ rio_enable,
+ rio_disable,
+ NULL, /* add class */
+ NULL, /* modify class */
+ NULL, /* delete class */
+ NULL, /* add filter */
+ NULL /* delete filter */
+};
+
+/*
+ * parser interface
+ */
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+int
+rio_interface_parser(const char *ifname, int argc, char **argv)
+{
+ u_int bandwidth = 100000000; /* 100Mbps */
+ u_int tbrsize = 0;
+ int weight = 0; /* 0: use default */
+ int lo_inv_pmax = 0; /* 0: use default */
+ int lo_th_min = 0; /* 0: use default */
+ int lo_th_max = 0; /* 0: use default */
+ int med_inv_pmax = 0; /* 0: use default */
+ int med_th_min = 0; /* 0: use default */
+ int med_th_max = 0; /* 0: use default */
+ int hi_inv_pmax = 0; /* 0: use default */
+ int hi_th_min = 0; /* 0: use default */
+ int hi_th_max = 0; /* 0: use default */
+ int qlimit = 60;
+ int pkttime = 0;
+ int flags = 0;
+ int packet_size = 1000;
+
+ /*
+ * process options
+ */
+ while (argc > 0) {
+ if (EQUAL(*argv, "bandwidth")) {
+ argc--; argv++;
+ if (argc > 0)
+ bandwidth = atobps(*argv);
+ } else if (EQUAL(*argv, "tbrsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ tbrsize = atobytes(*argv);
+ } else if (EQUAL(*argv, "packetsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ packet_size = atobytes(*argv);
+ } else if (EQUAL(*argv, "weight")) {
+ argc--; argv++;
+ if (argc > 0)
+ weight = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "qlimit")) {
+ argc--; argv++;
+ if (argc > 0)
+ qlimit = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "lo_thmin")) {
+ argc--; argv++;
+ if (argc > 0)
+ lo_th_min = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "lo_thmax")) {
+ argc--; argv++;
+ if (argc > 0)
+ lo_th_max = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "lo_invpmax")) {
+ argc--; argv++;
+ if (argc > 0)
+ lo_inv_pmax = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "med_thmin")) {
+ argc--; argv++;
+ if (argc > 0)
+ med_th_min = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "med_thmax")) {
+ argc--; argv++;
+ if (argc > 0)
+ med_th_max = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "med_invpmax")) {
+ argc--; argv++;
+ if (argc > 0)
+ med_inv_pmax = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "hi_thmin")) {
+ argc--; argv++;
+ if (argc > 0)
+ hi_th_min = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "hi_thmax")) {
+ argc--; argv++;
+ if (argc > 0)
+ hi_th_max = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "hi_invpmax")) {
+ argc--; argv++;
+ if (argc > 0)
+ hi_inv_pmax = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "rio")) {
+ /* just skip */
+ } else if (EQUAL(*argv, "ecn")) {
+ flags |= RIOF_ECN;
+ } else {
+ LOG(LOG_ERR, 0, "Unknown keyword '%s'\n", argv);
+ return (0);
+ }
+ argc--; argv++;
+ }
+
+ if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0)
+ return (0);
+
+ pkttime = packet_size * 8 * 1000 / (bandwidth / 1000);
+ if (weight != 0) {
+ /* check if weight is power of 2 */
+ int i, w;
+
+ w = weight;
+ for (i = 0; w > 1; i++)
+ w = w >> 1;
+ w = 1 << i;
+ if (weight != w) {
+ LOG(LOG_ERR, 0, "weight %d: should be power of 2",
+ weight);
+ return (0);
+ }
+ }
+
+ if (qcmd_rio_add_if(ifname, bandwidth, weight,
+ lo_inv_pmax, lo_th_min, lo_th_max,
+ med_inv_pmax, med_th_min, med_th_max,
+ hi_inv_pmax, hi_th_min, hi_th_max,
+ qlimit, pkttime, flags) != 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * qcmd api
+ */
+int
+qcmd_rio_add_if(const char *ifname, u_int bandwidth, int weight,
+ int lo_inv_pmax, int lo_th_min, int lo_th_max,
+ int med_inv_pmax, int med_th_min, int med_th_max,
+ int hi_inv_pmax, int hi_th_min, int hi_th_max,
+ int qlimit, int pkttime, int flags)
+{
+ struct redparams red_params[RIO_NDROPPREC];
+ int error;
+
+ red_params[0].inv_pmax = lo_inv_pmax;
+ red_params[0].th_min = lo_th_min;
+ red_params[0].th_max = lo_th_max;
+ red_params[1].inv_pmax = med_inv_pmax;
+ red_params[1].th_min = med_th_min;
+ red_params[1].th_max = med_th_max;
+ red_params[2].inv_pmax = hi_inv_pmax;
+ red_params[2].th_min = hi_th_min;
+ red_params[2].th_max = hi_th_max;
+
+ error = qop_rio_add_if(NULL, ifname, bandwidth, weight, red_params,
+ qlimit, pkttime, flags);
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: can't add rio on interface '%s'\n",
+ qoperror(error), ifname);
+ return (error);
+}
+
+/*
+ * qop api
+ */
+int
+qop_rio_add_if(struct ifinfo **rp, const char *ifname,
+ u_int bandwidth, int weight, struct redparams *red_params,
+ int qlimit, int pkttime, int flags)
+{
+ struct ifinfo *ifinfo = NULL;
+ struct rio_ifinfo *rio_ifinfo;
+ int i, error;
+
+ if ((rio_ifinfo = calloc(1, sizeof(*rio_ifinfo))) == NULL)
+ return (QOPERR_NOMEM);
+ for (i = 0; i < RIO_NDROPPREC; i++)
+ rio_ifinfo->red_params[i] = red_params[i];
+ rio_ifinfo->weight = weight;
+ rio_ifinfo->qlimit = qlimit;
+ rio_ifinfo->pkttime = pkttime;
+ rio_ifinfo->flags = flags;
+
+ error = qop_add_if(&ifinfo, ifname, bandwidth,
+ &rio_qdisc, rio_ifinfo);
+ if (error != 0) {
+ free(rio_ifinfo);
+ return (error);
+ }
+
+ if (rp != NULL)
+ *rp = ifinfo;
+ return (0);
+}
+
+/*
+ * system call interfaces for qdisc_ops
+ */
+static int
+rio_attach(struct ifinfo *ifinfo)
+{
+ struct rio_interface iface;
+ struct rio_ifinfo *rio_ifinfo;
+ struct rio_conf conf;
+ int i;
+
+ if (rio_fd < 0 &&
+ (rio_fd = open(RIO_DEVICE, O_RDWR)) < 0 &&
+ (rio_fd = open_module(RIO_DEVICE, O_RDWR)) < 0) {
+ LOG(LOG_ERR, errno, "RIO open\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ rio_refcount++;
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.rio_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(rio_fd, RIO_IF_ATTACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ /* set rio parameters */
+ rio_ifinfo = (struct rio_ifinfo *)ifinfo->private;
+ memset(&conf, 0, sizeof(conf));
+ strncpy(conf.iface.rio_ifname, ifinfo->ifname, IFNAMSIZ);
+ for (i = 0; i < RIO_NDROPPREC; i++)
+ conf.q_params[i] = rio_ifinfo->red_params[i];
+ conf.rio_weight = rio_ifinfo->weight;
+ conf.rio_limit = rio_ifinfo->qlimit;
+ conf.rio_flags = rio_ifinfo->flags;
+ if (ioctl(rio_fd, RIO_CONFIG, &conf) < 0)
+ return (QOPERR_SYSCALL);
+
+#if 1
+ LOG(LOG_INFO, 0, "rio attached to %s\n", iface.rio_ifname);
+#endif
+ return (0);
+}
+
+static int
+rio_detach(struct ifinfo *ifinfo)
+{
+ struct rio_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.rio_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(rio_fd, RIO_IF_DETACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ if (--rio_refcount == 0) {
+ close(rio_fd);
+ rio_fd = -1;
+ }
+ return (0);
+}
+
+static int
+rio_enable(struct ifinfo *ifinfo)
+{
+ struct rio_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.rio_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(rio_fd, RIO_ENABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+rio_disable(struct ifinfo *ifinfo)
+{
+ struct rio_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.rio_ifname, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(rio_fd, RIO_DISABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
diff --git a/usr.sbin/altq/libaltq/qop_rio.h b/usr.sbin/altq/libaltq/qop_rio.h
new file mode 100644
index 00000000000..2d7ce71f209
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_rio.h
@@ -0,0 +1,48 @@
+/* $OpenBSD: qop_rio.h,v 1.1 2001/06/27 18:23:36 kjc Exp $ */
+/* $KAME: qop_rio.h,v 1.2 2000/10/18 09:15:20 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * rio private ifinfo structure
+ */
+struct rio_ifinfo {
+ struct redparams red_params[RIO_NDROPPREC];
+ int weight; /* weight for EWMA */
+ int qlimit; /* max queue length */
+ int pkttime; /* average packet time in usec */
+ int flags; /* see below */
+};
+
+int rio_interface_parser(const char *ifname, int argc, char **argv);
+int qcmd_rio_add_if(const char *ifname, u_int bandwidth, int weight,
+ int lo_inv_pmax, int lo_th_min, int lo_th_max,
+ int mid_inv_pmax, int mid_th_min, int mid_th_max,
+ int hi_inv_pmax, int hi_th_min, int hi_th_max,
+ int qlimit, int pkttime, int flags);
+int qop_rio_add_if(struct ifinfo **rp, const char *ifname,
+ u_int bandwidth, int weight, struct redparams *red_params,
+ int qlimit, int pkttime, int flags);
diff --git a/usr.sbin/altq/libaltq/qop_wfq.c b/usr.sbin/altq/libaltq/qop_wfq.c
new file mode 100644
index 00000000000..72dc0bf2b09
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_wfq.c
@@ -0,0 +1,283 @@
+/* $OpenBSD: qop_wfq.c,v 1.1 2001/06/27 18:23:36 kjc Exp $ */
+/* $KAME: qop_wfq.c,v 1.3 2000/10/18 09:15:20 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <altq/altq.h>
+#include <altq/altq_wfq.h>
+#include "altq_qop.h"
+#include "qop_wfq.h"
+
+static int wfq_attach(struct ifinfo *ifinfo);
+static int wfq_detach(struct ifinfo *ifinfo);
+static int wfq_enable(struct ifinfo *ifinfo);
+static int wfq_disable(struct ifinfo *ifinfo);
+
+#define WFQ_DEVICE "/dev/altq/wfq"
+
+static int wfq_fd = -1;
+static int wfq_refcount = 0;
+
+static struct qdisc_ops wfq_qdisc = {
+ ALTQT_WFQ,
+ "wfq",
+ wfq_attach,
+ wfq_detach,
+ NULL, /* clear */
+ wfq_enable,
+ wfq_disable,
+ NULL, /* add class */
+ NULL, /* modify class */
+ NULL, /* delete class */
+ NULL, /* add filter */
+ NULL /* delete filter */
+};
+
+/*
+ * parser interface
+ */
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+int
+wfq_interface_parser(const char *ifname, int argc, char **argv)
+{
+ u_int bandwidth = 100000000; /* 100Mbps */
+ u_int tbrsize = 0;
+ int hash_policy = 0; /* 0: use default */
+ int nqueues = 0; /* 0: use default */
+ int qsize = 0; /* 0: use default */
+
+ /*
+ * process options
+ */
+ while (argc > 0) {
+ if (EQUAL(*argv, "bandwidth")) {
+ argc--; argv++;
+ if (argc > 0)
+ bandwidth = atobps(*argv);
+ } else if (EQUAL(*argv, "tbrsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ tbrsize = atobytes(*argv);
+ } else if (EQUAL(*argv, "nqueues")) {
+ argc--; argv++;
+ if (argc > 0)
+ nqueues = (int)strtol(*argv, NULL, 0);
+ } else if (EQUAL(*argv, "qsize")) {
+ argc--; argv++;
+ if (argc > 0)
+ qsize = atobytes(*argv);
+ } else if (EQUAL(*argv, "hash")) {
+ argc--; argv++;
+ if (argc > 0) {
+ if (EQUAL(*argv, "dstaddr"))
+ hash_policy = WFQ_HASH_DSTADDR;
+ else if (EQUAL(*argv, "full"))
+ hash_policy = WFQ_HASH_FULL;
+ else if (EQUAL(*argv, "srcport"))
+ hash_policy = WFQ_HASH_SRCPORT;
+ else {
+ LOG(LOG_ERR, 0,
+ "Unknown hash policy '%s'\n",
+ argv);
+ return (0);
+ }
+ }
+ } else if (EQUAL(*argv, "wfq")) {
+ /* just skip */
+ } else {
+ LOG(LOG_ERR, 0, "Unknown keyword '%s'\n", argv);
+ return (0);
+ }
+ argc--; argv++;
+ }
+
+ if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0)
+ return (0);
+
+ if (qsize != 0 && qsize < 1500) {
+ LOG(LOG_ERR, 0, "qsize too small: %d bytes", qsize);
+ return (0);
+ }
+
+ if (qcmd_wfq_add_if(ifname, bandwidth,
+ hash_policy, nqueues, qsize) != 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * qcmd api
+ */
+int
+qcmd_wfq_add_if(const char *ifname, u_int bandwidth, int hash_policy,
+ int nqueues, int qsize)
+{
+ int error;
+
+ error = qop_wfq_add_if(NULL, ifname, bandwidth,
+ hash_policy, nqueues, qsize);
+ if (error != 0)
+ LOG(LOG_ERR, errno, "%s: can't add wfq on interface '%s'\n",
+ qoperror(error), ifname);
+ return (error);
+}
+
+/*
+ * qop api
+ */
+int
+qop_wfq_add_if(struct ifinfo **rp, const char *ifname, u_int bandwidth,
+ int hash_policy, int nqueues, int qsize)
+{
+ struct ifinfo *ifinfo = NULL;
+ struct wfq_ifinfo *wfq_ifinfo;
+ int error;
+
+ if ((wfq_ifinfo = calloc(1, sizeof(*wfq_ifinfo))) == NULL)
+ return (QOPERR_NOMEM);
+ wfq_ifinfo->hash_policy = hash_policy;
+ wfq_ifinfo->nqueues = nqueues;
+ wfq_ifinfo->qsize = qsize;
+
+ error = qop_add_if(&ifinfo, ifname, bandwidth,
+ &wfq_qdisc, wfq_ifinfo);
+ if (error != 0) {
+ free(wfq_ifinfo);
+ return (error);
+ }
+
+ if (rp != NULL)
+ *rp = ifinfo;
+ return (0);
+}
+
+/*
+ * system call interfaces for qdisc_ops
+ */
+static int
+wfq_attach(struct ifinfo *ifinfo)
+{
+ struct wfq_interface iface;
+ struct wfq_ifinfo *wfq_ifinfo;
+ struct wfq_conf conf;
+
+ if (wfq_fd < 0 &&
+ (wfq_fd = open(WFQ_DEVICE, O_RDWR)) < 0 &&
+ (wfq_fd = open_module(WFQ_DEVICE, O_RDWR)) < 0) {
+ LOG(LOG_ERR, errno, "WFQ open\n");
+ return (QOPERR_SYSCALL);
+ }
+
+ wfq_refcount++;
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.wfq_ifacename, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(wfq_fd, WFQ_IF_ATTACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ /* set wfq parameters */
+ wfq_ifinfo = (struct wfq_ifinfo *)ifinfo->private;
+ if (wfq_ifinfo->hash_policy != 0 || wfq_ifinfo->nqueues != 0 ||
+ wfq_ifinfo->qsize != 0) {
+ memset(&conf, 0, sizeof(conf));
+ strncpy(conf.iface.wfq_ifacename, ifinfo->ifname, IFNAMSIZ);
+ conf.hash_policy = wfq_ifinfo->hash_policy;
+ conf.nqueues = wfq_ifinfo->nqueues;
+ conf.qlimit = wfq_ifinfo->qsize;
+ if (ioctl(wfq_fd, WFQ_CONFIG, &conf) < 0) {
+ LOG(LOG_ERR, errno, "WFQ_CONFIG\n");
+ return (QOPERR_SYSCALL);
+ }
+ }
+#if 1
+ LOG(LOG_INFO, 0, "wfq attached to %s\n", iface.wfq_ifacename);
+#endif
+ return (0);
+}
+
+static int
+wfq_detach(struct ifinfo *ifinfo)
+{
+ struct wfq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.wfq_ifacename, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(wfq_fd, WFQ_IF_DETACH, &iface) < 0)
+ return (QOPERR_SYSCALL);
+
+ if (--wfq_refcount == 0) {
+ close(wfq_fd);
+ wfq_fd = -1;
+ }
+ return (0);
+}
+
+static int
+wfq_enable(struct ifinfo *ifinfo)
+{
+ struct wfq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.wfq_ifacename, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(wfq_fd, WFQ_ENABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
+
+static int
+wfq_disable(struct ifinfo *ifinfo)
+{
+ struct wfq_interface iface;
+
+ memset(&iface, 0, sizeof(iface));
+ strncpy(iface.wfq_ifacename, ifinfo->ifname, IFNAMSIZ);
+
+ if (ioctl(wfq_fd, WFQ_DISABLE, &iface) < 0)
+ return (QOPERR_SYSCALL);
+ return (0);
+}
diff --git a/usr.sbin/altq/libaltq/qop_wfq.h b/usr.sbin/altq/libaltq/qop_wfq.h
new file mode 100644
index 00000000000..5d922b2fcdb
--- /dev/null
+++ b/usr.sbin/altq/libaltq/qop_wfq.h
@@ -0,0 +1,42 @@
+/* $OpenBSD: qop_wfq.h,v 1.1 2001/06/27 18:23:36 kjc Exp $ */
+/* $KAME: qop_wfq.h,v 1.2 2000/10/18 09:15:21 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * wfq private ifinfo structure
+ */
+struct wfq_ifinfo {
+ int hash_policy; /* hash policy */
+ int nqueues; /* number of queues */
+ int qsize; /* queue size in bytes */
+};
+
+int wfq_interface_parser(const char *ifname, int argc, char **argv);
+int qcmd_wfq_add_if(const char *ifname, u_int bandwidth, int hash_policy,
+ int nqueues, int qsize);
+int qop_wfq_add_if(struct ifinfo **rp, const char *ifname, u_int bandwidth,
+ int hash_policy, int nqueues, int qsize);
diff --git a/usr.sbin/altq/libaltq/quip_server.c b/usr.sbin/altq/libaltq/quip_server.c
new file mode 100644
index 00000000000..2d7b7a56877
--- /dev/null
+++ b/usr.sbin/altq/libaltq/quip_server.c
@@ -0,0 +1,483 @@
+/* $OpenBSD: quip_server.c,v 1.1 2001/06/27 18:23:31 kjc Exp $ */
+/* $KAME: quip_server.c,v 1.2 2000/10/18 09:15:21 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <err.h>
+
+#include <altq/altq.h>
+#include <altq/altq_red.h>
+#include <altq/altq_rio.h>
+
+#include "altq_qop.h"
+#include "quip_server.h"
+
+extern LIST_HEAD(qop_iflist, ifinfo) qop_iflist;
+
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+static int next_word(char **cpp, char *b);
+
+static int query_list(const char *cmd, const char *arg, char *msg);
+static int query_handle2name(const char *cmd, const char *arg, char *msg);
+static int query_qdisc(const char *cmd, const char *arg, char *msg);
+static int query_filterspec(const char *cmd, const char *arg, char *msg);
+
+int
+quip_input(FILE *fp)
+{
+ char request[256], result[256], body[8192], w[256], *cp, *query;
+ int n = 0;
+
+ while (1) {
+ if (fgets(request, 128, fp) == NULL) /* EOF */
+ return (-1);
+ /* skip preceding blank lines */
+ if (request[0] == '\n')
+ continue;
+ break;
+ }
+
+ /* remove trailing newline and white space */
+ if ((cp = strrchr(request, '\n')) != NULL) {
+ *cp-- = '\0';
+ while (*cp == ' ' || *cp == '\t')
+ *cp-- = '\0';
+ }
+
+ body[0] = '\0';
+ cp = request;
+ if (!next_word(&cp, w)) {
+ sprintf(result, "400 Bad request\n");
+ goto done;
+ }
+ if (EQUAL(w, "GET")) {
+ if (!next_word(&cp, w)) {
+ sprintf(result, "400 Bad request\n");
+ goto done;
+ }
+ if ((query = strchr(w, '?')) != NULL) {
+ /* request has a query string */
+ *query = '\0';
+ query++;
+ }
+
+ if (EQUAL(w, "list")) {
+ n = query_list(w, query, body);
+ } else if (EQUAL(w, "handle-to-name")) {
+ n = query_handle2name(w, query, body);
+ } else if (EQUAL(w, "qdisc")) {
+ n = query_qdisc(w, query, body);
+ } else if (EQUAL(w, "filter")) {
+ n = query_filterspec(w, query, body);
+ } else {
+ sprintf(result, "400 Bad request\n");
+ goto done;
+ }
+ } else {
+ sprintf(result, "400 Bad request\n");
+ goto done;
+ }
+
+ if (n == 0) {
+ sprintf(result, "204 No content\n");
+ } else if (n < 0) {
+ sprintf(result, "400 Bad request\n");
+ } else {
+ sprintf(result, "200 OK\nContent-Length:%d\n", n);
+ }
+
+ done:
+ /* send a result line and a blank line */
+ if (fputs ("QUIP/1.0 ", fp) != 0 ||
+ fputs(result, fp) != 0 || fputs("\n", fp) != 0)
+ return (-1);
+
+ /* send message body */
+ if (fputs(body, fp) != 0)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Skip leading blanks, then copy next word (delimited by blank or zero, but
+ * no longer than 63 bytes) into buffer b, set scan pointer to following
+ * non-blank (or end of string), and return 1. If there is no non-blank text,
+ * set scan ptr to point to 0 byte and return 0.
+ */
+static int
+next_word(char **cpp, char *b)
+{
+ char *tp;
+ int L;
+
+ *cpp += strspn(*cpp, " \t");
+ if (**cpp == '\0' || **cpp == '\n' || **cpp == '#')
+ return(0);
+
+ tp = strpbrk(*cpp, " \t\n#");
+ L = MIN((tp)?(tp-*cpp):strlen(*cpp), 63);
+ strncpy(b, *cpp, L);
+ *(b + L) = '\0';
+ *cpp += L;
+ *cpp += strspn(*cpp, " \t");
+ return (1);
+}
+
+
+/*
+ * expand_classname creates a long class name.
+ * <ifname>:/<root_name>/../<parent_name>/<class_name>
+ */
+static int
+expand_classname(struct classinfo *clinfo, char *name)
+{
+ struct classinfo *ci = clinfo;
+ char buf[2][256], *b0, *b1, *tmp;
+
+ b0 = buf[0]; b1 = buf[1];
+ b1[0] = '\0';
+ while (ci != NULL) {
+ strcpy(b0, "/");
+ strcat(b0, ci->clname);
+ strcat(b0, b1);
+
+ ci = ci->parent;
+ tmp = b0; b0 = b1; b1 = tmp;
+ }
+ sprintf(b0, "%s:", clinfo->ifinfo->ifname);
+ strcat(b0, b1);
+ strcpy(name, b0);
+ return (strlen(name));
+}
+
+/*
+ * expand_filtername creates a long filter name.
+ * <ifname>:/<root_name>/../<parent_name>/<class_name>:<fltr_name>
+ */
+static int
+expand_filtername(struct fltrinfo *fltrinfo, char *name)
+{
+ int len;
+
+ len = expand_classname(fltrinfo->clinfo, name);
+ sprintf(name + len, ":%s", fltrinfo->flname);
+ return (len + strlen(name + len));
+}
+
+static int
+query_handle2name(const char *cmd, const char *arg, char *msg)
+{
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+ struct fltrinfo *fltrinfo;
+ char *ifname, *class_field, *fltr_field, buf[256], *cp;
+ u_long handle;
+ int len, size;
+
+ strcpy(buf, arg);
+ cp = buf;
+ ifname = strsep(&cp, ":");
+ class_field = strsep(&cp, ":");
+ fltr_field = cp;
+
+ if (fltr_field != NULL) {
+ if (sscanf(fltr_field, "%lx", &handle) != 1)
+ return (-1);
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ return (-1);
+ if ((fltrinfo = flhandle2fltrinfo(ifinfo, handle)) == NULL)
+ return (-1);
+
+ len = expand_filtername(fltrinfo, msg);
+ } else {
+ if (sscanf(class_field, "%lx", &handle) != 1)
+ return (-1);
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ return (-1);
+ if ((clinfo = clhandle2clinfo(ifinfo, handle)) == NULL)
+ return (-1);
+
+ len = expand_classname(clinfo, msg);
+ }
+ size = len + sprintf(msg + len, "\n");
+ return (size);
+}
+
+static int
+query_qdisc(const char *cmd, const char *arg, char *msg)
+{
+ struct ifinfo *ifinfo;
+ int size;
+
+ if ((ifinfo = ifname2ifinfo(arg)) == NULL)
+ return (-1);
+
+ size = sprintf(msg, "%s\nbandwidth:%.2fMbps\nstatus:%s\n",
+ ifinfo->qdisc->qname, (double)ifinfo->bandwidth/1000000,
+ (ifinfo->enabled ? "enabled" : "disabled"));
+ return (size);
+}
+
+static int
+query_filterspec(const char *cmd, const char *arg, char *msg)
+{
+ struct ifinfo *ifinfo;
+ struct fltrinfo *fltrinfo;
+ struct flow_filter *filt;
+ char *ifname, *class_field, *fltr_field, buf[256], *cp;
+ u_long handle;
+ int size;
+
+ strcpy(buf, arg);
+ cp = buf;
+ ifname = strsep(&cp, ":");
+ class_field = strsep(&cp, ":");
+ fltr_field = cp;
+
+ if (fltr_field == NULL)
+ return (-1);
+ if (sscanf(fltr_field, "%lx", &handle) != 1)
+ return (-1);
+
+ if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
+ return (-1);
+ if ((fltrinfo = flhandle2fltrinfo(ifinfo, handle)) == NULL)
+ return (-1);
+
+ filt = &fltrinfo->fltr;
+
+ if (filt->ff_flow.fi_family == AF_INET) {
+ char src[128], dst[128], smask[128], dmask[128], tos[128];
+
+ if (filt->ff_flow.fi_dst.s_addr == 0) {
+ sprintf(dst, "0");
+ dmask[0] = '\0';
+ } else {
+ sprintf(dst, "%s", inet_ntoa(filt->ff_flow.fi_dst));
+
+ if (filt->ff_mask.mask_dst.s_addr == 0xffffffff)
+ dmask[0] = '\0';
+ else
+ sprintf(dmask, " mask %#x",
+ ntoh32(filt->ff_mask.mask_dst.s_addr));
+ }
+ if (filt->ff_flow.fi_src.s_addr == 0) {
+ sprintf(src, "0");
+ smask[0] = '\0';
+ } else {
+ sprintf(src, "%s", inet_ntoa(filt->ff_flow.fi_src));
+
+ if (filt->ff_mask.mask_src.s_addr == 0xffffffff)
+ smask[0] = '\0';
+ else
+ sprintf(smask, " mask %#x",
+ ntoh32(filt->ff_mask.mask_src.s_addr));
+ }
+ if (filt->ff_flow.fi_tos == 0)
+ tos[0] = '\0';
+ else
+ sprintf(tos, " tos %#x tosmask %#x",
+ filt->ff_flow.fi_tos,
+ filt->ff_mask.mask_tos);
+
+ size = sprintf(msg, "inet %s%s %d %s%s %d %d%s\n",
+ dst, dmask,
+ ntoh16(filt->ff_flow.fi_dport),
+ src, smask,
+ ntoh16(filt->ff_flow.fi_sport),
+ filt->ff_flow.fi_proto, tos);
+ }
+#ifdef INET6
+ else if (filt->ff_flow.fi_family == AF_INET6) {
+ struct flow_filter6 *filt6;
+ char dst6[INET6_ADDRSTRLEN], dmask6[INET6_ADDRSTRLEN];
+ char src6[INET6_ADDRSTRLEN], smask6[INET6_ADDRSTRLEN];
+ char tclass6[128];
+ const struct in6_addr mask128 =
+ {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}};
+
+ filt6 = (struct flow_filter6 *)&fltrinfo->fltr;
+ if (IN6_IS_ADDR_UNSPECIFIED(&filt6->ff_flow6.fi6_dst)) {
+ sprintf(dst6, "0");
+ dmask6[0] = '\0';
+ } else {
+ inet_ntop(AF_INET6, &filt6->ff_flow6.fi6_dst,
+ dst6, sizeof(dst6));
+ if (IN6_ARE_ADDR_EQUAL(&mask128,
+ &filt6->ff_mask6.mask6_dst))
+ dmask6[0] = '\0';
+ else {
+ sprintf(dmask6, " mask ");
+ inet_ntop(AF_INET6, &filt6->ff_mask6.mask6_dst,
+ dmask6 + 6, sizeof(dmask6) -6);
+ }
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&filt6->ff_flow6.fi6_src)) {
+ sprintf(src6, "0");
+ smask6[0] = '\0';
+ } else {
+ inet_ntop(AF_INET6, &filt6->ff_flow6.fi6_src,
+ src6, sizeof(src6));
+ if (IN6_ARE_ADDR_EQUAL(&mask128,
+ &filt6->ff_mask6.mask6_src))
+ smask6[0] = '\0';
+ else {
+ sprintf(smask6, " mask ");
+ inet_ntop(AF_INET6, &filt6->ff_mask6.mask6_src,
+ smask6 + 6, sizeof(smask6) -6);
+ }
+ }
+ if (filt6->ff_flow6.fi6_tclass == 0)
+ tclass6[0] = '\0';
+ else
+ sprintf(tclass6, " tclass %#x tclassmask %#x",
+ filt6->ff_flow6.fi6_tclass,
+ filt6->ff_mask6.mask6_tclass);
+
+ size = sprintf(msg, "inet6 %s%s %d %s%s %d %d%s\n",
+ dst6, dmask6,
+ ntoh16(filt6->ff_flow6.fi6_dport),
+ src6, smask6,
+ ntoh16(filt6->ff_flow6.fi6_sport),
+ filt6->ff_flow6.fi6_proto, tclass6);
+ }
+#endif /* INET6 */
+
+ return (size);
+}
+
+
+/*
+ * string_match compares 2 strings and returns 1 when s1 matches s2.
+ * s1: possibly includes wildcards, "*".
+ * s2: must be a full string (should not include "*").
+ */
+static int
+string_match(const char *s1, const char *s2)
+{
+ char *ap, *next, sub[256];
+ int prefixlen, sublen;
+
+ /* if there's no wild card, compare full string */
+ if ((ap = strchr(s1, '*')) == NULL)
+ return (strcmp(s1, s2) == 0);
+
+ /* compare string prefix */
+ prefixlen = ap - s1;
+ if (strncmp(s1, s2, prefixlen) != 0)
+ return (0);
+ s2 += prefixlen;
+
+ /*
+ * if there is another wildcard in the rest of the string,
+ * compare the substring between the 2 wildcards.
+ */
+ while ((next = strchr(ap + 1, '*')) != NULL) {
+ sublen = next - ap - 1;
+ strncpy(sub, ap+1, sublen);
+ sub[sublen] = '\0';
+ if ((s2 = strstr(s2, sub)) == NULL)
+ return (0);
+
+ s2 += sublen;
+ ap = next;
+ }
+
+ /* no more wildcard, compare the rest of the string */
+ return (strcmp(ap+1, s2+strlen(s2)-strlen(ap+1)) == 0);
+}
+
+static int
+query_list(const char *cmd, const char *arg, char *msg)
+{
+ char tmp[256], *cp;
+ struct ifinfo *ifinfo;
+ struct classinfo *clinfo;
+ struct fltrinfo *fltrinfo;
+ int print_if, print_class, print_fltr, size = 0;
+
+ if (arg == NULL) {
+ /* no arg, print all */
+ print_if = print_class = print_fltr = 1;
+ } else {
+ print_if = print_class = print_fltr = 0;
+ if ((cp = strchr(arg, ':')) == NULL)
+ print_if = 1;
+ else if (strchr(cp+1, ':') == NULL)
+ print_class = 1;
+ else
+ print_fltr = 1;
+ }
+
+ cp = msg;
+ LIST_FOREACH(ifinfo, &qop_iflist, next) {
+ if (print_if) {
+ strcpy(tmp, ifinfo->ifname);
+ if (arg == NULL || string_match(arg, tmp))
+ cp += sprintf(cp, "%#010x\t%s\n",
+ ifinfo->ifindex, tmp);
+ }
+ if (!print_class && !print_fltr)
+ continue;
+ for (clinfo = get_rootclass(ifinfo);
+ clinfo != NULL; clinfo = get_nextclass(clinfo)) {
+ if (print_class) {
+ expand_classname(clinfo, tmp);
+ if (arg == NULL || string_match(arg, tmp))
+ cp += sprintf(cp, "%#010lx\t%s\n",
+ clinfo->handle, tmp);
+ }
+ if (!print_fltr)
+ continue;
+ LIST_FOREACH(fltrinfo, &clinfo->fltrlist, next) {
+ expand_filtername(fltrinfo, tmp);
+ if (arg == NULL || string_match(arg, tmp))
+ cp += sprintf(cp, "%#010lx\t%s\n",
+ fltrinfo->handle, tmp);
+ }
+ }
+ }
+ size = cp - msg;
+ return (size);
+}
+
diff --git a/usr.sbin/altq/libaltq/quip_server.h b/usr.sbin/altq/libaltq/quip_server.h
new file mode 100644
index 00000000000..5c937fea36d
--- /dev/null
+++ b/usr.sbin/altq/libaltq/quip_server.h
@@ -0,0 +1,37 @@
+/* $OpenBSD: quip_server.h,v 1.1 2001/06/27 18:23:36 kjc Exp $ */
+/* $KAME: quip_server.h,v 1.2 2000/10/18 09:15:21 kjc Exp $ */
+/*
+ * Copyright (C) 1999-2000
+ * Sony Computer Science Laboratories, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _QUIP_SERVER_H_
+#define _QUIP_SERVER_H_
+
+/* unix domain socket for quip */
+#define QUIP_PATH "/var/run/altq_quip"
+
+int quip_input(FILE *fp);
+
+#endif /* _QUIP_SERVER_H_ */
diff --git a/usr.sbin/altq/tbrconfig/Makefile b/usr.sbin/altq/tbrconfig/Makefile
new file mode 100644
index 00000000000..e918f6247a1
--- /dev/null
+++ b/usr.sbin/altq/tbrconfig/Makefile
@@ -0,0 +1,11 @@
+# $OpenBSD: Makefile,v 1.1 2001/06/27 18:23:36 kjc Exp $
+# $NetBSD: Makefile,v 1.2 2001/04/05 21:48:05 thorpej Exp $
+
+BINDIR=/sbin
+
+PROG= tbrconfig
+MAN= tbrconfig.8
+
+CPPFLAGS+= -DALTQ -I${.CURDIR}/../libaltq
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/altq/tbrconfig/tbrconfig.8 b/usr.sbin/altq/tbrconfig/tbrconfig.8
new file mode 100644
index 00000000000..1b05f6b242e
--- /dev/null
+++ b/usr.sbin/altq/tbrconfig/tbrconfig.8
@@ -0,0 +1,170 @@
+.\" $OpenBSD: tbrconfig.8,v 1.1 2001/06/27 18:23:36 kjc Exp $
+.\" $KAME: tbrconfig.8,v 1.2 2001/04/09 16:26:30 thorpej Exp $
+.\"
+.\" Copyright (C) 2000
+.\" Sony Computer Science Laboratories Inc. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd July 25, 2000
+.Dt TBRCONFIG 8
+.Os KAME
+.\"
+.Sh NAME
+.Nm tbrconfig
+.Nd configure a token bucket regulator for an output queue
+.\"
+.Sh SYNOPSIS
+.Nm
+.Ar interface
+.Oo
+.Ar tokenrate
+.Op Ar bucketsize
+.Oc
+.Nm tbrconfig
+.Fl d
+.Ar interface
+.Nm tbrconfig
+.Fl a
+.Sh DESCRIPTION
+.Nm
+configures a token bucket regulator for the output network
+inteface queue.
+A token bucket regulator limits both the average amount and
+instantaneous amount of packets that the underlying driver can dequeue
+from the network interface within the kernel.
+.Pp
+Conceptually, tokens accumulate in a bucket at the average
+.Ar tokenrate ,
+up to the
+.Ar bucketsize .
+The driver can dequeue packets as long as there are positive amount
+of tokens, and the length of the dequeued packet is subtracted from
+the remaining tokens. Tokens can be negative as a deficit, and
+packets are not dequeued from the interface queue until the tokens
+become positive again.
+The
+.Ar tokenrate
+limits the average rate, and the
+.Ar bucketsize
+limits the maximum burst size.
+.Pp
+Limiting the burst size is essential to packet scheduling, since the
+scheduler schedules packets backlogged at the network interface.
+Limiting the burst size is also needed for drivers which dequeues more
+packets than they can send and end up with discarding excess packets.
+.Pp
+When the
+.Ar tokenrate
+is set to higher than the actual transmission rate, the transmission
+complete interrupt will trigger the next dequeue.
+On the other hand, when the
+.Ar tokenrate
+is set to lower than the actual transmission rate, the transmission
+complete interrupt would occur before the tokens become positive.
+In this case, the next dequeue will be triggered by a timer event.
+Because the kernel timer has a limited granularity, a larger
+.Ar bucketsize
+is required for a higher
+.Ar tokenrate .
+.Pp
+The
+.Ar interface
+parameter is a string of the form
+.Dq name unit ,
+for example,
+.Dq en0 .
+.Pp
+The
+.Ar tokenrate
+parameter specifies the average rate in bits per second, and
+.Dq K
+or
+.Dq M
+can be appended to
+.Ar tokenrate
+as a short hand of
+.Dq Kilo-bps
+or
+.Dq Mega-bps ,
+respectively.
+When
+.Ar tokenrate
+is omitted,
+.Nm
+displays the current parameter values.
+.Pp
+The
+.Ar bucketsize
+parameter specifies the bucket size in bytes, and
+.Dq K
+can be appended to
+.Ar bucketsize
+as a short hand of
+.Dq Kilo-bytes .
+When
+.Ar bucketsize
+is omitted,
+.Nm
+assumes the regulator is driven by transmission complete interrupts
+and, using heuristics, assigns a small bucket size according to the
+.Ar tokenrate .
+When the keyword
+.Dq auto
+is given as
+.Ar bucketsize ,
+.Nm
+assumes the regulator is driven by the kernel timer, and
+computes the bucket size from
+.Ar tokenrate
+and the kernel clock frequency.
+.Pp
+If the
+.Fl d
+flag is passed before an interface name,
+.Nm
+will remove the token bucket regulator for the specified interface.
+.Pp
+Optionally, the
+.Fl a
+flag may be used instead of an interface name. This flag instructs
+.Nm
+to display information about all interfaces in the system.
+.Sh EXAMPLES
+To configure a token bucket regulator for the interface en0 with
+10Mbps token rate and 8KB bucket size,
+.Bd -literal -offset
+# tbrconfig en0 10M 8K
+.Ed
+.Pp
+To rate-limit the interface en0 up to 3Mbps,
+.Bd -literal -offset
+# tbrconfig en0 3M auto
+.Ed
+.Sh SEE ALSO
+.Xr altq.conf 5 ,
+.Xr altqd 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE/KAME IPv6 protocol stack kit as part of
+ALTQ tools.
diff --git a/usr.sbin/altq/tbrconfig/tbrconfig.c b/usr.sbin/altq/tbrconfig/tbrconfig.c
new file mode 100644
index 00000000000..0baf7dd60d7
--- /dev/null
+++ b/usr.sbin/altq/tbrconfig/tbrconfig.c
@@ -0,0 +1,322 @@
+/* $OpenBSD: tbrconfig.c,v 1.1 2001/06/27 18:23:36 kjc Exp $ */
+/* $KAME: tbrconfig.c,v 1.3 2001/05/08 04:36:39 itojun Exp $ */
+/*
+ * Copyright (C) 2000
+ * Sony Computer Science Laboratories Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/sysctl.h>
+#include <net/if.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <err.h>
+
+#include <altq/altq.h>
+
+#define ALTQ_DEVICE "/dev/altq/altq"
+
+static void usage(void);
+static u_long atobps(const char *s);
+static u_long atobytes(const char *s);
+static u_int size_bucket(const char *ifname, const u_int rate);
+static u_int autosize_bucket(const char *ifname, const u_int rate);
+static int get_clockfreq(void);
+static int get_ifmtu(const char *ifname);
+static void list_all(void);
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: tbrconfig interface [tokenrate [bucketsize]\n");
+ fprintf(stderr, " tbrconfig -d interface\n");
+ fprintf(stderr, " tbrconfig -a\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct tbrreq req;
+ u_int rate, depth;
+ int fd, ch, delete;
+
+ delete = 0;
+ rate = 0;
+ depth = 0;
+
+ while ((ch = getopt(argc, argv, "ad")) != -1) {
+ switch (ch) {
+ case 'a':
+ list_all();
+ return (0);
+ case 'd':
+ delete = 1;
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (argc < 1)
+ usage();
+
+ req.ifname[IFNAMSIZ-1] = '\0';
+ strncpy(req.ifname, argv[0], IFNAMSIZ-1);
+ if (argc > 1)
+ rate = (u_int)atobps(argv[1]);
+ if (argc > 2) {
+ if (strncmp(argv[2], "auto", strlen("auto")) == 0)
+ depth = autosize_bucket(req.ifname, rate);
+ else
+ depth = (u_int)atobytes(argv[2]);
+ }
+ if (argc > 3)
+ usage();
+
+ if (delete || rate > 0) {
+ /* set token bucket regulator */
+ if (delete)
+ rate = 0;
+ else if (depth == 0)
+ depth = size_bucket(req.ifname, rate);
+
+ req.tb_prof.rate = rate;
+ req.tb_prof.depth = depth;
+
+ if ((fd = open(ALTQ_DEVICE, O_RDWR)) < 0)
+ err(1, "can't open altq device");
+
+ if (ioctl(fd, ALTQTBRSET, &req) < 0)
+ err(1, "ALTQTBRSET for interface %s", req.ifname);
+
+ close(fd);
+
+ if (delete) {
+ printf("deleted token bucket regulator on %s\n",
+ req.ifname);
+ return (0);
+ }
+ }
+
+ /* get token bucket regulator */
+ if ((fd = open(ALTQ_DEVICE, O_RDONLY)) < 0)
+ err(1, "can't open altq device");
+ if (ioctl(fd, ALTQTBRGET, &req) < 0)
+ err(1, "ALTQTBRGET for interface %s", req.ifname);
+ if (req.tb_prof.rate == 0)
+ printf("no token bucket regulater found on %s\n", req.ifname);
+ else {
+ char rate_str[64], size_str[64];
+
+ if (req.tb_prof.rate < 999999)
+ sprintf(rate_str, "%.2fK",
+ (double)req.tb_prof.rate/1000.0);
+ else
+ sprintf(rate_str, "%.2fM",
+ (double)req.tb_prof.rate/1000000.0);
+ if (req.tb_prof.depth < 10240)
+ sprintf(size_str, "%u", req.tb_prof.depth);
+ else
+ sprintf(size_str, "%.2fK",
+ (double)req.tb_prof.depth/1024.0);
+ printf("%s: tokenrate %s(bps) bucketsize %s(bytes)\n",
+ req.ifname, rate_str, size_str);
+ }
+ close(fd);
+ return (0);
+}
+
+static void
+list_all(void)
+{
+ struct if_nameindex *ifn_list, *ifnp;
+ struct tbrreq req;
+ char rate_str[64], size_str[64];
+ int fd, ntbr;
+
+ if ((ifn_list = if_nameindex()) == NULL)
+ err(1, "if_nameindex failed");
+
+ if ((fd = open(ALTQ_DEVICE, O_RDONLY)) < 0)
+ err(1, "can't open altq device");
+
+ ntbr = 0;
+ for (ifnp = ifn_list; ifnp->if_name != NULL; ifnp++) {
+ req.ifname[IFNAMSIZ-1] = '\0';
+ strncpy(req.ifname, ifnp->if_name, IFNAMSIZ-1);
+ if (ioctl(fd, ALTQTBRGET, &req) < 0)
+ err(1, "ALTQTBRGET");
+ if (req.tb_prof.rate == 0)
+ continue;
+
+ if (req.tb_prof.rate < 999999)
+ sprintf(rate_str, "%.2fK",
+ (double)req.tb_prof.rate/1000.0);
+ else
+ sprintf(rate_str, "%.2fM",
+ (double)req.tb_prof.rate/1000000.0);
+ if (req.tb_prof.depth < 10240)
+ sprintf(size_str, "%u", req.tb_prof.depth);
+ else
+ sprintf(size_str, "%.2fK",
+ (double)req.tb_prof.depth/1024.0);
+ printf("%s: tokenrate %s(bps) bucketsize %s(bytes)\n",
+ req.ifname, rate_str, size_str);
+ ntbr++;
+ }
+ if (ntbr == 0)
+ printf("no active token bucket regulator\n");
+
+ close(fd);
+ if_freenameindex(ifn_list);
+}
+
+static u_long
+atobps(const char *s)
+{
+ u_long bandwidth;
+ char *cp;
+
+ bandwidth = strtoul(s, &cp, 0);
+ if (cp != NULL) {
+ if (*cp == 'K' || *cp == 'k')
+ bandwidth *= 1000;
+ else if (*cp == 'M' || *cp == 'm')
+ bandwidth *= 1000000;
+ else if (*cp == 'G' || *cp == 'g')
+ bandwidth *= 1000000000;
+ }
+ return (bandwidth);
+}
+
+static u_long
+atobytes(const char *s)
+{
+ u_long bytes;
+ char *cp;
+
+ bytes = strtoul(s, &cp, 0);
+ if (cp != NULL) {
+ if (*cp == 'K' || *cp == 'k')
+ bytes *= 1024;
+ else if (*cp == 'M' || *cp == 'm')
+ bytes *= 1024 * 1024;
+ else if (*cp == 'G' || *cp == 'g')
+ bytes *= 1024 * 1024 * 1024;
+ }
+ return (bytes);
+}
+
+/*
+ * use heuristics to determin the bucket size
+ */
+static u_int
+size_bucket(const char *ifname, const u_int rate)
+{
+ u_int size, mtu;
+
+ mtu = get_ifmtu(ifname);
+ if (mtu > 1500)
+ mtu = 1500; /* assume that the path mtu is still 1500 */
+
+ if (rate <= 1*1000*1000)
+ size = 1;
+ else if (rate <= 10*1000*1000)
+ size = 4;
+ else if (rate <= 200*1000*1000)
+ size = 8;
+ else
+ size = 24;
+
+ size = size * mtu;
+ return (size);
+}
+
+/*
+ * compute the bucket size to be required to fill the rate
+ * even when the rate is controlled only by the kernel timer.
+ */
+static u_int
+autosize_bucket(const char *ifname, const u_int rate)
+{
+ u_int size, freq, mtu;
+
+ mtu = get_ifmtu(ifname);
+ freq = get_clockfreq();
+ size = rate / 8 / freq;
+ if (size < mtu)
+ size = mtu;
+ return (size);
+}
+
+static int
+get_clockfreq(void)
+{
+ struct clockinfo clkinfo;
+ int mib[2];
+ size_t len;
+
+ clkinfo.hz = 100; /* default Hz */
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_CLOCKRATE;
+ len = sizeof(struct clockinfo);
+ if (sysctl(mib, 2, &clkinfo, &len, NULL, 0) == -1)
+ warnx("can't get clockrate via sysctl! use %dHz", clkinfo.hz);
+ return (clkinfo.hz);
+}
+
+static int
+get_ifmtu(const char *ifname)
+{
+ int s, mtu;
+ struct ifreq ifr;
+#ifdef __OpenBSD__
+ struct if_data ifdata;
+#endif
+
+ mtu = 512; /* default MTU */
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ return (mtu);
+ strncpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name);
+#ifdef __OpenBSD__
+ ifr.ifr_data = (caddr_t)&ifdata;
+ if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == 0)
+ mtu = ifdata.ifi_mtu;
+#else
+ if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == 0)
+ mtu = ifr.ifr_mtu;
+#endif
+ close(s);
+ return (mtu);
+}