#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
+#include <linux/if.h>
#include "utils.h"
#include "tc_util.h"
return -1;
sel->keys[i].val |= key;
sel->keys[i].mask |= mask;
+ sel->keys[i].off = 0;
+ sel->keys[i].offmask = 0;
return 0;
}
}
plen = addr.bitlen;
for (i=0; i<plen; i+=32) {
- if (((i+31)&~0x1F)<=plen) {
+// if (((i+31)&~0x1F)<=plen) {
+ if (((i+31))<=plen) {
if ((res = pack_key(sel, addr.data[i/32], 0xFFFFFFFF, off+4*(i/32), offmask)) < 0)
return -1;
} else if (i<plen) {
while (argc > 0) {
if (matches(*argv, "mask") == 0) {
__u32 mask;
+ int i = 0;
NEXT_ARG();
if (get_u32(&mask, *argv, 16))
return -1;
sel->hmask = htonl(mask);
+ mask = sel->hmask;
+ while (!(mask & 1)) {
+ i++;
+ mask>>=1;
+ }
+#ifdef fix_u32_bug
+ sel->fshift = i;
+#endif
} else if (matches(*argv, "at") == 0) {
int num;
NEXT_ARG();
return 0;
tail = (struct rtattr*)(((void*)n)+NLMSG_ALIGN(n->nlmsg_len));
- addattr_l(n, 4096, TCA_OPTIONS, NULL, 0);
+ addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0);
while (argc > 0) {
if (matches(*argv, "match") == 0) {
fprintf(stderr, "Illegal \"classid\"\n");
return -1;
}
- addattr_l(n, 4096, TCA_U32_CLASSID, &handle, 4);
+ addattr_l(n, MAX_MSG, TCA_U32_CLASSID, &handle, 4);
sel.sel.flags |= TC_U32_TERMINAL;
} else if (matches(*argv, "divisor") == 0) {
unsigned divisor;
fprintf(stderr, "Illegal \"divisor\"\n");
return -1;
}
- addattr_l(n, 4096, TCA_U32_DIVISOR, &divisor, 4);
+ addattr_l(n, MAX_MSG, TCA_U32_DIVISOR, &divisor, 4);
} else if (matches(*argv, "order") == 0) {
NEXT_ARG();
if (get_u32(&order, *argv, 0)) {
fprintf(stderr, "\"link\" must be a hash table.\n");
return -1;
}
- addattr_l(n, 4096, TCA_U32_LINK, &handle, 4);
+ addattr_l(n, MAX_MSG, TCA_U32_LINK, &handle, 4);
} else if (strcmp(*argv, "ht") == 0) {
unsigned handle;
NEXT_ARG();
htid = ((hash<<12)&0xFF000)|(htid&0xFFF00000);
sample_ok = 1;
continue;
+ } else if (strcmp(*argv, "indev") == 0) {
+ char ind[IFNAMSIZ + 1];
+ memset(ind, 0, sizeof (ind));
+ argc--;
+ argv++;
+ if (argc < 1) {
+ fprintf(stderr, "Illegal indev\n");
+ return -1;
+ }
+ strncpy(ind, *argv, sizeof (ind) - 1);
+ addattr_l(n, MAX_MSG, TCA_U32_INDEV, ind, strlen(ind) + 1);
+ } else if (matches(*argv, "action") == 0) {
+ NEXT_ARG();
+ if (parse_action(&argc, &argv, TCA_U32_ACT, n)) {
+ fprintf(stderr, "Illegal \"action\"\n");
+ return -1;
+ }
+ continue;
} else if (matches(*argv, "police") == 0) {
NEXT_ARG();
if (parse_police(&argc, &argv, TCA_U32_POLICE, n)) {
}
if (htid)
- addattr_l(n, 4096, TCA_U32_HASH, &htid, 4);
+ addattr_l(n, MAX_MSG, TCA_U32_HASH, &htid, 4);
if (sel_ok)
- addattr_l(n, 4096, TCA_U32_SEL, &sel, sizeof(sel.sel)+sel.sel.nkeys*sizeof(struct tc_u32_key));
+ addattr_l(n, MAX_MSG, TCA_U32_SEL, &sel, sizeof(sel.sel)+sel.sel.nkeys*sizeof(struct tc_u32_key));
tail->rta_len = (((void*)n)+n->nlmsg_len) - (void*)tail;
return 0;
}
SPRINT_BUF(b1);
fprintf(f, "link %s ", sprint_u32_handle(*(__u32*)RTA_DATA(tb[TCA_U32_LINK]), b1));
}
- if (tb[TCA_U32_POLICE]) {
- fprintf(f, "\n");
- tc_print_police(f, tb[TCA_U32_POLICE]);
- }
+
if (sel) {
int i;
struct tc_u32_key *key = sel->keys;
-
if (sel->nkeys) {
- for (i=0; i<sel->nkeys; i++, key++)
+ for (i=0; i<sel->nkeys; i++, key++) {
fprintf(f, "\n match %08x/%08x at %s%d",
(unsigned int)ntohl(key->val),
(unsigned int)ntohl(key->mask),
key->offmask ? "nexthdr+" : "",
key->off);
+ }
}
if (sel->flags&(TC_U32_VAROFFSET|TC_U32_OFFSET)) {
}
}
+ if (show_stats && tb[TCA_U32_PCNT]){
+ struct tc_u32_pcnt *p = RTA_DATA(tb[TCA_U32_PCNT]);
+ fprintf(f, " (rule hit %llu success %llu)",
+ p->rcnt, p->rhit);
+ }
+ if (tb[TCA_U32_POLICE]) {
+ fprintf(f, "\n");
+ tc_print_police(f, tb[TCA_U32_POLICE]);
+ }
+ if (tb[TCA_U32_INDEV]) {
+ struct rtattr *idev = tb[TCA_U32_INDEV];
+ fprintf(f, "\n input dev %s\n", (char *) RTA_DATA(idev));
+ }
+ if (tb[TCA_U32_ACT]) {
+ tc_print_action(f, tb[TCA_U32_ACT]);
+ }
+
return 0;
}
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
* FIXES: 19990619 - J Hadi Salim (hadi@cyberus.ca)
* simple addattr packaging fix.
+ * 2002: J Hadi Salim - Add tc action extensions syntax
+ *
*/
#include <stdio.h>
#include "utils.h"
#include "tc_util.h"
+struct action_util police_util = {
+ .id = "police",
+ .parse_aopt = act_parse_police,
+ .print_aopt = print_police,
+ .print_xstats = police_print_xstats,
+};
+
static void explain(void)
{
fprintf(stderr, "Usage: ... police rate BPS burst BYTES[/BYTES] [ mtu BYTES[/BYTES] ]\n");
fprintf(stderr, " [ peakrate BPS ] [ avrate BPS ]\n");
- fprintf(stderr, " [ ACTION ]\n");
- fprintf(stderr, "Where: ACTION := reclassify | drop | continue \n");
+ fprintf(stderr, " [ ACTIONTERM ]\n");
+ fprintf(stderr, "Old Syntax ACTIOTERMN := action <EXCEEDACT>[/NOTEXCEEDACT] \n");
+ fprintf(stderr, "New Syntax ACTIONTERM := conform-exceed <EXCEEDACT>[/NOTEXCEEDACT] \n");
+ fprintf(stderr, "Where: *EXCEEDACT := pipe | ok | reclassify | drop | continue \n");
+ fprintf(stderr, "Where: pipe is only valid for new syntax \n");
}
static void explain1(char *arg)
break;
case TC_POLICE_RECLASSIFY:
return "reclassify";
+ case TC_POLICE_PIPE:
+ return "pipe";
default:
snprintf(buf, len, "%d", action);
return buf;
res = TC_POLICE_OK;
else if (matches(arg, "reclassify") == 0)
res = TC_POLICE_RECLASSIFY;
+ else if (matches(arg, "pipe") == 0)
+ res = TC_POLICE_PIPE;
else {
char dummy;
if (sscanf(arg, "%d%c", &res, &dummy) != 1)
return 0;
}
-int parse_police(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+
+int act_parse_police(struct action_util *a,int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
{
int argc = *argc_p;
char **argv = *argv_p;
memset(&p, 0, sizeof(p));
p.action = TC_POLICE_RECLASSIFY;
+ if (a) /* new way of doing things */
+ NEXT_ARG();
+
if (argc <= 0)
return -1;
while (argc > 0) {
+
if (matches(*argv, "index") == 0) {
NEXT_ARG();
- if (get_u32(&p.index, *argv, 16)) {
+ if (get_u32(&p.index, *argv, 10)) {
fprintf(stderr, "Illegal \"index\"\n");
return -1;
}
p.action = TC_POLICE_UNSPEC;
} else if (matches(*argv, "pass") == 0) {
p.action = TC_POLICE_OK;
- } else if (strcmp(*argv, "action") == 0) {
+ } else if (matches(*argv, "pipe") == 0) {
+ p.action = TC_POLICE_PIPE;
+ } else if (strcmp(*argv, "conform-exceed") == 0) {
NEXT_ARG();
if (get_police_result(&p.action, &presult, *argv)) {
fprintf(stderr, "Illegal \"action\"\n");
}
tail = (struct rtattr*)(((void*)n)+NLMSG_ALIGN(n->nlmsg_len));
- addattr_l(n, 1024, tca_id, NULL, 0);
- addattr_l(n, 2024, TCA_POLICE_TBF, &p, sizeof(p));
+ addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+ addattr_l(n, MAX_MSG, TCA_POLICE_TBF, &p, sizeof(p));
if (p.rate.rate)
- addattr_l(n, 3024, TCA_POLICE_RATE, rtab, 1024);
+ addattr_l(n, MAX_MSG, TCA_POLICE_RATE, rtab, 1024);
if (p.peakrate.rate)
- addattr_l(n, 4096, TCA_POLICE_PEAKRATE, ptab, 1024);
+ addattr_l(n, MAX_MSG, TCA_POLICE_PEAKRATE, ptab, 1024);
if (avrate)
- addattr32(n, 4096, TCA_POLICE_AVRATE, avrate);
+ addattr32(n, MAX_MSG, TCA_POLICE_AVRATE, avrate);
if (presult)
- addattr32(n, 4096, TCA_POLICE_RESULT, presult);
-#if 0
-#endif
+ addattr32(n, MAX_MSG, TCA_POLICE_RESULT, presult);
tail->rta_len = (((void*)n)+NLMSG_ALIGN(n->nlmsg_len)) - (void*)tail;
res = 0;
return res;
}
+int parse_police(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+ return act_parse_police(NULL,argc_p,argv_p,tca_id,n);
+}
-int tc_print_police(FILE *f, struct rtattr *arg)
+int
+print_police(struct action_util *a,FILE *f, struct rtattr *arg)
{
SPRINT_BUF(b1);
struct tc_police *p;
fprintf(f, "[NULL police tbf]");
return 0;
}
+#ifndef STOOPID_8BYTE
if (RTA_PAYLOAD(tb[TCA_POLICE_TBF]) < sizeof(*p)) {
fprintf(f, "[truncated police tbf]");
return -1;
}
+#endif
p = RTA_DATA(tb[TCA_POLICE_TBF]);
- fprintf(f, "police %x ", p->index);
- fprintf(f, "action %s", police_action_n2a(p->action, b1, sizeof(b1)));
- if (tb[TCA_POLICE_RESULT]) {
- fprintf(f, "/%s ", police_action_n2a(*(int*)RTA_DATA(tb[TCA_POLICE_RESULT]), b1, sizeof(b1)));
- } else
- fprintf(f, " ");
+ fprintf(f, " police 0x%x ", p->index);
fprintf(f, "rate %s ", sprint_rate(p->rate.rate, b1));
buffer = ((double)p->rate.rate*tc_core_tick2usec(p->burst))/1000000;
fprintf(f, "burst %s ", sprint_size(buffer, b1));
fprintf(f, "peakrate %s ", sprint_rate(p->peakrate.rate, b1));
if (tb[TCA_POLICE_AVRATE])
fprintf(f, "avrate %s ", sprint_rate(*(__u32*)RTA_DATA(tb[TCA_POLICE_AVRATE]), b1));
+ fprintf(f, "action %s", police_action_n2a(p->action, b1, sizeof(b1)));
+ if (tb[TCA_POLICE_RESULT]) {
+ fprintf(f, "/%s ", police_action_n2a(*(int*)RTA_DATA(tb[TCA_POLICE_RESULT]), b1, sizeof(b1)));
+ } else
+ fprintf(f, " ");
+ fprintf(f, "\nref %d bind %d\n",p->refcnt, p->bindcnt);
return 0;
}
+int
+tc_print_police(FILE *f, struct rtattr *arg) {
+ return print_police(&police_util,f,arg);
+}
+
+int
+police_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats)
+{
+ return 0;
+}
+
static void usage(void)
{
fprintf(stderr, "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n"
- "where OBJECT := { qdisc | class | filter }\n"
+ "where OBJECT := { qdisc | class | filter | action }\n"
" OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -b[atch] file }\n");
exit(-1);
}
ret += do_class(largc-1, largv+1);
} else if (matches(largv[0], "filter") == 0) {
ret += do_filter(largc-1, largv+1);
+ } else if (matches(largv[0], "action") == 0) {
+ ret += do_action(largc-1, largv+1);
} else if (matches(largv[0], "help") == 0) {
usage(); /* note that usage() doesn't return */
} else {
return do_class(argc-2, argv+2);
if (matches(argv[1], "filter") == 0)
return do_filter(argc-2, argv+2);
+ if (matches(argv[1], "actions") == 0)
+ return do_action(argc-2, argv+2);
if (matches(argv[1], "help") == 0)
usage();
fprintf(stderr, "Object \"%s\" is unknown, try \"tc help\".\n", argv[1]);
parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len);
if (tb[TCA_KIND] == NULL) {
- fprintf(stderr, "NULL kind\n");
+ fprintf(stderr, "print_class: NULL kind\n");
return -1;
}
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
}
if (q && tb[TCA_XSTATS]) {
q->print_xstats(q, fp, tb[TCA_XSTATS]);
if (rtnl_dump_request(&rth, RTM_GETTCLASS, &t, sizeof(t)) < 0) {
perror("Cannot send dump request");
+ rtnl_close(&rth);
exit(1);
}
if (rtnl_dump_filter(&rth, print_class, stdout, NULL, NULL) < 0) {
fprintf(stderr, "Dump terminated\n");
+ rtnl_close(&rth);
exit(1);
}
extern int do_qdisc(int argc, char **argv);
extern int do_class(int argc, char **argv);
extern int do_filter(int argc, char **argv);
+extern int do_action(int argc, char **argv);
extern int parse_estimator(int *p_argc, char ***p_argv, struct tc_estimator *est);
struct {
struct nlmsghdr n;
struct tcmsg t;
- char buf[4096];
+ char buf[MAX_MSG];
} req;
struct filter_util *q = NULL;
__u32 prio = 0;
if ((req.t.tcm_ifindex = ll_name_to_index(d)) == 0) {
fprintf(stderr, "Cannot find device \"%s\"\n", d);
+ rtnl_close(&rth);
exit(1);
}
}
- if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
+ fprintf(stderr, "We have an error talking to the kernel\n");
+ rtnl_close(&rth);
exit(2);
+ }
rtnl_close(&rth);
return 0;
parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len);
if (tb[TCA_KIND] == NULL) {
- fprintf(stderr, "NULL kind\n");
+ fprintf(stderr, "print_filter: NULL kind\n");
return -1;
}
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_tcstats(fp, &st);
fprintf(fp, "\n");
+#ifndef STOOPID_8BYTE
}
+#endif
}
}
fflush(fp);
if (d[0]) {
if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) {
fprintf(stderr, "Cannot find device \"%s\"\n", d);
+ rtnl_close(&rth);
exit(1);
}
filter_ifindex = t.tcm_ifindex;
if (rtnl_dump_request(&rth, RTM_GETTFILTER, &t, sizeof(t)) < 0) {
perror("Cannot send dump request");
+ rtnl_close(&rth);
exit(1);
}
if (rtnl_open(&rth, 0) < 0) {
fprintf(stderr, "Cannot open rtnetlink\n");
+ rtnl_close(&rth);
exit(1);
}
if ((idx = ll_name_to_index(d)) == 0) {
fprintf(stderr, "Cannot find device \"%s\"\n", d);
+ rtnl_close(&rth);
exit(1);
}
req.t.tcm_ifindex = idx;
}
- if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
+ rtnl_close(&rth);
exit(2);
+ }
rtnl_close(&rth);
return 0;
{
SPRINT_BUF(b1);
- fprintf(fp, " Sent %llu bytes %u pkts (dropped %u, overlimits %u) ",
+ 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) {
parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len);
if (tb[TCA_KIND] == NULL) {
- fprintf(stderr, "NULL kind\n");
+ fprintf(stderr, "print_qdisc: NULL kind\n");
return -1;
}
if (t->tcm_info != 1) {
fprintf(fp, "refcnt %d ", t->tcm_info);
}
- q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND]));
+ /* pfifo_fast is generic enough to warrant the hardcoding --JHS */
+
+ if (0 == strcmp("pfifo_fast", RTA_DATA(tb[TCA_KIND])))
+ q = get_qdisc_kind("prio");
+ else
+ q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND]));
+
if (tb[TCA_OPTIONS]) {
if (q)
q->print_qopt(q, fp, tb[TCA_OPTIONS]);
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_tcstats(fp, &st);
fprintf(fp, "\n");
}
+#ifndef STOOPID_8BYTE
}
+#endif
if (q && tb[TCA_XSTATS]) {
q->print_xstats(q, fp, tb[TCA_XSTATS]);
fprintf(fp, "\n");
if (d[0]) {
if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) {
fprintf(stderr, "Cannot find device \"%s\"\n", d);
+ rtnl_close(&rth);
exit(1);
}
filter_ifindex = t.tcm_ifindex;
if (rtnl_dump_request(&rth, RTM_GETQDISC, &t, sizeof(t)) < 0) {
perror("Cannot send dump request");
+ rtnl_close(&rth);
exit(1);
}
if (rtnl_dump_filter(&rth, print_qdisc, stdout, NULL, NULL) < 0) {
fprintf(stderr, "Dump terminated\n");
+ rtnl_close(&rth);
exit(1);
}
return buf;
}
+char * action_n2a(int action, char *buf, int len)
+{
+ switch (action) {
+ case -1:
+ return "continue";
+ break;
+ case TC_ACT_OK:
+ return "pass";
+ break;
+ case TC_ACT_SHOT:
+ return "drop";
+ break;
+ case TC_ACT_RECLASSIFY:
+ return "reclassify";
+ case TC_ACT_PIPE:
+ return "pipe";
+ case TC_ACT_STOLEN:
+ return "stolen";
+ default:
+ snprintf(buf, len, "%d", action);
+ return buf;
+ }
+}
+int action_a2n(char *arg, int *result)
+{
+ int res;
+
+ if (matches(arg, "continue") == 0)
+ res = -1;
+ else if (matches(arg, "drop") == 0)
+ res = TC_ACT_SHOT;
+ else if (matches(arg, "shot") == 0)
+ res = TC_ACT_SHOT;
+ else if (matches(arg, "pass") == 0)
+ res = TC_ACT_OK;
+ else if (strcmp(arg, "ok") == 0)
+ res = TC_ACT_OK;
+ else if (matches(arg, "reclassify") == 0)
+ res = TC_ACT_RECLASSIFY;
+ else {
+ char dummy;
+ if (sscanf(arg, "%d%c", &res, &dummy) != 1)
+ return -1;
+ }
+ *result = res;
+ return 0;
+}
+
+void print_tm(FILE * f, struct tcf_t *tm)
+{
+ int hz = get_hz();
+ if (tm->install != 0)
+ fprintf(f, " installed %d sec", tm->install/hz);
+ if (tm->lastuse != 0)
+ fprintf(f, " used %d sec", tm->lastuse/hz);
+ if (tm->expires != 0)
+ fprintf(f, " expires %d sec", tm->expires/hz);
+}
#ifndef _TC_UTIL_H_
#define _TC_UTIL_H_ 1
+#define MAX_MSG 16384
#include <linux/pkt_sched.h>
#include <linux/pkt_cls.h>
#include "tc_core.h"
struct qdisc_util
{
- struct qdisc_util *next;
+ struct qdisc_util *next;
char id[16];
int (*parse_qopt)(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n);
int (*print_qopt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt);
int (*print_fopt)(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle);
};
+struct action_util
+{
+ struct action_util *next;
+ char id[16];
+ int (*parse_aopt)(struct action_util *a, int *argc, char ***argv, int code, struct nlmsghdr *n);
+ int (*print_aopt)(struct action_util *au, FILE *f, struct rtattr *opt);
+ int (*print_xstats)(struct action_util *au, FILE *f, struct rtattr *xstats);
+
+};
+
extern struct qdisc_util *get_qdisc_kind(const char *str);
extern struct filter_util *get_filter_kind(const char *str);
extern int tc_print_police(FILE *f, struct rtattr *tb);
extern int parse_police(int *, char ***, int, struct nlmsghdr *);
+extern char *action_n2a(int action, char *buf, int len);
+extern int action_a2n(char *arg, int *result);
+extern int act_parse_police(struct action_util *a,int *, char ***, int, struct nlmsghdr *);
+extern int print_police(struct action_util *a,FILE *f, struct rtattr *tb);
+extern int police_print_xstats(struct action_util *a,FILE *f, struct rtattr *tb);
+extern int tc_print_action(FILE *f, struct rtattr *tb);
+extern int tc_print_ipt(FILE *f, struct rtattr *tb);
+extern int parse_action(int *, char ***, int, struct nlmsghdr *);
+extern void print_tm(FILE *f, struct tcf_t *tm);
#endif