1/*
2 * q_red.c		RED.
3 *
4 *		This program is free software; you can redistribute it and/or
5 *		modify it under the terms of the GNU General Public License
6 *		as published by the Free Software Foundation; either version
7 *		2 of the License, or (at your option) any later version.
8 *
9 * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <syslog.h>
17#include <fcntl.h>
18#include <sys/socket.h>
19#include <netinet/in.h>
20#include <arpa/inet.h>
21#include <string.h>
22
23#include "utils.h"
24#include "tc_util.h"
25
26#include "tc_red.h"
27
28static void explain(void)
29{
30	fprintf(stderr, "Usage: ... red limit BYTES min BYTES max BYTES avpkt BYTES burst PACKETS\n");
31	fprintf(stderr, "               probability PROBABILITY bandwidth KBPS [ ecn ]\n");
32}
33
34#define usage() return(-1)
35
36static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
37{
38	int ok=0;
39	struct tc_red_qopt opt;
40	unsigned burst = 0;
41	unsigned avpkt = 0;
42	double probability = 0.02;
43	unsigned rate = 0;
44	int ecn_ok = 0;
45	int wlog;
46	__u8 sbuf[256];
47	struct rtattr *tail;
48
49	memset(&opt, 0, sizeof(opt));
50
51	while (argc > 0) {
52		if (strcmp(*argv, "limit") == 0) {
53			NEXT_ARG();
54			if (get_size(&opt.limit, *argv)) {
55				fprintf(stderr, "Illegal \"limit\"\n");
56				return -1;
57			}
58			ok++;
59		} else if (strcmp(*argv, "min") == 0) {
60			NEXT_ARG();
61			if (get_size(&opt.qth_min, *argv)) {
62				fprintf(stderr, "Illegal \"min\"\n");
63				return -1;
64			}
65			ok++;
66		} else if (strcmp(*argv, "max") == 0) {
67			NEXT_ARG();
68			if (get_size(&opt.qth_max, *argv)) {
69				fprintf(stderr, "Illegal \"max\"\n");
70				return -1;
71			}
72			ok++;
73		} else if (strcmp(*argv, "burst") == 0) {
74			NEXT_ARG();
75			if (get_unsigned(&burst, *argv, 0)) {
76				fprintf(stderr, "Illegal \"burst\"\n");
77				return -1;
78			}
79			ok++;
80		} else if (strcmp(*argv, "avpkt") == 0) {
81			NEXT_ARG();
82			if (get_size(&avpkt, *argv)) {
83				fprintf(stderr, "Illegal \"avpkt\"\n");
84				return -1;
85			}
86			ok++;
87		} else if (strcmp(*argv, "probability") == 0) {
88			NEXT_ARG();
89			if (sscanf(*argv, "%lg", &probability) != 1) {
90				fprintf(stderr, "Illegal \"probability\"\n");
91				return -1;
92			}
93			ok++;
94		} else if (strcmp(*argv, "bandwidth") == 0) {
95			NEXT_ARG();
96			if (get_rate(&rate, *argv)) {
97				fprintf(stderr, "Illegal \"bandwidth\"\n");
98				return -1;
99			}
100			ok++;
101		} else if (strcmp(*argv, "ecn") == 0) {
102			ecn_ok = 1;
103			ok++;
104		} else if (strcmp(*argv, "help") == 0) {
105			explain();
106			return -1;
107		} else {
108			fprintf(stderr, "What is \"%s\"?\n", *argv);
109			explain();
110			return -1;
111		}
112		argc--; argv++;
113	}
114
115	if (!ok)
116		return 0;
117
118	if (rate == 0)
119		get_rate(&rate, "10Mbit");
120
121	if (!opt.qth_min || !opt.qth_max || !burst || !opt.limit || !avpkt) {
122		fprintf(stderr, "Required parameter (min, max, burst, limit, avpket) is missing\n");
123		return -1;
124	}
125
126	if ((wlog = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) {
127		fprintf(stderr, "RED: failed to calculate EWMA constant.\n");
128		return -1;
129	}
130	if (wlog >= 10)
131		fprintf(stderr, "RED: WARNING. Burst %d seems to be to large.\n", burst);
132	opt.Wlog = wlog;
133	if ((wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) {
134		fprintf(stderr, "RED: failed to calculate probability.\n");
135		return -1;
136	}
137	opt.Plog = wlog;
138	if ((wlog = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) {
139		fprintf(stderr, "RED: failed to calculate idle damping table.\n");
140		return -1;
141	}
142	opt.Scell_log = wlog;
143	if (ecn_ok) {
144#ifdef TC_RED_ECN
145		opt.flags |= TC_RED_ECN;
146#else
147		fprintf(stderr, "RED: ECN support is missing in this binary.\n");
148		return -1;
149#endif
150	}
151
152	tail = NLMSG_TAIL(n);
153	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
154	addattr_l(n, 1024, TCA_RED_PARMS, &opt, sizeof(opt));
155	addattr_l(n, 1024, TCA_RED_STAB, sbuf, 256);
156	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
157	return 0;
158}
159
160static int red_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
161{
162	struct rtattr *tb[TCA_RED_STAB+1];
163	struct tc_red_qopt *qopt;
164	SPRINT_BUF(b1);
165	SPRINT_BUF(b2);
166	SPRINT_BUF(b3);
167
168	if (opt == NULL)
169		return 0;
170
171	parse_rtattr_nested(tb, TCA_RED_STAB, opt);
172
173	if (tb[TCA_RED_PARMS] == NULL)
174		return -1;
175	qopt = RTA_DATA(tb[TCA_RED_PARMS]);
176	if (RTA_PAYLOAD(tb[TCA_RED_PARMS])  < sizeof(*qopt))
177		return -1;
178	fprintf(f, "limit %s min %s max %s ",
179		sprint_size(qopt->limit, b1),
180		sprint_size(qopt->qth_min, b2),
181		sprint_size(qopt->qth_max, b3));
182#ifdef TC_RED_ECN
183	if (qopt->flags & TC_RED_ECN)
184		fprintf(f, "ecn ");
185#endif
186	if (show_details) {
187		fprintf(f, "ewma %u Plog %u Scell_log %u",
188			qopt->Wlog, qopt->Plog, qopt->Scell_log);
189	}
190	return 0;
191}
192
193static int red_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
194{
195#ifdef TC_RED_ECN
196	struct tc_red_xstats *st;
197
198	if (xstats == NULL)
199		return 0;
200
201	if (RTA_PAYLOAD(xstats) < sizeof(*st))
202		return -1;
203
204	st = RTA_DATA(xstats);
205	fprintf(f, "  marked %u early %u pdrop %u other %u",
206		st->marked, st->early, st->pdrop, st->other);
207	return 0;
208
209#endif
210	return 0;
211}
212
213
214struct qdisc_util red_qdisc_util = {
215	.id		= "red",
216	.parse_qopt	= red_parse_opt,
217	.print_qopt	= red_print_opt,
218	.print_xstats	= red_print_xstats,
219};
220