diff options
author | Kenjiro Cho <kjc@cvs.openbsd.org> | 2001-06-27 18:23:37 +0000 |
---|---|---|
committer | Kenjiro Cho <kjc@cvs.openbsd.org> | 2001-06-27 18:23:37 +0000 |
commit | 77495dfc56dcd9fa3f83095f0c6e02d31e3e62ab (patch) | |
tree | a54e11370c1ed886374557f82f27156196a46ba8 /usr.sbin/altq | |
parent | 54fed91c5c2a712e95e1cd420ef821213ea00f3f (diff) |
import ALTQ userland tools from KAME.
Diffstat (limited to 'usr.sbin/altq')
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(¶ms[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, ¶ms) < 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); +} |