mirror_iproute2/tc/tc_class.c
Phil Sutter d17b136f7d Use C99 style initializers everywhere
This big patch was compiled by vimgrepping for memset calls and changing
to C99 initializer if applicable. One notable exception is the
initialization of union bpf_attr in tc/tc_bpf.c: changing it would break
for older gcc versions (at least <=3.4.6).

Calls to memset for struct rtattr pointer fields for parse_rtattr*()
were just dropped since they are not needed.

The changes here allowed the compiler to discover some unused variables,
so get rid of them, too.

Signed-off-by: Phil Sutter <phil@nwl.cc>
Acked-by: David Ahern <dsa@cumulusnetworks.com>
2016-07-20 12:05:24 -07:00

495 lines
12 KiB
C

/*
* tc_class.c "tc class".
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <math.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;
}
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 = {
.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] = {};
while (argc > 0) {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
if (d[0])
duparg("dev", *argv);
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("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");
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("invalid parent ID", *argv);
req.t.tcm_parent = handle;
} else if (matches(*argv, "estimator") == 0) {
if (parse_estimator(&argc, &argv, &est))
return -1;
} else if (matches(*argv, "help") == 0) {
usage();
} else {
strncpy(k, *argv, sizeof(k)-1);
q = get_qdisc_kind(k);
argc--; argv++;
break;
}
argc--; argv++;
}
if (k[0])
addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
if (est.ewma_log)
addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
if (q) {
if (q->parse_copt == NULL) {
fprintf(stderr, "Error: Qdisc \"%s\" is classless.\n", k);
return 1;
}
if (q->parse_copt(q, argc, argv, &req.n))
return 1;
} else {
if (argc) {
if (matches(*argv, "help") == 0)
usage();
fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc class help\".", *argv);
return -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);
return 1;
}
}
if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
return 2;
return 0;
}
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)
{
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;
}
}
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 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 qdisc_util *q;
char abuf[256];
if (n->nlmsg_type != RTM_NEWTCLASS && n->nlmsg_type != RTM_DELTCLASS) {
fprintf(stderr, "Not a class\n");
return 0;
}
len -= NLMSG_LENGTH(sizeof(*t));
if (len < 0) {
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;
parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len);
if (tb[TCA_KIND] == NULL) {
fprintf(stderr, "print_class: NULL kind\n");
return -1;
}
if (n->nlmsg_type == RTM_DELTCLASS)
fprintf(fp, "deleted ");
abuf[0] = 0;
if (t->tcm_handle) {
if (filter_qdisc)
print_tc_classid(abuf, sizeof(abuf), TC_H_MIN(t->tcm_handle));
else
print_tc_classid(abuf, sizeof(abuf), t->tcm_handle);
}
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));
if (t->tcm_parent == TC_H_ROOT)
fprintf(fp, "root ");
else {
if (filter_qdisc)
print_tc_classid(abuf, sizeof(abuf), TC_H_MIN(t->tcm_parent));
else
print_tc_classid(abuf, sizeof(abuf), t->tcm_parent);
fprintf(fp, "parent %s ", abuf);
}
if (t->tcm_info)
fprintf(fp, "leaf %x: ", t->tcm_info>>16);
q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND]));
if (tb[TCA_OPTIONS]) {
if (q && q->print_copt)
q->print_copt(q, fp, tb[TCA_OPTIONS]);
else
fprintf(fp, "[cannot parse class parameters]");
}
fprintf(fp, "\n");
if (show_stats) {
struct rtattr *xstats = NULL;
if (tb[TCA_STATS] || tb[TCA_STATS2]) {
print_tcstats_attr(fp, tb, " ", &xstats);
fprintf(fp, "\n");
}
if (q && (xstats || tb[TCA_XSTATS]) && q->print_xstats) {
q->print_xstats(q, fp, xstats ? : tb[TCA_XSTATS]);
fprintf(fp, "\n");
}
}
fflush(fp);
return 0;
}
static int tc_class_list(int argc, char **argv)
{
struct tcmsg t = { .tcm_family = AF_UNSPEC };
char d[16] = {};
char buf[1024] = {0};
filter_qdisc = 0;
filter_classid = 0;
while (argc > 0) {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
if (d[0])
duparg("dev", *argv);
strncpy(d, *argv, sizeof(d)-1);
} else if (strcmp(*argv, "qdisc") == 0) {
NEXT_ARG();
if (filter_qdisc)
duparg("qdisc", *argv);
if (get_qdisc_handle(&filter_qdisc, *argv))
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");
return -1;
}
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("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);
return -1;
}
argc--; argv++;
}
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;
}
filter_ifindex = t.tcm_ifindex;
}
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) {
fprintf(stderr, "Dump terminated\n");
return 1;
}
if (show_graph)
graph_cls_show(stdout, &buf[0], &root_cls_list, 0);
return 0;
}
int do_class(int argc, char **argv)
{
if (argc < 1)
return tc_class_list(0, NULL);
if (matches(*argv, "add") == 0)
return tc_class_modify(RTM_NEWTCLASS, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1);
if (matches(*argv, "change") == 0)
return tc_class_modify(RTM_NEWTCLASS, 0, argc-1, argv+1);
if (matches(*argv, "replace") == 0)
return tc_class_modify(RTM_NEWTCLASS, NLM_F_CREATE, argc-1, argv+1);
if (matches(*argv, "delete") == 0)
return tc_class_modify(RTM_DELTCLASS, 0, argc-1, argv+1);
#if 0
if (matches(*argv, "get") == 0)
return tc_class_get(RTM_GETTCLASS, 0, argc-1, argv+1);
#endif
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) {
usage();
return 0;
}
fprintf(stderr, "Command \"%s\" is unknown, try \"tc class help\".\n", *argv);
return -1;
}