Add the argument '-tree' to ip-link to show network devices dependency tree.
Example: $ ip -tree link eth0 bond0 eth1 bond0 eth2 bond1 eth3 bond1 Signed-off-by: Zaboj Campula <zaboj.camp...@post.cz> --- include/utils.h | 1 + ip/ip.c | 5 ++- ip/ipaddress.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/include/utils.h b/include/utils.h index 22369e0..f1acf4d 100644 --- a/include/utils.h +++ b/include/utils.h @@ -20,6 +20,7 @@ extern int show_raw; extern int resolve_hosts; extern int oneline; extern int brief; +extern int tree;; extern int timestamp; extern int timestamp_short; extern const char * _SL_; diff --git a/ip/ip.c b/ip/ip.c index 07050b0..29747a5 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -33,6 +33,7 @@ int show_details; int resolve_hosts; int oneline; int brief; +int tree; int timestamp; const char *_SL_; int force; @@ -57,7 +58,7 @@ static void usage(void) " -h[uman-readable] | -iec |\n" " -f[amily] { inet | inet6 | ipx | dnet | mpls | bridge | link } |\n" " -4 | -6 | -I | -D | -B | -0 |\n" -" -l[oops] { maximum-addr-flush-attempts } | -br[ief] |\n" +" -l[oops] { maximum-addr-flush-attempts } | -br[ief] | -tr[ee] |\n" " -o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] |\n" " -rc[vbuf] [size] | -n[etns] name | -a[ll] | -c[olor]}\n"); exit(-1); @@ -257,6 +258,8 @@ int main(int argc, char **argv) batch_file = argv[1]; } else if (matches(opt, "-brief") == 0) { ++brief; + } else if (matches(opt, "-tree") == 0) { + ++tree; } else if (matches(opt, "-rcvbuf") == 0) { unsigned int size; diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 242c6ea..5ebcb1a 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -1534,6 +1534,69 @@ static int iplink_filter_req(struct nlmsghdr *nlh, int reqlen) return 0; } +static int has_master(struct nlmsg_chain *linfo, int index) +{ + struct nlmsg_list *l; + struct rtattr *tb[IFLA_MAX+1]; + int len; + for (l = linfo->head; l; l = l->next) { + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + len = l->h.nlmsg_len; + len -= NLMSG_LENGTH(sizeof(*ifi)); + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + if (tb[IFLA_MASTER] && *(int *)RTA_DATA(tb[IFLA_MASTER]) == index) + return 1; + } + return 0; +} + +static struct nlmsg_list *get_master(struct nlmsg_chain *linfo, struct rtattr **tb) +{ + struct nlmsg_list *l; + if (tb[IFLA_MASTER]) { + int master = *(int *)RTA_DATA(tb[IFLA_MASTER]); + for (l = linfo->head; l; l = l->next) { + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + if (ifi->ifi_index == master) + return l; + } + } + return NULL; +} + +static void print_dev_tree_item(struct nlmsg_chain *linfo, struct nlmsg_list *l, int indent) { + char *name; + int len; + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + struct rtattr *tb[IFLA_MAX+1]; + len = l->h.nlmsg_len; + len -= NLMSG_LENGTH(sizeof(*ifi)); + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + name = (char *)(tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : "<nil>"); + + printf("%*s%s\n", indent * 4, "", name); + + struct nlmsg_list *master = get_master(linfo, tb); + if (master) { + if (indent > 8) { + printf("%*s...\n", (indent + 1) * 4, ""); + } else { + print_dev_tree_item(linfo, master, indent + 1); + } + } +} + +static void print_devtree(struct nlmsg_chain *linfo) +{ + struct nlmsg_list *l; + for (l = linfo->head; l; l = l->next) { + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + if (!has_master(linfo, ifi->ifi_index)) { + print_dev_tree_item(linfo, l, 0); + } + } +} + static int ipaddr_list_flush_or_save(int argc, char **argv, int action) { struct nlmsg_chain linfo = { NULL, NULL}; @@ -1742,23 +1805,27 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action) ipaddr_filter(&linfo, &ainfo); } - for (l = linfo.head; l; l = l->next) { - int res = 0; - struct ifinfomsg *ifi = NLMSG_DATA(&l->h); - - if (brief) { - if (print_linkinfo_brief(NULL, &l->h, stdout) == 0) + if (tree) { + print_devtree(&linfo); + } else { + for (l = linfo.head; l; l = l->next) { + int res = 0; + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + + if (brief) { + if (print_linkinfo_brief(NULL, &l->h, stdout) == 0) + if (filter.family != AF_PACKET) + print_selected_addrinfo(ifi, + ainfo.head, + stdout); + } else if (no_link || + (res = print_linkinfo(NULL, &l->h, stdout)) >= 0) { if (filter.family != AF_PACKET) print_selected_addrinfo(ifi, - ainfo.head, - stdout); - } else if (no_link || - (res = print_linkinfo(NULL, &l->h, stdout)) >= 0) { - if (filter.family != AF_PACKET) - print_selected_addrinfo(ifi, - ainfo.head, stdout); - if (res > 0 && !do_link && show_stats) - print_link_stats(stdout, &l->h); + ainfo.head, stdout); + if (res > 0 && !do_link && show_stats) + print_link_stats(stdout, &l->h); + } } } fflush(stdout); -- 2.9.3