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

Reply via email to