dummynet.c revision 301231
118334Speter/*
290075Sobrien * Copyright (c) 2002-2003,2010 Luigi Rizzo
3169689Skan *
418334Speter * Redistribution and use in source forms, with and without modification,
590075Sobrien * are permitted provided that this entire comment appears intact.
618334Speter *
790075Sobrien * Redistribution in binary form may occur without any restrictions.
890075Sobrien * Obviously, it would be nice if you gave credit where credit is due
990075Sobrien * but requiring it would be too onerous.
1090075Sobrien *
1118334Speter * This software is provided ``AS IS'' without any warranties of any kind.
1290075Sobrien *
1390075Sobrien * $FreeBSD: stable/10/sbin/ipfw/dummynet.c 301231 2016-06-03 00:48:50Z truckman $
1490075Sobrien *
1590075Sobrien * dummynet support
1618334Speter */
1718334Speter
1890075Sobrien#include <sys/types.h>
19169689Skan#include <sys/socket.h>
20169689Skan/* XXX there are several sysctl leftover here */
2118334Speter#include <sys/sysctl.h>
2218334Speter
2318334Speter#include "ipfw2.h"
2418334Speter
2518334Speter#include <ctype.h>
2618334Speter#include <err.h>
2718334Speter#include <errno.h>
2818334Speter#include <libutil.h>
2918334Speter#include <netdb.h>
3018334Speter#include <stdio.h>
3118334Speter#include <stdlib.h>
3218334Speter#include <string.h>
3318334Speter#include <sysexits.h>
3418334Speter
3518334Speter#include <net/if.h>
3618334Speter#include <netinet/in.h>
3718334Speter#include <netinet/ip_fw.h>
3818334Speter#include <netinet/ip_dummynet.h>
3918334Speter#include <arpa/inet.h>	/* inet_ntoa */
4018334Speter
4118334Speter
4218334Speterstatic struct _s_x dummynet_params[] = {
4318334Speter	{ "plr",		TOK_PLR },
4418334Speter	{ "noerror",		TOK_NOERROR },
45117395Skan	{ "buckets",		TOK_BUCKETS },
46132718Skan	{ "dst-ip",		TOK_DSTIP },
47132718Skan	{ "src-ip",		TOK_SRCIP },
48117395Skan	{ "dst-port",		TOK_DSTPORT },
49117395Skan	{ "src-port",		TOK_SRCPORT },
50117395Skan	{ "proto",		TOK_PROTO },
51169689Skan	{ "weight",		TOK_WEIGHT },
5218334Speter	{ "lmax",		TOK_LMAX },
53117395Skan	{ "maxlen",		TOK_LMAX },
54117395Skan	{ "all",		TOK_ALL },
55132718Skan	{ "mask",		TOK_MASK }, /* alias for both */
56132718Skan	{ "sched_mask",		TOK_SCHED_MASK },
57132718Skan	{ "flow_mask",		TOK_FLOW_MASK },
58132718Skan	{ "droptail",		TOK_DROPTAIL },
59169689Skan	{ "ecn",		TOK_ECN },
60169689Skan	{ "red",		TOK_RED },
61169689Skan	{ "gred",		TOK_GRED },
62169689Skan	{ "bw",			TOK_BW },
63169689Skan	{ "bandwidth",		TOK_BW },
64169689Skan	{ "delay",		TOK_DELAY },
65169689Skan	{ "link",		TOK_LINK },
66169689Skan	{ "pipe",		TOK_PIPE },
67169689Skan	{ "queue",		TOK_QUEUE },
68169689Skan	{ "flowset",		TOK_FLOWSET },
69169689Skan	{ "sched",		TOK_SCHED },
7018334Speter	{ "pri",		TOK_PRI },
7118334Speter	{ "priority",		TOK_PRI },
7218334Speter	{ "type",		TOK_TYPE },
7318334Speter	{ "flow-id",		TOK_FLOWID},
7418334Speter	{ "dst-ipv6",		TOK_DSTIP6},
7518334Speter	{ "dst-ip6",		TOK_DSTIP6},
7618334Speter	{ "src-ipv6",		TOK_SRCIP6},
7750397Sobrien	{ "src-ip6",		TOK_SRCIP6},
7850397Sobrien	{ "profile",		TOK_PROFILE},
7990075Sobrien	{ "burst",		TOK_BURST},
8018334Speter	{ "dummynet-params",	TOK_NULL },
81117395Skan	{ NULL, 0 }	/* terminator */
82132718Skan};
8318334Speter
8418334Speter#define O_NEXT(p, len) ((void *)((char *)p + len))
8518334Speter
8618334Speterstatic void
87169689Skanoid_fill(struct dn_id *oid, int len, int type, uintptr_t id)
8818334Speter{
8918334Speter	oid->len = len;
9018334Speter	oid->type = type;
9118334Speter	oid->subtype = 0;
9218334Speter	oid->id = id;
9318334Speter}
9418334Speter
9518334Speter/* make room in the buffer and move the pointer forward */
9618334Speterstatic void *
9718334Spetero_next(struct dn_id **o, int len, int type)
9818334Speter{
9918334Speter	struct dn_id *ret = *o;
10018334Speter	oid_fill(ret, len, type, 0);
10118334Speter	*o = O_NEXT(*o, len);
10218334Speter	return ret;
10318334Speter}
10418334Speter
10518334Speter#if 0
10618334Speterstatic int
10718334Spetersort_q(void *arg, const void *pa, const void *pb)
10818334Speter{
10918334Speter	int rev = (co.do_sort < 0);
11018334Speter	int field = rev ? -co.do_sort : co.do_sort;
11118334Speter	long long res = 0;
11250397Sobrien	const struct dn_flow_queue *a = pa;
11318334Speter	const struct dn_flow_queue *b = pb;
114132718Skan
115132718Skan	switch (field) {
116132718Skan	case 1: /* pkts */
117132718Skan		res = a->len - b->len;
118132718Skan		break;
119132718Skan	case 2: /* bytes */
120132718Skan		res = a->len_bytes - b->len_bytes;
121132718Skan		break;
122132718Skan
12390075Sobrien	case 3: /* tot pkts */
124132718Skan		res = a->tot_pkts - b->tot_pkts;
12518334Speter		break;
126132718Skan
127132718Skan	case 4: /* tot bytes */
128132718Skan		res = a->tot_bytes - b->tot_bytes;
129132718Skan		break;
130132718Skan	}
131132718Skan	if (res < 0)
132132718Skan		res = -1;
133132718Skan	if (res > 0)
134132718Skan		res = 1;
135132718Skan	return (int)(rev ? res : -res);
136132718Skan}
137132718Skan#endif
13890075Sobrien
139132718Skan/* print a mask and header for the subsequent list of flows */
140132718Skanstatic void
14118334Speterprint_mask(struct ipfw_flow_id *id)
142132718Skan{
143132718Skan	if (!IS_IP6_FLOW_ID(id)) {
144132718Skan		printf("    "
145132718Skan		    "mask: %s 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
146132718Skan		    id->extra ? "queue," : "",
147132718Skan		    id->proto,
148132718Skan		    id->src_ip, id->src_port,
14918334Speter		    id->dst_ip, id->dst_port);
15018334Speter	} else {
15118334Speter		char buf[255];
15218334Speter		printf("\n        mask: %sproto: 0x%02x, flow_id: 0x%08x,  ",
15318334Speter		    id->extra ? "queue," : "",
15418334Speter		    id->proto, id->flow_id6);
15518334Speter		inet_ntop(AF_INET6, &(id->src_ip6), buf, sizeof(buf));
15618334Speter		printf("%s/0x%04x -> ", buf, id->src_port);
15718334Speter		inet_ntop(AF_INET6, &(id->dst_ip6), buf, sizeof(buf));
15818334Speter		printf("%s/0x%04x\n", buf, id->dst_port);
15918334Speter	}
16018334Speter}
16118334Speter
16218334Speterstatic void
16318334Speterprint_header(struct ipfw_flow_id *id)
16418334Speter{
16518334Speter	if (!IS_IP6_FLOW_ID(id))
16650397Sobrien		printf("BKT Prot ___Source IP/port____ "
16750397Sobrien		    "____Dest. IP/port____ "
168132718Skan		    "Tot_pkt/bytes Pkt/Byte Drp\n");
169132718Skan	else
17050397Sobrien		printf("BKT ___Prot___ _flow-id_ "
17150397Sobrien		    "______________Source IPv6/port_______________ "
17218334Speter		    "_______________Dest. IPv6/port_______________ "
17318334Speter		    "Tot_pkt/bytes Pkt/Byte Drp\n");
17418334Speter}
17518334Speter
17618334Speterstatic void
17718334Speterlist_flow(struct dn_flow *ni, int *print)
17818334Speter{
17918334Speter	char buff[255];
18018334Speter	struct protoent *pe = NULL;
18118334Speter	struct in_addr ina;
18218334Speter	struct ipfw_flow_id *id = &ni->fid;
18318334Speter
184132718Skan	if (*print) {
18518334Speter		print_header(&ni->fid);
18618334Speter		*print = 0;
18718334Speter	}
18818334Speter	pe = getprotobynumber(id->proto);
18918334Speter		/* XXX: Should check for IPv4 flows */
19018334Speter	printf("%3u%c", (ni->oid.id) & 0xff,
19118334Speter		id->extra ? '*' : ' ');
19218334Speter	if (!IS_IP6_FLOW_ID(id)) {
19318334Speter		if (pe)
19418334Speter			printf("%-4s ", pe->p_name);
19518334Speter		else
19618334Speter			printf("%4u ", id->proto);
19718334Speter		ina.s_addr = htonl(id->src_ip);
19818334Speter		printf("%15s/%-5d ",
19918334Speter		    inet_ntoa(ina), id->src_port);
20018334Speter		ina.s_addr = htonl(id->dst_ip);
20150397Sobrien		printf("%15s/%-5d ",
20250397Sobrien		    inet_ntoa(ina), id->dst_port);
203132718Skan	} else {
204132718Skan		/* Print IPv6 flows */
20550397Sobrien		if (pe != NULL)
20618334Speter			printf("%9s ", pe->p_name);
20718334Speter		else
20818334Speter			printf("%9u ", id->proto);
20918334Speter		printf("%7d  %39s/%-5d ", id->flow_id6,
21018334Speter		    inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)),
21118334Speter		    id->src_port);
21218334Speter		printf(" %39s/%-5d ",
21318334Speter		    inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)),
21418334Speter		    id->dst_port);
21518334Speter	}
21618334Speter	pr_u64(&ni->tot_pkts, 4);
21718334Speter	pr_u64(&ni->tot_bytes, 8);
21818334Speter	printf("%2u %4u %3u\n",
21918334Speter	    ni->length, ni->len_bytes, ni->drops);
22018334Speter}
22118334Speter
22218334Speterstatic void
22390075Sobrienprint_flowset_parms(struct dn_fs *fs, char *prefix)
22418334Speter{
22518334Speter	int l;
22618334Speter	char qs[30];
22718334Speter	char plr[30];
22818334Speter	char red[90];	/* Display RED parameters */
22918334Speter
23018334Speter	l = fs->qsize;
23118334Speter	if (fs->flags & DN_QSIZE_BYTES) {
23218334Speter		if (l >= 8192)
23318334Speter			sprintf(qs, "%d KB", l / 1024);
23418334Speter		else
23518334Speter			sprintf(qs, "%d B", l);
23618334Speter	} else
23718334Speter		sprintf(qs, "%3d sl.", l);
23818334Speter	if (fs->plr)
23918334Speter		sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
24018334Speter	else
24118334Speter		plr[0] = '\0';
24218334Speter
24318334Speter	if (fs->flags & DN_IS_RED) {	/* RED parameters */
24418334Speter		sprintf(red,
24518334Speter		    "\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
24618334Speter		    (fs->flags & DN_IS_GENTLE_RED) ? 'G' : ' ',
24718334Speter		    1.0 * fs->w_q / (double)(1 << SCALE_RED),
24818334Speter		    fs->min_th,
24918334Speter		    fs->max_th,
25018334Speter		    1.0 * fs->max_p / (double)(1 << SCALE_RED));
25118334Speter		if (fs->flags & DN_IS_ECN)
25218334Speter			strncat(red, " (ecn)", 6);
25318334Speter	} else
25418334Speter		sprintf(red, "droptail");
25518334Speter
25690075Sobrien	if (prefix[0]) {
25718334Speter	    printf("%s %s%s %d queues (%d buckets) %s\n",
25818334Speter		prefix, qs, plr, fs->oid.id, fs->buckets, red);
25918334Speter	    prefix[0] = '\0';
26018334Speter	} else {
26118334Speter	    printf("q%05d %s%s %d flows (%d buckets) sched %d "
26218334Speter			"weight %d lmax %d pri %d %s\n",
263132718Skan		fs->fs_nr, qs, plr, fs->oid.id, fs->buckets,
26418334Speter		fs->sched_nr, fs->par[0], fs->par[1], fs->par[2], red);
26518334Speter	    if (fs->flags & DN_HAVE_MASK)
26618334Speter		print_mask(&fs->flow_mask);
26718334Speter	}
26818334Speter}
26918334Speter
27018334Speterstatic void
27118334Speterprint_extra_delay_parms(struct dn_profile *p)
27218334Speter{
27318334Speter	double loss;
27418334Speter	if (p->samples_no <= 0)
27590075Sobrien		return;
27690075Sobrien
27750397Sobrien	loss = p->loss_level;
27850397Sobrien	loss /= p->samples_no;
27950397Sobrien	printf("\t profile: name \"%s\" loss %f samples %d\n",
28050397Sobrien		p->name, loss, p->samples_no);
28150397Sobrien}
28250397Sobrien
28350397Sobrienstatic void
28450397Sobrienflush_buf(char *buf)
28550397Sobrien{
28650397Sobrien	if (buf[0])
28750397Sobrien		printf("%s\n", buf);
28850397Sobrien	buf[0] = '\0';
28950397Sobrien}
29050397Sobrien
29150397Sobrien/*
29250397Sobrien * generic list routine. We expect objects in a specific order, i.e.
29350397Sobrien * PIPES AND SCHEDULERS:
29450397Sobrien *	link; scheduler; internal flowset if any; instances
29550397Sobrien * we can tell a pipe from the number.
29690075Sobrien *
29750397Sobrien * FLOWSETS:
29850397Sobrien *	flowset; queues;
29950397Sobrien * link i (int queue); scheduler i; si(i) { flowsets() : queues }
30050397Sobrien */
30150397Sobrienstatic void
30250397Sobrienlist_pipes(struct dn_id *oid, struct dn_id *end)
30350397Sobrien{
304132718Skan    char buf[160];	/* pending buffer */
30590075Sobrien    int toPrint = 1;	/* print header */
306117395Skan
30718334Speter    buf[0] = '\0';
308169689Skan    for (; oid != end; oid = O_NEXT(oid, oid->len)) {
309169689Skan	if (oid->len < sizeof(*oid))
310169689Skan		errx(1, "invalid oid len %d\n", oid->len);
311169689Skan
312169689Skan	switch (oid->type) {
313169689Skan	default:
314169689Skan	    flush_buf(buf);
315169689Skan	    printf("unrecognized object %d size %d\n", oid->type, oid->len);
316169689Skan	    break;
317169689Skan	case DN_TEXT: /* list of attached flowsets */
31850397Sobrien	    {
31990075Sobrien		int i, l;
32090075Sobrien		struct {
321169689Skan			struct dn_id id;
322169689Skan			uint32_t p[0];
32390075Sobrien		} *d = (void *)oid;
324169689Skan		l = (oid->len - sizeof(*oid))/sizeof(d->p[0]);
325169689Skan		if (l == 0)
32650397Sobrien		    break;
327169689Skan		printf("   Children flowsets: ");
328169689Skan		for (i = 0; i < l; i++)
329169689Skan			printf("%u ", d->p[i]);
330169689Skan		printf("\n");
331169689Skan		break;
332169689Skan	    }
333169689Skan	case DN_CMD_GET:
334169689Skan	    if (co.verbose)
335169689Skan		printf("answer for cmd %d, len %d\n", oid->type, oid->id);
336169689Skan	    break;
337169689Skan	case DN_SCH: {
338169689Skan	    struct dn_sch *s = (struct dn_sch *)oid;
339169689Skan	    flush_buf(buf);
340169689Skan	    printf(" sched %d type %s flags 0x%x %d buckets %d active\n",
34190075Sobrien			s->sched_nr,
34218334Speter			s->name, s->flags, s->buckets, s->oid.id);
34318334Speter	    if (s->flags & DN_HAVE_MASK)
34418334Speter		print_mask(&s->sched_mask);
34518334Speter	    }
346132718Skan	    break;
34718334Speter
34818334Speter	case DN_FLOW:
34918334Speter	    list_flow((struct dn_flow *)oid, &toPrint);
350169689Skan	    break;
35118334Speter
352117395Skan	case DN_LINK: {
35318334Speter	    struct dn_link *p = (struct dn_link *)oid;
35418334Speter	    double b = p->bandwidth;
355132718Skan	    char bwbuf[30];
35618334Speter	    char burst[5 + 7];
35718334Speter
35818334Speter	    /* This starts a new object so flush buffer */
35918334Speter	    flush_buf(buf);
36018334Speter	    /* data rate */
36118334Speter	    if (b == 0)
36218334Speter		sprintf(bwbuf, "unlimited     ");
36318334Speter	    else if (b >= 1000000)
36418334Speter		sprintf(bwbuf, "%7.3f Mbit/s", b/1000000);
36518334Speter	    else if (b >= 1000)
36618334Speter		sprintf(bwbuf, "%7.3f Kbit/s", b/1000);
36718334Speter	    else
36818334Speter		sprintf(bwbuf, "%7.3f bit/s ", b);
36918334Speter
37018334Speter	    if (humanize_number(burst, sizeof(burst), p->burst,
37118334Speter		    "", HN_AUTOSCALE, 0) < 0 || co.verbose)
37218334Speter		sprintf(burst, "%d", (int)p->burst);
37318334Speter	    sprintf(buf, "%05d: %s %4d ms burst %s",
37490075Sobrien		p->link_nr % DN_MAX_ID, bwbuf, p->delay, burst);
37590075Sobrien	    }
37690075Sobrien	    break;
37718334Speter
37818334Speter	case DN_FS:
37918334Speter	    print_flowset_parms((struct dn_fs *)oid, buf);
38018334Speter	    break;
38118334Speter	case DN_PROFILE:
38218334Speter	    flush_buf(buf);
38318334Speter	    print_extra_delay_parms((struct dn_profile *)oid);
38418334Speter	}
38518334Speter	flush_buf(buf); // XXX does it really go here ?
38618334Speter    }
38718334Speter}
388132718Skan
38918334Speter/*
39018334Speter * Delete pipe, queue or scheduler i
39118334Speter */
39218334Speterint
39318334Speteripfw_delete_pipe(int do_pipe, int i)
39418334Speter{
39518334Speter	struct {
39618334Speter		struct dn_id oid;
39718334Speter		uintptr_t a[1];	/* add more if we want a list */
39818334Speter	} cmd;
39918334Speter	oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION);
40018334Speter	cmd.oid.subtype = (do_pipe == 1) ? DN_LINK :
40118334Speter		( (do_pipe == 2) ? DN_FS : DN_SCH);
40218334Speter	cmd.a[0] = i;
40318334Speter	i = do_cmd(IP_DUMMYNET3, &cmd, cmd.oid.len);
40418334Speter	if (i) {
40518334Speter		i = 1;
40618334Speter		warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", i);
40718334Speter	}
40818334Speter	return i;
40918334Speter}
41018334Speter
41118334Speter/*
41218334Speter * Code to parse delay profiles.
41318334Speter *
41418334Speter * Some link types introduce extra delays in the transmission
41518334Speter * of a packet, e.g. because of MAC level framing, contention on
41618334Speter * the use of the channel, MAC level retransmissions and so on.
41718334Speter * From our point of view, the channel is effectively unavailable
418132718Skan * for this extra time, which is constant or variable depending
41918334Speter * on the link type. Additionally, packets may be dropped after this
42090075Sobrien * time (e.g. on a wireless link after too many retransmissions).
42118334Speter * We can model the additional delay with an empirical curve
42218334Speter * that represents its distribution.
42318334Speter *
42418334Speter *      cumulative probability
42518334Speter *      1.0 ^
42618334Speter *          |
42718334Speter *      L   +-- loss-level          x
428132718Skan *          |                 ******
42918334Speter *          |                *
43090075Sobrien *          |           *****
43118334Speter *          |          *
43218334Speter *          |        **
43318334Speter *          |       *
43418334Speter *          +-------*------------------->
43518334Speter *                      delay
43618334Speter *
43718334Speter * The empirical curve may have both vertical and horizontal lines.
43818334Speter * Vertical lines represent constant delay for a range of
43990075Sobrien * probabilities; horizontal lines correspond to a discontinuty
44018334Speter * in the delay distribution: the link will use the largest delay
44118334Speter * for a given probability.
44218334Speter *
44318334Speter * To pass the curve to dummynet, we must store the parameters
44418334Speter * in a file as described below, and issue the command
44518334Speter *
44618334Speter *      ipfw pipe <n> config ... bw XXX profile <filename> ...
44718334Speter *
44818334Speter * The file format is the following, with whitespace acting as
44918334Speter * a separator and '#' indicating the beginning a comment:
45018334Speter *
45118334Speter *	samples N
45218334Speter *		the number of samples used in the internal
45318334Speter *		representation (2..1024; default 100);
45418334Speter *
45518334Speter *	loss-level L
45618334Speter *		The probability above which packets are lost.
45718334Speter *	       (0.0 <= L <= 1.0, default 1.0 i.e. no loss);
45818334Speter *
45918334Speter *	name identifier
46018334Speter *		Optional a name (listed by "ipfw pipe show")
46118334Speter *		to identify the distribution;
46218334Speter *
46318334Speter *	"delay prob" | "prob delay"
46418334Speter *		One of these two lines is mandatory and defines
46518334Speter *		the format of the following lines with data points.
46618334Speter *
46718334Speter *	XXX YYY
46818334Speter *		2 or more lines representing points in the curve,
46918334Speter *		with either delay or probability first, according
47018334Speter *		to the chosen format.
47118334Speter *		The unit for delay is milliseconds.
47218334Speter *
47318334Speter * Data points does not need to be ordered or equal to the number
47418334Speter * specified in the "samples" line. ipfw will sort and interpolate
47518334Speter * the curve as needed.
47618334Speter *
47718334Speter * Example of a profile file:
478132718Skan
47918334Speter	name    bla_bla_bla
48018334Speter	samples 100
48118334Speter	loss-level    0.86
48218334Speter	prob    delay
48318334Speter	0       200	# minimum overhead is 200ms
48418334Speter	0.5     200
48518334Speter	0.5     300
48618334Speter	0.8     1000
48718334Speter	0.9     1300
48818334Speter	1       1300
48918334Speter
49018334Speter * Internally, we will convert the curve to a fixed number of
49152284Sobrien * samples, and when it is time to transmit a packet we will
49218334Speter * model the extra delay as extra bits in the packet.
49318334Speter *
49418334Speter */
49518334Speter
49618334Speter#define ED_MAX_LINE_LEN	256+ED_MAX_NAME_LEN
49718334Speter#define ED_TOK_SAMPLES	"samples"
49818334Speter#define ED_TOK_LOSS	"loss-level"
49918334Speter#define ED_TOK_NAME	"name"
50018334Speter#define ED_TOK_DELAY	"delay"
50118334Speter#define ED_TOK_PROB	"prob"
50218334Speter#define ED_TOK_BW	"bw"
50318334Speter#define ED_SEPARATORS	" \t\n"
50418334Speter#define ED_MIN_SAMPLES_NO	2
50590075Sobrien
50690075Sobrien/*
50718334Speter * returns 1 if s is a non-negative number, with at least one '.'
50850397Sobrien */
50950397Sobrienstatic int
51018334Speteris_valid_number(const char *s)
51118334Speter{
51218334Speter	int i, dots_found = 0;
51318334Speter	int len = strlen(s);
51450397Sobrien
51550397Sobrien	for (i = 0; i<len; ++i)
51618334Speter		if (!isdigit(s[i]) && (s[i] !='.' || ++dots_found > 1))
51718334Speter			return 0;
51818334Speter	return 1;
51918334Speter}
52018334Speter
52118334Speter/*
52218334Speter * Take as input a string describing a bandwidth value
52318334Speter * and return the numeric bandwidth value.
52418334Speter * set clocking interface or bandwidth value
52518334Speter */
52618334Speterstatic void
52718334Speterread_bandwidth(char *arg, int *bandwidth, char *if_name, int namelen)
52850397Sobrien{
529169689Skan	if (*bandwidth != -1)
53018334Speter		warnx("duplicate token, override bandwidth value!");
531169689Skan
53218334Speter	if (arg[0] >= 'a' && arg[0] <= 'z') {
533169689Skan		if (!if_name) {
53418334Speter			errx(1, "no if support");
535169689Skan		}
53618334Speter		if (namelen >= IFNAMSIZ)
537169689Skan			warn("interface name truncated");
53818334Speter		namelen--;
53918334Speter		/* interface name */
54018334Speter		strncpy(if_name, arg, namelen);
54118334Speter		if_name[namelen] = '\0';
54218334Speter		*bandwidth = 0;
54350397Sobrien	} else {	/* read bandwidth value */
54450397Sobrien		int bw;
54518334Speter		char *end = NULL;
54650397Sobrien
54718334Speter		bw = strtoul(arg, &end, 0);
54850397Sobrien		if (*end == 'K' || *end == 'k') {
54950397Sobrien			end++;
55050397Sobrien			bw *= 1000;
55150397Sobrien		} else if (*end == 'M' || *end == 'm') {
55250397Sobrien			end++;
55350397Sobrien			bw *= 1000000;
55450397Sobrien		}
55518334Speter		if ((*end == 'B' &&
55618334Speter			_substrcmp2(end, "Bi", "Bit/s") != 0) ||
55718334Speter		    _substrcmp2(end, "by", "bytes") == 0)
55818334Speter			bw *= 8;
55918334Speter
56018334Speter		if (bw < 0)
56118334Speter			errx(EX_DATAERR, "bandwidth too large");
56218334Speter
56318334Speter		*bandwidth = bw;
56418334Speter		if (if_name)
56518334Speter			if_name[0] = '\0';
56618334Speter	}
56718334Speter}
56890075Sobrien
56990075Sobrienstruct point {
57090075Sobrien	double prob;
57190075Sobrien	double delay;
57290075Sobrien};
57390075Sobrien
57418334Speterstatic int
57590075Sobriencompare_points(const void *vp1, const void *vp2)
57618334Speter{
57718334Speter	const struct point *p1 = vp1;
57818334Speter	const struct point *p2 = vp2;
57918334Speter	double res = 0;
58018334Speter
58118334Speter	res = p1->prob - p2->prob;
58218334Speter	if (res == 0)
58318334Speter		res = p1->delay - p2->delay;
58418334Speter	if (res < 0)
58518334Speter		return -1;
58618334Speter	else if (res > 0)
58718334Speter		return 1;
58818334Speter	else
58918334Speter		return 0;
59018334Speter}
59118334Speter
59218334Speter#define ED_EFMT(s) EX_DATAERR,"error in %s at line %d: "#s,filename,lineno
59318334Speter
59418334Speterstatic void
59518334Speterload_extra_delays(const char *filename, struct dn_profile *p,
59618334Speter	struct dn_link *link)
59718334Speter{
59818334Speter	char    line[ED_MAX_LINE_LEN];
59918334Speter	FILE    *f;
60050397Sobrien	int     lineno = 0;
60150397Sobrien	int     i;
60218334Speter
60318334Speter	int     samples = -1;
60418334Speter	double  loss = -1.0;
60518334Speter	char    profile_name[ED_MAX_NAME_LEN];
60618334Speter	int     delay_first = -1;
60718334Speter	int     do_points = 0;
60818334Speter	struct point    points[ED_MAX_SAMPLES_NO];
60918334Speter	int     points_no = 0;
61018334Speter
61118334Speter	/* XXX link never NULL? */
61218334Speter	p->link_nr = link->link_nr;
61318334Speter
61418334Speter	profile_name[0] = '\0';
61518334Speter	f = fopen(filename, "r");
61618334Speter	if (f == NULL)
61718334Speter		err(EX_UNAVAILABLE, "fopen: %s", filename);
61818334Speter
61918334Speter	while (fgets(line, ED_MAX_LINE_LEN, f)) {	 /* read commands */
62018334Speter		char *s, *cur = line, *name = NULL, *arg = NULL;
62118334Speter
62218334Speter		++lineno;
62318334Speter
62418334Speter		/* parse the line */
62518334Speter		while (cur) {
62618334Speter			s = strsep(&cur, ED_SEPARATORS);
62718334Speter			if (s == NULL || *s == '#')
62818334Speter				break;
62918334Speter			if (*s == '\0')
63018334Speter				continue;
63118334Speter			if (arg)
63218334Speter				errx(ED_EFMT("too many arguments"));
63318334Speter			if (name == NULL)
63418334Speter				name = s;
63518334Speter			else
63618334Speter				arg = s;
63718334Speter		}
63818334Speter		if (name == NULL)	/* empty line */
63918334Speter			continue;
640132718Skan		if (arg == NULL)
64118334Speter			errx(ED_EFMT("missing arg for %s"), name);
64218334Speter
64318334Speter		if (!strcasecmp(name, ED_TOK_SAMPLES)) {
64418334Speter		    if (samples > 0)
64518334Speter			errx(ED_EFMT("duplicate ``samples'' line"));
64618334Speter		    if (atoi(arg) <=0)
64718334Speter			errx(ED_EFMT("invalid number of samples"));
64818334Speter		    samples = atoi(arg);
649132718Skan		    if (samples>ED_MAX_SAMPLES_NO)
65018334Speter			    errx(ED_EFMT("too many samples, maximum is %d"),
65118334Speter				ED_MAX_SAMPLES_NO);
65218334Speter		    do_points = 0;
65318334Speter		} else if (!strcasecmp(name, ED_TOK_BW)) {
65418334Speter		    char buf[IFNAMSIZ];
65518334Speter		    read_bandwidth(arg, &link->bandwidth, buf, sizeof(buf));
65618334Speter		} else if (!strcasecmp(name, ED_TOK_LOSS)) {
65790075Sobrien		    if (loss != -1.0)
65890075Sobrien			errx(ED_EFMT("duplicated token: %s"), name);
65918334Speter		    if (!is_valid_number(arg))
66018334Speter			errx(ED_EFMT("invalid %s"), arg);
66190075Sobrien		    loss = atof(arg);
66218334Speter		    if (loss > 1)
66318334Speter			errx(ED_EFMT("%s greater than 1.0"), name);
66418334Speter		    do_points = 0;
66518334Speter		} else if (!strcasecmp(name, ED_TOK_NAME)) {
66618334Speter		    if (profile_name[0] != '\0')
66718334Speter			errx(ED_EFMT("duplicated token: %s"), name);
66818334Speter		    strncpy(profile_name, arg, sizeof(profile_name) - 1);
66918334Speter		    profile_name[sizeof(profile_name)-1] = '\0';
67018334Speter		    do_points = 0;
671132718Skan		} else if (!strcasecmp(name, ED_TOK_DELAY)) {
67218334Speter		    if (do_points)
67318334Speter			errx(ED_EFMT("duplicated token: %s"), name);
67418334Speter		    delay_first = 1;
675132718Skan		    do_points = 1;
67618334Speter		} else if (!strcasecmp(name, ED_TOK_PROB)) {
67718334Speter		    if (do_points)
67818334Speter			errx(ED_EFMT("duplicated token: %s"), name);
67918334Speter		    delay_first = 0;
68018334Speter		    do_points = 1;
68118334Speter		} else if (do_points) {
68218334Speter		    if (!is_valid_number(name) || !is_valid_number(arg))
68318334Speter			errx(ED_EFMT("invalid point found"));
68418334Speter		    if (delay_first) {
68518334Speter			points[points_no].delay = atof(name);
68618334Speter			points[points_no].prob = atof(arg);
68718334Speter		    } else {
68818334Speter			points[points_no].delay = atof(arg);
689132718Skan			points[points_no].prob = atof(name);
69018334Speter		    }
69118334Speter		    if (points[points_no].prob > 1.0)
69218334Speter			errx(ED_EFMT("probability greater than 1.0"));
69318334Speter		    ++points_no;
69418334Speter		} else {
69590075Sobrien		    errx(ED_EFMT("unrecognised command '%s'"), name);
69618334Speter		}
697169689Skan	}
698169689Skan
699169689Skan	fclose (f);
700169689Skan
701169689Skan	if (samples == -1) {
702169689Skan	    warnx("'%s' not found, assuming 100", ED_TOK_SAMPLES);
703169689Skan	    samples = 100;
704169689Skan	}
70518334Speter
70618334Speter	if (loss == -1.0) {
70718334Speter	    warnx("'%s' not found, assuming no loss", ED_TOK_LOSS);
70818334Speter	    loss = 1;
70918334Speter	}
71018334Speter
71118334Speter	/* make sure that there are enough points. */
71218334Speter	if (points_no < ED_MIN_SAMPLES_NO)
71318334Speter	    errx(ED_EFMT("too few samples, need at least %d"),
71418334Speter		ED_MIN_SAMPLES_NO);
71518334Speter
71618334Speter	qsort(points, points_no, sizeof(struct point), compare_points);
71718334Speter
71850397Sobrien	/* interpolation */
71950397Sobrien	for (i = 0; i<points_no-1; ++i) {
72050397Sobrien	    double y1 = points[i].prob * samples;
72150397Sobrien	    double x1 = points[i].delay;
72218334Speter	    double y2 = points[i+1].prob * samples;
723169689Skan	    double x2 = points[i+1].delay;
72418334Speter
72518334Speter	    int ix = y1;
72618334Speter	    int stop = y2;
72718334Speter
72818334Speter	    if (x1 == x2) {
72918334Speter		for (; ix<stop; ++ix)
73018334Speter		    p->samples[ix] = x1;
73118334Speter	    } else {
73218334Speter		double m = (y2-y1)/(x2-x1);
73318334Speter		double c = y1 - m*x1;
73418334Speter		for (; ix<stop ; ++ix)
73518334Speter		    p->samples[ix] = (ix - c)/m;
73618334Speter	    }
737169689Skan	}
738169689Skan	p->samples_no = samples;
739169689Skan	p->loss_level = loss * samples;
740169689Skan	strncpy(p->name, profile_name, sizeof(p->name));
741169689Skan}
74218334Speter
74318334Speter/*
74418334Speter * configuration of pipes, schedulers, flowsets.
74518334Speter * When we configure a new scheduler, an empty pipe is created, so:
74618334Speter *
74718334Speter * do_pipe = 1 -> "pipe N config ..." only for backward compatibility
74818334Speter *	sched N+Delta type fifo sched_mask ...
74918334Speter *	pipe N+Delta <parameters>
75018334Speter *	flowset N+Delta pipe N+Delta (no parameters)
75118334Speter *	sched N type wf2q+ sched_mask ...
75218334Speter *	pipe N <parameters>
75318334Speter *
754169689Skan * do_pipe = 2 -> flowset N config
75518334Speter *	flowset N parameters
75618334Speter *
75718334Speter * do_pipe = 3 -> sched N config
75818334Speter *	sched N parameters (default no pipe)
75918334Speter *	optional Pipe N config ...
76018334Speter * pipe ==>
76118334Speter */
76218334Spetervoid
76318334Speteripfw_config_pipe(int ac, char **av)
76418334Speter{
76518334Speter	int i;
76618334Speter	u_int j;
76718334Speter	char *end;
76818334Speter	struct dn_id *buf, *base;
76990075Sobrien	struct dn_sch *sch = NULL;
77018334Speter	struct dn_link *p = NULL;
77118334Speter	struct dn_fs *fs = NULL;
772117395Skan	struct dn_profile *pf = NULL;
77390075Sobrien	struct ipfw_flow_id *mask = NULL;
77418334Speter	int lmax;
77552284Sobrien	uint32_t _foo = 0, *flags = &_foo , *buckets = &_foo;
77618334Speter
77718334Speter	/*
77818334Speter	 * allocate space for 1 header,
77918334Speter	 * 1 scheduler, 1 link, 1 flowset, 1 profile
78018334Speter	 */
78118334Speter	lmax = sizeof(struct dn_id);	/* command header */
78218334Speter	lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) +
78318334Speter		sizeof(struct dn_fs) + sizeof(struct dn_profile);
78418334Speter
78518334Speter	av++; ac--;
786169689Skan	/* Pipe number */
78718334Speter	if (ac && isdigit(**av)) {
78890075Sobrien		i = atoi(*av); av++; ac--;
78918334Speter	} else
79018334Speter		i = -1;
79118334Speter	if (i <= 0)
79218334Speter		errx(EX_USAGE, "need a pipe/flowset/sched number");
79318334Speter	base = buf = safe_calloc(1, lmax);
79418334Speter	/* all commands start with a 'CONFIGURE' and a version */
79590075Sobrien	o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG);
796169689Skan	base->id = DN_API_VERSION;
79718334Speter
79890075Sobrien	switch (co.do_pipe) {
79918334Speter	case 1: /* "pipe N config ..." */
80018334Speter		/* Allocate space for the WF2Q+ scheduler, its link
80190075Sobrien		 * and the FIFO flowset. Set the number, but leave
80290075Sobrien		 * the scheduler subtype and other parameters to 0
80318334Speter		 * so the kernel will use appropriate defaults.
80418334Speter		 * XXX todo: add a flag to record if a parameter
80518334Speter		 * is actually configured.
80618334Speter		 * If we do a 'pipe config' mask -> sched_mask.
807169689Skan		 * The FIFO scheduler and link are derived from the
80818334Speter		 * WF2Q+ one in the kernel.
80918334Speter		 */
81018334Speter		sch = o_next(&buf, sizeof(*sch), DN_SCH);
81118334Speter		p = o_next(&buf, sizeof(*p), DN_LINK);
81218334Speter		fs = o_next(&buf, sizeof(*fs), DN_FS);
81318334Speter
81418334Speter		sch->sched_nr = i;
81518334Speter		sch->oid.subtype = 0;	/* defaults to WF2Q+ */
81618334Speter		mask = &sch->sched_mask;
81718334Speter		flags = &sch->flags;
81818334Speter		buckets = &sch->buckets;
81990075Sobrien		*flags |= DN_PIPE_CMD;
82018334Speter
82118334Speter		p->link_nr = i;
82218334Speter
82318334Speter		/* This flowset is only for the FIFO scheduler */
82418334Speter		fs->fs_nr = i + 2*DN_MAX_ID;
82518334Speter		fs->sched_nr = i + DN_MAX_ID;
826169689Skan		break;
82718334Speter
82818334Speter	case 2: /* "queue N config ... " */
82918334Speter		fs = o_next(&buf, sizeof(*fs), DN_FS);
83018334Speter		fs->fs_nr = i;
83118334Speter		mask = &fs->flow_mask;
83218334Speter		flags = &fs->flags;
83318334Speter		buckets = &fs->buckets;
83418334Speter		break;
83518334Speter
83690075Sobrien	case 3: /* "sched N config ..." */
83790075Sobrien		sch = o_next(&buf, sizeof(*sch), DN_SCH);
83890075Sobrien		fs = o_next(&buf, sizeof(*fs), DN_FS);
83990075Sobrien		sch->sched_nr = i;
84018334Speter		mask = &sch->sched_mask;
841169689Skan		flags = &sch->flags;
84218334Speter		buckets = &sch->buckets;
84318334Speter		/* fs is used only with !MULTIQUEUE schedulers */
84418334Speter		fs->fs_nr = i + DN_MAX_ID;
84518334Speter		fs->sched_nr = i;
84618334Speter		break;
84718334Speter	}
848117395Skan	/* set to -1 those fields for which we want to reuse existing
84918334Speter	 * values from the kernel.
85018334Speter	 * Also, *_nr and subtype = 0 mean reuse the value from the kernel.
85118334Speter	 * XXX todo: support reuse of the mask.
85218334Speter	 */
853117395Skan	if (p)
85418334Speter		p->bandwidth = -1;
85518334Speter	for (j = 0; j < sizeof(fs->par)/sizeof(fs->par[0]); j++)
85618334Speter		fs->par[j] = -1;
85718334Speter	while (ac > 0) {
85818334Speter		double d;
85918334Speter		int tok = match_token(dummynet_params, *av);
86018334Speter		ac--; av++;
86118334Speter
862169689Skan		switch(tok) {
863169689Skan		case TOK_NOERROR:
864169689Skan			NEED(fs, "noerror is only for pipes");
86518334Speter			fs->flags |= DN_NOERROR;
86618334Speter			break;
86718334Speter
86818334Speter		case TOK_PLR:
86918334Speter			NEED(fs, "plr is only for pipes");
87018334Speter			NEED1("plr needs argument 0..1\n");
87118334Speter			d = strtod(av[0], NULL);
87218334Speter			if (d > 1)
87318334Speter				d = 1;
874169689Skan			else if (d < 0)
87518334Speter				d = 0;
87618334Speter			fs->plr = (int)(d*0x7fffffff);
87718334Speter			ac--; av++;
87818334Speter			break;
87918334Speter
88018334Speter		case TOK_QUEUE:
88118334Speter			NEED(fs, "queue is only for pipes or flowsets");
88218334Speter			NEED1("queue needs queue size\n");
88318334Speter			end = NULL;
88418334Speter			fs->qsize = strtoul(av[0], &end, 0);
88518334Speter			if (*end == 'K' || *end == 'k') {
88618334Speter				fs->flags |= DN_QSIZE_BYTES;
88718334Speter				fs->qsize *= 1024;
88818334Speter			} else if (*end == 'B' ||
88918334Speter			    _substrcmp2(end, "by", "bytes") == 0) {
89052284Sobrien				fs->flags |= DN_QSIZE_BYTES;
89152284Sobrien			}
89252284Sobrien			ac--; av++;
89352284Sobrien			break;
89452284Sobrien
89552284Sobrien		case TOK_BUCKETS:
89618334Speter			NEED(fs, "buckets is only for pipes or flowsets");
897169689Skan			NEED1("buckets needs argument\n");
89818334Speter			*buckets = strtoul(av[0], NULL, 0);
899169689Skan			ac--; av++;
90018334Speter			break;
90118334Speter
90218334Speter		case TOK_FLOW_MASK:
90318334Speter		case TOK_SCHED_MASK:
904169689Skan		case TOK_MASK:
90518334Speter			NEED(mask, "tok_mask");
90618334Speter			NEED1("mask needs mask specifier\n");
90718334Speter			/*
90818334Speter			 * per-flow queue, mask is dst_ip, dst_port,
90918334Speter			 * src_ip, src_port, proto measured in bits
91018334Speter			 */
91118334Speter
91218334Speter			bzero(mask, sizeof(*mask));
91318334Speter			end = NULL;
91418334Speter
91518334Speter			while (ac >= 1) {
91618334Speter			    uint32_t *p32 = NULL;
91718334Speter			    uint16_t *p16 = NULL;
91818334Speter			    uint32_t *p20 = NULL;
91950397Sobrien			    struct in6_addr *pa6 = NULL;
92050397Sobrien			    uint32_t a;
92150397Sobrien
92218334Speter			    tok = match_token(dummynet_params, *av);
92318334Speter			    ac--; av++;
92418334Speter			    switch(tok) {
92518334Speter			    case TOK_ALL:
926132718Skan				    /*
92718334Speter				     * special case, all bits significant
92818334Speter				     * except 'extra' (the queue number)
92918334Speter				     */
93090075Sobrien				    mask->dst_ip = ~0;
931132718Skan				    mask->src_ip = ~0;
93218334Speter				    mask->dst_port = ~0;
93318334Speter				    mask->src_port = ~0;
93418334Speter				    mask->proto = ~0;
93518334Speter				    n2mask(&mask->dst_ip6, 128);
93618334Speter				    n2mask(&mask->src_ip6, 128);
93718334Speter				    mask->flow_id6 = ~0;
938169689Skan				    *flags |= DN_HAVE_MASK;
939169689Skan				    goto end_mask;
940169689Skan
94118334Speter			    case TOK_QUEUE:
94218334Speter				    mask->extra = ~0;
94318334Speter				    *flags |= DN_HAVE_MASK;
94418334Speter				    goto end_mask;
94518334Speter
94618334Speter			    case TOK_DSTIP:
94718334Speter				    mask->addr_type = 4;
94818334Speter				    p32 = &mask->dst_ip;
94918334Speter				    break;
95018334Speter
95118334Speter			    case TOK_SRCIP:
95218334Speter				    mask->addr_type = 4;
95318334Speter				    p32 = &mask->src_ip;
95418334Speter				    break;
955132718Skan
95618334Speter			    case TOK_DSTIP6:
95718334Speter				    mask->addr_type = 6;
95850397Sobrien				    pa6 = &mask->dst_ip6;
95918334Speter				    break;
96018334Speter
961132718Skan			    case TOK_SRCIP6:
96218334Speter				    mask->addr_type = 6;
96390075Sobrien				    pa6 = &mask->src_ip6;
96418334Speter				    break;
96518334Speter
96618334Speter			    case TOK_FLOWID:
967132718Skan				    mask->addr_type = 6;
96818334Speter				    p20 = &mask->flow_id6;
96990075Sobrien				    break;
97018334Speter
97118334Speter			    case TOK_DSTPORT:
97218334Speter				    p16 = &mask->dst_port;
97318334Speter				    break;
97418334Speter
97518334Speter			    case TOK_SRCPORT:
97618334Speter				    p16 = &mask->src_port;
97718334Speter				    break;
97890075Sobrien
97918334Speter			    case TOK_PROTO:
98018334Speter				    break;
98118334Speter
98218334Speter			    default:
98318334Speter				    ac++; av--; /* backtrack */
98418334Speter				    goto end_mask;
98518334Speter			    }
98618334Speter			    if (ac < 1)
987132718Skan				    errx(EX_USAGE, "mask: value missing");
98818334Speter			    if (*av[0] == '/') {
98918334Speter				    a = strtoul(av[0]+1, &end, 0);
99018334Speter				    if (pa6 == NULL)
99118334Speter					    a = (a == 32) ? ~0 : (1 << a) - 1;
992132718Skan			    } else
99318334Speter				    a = strtoul(av[0], &end, 0);
99490075Sobrien			    if (p32 != NULL)
99518334Speter				    *p32 = a;
99618334Speter			    else if (p16 != NULL) {
99718334Speter				    if (a > 0xFFFF)
99818334Speter					    errx(EX_DATAERR,
99918334Speter						"port mask must be 16 bit");
100018334Speter				    *p16 = (uint16_t)a;
100118334Speter			    } else if (p20 != NULL) {
100218334Speter				    if (a > 0xfffff)
100318334Speter					errx(EX_DATAERR,
100418334Speter					    "flow_id mask must be 20 bit");
1005132718Skan				    *p20 = (uint32_t)a;
100618334Speter			    } else if (pa6 != NULL) {
100718334Speter				    if (a > 128)
100818334Speter					errx(EX_DATAERR,
100918334Speter					    "in6addr invalid mask len");
101018334Speter				    else
101118334Speter					n2mask(pa6, a);
101218334Speter			    } else {
101318334Speter				    if (a > 0xFF)
101418334Speter					    errx(EX_DATAERR,
101518334Speter						"proto mask must be 8 bit");
101618334Speter				    mask->proto = (uint8_t)a;
101718334Speter			    }
101818334Speter			    if (a != 0)
1019132718Skan				    *flags |= DN_HAVE_MASK;
102018334Speter			    ac--; av++;
102118334Speter			} /* end while, config masks */
102250397Sobrienend_mask:
102318334Speter			break;
102490075Sobrien
102590075Sobrien		case TOK_RED:
102652284Sobrien		case TOK_GRED:
102790075Sobrien			NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
102890075Sobrien			fs->flags |= DN_IS_RED;
102990075Sobrien			if (tok == TOK_GRED)
103090075Sobrien				fs->flags |= DN_IS_GENTLE_RED;
103152284Sobrien			/*
103252284Sobrien			 * the format for parameters is w_q/min_th/max_th/max_p
103352284Sobrien			 */
103452284Sobrien			if ((end = strsep(&av[0], "/"))) {
103552284Sobrien			    double w_q = strtod(end, NULL);
103652284Sobrien			    if (w_q > 1 || w_q <= 0)
103718334Speter				errx(EX_DATAERR, "0 < w_q <= 1");
103818334Speter			    fs->w_q = (int) (w_q * (1 << SCALE_RED));
103918334Speter			}
104018334Speter			if ((end = strsep(&av[0], "/"))) {
104118334Speter			    fs->min_th = strtoul(end, &end, 0);
104218334Speter			    if (*end == 'K' || *end == 'k')
104318334Speter				fs->min_th *= 1024;
104418334Speter			}
104518334Speter			if ((end = strsep(&av[0], "/"))) {
104618334Speter			    fs->max_th = strtoul(end, &end, 0);
1047132718Skan			    if (*end == 'K' || *end == 'k')
104818334Speter				fs->max_th *= 1024;
104918334Speter			}
105018334Speter			if ((end = strsep(&av[0], "/"))) {
105118334Speter			    double max_p = strtod(end, NULL);
105218334Speter			    if (max_p > 1 || max_p < 0)
1053169689Skan				errx(EX_DATAERR, "0 <= max_p <= 1");
105418334Speter			    fs->max_p = (int)(max_p * (1 << SCALE_RED));
105518334Speter			}
105618334Speter			ac--; av++;
105718334Speter			break;
105818334Speter
105918334Speter		case TOK_ECN:
106018334Speter			fs->flags |= DN_IS_ECN;
106118334Speter			break;
106218334Speter
106318334Speter		case TOK_DROPTAIL:
106418334Speter			NEED(fs, "droptail is only for flowsets");
106518334Speter			fs->flags &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
106618334Speter			break;
106718334Speter
106890075Sobrien		case TOK_BW:
106918334Speter			NEED(p, "bw is only for links");
107018334Speter			NEED1("bw needs bandwidth or interface\n");
107118334Speter			read_bandwidth(av[0], &p->bandwidth, NULL, 0);
1072132718Skan			ac--; av++;
107318334Speter			break;
107418334Speter
107518334Speter		case TOK_DELAY:
1076132718Skan			NEED(p, "delay is only for links");
107718334Speter			NEED1("delay needs argument 0..10000ms\n");
107818334Speter			p->delay = strtoul(av[0], NULL, 0);
107918334Speter			ac--; av++;
108018334Speter			break;
108118334Speter
108218334Speter		case TOK_TYPE: {
108318334Speter			int l;
108418334Speter			NEED(sch, "type is only for schedulers");
108518334Speter			NEED1("type needs a string");
108618334Speter			l = strlen(av[0]);
108718334Speter			if (l == 0 || l > 15)
108818334Speter				errx(1, "type %s too long\n", av[0]);
108918334Speter			strcpy(sch->name, av[0]);
109018334Speter			sch->oid.subtype = 0; /* use string */
109118334Speter			ac--; av++;
109218334Speter			break;
109318334Speter		    }
109418334Speter
109518334Speter		case TOK_WEIGHT:
109618334Speter			NEED(fs, "weight is only for flowsets");
109718334Speter			NEED1("weight needs argument\n");
109818334Speter			fs->par[0] = strtol(av[0], &end, 0);
109918334Speter			ac--; av++;
110018334Speter			break;
110118334Speter
110290075Sobrien		case TOK_LMAX:
110318334Speter			NEED(fs, "lmax is only for flowsets");
110418334Speter			NEED1("lmax needs argument\n");
110518334Speter			fs->par[1] = strtol(av[0], &end, 0);
110618334Speter			ac--; av++;
110718334Speter			break;
110818334Speter
110918334Speter		case TOK_PRI:
111018334Speter			NEED(fs, "priority is only for flowsets");
111118334Speter			NEED1("priority needs argument\n");
111218334Speter			fs->par[2] = strtol(av[0], &end, 0);
111318334Speter			ac--; av++;
111418334Speter			break;
111518334Speter
111618334Speter		case TOK_SCHED:
111718334Speter		case TOK_PIPE:
111818334Speter			NEED(fs, "pipe/sched");
111918334Speter			NEED1("pipe/link/sched needs number\n");
112018334Speter			fs->sched_nr = strtoul(av[0], &end, 0);
112118334Speter			ac--; av++;
112218334Speter			break;
112318334Speter
112418334Speter		case TOK_PROFILE:
112518334Speter			NEED((!pf), "profile already set");
112618334Speter			NEED(p, "profile");
112718334Speter		    {
112818334Speter			NEED1("extra delay needs the file name\n");
112918334Speter			pf = o_next(&buf, sizeof(*pf), DN_PROFILE);
113050397Sobrien			load_extra_delays(av[0], pf, p); //XXX can't fail?
113150397Sobrien			--ac; ++av;
113250397Sobrien		    }
113318334Speter			break;
113418334Speter
113518334Speter		case TOK_BURST:
113618334Speter			NEED(p, "burst");
113718334Speter			NEED1("burst needs argument\n");
113818334Speter			errno = 0;
113918334Speter			if (expand_number(av[0], &p->burst) < 0)
114090075Sobrien				if (errno != ERANGE)
114152284Sobrien					errx(EX_DATAERR,
114252284Sobrien					    "burst: invalid argument");
1143169689Skan			if (errno || p->burst > (1ULL << 48) - 1)
114418334Speter				errx(EX_DATAERR,
1145169689Skan				    "burst: out of range (0..2^48-1)");
1146169689Skan			ac--; av++;
1147169689Skan			break;
1148169689Skan
1149169689Skan		default:
115018334Speter			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
115152284Sobrien		}
115252284Sobrien	}
1153169689Skan
115452284Sobrien	/* check validity of parameters */
115552284Sobrien	if (p) {
115652284Sobrien		if (p->delay > 10000)
115752284Sobrien			errx(EX_DATAERR, "delay must be < 10000");
115852284Sobrien		if (p->bandwidth == -1)
115952284Sobrien			p->bandwidth = 0;
116052284Sobrien	}
116152284Sobrien	if (fs) {
116252284Sobrien		/* XXX accept a 0 scheduler to keep the default */
116352284Sobrien	    if (fs->flags & DN_QSIZE_BYTES) {
116452284Sobrien		size_t len;
116552284Sobrien		long limit;
116652284Sobrien
116752284Sobrien		len = sizeof(limit);
116852284Sobrien		if (sysctlbyname("net.inet.ip.dummynet.pipe_byte_limit",
116990075Sobrien			&limit, &len, NULL, 0) == -1)
117052284Sobrien			limit = 1024*1024;
117152284Sobrien		if (fs->qsize > limit)
117252284Sobrien			errx(EX_DATAERR, "queue size must be < %ldB", limit);
117318334Speter	    } else {
117418334Speter		size_t len;
117518334Speter		long limit;
1176132718Skan
117718334Speter		len = sizeof(limit);
117818334Speter		if (sysctlbyname("net.inet.ip.dummynet.pipe_slot_limit",
117990075Sobrien			&limit, &len, NULL, 0) == -1)
1180169689Skan			limit = 100;
118190075Sobrien		if (fs->qsize > limit)
118290075Sobrien			errx(EX_DATAERR, "2 <= queue size <= %ld", limit);
118390075Sobrien	    }
118490075Sobrien
118590075Sobrien	    if ((fs->flags & DN_IS_ECN) && !(fs->flags & DN_IS_RED))
118690075Sobrien		errx(EX_USAGE, "enable red/gred for ECN");
118790075Sobrien
118890075Sobrien	    if (fs->flags & DN_IS_RED) {
118990075Sobrien		size_t len;
119018334Speter		int lookup_depth, avg_pkt_size;
119118334Speter
119218334Speter		if (!(fs->flags & DN_IS_ECN) && (fs->min_th >= fs->max_th))
119318334Speter		    errx(EX_DATAERR, "min_th %d must be < than max_th %d",
119418334Speter			fs->min_th, fs->max_th);
119518334Speter		else if ((fs->flags & DN_IS_ECN) && (fs->min_th > fs->max_th))
119618334Speter		    errx(EX_DATAERR, "min_th %d must be =< than max_th %d",
119718334Speter			fs->min_th, fs->max_th);
119818334Speter
119990075Sobrien		if (fs->max_th == 0)
120090075Sobrien		    errx(EX_DATAERR, "max_th must be > 0");
120190075Sobrien
120290075Sobrien		len = sizeof(int);
120318334Speter		if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
120490075Sobrien			&lookup_depth, &len, NULL, 0) == -1)
120518334Speter			lookup_depth = 256;
120690075Sobrien		if (lookup_depth == 0)
120718334Speter		    errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
120818334Speter			" must be greater than zero");
120918334Speter
121090075Sobrien		len = sizeof(int);
121118334Speter		if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
121218334Speter			&avg_pkt_size, &len, NULL, 0) == -1)
121390075Sobrien			avg_pkt_size = 512;
121418334Speter
121518334Speter		if (avg_pkt_size == 0)
121618334Speter			errx(EX_DATAERR,
121790075Sobrien			    "net.inet.ip.dummynet.red_avg_pkt_size must"
121818334Speter			    " be greater than zero");
121918334Speter
122018334Speter#if 0 /* the following computation is now done in the kernel */
122118334Speter		/*
122218334Speter		 * Ticks needed for sending a medium-sized packet.
1223132718Skan		 * Unfortunately, when we are configuring a WF2Q+ queue, we
122418334Speter		 * do not have bandwidth information, because that is stored
122518334Speter		 * in the parent pipe, and also we have multiple queues
122618334Speter		 * competing for it. So we set s=0, which is not very
122718334Speter		 * correct. But on the other hand, why do we want RED with
122818334Speter		 * WF2Q+ ?
122918334Speter		 */
123018334Speter		if (p.bandwidth==0) /* this is a WF2Q+ queue */
123118334Speter			s = 0;
123250397Sobrien		else
123350397Sobrien			s = (double)ck.hz * avg_pkt_size * 8 / p.bandwidth;
123450397Sobrien		/*
123518334Speter		 * max idle time (in ticks) before avg queue size becomes 0.
123618334Speter		 * NOTA:  (3/w_q) is approx the value x so that
123718334Speter		 * (1-w_q)^x < 10^-3.
1238132718Skan		 */
123918334Speter		w_q = ((double)fs->w_q) / (1 << SCALE_RED);
124018334Speter		idle = s * 3. / w_q;
124118334Speter		fs->lookup_step = (int)idle / lookup_depth;
124218334Speter		if (!fs->lookup_step)
124318334Speter			fs->lookup_step = 1;
124418334Speter		weight = 1 - w_q;
124518334Speter		for (t = fs->lookup_step; t > 1; --t)
124618334Speter			weight *= 1 - w_q;
124718334Speter		fs->lookup_weight = (int)(weight * (1 << SCALE_RED));
124818334Speter#endif /* code moved in the kernel */
124918334Speter	    }
125018334Speter	}
125118334Speter
1252132718Skan	i = do_cmd(IP_DUMMYNET3, base, (char *)buf - (char *)base);
125318334Speter
125418334Speter	if (i)
125518334Speter		err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
125618334Speter}
125718334Speter
125890075Sobrienvoid
125918334Speterdummynet_flush(void)
126018334Speter{
126118334Speter	struct dn_id oid;
126218334Speter	oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION);
126318334Speter	do_cmd(IP_DUMMYNET3, &oid, oid.len);
126418334Speter}
126550397Sobrien
126650397Sobrien/* Parse input for 'ipfw [pipe|sched|queue] show [range list]'
126790075Sobrien * Returns the number of ranges, and possibly stores them
126890075Sobrien * in the array v of size len.
126918334Speter */
127018334Speterstatic int
127118334Speterparse_range(int ac, char *av[], uint32_t *v, int len)
127218334Speter{
127318334Speter	int n = 0;
127418334Speter	char *endptr, *s;
127518334Speter	uint32_t base[2];
127618334Speter
127718334Speter	if (v == NULL || len < 2) {
127818334Speter		v = base;
127918334Speter		len = 2;
128018334Speter	}
128118334Speter
128218334Speter	for (s = *av; s != NULL; av++, ac--) {
128318334Speter		v[0] = strtoul(s, &endptr, 10);
128418334Speter		v[1] = (*endptr != '-') ? v[0] :
1285169689Skan			 strtoul(endptr+1, &endptr, 10);
128618334Speter		if (*endptr == '\0') { /* prepare for next round */
128718334Speter			s = (ac > 0) ? *(av+1) : NULL;
128818334Speter		} else {
128918334Speter			if (*endptr != ',') {
129018334Speter				warn("invalid number: %s", s);
129118334Speter				s = ++endptr;
129218334Speter				continue;
129318334Speter			}
129418334Speter			/* continue processing from here */
129518334Speter			s = ++endptr;
129618334Speter			ac++;
129718334Speter			av--;
129818334Speter		}
129918334Speter		if (v[1] < v[0] ||
130018334Speter			v[1] >= DN_MAX_ID-1 ||
130118334Speter			v[1] >= DN_MAX_ID-1) {
130218334Speter			continue; /* invalid entry */
130318334Speter		}
1304169689Skan		n++;
130518334Speter		/* translate if 'pipe list' */
130618334Speter		if (co.do_pipe == 1) {
130718334Speter			v[0] += DN_MAX_ID;
130818334Speter			v[1] += DN_MAX_ID;
130918334Speter		}
131018334Speter		v = (n*2 < len) ? v + 2 : base;
131118334Speter	}
131218334Speter	return n;
131318334Speter}
131418334Speter
131518334Speter/* main entry point for dummynet list functions. co.do_pipe indicates
131618334Speter * which function we want to support.
131718334Speter * av may contain filtering arguments, either individual entries
131818334Speter * or ranges, or lists (space or commas are valid separators).
131918334Speter * Format for a range can be n1-n2 or n3 n4 n5 ...
132018334Speter * In a range n1 must be <= n2, otherwise the range is ignored.
132118334Speter * A number 'n4' is translate in a range 'n4-n4'
132218334Speter * All number must be > 0 and < DN_MAX_ID-1
132318334Speter */
1324169689Skanvoid
132518334Speterdummynet_list(int ac, char *av[], int show_counters)
132618334Speter{
132718334Speter	struct dn_id *oid, *x = NULL;
132818334Speter	int ret, i;
132918334Speter	int n; 		/* # of ranges */
133018334Speter	u_int buflen, l;
133118334Speter	u_int max_size;	/* largest obj passed up */
133218334Speter
133390075Sobrien	(void)show_counters;	// XXX unused, but we should use it.
133418334Speter	ac--;
133518334Speter	av++; 		/* skip 'list' | 'show' word */
133618334Speter
133718334Speter	n = parse_range(ac, av, NULL, 0);	/* Count # of ranges. */
133818334Speter
133918334Speter	/* Allocate space to store ranges */
134018334Speter	l = sizeof(*oid) + sizeof(uint32_t) * n * 2;
134118334Speter	oid = safe_calloc(1, l);
134218334Speter	oid_fill(oid, l, DN_CMD_GET, DN_API_VERSION);
134350397Sobrien
134418334Speter	if (n > 0)	/* store ranges in idx */
134518334Speter		parse_range(ac, av, (uint32_t *)(oid + 1), n*2);
1346169689Skan	/*
134718334Speter	 * Compute the size of the largest object returned. If the
134818334Speter	 * response leaves at least this much spare space in the
134918334Speter	 * buffer, then surely the response is complete; otherwise
135018334Speter	 * there might be a risk of truncation and we will need to
135118334Speter	 * retry with a larger buffer.
135218334Speter	 * XXX don't bother with smaller structs.
135318334Speter	 */
135418334Speter	max_size = sizeof(struct dn_fs);
1355169689Skan	if (max_size < sizeof(struct dn_sch))
1356169689Skan		max_size = sizeof(struct dn_sch);
135718334Speter	if (max_size < sizeof(struct dn_flow))
135818334Speter		max_size = sizeof(struct dn_flow);
135918334Speter
136018334Speter	switch (co.do_pipe) {
136118334Speter	case 1:
136218334Speter		oid->subtype = DN_LINK;	/* list pipe */
136318334Speter		break;
136418334Speter	case 2:
136518334Speter		oid->subtype = DN_FS;	/* list queue */
136650397Sobrien		break;
136718334Speter	case 3:
136818334Speter		oid->subtype = DN_SCH;	/* list sched */
136918334Speter		break;
137018334Speter	}
137118334Speter
137218334Speter	/*
137318334Speter	 * Ask the kernel an estimate of the required space (result
137418334Speter	 * in oid.id), unless we are requesting a subset of objects,
137518334Speter	 * in which case the kernel does not give an exact answer.
137618334Speter	 * In any case, space might grow in the meantime due to the
137718334Speter	 * creation of new queues, so we must be prepared to retry.
137818334Speter	 */
137918334Speter	if (n > 0) {
138018334Speter		buflen = 4*1024;
138118334Speter	} else {
138218334Speter		ret = do_cmd(-IP_DUMMYNET3, oid, (uintptr_t)&l);
138318334Speter		if (ret != 0 || oid->id <= sizeof(*oid))
1384132718Skan			goto done;
138518334Speter		buflen = oid->id + max_size;
138618334Speter		oid->len = sizeof(*oid); /* restore */
138718334Speter	}
138818334Speter	/* Try a few times, until the buffer fits */
138990075Sobrien	for (i = 0; i < 20; i++) {
139018334Speter		l = buflen;
139118334Speter		x = safe_realloc(x, l);
139218334Speter		bcopy(oid, x, oid->len);
1393169689Skan		ret = do_cmd(-IP_DUMMYNET3, x, (uintptr_t)&l);
139418334Speter		if (ret != 0 || x->id <= sizeof(*oid))
139518334Speter			goto done; /* no response */
139618334Speter		if (l + max_size <= buflen)
139718334Speter			break; /* ok */
139818334Speter		buflen *= 2;	 /* double for next attempt */
139918334Speter	}
140018334Speter	list_pipes(x, O_NEXT(x, l));
140118334Speterdone:
140250397Sobrien	if (x)
140318334Speter		free(x);
140418334Speter	free(oid);
140518334Speter}
1406169689Skan