1
2
3#include <inttypes.h>
4#include <stdio.h>
5#include <linux/dcbnl.h>
6#include <libmnl/libmnl.h>
7#include <getopt.h>
8
9#include "dcb.h"
10#include "mnl_utils.h"
11#include "namespace.h"
12#include "utils.h"
13#include "version.h"
14
15static int dcb_init(struct dcb *dcb)
16{
17 dcb->buf = malloc(MNL_SOCKET_BUFFER_SIZE);
18 if (dcb->buf == NULL) {
19 perror("Netlink buffer allocation");
20 return -1;
21 }
22
23 dcb->nl = mnlu_socket_open(NETLINK_ROUTE);
24 if (dcb->nl == NULL) {
25 perror("Open netlink socket");
26 goto err_socket_open;
27 }
28
29 new_json_obj_plain(dcb->json_output);
30 return 0;
31
32err_socket_open:
33 free(dcb->buf);
34 return -1;
35}
36
37static void dcb_fini(struct dcb *dcb)
38{
39 delete_json_obj_plain();
40 mnl_socket_close(dcb->nl);
41 free(dcb->buf);
42}
43
44static struct dcb *dcb_alloc(void)
45{
46 struct dcb *dcb;
47
48 dcb = calloc(1, sizeof(*dcb));
49 if (!dcb)
50 return NULL;
51 return dcb;
52}
53
54static void dcb_free(struct dcb *dcb)
55{
56 free(dcb);
57}
58
59struct dcb_get_attribute {
60 struct dcb *dcb;
61 int attr;
62 void *payload;
63 __u16 payload_len;
64};
65
66static int dcb_get_attribute_attr_ieee_cb(const struct nlattr *attr, void *data)
67{
68 struct dcb_get_attribute *ga = data;
69
70 if (mnl_attr_get_type(attr) != ga->attr)
71 return MNL_CB_OK;
72
73 ga->payload = mnl_attr_get_payload(attr);
74 ga->payload_len = mnl_attr_get_payload_len(attr);
75 return MNL_CB_OK;
76}
77
78static int dcb_get_attribute_attr_cb(const struct nlattr *attr, void *data)
79{
80 if (mnl_attr_get_type(attr) != DCB_ATTR_IEEE)
81 return MNL_CB_OK;
82
83 return mnl_attr_parse_nested(attr, dcb_get_attribute_attr_ieee_cb, data);
84}
85
86static int dcb_get_attribute_cb(const struct nlmsghdr *nlh, void *data)
87{
88 return mnl_attr_parse(nlh, sizeof(struct dcbmsg), dcb_get_attribute_attr_cb, data);
89}
90
91static int dcb_get_attribute_bare_cb(const struct nlmsghdr *nlh, void *data)
92{
93
94
95
96
97 return mnl_attr_parse(nlh, sizeof(struct dcbmsg),
98 dcb_get_attribute_attr_ieee_cb, data);
99}
100
101struct dcb_set_attribute_response {
102 int response_attr;
103};
104
105static int dcb_set_attribute_attr_cb(const struct nlattr *attr, void *data)
106{
107 struct dcb_set_attribute_response *resp = data;
108 uint16_t len;
109 int8_t err;
110
111 if (mnl_attr_get_type(attr) != resp->response_attr)
112 return MNL_CB_OK;
113
114 len = mnl_attr_get_payload_len(attr);
115 if (len != 1) {
116 fprintf(stderr, "Response attribute expected to have size 1, not %d\n", len);
117 return MNL_CB_ERROR;
118 }
119
120
121
122
123 err = mnl_attr_get_u8(attr);
124 if (err) {
125 errno = -err;
126 return MNL_CB_ERROR;
127 }
128
129 return MNL_CB_OK;
130}
131
132static int dcb_set_attribute_cb(const struct nlmsghdr *nlh, void *data)
133{
134 return mnl_attr_parse(nlh, sizeof(struct dcbmsg), dcb_set_attribute_attr_cb, data);
135}
136
137static int dcb_talk(struct dcb *dcb, struct nlmsghdr *nlh, mnl_cb_t cb, void *data)
138{
139 int ret;
140
141 ret = mnl_socket_sendto(dcb->nl, nlh, nlh->nlmsg_len);
142 if (ret < 0) {
143 perror("mnl_socket_sendto");
144 return -1;
145 }
146
147 return mnlu_socket_recv_run(dcb->nl, nlh->nlmsg_seq, dcb->buf, MNL_SOCKET_BUFFER_SIZE,
148 cb, data);
149}
150
151static struct nlmsghdr *dcb_prepare(struct dcb *dcb, const char *dev,
152 uint32_t nlmsg_type, uint8_t dcb_cmd)
153{
154 struct dcbmsg dcbm = {
155 .cmd = dcb_cmd,
156 };
157 struct nlmsghdr *nlh;
158
159 nlh = mnlu_msg_prepare(dcb->buf, nlmsg_type, NLM_F_REQUEST | NLM_F_ACK,
160 &dcbm, sizeof(dcbm));
161 mnl_attr_put_strz(nlh, DCB_ATTR_IFNAME, dev);
162 return nlh;
163}
164
165static int __dcb_get_attribute(struct dcb *dcb, int command,
166 const char *dev, int attr,
167 void **payload_p, __u16 *payload_len_p,
168 int (*get_attribute_cb)(const struct nlmsghdr *nlh,
169 void *data))
170{
171 struct dcb_get_attribute ga;
172 struct nlmsghdr *nlh;
173 int ret;
174
175 nlh = dcb_prepare(dcb, dev, RTM_GETDCB, command);
176
177 ga = (struct dcb_get_attribute) {
178 .dcb = dcb,
179 .attr = attr,
180 .payload = NULL,
181 };
182 ret = dcb_talk(dcb, nlh, get_attribute_cb, &ga);
183 if (ret) {
184 perror("Attribute read");
185 return ret;
186 }
187 if (ga.payload == NULL) {
188 perror("Attribute not found");
189 return -ENOENT;
190 }
191
192 *payload_p = ga.payload;
193 *payload_len_p = ga.payload_len;
194 return 0;
195}
196
197int dcb_get_attribute_va(struct dcb *dcb, const char *dev, int attr,
198 void **payload_p, __u16 *payload_len_p)
199{
200 return __dcb_get_attribute(dcb, DCB_CMD_IEEE_GET, dev, attr,
201 payload_p, payload_len_p,
202 dcb_get_attribute_cb);
203}
204
205int dcb_get_attribute_bare(struct dcb *dcb, int cmd, const char *dev, int attr,
206 void **payload_p, __u16 *payload_len_p)
207{
208 return __dcb_get_attribute(dcb, cmd, dev, attr,
209 payload_p, payload_len_p,
210 dcb_get_attribute_bare_cb);
211}
212
213int dcb_get_attribute(struct dcb *dcb, const char *dev, int attr, void *data, size_t data_len)
214{
215 __u16 payload_len;
216 void *payload;
217 int ret;
218
219 ret = dcb_get_attribute_va(dcb, dev, attr, &payload, &payload_len);
220 if (ret)
221 return ret;
222
223 if (payload_len != data_len) {
224 fprintf(stderr, "Wrong len %d, expected %zd\n", payload_len, data_len);
225 return -EINVAL;
226 }
227
228 memcpy(data, payload, data_len);
229 return 0;
230}
231
232static int __dcb_set_attribute(struct dcb *dcb, int command, const char *dev,
233 int (*cb)(struct dcb *, struct nlmsghdr *, void *),
234 void *data, int response_attr)
235{
236 struct dcb_set_attribute_response resp = {
237 .response_attr = response_attr,
238 };
239 struct nlmsghdr *nlh;
240 int ret;
241
242 nlh = dcb_prepare(dcb, dev, RTM_SETDCB, command);
243
244 ret = cb(dcb, nlh, data);
245 if (ret)
246 return ret;
247
248 errno = 0;
249 ret = dcb_talk(dcb, nlh, dcb_set_attribute_cb, &resp);
250 if (ret) {
251 if (errno)
252 perror("Attribute write");
253 return ret;
254 }
255 return 0;
256}
257
258struct dcb_set_attribute_ieee_cb {
259 int (*cb)(struct dcb *dcb, struct nlmsghdr *nlh, void *data);
260 void *data;
261};
262
263static int dcb_set_attribute_ieee_cb(struct dcb *dcb, struct nlmsghdr *nlh, void *data)
264{
265 struct dcb_set_attribute_ieee_cb *ieee_data = data;
266 struct nlattr *nest;
267 int ret;
268
269 nest = mnl_attr_nest_start(nlh, DCB_ATTR_IEEE);
270 ret = ieee_data->cb(dcb, nlh, ieee_data->data);
271 if (ret)
272 return ret;
273 mnl_attr_nest_end(nlh, nest);
274
275 return 0;
276}
277
278int dcb_set_attribute_va(struct dcb *dcb, int command, const char *dev,
279 int (*cb)(struct dcb *dcb, struct nlmsghdr *nlh, void *data),
280 void *data)
281{
282 struct dcb_set_attribute_ieee_cb ieee_data = {
283 .cb = cb,
284 .data = data,
285 };
286
287 return __dcb_set_attribute(dcb, command, dev,
288 &dcb_set_attribute_ieee_cb, &ieee_data,
289 DCB_ATTR_IEEE);
290}
291
292struct dcb_set_attribute {
293 int attr;
294 const void *data;
295 size_t data_len;
296};
297
298static int dcb_set_attribute_put(struct dcb *dcb, struct nlmsghdr *nlh, void *data)
299{
300 struct dcb_set_attribute *dsa = data;
301
302 mnl_attr_put(nlh, dsa->attr, dsa->data_len, dsa->data);
303 return 0;
304}
305
306int dcb_set_attribute(struct dcb *dcb, const char *dev, int attr, const void *data, size_t data_len)
307{
308 struct dcb_set_attribute dsa = {
309 .attr = attr,
310 .data = data,
311 .data_len = data_len,
312 };
313
314 return dcb_set_attribute_va(dcb, DCB_CMD_IEEE_SET, dev,
315 &dcb_set_attribute_put, &dsa);
316}
317
318int dcb_set_attribute_bare(struct dcb *dcb, int command, const char *dev,
319 int attr, const void *data, size_t data_len,
320 int response_attr)
321{
322 struct dcb_set_attribute dsa = {
323 .attr = attr,
324 .data = data,
325 .data_len = data_len,
326 };
327
328 return __dcb_set_attribute(dcb, command, dev,
329 &dcb_set_attribute_put, &dsa, response_attr);
330}
331
332void dcb_print_array_u8(const __u8 *array, size_t size)
333{
334 size_t i;
335
336 for (i = 0; i < size; i++) {
337 print_uint(PRINT_JSON, NULL, NULL, array[i]);
338 print_uint(PRINT_FP, NULL, "%zd:", i);
339 print_uint(PRINT_FP, NULL, "%d ", array[i]);
340 }
341}
342
343void dcb_print_array_u64(const __u64 *array, size_t size)
344{
345 size_t i;
346
347 for (i = 0; i < size; i++) {
348 print_u64(PRINT_JSON, NULL, NULL, array[i]);
349 print_uint(PRINT_FP, NULL, "%zd:", i);
350 print_u64(PRINT_FP, NULL, "%" PRIu64 " ", array[i]);
351 }
352}
353
354void dcb_print_array_on_off(const __u8 *array, size_t size)
355{
356 size_t i;
357
358 for (i = 0; i < size; i++) {
359 print_on_off(PRINT_JSON, NULL, NULL, array[i]);
360 print_uint(PRINT_FP, NULL, "%zd:", i);
361 print_on_off(PRINT_FP, NULL, "%s ", array[i]);
362 }
363}
364
365void dcb_print_array_kw(const __u8 *array, size_t array_size,
366 const char *const kw[], size_t kw_size)
367{
368 size_t i;
369
370 for (i = 0; i < array_size; i++) {
371 const char *str = "???";
372 __u8 emt = array[i];
373
374 if (emt < kw_size && kw[emt])
375 str = kw[emt];
376 print_string(PRINT_JSON, NULL, NULL, str);
377 print_uint(PRINT_FP, NULL, "%zd:", i);
378 print_string(PRINT_FP, NULL, "%s ", str);
379 }
380}
381
382void dcb_print_named_array(const char *json_name, const char *fp_name,
383 const __u8 *array, size_t size,
384 void (*print_array)(const __u8 *, size_t))
385{
386 open_json_array(PRINT_JSON, json_name);
387 print_string(PRINT_FP, NULL, "%s ", fp_name);
388 print_array(array, size);
389 close_json_array(PRINT_JSON, json_name);
390}
391
392int dcb_parse_mapping(const char *what_key, __u32 key, __u32 max_key,
393 const char *what_value, __u64 value, __u64 max_value,
394 void (*set_array)(__u32 index, __u64 value, void *data),
395 void *set_array_data)
396{
397 bool is_all = key == (__u32) -1;
398
399 if (!is_all && key > max_key) {
400 fprintf(stderr, "In %s:%s mapping, %s is expected to be 0..%d\n",
401 what_key, what_value, what_key, max_key);
402 return -EINVAL;
403 }
404
405 if (value > max_value) {
406 fprintf(stderr, "In %s:%s mapping, %s is expected to be 0..%llu\n",
407 what_key, what_value, what_value, max_value);
408 return -EINVAL;
409 }
410
411 if (is_all) {
412 for (key = 0; key <= max_key; key++)
413 set_array(key, value, set_array_data);
414 } else {
415 set_array(key, value, set_array_data);
416 }
417
418 return 0;
419}
420
421void dcb_set_u8(__u32 key, __u64 value, void *data)
422{
423 __u8 *array = data;
424
425 array[key] = value;
426}
427
428void dcb_set_u32(__u32 key, __u64 value, void *data)
429{
430 __u32 *array = data;
431
432 array[key] = value;
433}
434
435void dcb_set_u64(__u32 key, __u64 value, void *data)
436{
437 __u64 *array = data;
438
439 array[key] = value;
440}
441
442int dcb_cmd_parse_dev(struct dcb *dcb, int argc, char **argv,
443 int (*and_then)(struct dcb *dcb, const char *dev,
444 int argc, char **argv),
445 void (*help)(void))
446{
447 const char *dev;
448
449 if (!argc || matches(*argv, "help") == 0) {
450 help();
451 return 0;
452 } else if (matches(*argv, "dev") == 0) {
453 NEXT_ARG();
454 dev = *argv;
455 if (check_ifname(dev)) {
456 invarg("not a valid ifname", *argv);
457 return -EINVAL;
458 }
459 NEXT_ARG_FWD();
460 return and_then(dcb, dev, argc, argv);
461 } else {
462 fprintf(stderr, "Expected `dev DEV', not `%s'", *argv);
463 help();
464 return -EINVAL;
465 }
466}
467
468static void dcb_help(void)
469{
470 fprintf(stderr,
471 "Usage: dcb [ OPTIONS ] OBJECT { COMMAND | help }\n"
472 " dcb [ -f | --force ] { -b | --batch } filename [ -n | --netns ] netnsname\n"
473 "where OBJECT := { app | apptrust | buffer | dcbx | ets | maxrate | pfc | rewr }\n"
474 " OPTIONS := [ -V | --Version | -i | --iec | -j | --json\n"
475 " | -N | --Numeric | -p | --pretty\n"
476 " | -s | --statistics | -v | --verbose]\n");
477}
478
479static int dcb_cmd(struct dcb *dcb, int argc, char **argv)
480{
481 if (!argc || matches(*argv, "help") == 0) {
482 dcb_help();
483 return 0;
484 } else if (matches(*argv, "app") == 0) {
485 return dcb_cmd_app(dcb, argc - 1, argv + 1);
486 } else if (strcmp(*argv, "apptrust") == 0) {
487 return dcb_cmd_apptrust(dcb, argc - 1, argv + 1);
488 } else if (strcmp(*argv, "rewr") == 0) {
489 return dcb_cmd_rewr(dcb, argc - 1, argv + 1);
490 } else if (matches(*argv, "buffer") == 0) {
491 return dcb_cmd_buffer(dcb, argc - 1, argv + 1);
492 } else if (matches(*argv, "dcbx") == 0) {
493 return dcb_cmd_dcbx(dcb, argc - 1, argv + 1);
494 } else if (matches(*argv, "ets") == 0) {
495 return dcb_cmd_ets(dcb, argc - 1, argv + 1);
496 } else if (matches(*argv, "maxrate") == 0) {
497 return dcb_cmd_maxrate(dcb, argc - 1, argv + 1);
498 } else if (matches(*argv, "pfc") == 0) {
499 return dcb_cmd_pfc(dcb, argc - 1, argv + 1);
500 }
501
502 fprintf(stderr, "Object \"%s\" is unknown\n", *argv);
503 return -ENOENT;
504}
505
506static int dcb_batch_cmd(int argc, char *argv[], void *data)
507{
508 struct dcb *dcb = data;
509
510 return dcb_cmd(dcb, argc, argv);
511}
512
513static int dcb_batch(struct dcb *dcb, const char *name, bool force)
514{
515 return do_batch(name, force, dcb_batch_cmd, dcb);
516}
517
518int main(int argc, char **argv)
519{
520 static const struct option long_options[] = {
521 { "Version", no_argument, NULL, 'V' },
522 { "force", no_argument, NULL, 'f' },
523 { "batch", required_argument, NULL, 'b' },
524 { "iec", no_argument, NULL, 'i' },
525 { "json", no_argument, NULL, 'j' },
526 { "Numeric", no_argument, NULL, 'N' },
527 { "pretty", no_argument, NULL, 'p' },
528 { "statistics", no_argument, NULL, 's' },
529 { "netns", required_argument, NULL, 'n' },
530 { "help", no_argument, NULL, 'h' },
531 { NULL, 0, NULL, 0 }
532 };
533 const char *batch_file = NULL;
534 bool force = false;
535 struct dcb *dcb;
536 int opt;
537 int err;
538 int ret;
539
540 dcb = dcb_alloc();
541 if (!dcb) {
542 fprintf(stderr, "Failed to allocate memory for dcb\n");
543 return EXIT_FAILURE;
544 }
545
546 while ((opt = getopt_long(argc, argv, "b:fhijn:psvNV",
547 long_options, NULL)) >= 0) {
548
549 switch (opt) {
550 case 'V':
551 printf("dcb utility, iproute2-%s\n", version);
552 ret = EXIT_SUCCESS;
553 goto dcb_free;
554 case 'f':
555 force = true;
556 break;
557 case 'b':
558 batch_file = optarg;
559 break;
560 case 'j':
561 dcb->json_output = true;
562 break;
563 case 'N':
564 dcb->numeric = true;
565 break;
566 case 'p':
567 pretty = true;
568 break;
569 case 's':
570 dcb->stats = true;
571 break;
572 case 'n':
573 if (netns_switch(optarg)) {
574 ret = EXIT_FAILURE;
575 goto dcb_free;
576 }
577 break;
578 case 'i':
579 dcb->use_iec = true;
580 break;
581 case 'h':
582 dcb_help();
583 ret = EXIT_SUCCESS;
584 goto dcb_free;
585 default:
586 fprintf(stderr, "Unknown option.\n");
587 dcb_help();
588 ret = EXIT_FAILURE;
589 goto dcb_free;
590 }
591 }
592
593 argc -= optind;
594 argv += optind;
595
596 err = dcb_init(dcb);
597 if (err) {
598 ret = EXIT_FAILURE;
599 goto dcb_free;
600 }
601
602 if (batch_file)
603 err = dcb_batch(dcb, batch_file, force);
604 else
605 err = dcb_cmd(dcb, argc, argv);
606
607 if (err) {
608 ret = EXIT_FAILURE;
609 goto dcb_fini;
610 }
611
612 ret = EXIT_SUCCESS;
613
614dcb_fini:
615 dcb_fini(dcb);
616dcb_free:
617 dcb_free(dcb);
618
619 return ret;
620}
621