]>
Commit | Line | Data |
---|---|---|
d2773f12 PM |
1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | ||
3 | /* | |
4 | * Enhanced Transmission Selection - 802.1Qaz-based Qdisc | |
5 | */ | |
6 | ||
7 | #include <stdio.h> | |
8 | #include <stdlib.h> | |
9 | #include <unistd.h> | |
10 | #include <fcntl.h> | |
11 | #include <sys/socket.h> | |
12 | #include <netinet/in.h> | |
13 | #include <arpa/inet.h> | |
14 | #include <string.h> | |
15 | ||
16 | #include "utils.h" | |
17 | #include "tc_util.h" | |
18 | ||
19 | static void explain(void) | |
20 | { | |
21 | fprintf(stderr, "Usage: ... ets [bands NUMBER] [strict NUMBER] [quanta Q1 Q2...] [priomap P1 P2...]\n"); | |
22 | } | |
23 | ||
24 | static void cexplain(void) | |
25 | { | |
26 | fprintf(stderr, "Usage: ... ets [quantum Q1]\n"); | |
27 | } | |
28 | ||
29 | static unsigned int parse_quantum(const char *arg) | |
30 | { | |
31 | unsigned int quantum; | |
32 | ||
33 | if (get_unsigned(&quantum, arg, 10)) { | |
34 | fprintf(stderr, "Illegal \"quanta\" element\n"); | |
35 | return 0; | |
36 | } | |
37 | if (!quantum) | |
38 | fprintf(stderr, "\"quanta\" must be > 0\n"); | |
39 | return quantum; | |
40 | } | |
41 | ||
42 | static int parse_nbands(const char *arg, __u8 *pnbands, const char *what) | |
43 | { | |
44 | unsigned int tmp; | |
45 | ||
46 | if (get_unsigned(&tmp, arg, 10)) { | |
47 | fprintf(stderr, "Illegal \"%s\"\n", what); | |
48 | return -1; | |
49 | } | |
50 | if (tmp > TCQ_ETS_MAX_BANDS) { | |
51 | fprintf(stderr, "The number of \"%s\" must be <= %d\n", | |
52 | what, TCQ_ETS_MAX_BANDS); | |
53 | return -1; | |
54 | } | |
55 | ||
56 | *pnbands = tmp; | |
57 | return 0; | |
58 | } | |
59 | ||
60 | static int ets_parse_opt(struct qdisc_util *qu, int argc, char **argv, | |
61 | struct nlmsghdr *n, const char *dev) | |
62 | { | |
63 | __u8 nbands = 0; | |
64 | __u8 nstrict = 0; | |
65 | bool quanta_mode = false; | |
66 | unsigned int nquanta = 0; | |
67 | __u32 quanta[TCQ_ETS_MAX_BANDS]; | |
68 | bool priomap_mode = false; | |
69 | unsigned int nprio = 0; | |
70 | __u8 priomap[TC_PRIO_MAX + 1]; | |
71 | unsigned int tmp; | |
72 | struct rtattr *tail, *nest; | |
73 | ||
74 | while (argc > 0) { | |
75 | if (strcmp(*argv, "bands") == 0) { | |
76 | if (nbands) { | |
77 | fprintf(stderr, "Duplicate \"bands\"\n"); | |
78 | return -1; | |
79 | } | |
80 | NEXT_ARG(); | |
81 | if (parse_nbands(*argv, &nbands, "bands")) | |
82 | return -1; | |
83 | priomap_mode = quanta_mode = false; | |
84 | } else if (strcmp(*argv, "strict") == 0) { | |
85 | if (nstrict) { | |
86 | fprintf(stderr, "Duplicate \"strict\"\n"); | |
87 | return -1; | |
88 | } | |
89 | NEXT_ARG(); | |
90 | if (parse_nbands(*argv, &nstrict, "strict")) | |
91 | return -1; | |
92 | priomap_mode = quanta_mode = false; | |
93 | } else if (strcmp(*argv, "quanta") == 0) { | |
94 | if (nquanta) { | |
95 | fprintf(stderr, "Duplicate \"quanta\"\n"); | |
96 | return -1; | |
97 | } | |
98 | NEXT_ARG(); | |
99 | priomap_mode = false; | |
100 | quanta_mode = true; | |
101 | goto parse_quantum; | |
102 | } else if (strcmp(*argv, "priomap") == 0) { | |
103 | if (nprio) { | |
104 | fprintf(stderr, "Duplicate \"priomap\"\n"); | |
105 | return -1; | |
106 | } | |
107 | NEXT_ARG(); | |
108 | priomap_mode = true; | |
109 | quanta_mode = false; | |
110 | goto parse_priomap; | |
111 | } else if (strcmp(*argv, "help") == 0) { | |
112 | explain(); | |
113 | return -1; | |
114 | } else if (quanta_mode) { | |
115 | unsigned int quantum; | |
116 | ||
117 | parse_quantum: | |
118 | quantum = parse_quantum(*argv); | |
119 | if (!quantum) | |
120 | return -1; | |
121 | quanta[nquanta++] = quantum; | |
122 | } else if (priomap_mode) { | |
123 | unsigned int band; | |
124 | ||
125 | parse_priomap: | |
126 | if (get_unsigned(&band, *argv, 10)) { | |
127 | fprintf(stderr, "Illegal \"priomap\" element\n"); | |
128 | return -1; | |
129 | } | |
130 | if (nprio > TC_PRIO_MAX) { | |
131 | fprintf(stderr, "\"priomap\" index cannot be higher than %u\n", TC_PRIO_MAX); | |
132 | return -1; | |
133 | } | |
134 | priomap[nprio++] = band; | |
135 | } else { | |
136 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
137 | explain(); | |
138 | return -1; | |
139 | } | |
140 | argc--; argv++; | |
141 | } | |
142 | ||
143 | if (!nbands) | |
144 | nbands = nquanta + nstrict; | |
145 | if (!nbands) { | |
146 | fprintf(stderr, "One of \"bands\", \"quanta\" or \"strict\" needs to be specified\n"); | |
147 | explain(); | |
148 | return -1; | |
149 | } | |
150 | if (nbands < 1) { | |
151 | fprintf(stderr, "The number of \"bands\" must be >= 1\n"); | |
152 | explain(); | |
153 | return -1; | |
154 | } | |
155 | if (nstrict + nquanta > nbands) { | |
156 | fprintf(stderr, "Not enough total bands to cover all the strict bands and quanta\n"); | |
157 | explain(); | |
158 | return -1; | |
159 | } | |
160 | for (tmp = 0; tmp < nprio; tmp++) { | |
161 | if (priomap[tmp] >= nbands) { | |
162 | fprintf(stderr, "\"priomap\" element is out of bounds\n"); | |
163 | return -1; | |
164 | } | |
165 | } | |
166 | ||
167 | tail = addattr_nest(n, 1024, TCA_OPTIONS | NLA_F_NESTED); | |
168 | addattr_l(n, 1024, TCA_ETS_NBANDS, &nbands, sizeof(nbands)); | |
169 | if (nstrict) | |
170 | addattr_l(n, 1024, TCA_ETS_NSTRICT, &nstrict, sizeof(nstrict)); | |
171 | if (nquanta) { | |
172 | nest = addattr_nest(n, 1024, TCA_ETS_QUANTA | NLA_F_NESTED); | |
173 | for (tmp = 0; tmp < nquanta; tmp++) | |
174 | addattr_l(n, 1024, TCA_ETS_QUANTA_BAND, | |
175 | &quanta[tmp], sizeof(quanta[0])); | |
176 | addattr_nest_end(n, nest); | |
177 | } | |
178 | if (nprio) { | |
179 | nest = addattr_nest(n, 1024, TCA_ETS_PRIOMAP | NLA_F_NESTED); | |
180 | for (tmp = 0; tmp < nprio; tmp++) | |
181 | addattr_l(n, 1024, TCA_ETS_PRIOMAP_BAND, | |
182 | &priomap[tmp], sizeof(priomap[0])); | |
183 | addattr_nest_end(n, nest); | |
184 | } | |
185 | addattr_nest_end(n, tail); | |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
190 | static int ets_parse_copt(struct qdisc_util *qu, int argc, char **argv, | |
191 | struct nlmsghdr *n, const char *dev) | |
192 | { | |
193 | unsigned int quantum = 0; | |
194 | struct rtattr *tail; | |
195 | ||
196 | while (argc > 0) { | |
197 | if (strcmp(*argv, "quantum") == 0) { | |
198 | if (quantum) { | |
199 | fprintf(stderr, "Duplicate \"quantum\"\n"); | |
200 | return -1; | |
201 | } | |
202 | NEXT_ARG(); | |
203 | quantum = parse_quantum(*argv); | |
204 | if (!quantum) | |
205 | return -1; | |
206 | } else if (strcmp(*argv, "help") == 0) { | |
207 | cexplain(); | |
208 | return -1; | |
209 | } else { | |
210 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
211 | cexplain(); | |
212 | return -1; | |
213 | } | |
214 | argc--; argv++; | |
215 | } | |
216 | ||
217 | tail = addattr_nest(n, 1024, TCA_OPTIONS | NLA_F_NESTED); | |
218 | if (quantum) | |
219 | addattr_l(n, 1024, TCA_ETS_QUANTA_BAND, &quantum, | |
220 | sizeof(quantum)); | |
221 | addattr_nest_end(n, tail); | |
222 | ||
223 | return 0; | |
224 | } | |
225 | ||
226 | static int ets_print_opt_quanta(struct rtattr *opt) | |
227 | { | |
228 | int len = RTA_PAYLOAD(opt); | |
229 | unsigned int offset; | |
230 | ||
231 | open_json_array(PRINT_ANY, "quanta"); | |
232 | for (offset = 0; offset < len; ) { | |
233 | struct rtattr *tb[TCA_ETS_MAX + 1] = {NULL}; | |
234 | struct rtattr *attr; | |
235 | __u32 quantum; | |
236 | ||
237 | attr = RTA_DATA(opt) + offset; | |
238 | parse_rtattr(tb, TCA_ETS_MAX, attr, len - offset); | |
239 | offset += RTA_LENGTH(RTA_PAYLOAD(attr)); | |
240 | ||
241 | if (!tb[TCA_ETS_QUANTA_BAND]) { | |
242 | fprintf(stderr, "No ETS band quantum\n"); | |
243 | return -1; | |
244 | } | |
245 | ||
246 | quantum = rta_getattr_u32(tb[TCA_ETS_QUANTA_BAND]); | |
247 | print_uint(PRINT_ANY, NULL, " %u", quantum); | |
248 | ||
249 | } | |
250 | close_json_array(PRINT_ANY, " "); | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | static int ets_print_opt_priomap(struct rtattr *opt) | |
256 | { | |
257 | int len = RTA_PAYLOAD(opt); | |
258 | unsigned int offset; | |
259 | ||
260 | open_json_array(PRINT_ANY, "priomap"); | |
261 | for (offset = 0; offset < len; ) { | |
262 | struct rtattr *tb[TCA_ETS_MAX + 1] = {NULL}; | |
263 | struct rtattr *attr; | |
264 | __u8 band; | |
265 | ||
266 | attr = RTA_DATA(opt) + offset; | |
267 | parse_rtattr(tb, TCA_ETS_MAX, attr, len - offset); | |
268 | offset += RTA_LENGTH(RTA_PAYLOAD(attr)) + 3 /* padding */; | |
269 | ||
270 | if (!tb[TCA_ETS_PRIOMAP_BAND]) { | |
271 | fprintf(stderr, "No ETS priomap band\n"); | |
272 | return -1; | |
273 | } | |
274 | ||
275 | band = rta_getattr_u8(tb[TCA_ETS_PRIOMAP_BAND]); | |
276 | print_uint(PRINT_ANY, NULL, " %u", band); | |
277 | ||
278 | } | |
279 | close_json_array(PRINT_ANY, " "); | |
280 | ||
281 | return 0; | |
282 | } | |
283 | ||
284 | static int ets_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) | |
285 | { | |
286 | struct rtattr *tb[TCA_ETS_MAX + 1]; | |
287 | __u8 nbands; | |
288 | __u8 nstrict; | |
289 | int err; | |
290 | ||
291 | if (opt == NULL) | |
292 | return 0; | |
293 | ||
294 | parse_rtattr_nested(tb, TCA_ETS_MAX, opt); | |
295 | ||
296 | if (!tb[TCA_ETS_NBANDS] || !tb[TCA_ETS_PRIOMAP]) { | |
297 | fprintf(stderr, "Incomplete ETS options\n"); | |
298 | return -1; | |
299 | } | |
300 | ||
301 | nbands = rta_getattr_u8(tb[TCA_ETS_NBANDS]); | |
302 | print_uint(PRINT_ANY, "bands", "bands %u ", nbands); | |
303 | ||
304 | if (tb[TCA_ETS_NSTRICT]) { | |
305 | nstrict = rta_getattr_u8(tb[TCA_ETS_NSTRICT]); | |
306 | print_uint(PRINT_ANY, "strict", "strict %u ", nstrict); | |
307 | } | |
308 | ||
309 | if (tb[TCA_ETS_QUANTA]) { | |
310 | err = ets_print_opt_quanta(tb[TCA_ETS_QUANTA]); | |
311 | if (err) | |
312 | return err; | |
313 | } | |
314 | ||
315 | return ets_print_opt_priomap(tb[TCA_ETS_PRIOMAP]); | |
316 | } | |
317 | ||
318 | static int ets_print_copt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) | |
319 | { | |
320 | struct rtattr *tb[TCA_ETS_MAX + 1]; | |
321 | __u32 quantum; | |
322 | ||
323 | if (opt == NULL) | |
324 | return 0; | |
325 | ||
326 | parse_rtattr_nested(tb, TCA_ETS_MAX, opt); | |
327 | ||
328 | if (tb[TCA_ETS_QUANTA_BAND]) { | |
329 | quantum = rta_getattr_u32(tb[TCA_ETS_QUANTA_BAND]); | |
330 | print_uint(PRINT_ANY, "quantum", "quantum %u ", quantum); | |
331 | } | |
332 | ||
333 | return 0; | |
334 | } | |
335 | ||
336 | struct qdisc_util ets_qdisc_util = { | |
337 | .id = "ets", | |
338 | .parse_qopt = ets_parse_opt, | |
339 | .parse_copt = ets_parse_copt, | |
340 | .print_qopt = ets_print_opt, | |
341 | .print_copt = ets_print_copt, | |
342 | }; |