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