#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
-#include <syslog.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "utils.h"
#include "tc_util.h"
#include "tc_common.h"
+#include "list.h"
+
+struct graph_node {
+ struct hlist_node hlist;
+ __u32 id;
+ __u32 parent_id;
+ struct graph_node *parent_node;
+ struct graph_node *right_node;
+ void *data;
+ int data_len;
+ int nodes_count;
+};
+
+static struct hlist_head cls_list = {};
+static struct hlist_head root_cls_list = {};
static void usage(void);
static void usage(void)
{
- fprintf(stderr, "Usage: tc class [ add | del | change | replace | show ] dev STRING\n");
- fprintf(stderr, " [ classid CLASSID ] [ root | parent CLASSID ]\n");
- fprintf(stderr, " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n");
- fprintf(stderr, "\n");
- fprintf(stderr, " tc class show [ dev STRING ] [ root | parent CLASSID ]\n");
- fprintf(stderr, "Where:\n");
- fprintf(stderr, "QDISC_KIND := { prio | cbq | etc. }\n");
- fprintf(stderr, "OPTIONS := ... try tc class add <desired QDISC_KIND> help\n");
- return;
+ fprintf(stderr,
+ "Usage: tc class [ add | del | change | replace | show ] dev STRING\n"
+ " [ classid CLASSID ] [ root | parent CLASSID ]\n"
+ " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"
+ "\n"
+ " tc class show [ dev STRING ] [ root | parent CLASSID ]\n"
+ "Where:\n"
+ "QDISC_KIND := { prio | cbq | etc. }\n"
+ "OPTIONS := ... try tc class add <desired QDISC_KIND> help\n");
}
-int tc_class_modify(int cmd, unsigned flags, int argc, char **argv)
+static int tc_class_modify(int cmd, unsigned int flags, int argc, char **argv)
{
struct {
- struct nlmsghdr n;
- struct tcmsg t;
- char buf[4096];
- } req;
+ struct nlmsghdr n;
+ struct tcmsg t;
+ char buf[4096];
+ } req = {
+ .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
+ .n.nlmsg_flags = NLM_F_REQUEST | flags,
+ .n.nlmsg_type = cmd,
+ .t.tcm_family = AF_UNSPEC,
+ };
struct qdisc_util *q = NULL;
- struct tc_estimator est;
- char d[16];
- char k[16];
-
- memset(&req, 0, sizeof(req));
- memset(&est, 0, sizeof(est));
- memset(d, 0, sizeof(d));
- memset(k, 0, sizeof(k));
-
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
- req.n.nlmsg_flags = NLM_F_REQUEST|flags;
- req.n.nlmsg_type = cmd;
- req.t.tcm_family = AF_UNSPEC;
+ struct tc_estimator est = {};
+ char d[IFNAMSIZ] = {};
+ char k[FILTER_NAMESZ] = {};
while (argc > 0) {
if (strcmp(*argv, "dev") == 0) {
strncpy(d, *argv, sizeof(d)-1);
} else if (strcmp(*argv, "classid") == 0) {
__u32 handle;
+
NEXT_ARG();
if (req.t.tcm_handle)
duparg("classid", *argv);
if (get_tc_classid(&handle, *argv))
- invarg(*argv, "invalid class ID");
+ invarg("invalid class ID", *argv);
req.t.tcm_handle = handle;
} else if (strcmp(*argv, "handle") == 0) {
fprintf(stderr, "Error: try \"classid\" instead of \"handle\"\n");
return -1;
- } else if (strcmp(*argv, "root") == 0) {
+ } else if (strcmp(*argv, "root") == 0) {
if (req.t.tcm_parent) {
fprintf(stderr, "Error: \"root\" is duplicate parent ID.\n");
return -1;
req.t.tcm_parent = TC_H_ROOT;
} else if (strcmp(*argv, "parent") == 0) {
__u32 handle;
+
NEXT_ARG();
if (req.t.tcm_parent)
duparg("parent", *argv);
if (get_tc_classid(&handle, *argv))
- invarg(*argv, "invalid parent ID");
+ invarg("invalid parent ID", *argv);
req.t.tcm_parent = handle;
} else if (matches(*argv, "estimator") == 0) {
if (parse_estimator(&argc, &argv, &est))
fprintf(stderr, "Error: Qdisc \"%s\" is classless.\n", k);
return 1;
}
- if (q->parse_copt(q, argc, argv, &req.n))
+ if (q->parse_copt(q, argc, argv, &req.n, d))
return 1;
} else {
if (argc) {
if (d[0]) {
ll_init_map(&rth);
- if ((req.t.tcm_ifindex = ll_name_to_index(d)) == 0) {
- fprintf(stderr, "Cannot find device \"%s\"\n", d);
- return 1;
- }
+ req.t.tcm_ifindex = ll_name_to_index(d);
+ if (!req.t.tcm_ifindex)
+ return -nodev(d);
}
- if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+ if (rtnl_talk(&rth, &req.n, NULL) < 0)
return 2;
return 0;
}
-int filter_ifindex;
-__u32 filter_qdisc;
-__u32 filter_classid;
+static int filter_ifindex;
+static __u32 filter_qdisc;
+static __u32 filter_classid;
+
+static void graph_node_add(__u32 parent_id, __u32 id, void *data,
+ int len)
+{
+ struct graph_node *node = calloc(1, sizeof(struct graph_node));
+
+ node->id = id;
+ node->parent_id = parent_id;
+
+ if (data && len) {
+ node->data = malloc(len);
+ node->data_len = len;
+ memcpy(node->data, data, len);
+ }
+
+ if (parent_id == TC_H_ROOT)
+ hlist_add_head(&node->hlist, &root_cls_list);
+ else
+ hlist_add_head(&node->hlist, &cls_list);
+}
+
+static void graph_indent(char *buf, struct graph_node *node, int is_newline,
+ int add_spaces)
+{
+ char spaces[100] = {0};
+
+ while (node && node->parent_node) {
+ node->parent_node->right_node = node;
+ node = node->parent_node;
+ }
+ while (node && node->right_node) {
+ if (node->hlist.next)
+ strcat(buf, "| ");
+ else
+ strcat(buf, " ");
+
+ node = node->right_node;
+ }
+
+ if (is_newline) {
+ if (node->hlist.next && node->nodes_count)
+ strcat(buf, "| |");
+ else if (node->hlist.next)
+ strcat(buf, "| ");
+ else if (node->nodes_count)
+ strcat(buf, " |");
+ else if (!node->hlist.next)
+ strcat(buf, " ");
+ }
+ if (add_spaces > 0) {
+ sprintf(spaces, "%-*s", add_spaces, "");
+ strcat(buf, spaces);
+ }
+}
+
+static void graph_cls_show(FILE *fp, char *buf, struct hlist_head *root_list,
+ int level)
+{
+ struct hlist_node *n, *tmp_cls;
+ char cls_id_str[256] = {};
+ struct rtattr *tb[TCA_MAX + 1];
+ struct qdisc_util *q;
+ char str[300] = {};
+
+ hlist_for_each_safe(n, tmp_cls, root_list) {
+ struct hlist_node *c, *tmp_chld;
+ struct hlist_head children = {};
+ struct graph_node *cls = container_of(n, struct graph_node,
+ hlist);
+
+ hlist_for_each_safe(c, tmp_chld, &cls_list) {
+ struct graph_node *child = container_of(c,
+ struct graph_node, hlist);
+
+ if (cls->id == child->parent_id) {
+ hlist_del(c);
+ hlist_add_head(c, &children);
+ cls->nodes_count++;
+ child->parent_node = cls;
+ }
+ }
+
+ graph_indent(buf, cls, 0, 0);
+
+ print_tc_classid(cls_id_str, sizeof(cls_id_str), cls->id);
+ snprintf(str, sizeof(str),
+ "+---(%s)", cls_id_str);
+ strcat(buf, str);
+
+ parse_rtattr(tb, TCA_MAX, (struct rtattr *)cls->data,
+ cls->data_len);
+
+ if (tb[TCA_KIND] == NULL) {
+ strcat(buf, " [unknown qdisc kind] ");
+ } else {
+ const char *kind = rta_getattr_str(tb[TCA_KIND]);
+
+ sprintf(str, " %s ", kind);
+ strcat(buf, str);
+ fprintf(fp, "%s", buf);
+ buf[0] = '\0';
+
+ q = get_qdisc_kind(kind);
+ if (q && q->print_copt) {
+ q->print_copt(q, fp, tb[TCA_OPTIONS]);
+ }
+ if (q && show_stats) {
+ int cls_indent = strlen(q->id) - 2 +
+ strlen(cls_id_str);
+ struct rtattr *stats = NULL;
+
+ graph_indent(buf, cls, 1, cls_indent);
+
+ if (tb[TCA_STATS] || tb[TCA_STATS2]) {
+ fprintf(fp, "\n");
+ print_tcstats_attr(fp, tb, buf, &stats);
+ buf[0] = '\0';
+ }
+ if (cls->hlist.next || cls->nodes_count) {
+ strcat(buf, "\n");
+ graph_indent(buf, cls, 1, 0);
+ }
+ }
+ }
+ free(cls->data);
+ fprintf(fp, "%s\n", buf);
+ buf[0] = '\0';
+
+ graph_cls_show(fp, buf, &children, level + 1);
+ if (!cls->hlist.next) {
+ graph_indent(buf, cls, 0, 0);
+ strcat(buf, "\n");
+ }
-int print_class(const struct sockaddr_nl *who,
- struct nlmsghdr *n, void *arg)
+ fprintf(fp, "%s", buf);
+ buf[0] = '\0';
+ free(cls);
+ }
+}
+
+int print_class(struct nlmsghdr *n, void *arg)
{
- FILE *fp = (FILE*)arg;
+ FILE *fp = (FILE *)arg;
struct tcmsg *t = NLMSG_DATA(n);
int len = n->nlmsg_len;
- struct rtattr * tb[TCA_MAX+1];
+ struct rtattr *tb[TCA_MAX + 1];
struct qdisc_util *q;
char abuf[256];
fprintf(stderr, "Wrong len %d\n", len);
return -1;
}
+
+ if (show_graph) {
+ graph_node_add(t->tcm_parent, t->tcm_handle, TCA_RTA(t), len);
+ return 0;
+ }
+
if (filter_qdisc && TC_H_MAJ(t->tcm_handle^filter_qdisc))
return 0;
if (filter_classid && t->tcm_handle != filter_classid)
return 0;
- memset(tb, 0, sizeof(tb));
parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len);
if (tb[TCA_KIND] == NULL) {
else
print_tc_classid(abuf, sizeof(abuf), t->tcm_handle);
}
- fprintf(fp, "class %s %s ", (char*)RTA_DATA(tb[TCA_KIND]), abuf);
+ fprintf(fp, "class %s %s ", rta_getattr_str(tb[TCA_KIND]), abuf);
if (filter_ifindex == 0)
fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex));
}
-int tc_class_list(int argc, char **argv)
+static int tc_class_list(int argc, char **argv)
{
- struct tcmsg t;
- char d[16];
+ struct tcmsg t = { .tcm_family = AF_UNSPEC };
+ char d[IFNAMSIZ] = {};
+ char buf[1024] = {0};
- memset(&t, 0, sizeof(t));
- t.tcm_family = AF_UNSPEC;
- memset(d, 0, sizeof(d));
+ filter_qdisc = 0;
+ filter_classid = 0;
while (argc > 0) {
if (strcmp(*argv, "dev") == 0) {
if (filter_qdisc)
duparg("qdisc", *argv);
if (get_qdisc_handle(&filter_qdisc, *argv))
- invarg(*argv, "invalid qdisc ID");
+ invarg("invalid qdisc ID", *argv);
} else if (strcmp(*argv, "classid") == 0) {
NEXT_ARG();
if (filter_classid)
duparg("classid", *argv);
if (get_tc_classid(&filter_classid, *argv))
- invarg(*argv, "invalid class ID");
+ invarg("invalid class ID", *argv);
} else if (strcmp(*argv, "root") == 0) {
if (t.tcm_parent) {
fprintf(stderr, "Error: \"root\" is duplicate parent ID\n");
t.tcm_parent = TC_H_ROOT;
} else if (strcmp(*argv, "parent") == 0) {
__u32 handle;
+
if (t.tcm_parent)
duparg("parent", *argv);
NEXT_ARG();
if (get_tc_classid(&handle, *argv))
- invarg(*argv, "invalid parent ID");
+ invarg("invalid parent ID", *argv);
t.tcm_parent = handle;
} else if (matches(*argv, "help") == 0) {
usage();
argc--; argv++;
}
- ll_init_map(&rth);
+ ll_init_map(&rth);
if (d[0]) {
- if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) {
- fprintf(stderr, "Cannot find device \"%s\"\n", d);
- return 1;
- }
+ t.tcm_ifindex = ll_name_to_index(d);
+ if (!t.tcm_ifindex)
+ return -nodev(d);
filter_ifindex = t.tcm_ifindex;
}
- if (rtnl_dump_request(&rth, RTM_GETTCLASS, &t, sizeof(t)) < 0) {
+ if (rtnl_dump_request(&rth, RTM_GETTCLASS, &t, sizeof(t)) < 0) {
perror("Cannot send dump request");
return 1;
}
- if (rtnl_dump_filter(&rth, print_class, stdout) < 0) {
+ if (rtnl_dump_filter(&rth, print_class, stdout) < 0) {
fprintf(stderr, "Dump terminated\n");
return 1;
}
+ if (show_graph)
+ graph_cls_show(stdout, &buf[0], &root_cls_list, 0);
+
return 0;
}