]>
Commit | Line | Data |
---|---|---|
9e9d615e | 1 | /* |
2 | * q_htb.c HTB. | |
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: Martin Devera, devik@cdi.cz | |
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 | #define HTB_TC_VER 0x30003 | |
27 | #if HTB_TC_VER >> 16 != TC_HTB_PROTOVER | |
28 | #error "Different kernel and TC HTB versions" | |
29 | #endif | |
30 | ||
31 | static void explain(void) | |
32 | { | |
33 | fprintf(stderr, "Usage: ... qdisc add ... htb [default N] [r2q N]\n" | |
b43f3318 | 34 | " [direct_qlen P]\n" |
9e9d615e | 35 | " default minor id of class to which unclassified packets are sent {0}\n" |
36 | " r2q DRR quantums are computed as rate in Bps/r2q {10}\n" | |
37 | " debug string of 16 numbers each 0-3 {0}\n\n" | |
b43f3318 | 38 | " direct_qlen Limit of the direct queue {in packets}\n" |
a166d246 SH |
39 | "... class add ... htb rate R1 [burst B1] [mpu B] [overhead O]\n" |
40 | " [prio P] [slot S] [pslot PS]\n" | |
9e9d615e | 41 | " [ceil R2] [cburst B2] [mtu MTU] [quantum Q]\n" |
42 | " rate rate allocated to this class (class can still borrow)\n" | |
43 | " burst max bytes burst which can be accumulated during idle period {computed}\n" | |
a166d246 SH |
44 | " mpu minimum packet size used in rate computations\n" |
45 | " overhead per-packet size overhead used in rate computations\n" | |
292f29b4 | 46 | " linklay adapting to a linklayer e.g. atm\n" |
9e9d615e | 47 | " ceil definite upper class rate (no borrows) {rate}\n" |
48 | " cburst burst but for ceil {computed}\n" | |
49 | " mtu max packet size we create rate map for {1600}\n" | |
50 | " prio priority of leaf; lower are served first {0}\n" | |
51 | " quantum how much bytes to serve from leaf at once {use r2q}\n" | |
32a121cb | 52 | "\nTC HTB version %d.%d\n", HTB_TC_VER>>16, HTB_TC_VER&0xffff |
9e9d615e | 53 | ); |
54 | } | |
55 | ||
56 | static void explain1(char *arg) | |
57 | { | |
58 | fprintf(stderr, "Illegal \"%s\"\n", arg); | |
59 | explain(); | |
60 | } | |
61 | ||
62 | ||
9e9d615e | 63 | static int htb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) |
64 | { | |
4d4da09e | 65 | unsigned int direct_qlen = ~0U; |
d17b136f PS |
66 | struct tc_htb_glob opt = { |
67 | .rate2quantum = 10, | |
68 | .version = 3, | |
69 | }; | |
9e9d615e | 70 | struct rtattr *tail; |
32a121cb SH |
71 | unsigned int i; char *p; |
72 | ||
9e9d615e | 73 | while (argc > 0) { |
74 | if (matches(*argv, "r2q") == 0) { | |
22fa92e3 SH |
75 | NEXT_ARG(); |
76 | if (get_u32(&opt.rate2quantum, *argv, 10)) { | |
77 | explain1("r2q"); return -1; | |
78 | } | |
9e9d615e | 79 | } else if (matches(*argv, "default") == 0) { |
22fa92e3 SH |
80 | NEXT_ARG(); |
81 | if (get_u32(&opt.defcls, *argv, 16)) { | |
82 | explain1("default"); return -1; | |
83 | } | |
9e9d615e | 84 | } else if (matches(*argv, "debug") == 0) { |
22fa92e3 | 85 | NEXT_ARG(); p = *argv; |
32a121cb SH |
86 | for (i = 0; i < 16; i++, p++) { |
87 | if (*p < '0' || *p > '3') break; | |
22fa92e3 SH |
88 | opt.debug |= (*p-'0')<<(2*i); |
89 | } | |
4d4da09e HS |
90 | } else if (matches(*argv, "direct_qlen") == 0) { |
91 | NEXT_ARG(); | |
92 | if (get_u32(&direct_qlen, *argv, 10)) { | |
93 | explain1("direct_qlen"); return -1; | |
94 | } | |
9e9d615e | 95 | } else { |
96 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
97 | explain(); | |
98 | return -1; | |
99 | } | |
100 | argc--; argv++; | |
101 | } | |
1b52a762 | 102 | tail = NLMSG_TAIL(n); |
9e9d615e | 103 | addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); |
104 | addattr_l(n, 2024, TCA_HTB_INIT, &opt, NLMSG_ALIGN(sizeof(opt))); | |
4d4da09e HS |
105 | if (direct_qlen != ~0U) |
106 | addattr_l(n, 2024, TCA_HTB_DIRECT_QLEN, | |
107 | &direct_qlen, sizeof(direct_qlen)); | |
1b52a762 | 108 | tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; |
9e9d615e | 109 | return 0; |
110 | } | |
111 | ||
112 | static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) | |
113 | { | |
32a121cb | 114 | int ok = 0; |
d17b136f | 115 | struct tc_htb_opt opt = {}; |
32a121cb SH |
116 | __u32 rtab[256], ctab[256]; |
117 | unsigned buffer = 0, cbuffer = 0; | |
118 | int cell_log = -1, ccell_log = -1; | |
d17b136f | 119 | unsigned int mtu = 1600; /* eth packet len */ |
bccd014b JDB |
120 | unsigned short mpu = 0; |
121 | unsigned short overhead = 0; | |
292f29b4 | 122 | unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */ |
9e9d615e | 123 | struct rtattr *tail; |
8334bb32 | 124 | __u64 ceil64 = 0, rate64 = 0; |
9e9d615e | 125 | |
9e9d615e | 126 | while (argc > 0) { |
127 | if (matches(*argv, "prio") == 0) { | |
128 | NEXT_ARG(); | |
129 | if (get_u32(&opt.prio, *argv, 10)) { | |
130 | explain1("prio"); return -1; | |
131 | } | |
132 | ok++; | |
133 | } else if (matches(*argv, "mtu") == 0) { | |
134 | NEXT_ARG(); | |
135 | if (get_u32(&mtu, *argv, 10)) { | |
136 | explain1("mtu"); return -1; | |
137 | } | |
a166d246 SH |
138 | } else if (matches(*argv, "mpu") == 0) { |
139 | NEXT_ARG(); | |
bccd014b | 140 | if (get_u16(&mpu, *argv, 10)) { |
a166d246 SH |
141 | explain1("mpu"); return -1; |
142 | } | |
143 | } else if (matches(*argv, "overhead") == 0) { | |
144 | NEXT_ARG(); | |
bccd014b | 145 | if (get_u16(&overhead, *argv, 10)) { |
a166d246 SH |
146 | explain1("overhead"); return -1; |
147 | } | |
292f29b4 JDB |
148 | } else if (matches(*argv, "linklayer") == 0) { |
149 | NEXT_ARG(); | |
150 | if (get_linklayer(&linklayer, *argv)) { | |
151 | explain1("linklayer"); return -1; | |
152 | } | |
9e9d615e | 153 | } else if (matches(*argv, "quantum") == 0) { |
154 | NEXT_ARG(); | |
155 | if (get_u32(&opt.quantum, *argv, 10)) { | |
156 | explain1("quantum"); return -1; | |
157 | } | |
158 | } else if (matches(*argv, "burst") == 0 || | |
22fa92e3 SH |
159 | strcmp(*argv, "buffer") == 0 || |
160 | strcmp(*argv, "maxburst") == 0) { | |
9e9d615e | 161 | NEXT_ARG(); |
162 | if (get_size_and_cell(&buffer, &cell_log, *argv) < 0) { | |
163 | explain1("buffer"); | |
164 | return -1; | |
165 | } | |
166 | ok++; | |
167 | } else if (matches(*argv, "cburst") == 0 || | |
22fa92e3 SH |
168 | strcmp(*argv, "cbuffer") == 0 || |
169 | strcmp(*argv, "cmaxburst") == 0) { | |
9e9d615e | 170 | NEXT_ARG(); |
171 | if (get_size_and_cell(&cbuffer, &ccell_log, *argv) < 0) { | |
172 | explain1("cbuffer"); | |
173 | return -1; | |
174 | } | |
175 | ok++; | |
176 | } else if (strcmp(*argv, "ceil") == 0) { | |
177 | NEXT_ARG(); | |
8334bb32 | 178 | if (ceil64) { |
9e9d615e | 179 | fprintf(stderr, "Double \"ceil\" spec\n"); |
180 | return -1; | |
181 | } | |
8334bb32 | 182 | if (get_rate64(&ceil64, *argv)) { |
9e9d615e | 183 | explain1("ceil"); |
184 | return -1; | |
185 | } | |
186 | ok++; | |
187 | } else if (strcmp(*argv, "rate") == 0) { | |
188 | NEXT_ARG(); | |
8334bb32 | 189 | if (rate64) { |
9e9d615e | 190 | fprintf(stderr, "Double \"rate\" spec\n"); |
191 | return -1; | |
192 | } | |
8334bb32 | 193 | if (get_rate64(&rate64, *argv)) { |
9e9d615e | 194 | explain1("rate"); |
195 | return -1; | |
196 | } | |
197 | ok++; | |
198 | } else if (strcmp(*argv, "help") == 0) { | |
199 | explain(); | |
200 | return -1; | |
201 | } else { | |
202 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
203 | explain(); | |
204 | return -1; | |
205 | } | |
206 | argc--; argv++; | |
207 | } | |
208 | ||
22fa92e3 | 209 | /* if (!ok) |
9e9d615e | 210 | return 0;*/ |
211 | ||
8334bb32 | 212 | if (!rate64) { |
9e9d615e | 213 | fprintf(stderr, "\"rate\" is required.\n"); |
214 | return -1; | |
215 | } | |
216 | /* if ceil params are missing, use the same as rate */ | |
8334bb32 ED |
217 | if (!ceil64) |
218 | ceil64 = rate64; | |
219 | ||
220 | opt.rate.rate = (rate64 >= (1ULL << 32)) ? ~0U : rate64; | |
221 | opt.ceil.rate = (ceil64 >= (1ULL << 32)) ? ~0U : ceil64; | |
9e9d615e | 222 | |
223 | /* compute minimal allowed burst from rate; mtu is added here to make | |
224 | sute that buffer is larger than mtu and to have some safeguard space */ | |
8334bb32 ED |
225 | if (!buffer) |
226 | buffer = rate64 / get_hz() + mtu; | |
227 | if (!cbuffer) | |
228 | cbuffer = ceil64 / get_hz() + mtu; | |
9e9d615e | 229 | |
bccd014b JDB |
230 | opt.ceil.overhead = overhead; |
231 | opt.rate.overhead = overhead; | |
232 | ||
233 | opt.ceil.mpu = mpu; | |
234 | opt.rate.mpu = mpu; | |
a166d246 | 235 | |
292f29b4 | 236 | if (tc_calc_rtable(&opt.rate, rtab, cell_log, mtu, linklayer) < 0) { |
9e9d615e | 237 | fprintf(stderr, "htb: failed to calculate rate table.\n"); |
238 | return -1; | |
239 | } | |
8334bb32 | 240 | opt.buffer = tc_calc_xmittime(rate64, buffer); |
ae665a52 | 241 | |
292f29b4 | 242 | if (tc_calc_rtable(&opt.ceil, ctab, ccell_log, mtu, linklayer) < 0) { |
9e9d615e | 243 | fprintf(stderr, "htb: failed to calculate ceil rate table.\n"); |
244 | return -1; | |
245 | } | |
8334bb32 | 246 | opt.cbuffer = tc_calc_xmittime(ceil64, cbuffer); |
9e9d615e | 247 | |
1b52a762 | 248 | tail = NLMSG_TAIL(n); |
9e9d615e | 249 | addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); |
8334bb32 ED |
250 | |
251 | if (rate64 >= (1ULL << 32)) | |
252 | addattr_l(n, 1124, TCA_HTB_RATE64, &rate64, sizeof(rate64)); | |
253 | ||
254 | if (ceil64 >= (1ULL << 32)) | |
255 | addattr_l(n, 1224, TCA_HTB_CEIL64, &ceil64, sizeof(ceil64)); | |
256 | ||
9e9d615e | 257 | addattr_l(n, 2024, TCA_HTB_PARMS, &opt, sizeof(opt)); |
258 | addattr_l(n, 3024, TCA_HTB_RTAB, rtab, 1024); | |
259 | addattr_l(n, 4024, TCA_HTB_CTAB, ctab, 1024); | |
1b52a762 | 260 | tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; |
9e9d615e | 261 | return 0; |
262 | } | |
263 | ||
264 | static int htb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) | |
265 | { | |
b43f3318 | 266 | struct rtattr *tb[TCA_HTB_MAX + 1]; |
9e9d615e | 267 | struct tc_htb_opt *hopt; |
268 | struct tc_htb_glob *gopt; | |
32a121cb | 269 | double buffer, cbuffer; |
3e92ff52 | 270 | unsigned int linklayer; |
8334bb32 | 271 | __u64 rate64, ceil64; |
32a121cb | 272 | |
9e9d615e | 273 | SPRINT_BUF(b1); |
274 | SPRINT_BUF(b2); | |
467f9fce | 275 | SPRINT_BUF(b3); |
9e9d615e | 276 | |
277 | if (opt == NULL) | |
278 | return 0; | |
279 | ||
b43f3318 | 280 | parse_rtattr_nested(tb, TCA_HTB_MAX, opt); |
9e9d615e | 281 | |
282 | if (tb[TCA_HTB_PARMS]) { | |
22fa92e3 SH |
283 | hopt = RTA_DATA(tb[TCA_HTB_PARMS]); |
284 | if (RTA_PAYLOAD(tb[TCA_HTB_PARMS]) < sizeof(*hopt)) return -1; | |
9e9d615e | 285 | |
286 | if (!hopt->level) { | |
287 | fprintf(f, "prio %d ", (int)hopt->prio); | |
288 | if (show_details) | |
289 | fprintf(f, "quantum %d ", (int)hopt->quantum); | |
290 | } | |
8334bb32 ED |
291 | |
292 | rate64 = hopt->rate.rate; | |
293 | if (tb[TCA_HTB_RATE64] && | |
294 | RTA_PAYLOAD(tb[TCA_HTB_RATE64]) >= sizeof(rate64)) { | |
295 | rate64 = rta_getattr_u64(tb[TCA_HTB_RATE64]); | |
296 | } | |
297 | ||
298 | ceil64 = hopt->ceil.rate; | |
299 | if (tb[TCA_HTB_CEIL64] && | |
300 | RTA_PAYLOAD(tb[TCA_HTB_CEIL64]) >= sizeof(ceil64)) | |
301 | ceil64 = rta_getattr_u64(tb[TCA_HTB_CEIL64]); | |
302 | ||
303 | fprintf(f, "rate %s ", sprint_rate(rate64, b1)); | |
22fa92e3 SH |
304 | if (hopt->rate.overhead) |
305 | fprintf(f, "overhead %u ", hopt->rate.overhead); | |
8334bb32 ED |
306 | buffer = tc_calc_xmitsize(rate64, hopt->buffer); |
307 | ||
308 | fprintf(f, "ceil %s ", sprint_rate(ceil64, b1)); | |
309 | cbuffer = tc_calc_xmitsize(ceil64, hopt->cbuffer); | |
3e92ff52 JDB |
310 | linklayer = (hopt->rate.linklayer & TC_LINKLAYER_MASK); |
311 | if (linklayer > TC_LINKLAYER_ETHERNET || show_details) | |
467f9fce | 312 | fprintf(f, "linklayer %s ", sprint_linklayer(linklayer, b3)); |
22fa92e3 | 313 | if (show_details) { |
1aea7fea | 314 | fprintf(f, "burst %s/%u mpu %s ", |
22fa92e3 SH |
315 | sprint_size(buffer, b1), |
316 | 1<<hopt->rate.cell_log, | |
1aea7fea DS |
317 | sprint_size(hopt->rate.mpu, b2)); |
318 | fprintf(f, "cburst %s/%u mpu %s ", | |
22fa92e3 SH |
319 | sprint_size(cbuffer, b1), |
320 | 1<<hopt->ceil.cell_log, | |
1aea7fea | 321 | sprint_size(hopt->ceil.mpu, b2)); |
22fa92e3 SH |
322 | fprintf(f, "level %d ", (int)hopt->level); |
323 | } else { | |
324 | fprintf(f, "burst %s ", sprint_size(buffer, b1)); | |
325 | fprintf(f, "cburst %s ", sprint_size(cbuffer, b1)); | |
326 | } | |
327 | if (show_raw) | |
328 | fprintf(f, "buffer [%08x] cbuffer [%08x] ", | |
32a121cb | 329 | hopt->buffer, hopt->cbuffer); |
9e9d615e | 330 | } |
331 | if (tb[TCA_HTB_INIT]) { | |
22fa92e3 SH |
332 | gopt = RTA_DATA(tb[TCA_HTB_INIT]); |
333 | if (RTA_PAYLOAD(tb[TCA_HTB_INIT]) < sizeof(*gopt)) return -1; | |
9e9d615e | 334 | |
22fa92e3 | 335 | fprintf(f, "r2q %d default %x direct_packets_stat %u", |
32a121cb | 336 | gopt->rate2quantum, gopt->defcls, gopt->direct_pkts); |
9e9d615e | 337 | if (show_details) |
32a121cb | 338 | fprintf(f, " ver %d.%d", gopt->version >> 16, gopt->version & 0xffff); |
9e9d615e | 339 | } |
b43f3318 ED |
340 | if (tb[TCA_HTB_DIRECT_QLEN] && |
341 | RTA_PAYLOAD(tb[TCA_HTB_DIRECT_QLEN]) >= sizeof(__u32)) { | |
342 | __u32 direct_qlen = rta_getattr_u32(tb[TCA_HTB_DIRECT_QLEN]); | |
343 | ||
344 | fprintf(f, " direct_qlen %u", direct_qlen); | |
345 | } | |
9e9d615e | 346 | return 0; |
347 | } | |
348 | ||
349 | static int htb_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) | |
350 | { | |
351 | struct tc_htb_xstats *st; | |
32a121cb | 352 | |
9e9d615e | 353 | if (xstats == NULL) |
354 | return 0; | |
355 | ||
356 | if (RTA_PAYLOAD(xstats) < sizeof(*st)) | |
357 | return -1; | |
358 | ||
359 | st = RTA_DATA(xstats); | |
ae665a52 | 360 | fprintf(f, " lended: %u borrowed: %u giants: %u\n", |
32a121cb SH |
361 | st->lends, st->borrows, st->giants); |
362 | fprintf(f, " tokens: %d ctokens: %d\n", st->tokens, st->ctokens); | |
9e9d615e | 363 | return 0; |
364 | } | |
365 | ||
95812b56 | 366 | struct qdisc_util htb_qdisc_util = { |
32a121cb | 367 | .id = "htb", |
f2f99e2e SH |
368 | .parse_qopt = htb_parse_opt, |
369 | .print_qopt = htb_print_opt, | |
32a121cb | 370 | .print_xstats = htb_print_xstats, |
f2f99e2e SH |
371 | .parse_copt = htb_parse_class_opt, |
372 | .print_copt = htb_print_opt, | |
9e9d615e | 373 | }; |