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