• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * link.c	TIPC link functionality.
3  *
4  *		This program is free software; you can redistribute it and/or
5  *		modify it under the terms of the GNU General Public License
6  *		as published by the Free Software Foundation; either version
7  *		2 of the License, or (at your option) any later version.
8  *
9  * Authors:	Richard Alpe <richard.alpe@ericsson.com>
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <errno.h>
16 
17 #include <linux/tipc_netlink.h>
18 #include <linux/tipc.h>
19 #include <linux/genetlink.h>
20 #include <libmnl/libmnl.h>
21 
22 #include "cmdl.h"
23 #include "msg.h"
24 #include "link.h"
25 
link_list_cb(const struct nlmsghdr * nlh,void * data)26 static int link_list_cb(const struct nlmsghdr *nlh, void *data)
27 {
28 	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
29 	struct nlattr *info[TIPC_NLA_MAX + 1] = {};
30 	struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1] = {};
31 
32 	mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
33 	if (!info[TIPC_NLA_LINK])
34 		return MNL_CB_ERROR;
35 
36 	mnl_attr_parse_nested(info[TIPC_NLA_LINK], parse_attrs, attrs);
37 	if (!attrs[TIPC_NLA_LINK_NAME])
38 		return MNL_CB_ERROR;
39 
40 	printf("%s: ", mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME]));
41 
42 	if (attrs[TIPC_NLA_LINK_UP])
43 		printf("up\n");
44 	else
45 		printf("down\n");
46 
47 	return MNL_CB_OK;
48 }
49 
cmd_link_list(struct nlmsghdr * nlh,const struct cmd * cmd,struct cmdl * cmdl,void * data)50 static int cmd_link_list(struct nlmsghdr *nlh, const struct cmd *cmd,
51 			 struct cmdl *cmdl, void *data)
52 {
53 	char buf[MNL_SOCKET_BUFFER_SIZE];
54 
55 	if (help_flag) {
56 		fprintf(stderr, "Usage: %s link list\n", cmdl->argv[0]);
57 		return -EINVAL;
58 	}
59 
60 	if (!(nlh = msg_init(buf, TIPC_NL_LINK_GET))) {
61 		fprintf(stderr, "error, message initialisation failed\n");
62 		return -1;
63 	}
64 
65 	return msg_dumpit(nlh, link_list_cb, NULL);
66 }
67 
link_get_cb(const struct nlmsghdr * nlh,void * data)68 static int link_get_cb(const struct nlmsghdr *nlh, void *data)
69 {
70 	int *prop = data;
71 	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
72 	struct nlattr *info[TIPC_NLA_MAX + 1] = {};
73 	struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1] = {};
74 	struct nlattr *props[TIPC_NLA_PROP_MAX + 1] = {};
75 
76 	mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
77 	if (!info[TIPC_NLA_LINK])
78 		return MNL_CB_ERROR;
79 
80 	mnl_attr_parse_nested(info[TIPC_NLA_LINK], parse_attrs, attrs);
81 	if (!attrs[TIPC_NLA_LINK_PROP])
82 		return MNL_CB_ERROR;
83 
84 	mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_PROP], parse_attrs, props);
85 	if (!props[*prop])
86 		return MNL_CB_ERROR;
87 
88 	printf("%u\n", mnl_attr_get_u32(props[*prop]));
89 
90 	return MNL_CB_OK;
91 }
92 
93 
cmd_link_get_prop(struct nlmsghdr * nlh,const struct cmd * cmd,struct cmdl * cmdl,void * data)94 static int cmd_link_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
95 			     struct cmdl *cmdl, void *data)
96 {
97 	int prop;
98 	char buf[MNL_SOCKET_BUFFER_SIZE];
99 	struct opt *opt;
100 	struct opt opts[] = {
101 		{ "link",		NULL },
102 		{ NULL }
103 	};
104 
105 	if (strcmp(cmd->cmd, "priority") == 0)
106 		prop = TIPC_NLA_PROP_PRIO;
107 	else if ((strcmp(cmd->cmd, "tolerance") == 0))
108 		prop = TIPC_NLA_PROP_TOL;
109 	else if ((strcmp(cmd->cmd, "window") == 0))
110 		prop = TIPC_NLA_PROP_WIN;
111 	else
112 		return -EINVAL;
113 
114 	if (help_flag) {
115 		(cmd->help)(cmdl);
116 		return -EINVAL;
117 	}
118 
119 	if (parse_opts(opts, cmdl) < 0)
120 		return -EINVAL;
121 
122 	if (!(nlh = msg_init(buf, TIPC_NL_LINK_GET))) {
123 		fprintf(stderr, "error, message initialisation failed\n");
124 		return -1;
125 	}
126 
127 	if (!(opt = get_opt(opts, "link"))) {
128 		fprintf(stderr, "error, missing link\n");
129 		return -EINVAL;
130 	}
131 	mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, opt->val);
132 
133 	return msg_doit(nlh, link_get_cb, &prop);
134 }
135 
cmd_link_get_help(struct cmdl * cmdl)136 static void cmd_link_get_help(struct cmdl *cmdl)
137 {
138 	fprintf(stderr, "Usage: %s link get PPROPERTY link LINK\n\n"
139 		"PROPERTIES\n"
140 		" tolerance             - Get link tolerance\n"
141 		" priority              - Get link priority\n"
142 		" window                - Get link window\n",
143 		cmdl->argv[0]);
144 }
145 
cmd_link_get(struct nlmsghdr * nlh,const struct cmd * cmd,struct cmdl * cmdl,void * data)146 static int cmd_link_get(struct nlmsghdr *nlh, const struct cmd *cmd,
147 			struct cmdl *cmdl, void *data)
148 {
149 	const struct cmd cmds[] = {
150 		{ "priority",	cmd_link_get_prop,	cmd_link_get_help },
151 		{ "tolerance",	cmd_link_get_prop,	cmd_link_get_help },
152 		{ "window",	cmd_link_get_prop,	cmd_link_get_help },
153 		{ NULL }
154 	};
155 
156 	return run_cmd(nlh, cmd, cmds, cmdl, NULL);
157 }
158 
cmd_link_stat_reset_help(struct cmdl * cmdl)159 static void cmd_link_stat_reset_help(struct cmdl *cmdl)
160 {
161 	fprintf(stderr, "Usage: %s link stat reset link LINK\n\n", cmdl->argv[0]);
162 }
163 
cmd_link_stat_reset(struct nlmsghdr * nlh,const struct cmd * cmd,struct cmdl * cmdl,void * data)164 static int cmd_link_stat_reset(struct nlmsghdr *nlh, const struct cmd *cmd,
165 			       struct cmdl *cmdl, void *data)
166 {
167 	char *link;
168 	char buf[MNL_SOCKET_BUFFER_SIZE];
169 	struct opt *opt;
170 	struct nlattr *nest;
171 	struct opt opts[] = {
172 		{ "link",		NULL },
173 		{ NULL }
174 	};
175 
176 	if (help_flag) {
177 		(cmd->help)(cmdl);
178 		return -EINVAL;
179 	}
180 
181 	if (parse_opts(opts, cmdl) != 1) {
182 		(cmd->help)(cmdl);
183 		return -EINVAL;
184 	}
185 
186 	if (!(nlh = msg_init(buf, TIPC_NL_LINK_RESET_STATS))) {
187 		fprintf(stderr, "error, message initialisation failed\n");
188 		return -1;
189 	}
190 
191 	if (!(opt = get_opt(opts, "link"))) {
192 		fprintf(stderr, "error, missing link\n");
193 		return -EINVAL;
194 	}
195 	link = opt->val;
196 
197 	nest = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);
198 	mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, link);
199 	mnl_attr_nest_end(nlh, nest);
200 
201 	return msg_doit(nlh, NULL, NULL);
202 }
203 
perc(uint32_t count,uint32_t total)204 static uint32_t perc(uint32_t count, uint32_t total)
205 {
206 	return (count * 100 + (total / 2)) / total;
207 }
208 
_show_link_stat(struct nlattr * attrs[],struct nlattr * prop[],struct nlattr * stats[])209 static int _show_link_stat(struct nlattr *attrs[], struct nlattr *prop[],
210 			   struct nlattr *stats[])
211 {
212 	uint32_t proft;
213 
214 	if (attrs[TIPC_NLA_LINK_ACTIVE])
215 		printf("  ACTIVE");
216 	else if (attrs[TIPC_NLA_LINK_UP])
217 		printf("  STANDBY");
218 	else
219 		printf("  DEFUNCT");
220 
221 	printf("  MTU:%u  Priority:%u  Tolerance:%u ms  Window:%u packets\n",
222 	       mnl_attr_get_u32(attrs[TIPC_NLA_LINK_MTU]),
223 	       mnl_attr_get_u32(prop[TIPC_NLA_PROP_PRIO]),
224 	       mnl_attr_get_u32(prop[TIPC_NLA_PROP_TOL]),
225 	       mnl_attr_get_u32(prop[TIPC_NLA_PROP_WIN]));
226 
227 	printf("  RX packets:%u fragments:%u/%u bundles:%u/%u\n",
228 	       mnl_attr_get_u32(attrs[TIPC_NLA_LINK_RX]) -
229 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_INFO]),
230 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]),
231 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]),
232 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]),
233 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED]));
234 
235 	printf("  TX packets:%u fragments:%u/%u bundles:%u/%u\n",
236 	       mnl_attr_get_u32(attrs[TIPC_NLA_LINK_TX]) -
237 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_INFO]),
238 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]),
239 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]),
240 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]),
241 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED]));
242 
243 	proft = mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT]);
244 	printf("  TX profile sample:%u packets  average:%u octets\n",
245 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_CNT]),
246 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_TOT]) / proft);
247 
248 	printf("  0-64:%u%% -256:%u%% -1024:%u%% -4096:%u%% "
249 	       "-16384:%u%% -32768:%u%% -66000:%u%%\n",
250 	       perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P0]), proft),
251 	       perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P1]), proft),
252 	       perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P2]), proft),
253 	       perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P3]), proft),
254 	       perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P4]), proft),
255 	       perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P5]), proft),
256 	       perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P6]), proft));
257 
258 	printf("  RX states:%u probes:%u naks:%u defs:%u dups:%u\n",
259 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_STATES]),
260 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_PROBES]),
261 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]),
262 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]),
263 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_DUPLICATES]));
264 
265 	printf("  TX states:%u probes:%u naks:%u acks:%u dups:%u\n",
266 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_STATES]),
267 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_PROBES]),
268 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]),
269 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]),
270 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED]));
271 
272 	printf("  Congestion link:%u  Send queue max:%u avg:%u\n",
273 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]),
274 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]),
275 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
276 
277 	return MNL_CB_OK;
278 }
279 
_show_bc_link_stat(struct nlattr * prop[],struct nlattr * stats[])280 static int _show_bc_link_stat(struct nlattr *prop[], struct nlattr *stats[])
281 {
282 	printf("  Window:%u packets\n",
283 	       mnl_attr_get_u32(prop[TIPC_NLA_PROP_WIN]));
284 
285 	printf("  RX packets:%u fragments:%u/%u bundles:%u/%u\n",
286 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_INFO]),
287 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]),
288 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]),
289 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]),
290 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED]));
291 
292 	printf("  TX packets:%u fragments:%u/%u bundles:%u/%u\n",
293 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_INFO]),
294 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]),
295 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]),
296 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]),
297 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED]));
298 
299 	printf("  RX naks:%u defs:%u dups:%u\n",
300 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]),
301 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]),
302 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_DUPLICATES]));
303 
304 	printf("  TX naks:%u acks:%u dups:%u\n",
305 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]),
306 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]),
307 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED]));
308 
309 	printf("  Congestion link:%u  Send queue max:%u avg:%u\n",
310 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]),
311 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]),
312 	       mnl_attr_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
313 
314 	return MNL_CB_OK;
315 }
316 
link_stat_show_cb(const struct nlmsghdr * nlh,void * data)317 static int link_stat_show_cb(const struct nlmsghdr *nlh, void *data)
318 {
319 	const char *name;
320 	const char *link = data;
321 	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
322 	struct nlattr *info[TIPC_NLA_MAX + 1] = {};
323 	struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1] = {};
324 	struct nlattr *prop[TIPC_NLA_PROP_MAX + 1] = {};
325 	struct nlattr *stats[TIPC_NLA_STATS_MAX + 1] = {};
326 
327 	mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
328 	if (!info[TIPC_NLA_LINK])
329 		return MNL_CB_ERROR;
330 
331 	mnl_attr_parse_nested(info[TIPC_NLA_LINK], parse_attrs, attrs);
332 	if (!attrs[TIPC_NLA_LINK_NAME] || !attrs[TIPC_NLA_LINK_PROP] ||
333 	    !attrs[TIPC_NLA_LINK_STATS])
334 		return MNL_CB_ERROR;
335 
336 	mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_PROP], parse_attrs, prop);
337 	mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_STATS], parse_attrs, stats);
338 
339 	name = mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME]);
340 
341 	/* If a link is passed, skip all but that link */
342 	if (link && (strcmp(name, link) != 0))
343 		return MNL_CB_OK;
344 
345 	if (attrs[TIPC_NLA_LINK_BROADCAST]) {
346 		printf("Link <%s>\n", name);
347 		return _show_bc_link_stat(prop, stats);
348 	}
349 
350 	printf("\nLink <%s>\n", name);
351 
352 	return _show_link_stat(attrs, prop, stats);
353 }
354 
cmd_link_stat_show_help(struct cmdl * cmdl)355 static void cmd_link_stat_show_help(struct cmdl *cmdl)
356 {
357 	fprintf(stderr, "Usage: %s link stat show [ link LINK ]\n",
358 		cmdl->argv[0]);
359 }
360 
cmd_link_stat_show(struct nlmsghdr * nlh,const struct cmd * cmd,struct cmdl * cmdl,void * data)361 static int cmd_link_stat_show(struct nlmsghdr *nlh, const struct cmd *cmd,
362 			      struct cmdl *cmdl, void *data)
363 {
364 	char *link = NULL;
365 	char buf[MNL_SOCKET_BUFFER_SIZE];
366 	struct opt *opt;
367 	struct opt opts[] = {
368 		{ "link",		NULL },
369 		{ NULL }
370 	};
371 
372 	if (help_flag) {
373 		(cmd->help)(cmdl);
374 		return -EINVAL;
375 	}
376 
377 	if (!(nlh = msg_init(buf, TIPC_NL_LINK_GET))) {
378 		fprintf(stderr, "error, message initialisation failed\n");
379 		return -1;
380 	}
381 
382 	if (parse_opts(opts, cmdl) < 0)
383 		return -EINVAL;
384 
385 	if ((opt = get_opt(opts, "link")))
386 		link = opt->val;
387 
388 	return msg_dumpit(nlh, link_stat_show_cb, link);
389 }
390 
cmd_link_stat_help(struct cmdl * cmdl)391 static void cmd_link_stat_help(struct cmdl *cmdl)
392 {
393 	fprintf(stderr, "Usage: %s link stat COMMAND [ARGS]\n\n"
394 		"COMMANDS:\n"
395 		" reset                 - Reset link statistics for link\n"
396 		" show                  - Get link priority\n",
397 		cmdl->argv[0]);
398 }
399 
cmd_link_stat(struct nlmsghdr * nlh,const struct cmd * cmd,struct cmdl * cmdl,void * data)400 static int cmd_link_stat(struct nlmsghdr *nlh, const struct cmd *cmd,
401 			 struct cmdl *cmdl, void *data)
402 {
403 	const struct cmd cmds[] = {
404 		{ "reset",	cmd_link_stat_reset,	cmd_link_stat_reset_help },
405 		{ "show",	cmd_link_stat_show,	cmd_link_stat_show_help },
406 		{ NULL }
407 	};
408 
409 	return run_cmd(nlh, cmd, cmds, cmdl, NULL);
410 }
411 
cmd_link_set_help(struct cmdl * cmdl)412 static void cmd_link_set_help(struct cmdl *cmdl)
413 {
414 	fprintf(stderr, "Usage: %s link set PPROPERTY link LINK\n\n"
415 		"PROPERTIES\n"
416 		" tolerance TOLERANCE   - Set link tolerance\n"
417 		" priority PRIORITY     - Set link priority\n"
418 		" window WINDOW         - Set link window\n",
419 		cmdl->argv[0]);
420 }
421 
cmd_link_set_prop(struct nlmsghdr * nlh,const struct cmd * cmd,struct cmdl * cmdl,void * data)422 static int cmd_link_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
423 			     struct cmdl *cmdl, void *data)
424 {
425 	int val;
426 	int prop;
427 	char buf[MNL_SOCKET_BUFFER_SIZE];
428 	struct nlattr *props;
429 	struct nlattr *attrs;
430 	struct opt *opt;
431 	struct opt opts[] = {
432 		{ "link",	NULL },
433 		{ NULL }
434 	};
435 
436 	if (strcmp(cmd->cmd, "priority") == 0)
437 		prop = TIPC_NLA_PROP_PRIO;
438 	else if ((strcmp(cmd->cmd, "tolerance") == 0))
439 		prop = TIPC_NLA_PROP_TOL;
440 	else if ((strcmp(cmd->cmd, "window") == 0))
441 		prop = TIPC_NLA_PROP_WIN;
442 	else
443 		return -EINVAL;
444 
445 	if (help_flag) {
446 		(cmd->help)(cmdl);
447 		return -EINVAL;
448 	}
449 
450 	if (cmdl->optind >= cmdl->argc) {
451 		fprintf(stderr, "error, missing value\n");
452 		return -EINVAL;
453 	}
454 	val = atoi(shift_cmdl(cmdl));
455 
456 	if (parse_opts(opts, cmdl) < 0)
457 		return -EINVAL;
458 
459 	if (!(nlh = msg_init(buf, TIPC_NL_LINK_SET))) {
460 		fprintf(stderr, "error, message initialisation failed\n");
461 		return -1;
462 	}
463 	attrs = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);
464 
465 	if (!(opt = get_opt(opts, "link"))) {
466 		fprintf(stderr, "error, missing link\n");
467 		return -EINVAL;
468 	}
469 	mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, opt->val);
470 
471 	props = mnl_attr_nest_start(nlh, TIPC_NLA_LINK_PROP);
472 	mnl_attr_put_u32(nlh, prop, val);
473 	mnl_attr_nest_end(nlh, props);
474 
475 	mnl_attr_nest_end(nlh, attrs);
476 
477 	return msg_doit(nlh, link_get_cb, &prop);
478 
479 	return 0;
480 }
481 
cmd_link_set(struct nlmsghdr * nlh,const struct cmd * cmd,struct cmdl * cmdl,void * data)482 static int cmd_link_set(struct nlmsghdr *nlh, const struct cmd *cmd,
483 			struct cmdl *cmdl, void *data)
484 {
485 	const struct cmd cmds[] = {
486 		{ "priority",	cmd_link_set_prop,	cmd_link_set_help },
487 		{ "tolerance",	cmd_link_set_prop,	cmd_link_set_help },
488 		{ "window",	cmd_link_set_prop,	cmd_link_set_help },
489 		{ NULL }
490 	};
491 
492 	return run_cmd(nlh, cmd, cmds, cmdl, NULL);
493 }
494 
cmd_link_help(struct cmdl * cmdl)495 void cmd_link_help(struct cmdl *cmdl)
496 {
497 	fprintf(stderr,
498 		"Usage: %s link COMMAND [ARGS] ...\n"
499 		"\n"
500 		"COMMANDS\n"
501 		" list                  - List links\n"
502 		" get                   - Get various link properties\n"
503 		" set                   - Set various link properties\n"
504 		" statistics            - Show or reset statistics\n",
505 		cmdl->argv[0]);
506 }
507 
cmd_link(struct nlmsghdr * nlh,const struct cmd * cmd,struct cmdl * cmdl,void * data)508 int cmd_link(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl,
509 	     void *data)
510 {
511 	const struct cmd cmds[] = {
512 		{ "get",	cmd_link_get,	cmd_link_get_help },
513 		{ "list",	cmd_link_list,	NULL },
514 		{ "set",	cmd_link_set,	cmd_link_set_help },
515 		{ "statistics", cmd_link_stat,	cmd_link_stat_help },
516 		{ NULL }
517 	};
518 
519 	return run_cmd(nlh, cmd, cmds, cmdl, NULL);
520 }
521