#include "utils.h"
#include "tc_util.h"
#include "tc_common.h"
+#include "hlist.h"
-static void usage(void) __attribute__((noreturn));
+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 | get ] dev STRING\n");
+ 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, "Where:\n");
fprintf(stderr, "QDISC_KIND := { prio | cbq | etc. }\n");
fprintf(stderr, "OPTIONS := ... try tc class add <desired QDISC_KIND> help\n");
- exit(-1);
+ return;
}
-int tc_class_modify(int cmd, unsigned flags, int argc, char **argv)
+static int tc_class_modify(int cmd, unsigned flags, int argc, char **argv)
{
- struct rtnl_handle rth;
struct {
struct nlmsghdr n;
struct tcmsg t;
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) {
if (req.t.tcm_parent) {
fprintf(stderr, "Error: \"root\" is duplicate parent ID.\n");
- exit(-1);
+ return -1;
}
req.t.tcm_parent = TC_H_ROOT;
} else if (strcmp(*argv, "parent") == 0) {
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))
if (q) {
if (q->parse_copt == NULL) {
fprintf(stderr, "Error: Qdisc \"%s\" is classless.\n", k);
- exit(1);
+ return 1;
}
if (q->parse_copt(q, argc, argv, &req.n))
- exit(1);
+ return 1;
} else {
if (argc) {
if (matches(*argv, "help") == 0)
usage();
fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc class help\".", *argv);
- exit(-1);
+ return -1;
}
}
- if (rtnl_open(&rth, 0) < 0) {
- fprintf(stderr, "Cannot open rtnetlink\n");
- exit(1);
- }
-
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);
- exit(1);
+ return 1;
}
}
- if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
- exit(2);
+ if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
+ return 2;
- rtnl_close(&rth);
return 0;
}
-void print_class_tcstats(FILE *fp, struct tc_stats *st)
+int filter_ifindex;
+__u32 filter_qdisc;
+__u32 filter_classid;
+
+static void graph_node_add(__u32 parent_id, __u32 id, void *data,
+ int len)
+{
+ struct graph_node *node = malloc(sizeof(struct graph_node));
+
+ memset(node, 0, sizeof(*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)
{
- SPRINT_BUF(b1);
-
- fprintf(fp, " Sent %llu bytes %u pkts (dropped %u, overlimits %u) ",
- (unsigned long long)st->bytes, st->packets, st->drops, st->overlimits);
- if (st->bps || st->pps || st->qlen || st->backlog) {
- fprintf(fp, "\n ");
- if (st->bps || st->pps) {
- fprintf(fp, "rate ");
- if (st->bps)
- fprintf(fp, "%s ", sprint_rate(st->bps, b1));
- if (st->pps)
- fprintf(fp, "%upps ", st->pps);
+ struct hlist_node *n, *tmp_cls;
+ char cls_id_str[256] = {};
+ struct rtattr *tb[TCA_MAX + 1] = {};
+ struct qdisc_util *q;
+ char str[100] = {};
+
+ 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;
+ }
}
- if (st->qlen || st->backlog) {
- fprintf(fp, "backlog ");
- if (st->backlog)
- fprintf(fp, "%s ", sprint_size(st->backlog, b1));
- if (st->qlen)
- fprintf(fp, "%up ", st->qlen);
+
+ graph_indent(buf, cls, 0, 0);
+
+ print_tc_classid(cls_id_str, sizeof(cls_id_str), cls->id);
+ sprintf(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");
}
+
+ fprintf(fp, "%s", buf);
+ buf[0] = '\0';
+ free(cls);
}
}
-int filter_ifindex;
-__u32 filter_qdisc;
-
-int print_class(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int print_class(const struct sockaddr_nl *who,
+ struct nlmsghdr *n, void *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;
- memset(tb, 0, sizeof(tb));
+ if (filter_classid && t->tcm_handle != filter_classid)
+ return 0;
+
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));
}
fprintf(fp, "\n");
if (show_stats) {
- if (tb[TCA_STATS]) {
-#ifndef STOOPID_8BYTE
- if (RTA_PAYLOAD(tb[TCA_STATS]) < sizeof(struct tc_stats))
- fprintf(fp, "statistics truncated");
- else {
-#endif
- struct tc_stats st;
- memcpy(&st, RTA_DATA(tb[TCA_STATS]), sizeof(st));
- print_class_tcstats(fp, &st);
- fprintf(fp, "\n");
-#ifndef STOOPID_8BYTE
- }
-#endif
+ struct rtattr *xstats = NULL;
+
+ if (tb[TCA_STATS] || tb[TCA_STATS2]) {
+ print_tcstats_attr(fp, tb, " ", &xstats);
+ fprintf(fp, "\n");
}
- if (q && tb[TCA_XSTATS]) {
- q->print_xstats(q, fp, tb[TCA_XSTATS]);
+ if (q && (xstats || tb[TCA_XSTATS]) && q->print_xstats) {
+ q->print_xstats(q, fp, xstats ? : tb[TCA_XSTATS]);
fprintf(fp, "\n");
}
}
}
-int tc_class_list(int argc, char **argv)
+static int tc_class_list(int argc, char **argv)
{
struct tcmsg t;
- struct rtnl_handle rth;
char d[16];
+ 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) {
NEXT_ARG();
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("invalid class ID", *argv);
} else if (strcmp(*argv, "root") == 0) {
if (t.tcm_parent) {
fprintf(stderr, "Error: \"root\" is duplicate parent ID\n");
- exit(-1);
+ return -1;
}
t.tcm_parent = TC_H_ROOT;
} else if (strcmp(*argv, "parent") == 0) {
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();
} else {
fprintf(stderr, "What is \"%s\"? Try \"tc class help\".\n", *argv);
- exit(-1);
+ return -1;
}
argc--; argv++;
}
- if (rtnl_open(&rth, 0) < 0) {
- fprintf(stderr, "Cannot open rtnetlink\n");
- exit(1);
- }
-
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);
- exit(1);
+ return 1;
}
filter_ifindex = t.tcm_ifindex;
}
if (rtnl_dump_request(&rth, RTM_GETTCLASS, &t, sizeof(t)) < 0) {
perror("Cannot send dump request");
- rtnl_close(&rth);
- exit(1);
+ return 1;
}
- if (rtnl_dump_filter(&rth, print_class, stdout, NULL, NULL) < 0) {
+ if (rtnl_dump_filter(&rth, print_class, stdout) < 0) {
fprintf(stderr, "Dump terminated\n");
- rtnl_close(&rth);
- exit(1);
+ return 1;
}
- rtnl_close(&rth);
+ if (show_graph)
+ graph_cls_show(stdout, &buf[0], &root_cls_list, 0);
+
return 0;
}
if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
|| matches(*argv, "lst") == 0)
return tc_class_list(argc-1, argv+1);
- if (matches(*argv, "help") == 0)
+ if (matches(*argv, "help") == 0) {
usage();
+ return 0;
+ }
fprintf(stderr, "Command \"%s\" is unknown, try \"tc class help\".\n", *argv);
return -1;
}