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 *data;
63 size_t data_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 uint16_t len;
70
71 if (mnl_attr_get_type(attr) != ga->attr)
72 return MNL_CB_OK;
73
74 len = mnl_attr_get_payload_len(attr);
75 if (len != ga->data_len) {
76 fprintf(stderr, "Wrong len %d, expected %zd\n", len, ga->data_len);
77 return MNL_CB_ERROR;
78 }
79
80 memcpy(ga->data, mnl_attr_get_payload(attr), ga->data_len);
81 return MNL_CB_STOP;
82}
83
84static int dcb_get_attribute_attr_cb(const struct nlattr *attr, void *data)
85{
86 if (mnl_attr_get_type(attr) != DCB_ATTR_IEEE)
87 return MNL_CB_OK;
88
89 return mnl_attr_parse_nested(attr, dcb_get_attribute_attr_ieee_cb, data);
90}
91
92static int dcb_get_attribute_cb(const struct nlmsghdr *nlh, void *data)
93{
94 return mnl_attr_parse(nlh, sizeof(struct dcbmsg), dcb_get_attribute_attr_cb, data);
95}
96
97static int dcb_set_attribute_attr_cb(const struct nlattr *attr, void *data)
98{
99 uint16_t len;
100 uint8_t err;
101
102 if (mnl_attr_get_type(attr) != DCB_ATTR_IEEE)
103 return MNL_CB_OK;
104
105 len = mnl_attr_get_payload_len(attr);
106 if (len != 1) {
107 fprintf(stderr, "Response attribute expected to have size 1, not %d\n", len);
108 return MNL_CB_ERROR;
109 }
110
111 err = mnl_attr_get_u8(attr);
112 if (err) {
113 fprintf(stderr, "Error when attempting to set attribute: %s\n",
114 strerror(err));
115 return MNL_CB_ERROR;
116 }
117
118 return MNL_CB_STOP;
119}
120
121static int dcb_set_attribute_cb(const struct nlmsghdr *nlh, void *data)
122{
123 return mnl_attr_parse(nlh, sizeof(struct dcbmsg), dcb_set_attribute_attr_cb, data);
124}
125
126static int dcb_talk(struct dcb *dcb, struct nlmsghdr *nlh, mnl_cb_t cb, void *data)
127{
128 int ret;
129
130 ret = mnl_socket_sendto(dcb->nl, nlh, nlh->nlmsg_len);
131 if (ret < 0) {
132 perror("mnl_socket_sendto");
133 return -1;
134 }
135
136 return mnlu_socket_recv_run(dcb->nl, nlh->nlmsg_seq, dcb->buf, MNL_SOCKET_BUFFER_SIZE,
137 cb, data);
138}
139
140static struct nlmsghdr *dcb_prepare(struct dcb *dcb, const char *dev,
141 uint32_t nlmsg_type, uint8_t dcb_cmd)
142{
143 struct dcbmsg dcbm = {
144 .cmd = dcb_cmd,
145 };
146 struct nlmsghdr *nlh;
147
148 nlh = mnlu_msg_prepare(dcb->buf, nlmsg_type, NLM_F_REQUEST, &dcbm, sizeof(dcbm));
149 mnl_attr_put_strz(nlh, DCB_ATTR_IFNAME, dev);
150 return nlh;
151}
152
153int dcb_get_attribute(struct dcb *dcb, const char *dev, int attr, void *data, size_t data_len)
154{
155 struct dcb_get_attribute ga;
156 struct nlmsghdr *nlh;
157 int ret;
158
159 nlh = dcb_prepare(dcb, dev, RTM_GETDCB, DCB_CMD_IEEE_GET);
160
161 ga = (struct dcb_get_attribute) {
162 .dcb = dcb,
163 .attr = attr,
164 .data = data,
165 .data_len = data_len,
166 };
167 ret = dcb_talk(dcb, nlh, dcb_get_attribute_cb, &ga);
168 if (ret) {
169 perror("Attribute read");
170 return ret;
171 }
172 return 0;
173}
174
175int dcb_set_attribute(struct dcb *dcb, const char *dev, int attr, const void *data, size_t data_len)
176{
177 struct nlmsghdr *nlh;
178 struct nlattr *nest;
179 int ret;
180
181 nlh = dcb_prepare(dcb, dev, RTM_SETDCB, DCB_CMD_IEEE_SET);
182
183 nest = mnl_attr_nest_start(nlh, DCB_ATTR_IEEE);
184 mnl_attr_put(nlh, attr, data_len, data);
185 mnl_attr_nest_end(nlh, nest);
186
187 ret = dcb_talk(dcb, nlh, dcb_set_attribute_cb, NULL);
188 if (ret) {
189 perror("Attribute write");
190 return ret;
191 }
192 return 0;
193}
194
195void dcb_print_array_u8(const __u8 *array, size_t size)
196{
197 SPRINT_BUF(b);
198 size_t i;
199
200 for (i = 0; i < size; i++) {
201 snprintf(b, sizeof(b), "%zd:%%d ", i);
202 print_uint(PRINT_ANY, NULL, b, array[i]);
203 }
204}
205
206void dcb_print_array_u64(const __u64 *array, size_t size)
207{
208 SPRINT_BUF(b);
209 size_t i;
210
211 for (i = 0; i < size; i++) {
212 snprintf(b, sizeof(b), "%zd:%%" PRIu64 " ", i);
213 print_u64(PRINT_ANY, NULL, b, array[i]);
214 }
215}
216
217void dcb_print_array_on_off(const __u8 *array, size_t size)
218{
219 SPRINT_BUF(b);
220 size_t i;
221
222 for (i = 0; i < size; i++) {
223 snprintf(b, sizeof(b), "%zd:%%s ", i);
224 print_on_off(PRINT_ANY, NULL, b, array[i]);
225 }
226}
227
228void dcb_print_array_kw(const __u8 *array, size_t array_size,
229 const char *const kw[], size_t kw_size)
230{
231 SPRINT_BUF(b);
232 size_t i;
233
234 for (i = 0; i < array_size; i++) {
235 __u8 emt = array[i];
236
237 snprintf(b, sizeof(b), "%zd:%%s ", i);
238 if (emt < kw_size && kw[emt])
239 print_string(PRINT_ANY, NULL, b, kw[emt]);
240 else
241 print_string(PRINT_ANY, NULL, b, "???");
242 }
243}
244
245void dcb_print_named_array(const char *json_name, const char *fp_name,
246 const __u8 *array, size_t size,
247 void (*print_array)(const __u8 *, size_t))
248{
249 open_json_array(PRINT_JSON, json_name);
250 print_string(PRINT_FP, NULL, "%s ", fp_name);
251 print_array(array, size);
252 close_json_array(PRINT_JSON, json_name);
253}
254
255int dcb_parse_mapping(const char *what_key, __u32 key, __u32 max_key,
256 const char *what_value, __u64 value, __u64 max_value,
257 void (*set_array)(__u32 index, __u64 value, void *data),
258 void *set_array_data)
259{
260 bool is_all = key == (__u32) -1;
261
262 if (!is_all && key > max_key) {
263 fprintf(stderr, "In %s:%s mapping, %s is expected to be 0..%d\n",
264 what_key, what_value, what_key, max_key);
265 return -EINVAL;
266 }
267
268 if (value > max_value) {
269 fprintf(stderr, "In %s:%s mapping, %s is expected to be 0..%llu\n",
270 what_key, what_value, what_value, max_value);
271 return -EINVAL;
272 }
273
274 if (is_all) {
275 for (key = 0; key <= max_key; key++)
276 set_array(key, value, set_array_data);
277 } else {
278 set_array(key, value, set_array_data);
279 }
280
281 return 0;
282}
283
284void dcb_set_u8(__u32 key, __u64 value, void *data)
285{
286 __u8 *array = data;
287
288 array[key] = value;
289}
290
291void dcb_set_u32(__u32 key, __u64 value, void *data)
292{
293 __u32 *array = data;
294
295 array[key] = value;
296}
297
298void dcb_set_u64(__u32 key, __u64 value, void *data)
299{
300 __u64 *array = data;
301
302 array[key] = value;
303}
304
305int dcb_cmd_parse_dev(struct dcb *dcb, int argc, char **argv,
306 int (*and_then)(struct dcb *dcb, const char *dev,
307 int argc, char **argv),
308 void (*help)(void))
309{
310 const char *dev;
311
312 if (!argc || matches(*argv, "help") == 0) {
313 help();
314 return 0;
315 } else if (matches(*argv, "dev") == 0) {
316 NEXT_ARG();
317 dev = *argv;
318 if (check_ifname(dev)) {
319 invarg("not a valid ifname", *argv);
320 return -EINVAL;
321 }
322 NEXT_ARG_FWD();
323 return and_then(dcb, dev, argc, argv);
324 } else {
325 fprintf(stderr, "Expected `dev DEV', not `%s'", *argv);
326 help();
327 return -EINVAL;
328 }
329}
330
331static void dcb_help(void)
332{
333 fprintf(stderr,
334 "Usage: dcb [ OPTIONS ] OBJECT { COMMAND | help }\n"
335 " dcb [ -f | --force ] { -b | --batch } filename [ -n | --netns ] netnsname\n"
336 "where OBJECT := { buffer | ets | maxrate | pfc }\n"
337 " OPTIONS := [ -V | --Version | -i | --iec | -j | --json\n"
338 " | -p | --pretty | -s | --statistics | -v | --verbose]\n");
339}
340
341static int dcb_cmd(struct dcb *dcb, int argc, char **argv)
342{
343 if (!argc || matches(*argv, "help") == 0) {
344 dcb_help();
345 return 0;
346 } else if (matches(*argv, "buffer") == 0) {
347 return dcb_cmd_buffer(dcb, argc - 1, argv + 1);
348 } else if (matches(*argv, "ets") == 0) {
349 return dcb_cmd_ets(dcb, argc - 1, argv + 1);
350 } else if (matches(*argv, "maxrate") == 0) {
351 return dcb_cmd_maxrate(dcb, argc - 1, argv + 1);
352 } else if (matches(*argv, "pfc") == 0) {
353 return dcb_cmd_pfc(dcb, argc - 1, argv + 1);
354 }
355
356 fprintf(stderr, "Object \"%s\" is unknown\n", *argv);
357 return -ENOENT;
358}
359
360static int dcb_batch_cmd(int argc, char *argv[], void *data)
361{
362 struct dcb *dcb = data;
363
364 return dcb_cmd(dcb, argc, argv);
365}
366
367static int dcb_batch(struct dcb *dcb, const char *name, bool force)
368{
369 return do_batch(name, force, dcb_batch_cmd, dcb);
370}
371
372int main(int argc, char **argv)
373{
374 static const struct option long_options[] = {
375 { "Version", no_argument, NULL, 'V' },
376 { "force", no_argument, NULL, 'f' },
377 { "batch", required_argument, NULL, 'b' },
378 { "iec", no_argument, NULL, 'i' },
379 { "json", no_argument, NULL, 'j' },
380 { "pretty", no_argument, NULL, 'p' },
381 { "statistics", no_argument, NULL, 's' },
382 { "netns", required_argument, NULL, 'n' },
383 { "help", no_argument, NULL, 'h' },
384 { NULL, 0, NULL, 0 }
385 };
386 const char *batch_file = NULL;
387 bool force = false;
388 struct dcb *dcb;
389 int opt;
390 int err;
391 int ret;
392
393 dcb = dcb_alloc();
394 if (!dcb) {
395 fprintf(stderr, "Failed to allocate memory for dcb\n");
396 return EXIT_FAILURE;
397 }
398
399 while ((opt = getopt_long(argc, argv, "b:fhijn:psvV",
400 long_options, NULL)) >= 0) {
401
402 switch (opt) {
403 case 'V':
404 printf("dcb utility, iproute2-%s\n", version);
405 ret = EXIT_SUCCESS;
406 goto dcb_free;
407 case 'f':
408 force = true;
409 break;
410 case 'b':
411 batch_file = optarg;
412 break;
413 case 'j':
414 dcb->json_output = true;
415 break;
416 case 'p':
417 pretty = true;
418 break;
419 case 's':
420 dcb->stats = true;
421 break;
422 case 'n':
423 if (netns_switch(optarg)) {
424 ret = EXIT_FAILURE;
425 goto dcb_free;
426 }
427 break;
428 case 'i':
429 dcb->use_iec = true;
430 break;
431 case 'h':
432 dcb_help();
433 return 0;
434 default:
435 fprintf(stderr, "Unknown option.\n");
436 dcb_help();
437 ret = EXIT_FAILURE;
438 goto dcb_free;
439 }
440 }
441
442 argc -= optind;
443 argv += optind;
444
445 err = dcb_init(dcb);
446 if (err) {
447 ret = EXIT_FAILURE;
448 goto dcb_free;
449 }
450
451 if (batch_file)
452 err = dcb_batch(dcb, batch_file, force);
453 else
454 err = dcb_cmd(dcb, argc, argv);
455
456 if (err) {
457 ret = EXIT_FAILURE;
458 goto dcb_fini;
459 }
460
461 ret = EXIT_SUCCESS;
462
463dcb_fini:
464 dcb_fini(dcb);
465dcb_free:
466 dcb_free(dcb);
467
468 return ret;
469}
470