dummynet.c revision 200056
1187769Sluigi/*
2187769Sluigi * Copyright (c) 2002-2003 Luigi Rizzo
3187769Sluigi * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
4187769Sluigi * Copyright (c) 1994 Ugen J.S.Antsilevich
5187769Sluigi *
6187769Sluigi * Idea and grammar partially left from:
7187769Sluigi * Copyright (c) 1993 Daniel Boulet
8187769Sluigi *
9187769Sluigi * Redistribution and use in source forms, with and without modification,
10187769Sluigi * are permitted provided that this entire comment appears intact.
11187769Sluigi *
12187769Sluigi * Redistribution in binary form may occur without any restrictions.
13187769Sluigi * Obviously, it would be nice if you gave credit where credit is due
14187769Sluigi * but requiring it would be too onerous.
15187769Sluigi *
16187769Sluigi * This software is provided ``AS IS'' without any warranties of any kind.
17187769Sluigi *
18187769Sluigi * NEW command line interface for IP firewall facility
19187769Sluigi *
20187769Sluigi * $FreeBSD: head/sbin/ipfw/dummynet.c 200056 2009-12-03 12:23:48Z luigi $
21187769Sluigi *
22187769Sluigi * dummynet support
23187769Sluigi */
24187769Sluigi
25187769Sluigi#include <sys/types.h>
26187769Sluigi#include <sys/socket.h>
27187983Sluigi#include <sys/queue.h>
28187983Sluigi/* XXX there are several sysctl leftover here */
29187769Sluigi#include <sys/sysctl.h>
30187769Sluigi
31187769Sluigi#include "ipfw2.h"
32187769Sluigi
33187769Sluigi#include <ctype.h>
34187769Sluigi#include <err.h>
35194930Soleg#include <errno.h>
36194930Soleg#include <libutil.h>
37187769Sluigi#include <netdb.h>
38187769Sluigi#include <stdio.h>
39187769Sluigi#include <stdlib.h>
40187769Sluigi#include <string.h>
41187769Sluigi#include <sysexits.h>
42187769Sluigi
43187769Sluigi#include <net/if.h>
44187769Sluigi#include <netinet/in.h>
45187769Sluigi#include <netinet/ip_fw.h>
46187769Sluigi#include <netinet/ip_dummynet.h>
47187983Sluigi#include <arpa/inet.h>	/* inet_ntoa */
48187769Sluigi
49187769Sluigistatic struct _s_x dummynet_params[] = {
50187769Sluigi	{ "plr",		TOK_PLR },
51187769Sluigi	{ "noerror",		TOK_NOERROR },
52187769Sluigi	{ "buckets",		TOK_BUCKETS },
53187769Sluigi	{ "dst-ip",		TOK_DSTIP },
54187769Sluigi	{ "src-ip",		TOK_SRCIP },
55187769Sluigi	{ "dst-port",		TOK_DSTPORT },
56187769Sluigi	{ "src-port",		TOK_SRCPORT },
57187769Sluigi	{ "proto",		TOK_PROTO },
58187769Sluigi	{ "weight",		TOK_WEIGHT },
59187769Sluigi	{ "all",		TOK_ALL },
60187769Sluigi	{ "mask",		TOK_MASK },
61187769Sluigi	{ "droptail",		TOK_DROPTAIL },
62187769Sluigi	{ "red",		TOK_RED },
63187769Sluigi	{ "gred",		TOK_GRED },
64187769Sluigi	{ "bw",			TOK_BW },
65187769Sluigi	{ "bandwidth",		TOK_BW },
66187769Sluigi	{ "delay",		TOK_DELAY },
67187769Sluigi	{ "pipe",		TOK_PIPE },
68187769Sluigi	{ "queue",		TOK_QUEUE },
69187769Sluigi	{ "flow-id",		TOK_FLOWID},
70187769Sluigi	{ "dst-ipv6",		TOK_DSTIP6},
71187769Sluigi	{ "dst-ip6",		TOK_DSTIP6},
72187769Sluigi	{ "src-ipv6",		TOK_SRCIP6},
73187769Sluigi	{ "src-ip6",		TOK_SRCIP6},
74190865Sluigi	{ "profile",		TOK_PIPE_PROFILE},
75194930Soleg	{ "burst",		TOK_BURST},
76187769Sluigi	{ "dummynet-params",	TOK_NULL },
77187769Sluigi	{ NULL, 0 }	/* terminator */
78187769Sluigi};
79187769Sluigi
80187769Sluigistatic int
81200056Sluigisort_q(void *arg, const void *pa, const void *pb)
82187769Sluigi{
83187769Sluigi	int rev = (co.do_sort < 0);
84187769Sluigi	int field = rev ? -co.do_sort : co.do_sort;
85187769Sluigi	long long res = 0;
86187769Sluigi	const struct dn_flow_queue *a = pa;
87187769Sluigi	const struct dn_flow_queue *b = pb;
88187769Sluigi
89187769Sluigi	switch (field) {
90187769Sluigi	case 1: /* pkts */
91187769Sluigi		res = a->len - b->len;
92187769Sluigi		break;
93187769Sluigi	case 2: /* bytes */
94187769Sluigi		res = a->len_bytes - b->len_bytes;
95187769Sluigi		break;
96187769Sluigi
97187769Sluigi	case 3: /* tot pkts */
98187769Sluigi		res = a->tot_pkts - b->tot_pkts;
99187769Sluigi		break;
100187769Sluigi
101187769Sluigi	case 4: /* tot bytes */
102187769Sluigi		res = a->tot_bytes - b->tot_bytes;
103187769Sluigi		break;
104187769Sluigi	}
105187769Sluigi	if (res < 0)
106187769Sluigi		res = -1;
107187769Sluigi	if (res > 0)
108187769Sluigi		res = 1;
109187769Sluigi	return (int)(rev ? res : -res);
110187769Sluigi}
111187769Sluigi
112187769Sluigistatic void
113187769Sluigilist_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
114187769Sluigi{
115187769Sluigi	int l;
116187769Sluigi	int index_printed, indexes = 0;
117187769Sluigi	char buff[255];
118187769Sluigi	struct protoent *pe;
119187769Sluigi
120187769Sluigi	if (fs->rq_elements == 0)
121187769Sluigi		return;
122187769Sluigi
123187769Sluigi	if (co.do_sort != 0)
124200056Sluigi		qsort_r(q, fs->rq_elements, sizeof *q, NULL, sort_q);
125187769Sluigi
126187769Sluigi	/* Print IPv4 flows */
127187769Sluigi	index_printed = 0;
128187769Sluigi	for (l = 0; l < fs->rq_elements; l++) {
129187769Sluigi		struct in_addr ina;
130187769Sluigi
131187769Sluigi		/* XXX: Should check for IPv4 flows */
132187769Sluigi		if (IS_IP6_FLOW_ID(&(q[l].id)))
133187769Sluigi			continue;
134187769Sluigi
135187769Sluigi		if (!index_printed) {
136187769Sluigi			index_printed = 1;
137187769Sluigi			if (indexes > 0)	/* currently a no-op */
138187769Sluigi				printf("\n");
139187769Sluigi			indexes++;
140187769Sluigi			printf("    "
141187769Sluigi			    "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
142187769Sluigi			    fs->flow_mask.proto,
143187769Sluigi			    fs->flow_mask.src_ip, fs->flow_mask.src_port,
144187769Sluigi			    fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
145187769Sluigi
146187769Sluigi			printf("BKT Prot ___Source IP/port____ "
147187769Sluigi			    "____Dest. IP/port____ "
148187769Sluigi			    "Tot_pkt/bytes Pkt/Byte Drp\n");
149187769Sluigi		}
150187769Sluigi
151187769Sluigi		printf("%3d ", q[l].hash_slot);
152187769Sluigi		pe = getprotobynumber(q[l].id.proto);
153187769Sluigi		if (pe)
154187769Sluigi			printf("%-4s ", pe->p_name);
155187769Sluigi		else
156187769Sluigi			printf("%4u ", q[l].id.proto);
157187769Sluigi		ina.s_addr = htonl(q[l].id.src_ip);
158187769Sluigi		printf("%15s/%-5d ",
159187769Sluigi		    inet_ntoa(ina), q[l].id.src_port);
160187769Sluigi		ina.s_addr = htonl(q[l].id.dst_ip);
161187769Sluigi		printf("%15s/%-5d ",
162187769Sluigi		    inet_ntoa(ina), q[l].id.dst_port);
163187787Sluigi		printf("%4llu %8llu %2u %4u %3u\n",
164187787Sluigi		    align_uint64(&q[l].tot_pkts),
165187787Sluigi		    align_uint64(&q[l].tot_bytes),
166187769Sluigi		    q[l].len, q[l].len_bytes, q[l].drops);
167187769Sluigi		if (co.verbose)
168187787Sluigi			printf("   S %20llu  F %20llu\n",
169187787Sluigi			    align_uint64(&q[l].S), align_uint64(&q[l].F));
170187769Sluigi	}
171187769Sluigi
172187769Sluigi	/* Print IPv6 flows */
173187769Sluigi	index_printed = 0;
174187769Sluigi	for (l = 0; l < fs->rq_elements; l++) {
175187769Sluigi		if (!IS_IP6_FLOW_ID(&(q[l].id)))
176187769Sluigi			continue;
177187769Sluigi
178187769Sluigi		if (!index_printed) {
179187769Sluigi			index_printed = 1;
180187769Sluigi			if (indexes > 0)
181187769Sluigi				printf("\n");
182187769Sluigi			indexes++;
183187769Sluigi			printf("\n        mask: proto: 0x%02x, flow_id: 0x%08x,  ",
184187769Sluigi			    fs->flow_mask.proto, fs->flow_mask.flow_id6);
185187769Sluigi			inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6),
186187769Sluigi			    buff, sizeof(buff));
187187769Sluigi			printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port);
188187769Sluigi			inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6),
189187769Sluigi			    buff, sizeof(buff) );
190187769Sluigi			printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port);
191187769Sluigi
192187769Sluigi			printf("BKT ___Prot___ _flow-id_ "
193187769Sluigi			    "______________Source IPv6/port_______________ "
194187769Sluigi			    "_______________Dest. IPv6/port_______________ "
195187769Sluigi			    "Tot_pkt/bytes Pkt/Byte Drp\n");
196187769Sluigi		}
197187769Sluigi		printf("%3d ", q[l].hash_slot);
198187769Sluigi		pe = getprotobynumber(q[l].id.proto);
199187769Sluigi		if (pe != NULL)
200187769Sluigi			printf("%9s ", pe->p_name);
201187769Sluigi		else
202187769Sluigi			printf("%9u ", q[l].id.proto);
203187769Sluigi		printf("%7d  %39s/%-5d ", q[l].id.flow_id6,
204187769Sluigi		    inet_ntop(AF_INET6, &(q[l].id.src_ip6), buff, sizeof(buff)),
205187769Sluigi		    q[l].id.src_port);
206187769Sluigi		printf(" %39s/%-5d ",
207187769Sluigi		    inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)),
208187769Sluigi		    q[l].id.dst_port);
209187787Sluigi		printf(" %4llu %8llu %2u %4u %3u\n",
210187787Sluigi		    align_uint64(&q[l].tot_pkts),
211187787Sluigi		    align_uint64(&q[l].tot_bytes),
212187769Sluigi		    q[l].len, q[l].len_bytes, q[l].drops);
213187769Sluigi		if (co.verbose)
214187787Sluigi			printf("   S %20llu  F %20llu\n",
215187787Sluigi			    align_uint64(&q[l].S),
216187787Sluigi			    align_uint64(&q[l].F));
217187769Sluigi	}
218187769Sluigi}
219187769Sluigi
220187769Sluigistatic void
221187769Sluigiprint_flowset_parms(struct dn_flow_set *fs, char *prefix)
222187769Sluigi{
223187769Sluigi	int l;
224187769Sluigi	char qs[30];
225187769Sluigi	char plr[30];
226187769Sluigi	char red[90];	/* Display RED parameters */
227187769Sluigi
228187769Sluigi	l = fs->qsize;
229187769Sluigi	if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
230187769Sluigi		if (l >= 8192)
231187769Sluigi			sprintf(qs, "%d KB", l / 1024);
232187769Sluigi		else
233187769Sluigi			sprintf(qs, "%d B", l);
234187769Sluigi	} else
235187769Sluigi		sprintf(qs, "%3d sl.", l);
236187769Sluigi	if (fs->plr)
237187769Sluigi		sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
238187769Sluigi	else
239187769Sluigi		plr[0] = '\0';
240187769Sluigi	if (fs->flags_fs & DN_IS_RED)	/* RED parameters */
241187769Sluigi		sprintf(red,
242194930Soleg		    "\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
243187769Sluigi		    (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
244187769Sluigi		    1.0 * fs->w_q / (double)(1 << SCALE_RED),
245187769Sluigi		    SCALE_VAL(fs->min_th),
246187769Sluigi		    SCALE_VAL(fs->max_th),
247187769Sluigi		    1.0 * fs->max_p / (double)(1 << SCALE_RED));
248187769Sluigi	else
249187769Sluigi		sprintf(red, "droptail");
250187769Sluigi
251187769Sluigi	printf("%s %s%s %d queues (%d buckets) %s\n",
252187769Sluigi	    prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
253187769Sluigi}
254187769Sluigi
255190865Sluigistatic void
256194930Solegprint_extra_delay_parms(struct dn_pipe *p)
257190865Sluigi{
258190865Sluigi	double loss;
259190865Sluigi	if (p->samples_no <= 0)
260190865Sluigi		return;
261190865Sluigi
262190865Sluigi	loss = p->loss_level;
263190865Sluigi	loss /= p->samples_no;
264194930Soleg	printf("\t profile: name \"%s\" loss %f samples %d\n",
265194930Soleg		p->name, loss, p->samples_no);
266190865Sluigi}
267190865Sluigi
268187769Sluigivoid
269187769Sluigiipfw_list_pipes(void *data, uint nbytes, int ac, char *av[])
270187769Sluigi{
271187769Sluigi	int rulenum;
272187769Sluigi	void *next = data;
273187769Sluigi	struct dn_pipe *p = (struct dn_pipe *) data;
274187769Sluigi	struct dn_flow_set *fs;
275187769Sluigi	struct dn_flow_queue *q;
276187769Sluigi	int l;
277187769Sluigi
278187769Sluigi	if (ac > 0)
279187769Sluigi		rulenum = strtoul(*av++, NULL, 10);
280187769Sluigi	else
281187769Sluigi		rulenum = 0;
282187769Sluigi	for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) {
283187769Sluigi		double b = p->bandwidth;
284187769Sluigi		char buf[30];
285187769Sluigi		char prefix[80];
286194930Soleg		char burst[5 + 7];
287187769Sluigi
288187769Sluigi		if (SLIST_NEXT(p, next) != (struct dn_pipe *)DN_IS_PIPE)
289187769Sluigi			break;	/* done with pipes, now queues */
290187769Sluigi
291187769Sluigi		/*
292187769Sluigi		 * compute length, as pipe have variable size
293187769Sluigi		 */
294187769Sluigi		l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
295187769Sluigi		next = (char *)p + l;
296187769Sluigi		nbytes -= l;
297187769Sluigi
298187769Sluigi		if ((rulenum != 0 && rulenum != p->pipe_nr) || co.do_pipe == 2)
299187769Sluigi			continue;
300187769Sluigi
301187769Sluigi		/*
302187769Sluigi		 * Print rate (or clocking interface)
303187769Sluigi		 */
304187769Sluigi		if (p->if_name[0] != '\0')
305187769Sluigi			sprintf(buf, "%s", p->if_name);
306187769Sluigi		else if (b == 0)
307187769Sluigi			sprintf(buf, "unlimited");
308187769Sluigi		else if (b >= 1000000)
309187769Sluigi			sprintf(buf, "%7.3f Mbit/s", b/1000000);
310187769Sluigi		else if (b >= 1000)
311187769Sluigi			sprintf(buf, "%7.3f Kbit/s", b/1000);
312187769Sluigi		else
313187769Sluigi			sprintf(buf, "%7.3f bit/s ", b);
314187769Sluigi
315187769Sluigi		sprintf(prefix, "%05d: %s %4d ms ",
316187769Sluigi		    p->pipe_nr, buf, p->delay);
317190865Sluigi
318187769Sluigi		print_flowset_parms(&(p->fs), prefix);
319187769Sluigi
320194930Soleg		if (humanize_number(burst, sizeof(burst), p->burst,
321194930Soleg		    "Byte", HN_AUTOSCALE, 0) < 0 || co.verbose)
322194930Soleg			printf("\t burst: %ju Byte\n", p->burst);
323194930Soleg		else
324194930Soleg			printf("\t burst: %s\n", burst);
325194930Soleg
326194930Soleg		print_extra_delay_parms(p);
327194930Soleg
328187769Sluigi		q = (struct dn_flow_queue *)(p+1);
329187769Sluigi		list_queues(&(p->fs), q);
330187769Sluigi	}
331187769Sluigi	for (fs = next; nbytes >= sizeof *fs; fs = next) {
332187769Sluigi		char prefix[80];
333187769Sluigi
334187769Sluigi		if (SLIST_NEXT(fs, next) != (struct dn_flow_set *)DN_IS_QUEUE)
335187769Sluigi			break;
336187769Sluigi		l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
337187769Sluigi		next = (char *)fs + l;
338187769Sluigi		nbytes -= l;
339187769Sluigi
340187769Sluigi		if (rulenum != 0 && ((rulenum != fs->fs_nr && co.do_pipe == 2) ||
341187769Sluigi		    (rulenum != fs->parent_nr && co.do_pipe == 1))) {
342187769Sluigi			continue;
343187769Sluigi		}
344187769Sluigi
345187769Sluigi		q = (struct dn_flow_queue *)(fs+1);
346187769Sluigi		sprintf(prefix, "q%05d: weight %d pipe %d ",
347187769Sluigi		    fs->fs_nr, fs->weight, fs->parent_nr);
348187769Sluigi		print_flowset_parms(fs, prefix);
349187769Sluigi		list_queues(fs, q);
350187769Sluigi	}
351187769Sluigi}
352187769Sluigi
353187769Sluigi/*
354187769Sluigi * Delete pipe or queue i
355187769Sluigi */
356187769Sluigiint
357187771Sluigiipfw_delete_pipe(int pipe_or_queue, int i)
358187769Sluigi{
359187769Sluigi	struct dn_pipe p;
360187769Sluigi
361187769Sluigi	memset(&p, 0, sizeof p);
362187769Sluigi	if (pipe_or_queue == 1)
363187769Sluigi		p.pipe_nr = i;		/* pipe */
364187769Sluigi	else
365187769Sluigi		p.fs.fs_nr = i;		/* queue */
366187769Sluigi	i = do_cmd(IP_DUMMYNET_DEL, &p, sizeof p);
367187769Sluigi	if (i) {
368187769Sluigi		i = 1;
369187769Sluigi		warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", i);
370187769Sluigi	}
371187769Sluigi	return i;
372187769Sluigi}
373187769Sluigi
374190865Sluigi/*
375190865Sluigi * Code to parse delay profiles.
376190865Sluigi *
377190865Sluigi * Some link types introduce extra delays in the transmission
378190865Sluigi * of a packet, e.g. because of MAC level framing, contention on
379190865Sluigi * the use of the channel, MAC level retransmissions and so on.
380190865Sluigi * From our point of view, the channel is effectively unavailable
381190865Sluigi * for this extra time, which is constant or variable depending
382190865Sluigi * on the link type. Additionally, packets may be dropped after this
383190865Sluigi * time (e.g. on a wireless link after too many retransmissions).
384190865Sluigi * We can model the additional delay with an empirical curve
385190865Sluigi * that represents its distribution.
386190865Sluigi *
387190865Sluigi *	cumulative probability
388190865Sluigi *	1.0 ^
389190865Sluigi *	    |
390190865Sluigi *	L   +-- loss-level          x
391190865Sluigi *	    |                 ******
392190865Sluigi *	    |                *
393190865Sluigi *	    |           *****
394190865Sluigi *	    |          *
395190865Sluigi *	    |        **
396190865Sluigi *	    |       *
397190865Sluigi *	    +-------*------------------->
398190865Sluigi *			delay
399190865Sluigi *
400190865Sluigi * The empirical curve may have both vertical and horizontal lines.
401190865Sluigi * Vertical lines represent constant delay for a range of
402190865Sluigi * probabilities; horizontal lines correspond to a discontinuty
403190865Sluigi * in the delay distribution: the pipe will use the largest delay
404190865Sluigi * for a given probability.
405190865Sluigi *
406190865Sluigi * To pass the curve to dummynet, we must store the parameters
407190865Sluigi * in a file as described below, and issue the command
408190865Sluigi *
409190865Sluigi *      ipfw pipe <n> config ... bw XXX profile <filename> ...
410190865Sluigi *
411190865Sluigi * The file format is the following, with whitespace acting as
412190865Sluigi * a separator and '#' indicating the beginning a comment:
413190865Sluigi *
414190865Sluigi *	samples N
415190865Sluigi *		the number of samples used in the internal
416190865Sluigi *		representation (2..1024; default 100);
417190865Sluigi *
418190865Sluigi *	loss-level L
419190865Sluigi *		The probability above which packets are lost.
420190865Sluigi *               (0.0 <= L <= 1.0, default 1.0 i.e. no loss);
421190865Sluigi *
422190865Sluigi *	name identifier
423190865Sluigi *		Optional a name (listed by "ipfw pipe show")
424190865Sluigi *		to identify the distribution;
425190865Sluigi *
426190865Sluigi *	"delay prob" | "prob delay"
427190865Sluigi *		One of these two lines is mandatory and defines
428190865Sluigi *		the format of the following lines with data points.
429190865Sluigi *
430190865Sluigi *	XXX YYY
431190865Sluigi *		2 or more lines representing points in the curve,
432190865Sluigi *		with either delay or probability first, according
433190865Sluigi *		to the chosen format.
434190865Sluigi *		The unit for delay is milliseconds.
435190865Sluigi *
436190865Sluigi * Data points does not need to be ordered or equal to the number
437190865Sluigi * specified in the "samples" line. ipfw will sort and interpolate
438190865Sluigi * the curve as needed.
439190865Sluigi *
440190865Sluigi * Example of a profile file:
441190865Sluigi
442190865Sluigi        name    bla_bla_bla
443190865Sluigi        samples 100
444190865Sluigi        loss-level    0.86
445190865Sluigi        prob    delay
446190865Sluigi        0       200	# minimum overhead is 200ms
447190865Sluigi        0.5     200
448190865Sluigi        0.5     300
449190865Sluigi        0.8     1000
450190865Sluigi        0.9     1300
451190865Sluigi        1       1300
452190865Sluigi
453190865Sluigi * Internally, we will convert the curve to a fixed number of
454190865Sluigi * samples, and when it is time to transmit a packet we will
455190865Sluigi * model the extra delay as extra bits in the packet.
456190865Sluigi *
457190865Sluigi */
458190865Sluigi
459190865Sluigi#define ED_MAX_LINE_LEN	256+ED_MAX_NAME_LEN
460190865Sluigi#define ED_TOK_SAMPLES	"samples"
461190865Sluigi#define ED_TOK_LOSS	"loss-level"
462190865Sluigi#define ED_TOK_NAME	"name"
463190865Sluigi#define ED_TOK_DELAY	"delay"
464190865Sluigi#define ED_TOK_PROB	"prob"
465193715Sluigi#define ED_TOK_BW	"bw"
466190865Sluigi#define ED_SEPARATORS	" \t\n"
467190865Sluigi#define ED_MIN_SAMPLES_NO	2
468190865Sluigi
469190865Sluigi/*
470190865Sluigi * returns 1 if s is a non-negative number, with at least one '.'
471190865Sluigi */
472190865Sluigistatic int
473190865Sluigiis_valid_number(const char *s)
474190865Sluigi{
475190865Sluigi	int i, dots_found = 0;
476190865Sluigi	int len = strlen(s);
477190865Sluigi
478190865Sluigi	for (i = 0; i<len; ++i)
479190865Sluigi		if (!isdigit(s[i]) && (s[i] !='.' || ++dots_found > 1))
480190865Sluigi			return 0;
481190865Sluigi	return 1;
482190865Sluigi}
483190865Sluigi
484193715Sluigi/*
485193715Sluigi * Take as input a string describing a bandwidth value
486193715Sluigi * and return the numeric bandwidth value.
487193715Sluigi * set clocking interface or bandwidth value
488193715Sluigi */
489200056Sluigistatic void
490193715Sluigiread_bandwidth(char *arg, int *bandwidth, char *if_name, int namelen)
491193715Sluigi{
492193715Sluigi	if (*bandwidth != -1)
493193715Sluigi		warn("duplicate token, override bandwidth value!");
494193715Sluigi
495193715Sluigi	if (arg[0] >= 'a' && arg[0] <= 'z') {
496193715Sluigi		if (namelen >= IFNAMSIZ)
497193715Sluigi			warn("interface name truncated");
498193715Sluigi		namelen--;
499193715Sluigi		/* interface name */
500193715Sluigi		strncpy(if_name, arg, namelen);
501193715Sluigi		if_name[namelen] = '\0';
502193715Sluigi		*bandwidth = 0;
503193715Sluigi	} else {	/* read bandwidth value */
504193715Sluigi		int bw;
505193715Sluigi		char *end = NULL;
506193715Sluigi
507193715Sluigi		bw = strtoul(arg, &end, 0);
508193715Sluigi		if (*end == 'K' || *end == 'k') {
509193715Sluigi			end++;
510193715Sluigi			bw *= 1000;
511193715Sluigi		} else if (*end == 'M') {
512193715Sluigi			end++;
513193715Sluigi			bw *= 1000000;
514193715Sluigi		}
515193715Sluigi		if ((*end == 'B' &&
516193715Sluigi			_substrcmp2(end, "Bi", "Bit/s") != 0) ||
517193715Sluigi		    _substrcmp2(end, "by", "bytes") == 0)
518193715Sluigi			bw *= 8;
519193715Sluigi
520193715Sluigi		if (bw < 0)
521193715Sluigi			errx(EX_DATAERR, "bandwidth too large");
522193715Sluigi
523193715Sluigi		*bandwidth = bw;
524193715Sluigi		if_name[0] = '\0';
525193715Sluigi	}
526193715Sluigi}
527193715Sluigi
528190865Sluigistruct point {
529190865Sluigi	double prob;
530190865Sluigi	double delay;
531190865Sluigi};
532190865Sluigi
533200056Sluigistatic int
534190865Sluigicompare_points(const void *vp1, const void *vp2)
535190865Sluigi{
536190865Sluigi	const struct point *p1 = vp1;
537190865Sluigi	const struct point *p2 = vp2;
538190865Sluigi	double res = 0;
539190865Sluigi
540190865Sluigi	res = p1->prob - p2->prob;
541190865Sluigi	if (res == 0)
542190865Sluigi		res = p1->delay - p2->delay;
543190865Sluigi	if (res < 0)
544190865Sluigi		return -1;
545190865Sluigi	else if (res > 0)
546190865Sluigi		return 1;
547190865Sluigi	else
548190865Sluigi		return 0;
549190865Sluigi}
550190865Sluigi
551190865Sluigi#define ED_EFMT(s) EX_DATAERR,"error in %s at line %d: "#s,filename,lineno
552190865Sluigi
553190865Sluigistatic void
554190865Sluigiload_extra_delays(const char *filename, struct dn_pipe *p)
555190865Sluigi{
556190865Sluigi	char    line[ED_MAX_LINE_LEN];
557190865Sluigi	FILE    *f;
558190865Sluigi	int     lineno = 0;
559190865Sluigi	int     i;
560190865Sluigi
561190865Sluigi	int     samples = -1;
562190865Sluigi	double  loss = -1.0;
563190865Sluigi	char    profile_name[ED_MAX_NAME_LEN];
564190865Sluigi	int     delay_first = -1;
565190865Sluigi	int     do_points = 0;
566190865Sluigi	struct point    points[ED_MAX_SAMPLES_NO];
567190865Sluigi	int     points_no = 0;
568190865Sluigi
569190865Sluigi	profile_name[0] = '\0';
570190865Sluigi	f = fopen(filename, "r");
571190865Sluigi	if (f == NULL)
572190865Sluigi		err(EX_UNAVAILABLE, "fopen: %s", filename);
573190865Sluigi
574190865Sluigi	while (fgets(line, ED_MAX_LINE_LEN, f)) {         /* read commands */
575190865Sluigi		char *s, *cur = line, *name = NULL, *arg = NULL;
576190865Sluigi
577190865Sluigi		++lineno;
578190865Sluigi
579190865Sluigi		/* parse the line */
580190865Sluigi		while (cur) {
581190865Sluigi			s = strsep(&cur, ED_SEPARATORS);
582190865Sluigi			if (s == NULL || *s == '#')
583190865Sluigi				break;
584190865Sluigi			if (*s == '\0')
585190865Sluigi				continue;
586190865Sluigi			if (arg)
587190865Sluigi				errx(ED_EFMT("too many arguments"));
588190865Sluigi			if (name == NULL)
589190865Sluigi				name = s;
590190865Sluigi			else
591190865Sluigi				arg = s;
592190865Sluigi		}
593190865Sluigi		if (name == NULL)	/* empty line */
594190865Sluigi			continue;
595190865Sluigi		if (arg == NULL)
596190865Sluigi			errx(ED_EFMT("missing arg for %s"), name);
597190865Sluigi
598190865Sluigi		if (!strcasecmp(name, ED_TOK_SAMPLES)) {
599190865Sluigi		    if (samples > 0)
600190865Sluigi			errx(ED_EFMT("duplicate ``samples'' line"));
601190865Sluigi		    if (atoi(arg) <=0)
602190865Sluigi			errx(ED_EFMT("invalid number of samples"));
603190865Sluigi		    samples = atoi(arg);
604190865Sluigi		    if (samples>ED_MAX_SAMPLES_NO)
605190865Sluigi			    errx(ED_EFMT("too many samples, maximum is %d"),
606190865Sluigi				ED_MAX_SAMPLES_NO);
607190865Sluigi		    do_points = 0;
608193715Sluigi		} else if (!strcasecmp(name, ED_TOK_BW)) {
609193715Sluigi		    read_bandwidth(arg, &p->bandwidth, p->if_name, sizeof(p->if_name));
610190865Sluigi		} else if (!strcasecmp(name, ED_TOK_LOSS)) {
611190865Sluigi		    if (loss != -1.0)
612190865Sluigi			errx(ED_EFMT("duplicated token: %s"), name);
613190865Sluigi		    if (!is_valid_number(arg))
614190865Sluigi			errx(ED_EFMT("invalid %s"), arg);
615190865Sluigi		    loss = atof(arg);
616190865Sluigi		    if (loss > 1)
617190865Sluigi			errx(ED_EFMT("%s greater than 1.0"), name);
618190865Sluigi		    do_points = 0;
619190865Sluigi		} else if (!strcasecmp(name, ED_TOK_NAME)) {
620190865Sluigi		    if (profile_name[0] != '\0')
621190865Sluigi			errx(ED_EFMT("duplicated token: %s"), name);
622190865Sluigi		    strncpy(profile_name, arg, sizeof(profile_name) - 1);
623190865Sluigi		    profile_name[sizeof(profile_name)-1] = '\0';
624190865Sluigi		    do_points = 0;
625190865Sluigi		} else if (!strcasecmp(name, ED_TOK_DELAY)) {
626190865Sluigi		    if (do_points)
627190865Sluigi			errx(ED_EFMT("duplicated token: %s"), name);
628190865Sluigi		    delay_first = 1;
629190865Sluigi		    do_points = 1;
630190865Sluigi		} else if (!strcasecmp(name, ED_TOK_PROB)) {
631190865Sluigi		    if (do_points)
632190865Sluigi			errx(ED_EFMT("duplicated token: %s"), name);
633190865Sluigi		    delay_first = 0;
634190865Sluigi		    do_points = 1;
635190865Sluigi		} else if (do_points) {
636190865Sluigi		    if (!is_valid_number(name) || !is_valid_number(arg))
637190865Sluigi			errx(ED_EFMT("invalid point found"));
638190865Sluigi		    if (delay_first) {
639190865Sluigi			points[points_no].delay = atof(name);
640190865Sluigi			points[points_no].prob = atof(arg);
641190865Sluigi		    } else {
642190865Sluigi			points[points_no].delay = atof(arg);
643190865Sluigi			points[points_no].prob = atof(name);
644190865Sluigi		    }
645190865Sluigi		    if (points[points_no].prob > 1.0)
646190865Sluigi			errx(ED_EFMT("probability greater than 1.0"));
647190865Sluigi		    ++points_no;
648190865Sluigi		} else {
649190865Sluigi		    errx(ED_EFMT("unrecognised command '%s'"), name);
650190865Sluigi		}
651190865Sluigi	}
652190865Sluigi
653199626Snetchild	fclose (f);
654199626Snetchild
655190865Sluigi	if (samples == -1) {
656190865Sluigi	    warnx("'%s' not found, assuming 100", ED_TOK_SAMPLES);
657190865Sluigi	    samples = 100;
658190865Sluigi	}
659190865Sluigi
660190865Sluigi	if (loss == -1.0) {
661190865Sluigi	    warnx("'%s' not found, assuming no loss", ED_TOK_LOSS);
662190865Sluigi	    loss = 1;
663190865Sluigi	}
664190865Sluigi
665190865Sluigi	/* make sure that there are enough points. */
666190865Sluigi	if (points_no < ED_MIN_SAMPLES_NO)
667190865Sluigi	    errx(ED_EFMT("too few samples, need at least %d"),
668190865Sluigi		ED_MIN_SAMPLES_NO);
669190865Sluigi
670190865Sluigi	qsort(points, points_no, sizeof(struct point), compare_points);
671190865Sluigi
672190865Sluigi	/* interpolation */
673190865Sluigi	for (i = 0; i<points_no-1; ++i) {
674190865Sluigi	    double y1 = points[i].prob * samples;
675190865Sluigi	    double x1 = points[i].delay;
676190865Sluigi	    double y2 = points[i+1].prob * samples;
677190865Sluigi	    double x2 = points[i+1].delay;
678190865Sluigi
679190865Sluigi	    int index = y1;
680190865Sluigi	    int stop = y2;
681190865Sluigi
682190865Sluigi	    if (x1 == x2) {
683190865Sluigi		for (; index<stop; ++index)
684190865Sluigi		    p->samples[index] = x1;
685190865Sluigi	    } else {
686190865Sluigi		double m = (y2-y1)/(x2-x1);
687190865Sluigi		double c = y1 - m*x1;
688190865Sluigi		for (; index<stop ; ++index)
689190865Sluigi		    p->samples[index] = (index - c)/m;
690190865Sluigi	    }
691190865Sluigi	}
692190865Sluigi	p->samples_no = samples;
693190865Sluigi	p->loss_level = loss * samples;
694190865Sluigi	strncpy(p->name, profile_name, sizeof(p->name));
695190865Sluigi}
696190865Sluigi
697187769Sluigivoid
698187769Sluigiipfw_config_pipe(int ac, char **av)
699187769Sluigi{
700190865Sluigi	int samples[ED_MAX_SAMPLES_NO];
701187769Sluigi	struct dn_pipe p;
702187769Sluigi	int i;
703187769Sluigi	char *end;
704187769Sluigi	void *par = NULL;
705187769Sluigi
706187769Sluigi	memset(&p, 0, sizeof p);
707193715Sluigi	p.bandwidth = -1;
708187769Sluigi
709187769Sluigi	av++; ac--;
710187769Sluigi	/* Pipe number */
711187769Sluigi	if (ac && isdigit(**av)) {
712187769Sluigi		i = atoi(*av); av++; ac--;
713187769Sluigi		if (co.do_pipe == 1)
714187769Sluigi			p.pipe_nr = i;
715187769Sluigi		else
716187769Sluigi			p.fs.fs_nr = i;
717187769Sluigi	}
718187769Sluigi	while (ac > 0) {
719187769Sluigi		double d;
720187769Sluigi		int tok = match_token(dummynet_params, *av);
721187769Sluigi		ac--; av++;
722187769Sluigi
723187769Sluigi		switch(tok) {
724187769Sluigi		case TOK_NOERROR:
725187769Sluigi			p.fs.flags_fs |= DN_NOERROR;
726187769Sluigi			break;
727187769Sluigi
728187769Sluigi		case TOK_PLR:
729187769Sluigi			NEED1("plr needs argument 0..1\n");
730187769Sluigi			d = strtod(av[0], NULL);
731187769Sluigi			if (d > 1)
732187769Sluigi				d = 1;
733187769Sluigi			else if (d < 0)
734187769Sluigi				d = 0;
735187769Sluigi			p.fs.plr = (int)(d*0x7fffffff);
736187769Sluigi			ac--; av++;
737187769Sluigi			break;
738187769Sluigi
739187769Sluigi		case TOK_QUEUE:
740187769Sluigi			NEED1("queue needs queue size\n");
741187769Sluigi			end = NULL;
742187769Sluigi			p.fs.qsize = strtoul(av[0], &end, 0);
743187769Sluigi			if (*end == 'K' || *end == 'k') {
744187769Sluigi				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
745187769Sluigi				p.fs.qsize *= 1024;
746187769Sluigi			} else if (*end == 'B' ||
747187769Sluigi			    _substrcmp2(end, "by", "bytes") == 0) {
748187769Sluigi				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
749187769Sluigi			}
750187769Sluigi			ac--; av++;
751187769Sluigi			break;
752187769Sluigi
753187769Sluigi		case TOK_BUCKETS:
754187769Sluigi			NEED1("buckets needs argument\n");
755187769Sluigi			p.fs.rq_size = strtoul(av[0], NULL, 0);
756187769Sluigi			ac--; av++;
757187769Sluigi			break;
758187769Sluigi
759187769Sluigi		case TOK_MASK:
760187769Sluigi			NEED1("mask needs mask specifier\n");
761187769Sluigi			/*
762187769Sluigi			 * per-flow queue, mask is dst_ip, dst_port,
763187769Sluigi			 * src_ip, src_port, proto measured in bits
764187769Sluigi			 */
765187769Sluigi			par = NULL;
766187769Sluigi
767187769Sluigi			bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask));
768187769Sluigi			end = NULL;
769187769Sluigi
770187769Sluigi			while (ac >= 1) {
771187769Sluigi			    uint32_t *p32 = NULL;
772187769Sluigi			    uint16_t *p16 = NULL;
773187769Sluigi			    uint32_t *p20 = NULL;
774187769Sluigi			    struct in6_addr *pa6 = NULL;
775187769Sluigi			    uint32_t a;
776187769Sluigi
777187769Sluigi			    tok = match_token(dummynet_params, *av);
778187769Sluigi			    ac--; av++;
779187769Sluigi			    switch(tok) {
780187769Sluigi			    case TOK_ALL:
781187769Sluigi				    /*
782187769Sluigi				     * special case, all bits significant
783187769Sluigi				     */
784187769Sluigi				    p.fs.flow_mask.dst_ip = ~0;
785187769Sluigi				    p.fs.flow_mask.src_ip = ~0;
786187769Sluigi				    p.fs.flow_mask.dst_port = ~0;
787187769Sluigi				    p.fs.flow_mask.src_port = ~0;
788187769Sluigi				    p.fs.flow_mask.proto = ~0;
789187769Sluigi				    n2mask(&(p.fs.flow_mask.dst_ip6), 128);
790187769Sluigi				    n2mask(&(p.fs.flow_mask.src_ip6), 128);
791187769Sluigi				    p.fs.flow_mask.flow_id6 = ~0;
792187769Sluigi				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
793187769Sluigi				    goto end_mask;
794187769Sluigi
795187769Sluigi			    case TOK_DSTIP:
796187769Sluigi				    p32 = &p.fs.flow_mask.dst_ip;
797187769Sluigi				    break;
798187769Sluigi
799187769Sluigi			    case TOK_SRCIP:
800187769Sluigi				    p32 = &p.fs.flow_mask.src_ip;
801187769Sluigi				    break;
802187769Sluigi
803187769Sluigi			    case TOK_DSTIP6:
804187769Sluigi				    pa6 = &(p.fs.flow_mask.dst_ip6);
805187769Sluigi				    break;
806187769Sluigi
807187769Sluigi			    case TOK_SRCIP6:
808187769Sluigi				    pa6 = &(p.fs.flow_mask.src_ip6);
809187769Sluigi				    break;
810187769Sluigi
811187769Sluigi			    case TOK_FLOWID:
812187769Sluigi				    p20 = &p.fs.flow_mask.flow_id6;
813187769Sluigi				    break;
814187769Sluigi
815187769Sluigi			    case TOK_DSTPORT:
816187769Sluigi				    p16 = &p.fs.flow_mask.dst_port;
817187769Sluigi				    break;
818187769Sluigi
819187769Sluigi			    case TOK_SRCPORT:
820187769Sluigi				    p16 = &p.fs.flow_mask.src_port;
821187769Sluigi				    break;
822187769Sluigi
823187769Sluigi			    case TOK_PROTO:
824187769Sluigi				    break;
825187769Sluigi
826187769Sluigi			    default:
827187769Sluigi				    ac++; av--; /* backtrack */
828187769Sluigi				    goto end_mask;
829187769Sluigi			    }
830187769Sluigi			    if (ac < 1)
831187769Sluigi				    errx(EX_USAGE, "mask: value missing");
832187769Sluigi			    if (*av[0] == '/') {
833187769Sluigi				    a = strtoul(av[0]+1, &end, 0);
834187769Sluigi				    if (pa6 == NULL)
835187769Sluigi					    a = (a == 32) ? ~0 : (1 << a) - 1;
836187769Sluigi			    } else
837187769Sluigi				    a = strtoul(av[0], &end, 0);
838187769Sluigi			    if (p32 != NULL)
839187769Sluigi				    *p32 = a;
840187769Sluigi			    else if (p16 != NULL) {
841187769Sluigi				    if (a > 0xFFFF)
842187769Sluigi					    errx(EX_DATAERR,
843187769Sluigi						"port mask must be 16 bit");
844187769Sluigi				    *p16 = (uint16_t)a;
845187769Sluigi			    } else if (p20 != NULL) {
846187769Sluigi				    if (a > 0xfffff)
847187769Sluigi					errx(EX_DATAERR,
848187769Sluigi					    "flow_id mask must be 20 bit");
849187769Sluigi				    *p20 = (uint32_t)a;
850187769Sluigi			    } else if (pa6 != NULL) {
851187769Sluigi				    if (a > 128)
852187769Sluigi					errx(EX_DATAERR,
853187769Sluigi					    "in6addr invalid mask len");
854187769Sluigi				    else
855187769Sluigi					n2mask(pa6, a);
856187769Sluigi			    } else {
857187769Sluigi				    if (a > 0xFF)
858187769Sluigi					    errx(EX_DATAERR,
859187769Sluigi						"proto mask must be 8 bit");
860187769Sluigi				    p.fs.flow_mask.proto = (uint8_t)a;
861187769Sluigi			    }
862187769Sluigi			    if (a != 0)
863187769Sluigi				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
864187769Sluigi			    ac--; av++;
865187769Sluigi			} /* end while, config masks */
866187769Sluigiend_mask:
867187769Sluigi			break;
868187769Sluigi
869187769Sluigi		case TOK_RED:
870187769Sluigi		case TOK_GRED:
871187769Sluigi			NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
872187769Sluigi			p.fs.flags_fs |= DN_IS_RED;
873187769Sluigi			if (tok == TOK_GRED)
874187769Sluigi				p.fs.flags_fs |= DN_IS_GENTLE_RED;
875187769Sluigi			/*
876187769Sluigi			 * the format for parameters is w_q/min_th/max_th/max_p
877187769Sluigi			 */
878187769Sluigi			if ((end = strsep(&av[0], "/"))) {
879187769Sluigi			    double w_q = strtod(end, NULL);
880187769Sluigi			    if (w_q > 1 || w_q <= 0)
881187769Sluigi				errx(EX_DATAERR, "0 < w_q <= 1");
882187769Sluigi			    p.fs.w_q = (int) (w_q * (1 << SCALE_RED));
883187769Sluigi			}
884187769Sluigi			if ((end = strsep(&av[0], "/"))) {
885187769Sluigi			    p.fs.min_th = strtoul(end, &end, 0);
886187769Sluigi			    if (*end == 'K' || *end == 'k')
887187769Sluigi				p.fs.min_th *= 1024;
888187769Sluigi			}
889187769Sluigi			if ((end = strsep(&av[0], "/"))) {
890187769Sluigi			    p.fs.max_th = strtoul(end, &end, 0);
891187769Sluigi			    if (*end == 'K' || *end == 'k')
892187769Sluigi				p.fs.max_th *= 1024;
893187769Sluigi			}
894187769Sluigi			if ((end = strsep(&av[0], "/"))) {
895187769Sluigi			    double max_p = strtod(end, NULL);
896187769Sluigi			    if (max_p > 1 || max_p <= 0)
897187769Sluigi				errx(EX_DATAERR, "0 < max_p <= 1");
898187769Sluigi			    p.fs.max_p = (int)(max_p * (1 << SCALE_RED));
899187769Sluigi			}
900187769Sluigi			ac--; av++;
901187769Sluigi			break;
902187769Sluigi
903187769Sluigi		case TOK_DROPTAIL:
904187769Sluigi			p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
905187769Sluigi			break;
906187769Sluigi
907187769Sluigi		case TOK_BW:
908187769Sluigi			NEED1("bw needs bandwidth or interface\n");
909187769Sluigi			if (co.do_pipe != 1)
910187769Sluigi			    errx(EX_DATAERR, "bandwidth only valid for pipes");
911193715Sluigi			read_bandwidth(av[0], &p.bandwidth, p.if_name, sizeof(p.if_name));
912187769Sluigi			ac--; av++;
913187769Sluigi			break;
914187769Sluigi
915187769Sluigi		case TOK_DELAY:
916187769Sluigi			if (co.do_pipe != 1)
917187769Sluigi				errx(EX_DATAERR, "delay only valid for pipes");
918187769Sluigi			NEED1("delay needs argument 0..10000ms\n");
919187769Sluigi			p.delay = strtoul(av[0], NULL, 0);
920187769Sluigi			ac--; av++;
921187769Sluigi			break;
922187769Sluigi
923187769Sluigi		case TOK_WEIGHT:
924187769Sluigi			if (co.do_pipe == 1)
925187769Sluigi				errx(EX_DATAERR,"weight only valid for queues");
926187769Sluigi			NEED1("weight needs argument 0..100\n");
927187769Sluigi			p.fs.weight = strtoul(av[0], &end, 0);
928187769Sluigi			ac--; av++;
929187769Sluigi			break;
930187769Sluigi
931187769Sluigi		case TOK_PIPE:
932187769Sluigi			if (co.do_pipe == 1)
933187769Sluigi				errx(EX_DATAERR,"pipe only valid for queues");
934187769Sluigi			NEED1("pipe needs pipe_number\n");
935187769Sluigi			p.fs.parent_nr = strtoul(av[0], &end, 0);
936187769Sluigi			ac--; av++;
937187769Sluigi			break;
938187769Sluigi
939190865Sluigi		case TOK_PIPE_PROFILE:
940190865Sluigi			if (co.do_pipe != 1)
941190865Sluigi			    errx(EX_DATAERR, "extra delay only valid for pipes");
942190865Sluigi			NEED1("extra delay needs the file name\n");
943190865Sluigi			p.samples = &samples[0];
944190865Sluigi			load_extra_delays(av[0], &p);
945190865Sluigi			--ac; ++av;
946190865Sluigi			break;
947190865Sluigi
948194930Soleg		case TOK_BURST:
949194930Soleg			if (co.do_pipe != 1)
950194930Soleg				errx(EX_DATAERR, "burst only valid for pipes");
951194930Soleg			NEED1("burst needs argument\n");
952194930Soleg			errno = 0;
953194930Soleg			if (expand_number(av[0], &p.burst) < 0)
954194930Soleg				if (errno != ERANGE)
955194930Soleg					errx(EX_DATAERR,
956194930Soleg					    "burst: invalid argument");
957194930Soleg			if (errno || p.burst > (1ULL << 48) - 1)
958194930Soleg				errx(EX_DATAERR,
959194930Soleg				    "burst: out of range (0..2^48-1)");
960194930Soleg			ac--; av++;
961194930Soleg			break;
962194930Soleg
963187769Sluigi		default:
964187769Sluigi			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
965187769Sluigi		}
966187769Sluigi	}
967187769Sluigi	if (co.do_pipe == 1) {
968187769Sluigi		if (p.pipe_nr == 0)
969187769Sluigi			errx(EX_DATAERR, "pipe_nr must be > 0");
970187769Sluigi		if (p.delay > 10000)
971187769Sluigi			errx(EX_DATAERR, "delay must be < 10000");
972187769Sluigi	} else { /* co.do_pipe == 2, queue */
973187769Sluigi		if (p.fs.parent_nr == 0)
974187769Sluigi			errx(EX_DATAERR, "pipe must be > 0");
975187769Sluigi		if (p.fs.weight >100)
976187769Sluigi			errx(EX_DATAERR, "weight must be <= 100");
977187769Sluigi	}
978193715Sluigi
979193715Sluigi	/* check for bandwidth value */
980193715Sluigi	if (p.bandwidth == -1) {
981193715Sluigi		p.bandwidth = 0;
982193715Sluigi		if (p.samples_no > 0)
983193715Sluigi			errx(EX_DATAERR, "profile requires a bandwidth limit");
984193715Sluigi	}
985193715Sluigi
986187769Sluigi	if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) {
987187769Sluigi		size_t len;
988187769Sluigi		long limit;
989187769Sluigi
990187769Sluigi		len = sizeof(limit);
991187769Sluigi		if (sysctlbyname("net.inet.ip.dummynet.pipe_byte_limit",
992187769Sluigi			&limit, &len, NULL, 0) == -1)
993187769Sluigi			limit = 1024*1024;
994187769Sluigi		if (p.fs.qsize > limit)
995187769Sluigi			errx(EX_DATAERR, "queue size must be < %ldB", limit);
996187769Sluigi	} else {
997187769Sluigi		size_t len;
998187769Sluigi		long limit;
999187769Sluigi
1000187769Sluigi		len = sizeof(limit);
1001187769Sluigi		if (sysctlbyname("net.inet.ip.dummynet.pipe_slot_limit",
1002187769Sluigi			&limit, &len, NULL, 0) == -1)
1003187769Sluigi			limit = 100;
1004187769Sluigi		if (p.fs.qsize > limit)
1005187769Sluigi			errx(EX_DATAERR, "2 <= queue size <= %ld", limit);
1006187769Sluigi	}
1007187769Sluigi	if (p.fs.flags_fs & DN_IS_RED) {
1008187769Sluigi		size_t len;
1009187769Sluigi		int lookup_depth, avg_pkt_size;
1010187769Sluigi		double s, idle, weight, w_q;
1011187769Sluigi		struct clockinfo ck;
1012187769Sluigi		int t;
1013187769Sluigi
1014187769Sluigi		if (p.fs.min_th >= p.fs.max_th)
1015187769Sluigi		    errx(EX_DATAERR, "min_th %d must be < than max_th %d",
1016187769Sluigi			p.fs.min_th, p.fs.max_th);
1017187769Sluigi		if (p.fs.max_th == 0)
1018187769Sluigi		    errx(EX_DATAERR, "max_th must be > 0");
1019187769Sluigi
1020187769Sluigi		len = sizeof(int);
1021187769Sluigi		if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
1022187769Sluigi			&lookup_depth, &len, NULL, 0) == -1)
1023187769Sluigi		    errx(1, "sysctlbyname(\"%s\")",
1024187769Sluigi			"net.inet.ip.dummynet.red_lookup_depth");
1025187769Sluigi		if (lookup_depth == 0)
1026187769Sluigi		    errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
1027187769Sluigi			" must be greater than zero");
1028187769Sluigi
1029187769Sluigi		len = sizeof(int);
1030187769Sluigi		if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
1031187769Sluigi			&avg_pkt_size, &len, NULL, 0) == -1)
1032187769Sluigi
1033187769Sluigi		    errx(1, "sysctlbyname(\"%s\")",
1034187769Sluigi			"net.inet.ip.dummynet.red_avg_pkt_size");
1035187769Sluigi		if (avg_pkt_size == 0)
1036187769Sluigi			errx(EX_DATAERR,
1037187769Sluigi			    "net.inet.ip.dummynet.red_avg_pkt_size must"
1038187769Sluigi			    " be greater than zero");
1039187769Sluigi
1040187769Sluigi		len = sizeof(struct clockinfo);
1041187769Sluigi		if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1)
1042187769Sluigi			errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
1043187769Sluigi
1044187769Sluigi		/*
1045187769Sluigi		 * Ticks needed for sending a medium-sized packet.
1046187769Sluigi		 * Unfortunately, when we are configuring a WF2Q+ queue, we
1047187769Sluigi		 * do not have bandwidth information, because that is stored
1048187769Sluigi		 * in the parent pipe, and also we have multiple queues
1049187769Sluigi		 * competing for it. So we set s=0, which is not very
1050187769Sluigi		 * correct. But on the other hand, why do we want RED with
1051187769Sluigi		 * WF2Q+ ?
1052187769Sluigi		 */
1053187769Sluigi		if (p.bandwidth==0) /* this is a WF2Q+ queue */
1054187769Sluigi			s = 0;
1055187769Sluigi		else
1056187769Sluigi			s = (double)ck.hz * avg_pkt_size * 8 / p.bandwidth;
1057187769Sluigi
1058187769Sluigi		/*
1059187769Sluigi		 * max idle time (in ticks) before avg queue size becomes 0.
1060187769Sluigi		 * NOTA:  (3/w_q) is approx the value x so that
1061187769Sluigi		 * (1-w_q)^x < 10^-3.
1062187769Sluigi		 */
1063187769Sluigi		w_q = ((double)p.fs.w_q) / (1 << SCALE_RED);
1064187769Sluigi		idle = s * 3. / w_q;
1065187769Sluigi		p.fs.lookup_step = (int)idle / lookup_depth;
1066187769Sluigi		if (!p.fs.lookup_step)
1067187769Sluigi			p.fs.lookup_step = 1;
1068187769Sluigi		weight = 1 - w_q;
1069187769Sluigi		for (t = p.fs.lookup_step; t > 1; --t)
1070187769Sluigi			weight *= 1 - w_q;
1071187769Sluigi		p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
1072187769Sluigi	}
1073190865Sluigi	if (p.samples_no <= 0) {
1074190865Sluigi		i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, sizeof p);
1075190865Sluigi	} else {
1076190865Sluigi		struct dn_pipe_max pm;
1077190865Sluigi		int len = sizeof(pm);
1078190865Sluigi
1079190865Sluigi		memcpy(&pm.pipe, &p, sizeof(pm.pipe));
1080190865Sluigi		memcpy(&pm.samples, samples, sizeof(pm.samples));
1081190865Sluigi
1082190865Sluigi		i = do_cmd(IP_DUMMYNET_CONFIGURE, &pm, len);
1083190865Sluigi	}
1084190865Sluigi
1085187769Sluigi	if (i)
1086187769Sluigi		err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
1087187769Sluigi}
1088