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