• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * q_tbf.c		TBF.
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 
explain(void)26 static void explain(void)
27 {
28 	fprintf(stderr, "Usage: ... tbf limit BYTES burst BYTES[/BYTES] rate KBPS [ mtu BYTES[/BYTES] ]\n");
29 	fprintf(stderr, "               [ peakrate KBPS ] [ latency TIME ] ");
30 	fprintf(stderr, "[ overhead BYTES ] [ linklayer TYPE ]\n");
31 }
32 
explain1(const char * arg,const char * val)33 static void explain1(const char *arg, const char *val)
34 {
35 	fprintf(stderr, "tbf: illegal value for \"%s\": \"%s\"\n", arg, val);
36 }
37 
38 
tbf_parse_opt(struct qdisc_util * qu,int argc,char ** argv,struct nlmsghdr * n)39 static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
40 {
41 	int ok=0;
42 	struct tc_tbf_qopt opt;
43 	__u32 rtab[256];
44 	__u32 ptab[256];
45 	unsigned buffer=0, mtu=0, mpu=0, latency=0;
46 	int Rcell_log=-1, Pcell_log = -1;
47 	unsigned short overhead=0;
48 	unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
49 	struct rtattr *tail;
50 	__u64 rate64 = 0, prate64 = 0;
51 
52 	memset(&opt, 0, sizeof(opt));
53 
54 	while (argc > 0) {
55 		if (matches(*argv, "limit") == 0) {
56 			NEXT_ARG();
57 			if (opt.limit) {
58 				fprintf(stderr, "tbf: duplicate \"limit\" specification\n");
59 				return -1;
60 			}
61 			if (latency) {
62 				fprintf(stderr, "tbf: specifying both \"latency\" and \"limit\" is not allowed\n");
63 				return -1;
64 			}
65 			if (get_size(&opt.limit, *argv)) {
66 				explain1("limit", *argv);
67 				return -1;
68 			}
69 			ok++;
70 		} else if (matches(*argv, "latency") == 0) {
71 			NEXT_ARG();
72 			if (latency) {
73 				fprintf(stderr, "tbf: duplicate \"latency\" specification\n");
74 				return -1;
75 			}
76 			if (opt.limit) {
77 				fprintf(stderr, "tbf: specifying both \"limit\" and \"/latency\" is not allowed\n");
78 				return -1;
79 			}
80 			if (get_time(&latency, *argv)) {
81 				explain1("latency", *argv);
82 				return -1;
83 			}
84 			ok++;
85 		} else if (matches(*argv, "burst") == 0 ||
86 			strcmp(*argv, "buffer") == 0 ||
87 			strcmp(*argv, "maxburst") == 0) {
88 			const char *parm_name = *argv;
89 			NEXT_ARG();
90 			if (buffer) {
91 				fprintf(stderr, "tbf: duplicate \"buffer/burst/maxburst\" specification\n");
92 				return -1;
93 			}
94 			if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) {
95 				explain1(parm_name, *argv);
96 				return -1;
97 			}
98 			ok++;
99 		} else if (strcmp(*argv, "mtu") == 0 ||
100 			   strcmp(*argv, "minburst") == 0) {
101 			const char *parm_name = *argv;
102 			NEXT_ARG();
103 			if (mtu) {
104 				fprintf(stderr, "tbf: duplicate \"mtu/minburst\" specification\n");
105 				return -1;
106 			}
107 			if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) {
108 				explain1(parm_name, *argv);
109 				return -1;
110 			}
111 			ok++;
112 		} else if (strcmp(*argv, "mpu") == 0) {
113 			NEXT_ARG();
114 			if (mpu) {
115 				fprintf(stderr, "tbf: duplicate \"mpu\" specification\n");
116 				return -1;
117 			}
118 			if (get_size(&mpu, *argv)) {
119 				explain1("mpu", *argv);
120 				return -1;
121 			}
122 			ok++;
123 		} else if (strcmp(*argv, "rate") == 0) {
124 			NEXT_ARG();
125 			if (rate64) {
126 				fprintf(stderr, "tbf: duplicate \"rate\" specification\n");
127 				return -1;
128 			}
129 			if (get_rate64(&rate64, *argv)) {
130 				explain1("rate", *argv);
131 				return -1;
132 			}
133 			ok++;
134 		} else if (matches(*argv, "peakrate") == 0) {
135 			NEXT_ARG();
136 			if (prate64) {
137 				fprintf(stderr, "tbf: duplicate \"peakrate\" specification\n");
138 				return -1;
139 			}
140 			if (get_rate64(&prate64, *argv)) {
141 				explain1("peakrate", *argv);
142 				return -1;
143 			}
144 			ok++;
145 		} else if (matches(*argv, "overhead") == 0) {
146 			NEXT_ARG();
147 			if (overhead) {
148 				fprintf(stderr, "tbf: duplicate \"overhead\" specification\n");
149 				return -1;
150 			}
151 			if (get_u16(&overhead, *argv, 10)) {
152 				explain1("overhead", *argv); return -1;
153 			}
154 		} else if (matches(*argv, "linklayer") == 0) {
155 			NEXT_ARG();
156 			if (get_linklayer(&linklayer, *argv)) {
157 				explain1("linklayer", *argv); return -1;
158 			}
159 		} else if (strcmp(*argv, "help") == 0) {
160 			explain();
161 			return -1;
162 		} else {
163 			fprintf(stderr, "tbf: unknown parameter \"%s\"\n", *argv);
164 			explain();
165 			return -1;
166 		}
167 		argc--; argv++;
168 	}
169 
170         int verdict = 0;
171 
172         /* Be nice to the user: try to emit all error messages in
173          * one go rather than reveal one more problem when a
174          * previous one has been fixed.
175          */
176 	if (rate64 == 0) {
177 		fprintf(stderr, "tbf: the \"rate\" parameter is mandatory.\n");
178 		verdict = -1;
179 	}
180 	if (!buffer) {
181 		fprintf(stderr, "tbf: the \"burst\" parameter is mandatory.\n");
182 		verdict = -1;
183 	}
184 	if (prate64) {
185 		if (!mtu) {
186 			fprintf(stderr, "tbf: when \"peakrate\" is specified, \"mtu\" must also be specified.\n");
187 			verdict = -1;
188 		}
189 	}
190 
191 	if (opt.limit == 0 && latency == 0) {
192 		fprintf(stderr, "tbf: either \"limit\" or \"latency\" is required.\n");
193 		verdict = -1;
194 	}
195 
196         if (verdict != 0) {
197                 explain();
198                 return verdict;
199         }
200 
201 	opt.rate.rate = (rate64 >= (1ULL << 32)) ? ~0U : rate64;
202 	opt.peakrate.rate = (prate64 >= (1ULL << 32)) ? ~0U : prate64;
203 
204 	if (opt.limit == 0) {
205 		double lim = rate64*(double)latency/TIME_UNITS_PER_SEC + buffer;
206 		if (prate64) {
207 			double lim2 = prate64*(double)latency/TIME_UNITS_PER_SEC + mtu;
208 			if (lim2 < lim)
209 				lim = lim2;
210 		}
211 		opt.limit = lim;
212 	}
213 
214 	opt.rate.mpu      = mpu;
215 	opt.rate.overhead = overhead;
216 	if (tc_calc_rtable(&opt.rate, rtab, Rcell_log, mtu, linklayer) < 0) {
217 		fprintf(stderr, "tbf: failed to calculate rate table.\n");
218 		return -1;
219 	}
220 	opt.buffer = tc_calc_xmittime(opt.rate.rate, buffer);
221 
222 	if (opt.peakrate.rate) {
223 		opt.peakrate.mpu      = mpu;
224 		opt.peakrate.overhead = overhead;
225 		if (tc_calc_rtable(&opt.peakrate, ptab, Pcell_log, mtu, linklayer) < 0) {
226 			fprintf(stderr, "tbf: failed to calculate peak rate table.\n");
227 			return -1;
228 		}
229 		opt.mtu = tc_calc_xmittime(opt.peakrate.rate, mtu);
230 	}
231 
232 	tail = NLMSG_TAIL(n);
233 	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
234 	addattr_l(n, 2024, TCA_TBF_PARMS, &opt, sizeof(opt));
235 	addattr_l(n, 2124, TCA_TBF_BURST, &buffer, sizeof(buffer));
236 	if (rate64 >= (1ULL << 32))
237 		addattr_l(n, 2124, TCA_TBF_RATE64, &rate64, sizeof(rate64));
238 	addattr_l(n, 3024, TCA_TBF_RTAB, rtab, 1024);
239 	if (opt.peakrate.rate) {
240 		if (prate64 >= (1ULL << 32))
241 			addattr_l(n, 3124, TCA_TBF_PRATE64, &prate64, sizeof(prate64));
242 		addattr_l(n, 3224, TCA_TBF_PBURST, &mtu, sizeof(mtu));
243 		addattr_l(n, 4096, TCA_TBF_PTAB, ptab, 1024);
244 	}
245 	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
246 	return 0;
247 }
248 
tbf_print_opt(struct qdisc_util * qu,FILE * f,struct rtattr * opt)249 static int tbf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
250 {
251 	struct rtattr *tb[TCA_TBF_MAX+1];
252 	struct tc_tbf_qopt *qopt;
253 	unsigned int linklayer;
254 	double buffer, mtu;
255 	double latency;
256 	__u64 rate64 = 0, prate64 = 0;
257 	SPRINT_BUF(b1);
258 	SPRINT_BUF(b2);
259 	SPRINT_BUF(b3);
260 
261 	if (opt == NULL)
262 		return 0;
263 
264 	parse_rtattr_nested(tb, TCA_TBF_MAX, opt);
265 
266 	if (tb[TCA_TBF_PARMS] == NULL)
267 		return -1;
268 
269 	qopt = RTA_DATA(tb[TCA_TBF_PARMS]);
270 	if (RTA_PAYLOAD(tb[TCA_TBF_PARMS])  < sizeof(*qopt))
271 		return -1;
272 	rate64 = qopt->rate.rate;
273 	if (tb[TCA_TBF_RATE64] &&
274 	    RTA_PAYLOAD(tb[TCA_TBF_RATE64]) >= sizeof(rate64))
275 		rate64 = rta_getattr_u64(tb[TCA_TBF_RATE64]);
276 	fprintf(f, "rate %s ", sprint_rate(rate64, b1));
277 	buffer = tc_calc_xmitsize(rate64, qopt->buffer);
278 	if (show_details) {
279 		fprintf(f, "burst %s/%u mpu %s ", sprint_size(buffer, b1),
280 			1<<qopt->rate.cell_log, sprint_size(qopt->rate.mpu, b2));
281 	} else {
282 		fprintf(f, "burst %s ", sprint_size(buffer, b1));
283 	}
284 	if (show_raw)
285 		fprintf(f, "[%08x] ", qopt->buffer);
286 	prate64 = qopt->peakrate.rate;
287 	if (tb[TCA_TBF_PRATE64] &&
288 	    RTA_PAYLOAD(tb[TCA_TBF_PRATE64]) >= sizeof(prate64))
289 		prate64 = rta_getattr_u64(tb[TCA_TBF_PRATE64]);
290 	if (prate64) {
291 		fprintf(f, "peakrate %s ", sprint_rate(prate64, b1));
292 		if (qopt->mtu || qopt->peakrate.mpu) {
293 			mtu = tc_calc_xmitsize(prate64, qopt->mtu);
294 			if (show_details) {
295 				fprintf(f, "mtu %s/%u mpu %s ", sprint_size(mtu, b1),
296 					1<<qopt->peakrate.cell_log, sprint_size(qopt->peakrate.mpu, b2));
297 			} else {
298 				fprintf(f, "minburst %s ", sprint_size(mtu, b1));
299 			}
300 			if (show_raw)
301 				fprintf(f, "[%08x] ", qopt->mtu);
302 		}
303 	}
304 
305 	latency = TIME_UNITS_PER_SEC*(qopt->limit/(double)rate64) - tc_core_tick2time(qopt->buffer);
306 	if (prate64) {
307 		double lat2 = TIME_UNITS_PER_SEC*(qopt->limit/(double)prate64) - tc_core_tick2time(qopt->mtu);
308 		if (lat2 > latency)
309 			latency = lat2;
310 	}
311 	if (latency >= 0.0)
312 		fprintf(f, "lat %s ", sprint_time(latency, b1));
313 	if (show_raw || latency < 0.0)
314 		fprintf(f, "limit %s ", sprint_size(qopt->limit, b1));
315 
316 	if (qopt->rate.overhead) {
317 		fprintf(f, "overhead %d", qopt->rate.overhead);
318 	}
319 	linklayer = (qopt->rate.linklayer & TC_LINKLAYER_MASK);
320 	if (linklayer > TC_LINKLAYER_ETHERNET || show_details)
321 		fprintf(f, "linklayer %s ", sprint_linklayer(linklayer, b3));
322 
323 	return 0;
324 }
325 
326 struct qdisc_util tbf_qdisc_util = {
327 	.id		= "tbf",
328 	.parse_qopt	= tbf_parse_opt,
329 	.print_qopt	= tbf_print_opt,
330 };
331