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
26static 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
50static 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
68static 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
94static 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
136static 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
146static 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
159static 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
164static 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
204static uint32_t perc(uint32_t count, uint32_t total)
205{
206	return (count * 100 + (total / 2)) / total;
207}
208
209static 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
280static 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
317static 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
355static 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
361static 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
391static 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
400static 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
412static 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
422static 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
482static 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
495void 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
508int 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