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/tbrconfig | |
parent | 54fed91c5c2a712e95e1cd420ef821213ea00f3f (diff) |
import ALTQ userland tools from KAME.
Diffstat (limited to 'usr.sbin/altq/tbrconfig')
-rw-r--r-- | usr.sbin/altq/tbrconfig/Makefile | 11 | ||||
-rw-r--r-- | usr.sbin/altq/tbrconfig/tbrconfig.8 | 170 | ||||
-rw-r--r-- | usr.sbin/altq/tbrconfig/tbrconfig.c | 322 |
3 files changed, 503 insertions, 0 deletions
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); +} |