]> git.proxmox.com Git - mirror_iproute2.git/blame - tc/m_gate.c
lib: Move sprint_size() from tc here, add print_size()
[mirror_iproute2.git] / tc / m_gate.c
CommitLineData
07d5ee70
PL
1// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2/* Copyright 2020 NXP */
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <string.h>
8#include <linux/if_ether.h>
9#include "utils.h"
10#include "rt_names.h"
11#include "tc_util.h"
12#include "list.h"
13#include <linux/tc_act/tc_gate.h>
14
15struct gate_entry {
16 struct list_head list;
17 uint8_t gate_state;
18 uint32_t interval;
19 int32_t ipv;
20 int32_t maxoctets;
21};
22
23#define CLOCKID_INVALID (-1)
24static const struct clockid_table {
25 const char *name;
26 clockid_t clockid;
27} clockt_map[] = {
28 { "REALTIME", CLOCK_REALTIME },
29 { "TAI", CLOCK_TAI },
30 { "BOOTTIME", CLOCK_BOOTTIME },
31 { "MONOTONIC", CLOCK_MONOTONIC },
32 { NULL }
33};
34
35static void explain(void)
36{
37 fprintf(stderr,
38 "Usage: gate [ priority PRIO-SPEC ] [ base-time BASE-TIME ]\n"
39 " [ cycle-time CYCLE-TIME ]\n"
40 " [ cycle-time-ext CYCLE-TIME-EXT ]\n"
41 " [ clockid CLOCKID ] [flags FLAGS]\n"
42 " [ sched-entry GATE0 INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n"
43 " [ sched-entry GATE1 INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n"
44 " ......\n"
45 " [ sched-entry GATEn INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n"
46 " [ CONTROL ]\n"
47 " GATEn := open | close\n"
48 " INTERVAL : nanoseconds period of gate slot\n"
49 " INTERNAL-PRIO-VALUE : internal priority decide which\n"
50 " rx queue number direct to.\n"
51 " default to be -1 which means wildcard.\n"
52 " MAX-OCTETS : maximum number of MSDU octets that are\n"
53 " permitted to pas the gate during the\n"
54 " specified TimeInterval.\n"
55 " default to be -1 which means wildcard.\n"
56 " CONTROL := pipe | drop | continue | pass |\n"
57 " goto chain <CHAIN_INDEX>\n");
58}
59
60static void usage(void)
61{
62 explain();
63 exit(-1);
64}
65
66static void explain_entry_format(void)
67{
68 fprintf(stderr, "Usage: sched-entry <open | close> <interval> [ <interval ipv> <octets max bytes> ]\n");
69}
70
71static int parse_gate(struct action_util *a, int *argc_p, char ***argv_p,
72 int tca_id, struct nlmsghdr *n);
73static int print_gate(struct action_util *au, FILE *f, struct rtattr *arg);
74
75struct action_util gate_action_util = {
76 .id = "gate",
77 .parse_aopt = parse_gate,
78 .print_aopt = print_gate,
79};
80
81static int get_clockid(__s32 *val, const char *arg)
82{
83 const struct clockid_table *c;
84
85 if (strcasestr(arg, "CLOCK_") != NULL)
86 arg += sizeof("CLOCK_") - 1;
87
88 for (c = clockt_map; c->name; c++) {
89 if (strcasecmp(c->name, arg) == 0) {
90 *val = c->clockid;
91 return 0;
92 }
93 }
94
95 return -1;
96}
97
98static const char *get_clock_name(clockid_t clockid)
99{
100 const struct clockid_table *c;
101
102 for (c = clockt_map; c->name; c++) {
103 if (clockid == c->clockid)
104 return c->name;
105 }
106
107 return "invalid";
108}
109
110static int get_gate_state(__u8 *val, const char *arg)
111{
112 if (!strcasecmp("OPEN", arg)) {
113 *val = 1;
114 return 0;
115 }
116
117 if (!strcasecmp("CLOSE", arg)) {
118 *val = 0;
119 return 0;
120 }
121
122 return -1;
123}
124
125static struct gate_entry *create_gate_entry(uint8_t gate_state,
126 uint32_t interval,
127 int32_t ipv,
128 int32_t maxoctets)
129{
130 struct gate_entry *e;
131
132 e = calloc(1, sizeof(*e));
133 if (!e)
134 return NULL;
135
136 e->gate_state = gate_state;
137 e->interval = interval;
138 e->ipv = ipv;
139 e->maxoctets = maxoctets;
140
141 return e;
142}
143
144static int add_gate_list(struct list_head *gate_entries, struct nlmsghdr *n)
145{
146 struct gate_entry *e;
147
148 list_for_each_entry(e, gate_entries, list) {
149 struct rtattr *a;
150
151 a = addattr_nest(n, 1024, TCA_GATE_ONE_ENTRY | NLA_F_NESTED);
152
153 if (e->gate_state)
154 addattr(n, MAX_MSG, TCA_GATE_ENTRY_GATE);
155
156 addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_INTERVAL,
157 &e->interval, sizeof(e->interval));
158 addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_IPV,
159 &e->ipv, sizeof(e->ipv));
160 addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_MAX_OCTETS,
161 &e->maxoctets, sizeof(e->maxoctets));
162
163 addattr_nest_end(n, a);
164 }
165
166 return 0;
167}
168
169static void free_entries(struct list_head *gate_entries)
170{
171 struct gate_entry *e, *n;
172
173 list_for_each_entry_safe(e, n, gate_entries, list) {
174 list_del(&e->list);
175 free(e);
176 }
177}
178
179static int parse_gate(struct action_util *a, int *argc_p, char ***argv_p,
180 int tca_id, struct nlmsghdr *n)
181{
182 struct tc_gate parm = {.action = TC_ACT_PIPE};
183 struct list_head gate_entries;
184 __s32 clockid = CLOCKID_INVALID;
185 struct rtattr *tail, *nle;
186 char **argv = *argv_p;
187 int argc = *argc_p;
188 __s64 base_time = 0;
189 __s64 cycle_time = 0;
190 __s64 cycle_time_ext = 0;
191 int entry_num = 0;
192 char *invalidarg;
193 __u32 flags = 0;
194 int prio = -1;
195
196 int err;
197
198 if (matches(*argv, "gate") != 0)
199 return -1;
200
201 NEXT_ARG();
202 if (argc <= 0)
203 return -1;
204
205 INIT_LIST_HEAD(&gate_entries);
206
207 while (argc > 0) {
208 if (matches(*argv, "index") == 0) {
209 NEXT_ARG();
210 if (get_u32(&parm.index, *argv, 10)) {
211 invalidarg = "index";
212 goto err_arg;
213 }
214 } else if (matches(*argv, "priority") == 0) {
215 NEXT_ARG();
216 if (get_s32(&prio, *argv, 0)) {
217 invalidarg = "priority";
218 goto err_arg;
219 }
220 } else if (matches(*argv, "base-time") == 0) {
221 NEXT_ARG();
222 if (get_s64(&base_time, *argv, 10) &&
223 get_time64(&base_time, *argv)) {
224 invalidarg = "base-time";
225 goto err_arg;
226 }
227 } else if (matches(*argv, "cycle-time") == 0) {
228 NEXT_ARG();
229 if (get_s64(&cycle_time, *argv, 10) &&
230 get_time64(&cycle_time, *argv)) {
231 invalidarg = "cycle-time";
232 goto err_arg;
233 }
234 } else if (matches(*argv, "cycle-time-ext") == 0) {
235 NEXT_ARG();
236 if (get_s64(&cycle_time_ext, *argv, 10) &&
237 get_time64(&cycle_time_ext, *argv)) {
238 invalidarg = "cycle-time-ext";
239 goto err_arg;
240 }
241 } else if (matches(*argv, "clockid") == 0) {
242 NEXT_ARG();
243 if (get_clockid(&clockid, *argv)) {
244 invalidarg = "clockid";
245 goto err_arg;
246 }
247 } else if (matches(*argv, "flags") == 0) {
248 NEXT_ARG();
249 if (get_u32(&flags, *argv, 0)) {
250 invalidarg = "flags";
251 goto err_arg;
252 }
253 } else if (matches(*argv, "sched-entry") == 0) {
254 unsigned int maxoctets_uint = 0;
255 int32_t maxoctets = -1;
256 struct gate_entry *e;
257 uint8_t gate_state = 0;
258 __s64 interval_s64 = 0;
259 uint32_t interval = 0;
260 int32_t ipv = -1;
261
262 if (!NEXT_ARG_OK()) {
263 explain_entry_format();
cbf64817 264 fprintf(stderr, "\"sched-entry\" is incomplete\n");
07d5ee70
PL
265 free_entries(&gate_entries);
266 return -1;
267 }
268
269 NEXT_ARG();
270
271 if (get_gate_state(&gate_state, *argv)) {
272 explain_entry_format();
cbf64817 273 fprintf(stderr, "\"sched-entry\" is incomplete\n");
07d5ee70
PL
274 free_entries(&gate_entries);
275 return -1;
276 }
277
278 if (!NEXT_ARG_OK()) {
279 explain_entry_format();
cbf64817 280 fprintf(stderr, "\"sched-entry\" is incomplete\n");
07d5ee70
PL
281 free_entries(&gate_entries);
282 return -1;
283 }
284
285 NEXT_ARG();
286
287 if (get_u32(&interval, *argv, 0) &&
288 get_time64(&interval_s64, *argv)) {
289 explain_entry_format();
cbf64817 290 fprintf(stderr, "\"sched-entry\" is incomplete\n");
07d5ee70
PL
291 free_entries(&gate_entries);
292 return -1;
293 }
294
295 if (interval_s64 > UINT_MAX) {
296 fprintf(stderr, "\"interval\" is too large\n");
297 free_entries(&gate_entries);
298 return -1;
299 } else if (interval_s64) {
300 interval = interval_s64;
301 }
302
303 if (!NEXT_ARG_OK())
304 goto create_entry;
305
306 NEXT_ARG();
307
308 if (get_s32(&ipv, *argv, 0)) {
309 PREV_ARG();
310 goto create_entry;
311 }
312
313 if (!gate_state)
314 ipv = -1;
315
316 if (!NEXT_ARG_OK())
317 goto create_entry;
318
319 NEXT_ARG();
320
321 if (get_s32(&maxoctets, *argv, 0) &&
322 get_size(&maxoctets_uint, *argv))
323 PREV_ARG();
324
325 if (maxoctets_uint > INT_MAX) {
326 fprintf(stderr, "\"maxoctets\" is too large\n");
327 free_entries(&gate_entries);
328 return -1;
329 } else if (maxoctets_uint ) {
330 maxoctets = maxoctets_uint;
331 }
332
333 if (!gate_state)
334 maxoctets = -1;
335
336create_entry:
337 e = create_gate_entry(gate_state, interval,
338 ipv, maxoctets);
339 if (!e) {
340 fprintf(stderr, "gate: not enough memory\n");
341 free_entries(&gate_entries);
342 return -1;
343 }
344
345 list_add_tail(&e->list, &gate_entries);
346 entry_num++;
347 } else if (matches(*argv, "help") == 0) {
348 usage();
349 } else {
350 break;
351 }
352
353 argc--;
354 argv++;
355 }
356
357 parse_action_control_dflt(&argc, &argv, &parm.action,
358 false, TC_ACT_PIPE);
359
360 if (!entry_num && !parm.index) {
361 fprintf(stderr, "gate: must add at least one entry\n");
362 return -1;
363 }
364
365 tail = addattr_nest(n, MAX_MSG, tca_id | NLA_F_NESTED);
366 addattr_l(n, MAX_MSG, TCA_GATE_PARMS, &parm, sizeof(parm));
367
368 if (prio != -1)
369 addattr_l(n, MAX_MSG, TCA_GATE_PRIORITY, &prio, sizeof(prio));
370
371 if (flags)
372 addattr_l(n, MAX_MSG, TCA_GATE_FLAGS, &flags, sizeof(flags));
373
374 if (base_time)
375 addattr_l(n, MAX_MSG, TCA_GATE_BASE_TIME,
376 &base_time, sizeof(base_time));
377
378 if (cycle_time)
379 addattr_l(n, MAX_MSG, TCA_GATE_CYCLE_TIME,
380 &cycle_time, sizeof(cycle_time));
381
382 if (cycle_time_ext)
383 addattr_l(n, MAX_MSG, TCA_GATE_CYCLE_TIME_EXT,
384 &cycle_time_ext, sizeof(cycle_time_ext));
385
386 if (clockid != CLOCKID_INVALID)
387 addattr_l(n, MAX_MSG, TCA_GATE_CLOCKID,
388 &clockid, sizeof(clockid));
389
390 nle = addattr_nest(n, MAX_MSG, TCA_GATE_ENTRY_LIST | NLA_F_NESTED);
391 err = add_gate_list(&gate_entries, n);
392 if (err < 0) {
393 fprintf(stderr, "Could not add entries to netlink message\n");
394 free_entries(&gate_entries);
395 return -1;
396 }
397
398 addattr_nest_end(n, nle);
399 addattr_nest_end(n, tail);
400 free_entries(&gate_entries);
401 *argc_p = argc;
402 *argv_p = argv;
403
404 return 0;
405err_arg:
406 invarg(invalidarg, *argv);
407 free_entries(&gate_entries);
408
409 return -1;
410}
411
412static int print_gate_list(struct rtattr *list)
413{
414 struct rtattr *item;
415 int rem;
416
417 rem = RTA_PAYLOAD(list);
418
419 print_string(PRINT_FP, NULL, "%s", _SL_);
420 print_string(PRINT_FP, NULL, "\tschedule:%s", _SL_);
421 open_json_array(PRINT_JSON, "schedule");
422
423 for (item = RTA_DATA(list);
424 RTA_OK(item, rem);
425 item = RTA_NEXT(item, rem)) {
426 struct rtattr *tb[TCA_GATE_ENTRY_MAX + 1];
427 __u32 index = 0, interval = 0;
428 __u8 gate_state = 0;
429 __s32 ipv = -1, maxoctets = -1;
430 char buf[22];
431
432 parse_rtattr_nested(tb, TCA_GATE_ENTRY_MAX, item);
433
434 if (tb[TCA_GATE_ENTRY_INDEX])
435 index = rta_getattr_u32(tb[TCA_GATE_ENTRY_INDEX]);
436
437 if (tb[TCA_GATE_ENTRY_GATE])
438 gate_state = 1;
439
440 if (tb[TCA_GATE_ENTRY_INTERVAL])
441 interval = rta_getattr_u32(tb[TCA_GATE_ENTRY_INTERVAL]);
442
443 if (tb[TCA_GATE_ENTRY_IPV])
444 ipv = rta_getattr_s32(tb[TCA_GATE_ENTRY_IPV]);
445
446 if (tb[TCA_GATE_ENTRY_MAX_OCTETS])
447 maxoctets = rta_getattr_s32(tb[TCA_GATE_ENTRY_MAX_OCTETS]);
448
449 open_json_object(NULL);
450 print_uint(PRINT_ANY, "number", "\t number %4u", index);
451 print_string(PRINT_ANY, "gate_state", "\tgate-state %s ",
452 gate_state ? "open" : "close");
453
454 print_uint(PRINT_JSON, "interval", NULL, interval);
455
456 memset(buf, 0, sizeof(buf));
457 print_string(PRINT_FP, NULL, "\tinterval %s",
458 sprint_time64(interval, buf));
459
460 if (ipv != -1) {
461 print_uint(PRINT_ANY, "ipv", "\t ipv %-10u", ipv);
462 } else {
463 print_int(PRINT_JSON, "ipv", NULL, ipv);
464 print_string(PRINT_FP, NULL, "\t ipv %s", "wildcard");
465 }
466
467 if (maxoctets != -1) {
adbe5de9
PM
468 print_size(PRINT_ANY, "max_octets", "\t max-octets %s",
469 maxoctets);
07d5ee70
PL
470 } else {
471 print_string(PRINT_FP, NULL,
472 "\t max-octets %s", "wildcard");
473 print_int(PRINT_JSON, "max_octets", NULL, maxoctets);
474 }
475
476 close_json_object();
477 print_string(PRINT_FP, NULL, "%s", _SL_);
478 }
479
480 close_json_array(PRINT_ANY, "");
481
482 return 0;
483}
484
485static int print_gate(struct action_util *au, FILE *f, struct rtattr *arg)
486{
487 struct tc_gate *parm;
488 struct rtattr *tb[TCA_GATE_MAX + 1];
489 __s32 clockid = CLOCKID_INVALID;
490 __s64 base_time = 0;
491 __s64 cycle_time = 0;
492 __s64 cycle_time_ext = 0;
493 char buf[22];
494 int prio = -1;
495
496 if (arg == NULL)
497 return -1;
498
499 parse_rtattr_nested(tb, TCA_GATE_MAX, arg);
500
501 if (!tb[TCA_GATE_PARMS]) {
502 fprintf(stderr, "Missing gate parameters\n");
503 return -1;
504 }
505
506 print_string(PRINT_FP, NULL, "%s", "\n");
507
508 parm = RTA_DATA(tb[TCA_GATE_PARMS]);
509
510 if (tb[TCA_GATE_PRIORITY])
511 prio = rta_getattr_s32(tb[TCA_GATE_PRIORITY]);
512
513 if (prio != -1) {
514 print_int(PRINT_ANY, "priority", "\tpriority %-8d", prio);
515 } else {
516 print_string(PRINT_FP, NULL, "\tpriority %s", "wildcard");
517 print_int(PRINT_JSON, "priority", NULL, prio);
518 }
519
520 if (tb[TCA_GATE_CLOCKID])
521 clockid = rta_getattr_s32(tb[TCA_GATE_CLOCKID]);
522 print_string(PRINT_ANY, "clockid", "\tclockid %s",
523 get_clock_name(clockid));
524
525 if (tb[TCA_GATE_FLAGS]) {
526 __u32 flags;
527
528 flags = rta_getattr_u32(tb[TCA_GATE_FLAGS]);
529 print_0xhex(PRINT_ANY, "flags", "\tflags %#x", flags);
530 }
531
532 print_string(PRINT_FP, NULL, "%s", "\n");
533
534 if (tb[TCA_GATE_BASE_TIME])
535 base_time = rta_getattr_s64(tb[TCA_GATE_BASE_TIME]);
536
537 memset(buf, 0, sizeof(buf));
538 print_string(PRINT_FP, NULL, "\tbase-time %s",
539 sprint_time64(base_time, buf));
540 print_lluint(PRINT_JSON, "base_time", NULL, base_time);
541
542 if (tb[TCA_GATE_CYCLE_TIME])
543 cycle_time = rta_getattr_s64(tb[TCA_GATE_CYCLE_TIME]);
544
545 memset(buf, 0, sizeof(buf));
546 print_string(PRINT_FP, NULL,
547 "\tcycle-time %s", sprint_time64(cycle_time, buf));
548 print_lluint(PRINT_JSON, "cycle_time", NULL, cycle_time);
549
550 if (tb[TCA_GATE_CYCLE_TIME_EXT])
551 cycle_time_ext = rta_getattr_s64(tb[TCA_GATE_CYCLE_TIME_EXT]);
552
553 memset(buf, 0, sizeof(buf));
554 print_string(PRINT_FP, NULL, "\tcycle-time-ext %s",
555 sprint_time64(cycle_time_ext, buf));
556 print_lluint(PRINT_JSON, "cycle_time_ext", NULL, cycle_time_ext);
557
558 if (tb[TCA_GATE_ENTRY_LIST])
559 print_gate_list(tb[TCA_GATE_ENTRY_LIST]);
560
561 print_action_control(f, "\t", parm->action, "");
562
563 print_uint(PRINT_ANY, "index", "\n\t index %u", parm->index);
564 print_int(PRINT_ANY, "ref", " ref %d", parm->refcnt);
565 print_int(PRINT_ANY, "bind", " bind %d", parm->bindcnt);
566
567 if (show_stats) {
568 if (tb[TCA_GATE_TM]) {
569 struct tcf_t *tm = RTA_DATA(tb[TCA_GATE_TM]);
570
571 print_tm(f, tm);
572 }
573 }
574
575 print_string(PRINT_FP, NULL, "%s", "\n");
576
577 return 0;
578}