iproute2: Initial checkin of version iproute2-2.6.31
For http://b/issue?id=2576057
Change-Id: Ic2034b9512b4cbf7a2d66501cd9ef387355eba1d
Signed-off-by: San Mehat <san@google.com>
diff --git a/tc/.gitignore b/tc/.gitignore
new file mode 100644
index 0000000..e8e86c9
--- /dev/null
+++ b/tc/.gitignore
@@ -0,0 +1,5 @@
+*.yacc.c
+*.lex.c
+*.output
+*.yacc.h
+tc
diff --git a/tc/Makefile b/tc/Makefile
new file mode 100644
index 0000000..f3dd2b7
--- /dev/null
+++ b/tc/Makefile
@@ -0,0 +1,130 @@
+TCOBJ= tc.o tc_qdisc.o tc_class.o tc_filter.o tc_util.o \
+ tc_monitor.o m_police.o m_estimator.o m_action.o \
+ m_ematch.o emp_ematch.yacc.o emp_ematch.lex.o
+
+include ../Config
+SHARED_LIBS ?= y
+
+TCMODULES :=
+TCMODULES += q_fifo.o
+TCMODULES += q_sfq.o
+TCMODULES += q_red.o
+TCMODULES += q_prio.o
+TCMODULES += q_tbf.o
+TCMODULES += q_cbq.o
+TCMODULES += q_rr.o
+TCMODULES += q_multiq.o
+TCMODULES += q_netem.o
+TCMODULES += f_rsvp.o
+TCMODULES += f_u32.o
+TCMODULES += f_route.o
+TCMODULES += f_fw.o
+TCMODULES += f_basic.o
+TCMODULES += f_flow.o
+TCMODULES += f_cgroup.o
+TCMODULES += q_dsmark.o
+TCMODULES += q_gred.o
+TCMODULES += f_tcindex.o
+TCMODULES += q_ingress.o
+TCMODULES += q_hfsc.o
+TCMODULES += q_htb.o
+TCMODULES += q_drr.o
+TCMODULES += m_gact.o
+TCMODULES += m_mirred.o
+TCMODULES += m_nat.o
+TCMODULES += m_pedit.o
+TCMODULES += m_skbedit.o
+TCMODULES += p_ip.o
+TCMODULES += p_icmp.o
+TCMODULES += p_tcp.o
+TCMODULES += p_udp.o
+TCMODULES += em_nbyte.o
+TCMODULES += em_cmp.o
+TCMODULES += em_u32.o
+TCMODULES += em_meta.o
+
+
+ifeq ($(TC_CONFIG_XT),y)
+ TCMODULES += m_xt.o
+ LDLIBS += -lxtables
+else
+ ifeq ($(TC_CONFIG_XT_OLD),y)
+ TCMODULES += m_xt_old.o
+ LDLIBS += -lxtables
+ else
+ ifeq ($(TC_CONFIG_XT_OLD_H),y)
+ CFLAGS += -DTC_CONFIG_XT_H
+ TCMODULES += m_xt_old.o
+ LDLIBS += -lxtables
+ else
+ TCMODULES += m_ipt.o
+ endif
+ endif
+endif
+
+TCOBJ += $(TCMODULES)
+LDLIBS += -L. -ltc -lm
+
+ifeq ($(SHARED_LIBS),y)
+LDLIBS += -ldl
+LDFLAGS += -Wl,-export-dynamic
+endif
+
+TCLIB := tc_core.o
+TCLIB += tc_red.o
+TCLIB += tc_cbq.o
+TCLIB += tc_estimator.o
+TCLIB += tc_stab.o
+
+CFLAGS += -DCONFIG_GACT -DCONFIG_GACT_PROB
+
+TCSO :=
+ifeq ($(TC_CONFIG_ATM),y)
+ TCSO += q_atm.so
+endif
+
+YACC := bison
+LEX := flex
+
+%.so: %.c
+ $(CC) $(CFLAGS) -shared -fpic $< -o $@
+
+
+all: libtc.a tc $(TCSO)
+
+tc: $(TCOBJ) $(LIBNETLINK) $(LIBUTIL) $(TCLIB)
+
+libtc.a: $(TCLIB)
+ $(AR) rcs $@ $(TCLIB)
+
+install: all
+ mkdir -p $(DESTDIR)$(LIBDIR)/tc
+ install -m 0755 tc $(DESTDIR)$(SBINDIR)
+ for i in $(TCSO); \
+ do install -m 755 $$i $(DESTDIR)$(LIBDIR)/tc; \
+ done
+
+clean:
+ rm -f $(TCOBJ) $(TCLIB) libtc.a tc *.so emp_ematch.yacc.h; \
+ rm -f emp_ematch.yacc.output
+
+q_atm.so: q_atm.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -shared -fpic -o q_atm.so q_atm.c -latm
+
+%.yacc.c: %.y
+ $(YACC) $(YACCFLAGS) -o $@ $<
+
+%.lex.c: %.l
+ $(LEX) $(LEXFLAGS) -o$@ $<
+
+ifneq ($(SHARED_LIBS),y)
+
+tc: static-syms.o
+static-syms.o: static-syms.h
+static-syms.h: $(wildcard *.c)
+ files="$^" ; \
+ for s in `grep -B 3 '\<dlsym' $$files | sed -n '/snprintf/{s:.*"\([^"]*\)".*:\1:;s:%s::;p}'` ; do \
+ sed -n '/'$$s'[^ ]* =/{s:.* \([^ ]*'$$s'[^ ]*\) .*:extern char \1[] __attribute__((weak)); if (!strcmp(sym, "\1")) return \1;:;p}' $$files ; \
+ done > $@
+
+endif
diff --git a/tc/README.last b/tc/README.last
new file mode 100644
index 0000000..9400438
--- /dev/null
+++ b/tc/README.last
@@ -0,0 +1,47 @@
+Kernel code and interface.
+--------------------------
+
+* Compile time switches
+
+There is only one, but very important, compile time switch.
+It is not settable by "make config", but should be selected
+manually and after a bit of thinking in <include/net/pkt_sched.h>
+
+PSCHED_CLOCK_SOURCE can take three values:
+
+ PSCHED_GETTIMEOFDAY
+ PSCHED_JIFFIES
+ PSCHED_CPU
+
+
+ PSCHED_GETTIMEOFDAY
+
+Default setting is the most conservative PSCHED_GETTIMEOFDAY.
+It is very slow both because of weird slowness of do_gettimeofday()
+and because it forces code to use unnatural "timeval" format,
+where microseconds and seconds fields are separate.
+Besides that, it will misbehave, when delays exceed 2 seconds
+(f.e. very slow links or classes bounded to small slice of bandwidth)
+To resume: as only you will get it working, select correct clock
+source and forget about PSCHED_GETTIMEOFDAY forever.
+
+
+ PSCHED_JIFFIES
+
+Clock is derived from jiffies. On architectures with HZ=100
+granularity of this clock is not enough to make reasonable
+bindings to real time. However, taking into account Linux
+architecture problems, which force us to use artificial
+integrated clock in any case, this switch is not so bad
+for schduling even on high speed networks, though policing
+is not reliable.
+
+
+ PSCHED_CPU
+
+It is available only for alpha and pentiums with correct
+CPU timestamp. It is the fastest way, use it when it is available,
+but remember: not all pentiums have this facility, and
+a lot of them have clock, broken by APM etc. etc.
+
+
diff --git a/tc/em_cmp.c b/tc/em_cmp.c
new file mode 100644
index 0000000..6addce0
--- /dev/null
+++ b/tc/em_cmp.c
@@ -0,0 +1,187 @@
+/*
+ * em_cmp.c Simple comparison Ematch
+ *
+ * This program is free software; you can distribute 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: Thomas Graf <tgraf@suug.ch>
+ */
+
+#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 <errno.h>
+
+#include "m_ematch.h"
+#include <linux/tc_ematch/tc_em_cmp.h>
+
+extern struct ematch_util cmp_ematch_util;
+
+static void cmp_print_usage(FILE *fd)
+{
+ fprintf(fd,
+ "Usage: cmp(ALIGN at OFFSET [ ATTRS ] { eq | lt | gt } VALUE)\n" \
+ "where: ALIGN := { u8 | u16 | u32 }\n" \
+ " ATTRS := [ layer LAYER ] [ mask MASK ] [ trans ]\n" \
+ " LAYER := { link | network | transport | 0..%d }\n" \
+ "\n" \
+ "Example: cmp(u16 at 3 layer 2 mask 0xff00 gt 20)\n",
+ TCF_LAYER_MAX);
+}
+
+static int cmp_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
+ struct bstr *args)
+{
+ struct bstr *a;
+ int align, opnd = 0;
+ unsigned long offset = 0, layer = TCF_LAYER_NETWORK, mask = 0, value = 0;
+ int offset_present = 0, value_present = 0;
+ struct tcf_em_cmp cmp;
+
+ memset(&cmp, 0, sizeof(cmp));
+
+#define PARSE_ERR(CARG, FMT, ARGS...) \
+ em_parse_error(EINVAL, args, CARG, &cmp_ematch_util, FMT ,##ARGS)
+
+ if (args == NULL)
+ return PARSE_ERR(args, "cmp: missing arguments");
+
+ if (!bstrcmp(args, "u8"))
+ align = TCF_EM_ALIGN_U8;
+ else if (!bstrcmp(args, "u16"))
+ align = TCF_EM_ALIGN_U16;
+ else if (!bstrcmp(args, "u32"))
+ align = TCF_EM_ALIGN_U32;
+ else
+ return PARSE_ERR(args, "cmp: invalid alignment");
+
+ for (a = bstr_next(args); a; a = bstr_next(a)) {
+ if (!bstrcmp(a, "at")) {
+ if (a->next == NULL)
+ return PARSE_ERR(a, "cmp: missing argument");
+ a = bstr_next(a);
+
+ offset = bstrtoul(a);
+ if (offset == ULONG_MAX)
+ return PARSE_ERR(a, "cmp: invalid offset, " \
+ "must be numeric");
+
+ offset_present = 1;
+ } else if (!bstrcmp(a, "layer")) {
+ if (a->next == NULL)
+ return PARSE_ERR(a, "cmp: missing argument");
+ a = bstr_next(a);
+
+ layer = parse_layer(a);
+ if (layer == INT_MAX) {
+ layer = bstrtoul(a);
+ if (layer == ULONG_MAX)
+ return PARSE_ERR(a, "cmp: invalid " \
+ "layer");
+ }
+
+ if (layer > TCF_LAYER_MAX)
+ return PARSE_ERR(a, "cmp: illegal layer, " \
+ "must be in 0..%d", TCF_LAYER_MAX);
+ } else if (!bstrcmp(a, "mask")) {
+ if (a->next == NULL)
+ return PARSE_ERR(a, "cmp: missing argument");
+ a = bstr_next(a);
+
+ mask = bstrtoul(a);
+ if (mask == ULONG_MAX)
+ return PARSE_ERR(a, "cmp: invalid mask");
+ } else if (!bstrcmp(a, "trans")) {
+ cmp.flags |= TCF_EM_CMP_TRANS;
+ } else if (!bstrcmp(a, "eq") || !bstrcmp(a, "gt") ||
+ !bstrcmp(a, "lt")) {
+
+ if (!bstrcmp(a, "eq"))
+ opnd = TCF_EM_OPND_EQ;
+ else if (!bstrcmp(a, "gt"))
+ opnd = TCF_EM_OPND_GT;
+ else if (!bstrcmp(a, "lt"))
+ opnd = TCF_EM_OPND_LT;
+
+ if (a->next == NULL)
+ return PARSE_ERR(a, "cmp: missing argument");
+ a = bstr_next(a);
+
+ value = bstrtoul(a);
+ if (value == ULONG_MAX)
+ return PARSE_ERR(a, "cmp: invalid value");
+
+ value_present = 1;
+ } else
+ return PARSE_ERR(a, "nbyte: unknown parameter");
+ }
+
+ if (offset_present == 0 || value_present == 0)
+ return PARSE_ERR(a, "cmp: offset and value required");
+
+ cmp.val = (__u32) value;
+ cmp.mask = (__u32) mask;
+ cmp.off = (__u16) offset;
+ cmp.align = (__u8) align;
+ cmp.layer = (__u8) layer;
+ cmp.opnd = (__u8) opnd;
+
+ addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
+ addraw_l(n, MAX_MSG, &cmp, sizeof(cmp));
+
+#undef PARSE_ERR
+ return 0;
+}
+
+static int cmp_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
+ int data_len)
+{
+ struct tcf_em_cmp *cmp = data;
+
+ if (data_len < sizeof(*cmp)) {
+ fprintf(stderr, "CMP header size mismatch\n");
+ return -1;
+ }
+
+ if (cmp->align == TCF_EM_ALIGN_U8)
+ fprintf(fd, "u8 ");
+ else if (cmp->align == TCF_EM_ALIGN_U16)
+ fprintf(fd, "u16 ");
+ else if (cmp->align == TCF_EM_ALIGN_U16)
+ fprintf(fd, "u32 ");
+
+ fprintf(fd, "at %d layer %d ", cmp->off, cmp->layer);
+
+ if (cmp->mask)
+ fprintf(fd, "mask 0x%x ", cmp->mask);
+
+ if (cmp->flags & TCF_EM_CMP_TRANS)
+ fprintf(fd, "trans ");
+
+ if (cmp->opnd == TCF_EM_OPND_EQ)
+ fprintf(fd, "eq ");
+ else if (cmp->opnd == TCF_EM_OPND_LT)
+ fprintf(fd, "lt ");
+ else if (cmp->opnd == TCF_EM_OPND_GT)
+ fprintf(fd, "gt ");
+
+ fprintf(fd, "%d", cmp->val);
+
+ return 0;
+}
+
+struct ematch_util cmp_ematch_util = {
+ .kind = "cmp",
+ .kind_num = TCF_EM_CMP,
+ .parse_eopt = cmp_parse_eopt,
+ .print_eopt = cmp_print_eopt,
+ .print_usage = cmp_print_usage
+};
diff --git a/tc/em_meta.c b/tc/em_meta.c
new file mode 100644
index 0000000..033e29f
--- /dev/null
+++ b/tc/em_meta.c
@@ -0,0 +1,546 @@
+/*
+ * em_meta.c Metadata Ematch
+ *
+ * This program is free software; you can distribute 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: Thomas Graf <tgraf@suug.ch>
+ */
+
+#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 <errno.h>
+
+#include "m_ematch.h"
+#include <linux/tc_ematch/tc_em_meta.h>
+
+extern struct ematch_util meta_ematch_util;
+
+static void meta_print_usage(FILE *fd)
+{
+ fprintf(fd,
+ "Usage: meta(OBJECT { eq | lt | gt } OBJECT)\n" \
+ "where: OBJECT := { META_ID | VALUE }\n" \
+ " META_ID := id [ shift SHIFT ] [ mask MASK ]\n" \
+ "\n" \
+ "Example: meta(nfmark gt 24)\n" \
+ " meta(indev shift 1 eq \"ppp\")\n" \
+ " meta(tcindex mask 0xf0 eq 0xf0)\n" \
+ "\n" \
+ "For a list of meta identifiers, use meta(list).\n");
+}
+
+struct meta_entry {
+ int id;
+ char * kind;
+ char * mask;
+ char * desc;
+} meta_table[] = {
+#define TCF_META_ID_SECTION 0
+#define __A(id, name, mask, desc) { TCF_META_ID_##id, name, mask, desc }
+ __A(SECTION, "Generic", "", ""),
+ __A(RANDOM, "random", "i",
+ "Random value (32 bit)"),
+ __A(LOADAVG_0, "loadavg_1", "i",
+ "Load average in last minute"),
+ __A(LOADAVG_1, "loadavg_5", "i",
+ "Load average in last 5 minutes"),
+ __A(LOADAVG_2, "loadavg_15", "i",
+ "Load average in last 15 minutes"),
+
+ __A(SECTION, "Interfaces", "", ""),
+ __A(DEV, "dev", "iv",
+ "Device the packet is on"),
+ __A(SECTION, "Packet attributes", "", ""),
+ __A(PRIORITY, "priority", "i",
+ "Priority of packet"),
+ __A(PROTOCOL, "protocol", "i",
+ "Link layer protocol"),
+ __A(PKTTYPE, "pkt_type", "i",
+ "Packet type (uni|multi|broad|...)cast"),
+ __A(PKTLEN, "pkt_len", "i",
+ "Length of packet"),
+ __A(DATALEN, "data_len", "i",
+ "Length of data in packet"),
+ __A(MACLEN, "mac_len", "i",
+ "Length of link layer header"),
+
+ __A(SECTION, "Netfilter", "", ""),
+ __A(NFMARK, "nf_mark", "i",
+ "Netfilter mark"),
+ __A(NFMARK, "fwmark", "i",
+ "Alias for nf_mark"),
+
+ __A(SECTION, "Traffic Control", "", ""),
+ __A(TCINDEX, "tc_index", "i", "TC Index"),
+ __A(SECTION, "Routing", "", ""),
+ __A(RTCLASSID, "rt_classid", "i",
+ "Routing ClassID (cls_route)"),
+ __A(RTIIF, "rt_iif", "i",
+ "Incoming interface index"),
+ __A(VLAN_TAG, "vlan", "i", "Vlan tag"),
+
+ __A(SECTION, "Sockets", "", ""),
+ __A(SK_FAMILY, "sk_family", "i", "Address family"),
+ __A(SK_STATE, "sk_state", "i", "State"),
+ __A(SK_REUSE, "sk_reuse", "i", "Reuse Flag"),
+ __A(SK_BOUND_IF, "sk_bind_if", "iv", "Bound interface"),
+ __A(SK_REFCNT, "sk_refcnt", "i", "Reference counter"),
+ __A(SK_SHUTDOWN, "sk_shutdown", "i", "Shutdown mask"),
+ __A(SK_PROTO, "sk_proto", "i", "Protocol"),
+ __A(SK_TYPE, "sk_type", "i", "Type"),
+ __A(SK_RCVBUF, "sk_rcvbuf", "i", "Receive buffer size"),
+ __A(SK_RMEM_ALLOC, "sk_rmem", "i", "RMEM"),
+ __A(SK_WMEM_ALLOC, "sk_wmem", "i", "WMEM"),
+ __A(SK_OMEM_ALLOC, "sk_omem", "i", "OMEM"),
+ __A(SK_WMEM_QUEUED, "sk_wmem_queue","i", "WMEM queue"),
+ __A(SK_SND_QLEN, "sk_snd_queue", "i", "Send queue length"),
+ __A(SK_RCV_QLEN, "sk_rcv_queue", "i", "Receive queue length"),
+ __A(SK_ERR_QLEN, "sk_err_queue", "i", "Error queue length"),
+ __A(SK_FORWARD_ALLOCS, "sk_fwd_alloc", "i", "Forward allocations"),
+ __A(SK_SNDBUF, "sk_sndbuf", "i", "Send buffer size"),
+#undef __A
+};
+
+static inline int map_type(char k)
+{
+ switch (k) {
+ case 'i': return TCF_META_TYPE_INT;
+ case 'v': return TCF_META_TYPE_VAR;
+ }
+
+ fprintf(stderr, "BUG: Unknown map character '%c'\n", k);
+ return INT_MAX;
+}
+
+static struct meta_entry * lookup_meta_entry(struct bstr *kind)
+{
+ int i;
+
+ for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++)
+ if (!bstrcmp(kind, meta_table[i].kind) &&
+ meta_table[i].id != 0)
+ return &meta_table[i];
+
+ return NULL;
+}
+
+static struct meta_entry * lookup_meta_entry_byid(int id)
+{
+ int i;
+
+ for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++)
+ if (meta_table[i].id == id)
+ return &meta_table[i];
+
+ return NULL;
+}
+
+static inline void dump_value(struct nlmsghdr *n, int tlv, unsigned long val,
+ struct tcf_meta_val *hdr)
+{
+ __u32 t;
+
+ switch (TCF_META_TYPE(hdr->kind)) {
+ case TCF_META_TYPE_INT:
+ t = val;
+ addattr_l(n, MAX_MSG, tlv, &t, sizeof(t));
+ break;
+
+ case TCF_META_TYPE_VAR:
+ if (TCF_META_ID(hdr->kind) == TCF_META_ID_VALUE) {
+ struct bstr *a = (struct bstr *) val;
+ addattr_l(n, MAX_MSG, tlv, a->data, a->len);
+ }
+ break;
+ }
+}
+
+static inline int is_compatible(struct tcf_meta_val *what,
+ struct tcf_meta_val *needed)
+{
+ char *p;
+ struct meta_entry *entry;
+
+ entry = lookup_meta_entry_byid(TCF_META_ID(what->kind));
+
+ if (entry == NULL)
+ return 0;
+
+ for (p = entry->mask; p; p++)
+ if (map_type(*p) == TCF_META_TYPE(needed->kind))
+ return 1;
+
+ return 0;
+}
+
+static void list_meta_ids(FILE *fd)
+{
+ int i;
+
+ fprintf(fd,
+ "--------------------------------------------------------\n" \
+ " ID Type Description\n" \
+ "--------------------------------------------------------");
+
+ for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) {
+ if (meta_table[i].id == TCF_META_ID_SECTION) {
+ fprintf(fd, "\n%s:\n", meta_table[i].kind);
+ } else {
+ char *p = meta_table[i].mask;
+ char buf[64] = {0};
+
+ fprintf(fd, " %-16s ", meta_table[i].kind);
+
+ while (*p) {
+ int type = map_type(*p);
+
+ switch (type) {
+ case TCF_META_TYPE_INT:
+ strcat(buf, "INT");
+ break;
+
+ case TCF_META_TYPE_VAR:
+ strcat(buf, "VAR");
+ break;
+ }
+
+ if (*(++p))
+ strcat(buf, ",");
+ }
+
+ fprintf(fd, "%-10s %s\n", buf, meta_table[i].desc);
+ }
+ }
+
+ fprintf(fd,
+ "--------------------------------------------------------\n");
+}
+
+#undef TCF_META_ID_SECTION
+
+#define PARSE_FAILURE ((void *) (-1))
+
+#define PARSE_ERR(CARG, FMT, ARGS...) \
+ em_parse_error(EINVAL, args, CARG, &meta_ematch_util, FMT ,##ARGS)
+
+static inline int can_adopt(struct tcf_meta_val *val)
+{
+ return !!TCF_META_ID(val->kind);
+}
+
+static inline int overwrite_type(struct tcf_meta_val *src,
+ struct tcf_meta_val *dst)
+{
+ return (TCF_META_TYPE(dst->kind) << 12) | TCF_META_ID(src->kind);
+}
+
+
+static inline struct bstr *
+parse_object(struct bstr *args, struct bstr *arg, struct tcf_meta_val *obj,
+ unsigned long *dst, struct tcf_meta_val *left)
+{
+ struct meta_entry *entry;
+ unsigned long num;
+ struct bstr *a;
+
+ if (arg->quoted) {
+ obj->kind = TCF_META_TYPE_VAR << 12;
+ obj->kind |= TCF_META_ID_VALUE;
+ *dst = (unsigned long) arg;
+ return bstr_next(arg);
+ }
+
+ num = bstrtoul(arg);
+ if (num != ULONG_MAX) {
+ obj->kind = TCF_META_TYPE_INT << 12;
+ obj->kind |= TCF_META_ID_VALUE;
+ *dst = (unsigned long) num;
+ return bstr_next(arg);
+ }
+
+ entry = lookup_meta_entry(arg);
+
+ if (entry == NULL) {
+ PARSE_ERR(arg, "meta: unknown meta id\n");
+ return PARSE_FAILURE;
+ }
+
+ obj->kind = entry->id | (map_type(entry->mask[0]) << 12);
+
+ if (left) {
+ struct tcf_meta_val *right = obj;
+
+ if (TCF_META_TYPE(right->kind) == TCF_META_TYPE(left->kind))
+ goto compatible;
+
+ if (can_adopt(left) && !can_adopt(right)) {
+ if (is_compatible(left, right))
+ left->kind = overwrite_type(left, right);
+ else
+ goto not_compatible;
+ } else if (can_adopt(right) && !can_adopt(left)) {
+ if (is_compatible(right, left))
+ right->kind = overwrite_type(right, left);
+ else
+ goto not_compatible;
+ } else if (can_adopt(left) && can_adopt(right)) {
+ if (is_compatible(left, right))
+ left->kind = overwrite_type(left, right);
+ else if (is_compatible(right, left))
+ right->kind = overwrite_type(right, left);
+ else
+ goto not_compatible;
+ } else
+ goto not_compatible;
+ }
+
+compatible:
+
+ a = bstr_next(arg);
+
+ while(a) {
+ if (!bstrcmp(a, "shift")) {
+ unsigned long shift;
+
+ if (a->next == NULL) {
+ PARSE_ERR(a, "meta: missing argument");
+ return PARSE_FAILURE;
+ }
+ a = bstr_next(a);
+
+ shift = bstrtoul(a);
+ if (shift == ULONG_MAX) {
+ PARSE_ERR(a, "meta: invalid shift, must " \
+ "be numeric");
+ return PARSE_FAILURE;
+ }
+
+ obj->shift = (__u8) shift;
+ a = bstr_next(a);
+ } else if (!bstrcmp(a, "mask")) {
+ unsigned long mask;
+
+ if (a->next == NULL) {
+ PARSE_ERR(a, "meta: missing argument");
+ return PARSE_FAILURE;
+ }
+ a = bstr_next(a);
+
+ mask = bstrtoul(a);
+ if (mask == ULONG_MAX) {
+ PARSE_ERR(a, "meta: invalid mask, must be " \
+ "numeric");
+ return PARSE_FAILURE;
+ }
+ *dst = (unsigned long) mask;
+ a = bstr_next(a);
+ } else
+ break;
+ }
+
+ return a;
+
+not_compatible:
+ PARSE_ERR(arg, "lvalue and rvalue are not compatible.");
+ return PARSE_FAILURE;
+}
+
+static int meta_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
+ struct bstr *args)
+{
+ int opnd;
+ struct bstr *a;
+ struct tcf_meta_hdr meta_hdr;
+ unsigned long lvalue = 0, rvalue = 0;
+
+ memset(&meta_hdr, 0, sizeof(meta_hdr));
+
+ if (args == NULL)
+ return PARSE_ERR(args, "meta: missing arguments");
+
+ if (!bstrcmp(args, "list")) {
+ list_meta_ids(stderr);
+ return -1;
+ }
+
+ a = parse_object(args, args, &meta_hdr.left, &lvalue, NULL);
+ if (a == PARSE_FAILURE)
+ return -1;
+ else if (a == NULL)
+ return PARSE_ERR(args, "meta: missing operand");
+
+ if (!bstrcmp(a, "eq"))
+ opnd = TCF_EM_OPND_EQ;
+ else if (!bstrcmp(a, "gt"))
+ opnd = TCF_EM_OPND_GT;
+ else if (!bstrcmp(a, "lt"))
+ opnd = TCF_EM_OPND_LT;
+ else
+ return PARSE_ERR(a, "meta: invalid operand");
+
+ meta_hdr.left.op = (__u8) opnd;
+
+ if (a->next == NULL)
+ return PARSE_ERR(args, "meta: missing rvalue");
+ a = bstr_next(a);
+
+ a = parse_object(args, a, &meta_hdr.right, &rvalue, &meta_hdr.left);
+ if (a == PARSE_FAILURE)
+ return -1;
+ else if (a != NULL)
+ return PARSE_ERR(a, "meta: unexpected trailer");
+
+
+ addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
+
+ addattr_l(n, MAX_MSG, TCA_EM_META_HDR, &meta_hdr, sizeof(meta_hdr));
+
+ dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left);
+ dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right);
+
+ return 0;
+}
+#undef PARSE_ERR
+
+static inline void print_binary(FILE *fd, unsigned char *str, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (!isprint(str[i]))
+ goto binary;
+
+ for (i = 0; i < len; i++)
+ fprintf(fd, "%c", str[i]);
+ return;
+
+binary:
+ for (i = 0; i < len; i++)
+ fprintf(fd, "%02x ", str[i]);
+
+ fprintf(fd, "\"");
+ for (i = 0; i < len; i++)
+ fprintf(fd, "%c", isprint(str[i]) ? str[i] : '.');
+ fprintf(fd, "\"");
+}
+
+static inline int print_value(FILE *fd, int type, struct rtattr *rta)
+{
+ if (rta == NULL) {
+ fprintf(stderr, "Missing value TLV\n");
+ return -1;
+ }
+
+ switch(type) {
+ case TCF_META_TYPE_INT:
+ if (RTA_PAYLOAD(rta) < sizeof(__u32)) {
+ fprintf(stderr, "meta int type value TLV " \
+ "size mismatch.\n");
+ return -1;
+ }
+ fprintf(fd, "%d", *(__u32 *) RTA_DATA(rta));
+ break;
+
+ case TCF_META_TYPE_VAR:
+ print_binary(fd, RTA_DATA(rta), RTA_PAYLOAD(rta));
+ break;
+ }
+
+ return 0;
+}
+
+static int print_object(FILE *fd, struct tcf_meta_val *obj, struct rtattr *rta)
+{
+ int id = TCF_META_ID(obj->kind);
+ int type = TCF_META_TYPE(obj->kind);
+ struct meta_entry *entry;
+
+ if (id == TCF_META_ID_VALUE)
+ return print_value(fd, type, rta);
+
+ entry = lookup_meta_entry_byid(id);
+
+ if (entry == NULL)
+ fprintf(fd, "[unknown meta id %d]", id);
+ else
+ fprintf(fd, "%s", entry->kind);
+
+ if (obj->shift)
+ fprintf(fd, " shift %d", obj->shift);
+
+ switch (type) {
+ case TCF_META_TYPE_INT:
+ if (rta) {
+ if (RTA_PAYLOAD(rta) < sizeof(__u32))
+ goto size_mismatch;
+
+ fprintf(fd, " mask 0x%08x",
+ *(__u32*) RTA_DATA(rta));
+ }
+ break;
+ }
+
+ return 0;
+
+size_mismatch:
+ fprintf(stderr, "meta int type mask TLV size mismatch\n");
+ return -1;
+}
+
+
+static int meta_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
+ int data_len)
+{
+ struct rtattr *tb[TCA_EM_META_MAX+1];
+ struct tcf_meta_hdr *meta_hdr;
+
+ if (parse_rtattr(tb, TCA_EM_META_MAX, data, data_len) < 0)
+ return -1;
+
+ if (tb[TCA_EM_META_HDR] == NULL) {
+ fprintf(stderr, "Missing meta header\n");
+ return -1;
+ }
+
+ if (RTA_PAYLOAD(tb[TCA_EM_META_HDR]) < sizeof(*meta_hdr)) {
+ fprintf(stderr, "Meta header size mismatch\n");
+ return -1;
+ }
+
+ meta_hdr = RTA_DATA(tb[TCA_EM_META_HDR]);
+
+ if (print_object(fd, &meta_hdr->left, tb[TCA_EM_META_LVALUE]) < 0)
+ return -1;
+
+ switch (meta_hdr->left.op) {
+ case TCF_EM_OPND_EQ:
+ fprintf(fd, " eq ");
+ break;
+ case TCF_EM_OPND_LT:
+ fprintf(fd, " lt ");
+ break;
+ case TCF_EM_OPND_GT:
+ fprintf(fd, " gt ");
+ break;
+ }
+
+ return print_object(fd, &meta_hdr->right, tb[TCA_EM_META_RVALUE]);
+}
+
+struct ematch_util meta_ematch_util = {
+ .kind = "meta",
+ .kind_num = TCF_EM_META,
+ .parse_eopt = meta_parse_eopt,
+ .print_eopt = meta_print_eopt,
+ .print_usage = meta_print_usage
+};
diff --git a/tc/em_nbyte.c b/tc/em_nbyte.c
new file mode 100644
index 0000000..87f3e9d
--- /dev/null
+++ b/tc/em_nbyte.c
@@ -0,0 +1,143 @@
+/*
+ * em_nbyte.c N-Byte Ematch
+ *
+ * This program is free software; you can distribute 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: Thomas Graf <tgraf@suug.ch>
+ */
+
+#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 <errno.h>
+
+#include "m_ematch.h"
+#include <linux/tc_ematch/tc_em_nbyte.h>
+
+extern struct ematch_util nbyte_ematch_util;
+
+static void nbyte_print_usage(FILE *fd)
+{
+ fprintf(fd,
+ "Usage: nbyte(NEEDLE at OFFSET [layer LAYER])\n" \
+ "where: NEEDLE := { string | \"c-escape-sequence\" }\n" \
+ " OFFSET := int\n" \
+ " LAYER := { link | network | transport | 0..%d }\n" \
+ "\n" \
+ "Example: nbyte(\"ababa\" at 12 layer 1)\n",
+ TCF_LAYER_MAX);
+}
+
+static int nbyte_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
+ struct bstr *args)
+{
+ struct bstr *a;
+ struct bstr *needle = args;
+ unsigned long offset = 0, layer = TCF_LAYER_NETWORK;
+ int offset_present = 0;
+ struct tcf_em_nbyte nb;
+
+ memset(&nb, 0, sizeof(nb));
+
+#define PARSE_ERR(CARG, FMT, ARGS...) \
+ em_parse_error(EINVAL, args, CARG, &nbyte_ematch_util, FMT ,##ARGS)
+
+ if (args == NULL)
+ return PARSE_ERR(args, "nbyte: missing arguments");
+
+ if (needle->len <= 0)
+ return PARSE_ERR(args, "nbyte: needle length is 0");
+
+ for (a = bstr_next(args); a; a = bstr_next(a)) {
+ if (!bstrcmp(a, "at")) {
+ if (a->next == NULL)
+ return PARSE_ERR(a, "nbyte: missing argument");
+ a = bstr_next(a);
+
+ offset = bstrtoul(a);
+ if (offset == ULONG_MAX)
+ return PARSE_ERR(a, "nbyte: invalid offset, " \
+ "must be numeric");
+
+ offset_present = 1;
+ } else if (!bstrcmp(a, "layer")) {
+ if (a->next == NULL)
+ return PARSE_ERR(a, "nbyte: missing argument");
+ a = bstr_next(a);
+
+ layer = parse_layer(a);
+ if (layer == INT_MAX) {
+ layer = bstrtoul(a);
+ if (layer == ULONG_MAX)
+ return PARSE_ERR(a, "nbyte: invalid " \
+ "layer");
+ }
+
+ if (layer > TCF_LAYER_MAX)
+ return PARSE_ERR(a, "nbyte: illegal layer, " \
+ "must be in 0..%d", TCF_LAYER_MAX);
+ } else
+ return PARSE_ERR(a, "nbyte: unknown parameter");
+ }
+
+ if (offset_present == 0)
+ return PARSE_ERR(a, "nbyte: offset required");
+
+ nb.len = needle->len;
+ nb.layer = (__u8) layer;
+ nb.off = (__u16) offset;
+
+ addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
+ addraw_l(n, MAX_MSG, &nb, sizeof(nb));
+ addraw_l(n, MAX_MSG, needle->data, needle->len);
+
+#undef PARSE_ERR
+ return 0;
+}
+
+static int nbyte_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
+ int data_len)
+{
+ int i;
+ struct tcf_em_nbyte *nb = data;
+ __u8 *needle;
+
+ if (data_len < sizeof(*nb)) {
+ fprintf(stderr, "NByte header size mismatch\n");
+ return -1;
+ }
+
+ if (data_len < sizeof(*nb) + nb->len) {
+ fprintf(stderr, "NByte payload size mismatch\n");
+ return -1;
+ }
+
+ needle = data + sizeof(*nb);
+
+ for (i = 0; i < nb->len; i++)
+ fprintf(fd, "%02x ", needle[i]);
+
+ fprintf(fd, "\"");
+ for (i = 0; i < nb->len; i++)
+ fprintf(fd, "%c", isprint(needle[i]) ? needle[i] : '.');
+ fprintf(fd, "\" at %d layer %d", nb->off, nb->layer);
+
+ return 0;
+}
+
+struct ematch_util nbyte_ematch_util = {
+ .kind = "nbyte",
+ .kind_num = TCF_EM_NBYTE,
+ .parse_eopt = nbyte_parse_eopt,
+ .print_eopt = nbyte_print_eopt,
+ .print_usage = nbyte_print_usage
+};
diff --git a/tc/em_u32.c b/tc/em_u32.c
new file mode 100644
index 0000000..21ed70f
--- /dev/null
+++ b/tc/em_u32.c
@@ -0,0 +1,177 @@
+/*
+ * em_u32.c U32 Ematch
+ *
+ * This program is free software; you can distribute 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: Thomas Graf <tgraf@suug.ch>
+ */
+
+#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 <errno.h>
+
+#include "m_ematch.h"
+
+extern struct ematch_util u32_ematch_util;
+
+static void u32_print_usage(FILE *fd)
+{
+ fprintf(fd,
+ "Usage: u32(ALIGN VALUE MASK at [ nexthdr+ ] OFFSET)\n" \
+ "where: ALIGN := { u8 | u16 | u32 }\n" \
+ "\n" \
+ "Example: u32(u16 0x1122 0xffff at nexthdr+4)\n");
+}
+
+static int u32_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
+ struct bstr *args)
+{
+ struct bstr *a;
+ int align, nh_len;
+ unsigned long key, mask, offmask = 0, offset;
+ struct tc_u32_key u_key;
+
+ memset(&u_key, 0, sizeof(u_key));
+
+#define PARSE_ERR(CARG, FMT, ARGS...) \
+ em_parse_error(EINVAL, args, CARG, &u32_ematch_util, FMT ,##ARGS)
+
+ if (args == NULL)
+ return PARSE_ERR(args, "u32: missing arguments");
+
+ if (!bstrcmp(args, "u8"))
+ align = 1;
+ else if (!bstrcmp(args, "u16"))
+ align = 2;
+ else if (!bstrcmp(args, "u32"))
+ align = 4;
+ else
+ return PARSE_ERR(args, "u32: invalid alignment");
+
+ a = bstr_next(args);
+ if (a == NULL)
+ return PARSE_ERR(a, "u32: missing key");
+
+ key = bstrtoul(a);
+ if (key == ULONG_MAX)
+ return PARSE_ERR(a, "u32: invalid key, must be numeric");
+
+ a = bstr_next(a);
+ if (a == NULL)
+ return PARSE_ERR(a, "u32: missing mask");
+
+ mask = bstrtoul(a);
+ if (mask == ULONG_MAX)
+ return PARSE_ERR(a, "u32: invalid mask, must be numeric");
+
+ a = bstr_next(a);
+ if (a == NULL || bstrcmp(a, "at") != 0)
+ return PARSE_ERR(a, "u32: missing \"at\"");
+
+ a = bstr_next(a);
+ if (a == NULL)
+ return PARSE_ERR(a, "u32: missing offset");
+
+ nh_len = strlen("nexthdr+");
+ if (a->len > nh_len && !memcmp(a->data, "nexthdr+", nh_len)) {
+ char buf[a->len - nh_len + 1];
+ offmask = -1;
+ memcpy(buf, a->data + nh_len, a->len - nh_len);
+ offset = strtoul(buf, NULL, 0);
+ } else if (!bstrcmp(a, "nexthdr+")) {
+ a = bstr_next(a);
+ if (a == NULL)
+ return PARSE_ERR(a, "u32: missing offset");
+ offset = bstrtoul(a);
+ } else
+ offset = bstrtoul(a);
+
+ if (offset == ULONG_MAX)
+ return PARSE_ERR(a, "u32: invalid offset");
+
+ if (a->next)
+ return PARSE_ERR(a->next, "u32: unexpected trailer");
+
+ switch (align) {
+ case 1:
+ if (key > 0xFF)
+ return PARSE_ERR(a, "Illegal key (>0xFF)");
+ if (mask > 0xFF)
+ return PARSE_ERR(a, "Illegal mask (>0xFF)");
+
+ key <<= 24 - ((offset & 3) * 8);
+ mask <<= 24 - ((offset & 3) * 8);
+ offset &= ~3;
+ break;
+
+ case 2:
+ if (key > 0xFFFF)
+ return PARSE_ERR(a, "Illegal key (>0xFFFF)");
+ if (mask > 0xFFFF)
+ return PARSE_ERR(a, "Illegal mask (>0xFFFF)");
+
+ if ((offset & 3) == 0) {
+ key <<= 16;
+ mask <<= 16;
+ }
+ offset &= ~3;
+ break;
+ }
+
+ key = htonl(key);
+ mask = htonl(mask);
+
+ if (offset % 4)
+ return PARSE_ERR(a, "u32: invalid offset alignment, " \
+ "must be aligned to 4.");
+
+ key &= mask;
+
+ u_key.mask = mask;
+ u_key.val = key;
+ u_key.off = offset;
+ u_key.offmask = offmask;
+
+ addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
+ addraw_l(n, MAX_MSG, &u_key, sizeof(u_key));
+
+#undef PARSE_ERR
+ return 0;
+}
+
+static int u32_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
+ int data_len)
+{
+ struct tc_u32_key *u_key = data;
+
+ if (data_len < sizeof(*u_key)) {
+ fprintf(stderr, "U32 header size mismatch\n");
+ return -1;
+ }
+
+ fprintf(fd, "%08x/%08x at %s%d",
+ (unsigned int) ntohl(u_key->val),
+ (unsigned int) ntohl(u_key->mask),
+ u_key->offmask ? "nexthdr+" : "",
+ u_key->off);
+
+ return 0;
+}
+
+struct ematch_util u32_ematch_util = {
+ .kind = "u32",
+ .kind_num = TCF_EM_U32,
+ .parse_eopt = u32_parse_eopt,
+ .print_eopt = u32_print_eopt,
+ .print_usage = u32_print_usage
+};
diff --git a/tc/emp_ematch.l b/tc/emp_ematch.l
new file mode 100644
index 0000000..0184753
--- /dev/null
+++ b/tc/emp_ematch.l
@@ -0,0 +1,145 @@
+%{
+ #include "emp_ematch.yacc.h"
+ #include "m_ematch.h"
+
+ extern int ematch_argc;
+ extern char **ematch_argv;
+
+ #define yylval ematch_lval
+
+ #define NEXT_EM_ARG() do { ematch_argc--; ematch_argv++; } while(0);
+
+ #define YY_INPUT(buf, result, max_size) \
+ { \
+ next: \
+ if (ematch_argc <= 0) \
+ result = YY_NULL; \
+ else if (**ematch_argv == '\0') { \
+ NEXT_EM_ARG(); \
+ goto next; \
+ } else { \
+ if (max_size <= strlen(*ematch_argv) + 1) { \
+ fprintf(stderr, "match argument too long.\n"); \
+ result = YY_NULL; \
+ } else { \
+ strcpy(buf, *ematch_argv); \
+ result = strlen(*ematch_argv) + 1; \
+ buf[result-1] = ' '; \
+ buf[result] = '\0'; \
+ NEXT_EM_ARG(); \
+ } \
+ } \
+ }
+
+ static void __attribute__ ((unused)) yyunput (int c,char *buf_ptr );
+ static void __attribute__ ((unused)) yy_push_state (int new_state );
+ static void __attribute__ ((unused)) yy_pop_state (void);
+ static int __attribute__ ((unused)) yy_top_state (void );
+
+ static char *strbuf;
+ static unsigned int strbuf_size;
+ static unsigned int strbuf_index;
+
+ static void strbuf_enlarge(void)
+ {
+ strbuf_size += 512;
+ strbuf = realloc(strbuf, strbuf_size);
+ }
+
+ static void strbuf_append_char(char c)
+ {
+ while (strbuf_index >= strbuf_size)
+ strbuf_enlarge();
+ strbuf[strbuf_index++] = c;
+ }
+
+ static void strbuf_append_charp(char *s)
+ {
+ while (strbuf_index >= strbuf_size)
+ strbuf_enlarge();
+ memcpy(strbuf + strbuf_index, s, strlen(s));
+ strbuf_index += strlen(s);
+ }
+
+%}
+
+%x str
+
+%option 8bit stack warn noyywrap prefix="ematch_"
+%%
+[ \t\r\n]+
+
+\" {
+ if (strbuf == NULL) {
+ strbuf_size = 512;
+ strbuf = calloc(1, strbuf_size);
+ if (strbuf == NULL)
+ return ERROR;
+ }
+ strbuf_index = 0;
+
+ BEGIN(str);
+ }
+
+<str>\" {
+ BEGIN(INITIAL);
+ yylval.b = bstr_new(strbuf, strbuf_index);
+ yylval.b->quoted = 1;
+ return ATTRIBUTE;
+ }
+
+<str>\\[0-7]{1,3} { /* octal escape sequence */
+ int res;
+
+ sscanf(yytext + 1, "%o", &res);
+ if (res > 0xFF) {
+ fprintf(stderr, "error: octal escape sequence" \
+ " out of range\n");
+ return ERROR;
+ }
+ strbuf_append_char((unsigned char) res);
+ }
+
+<str>\\[0-9]+ { /* catch wrong octal escape seq. */
+ fprintf(stderr, "error: invalid octale escape sequence\n");
+ return ERROR;
+ }
+
+<str>\\x[0-9a-fA-F]{1,2} {
+ int res;
+
+ sscanf(yytext + 2, "%x", &res);
+
+ if (res > 0xFF) {
+ fprintf(stderr, "error: hexadecimal escape " \
+ "sequence out of range\n");
+ return ERROR;
+ }
+ strbuf_append_char((unsigned char) res);
+ }
+
+<str>\\n strbuf_append_char('\n');
+<str>\\r strbuf_append_char('\r');
+<str>\\t strbuf_append_char('\t');
+<str>\\v strbuf_append_char('\v');
+<str>\\b strbuf_append_char('\b');
+<str>\\f strbuf_append_char('\f');
+<str>\\a strbuf_append_char('\a');
+
+<str>\\(.|\n) strbuf_append_char(yytext[1]);
+<str>[^\\\n\"]+ strbuf_append_charp(yytext);
+
+[aA][nN][dD] return AND;
+[oO][rR] return OR;
+[nN][oO][tT] return NOT;
+"(" |
+")" {
+ return yylval.i = *yytext;
+ }
+[^ \t\r\n()]+ {
+ yylval.b = bstr_alloc(yytext);
+ if (yylval.b == NULL)
+ return ERROR;
+ return ATTRIBUTE;
+ }
+%%
diff --git a/tc/emp_ematch.y b/tc/emp_ematch.y
new file mode 100644
index 0000000..e8d1671
--- /dev/null
+++ b/tc/emp_ematch.y
@@ -0,0 +1,101 @@
+%{
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <malloc.h>
+ #include <string.h>
+ #include "m_ematch.h"
+%}
+
+%locations
+%token-table
+%error-verbose
+%name-prefix="ematch_"
+
+%union {
+ unsigned int i;
+ struct bstr *b;
+ struct ematch *e;
+}
+
+%{
+ extern int ematch_lex(void);
+ extern void yyerror(char *s);
+ extern struct ematch *ematch_root;
+ extern char *ematch_err;
+%}
+
+%token <i> ERROR
+%token <b> ATTRIBUTE
+%token <i> AND OR NOT
+%type <i> invert relation
+%type <e> match expr
+%type <b> args
+%right AND OR
+%start input
+%%
+input:
+ /* empty */
+ | expr
+ { ematch_root = $1; }
+ | expr error
+ {
+ ematch_root = $1;
+ YYACCEPT;
+ }
+ ;
+
+expr:
+ match
+ { $$ = $1; }
+ | match relation expr
+ {
+ $1->relation = $2;
+ $1->next = $3;
+ $$ = $1;
+ }
+ ;
+
+match:
+ invert ATTRIBUTE '(' args ')'
+ {
+ $2->next = $4;
+ $$ = new_ematch($2, $1);
+ if ($$ == NULL)
+ YYABORT;
+ }
+ | invert '(' expr ')'
+ {
+ $$ = new_ematch(NULL, $1);
+ if ($$ == NULL)
+ YYABORT;
+ $$->child = $3;
+ }
+ ;
+
+args:
+ ATTRIBUTE
+ { $$ = $1; }
+ | ATTRIBUTE args
+ { $1->next = $2; }
+ ;
+
+relation:
+ AND
+ { $$ = TCF_EM_REL_AND; }
+ | OR
+ { $$ = TCF_EM_REL_OR; }
+ ;
+
+invert:
+ /* empty */
+ { $$ = 0; }
+ | NOT
+ { $$ = 1; }
+ ;
+%%
+
+ void yyerror(char *s)
+ {
+ ematch_err = strdup(s);
+ }
+
diff --git a/tc/f_basic.c b/tc/f_basic.c
new file mode 100644
index 0000000..ad41633
--- /dev/null
+++ b/tc/f_basic.c
@@ -0,0 +1,147 @@
+/*
+ * f_basic.c Basic Classifier
+ *
+ * This program is free software; you can distribute 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: Thomas Graf <tgraf@suug.ch>
+ *
+ */
+
+#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 <linux/if.h>
+
+#include "utils.h"
+#include "tc_util.h"
+#include "m_ematch.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... basic [ match EMATCH_TREE ] [ police POLICE_SPEC ]\n");
+ fprintf(stderr, " [ action ACTION_SPEC ] [ classid CLASSID ]\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Where: SELECTOR := SAMPLE SAMPLE ...\n");
+ fprintf(stderr, " FILTERID := X:Y:Z\n");
+ fprintf(stderr, "\nNOTE: CLASSID is parsed as hexadecimal input.\n");
+}
+
+static int basic_parse_opt(struct filter_util *qu, char *handle,
+ int argc, char **argv, struct nlmsghdr *n)
+{
+ struct tcmsg *t = NLMSG_DATA(n);
+ struct rtattr *tail;
+ long h = 0;
+
+ if (argc == 0)
+ return 0;
+
+ if (handle) {
+ h = strtol(handle, NULL, 0);
+ if (h == LONG_MIN || h == LONG_MAX) {
+ fprintf(stderr, "Illegal handle \"%s\", must be numeric.\n",
+ handle);
+ return -1;
+ }
+ }
+
+ t->tcm_handle = h;
+
+ tail = (struct rtattr*)(((void*)n)+NLMSG_ALIGN(n->nlmsg_len));
+ addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0);
+
+ while (argc > 0) {
+ if (matches(*argv, "match") == 0) {
+ NEXT_ARG();
+ if (parse_ematch(&argc, &argv, TCA_BASIC_EMATCHES, n)) {
+ fprintf(stderr, "Illegal \"ematch\"\n");
+ return -1;
+ }
+ continue;
+ } else if (matches(*argv, "classid") == 0 ||
+ strcmp(*argv, "flowid") == 0) {
+ unsigned handle;
+ NEXT_ARG();
+ if (get_tc_classid(&handle, *argv)) {
+ fprintf(stderr, "Illegal \"classid\"\n");
+ return -1;
+ }
+ addattr_l(n, MAX_MSG, TCA_BASIC_CLASSID, &handle, 4);
+ } else if (matches(*argv, "action") == 0) {
+ NEXT_ARG();
+ if (parse_action(&argc, &argv, TCA_BASIC_ACT, n)) {
+ fprintf(stderr, "Illegal \"action\"\n");
+ return -1;
+ }
+ continue;
+
+ } else if (matches(*argv, "police") == 0) {
+ NEXT_ARG();
+ if (parse_police(&argc, &argv, TCA_BASIC_POLICE, n)) {
+ fprintf(stderr, "Illegal \"police\"\n");
+ return -1;
+ }
+ continue;
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ tail->rta_len = (((void*)n)+n->nlmsg_len) - (void*)tail;
+ return 0;
+}
+
+static int basic_print_opt(struct filter_util *qu, FILE *f,
+ struct rtattr *opt, __u32 handle)
+{
+ struct rtattr *tb[TCA_BASIC_MAX+1];
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_BASIC_MAX, opt);
+
+ if (handle)
+ fprintf(f, "handle 0x%x ", handle);
+
+ if (tb[TCA_BASIC_CLASSID]) {
+ SPRINT_BUF(b1);
+ fprintf(f, "flowid %s ",
+ sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_BASIC_CLASSID]), b1));
+ }
+
+ if (tb[TCA_BASIC_EMATCHES])
+ print_ematch(f, tb[TCA_BASIC_EMATCHES]);
+
+ if (tb[TCA_BASIC_POLICE]) {
+ fprintf(f, "\n");
+ tc_print_police(f, tb[TCA_BASIC_POLICE]);
+ }
+
+ if (tb[TCA_BASIC_ACT]) {
+ tc_print_action(f, tb[TCA_BASIC_ACT]);
+ }
+
+ return 0;
+}
+
+struct filter_util basic_filter_util = {
+ .id = "basic",
+ .parse_fopt = basic_parse_opt,
+ .print_fopt = basic_print_opt,
+};
diff --git a/tc/f_cgroup.c b/tc/f_cgroup.c
new file mode 100644
index 0000000..192e697
--- /dev/null
+++ b/tc/f_cgroup.c
@@ -0,0 +1,115 @@
+/*
+ * f_cgroup.c Control Group Classifier
+ *
+ * This program is free software; you can distribute 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: Thomas Graf <tgraf@infradead.org>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "utils.h"
+#include "tc_util.h"
+#include "m_ematch.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... cgroup [ match EMATCH_TREE ] [ police POLICE_SPEC ]\n");
+ fprintf(stderr, " [ action ACTION_SPEC ]\n");
+}
+
+static int cgroup_parse_opt(struct filter_util *qu, char *handle,
+ int argc, char **argv, struct nlmsghdr *n)
+{
+ struct tcmsg *t = NLMSG_DATA(n);
+ struct rtattr *tail;
+ long h = 0;
+
+ if (handle) {
+ h = strtol(handle, NULL, 0);
+ if (h == LONG_MIN || h == LONG_MAX) {
+ fprintf(stderr, "Illegal handle \"%s\", must be numeric.\n",
+ handle);
+ return -1;
+ }
+ }
+
+ t->tcm_handle = h;
+
+ tail = (struct rtattr*)(((void*)n)+NLMSG_ALIGN(n->nlmsg_len));
+ addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0);
+
+ while (argc > 0) {
+ if (matches(*argv, "match") == 0) {
+ NEXT_ARG();
+ if (parse_ematch(&argc, &argv, TCA_CGROUP_EMATCHES, n)) {
+ fprintf(stderr, "Illegal \"ematch\"\n");
+ return -1;
+ }
+ continue;
+ } else if (matches(*argv, "action") == 0) {
+ NEXT_ARG();
+ if (parse_action(&argc, &argv, TCA_CGROUP_ACT, n)) {
+ fprintf(stderr, "Illegal \"action\"\n");
+ return -1;
+ }
+ continue;
+
+ } else if (matches(*argv, "police") == 0) {
+ NEXT_ARG();
+ if (parse_police(&argc, &argv, TCA_CGROUP_POLICE, n)) {
+ fprintf(stderr, "Illegal \"police\"\n");
+ return -1;
+ }
+ continue;
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ tail->rta_len = (((void*)n)+n->nlmsg_len) - (void*)tail;
+ return 0;
+}
+
+static int cgroup_print_opt(struct filter_util *qu, FILE *f,
+ struct rtattr *opt, __u32 handle)
+{
+ struct rtattr *tb[TCA_CGROUP_MAX+1];
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_CGROUP_MAX, opt);
+
+ if (handle)
+ fprintf(f, "handle 0x%x ", handle);
+
+ if (tb[TCA_CGROUP_EMATCHES])
+ print_ematch(f, tb[TCA_CGROUP_EMATCHES]);
+
+ if (tb[TCA_CGROUP_POLICE]) {
+ fprintf(f, "\n");
+ tc_print_police(f, tb[TCA_CGROUP_POLICE]);
+ }
+
+ if (tb[TCA_CGROUP_ACT])
+ tc_print_action(f, tb[TCA_CGROUP_ACT]);
+
+ return 0;
+}
+
+struct filter_util cgroup_filter_util = {
+ .id = "cgroup",
+ .parse_fopt = cgroup_parse_opt,
+ .print_fopt = cgroup_print_opt,
+};
diff --git a/tc/f_flow.c b/tc/f_flow.c
new file mode 100644
index 0000000..84b45c9
--- /dev/null
+++ b/tc/f_flow.c
@@ -0,0 +1,360 @@
+/*
+ * f_flow.c Flow filter
+ *
+ * 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: Patrick McHardy <kaber@trash.net>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "utils.h"
+#include "tc_util.h"
+#include "m_ematch.h"
+
+static void explain(void)
+{
+ fprintf(stderr,
+"Usage: ... flow ...\n"
+"\n"
+" [mapping mode]: map key KEY [ OPS ] ...\n"
+" [hashing mode]: hash keys KEY-LIST ... [ perturb SECS ]\n"
+"\n"
+" [ divisor NUM ] [ baseclass ID ] [ match EMATCH_TREE ]\n"
+" [ police POLICE_SPEC ] [ action ACTION_SPEC ]\n"
+"\n"
+"KEY-LIST := [ KEY-LIST , ] KEY\n"
+"KEY := [ src | dst | proto | proto-src | proto-dst | iif | priority | \n"
+" mark | nfct | nfct-src | nfct-dst | nfct-proto-src | \n"
+" nfct-proto-dst | rt-classid | sk-uid | sk-gid |\n"
+" vlan-tag ]\n"
+"OPS := [ or NUM | and NUM | xor NUM | rshift NUM | addend NUM ]\n"
+"ID := X:Y\n"
+ );
+}
+
+static const char *flow_keys[FLOW_KEY_MAX+1] = {
+ [FLOW_KEY_SRC] = "src",
+ [FLOW_KEY_DST] = "dst",
+ [FLOW_KEY_PROTO] = "proto",
+ [FLOW_KEY_PROTO_SRC] = "proto-src",
+ [FLOW_KEY_PROTO_DST] = "proto-dst",
+ [FLOW_KEY_IIF] = "iif",
+ [FLOW_KEY_PRIORITY] = "priority",
+ [FLOW_KEY_MARK] = "mark",
+ [FLOW_KEY_NFCT] = "nfct",
+ [FLOW_KEY_NFCT_SRC] = "nfct-src",
+ [FLOW_KEY_NFCT_DST] = "nfct-dst",
+ [FLOW_KEY_NFCT_PROTO_SRC] = "nfct-proto-src",
+ [FLOW_KEY_NFCT_PROTO_DST] = "nfct-proto-dst",
+ [FLOW_KEY_RTCLASSID] = "rt-classid",
+ [FLOW_KEY_SKUID] = "sk-uid",
+ [FLOW_KEY_SKGID] = "sk-gid",
+ [FLOW_KEY_VLAN_TAG] = "vlan-tag",
+};
+
+static int flow_parse_keys(__u32 *keys, __u32 *nkeys, char *argv)
+{
+ char *s, *sep;
+ unsigned int i;
+
+ *keys = 0;
+ *nkeys = 0;
+ s = argv;
+ while (s != NULL) {
+ sep = strchr(s, ',');
+ if (sep)
+ *sep = '\0';
+
+ for (i = 0; i <= FLOW_KEY_MAX; i++) {
+ if (matches(s, flow_keys[i]) == 0) {
+ *keys |= 1 << i;
+ (*nkeys)++;
+ break;
+ }
+ }
+ if (i > FLOW_KEY_MAX) {
+ fprintf(stderr, "Unknown flow key \"%s\"\n", s);
+ return -1;
+ }
+ s = sep ? sep + 1 : NULL;
+ }
+ return 0;
+}
+
+static void transfer_bitop(__u32 *mask, __u32 *xor, __u32 m, __u32 x)
+{
+ *xor = x ^ (*xor & m);
+ *mask &= m;
+}
+
+static int get_addend(__u32 *addend, char *argv, __u32 keys)
+{
+ inet_prefix addr;
+ int sign = 0;
+ __u32 tmp;
+
+ if (*argv == '-') {
+ sign = 1;
+ argv++;
+ }
+
+ if (get_u32(&tmp, argv, 0) == 0)
+ goto out;
+
+ if (keys & (FLOW_KEY_SRC | FLOW_KEY_DST |
+ FLOW_KEY_NFCT_SRC | FLOW_KEY_NFCT_DST) &&
+ get_addr(&addr, argv, AF_UNSPEC) == 0) {
+ switch (addr.family) {
+ case AF_INET:
+ tmp = ntohl(addr.data[0]);
+ goto out;
+ case AF_INET6:
+ tmp = ntohl(addr.data[3]);
+ goto out;
+ }
+ }
+
+ return -1;
+out:
+ if (sign)
+ tmp = -tmp;
+ *addend = tmp;
+ return 0;
+}
+
+static int flow_parse_opt(struct filter_util *fu, char *handle,
+ int argc, char **argv, struct nlmsghdr *n)
+{
+ struct tc_police tp;
+ struct tcmsg *t = NLMSG_DATA(n);
+ struct rtattr *tail;
+ __u32 mask = ~0U, xor = 0;
+ __u32 keys = 0, nkeys = 0;
+ __u32 mode = FLOW_MODE_MAP;
+ __u32 tmp;
+
+ memset(&tp, 0, sizeof(tp));
+
+ if (handle) {
+ if (get_u32(&t->tcm_handle, handle, 0)) {
+ fprintf(stderr, "Illegal \"handle\"\n");
+ return -1;
+ }
+ }
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 4096, TCA_OPTIONS, NULL, 0);
+
+ while (argc > 0) {
+ if (matches(*argv, "map") == 0) {
+ mode = FLOW_MODE_MAP;
+ } else if (matches(*argv, "hash") == 0) {
+ mode = FLOW_MODE_HASH;
+ } else if (matches(*argv, "keys") == 0) {
+ NEXT_ARG();
+ if (flow_parse_keys(&keys, &nkeys, *argv))
+ return -1;
+ addattr32(n, 4096, TCA_FLOW_KEYS, keys);
+ } else if (matches(*argv, "and") == 0) {
+ NEXT_ARG();
+ if (get_u32(&tmp, *argv, 0)) {
+ fprintf(stderr, "Illegal \"mask\"\n");
+ return -1;
+ }
+ transfer_bitop(&mask, &xor, tmp, 0);
+ } else if (matches(*argv, "or") == 0) {
+ NEXT_ARG();
+ if (get_u32(&tmp, *argv, 0)) {
+ fprintf(stderr, "Illegal \"or\"\n");
+ return -1;
+ }
+ transfer_bitop(&mask, &xor, ~tmp, tmp);
+ } else if (matches(*argv, "xor") == 0) {
+ NEXT_ARG();
+ if (get_u32(&tmp, *argv, 0)) {
+ fprintf(stderr, "Illegal \"xor\"\n");
+ return -1;
+ }
+ transfer_bitop(&mask, &xor, ~0, tmp);
+ } else if (matches(*argv, "rshift") == 0) {
+ NEXT_ARG();
+ if (get_u32(&tmp, *argv, 0)) {
+ fprintf(stderr, "Illegal \"rshift\"\n");
+ return -1;
+ }
+ addattr32(n, 4096, TCA_FLOW_RSHIFT, tmp);
+ } else if (matches(*argv, "addend") == 0) {
+ NEXT_ARG();
+ if (get_addend(&tmp, *argv, keys)) {
+ fprintf(stderr, "Illegal \"addend\"\n");
+ return -1;
+ }
+ addattr32(n, 4096, TCA_FLOW_ADDEND, tmp);
+ } else if (matches(*argv, "divisor") == 0) {
+ NEXT_ARG();
+ if (get_u32(&tmp, *argv, 0)) {
+ fprintf(stderr, "Illegal \"divisor\"\n");
+ return -1;
+ }
+ addattr32(n, 4096, TCA_FLOW_DIVISOR, tmp);
+ } else if (matches(*argv, "baseclass") == 0) {
+ NEXT_ARG();
+ if (get_tc_classid(&tmp, *argv) || TC_H_MIN(tmp) == 0) {
+ fprintf(stderr, "Illegal \"baseclass\"\n");
+ return -1;
+ }
+ addattr32(n, 4096, TCA_FLOW_BASECLASS, tmp);
+ } else if (matches(*argv, "perturb") == 0) {
+ NEXT_ARG();
+ if (get_u32(&tmp, *argv, 0)) {
+ fprintf(stderr, "Illegal \"perturb\"\n");
+ return -1;
+ }
+ addattr32(n, 4096, TCA_FLOW_PERTURB, tmp);
+ } else if (matches(*argv, "police") == 0) {
+ NEXT_ARG();
+ if (parse_police(&argc, &argv, TCA_FLOW_POLICE, n)) {
+ fprintf(stderr, "Illegal \"police\"\n");
+ return -1;
+ }
+ continue;
+ } else if (matches(*argv, "action") == 0) {
+ NEXT_ARG();
+ if (parse_action(&argc, &argv, TCA_FLOW_ACT, n)) {
+ fprintf(stderr, "Illegal \"action\"\n");
+ return -1;
+ }
+ continue;
+ } else if (matches(*argv, "match") == 0) {
+ NEXT_ARG();
+ if (parse_ematch(&argc, &argv, TCA_FLOW_EMATCHES, n)) {
+ fprintf(stderr, "Illegal \"ematch\"\n");
+ return -1;
+ }
+ continue;
+ } else if (matches(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argv++, argc--;
+ }
+
+ if (nkeys > 1 && mode != FLOW_MODE_HASH) {
+ fprintf(stderr, "Invalid mode \"map\" for multiple keys\n");
+ return -1;
+ }
+ addattr32(n, 4096, TCA_FLOW_MODE, mode);
+
+ if (mask != ~0 || xor != 0) {
+ addattr32(n, 4096, TCA_FLOW_MASK, mask);
+ addattr32(n, 4096, TCA_FLOW_XOR, xor);
+ }
+
+ tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail;
+ return 0;
+}
+
+static int flow_print_opt(struct filter_util *fu, FILE *f, struct rtattr *opt,
+ __u32 handle)
+{
+ struct rtattr *tb[TCA_FLOW_MAX+1];
+ SPRINT_BUF(b1);
+ unsigned int i;
+ __u32 mask = ~0, val = 0;
+
+ if (opt == NULL)
+ return -EINVAL;
+
+ parse_rtattr_nested(tb, TCA_FLOW_MAX, opt);
+
+ fprintf(f, "handle 0x%x ", handle);
+
+ if (tb[TCA_FLOW_MODE]) {
+ __u32 mode = *(__u32 *)RTA_DATA(tb[TCA_FLOW_MODE]);
+
+ switch (mode) {
+ case FLOW_MODE_MAP:
+ fprintf(f, "map ");
+ break;
+ case FLOW_MODE_HASH:
+ fprintf(f, "hash ");
+ break;
+ }
+ }
+
+ if (tb[TCA_FLOW_KEYS]) {
+ __u32 keymask = *(__u32 *)RTA_DATA(tb[TCA_FLOW_KEYS]);
+ char *sep = "";
+
+ fprintf(f, "keys ");
+ for (i = 0; i <= FLOW_KEY_MAX; i++) {
+ if (keymask & (1 << i)) {
+ fprintf(f, "%s%s", sep, flow_keys[i]);
+ sep = ",";
+ }
+ }
+ fprintf(f, " ");
+ }
+
+ if (tb[TCA_FLOW_MASK])
+ mask = *(__u32 *)RTA_DATA(tb[TCA_FLOW_MASK]);
+ if (tb[TCA_FLOW_XOR])
+ val = *(__u32 *)RTA_DATA(tb[TCA_FLOW_XOR]);
+
+ if (mask != ~0 || val != 0) {
+ __u32 or = (mask & val) ^ val;
+ __u32 xor = mask & val;
+
+ if (mask != ~0)
+ fprintf(f, "and 0x%.8x ", mask);
+ if (xor != 0)
+ fprintf(f, "xor 0x%.8x ", xor);
+ if (or != 0)
+ fprintf(f, "or 0x%.8x ", or);
+ }
+
+ if (tb[TCA_FLOW_RSHIFT])
+ fprintf(f, "rshift %u ",
+ *(__u32 *)RTA_DATA(tb[TCA_FLOW_RSHIFT]));
+ if (tb[TCA_FLOW_ADDEND])
+ fprintf(f, "addend 0x%x ",
+ *(__u32 *)RTA_DATA(tb[TCA_FLOW_ADDEND]));
+
+ if (tb[TCA_FLOW_DIVISOR])
+ fprintf(f, "divisor %u ",
+ *(__u32 *)RTA_DATA(tb[TCA_FLOW_DIVISOR]));
+ if (tb[TCA_FLOW_BASECLASS])
+ fprintf(f, "baseclass %s ",
+ sprint_tc_classid(*(__u32 *)RTA_DATA(tb[TCA_FLOW_BASECLASS]), b1));
+
+ if (tb[TCA_FLOW_PERTURB])
+ fprintf(f, "perturb %usec ",
+ *(__u32 *)RTA_DATA(tb[TCA_FLOW_PERTURB]));
+
+ if (tb[TCA_FLOW_EMATCHES])
+ print_ematch(f, tb[TCA_FLOW_EMATCHES]);
+ if (tb[TCA_FLOW_POLICE])
+ tc_print_police(f, tb[TCA_FLOW_POLICE]);
+ if (tb[TCA_FLOW_ACT]) {
+ fprintf(f, "\n");
+ tc_print_action(f, tb[TCA_FLOW_ACT]);
+ }
+ return 0;
+}
+
+struct filter_util flow_filter_util = {
+ .id = "flow",
+ .parse_fopt = flow_parse_opt,
+ .print_fopt = flow_print_opt,
+};
diff --git a/tc/f_fw.c b/tc/f_fw.c
new file mode 100644
index 0000000..cc8ea2d
--- /dev/null
+++ b/tc/f_fw.c
@@ -0,0 +1,164 @@
+/*
+ * f_fw.c FW filter.
+ *
+ * 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 <linux/if.h> /* IFNAMSIZ */
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... fw [ classid CLASSID ] [ police POLICE_SPEC ]\n");
+ fprintf(stderr, " POLICE_SPEC := ... look at TBF\n");
+ fprintf(stderr, " CLASSID := X:Y\n");
+ fprintf(stderr, "\nNOTE: CLASSID is parsed as hexadecimal input.\n");
+}
+
+#define usage() return(-1)
+
+static int fw_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n)
+{
+ struct tc_police tp;
+ struct tcmsg *t = NLMSG_DATA(n);
+ struct rtattr *tail;
+ __u32 mask = 0;
+ int mask_set = 0;
+
+ memset(&tp, 0, sizeof(tp));
+
+ if (handle) {
+ char *slash;
+ if ((slash = strchr(handle, '/')) != NULL)
+ *slash = '\0';
+ if (get_u32(&t->tcm_handle, handle, 0)) {
+ fprintf(stderr, "Illegal \"handle\"\n");
+ return -1;
+ }
+ if (slash) {
+ if (get_u32(&mask, slash+1, 0)) {
+ fprintf(stderr, "Illegal \"handle\" mask\n");
+ return -1;
+ }
+ mask_set = 1;
+ }
+ }
+
+ if (argc == 0)
+ return 0;
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 4096, TCA_OPTIONS, NULL, 0);
+
+ if (mask_set)
+ addattr32(n, MAX_MSG, TCA_FW_MASK, mask);
+
+ while (argc > 0) {
+ if (matches(*argv, "classid") == 0 ||
+ matches(*argv, "flowid") == 0) {
+ unsigned handle;
+ NEXT_ARG();
+ if (get_tc_classid(&handle, *argv)) {
+ fprintf(stderr, "Illegal \"classid\"\n");
+ return -1;
+ }
+ addattr_l(n, 4096, TCA_FW_CLASSID, &handle, 4);
+ } else if (matches(*argv, "police") == 0) {
+ NEXT_ARG();
+ if (parse_police(&argc, &argv, TCA_FW_POLICE, n)) {
+ fprintf(stderr, "Illegal \"police\"\n");
+ return -1;
+ }
+ continue;
+ } else if (matches(*argv, "action") == 0) {
+ NEXT_ARG();
+ if (parse_action(&argc, &argv, TCA_FW_ACT, n)) {
+ fprintf(stderr, "Illegal fw \"action\"\n");
+ return -1;
+ }
+ continue;
+ } else if (strcmp(*argv, "indev") == 0) {
+ char d[IFNAMSIZ+1];
+ memset(d, 0, sizeof (d));
+ argc--;
+ argv++;
+ if (argc < 1) {
+ fprintf(stderr, "Illegal indev\n");
+ return -1;
+ }
+ strncpy(d, *argv, sizeof (d) - 1);
+ addattr_l(n, MAX_MSG, TCA_FW_INDEV, d, strlen(d) + 1);
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static int fw_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle)
+{
+ struct rtattr *tb[TCA_FW_MAX+1];
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_FW_MAX, opt);
+
+ if (handle || tb[TCA_FW_MASK]) {
+ __u32 mark = 0, mask = 0;
+ if(handle)
+ mark = handle;
+ if(tb[TCA_FW_MASK] &&
+ (mask = *(__u32*)RTA_DATA(tb[TCA_FW_MASK])) != 0xFFFFFFFF)
+ fprintf(f, "handle 0x%x/0x%x ", mark, mask);
+ else
+ fprintf(f, "handle 0x%x ", handle);
+ }
+
+ if (tb[TCA_FW_CLASSID]) {
+ SPRINT_BUF(b1);
+ fprintf(f, "classid %s ", sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_FW_CLASSID]), b1));
+ }
+
+ if (tb[TCA_FW_POLICE])
+ tc_print_police(f, tb[TCA_FW_POLICE]);
+ if (tb[TCA_FW_INDEV]) {
+ struct rtattr *idev = tb[TCA_FW_INDEV];
+ fprintf(f, "input dev %s ",(char *)RTA_DATA(idev));
+ }
+
+ if (tb[TCA_FW_ACT]) {
+ fprintf(f, "\n");
+ tc_print_action(f, tb[TCA_FW_ACT]);
+ }
+ return 0;
+}
+
+struct filter_util fw_filter_util = {
+ .id = "fw",
+ .parse_fopt = fw_parse_opt,
+ .print_fopt = fw_print_opt,
+};
diff --git a/tc/f_route.c b/tc/f_route.c
new file mode 100644
index 0000000..67dd49c
--- /dev/null
+++ b/tc/f_route.c
@@ -0,0 +1,170 @@
+/*
+ * f_route.c ROUTE filter.
+ *
+ * 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 "utils.h"
+#include "rt_names.h"
+#include "tc_common.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... route [ from REALM | fromif TAG ] [ to REALM ]\n");
+ fprintf(stderr, " [ flowid CLASSID ] [ police POLICE_SPEC ]\n");
+ fprintf(stderr, " POLICE_SPEC := ... look at TBF\n");
+ fprintf(stderr, " CLASSID := X:Y\n");
+ fprintf(stderr, "\nNOTE: CLASSID is parsed as hexadecimal input.\n");
+}
+
+#define usage() return(-1)
+
+static int route_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n)
+{
+ struct tc_police tp;
+ struct tcmsg *t = NLMSG_DATA(n);
+ struct rtattr *tail;
+ __u32 fh = 0xFFFF8000;
+ __u32 order = 0;
+
+ memset(&tp, 0, sizeof(tp));
+
+ if (handle) {
+ if (get_u32(&t->tcm_handle, handle, 0)) {
+ fprintf(stderr, "Illegal \"handle\"\n");
+ return -1;
+ }
+ }
+
+ if (argc == 0)
+ return 0;
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 4096, TCA_OPTIONS, NULL, 0);
+
+ while (argc > 0) {
+ if (matches(*argv, "to") == 0) {
+ __u32 id;
+ NEXT_ARG();
+ if (rtnl_rtrealm_a2n(&id, *argv)) {
+ fprintf(stderr, "Illegal \"to\"\n");
+ return -1;
+ }
+ addattr_l(n, 4096, TCA_ROUTE4_TO, &id, 4);
+ fh &= ~0x80FF;
+ fh |= id&0xFF;
+ } else if (matches(*argv, "from") == 0) {
+ __u32 id;
+ NEXT_ARG();
+ if (rtnl_rtrealm_a2n(&id, *argv)) {
+ fprintf(stderr, "Illegal \"from\"\n");
+ return -1;
+ }
+ addattr_l(n, 4096, TCA_ROUTE4_FROM, &id, 4);
+ fh &= 0xFFFF;
+ fh |= id<<16;
+ } else if (matches(*argv, "fromif") == 0) {
+ __u32 id;
+ NEXT_ARG();
+ ll_init_map(&rth);
+ if ((id=ll_name_to_index(*argv)) <= 0) {
+ fprintf(stderr, "Illegal \"fromif\"\n");
+ return -1;
+ }
+ addattr_l(n, 4096, TCA_ROUTE4_IIF, &id, 4);
+ fh &= 0xFFFF;
+ fh |= (0x8000|id)<<16;
+ } else if (matches(*argv, "classid") == 0 ||
+ strcmp(*argv, "flowid") == 0) {
+ unsigned handle;
+ NEXT_ARG();
+ if (get_tc_classid(&handle, *argv)) {
+ fprintf(stderr, "Illegal \"classid\"\n");
+ return -1;
+ }
+ addattr_l(n, 4096, TCA_ROUTE4_CLASSID, &handle, 4);
+ } else if (matches(*argv, "police") == 0) {
+ NEXT_ARG();
+ if (parse_police(&argc, &argv, TCA_ROUTE4_POLICE, n)) {
+ fprintf(stderr, "Illegal \"police\"\n");
+ return -1;
+ }
+ continue;
+ } else if (matches(*argv, "order") == 0) {
+ NEXT_ARG();
+ if (get_u32(&order, *argv, 0)) {
+ fprintf(stderr, "Illegal \"order\"\n");
+ return -1;
+ }
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ if (order) {
+ fh &= ~0x7F00;
+ fh |= (order<<8)&0x7F00;
+ }
+ if (!t->tcm_handle)
+ t->tcm_handle = fh;
+ return 0;
+}
+
+static int route_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle)
+{
+ struct rtattr *tb[TCA_ROUTE4_MAX+1];
+ SPRINT_BUF(b1);
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_ROUTE4_MAX, opt);
+
+ if (handle)
+ fprintf(f, "fh 0x%08x ", handle);
+ if (handle&0x7F00)
+ fprintf(f, "order %d ", (handle>>8)&0x7F);
+
+ if (tb[TCA_ROUTE4_CLASSID]) {
+ SPRINT_BUF(b1);
+ fprintf(f, "flowid %s ", sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_ROUTE4_CLASSID]), b1));
+ }
+ if (tb[TCA_ROUTE4_TO])
+ fprintf(f, "to %s ", rtnl_rtrealm_n2a(*(__u32*)RTA_DATA(tb[TCA_ROUTE4_TO]), b1, sizeof(b1)));
+ if (tb[TCA_ROUTE4_FROM])
+ fprintf(f, "from %s ", rtnl_rtrealm_n2a(*(__u32*)RTA_DATA(tb[TCA_ROUTE4_FROM]), b1, sizeof(b1)));
+ if (tb[TCA_ROUTE4_IIF])
+ fprintf(f, "fromif %s", ll_index_to_name(*(int*)RTA_DATA(tb[TCA_ROUTE4_IIF])));
+ if (tb[TCA_ROUTE4_POLICE])
+ tc_print_police(f, tb[TCA_ROUTE4_POLICE]);
+ return 0;
+}
+
+struct filter_util route_filter_util = {
+ .id = "route",
+ .parse_fopt = route_parse_opt,
+ .print_fopt = route_print_opt,
+};
diff --git a/tc/f_rsvp.c b/tc/f_rsvp.c
new file mode 100644
index 0000000..7e1e6d9
--- /dev/null
+++ b/tc/f_rsvp.c
@@ -0,0 +1,405 @@
+/*
+ * q_rsvp.c RSVP filter.
+ *
+ * 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 "rt_names.h"
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... rsvp ipproto PROTOCOL session DST[/PORT | GPI ]\n");
+ fprintf(stderr, " [ sender SRC[/PORT | GPI ]\n");
+ fprintf(stderr, " [ classid CLASSID ] [ police POLICE_SPEC ]\n");
+ fprintf(stderr, " [ tunnelid ID ] [ tunnel ID skip NUMBER ]\n");
+ fprintf(stderr, "Where: GPI := { flowlabel NUMBER | spi/ah SPI | spi/esp SPI |\n");
+ fprintf(stderr, " u{8|16|32} NUMBER mask MASK at OFFSET}\n");
+ fprintf(stderr, " POLICE_SPEC := ... look at TBF\n");
+ fprintf(stderr, " FILTERID := X:Y\n");
+ fprintf(stderr, "\nNOTE: CLASSID is parsed as hexadecimal input.\n");
+}
+
+#define usage() return(-1)
+
+int get_addr_and_pi(int *argc_p, char ***argv_p, inet_prefix * addr,
+ struct tc_rsvp_pinfo *pinfo, int dir, int family)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ char *p = strchr(*argv, '/');
+ struct tc_rsvp_gpi *pi = dir ? &pinfo->dpi : &pinfo->spi;
+
+ if (p) {
+ __u16 tmp;
+
+ if (get_u16(&tmp, p+1, 0))
+ return -1;
+
+ if (dir == 0) {
+ /* Source port: u16 at offset 0 */
+ pi->key = htonl(((__u32)tmp)<<16);
+ pi->mask = htonl(0xFFFF0000);
+ } else {
+ /* Destination port: u16 at offset 2 */
+ pi->key = htonl(((__u32)tmp));
+ pi->mask = htonl(0x0000FFFF);
+ }
+ pi->offset = 0;
+ *p = 0;
+ }
+ if (get_addr_1(addr, *argv, family))
+ return -1;
+ if (p)
+ *p = '/';
+
+ argc--; argv++;
+
+ if (pi->mask || argc <= 0)
+ goto done;
+
+ if (strcmp(*argv, "spi/ah") == 0 ||
+ strcmp(*argv, "gpi/ah") == 0) {
+ __u32 gpi;
+ NEXT_ARG();
+ if (get_u32(&gpi, *argv, 0))
+ return -1;
+ pi->mask = htonl(0xFFFFFFFF);
+ pi->key = htonl(gpi);
+ pi->offset = 4;
+ if (pinfo->protocol == 0)
+ pinfo->protocol = IPPROTO_AH;
+ argc--; argv++;
+ } else if (strcmp(*argv, "spi/esp") == 0 ||
+ strcmp(*argv, "gpi/esp") == 0) {
+ __u32 gpi;
+ NEXT_ARG();
+ if (get_u32(&gpi, *argv, 0))
+ return -1;
+ pi->mask = htonl(0xFFFFFFFF);
+ pi->key = htonl(gpi);
+ pi->offset = 0;
+ if (pinfo->protocol == 0)
+ pinfo->protocol = IPPROTO_ESP;
+ argc--; argv++;
+ } else if (strcmp(*argv, "flowlabel") == 0) {
+ __u32 flabel;
+ NEXT_ARG();
+ if (get_u32(&flabel, *argv, 0))
+ return -1;
+ if (family != AF_INET6)
+ return -1;
+ pi->mask = htonl(0x000FFFFF);
+ pi->key = htonl(flabel) & pi->mask;
+ pi->offset = -40;
+ argc--; argv++;
+ } else if (strcmp(*argv, "u32") == 0 ||
+ strcmp(*argv, "u16") == 0 ||
+ strcmp(*argv, "u8") == 0) {
+ int sz = 1;
+ __u32 tmp;
+ __u32 mask = 0xff;
+ if (strcmp(*argv, "u32") == 0) {
+ sz = 4;
+ mask = 0xffff;
+ } else if (strcmp(*argv, "u16") == 0) {
+ mask = 0xffffffff;
+ sz = 2;
+ }
+ NEXT_ARG();
+ if (get_u32(&tmp, *argv, 0))
+ return -1;
+ argc--; argv++;
+ if (strcmp(*argv, "mask") == 0) {
+ NEXT_ARG();
+ if (get_u32(&mask, *argv, 16))
+ return -1;
+ argc--; argv++;
+ }
+ if (strcmp(*argv, "at") == 0) {
+ NEXT_ARG();
+ if (get_integer(&pi->offset, *argv, 0))
+ return -1;
+ argc--; argv++;
+ }
+ if (sz == 1) {
+ if ((pi->offset & 3) == 0) {
+ mask <<= 24;
+ tmp <<= 24;
+ } else if ((pi->offset & 3) == 1) {
+ mask <<= 16;
+ tmp <<= 16;
+ } else if ((pi->offset & 3) == 3) {
+ mask <<= 8;
+ tmp <<= 8;
+ }
+ } else if (sz == 2) {
+ if ((pi->offset & 3) == 0) {
+ mask <<= 16;
+ tmp <<= 16;
+ }
+ }
+ pi->offset &= ~3;
+ pi->mask = htonl(mask);
+ pi->key = htonl(tmp) & pi->mask;
+ }
+
+done:
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+}
+
+
+static int rsvp_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n)
+{
+ int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6;
+ struct tc_rsvp_pinfo pinfo;
+ struct tc_police tp;
+ struct tcmsg *t = NLMSG_DATA(n);
+ int pinfo_ok = 0;
+ struct rtattr *tail;
+
+ memset(&pinfo, 0, sizeof(pinfo));
+ memset(&tp, 0, sizeof(tp));
+
+ if (handle) {
+ if (get_u32(&t->tcm_handle, handle, 0)) {
+ fprintf(stderr, "Illegal \"handle\"\n");
+ return -1;
+ }
+ }
+
+ if (argc == 0)
+ return 0;
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 4096, TCA_OPTIONS, NULL, 0);
+
+ while (argc > 0) {
+ if (matches(*argv, "session") == 0) {
+ inet_prefix addr;
+ NEXT_ARG();
+ if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 1, family)) {
+ fprintf(stderr, "Illegal \"session\"\n");
+ return -1;
+ }
+ addattr_l(n, 4096, TCA_RSVP_DST, &addr.data, addr.bytelen);
+ if (pinfo.dpi.mask || pinfo.protocol)
+ pinfo_ok++;
+ continue;
+ } else if (matches(*argv, "sender") == 0 ||
+ matches(*argv, "flowspec") == 0) {
+ inet_prefix addr;
+ NEXT_ARG();
+ if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 0, family)) {
+ fprintf(stderr, "Illegal \"sender\"\n");
+ return -1;
+ }
+ addattr_l(n, 4096, TCA_RSVP_SRC, &addr.data, addr.bytelen);
+ if (pinfo.spi.mask || pinfo.protocol)
+ pinfo_ok++;
+ continue;
+ } else if (matches("ipproto", *argv) == 0) {
+ int num;
+ NEXT_ARG();
+ num = inet_proto_a2n(*argv);
+ if (num < 0) {
+ fprintf(stderr, "Illegal \"ipproto\"\n");
+ return -1;
+ }
+ pinfo.protocol = num;
+ pinfo_ok++;
+ } else if (matches(*argv, "classid") == 0 ||
+ strcmp(*argv, "flowid") == 0) {
+ unsigned handle;
+ NEXT_ARG();
+ if (get_tc_classid(&handle, *argv)) {
+ fprintf(stderr, "Illegal \"classid\"\n");
+ return -1;
+ }
+ addattr_l(n, 4096, TCA_RSVP_CLASSID, &handle, 4);
+ } else if (strcmp(*argv, "tunnelid") == 0) {
+ unsigned tid;
+ NEXT_ARG();
+ if (get_unsigned(&tid, *argv, 0)) {
+ fprintf(stderr, "Illegal \"tunnelid\"\n");
+ return -1;
+ }
+ pinfo.tunnelid = tid;
+ pinfo_ok++;
+ } else if (strcmp(*argv, "tunnel") == 0) {
+ unsigned tid;
+ NEXT_ARG();
+ if (get_unsigned(&tid, *argv, 0)) {
+ fprintf(stderr, "Illegal \"tunnel\"\n");
+ return -1;
+ }
+ addattr_l(n, 4096, TCA_RSVP_CLASSID, &tid, 4);
+ NEXT_ARG();
+ if (strcmp(*argv, "skip") == 0) {
+ NEXT_ARG();
+ }
+ if (get_unsigned(&tid, *argv, 0)) {
+ fprintf(stderr, "Illegal \"skip\"\n");
+ return -1;
+ }
+ pinfo.tunnelhdr = tid;
+ pinfo_ok++;
+ } else if (matches(*argv, "police") == 0) {
+ NEXT_ARG();
+ if (parse_police(&argc, &argv, TCA_RSVP_POLICE, n)) {
+ fprintf(stderr, "Illegal \"police\"\n");
+ return -1;
+ }
+ continue;
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ if (pinfo_ok)
+ addattr_l(n, 4096, TCA_RSVP_PINFO, &pinfo, sizeof(pinfo));
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static char * sprint_spi(struct tc_rsvp_gpi *pi, int dir, char *buf)
+{
+ if (pi->offset == 0) {
+ if (dir && pi->mask == htonl(0xFFFF)) {
+ snprintf(buf, SPRINT_BSIZE-1, "/%d", htonl(pi->key));
+ return buf;
+ }
+ if (!dir && pi->mask == htonl(0xFFFF0000)) {
+ snprintf(buf, SPRINT_BSIZE-1, "/%d", htonl(pi->key)>>16);
+ return buf;
+ }
+ if (pi->mask == htonl(0xFFFFFFFF)) {
+ snprintf(buf, SPRINT_BSIZE-1, " spi/esp 0x%08x", htonl(pi->key));
+ return buf;
+ }
+ } else if (pi->offset == 4 && pi->mask == htonl(0xFFFFFFFF)) {
+ snprintf(buf, SPRINT_BSIZE-1, " spi/ah 0x%08x", htonl(pi->key));
+ return buf;
+ } else if (pi->offset == -40 && pi->mask == htonl(0x000FFFFF)) {
+ snprintf(buf, SPRINT_BSIZE-1, " flowlabel 0x%05x", htonl(pi->key));
+ return buf;
+ }
+ snprintf(buf, SPRINT_BSIZE-1, " u32 0x%08x mask %08x at %d",
+ htonl(pi->key), htonl(pi->mask), pi->offset);
+ return buf;
+}
+
+static int rsvp_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle)
+{
+ int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6;
+ struct rtattr *tb[TCA_RSVP_MAX+1];
+ struct tc_rsvp_pinfo *pinfo = NULL;
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_RSVP_MAX, opt);
+
+ if (handle)
+ fprintf(f, "fh 0x%08x ", handle);
+
+ if (tb[TCA_RSVP_PINFO]) {
+ if (RTA_PAYLOAD(tb[TCA_RSVP_PINFO]) < sizeof(*pinfo))
+ return -1;
+
+ pinfo = RTA_DATA(tb[TCA_RSVP_PINFO]);
+ }
+
+ if (tb[TCA_RSVP_CLASSID]) {
+ SPRINT_BUF(b1);
+ if (!pinfo || pinfo->tunnelhdr == 0)
+ fprintf(f, "flowid %s ", sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_RSVP_CLASSID]), b1));
+ else
+ fprintf(f, "tunnel %d skip %d ", *(__u32*)RTA_DATA(tb[TCA_RSVP_CLASSID]), pinfo->tunnelhdr);
+ } else if (pinfo && pinfo->tunnelhdr)
+ fprintf(f, "tunnel [BAD] skip %d ", pinfo->tunnelhdr);
+
+ if (tb[TCA_RSVP_DST]) {
+ char buf[128];
+ fprintf(f, "session ");
+ if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_DST]), buf, sizeof(buf)) == 0)
+ fprintf(f, " [INVALID DADDR] ");
+ else
+ fprintf(f, "%s", buf);
+ if (pinfo && pinfo->dpi.mask) {
+ SPRINT_BUF(b2);
+ fprintf(f, "%s ", sprint_spi(&pinfo->dpi, 1, b2));
+ } else
+ fprintf(f, " ");
+ } else {
+ if (pinfo && pinfo->dpi.mask) {
+ SPRINT_BUF(b2);
+ fprintf(f, "session [NONE]%s ", sprint_spi(&pinfo->dpi, 1, b2));
+ } else
+ fprintf(f, "session NONE ");
+ }
+
+ if (pinfo && pinfo->protocol) {
+ SPRINT_BUF(b1);
+ fprintf(f, "ipproto %s ", inet_proto_n2a(pinfo->protocol, b1, sizeof(b1)));
+ }
+ if (pinfo && pinfo->tunnelid)
+ fprintf(f, "tunnelid %d ", pinfo->tunnelid);
+ if (tb[TCA_RSVP_SRC]) {
+ char buf[128];
+ fprintf(f, "sender ");
+ if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_SRC]), buf, sizeof(buf)) == 0) {
+ fprintf(f, "[BAD]");
+ } else {
+ fprintf(f, " %s", buf);
+ }
+ if (pinfo && pinfo->spi.mask) {
+ SPRINT_BUF(b2);
+ fprintf(f, "%s ", sprint_spi(&pinfo->spi, 0, b2));
+ } else
+ fprintf(f, " ");
+ } else if (pinfo && pinfo->spi.mask) {
+ SPRINT_BUF(b2);
+ fprintf(f, "sender [NONE]%s ", sprint_spi(&pinfo->spi, 0, b2));
+ }
+ if (tb[TCA_RSVP_POLICE])
+ tc_print_police(f, tb[TCA_RSVP_POLICE]);
+ return 0;
+}
+
+struct filter_util rsvp_filter_util = {
+ .id = "rsvp",
+ .parse_fopt = rsvp_parse_opt,
+ .print_fopt = rsvp_print_opt,
+};
+
+struct filter_util rsvp6_filter_util = {
+ .id = "rsvp6",
+ .parse_fopt = rsvp_parse_opt,
+ .print_fopt = rsvp_print_opt,
+};
diff --git a/tc/f_tcindex.c b/tc/f_tcindex.c
new file mode 100644
index 0000000..39ac75a
--- /dev/null
+++ b/tc/f_tcindex.c
@@ -0,0 +1,185 @@
+/*
+ * f_tcindex.c Traffic control index filter
+ *
+ * Written 1998,1999 by Werner Almesberger
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+ fprintf(stderr," Usage: ... tcindex [ hash SIZE ] [ mask MASK ]"
+ " [ shift SHIFT ]\n");
+ fprintf(stderr," [ pass_on | fall_through ]\n");
+ fprintf(stderr," [ classid CLASSID ] "
+ "[ police POLICE_SPEC ]\n");
+}
+
+
+#define usage() return(-1)
+
+
+static int tcindex_parse_opt(struct filter_util *qu, char *handle, int argc,
+ char **argv, struct nlmsghdr *n)
+{
+ struct tcmsg *t = NLMSG_DATA(n);
+ struct rtattr *tail;
+ char *end;
+
+ if (handle) {
+ t->tcm_handle = strtoul(handle,&end,0);
+ if (*end) {
+ fprintf(stderr, "Illegal filter ID\n");
+ return -1;
+ }
+ }
+ if (!argc) return 0;
+ tail = NLMSG_TAIL(n);
+ addattr_l(n,4096,TCA_OPTIONS,NULL,0);
+ while (argc) {
+ if (!strcmp(*argv,"hash")) {
+ int hash;
+
+ NEXT_ARG();
+ hash = strtoul(*argv,&end,0);
+ if (*end || !hash || hash > 0x10000) {
+ explain();
+ return -1;
+ }
+ addattr_l(n,4096,TCA_TCINDEX_HASH,&hash,sizeof(hash));
+ }
+ else if (!strcmp(*argv,"mask")) {
+ __u16 mask;
+
+ NEXT_ARG();
+ mask = strtoul(*argv,&end,0);
+ if (*end) {
+ explain();
+ return -1;
+ }
+ addattr_l(n,4096,TCA_TCINDEX_MASK,&mask,sizeof(mask));
+ }
+ else if (!strcmp(*argv,"shift")) {
+ int shift;
+
+ NEXT_ARG();
+ shift = strtoul(*argv,&end,0);
+ if (*end) {
+ explain();
+ return -1;
+ }
+ addattr_l(n,4096,TCA_TCINDEX_SHIFT,&shift,
+ sizeof(shift));
+ }
+ else if (!strcmp(*argv,"fall_through")) {
+ int value = 1;
+
+ addattr_l(n,4096,TCA_TCINDEX_FALL_THROUGH,&value,
+ sizeof(value));
+ }
+ else if (!strcmp(*argv,"pass_on")) {
+ int value = 0;
+
+ addattr_l(n,4096,TCA_TCINDEX_FALL_THROUGH,&value,
+ sizeof(value));
+ }
+ else if (!strcmp(*argv,"classid")) {
+ __u32 handle;
+
+ NEXT_ARG();
+ if (get_tc_classid(&handle,*argv)) {
+ fprintf(stderr, "Illegal \"classid\"\n");
+ return -1;
+ }
+ addattr_l(n, 4096, TCA_TCINDEX_CLASSID, &handle, 4);
+ }
+ else if (!strcmp(*argv,"police")) {
+ NEXT_ARG();
+ if (parse_police(&argc, &argv, TCA_TCINDEX_POLICE, n)) {
+ fprintf(stderr, "Illegal \"police\"\n");
+ return -1;
+ }
+ continue;
+ }
+ else {
+ explain();
+ return -1;
+ }
+ argc--;
+ argv++;
+ }
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+
+static int tcindex_print_opt(struct filter_util *qu, FILE *f,
+ struct rtattr *opt, __u32 handle)
+{
+ struct rtattr *tb[TCA_TCINDEX_MAX+1];
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_TCINDEX_MAX, opt);
+
+ if (handle != ~0) fprintf(f,"handle 0x%04x ",handle);
+ if (tb[TCA_TCINDEX_HASH]) {
+ __u16 hash;
+
+ if (RTA_PAYLOAD(tb[TCA_TCINDEX_HASH]) < sizeof(hash))
+ return -1;
+ hash = *(__u16 *) RTA_DATA(tb[TCA_TCINDEX_HASH]);
+ fprintf(f,"hash %d ",hash);
+ }
+ if (tb[TCA_TCINDEX_MASK]) {
+ __u16 mask;
+
+ if (RTA_PAYLOAD(tb[TCA_TCINDEX_MASK]) < sizeof(mask))
+ return -1;
+ mask = *(__u16 *) RTA_DATA(tb[TCA_TCINDEX_MASK]);
+ fprintf(f,"mask 0x%04x ",mask);
+ }
+ if (tb[TCA_TCINDEX_SHIFT]) {
+ int shift;
+
+ if (RTA_PAYLOAD(tb[TCA_TCINDEX_SHIFT]) < sizeof(shift))
+ return -1;
+ shift = *(int *) RTA_DATA(tb[TCA_TCINDEX_SHIFT]);
+ fprintf(f,"shift %d ",shift);
+ }
+ if (tb[TCA_TCINDEX_FALL_THROUGH]) {
+ int fall_through;
+
+ if (RTA_PAYLOAD(tb[TCA_TCINDEX_FALL_THROUGH]) <
+ sizeof(fall_through))
+ return -1;
+ fall_through = *(int *) RTA_DATA(tb[TCA_TCINDEX_FALL_THROUGH]);
+ fprintf(f,fall_through ? "fall_through " : "pass_on ");
+ }
+ if (tb[TCA_TCINDEX_CLASSID]) {
+ SPRINT_BUF(b1);
+ fprintf(f, "classid %s ",sprint_tc_classid(*(__u32 *)
+ RTA_DATA(tb[TCA_TCINDEX_CLASSID]), b1));
+ }
+ if (tb[TCA_TCINDEX_POLICE]) {
+ fprintf(f, "\n");
+ tc_print_police(f, tb[TCA_TCINDEX_POLICE]);
+ }
+ return 0;
+}
+
+struct filter_util tcindex_filter_util = {
+ .id = "tcindex",
+ .parse_fopt = tcindex_parse_opt,
+ .print_fopt = tcindex_print_opt,
+};
diff --git a/tc/f_u32.c b/tc/f_u32.c
new file mode 100644
index 0000000..cb3f67e
--- /dev/null
+++ b/tc/f_u32.c
@@ -0,0 +1,1261 @@
+/*
+ * q_u32.c U32 filter.
+ *
+ * This program is free software; you can u32istribute 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>
+ * Match mark added by Catalin(ux aka Dino) BOIE <catab at umbrella.ro> [5 nov 2004]
+ *
+ */
+
+#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 <linux/if.h>
+#include <linux/if_ether.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+extern int show_pretty;
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... u32 [ match SELECTOR ... ] [ link HTID ]"
+ " [ classid CLASSID ]\n");
+ fprintf(stderr, " [ police POLICE_SPEC ]"
+ " [ offset OFFSET_SPEC ]\n");
+ fprintf(stderr, " [ ht HTID ] [ hashkey HASHKEY_SPEC ]\n");
+ fprintf(stderr, " [ sample SAMPLE ]\n");
+ fprintf(stderr, "or u32 divisor DIVISOR\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Where: SELECTOR := SAMPLE SAMPLE ...\n");
+ fprintf(stderr, " SAMPLE := { ip | ip6 | udp | tcp | icmp |"
+ " u{32|16|8} | mark } SAMPLE_ARGS [divisor DIVISOR]\n");
+ fprintf(stderr, " FILTERID := X:Y:Z\n");
+ fprintf(stderr, "\nNOTE: CLASSID is parsed at hexadecimal input.\n");
+}
+
+#define usage() return(-1)
+
+int get_u32_handle(__u32 *handle, const char *str)
+{
+ __u32 htid=0, hash=0, nodeid=0;
+ char *tmp = strchr(str, ':');
+
+ if (tmp == NULL) {
+ if (memcmp("0x", str, 2) == 0)
+ return get_u32(handle, str, 16);
+ return -1;
+ }
+ htid = strtoul(str, &tmp, 16);
+ if (tmp == str && *str != ':' && *str != 0)
+ return -1;
+ if (htid>=0x1000)
+ return -1;
+ if (*tmp) {
+ str = tmp+1;
+ hash = strtoul(str, &tmp, 16);
+ if (tmp == str && *str != ':' && *str != 0)
+ return -1;
+ if (hash>=0x100)
+ return -1;
+ if (*tmp) {
+ str = tmp+1;
+ nodeid = strtoul(str, &tmp, 16);
+ if (tmp == str && *str != 0)
+ return -1;
+ if (nodeid>=0x1000)
+ return -1;
+ }
+ }
+ *handle = (htid<<20)|(hash<<12)|nodeid;
+ return 0;
+}
+
+char * sprint_u32_handle(__u32 handle, char *buf)
+{
+ int bsize = SPRINT_BSIZE-1;
+ __u32 htid = TC_U32_HTID(handle);
+ __u32 hash = TC_U32_HASH(handle);
+ __u32 nodeid = TC_U32_NODE(handle);
+ char *b = buf;
+
+ if (handle == 0) {
+ snprintf(b, bsize, "none");
+ return b;
+ }
+ if (htid) {
+ int l = snprintf(b, bsize, "%x:", htid>>20);
+ bsize -= l;
+ b += l;
+ }
+ if (nodeid|hash) {
+ if (hash) {
+ int l = snprintf(b, bsize, "%x", hash);
+ bsize -= l;
+ b += l;
+ }
+ if (nodeid) {
+ int l = snprintf(b, bsize, ":%x", nodeid);
+ bsize -= l;
+ b += l;
+ }
+ }
+ if (show_raw)
+ snprintf(b, bsize, "[%08x] ", handle);
+ return buf;
+}
+
+static int pack_key(struct tc_u32_sel *sel, __u32 key, __u32 mask,
+ int off, int offmask)
+{
+ int i;
+ int hwm = sel->nkeys;
+
+ key &= mask;
+
+ for (i=0; i<hwm; i++) {
+ if (sel->keys[i].off == off && sel->keys[i].offmask == offmask) {
+ __u32 intersect = mask&sel->keys[i].mask;
+
+ if ((key^sel->keys[i].val) & intersect)
+ return -1;
+ sel->keys[i].val |= key;
+ sel->keys[i].mask |= mask;
+ return 0;
+ }
+ }
+
+ if (hwm >= 128)
+ return -1;
+ if (off % 4)
+ return -1;
+ sel->keys[hwm].val = key;
+ sel->keys[hwm].mask = mask;
+ sel->keys[hwm].off = off;
+ sel->keys[hwm].offmask = offmask;
+ sel->nkeys++;
+ return 0;
+}
+
+static int pack_key32(struct tc_u32_sel *sel, __u32 key, __u32 mask,
+ int off, int offmask)
+{
+ key = htonl(key);
+ mask = htonl(mask);
+ return pack_key(sel, key, mask, off, offmask);
+}
+
+static int pack_key16(struct tc_u32_sel *sel, __u32 key, __u32 mask,
+ int off, int offmask)
+{
+ if (key > 0xFFFF || mask > 0xFFFF)
+ return -1;
+
+ if ((off & 3) == 0) {
+ key <<= 16;
+ mask <<= 16;
+ }
+ off &= ~3;
+ key = htonl(key);
+ mask = htonl(mask);
+
+ return pack_key(sel, key, mask, off, offmask);
+}
+
+static int pack_key8(struct tc_u32_sel *sel, __u32 key, __u32 mask, int off, int offmask)
+{
+ if (key > 0xFF || mask > 0xFF)
+ return -1;
+
+ if ((off & 3) == 0) {
+ key <<= 24;
+ mask <<= 24;
+ } else if ((off & 3) == 1) {
+ key <<= 16;
+ mask <<= 16;
+ } else if ((off & 3) == 2) {
+ key <<= 8;
+ mask <<= 8;
+ }
+ off &= ~3;
+ key = htonl(key);
+ mask = htonl(mask);
+
+ return pack_key(sel, key, mask, off, offmask);
+}
+
+
+int parse_at(int *argc_p, char ***argv_p, int *off, int *offmask)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ char *p = *argv;
+
+ if (argc <= 0)
+ return -1;
+
+ if (strlen(p) > strlen("nexthdr+") &&
+ memcmp(p, "nexthdr+", strlen("nexthdr+")) == 0) {
+ *offmask = -1;
+ p += strlen("nexthdr+");
+ } else if (matches(*argv, "nexthdr+") == 0) {
+ NEXT_ARG();
+ *offmask = -1;
+ p = *argv;
+ }
+
+ if (get_integer(off, p, 0))
+ return -1;
+ argc--; argv++;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+}
+
+
+static int parse_u32(int *argc_p, char ***argv_p, struct tc_u32_sel *sel,
+ int off, int offmask)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ __u32 key;
+ __u32 mask;
+
+ if (argc < 2)
+ return -1;
+
+ if (get_u32(&key, *argv, 0))
+ return -1;
+ argc--; argv++;
+
+ if (get_u32(&mask, *argv, 16))
+ return -1;
+ argc--; argv++;
+
+ if (argc > 0 && strcmp(argv[0], "at") == 0) {
+ NEXT_ARG();
+ if (parse_at(&argc, &argv, &off, &offmask))
+ return -1;
+ }
+
+ res = pack_key32(sel, key, mask, off, offmask);
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+static int parse_u16(int *argc_p, char ***argv_p, struct tc_u32_sel *sel,
+ int off, int offmask)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ __u32 key;
+ __u32 mask;
+
+ if (argc < 2)
+ return -1;
+
+ if (get_u32(&key, *argv, 0))
+ return -1;
+ argc--; argv++;
+
+ if (get_u32(&mask, *argv, 16))
+ return -1;
+ argc--; argv++;
+
+ if (argc > 0 && strcmp(argv[0], "at") == 0) {
+ NEXT_ARG();
+ if (parse_at(&argc, &argv, &off, &offmask))
+ return -1;
+ }
+ res = pack_key16(sel, key, mask, off, offmask);
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+static int parse_u8(int *argc_p, char ***argv_p, struct tc_u32_sel *sel,
+ int off, int offmask)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ __u32 key;
+ __u32 mask;
+
+ if (argc < 2)
+ return -1;
+
+ if (get_u32(&key, *argv, 0))
+ return -1;
+ argc--; argv++;
+
+ if (get_u32(&mask, *argv, 16))
+ return -1;
+ argc--; argv++;
+
+ if (key > 0xFF || mask > 0xFF)
+ return -1;
+
+ if (argc > 0 && strcmp(argv[0], "at") == 0) {
+ NEXT_ARG();
+ if (parse_at(&argc, &argv, &off, &offmask))
+ return -1;
+ }
+
+ res = pack_key8(sel, key, mask, off, offmask);
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+static int parse_ip_addr(int *argc_p, char ***argv_p, struct tc_u32_sel *sel,
+ int off)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ inet_prefix addr;
+ __u32 mask;
+ int offmask = 0;
+
+ if (argc < 1)
+ return -1;
+
+ if (get_prefix_1(&addr, *argv, AF_INET))
+ return -1;
+ argc--; argv++;
+
+ if (argc > 0 && strcmp(argv[0], "at") == 0) {
+ NEXT_ARG();
+ if (parse_at(&argc, &argv, &off, &offmask))
+ return -1;
+ }
+
+ mask = 0;
+ if (addr.bitlen)
+ mask = htonl(0xFFFFFFFF<<(32-addr.bitlen));
+ if (pack_key(sel, addr.data[0], mask, off, offmask) < 0)
+ return -1;
+ res = 0;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+static int parse_ip6_addr(int *argc_p, char ***argv_p,
+ struct tc_u32_sel *sel, int off)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int plen = 128;
+ int i;
+ inet_prefix addr;
+ int offmask = 0;
+
+ if (argc < 1)
+ return -1;
+
+ if (get_prefix_1(&addr, *argv, AF_INET6))
+ return -1;
+ argc--; argv++;
+
+ if (argc > 0 && strcmp(argv[0], "at") == 0) {
+ NEXT_ARG();
+ if (parse_at(&argc, &argv, &off, &offmask))
+ return -1;
+ }
+
+ plen = addr.bitlen;
+ for (i=0; i<plen; i+=32) {
+// if (((i+31)&~0x1F)<=plen) {
+ if (i + 31 <= plen) {
+ res = pack_key(sel, addr.data[i/32],
+ 0xFFFFFFFF, off+4*(i/32), offmask);
+ if (res < 0)
+ return -1;
+ } else if (i < plen) {
+ __u32 mask = htonl(0xFFFFFFFF << (32 - (plen -i )));
+ res = pack_key(sel, addr.data[i/32],
+ mask, off+4*(i/32), offmask);
+ if (res < 0)
+ return -1;
+ }
+ }
+ res = 0;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+static int parse_ether_addr(int *argc_p, char ***argv_p,
+ struct tc_u32_sel *sel, int off)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ __u8 addr[6];
+ int offmask = 0;
+ __u32 key;
+ int i;
+
+ if (argc < 1)
+ return -1;
+
+ if (sscanf(*argv, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ addr + 0, addr + 1, addr + 2,
+ addr + 3, addr + 4, addr + 5) != 6) {
+ fprintf(stderr, "parse_ether_addr: improperly formed address '%s'\n",
+ *argv);
+ return -1;
+ }
+
+ argc--; argv++;
+ if (argc > 0 && strcmp(argv[0], "at") == 0) {
+ NEXT_ARG();
+ if (parse_at(&argc, &argv, &off, &offmask))
+ return -1;
+ }
+
+ for (i = 0; i < 6; i += 2) {
+ key = *(__u16 *) (addr + i);
+
+ res = pack_key16(sel, key, 0xFFFF, off + i, offmask);
+ if (res < 0)
+ return -1;
+ }
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+static int parse_ip(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ if (argc < 2)
+ return -1;
+
+ if (strcmp(*argv, "src") == 0) {
+ NEXT_ARG();
+ res = parse_ip_addr(&argc, &argv, sel, 12);
+ } else if (strcmp(*argv, "dst") == 0) {
+ NEXT_ARG();
+ res = parse_ip_addr(&argc, &argv, sel, 16);
+ } else if (strcmp(*argv, "tos") == 0 ||
+ matches(*argv, "dsfield") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 1, 0);
+ } else if (strcmp(*argv, "ihl") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 0, 0);
+ } else if (strcmp(*argv, "protocol") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 9, 0);
+ } else if (matches(*argv, "precedence") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 1, 0);
+ } else if (strcmp(*argv, "nofrag") == 0) {
+ argc--; argv++;
+ res = pack_key16(sel, 0, 0x3FFF, 6, 0);
+ } else if (strcmp(*argv, "firstfrag") == 0) {
+ argc--; argv++;
+ res = pack_key16(sel, 0, 0x1FFF, 6, 0);
+ } else if (strcmp(*argv, "df") == 0) {
+ argc--; argv++;
+ res = pack_key16(sel, 0x4000, 0x4000, 6, 0);
+ } else if (strcmp(*argv, "mf") == 0) {
+ argc--; argv++;
+ res = pack_key16(sel, 0x2000, 0x2000, 6, 0);
+ } else if (strcmp(*argv, "dport") == 0) {
+ NEXT_ARG();
+ res = parse_u16(&argc, &argv, sel, 22, 0);
+ } else if (strcmp(*argv, "sport") == 0) {
+ NEXT_ARG();
+ res = parse_u16(&argc, &argv, sel, 20, 0);
+ } else if (strcmp(*argv, "icmp_type") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 20, 0);
+ } else if (strcmp(*argv, "icmp_code") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 20, 1);
+ } else
+ return -1;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+static int parse_ip6(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ if (argc < 2)
+ return -1;
+
+ if (strcmp(*argv, "src") == 0) {
+ NEXT_ARG();
+ res = parse_ip6_addr(&argc, &argv, sel, 8);
+ } else if (strcmp(*argv, "dst") == 0) {
+ NEXT_ARG();
+ res = parse_ip6_addr(&argc, &argv, sel, 24);
+ } else if (strcmp(*argv, "priority") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 4, 0);
+ } else if (strcmp(*argv, "protocol") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 6, 0);
+ } else if (strcmp(*argv, "flowlabel") == 0) {
+ NEXT_ARG();
+ res = parse_u32(&argc, &argv, sel, 0, 0);
+ } else if (strcmp(*argv, "dport") == 0) {
+ NEXT_ARG();
+ res = parse_u16(&argc, &argv, sel, 42, 0);
+ } else if (strcmp(*argv, "sport") == 0) {
+ NEXT_ARG();
+ res = parse_u16(&argc, &argv, sel, 40, 0);
+ } else if (strcmp(*argv, "icmp_type") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 40, 0);
+ } else if (strcmp(*argv, "icmp_code") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 41, 1);
+ } else
+ return -1;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+static int parse_ether(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ if (argc < 2)
+ return -1;
+
+ if (strcmp(*argv, "src") == 0) {
+ NEXT_ARG();
+ res = parse_ether_addr(&argc, &argv, sel, -8);
+ } else if (strcmp(*argv, "dst") == 0) {
+ NEXT_ARG();
+ res = parse_ether_addr(&argc, &argv, sel, -14);
+ } else {
+ fprintf(stderr, "Unknown match: ether %s\n", *argv);
+ return -1;
+ }
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+#define parse_tcp parse_udp
+static int parse_udp(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ if (argc < 2)
+ return -1;
+
+ if (strcmp(*argv, "src") == 0) {
+ NEXT_ARG();
+ res = parse_u16(&argc, &argv, sel, 0, -1);
+ } else if (strcmp(*argv, "dst") == 0) {
+ NEXT_ARG();
+ res = parse_u16(&argc, &argv, sel, 2, -1);
+ } else
+ return -1;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+
+static int parse_icmp(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ if (argc < 2)
+ return -1;
+
+ if (strcmp(*argv, "type") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 0, -1);
+ } else if (strcmp(*argv, "code") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 1, -1);
+ } else
+ return -1;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+static int parse_mark(int *argc_p, char ***argv_p, struct nlmsghdr *n)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ struct tc_u32_mark mark;
+
+ if (argc <= 1)
+ return -1;
+
+ if (get_u32(&mark.val, *argv, 0)) {
+ fprintf(stderr, "Illegal \"mark\" value\n");
+ return -1;
+ }
+ NEXT_ARG();
+
+ if (get_u32(&mark.mask, *argv, 0)) {
+ fprintf(stderr, "Illegal \"mark\" mask\n");
+ return -1;
+ }
+ NEXT_ARG();
+
+ if ((mark.val & mark.mask) != mark.val) {
+ fprintf(stderr, "Illegal \"mark\" (impossible combination)\n");
+ return -1;
+ }
+
+ addattr_l(n, MAX_MSG, TCA_U32_MARK, &mark, sizeof(mark));
+ res = 0;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+static int parse_selector(int *argc_p, char ***argv_p,
+ struct tc_u32_sel *sel, struct nlmsghdr *n)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int res = -1;
+
+ if (argc <= 0)
+ return -1;
+
+ if (matches(*argv, "u32") == 0) {
+ NEXT_ARG();
+ res = parse_u32(&argc, &argv, sel, 0, 0);
+ } else if (matches(*argv, "u16") == 0) {
+ NEXT_ARG();
+ res = parse_u16(&argc, &argv, sel, 0, 0);
+ } else if (matches(*argv, "u8") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, sel, 0, 0);
+ } else if (matches(*argv, "ip") == 0) {
+ NEXT_ARG();
+ res = parse_ip(&argc, &argv, sel);
+ } else if (matches(*argv, "ip6") == 0) {
+ NEXT_ARG();
+ res = parse_ip6(&argc, &argv, sel);
+ } else if (matches(*argv, "udp") == 0) {
+ NEXT_ARG();
+ res = parse_udp(&argc, &argv, sel);
+ } else if (matches(*argv, "tcp") == 0) {
+ NEXT_ARG();
+ res = parse_tcp(&argc, &argv, sel);
+ } else if (matches(*argv, "icmp") == 0) {
+ NEXT_ARG();
+ res = parse_icmp(&argc, &argv, sel);
+ } else if (matches(*argv, "mark") == 0) {
+ NEXT_ARG();
+ res = parse_mark(&argc, &argv, n);
+ } else if (matches(*argv, "ether") == 0) {
+ NEXT_ARG();
+ res = parse_ether(&argc, &argv, sel);
+ } else
+ return -1;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+static int parse_offset(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ while (argc > 0) {
+ if (matches(*argv, "plus") == 0) {
+ int off;
+ NEXT_ARG();
+ if (get_integer(&off, *argv, 0))
+ return -1;
+ sel->off = off;
+ sel->flags |= TC_U32_OFFSET;
+ } else if (matches(*argv, "at") == 0) {
+ int off;
+ NEXT_ARG();
+ if (get_integer(&off, *argv, 0))
+ return -1;
+ sel->offoff = off;
+ if (off%2) {
+ fprintf(stderr, "offset \"at\" must be even\n");
+ return -1;
+ }
+ sel->flags |= TC_U32_VAROFFSET;
+ } else if (matches(*argv, "mask") == 0) {
+ __u16 mask;
+ NEXT_ARG();
+ if (get_u16(&mask, *argv, 16))
+ return -1;
+ sel->offmask = htons(mask);
+ sel->flags |= TC_U32_VAROFFSET;
+ } else if (matches(*argv, "shift") == 0) {
+ int shift;
+ NEXT_ARG();
+ if (get_integer(&shift, *argv, 0))
+ return -1;
+ sel->offshift = shift;
+ sel->flags |= TC_U32_VAROFFSET;
+ } else if (matches(*argv, "eat") == 0) {
+ sel->flags |= TC_U32_EAT;
+ } else {
+ break;
+ }
+ argc--; argv++;
+ }
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+}
+
+static int parse_hashkey(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ while (argc > 0) {
+ if (matches(*argv, "mask") == 0) {
+ __u32 mask;
+ NEXT_ARG();
+ if (get_u32(&mask, *argv, 16))
+ return -1;
+ sel->hmask = htonl(mask);
+ } else if (matches(*argv, "at") == 0) {
+ int num;
+ NEXT_ARG();
+ if (get_integer(&num, *argv, 0))
+ return -1;
+ if (num%4)
+ return -1;
+ sel->hoff = num;
+ } else {
+ break;
+ }
+ argc--; argv++;
+ }
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+}
+
+static void print_ipv4(FILE *f, const struct tc_u32_key *key)
+{
+ char abuf[256];
+
+ switch (key->off) {
+ case 0:
+ switch (ntohl(key->mask)) {
+ case 0x0f000000:
+ fprintf(f, "\n match IP ihl %u", ntohl(key->val) >> 24);
+ return;
+ case 0x00ff0000:
+ fprintf(f, "\n match IP dsfield %#x", ntohl(key->val) >> 16);
+ return;
+ }
+ break;
+ case 8:
+ if (ntohl(key->mask) == 0x00ff0000) {
+ fprintf(f, "\n match IP protocol %d", ntohl(key->val) >> 16);
+ return;
+ }
+ break;
+ case 12:
+ case 16: {
+ int bits = mask2bits(key->mask);
+ if (bits >= 0) {
+ fprintf(f, "\n %s %s/%d",
+ key->off == 12 ? "match IP src" : "match IP dst",
+ inet_ntop(AF_INET, &key->val,
+ abuf, sizeof(abuf)),
+ bits);
+ return;
+ }
+ }
+ break;
+
+ case 20:
+ switch (ntohl(key->mask)) {
+ case 0x0000ffff:
+ fprintf(f, "\n match sport %u",
+ ntohl(key->val) & 0xffff);
+ return;
+ case 0xffff0000:
+ fprintf(f, "\n match dport %u",
+ ntohl(key->val) >> 16);
+ return;
+ case 0xffffffff:
+ fprintf(f, "\n match sport %u, match dport %u",
+ ntohl(key->val) & 0xffff,
+ ntohl(key->val) >> 16);
+
+ return;
+ }
+ /* XXX: Default print_raw */
+ }
+}
+
+static void print_ipv6(FILE *f, const struct tc_u32_key *key)
+{
+ char abuf[256];
+
+ switch (key->off) {
+ case 0:
+ switch (ntohl(key->mask)) {
+ case 0x0f000000:
+ fprintf(f, "\n match IP ihl %u", ntohl(key->val) >> 24);
+ return;
+ case 0x00ff0000:
+ fprintf(f, "\n match IP dsfield %#x", ntohl(key->val) >> 16);
+ return;
+ }
+ break;
+ case 8:
+ if (ntohl(key->mask) == 0x00ff0000) {
+ fprintf(f, "\n match IP protocol %d", ntohl(key->val) >> 16);
+ return;
+ }
+ break;
+ case 12:
+ case 16: {
+ int bits = mask2bits(key->mask);
+ if (bits >= 0) {
+ fprintf(f, "\n %s %s/%d",
+ key->off == 12 ? "match IP src" : "match IP dst",
+ inet_ntop(AF_INET, &key->val,
+ abuf, sizeof(abuf)),
+ bits);
+ return;
+ }
+ }
+ break;
+
+ case 20:
+ switch (ntohl(key->mask)) {
+ case 0x0000ffff:
+ fprintf(f, "\n match sport %u",
+ ntohl(key->val) & 0xffff);
+ return;
+ case 0xffff0000:
+ fprintf(f, "\n match dport %u",
+ ntohl(key->val) >> 16);
+ return;
+ case 0xffffffff:
+ fprintf(f, "\n match sport %u, match dport %u",
+ ntohl(key->val) & 0xffff,
+ ntohl(key->val) >> 16);
+
+ return;
+ }
+ /* XXX: Default print_raw */
+ }
+}
+
+static void print_raw(FILE *f, const struct tc_u32_key *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);
+}
+
+static const struct {
+ __u16 proto;
+ __u16 pad;
+ void (*pprinter)(FILE *f, const struct tc_u32_key *key);
+} u32_pprinters[] = {
+ {0, 0, print_raw},
+ {ETH_P_IP, 0, print_ipv4},
+ {ETH_P_IPV6, 0, print_ipv6},
+};
+
+static void show_keys(FILE *f, const struct tc_u32_key *key)
+{
+ int i = 0;
+
+ if (!show_pretty)
+ goto show_k;
+
+ for (i = 0; i < sizeof(u32_pprinters) / sizeof(u32_pprinters[0]); i++) {
+ if (u32_pprinters[i].proto == ntohs(f_proto)) {
+show_k:
+ u32_pprinters[i].pprinter(f, key);
+ return;
+ }
+ }
+
+ i = 0;
+ goto show_k;
+}
+
+static int u32_parse_opt(struct filter_util *qu, char *handle,
+ int argc, char **argv, struct nlmsghdr *n)
+{
+ struct {
+ struct tc_u32_sel sel;
+ struct tc_u32_key keys[128];
+ } sel;
+ struct tcmsg *t = NLMSG_DATA(n);
+ struct rtattr *tail;
+ int sel_ok = 0, terminal_ok = 0;
+ int sample_ok = 0;
+ __u32 htid = 0;
+ __u32 order = 0;
+
+ memset(&sel, 0, sizeof(sel));
+
+ if (handle && get_u32_handle(&t->tcm_handle, handle)) {
+ fprintf(stderr, "Illegal filter ID\n");
+ return -1;
+ }
+
+ if (argc == 0)
+ return 0;
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0);
+
+ while (argc > 0) {
+ if (matches(*argv, "match") == 0) {
+ NEXT_ARG();
+ if (parse_selector(&argc, &argv, &sel.sel, n)) {
+ fprintf(stderr, "Illegal \"match\"\n");
+ return -1;
+ }
+ sel_ok++;
+ continue;
+ } else if (matches(*argv, "offset") == 0) {
+ NEXT_ARG();
+ if (parse_offset(&argc, &argv, &sel.sel)) {
+ fprintf(stderr, "Illegal \"offset\"\n");
+ return -1;
+ }
+ continue;
+ } else if (matches(*argv, "hashkey") == 0) {
+ NEXT_ARG();
+ if (parse_hashkey(&argc, &argv, &sel.sel)) {
+ fprintf(stderr, "Illegal \"hashkey\"\n");
+ return -1;
+ }
+ continue;
+ } else if (matches(*argv, "classid") == 0 ||
+ strcmp(*argv, "flowid") == 0) {
+ unsigned handle;
+ NEXT_ARG();
+ if (get_tc_classid(&handle, *argv)) {
+ fprintf(stderr, "Illegal \"classid\"\n");
+ return -1;
+ }
+ addattr_l(n, MAX_MSG, TCA_U32_CLASSID, &handle, 4);
+ sel.sel.flags |= TC_U32_TERMINAL;
+ } else if (matches(*argv, "divisor") == 0) {
+ unsigned divisor;
+ NEXT_ARG();
+ if (get_unsigned(&divisor, *argv, 0) ||
+ divisor == 0 ||
+ divisor > 0x100 || ((divisor - 1) & divisor)) {
+ fprintf(stderr, "Illegal \"divisor\"\n");
+ return -1;
+ }
+ 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, "Illegal \"order\"\n");
+ return -1;
+ }
+ } else if (strcmp(*argv, "link") == 0) {
+ unsigned handle;
+ NEXT_ARG();
+ if (get_u32_handle(&handle, *argv)) {
+ fprintf(stderr, "Illegal \"link\"\n");
+ return -1;
+ }
+ if (handle && TC_U32_NODE(handle)) {
+ fprintf(stderr, "\"link\" must be a hash table.\n");
+ return -1;
+ }
+ addattr_l(n, MAX_MSG, TCA_U32_LINK, &handle, 4);
+ } else if (strcmp(*argv, "ht") == 0) {
+ unsigned handle;
+ NEXT_ARG();
+ if (get_u32_handle(&handle, *argv)) {
+ fprintf(stderr, "Illegal \"ht\"\n");
+ return -1;
+ }
+ if (handle && TC_U32_NODE(handle)) {
+ fprintf(stderr, "\"ht\" must be a hash table.\n");
+ return -1;
+ }
+ if (sample_ok)
+ htid = (htid&0xFF000)|(handle&0xFFF00000);
+ else
+ htid = (handle&0xFFFFF000);
+ } else if (strcmp(*argv, "sample") == 0) {
+ __u32 hash;
+ unsigned divisor = 0x100;
+
+ struct {
+ struct tc_u32_sel sel;
+ struct tc_u32_key keys[4];
+ } sel2;
+ memset(&sel2, 0, sizeof(sel2));
+ NEXT_ARG();
+ if (parse_selector(&argc, &argv, &sel2.sel, n)) {
+ fprintf(stderr, "Illegal \"sample\"\n");
+ return -1;
+ }
+ if (sel2.sel.nkeys != 1) {
+ fprintf(stderr, "\"sample\" must contain"
+ " exactly ONE key.\n");
+ return -1;
+ }
+ if (*argv != 0 && strcmp(*argv, "divisor") == 0) {
+ NEXT_ARG();
+ if (get_unsigned(&divisor, *argv, 0) || divisor == 0 ||
+ divisor > 0x100 || ((divisor - 1) & divisor)) {
+ fprintf(stderr, "Illegal sample \"divisor\"\n");
+ return -1;
+ }
+ NEXT_ARG();
+ }
+ hash = sel2.sel.keys[0].val&sel2.sel.keys[0].mask;
+ hash ^= hash>>16;
+ hash ^= hash>>8;
+ htid = ((hash%divisor)<<12)|(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;
+ }
+ terminal_ok++;
+ continue;
+
+ } else if (matches(*argv, "police") == 0) {
+ NEXT_ARG();
+ if (parse_police(&argc, &argv, TCA_U32_POLICE, n)) {
+ fprintf(stderr, "Illegal \"police\"\n");
+ return -1;
+ }
+ terminal_ok++;
+ continue;
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ /* We dont necessarily need class/flowids */
+ if (terminal_ok)
+ sel.sel.flags |= TC_U32_TERMINAL;
+
+ if (order) {
+ if (TC_U32_NODE(t->tcm_handle) && order != TC_U32_NODE(t->tcm_handle)) {
+ fprintf(stderr, "\"order\" contradicts \"handle\"\n");
+ return -1;
+ }
+ t->tcm_handle |= order;
+ }
+
+ if (htid)
+ addattr_l(n, MAX_MSG, TCA_U32_HASH, &htid, 4);
+ if (sel_ok)
+ addattr_l(n, MAX_MSG, TCA_U32_SEL, &sel,
+ sizeof(sel.sel)+sel.sel.nkeys*sizeof(struct tc_u32_key));
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt,
+ __u32 handle)
+{
+ struct rtattr *tb[TCA_U32_MAX+1];
+ struct tc_u32_sel *sel = NULL;
+ struct tc_u32_pcnt *pf = NULL;
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_U32_MAX, opt);
+
+ if (handle) {
+ SPRINT_BUF(b1);
+ fprintf(f, "fh %s ", sprint_u32_handle(handle, b1));
+ }
+ if (TC_U32_NODE(handle)) {
+ fprintf(f, "order %d ", TC_U32_NODE(handle));
+ }
+
+ if (tb[TCA_U32_SEL]) {
+ if (RTA_PAYLOAD(tb[TCA_U32_SEL]) < sizeof(*sel))
+ return -1;
+
+ sel = RTA_DATA(tb[TCA_U32_SEL]);
+ }
+
+ if (tb[TCA_U32_DIVISOR]) {
+ fprintf(f, "ht divisor %d ", *(__u32*)RTA_DATA(tb[TCA_U32_DIVISOR]));
+ } else if (tb[TCA_U32_HASH]) {
+ __u32 htid = *(__u32*)RTA_DATA(tb[TCA_U32_HASH]);
+ fprintf(f, "key ht %x bkt %x ", TC_U32_USERHTID(htid),
+ TC_U32_HASH(htid));
+ } else {
+ fprintf(f, "??? ");
+ }
+ if (tb[TCA_U32_CLASSID]) {
+ SPRINT_BUF(b1);
+ fprintf(f, "%sflowid %s ",
+ !sel || !(sel->flags&TC_U32_TERMINAL) ? "*" : "",
+ sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_U32_CLASSID]), b1));
+ } else if (sel && sel->flags&TC_U32_TERMINAL) {
+ fprintf(f, "terminal flowid ??? ");
+ }
+ if (tb[TCA_U32_LINK]) {
+ SPRINT_BUF(b1);
+ fprintf(f, "link %s ",
+ sprint_u32_handle(*(__u32*)RTA_DATA(tb[TCA_U32_LINK]), b1));
+ }
+
+ if (tb[TCA_U32_PCNT]) {
+ if (RTA_PAYLOAD(tb[TCA_U32_PCNT]) < sizeof(*pf)) {
+ fprintf(f, "Broken perf counters \n");
+ return -1;
+ }
+ pf = RTA_DATA(tb[TCA_U32_PCNT]);
+ }
+
+ if (sel && show_stats && NULL != pf)
+ fprintf(f, " (rule hit %llu success %llu)",
+ (unsigned long long) pf->rcnt,
+ (unsigned long long) pf->rhit);
+
+ if (tb[TCA_U32_MARK]) {
+ struct tc_u32_mark *mark = RTA_DATA(tb[TCA_U32_MARK]);
+ if (RTA_PAYLOAD(tb[TCA_U32_MARK]) < sizeof(*mark)) {
+ fprintf(f, "\n Invalid mark (kernel&iproute2 mismatch)\n");
+ } else {
+ fprintf(f, "\n mark 0x%04x 0x%04x (success %d)",
+ mark->val, mark->mask, mark->success);
+ }
+ }
+
+ if (sel) {
+ if (sel->nkeys) {
+ int i;
+ for (i=0; i<sel->nkeys; i++) {
+ show_keys(f, sel->keys + i);
+ if (show_stats && NULL != pf)
+ fprintf(f, " (success %llu ) ",
+ (unsigned long long) pf->kcnts[i]);
+ }
+ }
+
+ if (sel->flags&(TC_U32_VAROFFSET|TC_U32_OFFSET)) {
+ fprintf(f, "\n offset ");
+ if (sel->flags&TC_U32_VAROFFSET)
+ fprintf(f, "%04x>>%d at %d ",
+ ntohs(sel->offmask),
+ sel->offshift, sel->offoff);
+ if (sel->off)
+ fprintf(f, "plus %d ", sel->off);
+ }
+ if (sel->flags&TC_U32_EAT)
+ fprintf(f, " eat ");
+
+ if (sel->hmask) {
+ fprintf(f, "\n hash mask %08x at %d ",
+ (unsigned int)htonl(sel->hmask), sel->hoff);
+ }
+ }
+
+ 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;
+}
+
+struct filter_util u32_filter_util = {
+ .id = "u32",
+ .parse_fopt = u32_parse_opt,
+ .print_fopt = u32_print_opt,
+};
diff --git a/tc/m_action.c b/tc/m_action.c
new file mode 100644
index 0000000..9f24022
--- /dev/null
+++ b/tc/m_action.c
@@ -0,0 +1,630 @@
+/*
+ * m_action.c Action Management
+ *
+ * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
+ *
+ * TODO:
+ * - parse to be passed a filedescriptor for logging purposes
+ *
+*/
+
+#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 <dlfcn.h>
+
+#include "utils.h"
+#include "tc_common.h"
+#include "tc_util.h"
+
+static struct action_util * action_list;
+#ifdef CONFIG_GACT
+int gact_ld = 0 ; //fuckin backward compatibility
+#endif
+int batch_c = 0;
+int tab_flush = 0;
+
+void act_usage(void)
+{
+ /*XXX: In the near future add a action->print_help to improve
+ * usability
+ * This would mean new tc will not be backward compatible
+ * with any action .so from the old days. But if someone really
+ * does that, they would know how to fix this ..
+ *
+ */
+ fprintf (stderr, "usage: tc actions <ACTSPECOP>*\n");
+ fprintf(stderr,
+ "Where: \tACTSPECOP := ACR | GD | FL\n"
+ "\tACR := add | change | replace <ACTSPEC>* \n"
+ "\tGD := get | delete | <ACTISPEC>*\n"
+ "\tFL := ls | list | flush | <ACTNAMESPEC>\n"
+ "\tACTNAMESPEC := action <ACTNAME>\n"
+ "\tACTISPEC := <ACTNAMESPEC> <INDEXSPEC>\n"
+ "\tACTSPEC := action <ACTDETAIL> [INDEXSPEC]\n"
+ "\tINDEXSPEC := index <32 bit indexvalue>\n"
+ "\tACTDETAIL := <ACTNAME> <ACTPARAMS>\n"
+ "\t\tExample ACTNAME is gact, mirred etc\n"
+ "\t\tEach action has its own parameters (ACTPARAMS)\n"
+ "\n");
+
+ exit(-1);
+}
+
+static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt)
+{
+ if (opt && RTA_PAYLOAD(opt))
+ fprintf(f, "[Unknown action, optlen=%u] ",
+ (unsigned) RTA_PAYLOAD(opt));
+ return 0;
+}
+
+static int parse_noaopt(struct action_util *au, int *argc_p, char ***argv_p, int code, struct nlmsghdr *n)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ if (argc) {
+ fprintf(stderr, "Unknown action \"%s\", hence option \"%s\" is unparsable\n", au->id, *argv);
+ } else {
+ fprintf(stderr, "Unknown action \"%s\"\n", au->id);
+ }
+ return -1;
+}
+
+struct action_util *get_action_kind(char *str)
+{
+ static void *aBODY;
+ void *dlh;
+ char buf[256];
+ struct action_util *a;
+#ifdef CONFIG_GACT
+ int looked4gact = 0;
+restart_s:
+#endif
+ for (a = action_list; a; a = a->next) {
+ if (strcmp(a->id, str) == 0)
+ return a;
+ }
+
+ snprintf(buf, sizeof(buf), "m_%s.so", str);
+ dlh = dlopen(buf, RTLD_LAZY);
+ if (dlh == NULL) {
+ dlh = aBODY;
+ if (dlh == NULL) {
+ dlh = aBODY = dlopen(NULL, RTLD_LAZY);
+ if (dlh == NULL)
+ goto noexist;
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "%s_action_util", str);
+ a = dlsym(dlh, buf);
+ if (a == NULL)
+ goto noexist;
+
+reg:
+ a->next = action_list;
+ action_list = a;
+ return a;
+
+noexist:
+#ifdef CONFIG_GACT
+ if (!looked4gact) {
+ looked4gact = 1;
+ strcpy(str,"gact");
+ goto restart_s;
+ }
+#endif
+ a = malloc(sizeof(*a));
+ if (a) {
+ memset(a, 0, sizeof(*a));
+ strncpy(a->id, "noact", 15);
+ a->parse_aopt = parse_noaopt;
+ a->print_aopt = print_noaopt;
+ goto reg;
+ }
+ return a;
+}
+
+int
+new_cmd(char **argv)
+{
+ if ((matches(*argv, "change") == 0) ||
+ (matches(*argv, "replace") == 0)||
+ (matches(*argv, "delete") == 0)||
+ (matches(*argv, "add") == 0))
+ return 1;
+
+ return 0;
+
+}
+
+int
+parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ struct rtattr *tail, *tail2;
+ char k[16];
+ int ok = 0;
+ int eap = 0; /* expect action parameters */
+
+ int ret = 0;
+ int prio = 0;
+
+ if (argc <= 0)
+ return -1;
+
+ tail = tail2 = NLMSG_TAIL(n);
+
+ addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+
+ while (argc > 0) {
+
+ memset(k, 0, sizeof (k));
+
+ if (strcmp(*argv, "action") == 0 ) {
+ argc--;
+ argv++;
+ eap = 1;
+#ifdef CONFIG_GACT
+ if (!gact_ld) {
+ get_action_kind("gact");
+ }
+#endif
+ continue;
+ } else if (strcmp(*argv, "help") == 0) {
+ return -1;
+ } else if (new_cmd(argv)) {
+ goto done0;
+ } else {
+ struct action_util *a = NULL;
+ strncpy(k, *argv, sizeof (k) - 1);
+ eap = 0;
+ if (argc > 0 ) {
+ a = get_action_kind(k);
+ } else {
+done0:
+ if (ok)
+ break;
+ else
+ goto done;
+ }
+
+ if (NULL == a) {
+ goto bad_val;
+ }
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, ++prio, NULL, 0);
+ addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
+
+ ret = a->parse_aopt(a,&argc, &argv, TCA_ACT_OPTIONS, n);
+
+ if (ret < 0) {
+ fprintf(stderr,"bad action parsing\n");
+ goto bad_val;
+ }
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ ok++;
+ }
+
+ }
+
+ if (eap > 0) {
+ fprintf(stderr,"bad action empty %d\n",eap);
+ goto bad_val;
+ }
+
+ tail2->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail2;
+
+done:
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+bad_val:
+ /* no need to undo things, returning from here should
+ * cause enough pain */
+ fprintf(stderr, "parse_action: bad value (%d:%s)!\n",argc,*argv);
+ return -1;
+}
+
+int
+tc_print_one_action(FILE * f, struct rtattr *arg)
+{
+
+ struct rtattr *tb[TCA_ACT_MAX + 1];
+ int err = 0;
+ struct action_util *a = NULL;
+
+ if (arg == NULL)
+ return -1;
+
+ parse_rtattr_nested(tb, TCA_ACT_MAX, arg);
+ if (tb[TCA_ACT_KIND] == NULL) {
+ fprintf(stderr, "NULL Action!\n");
+ return -1;
+ }
+
+
+ a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND]));
+ if (NULL == a)
+ return err;
+
+ if (tab_flush) {
+ fprintf(f," %s \n", a->id);
+ tab_flush = 0;
+ return 0;
+ }
+
+ err = a->print_aopt(a,f,tb[TCA_ACT_OPTIONS]);
+
+
+ if (0 > err)
+ return err;
+
+ if (show_stats && tb[TCA_ACT_STATS]) {
+ fprintf(f, "\tAction statistics:\n");
+ print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL);
+ fprintf(f, "\n");
+ }
+
+ return 0;
+}
+
+int
+tc_print_action(FILE * f, const struct rtattr *arg)
+{
+
+ int i;
+ struct rtattr *tb[TCA_ACT_MAX_PRIO + 1];
+
+ if (arg == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_ACT_MAX_PRIO, arg);
+
+ if (tab_flush && NULL != tb[0] && NULL == tb[1]) {
+ int ret = tc_print_one_action(f, tb[0]);
+ return ret;
+ }
+
+ for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
+ if (tb[i]) {
+ fprintf(f, "\n\taction order %d: ", i + batch_c);
+ if (0 > tc_print_one_action(f, tb[i])) {
+ fprintf(f, "Error printing action\n");
+ }
+ }
+
+ }
+
+ batch_c+=TCA_ACT_MAX_PRIO ;
+ return 0;
+}
+
+int print_action(const struct sockaddr_nl *who,
+ struct nlmsghdr *n,
+ void *arg)
+{
+ FILE *fp = (FILE*)arg;
+ struct tcamsg *t = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr * tb[TCAA_MAX+1];
+
+ len -= NLMSG_LENGTH(sizeof(*t));
+
+ if (len < 0) {
+ fprintf(stderr, "Wrong len %d\n", len);
+ return -1;
+ }
+
+ parse_rtattr(tb, TCAA_MAX, TA_RTA(t), len);
+
+ if (NULL == tb[TCA_ACT_TAB]) {
+ if (n->nlmsg_type != RTM_GETACTION)
+ fprintf(stderr, "print_action: NULL kind\n");
+ return -1;
+ }
+
+ if (n->nlmsg_type == RTM_DELACTION) {
+ if (n->nlmsg_flags & NLM_F_ROOT) {
+ fprintf(fp, "Flushed table ");
+ tab_flush = 1;
+ } else {
+ fprintf(fp, "deleted action ");
+ }
+ }
+
+ if (n->nlmsg_type == RTM_NEWACTION)
+ fprintf(fp, "Added action ");
+ tc_print_action(fp, tb[TCA_ACT_TAB]);
+
+ return 0;
+}
+
+int tc_action_gd(int cmd, unsigned flags, int *argc_p, char ***argv_p)
+{
+ char k[16];
+ struct action_util *a = NULL;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int prio = 0;
+ int ret = 0;
+ __u32 i;
+ struct sockaddr_nl nladdr;
+ struct rtattr *tail;
+ struct rtattr *tail2;
+ struct nlmsghdr *ans = NULL;
+
+ struct {
+ struct nlmsghdr n;
+ struct tcamsg t;
+ char buf[MAX_MSG];
+ } req;
+
+ req.t.tca_family = AF_UNSPEC;
+
+ memset(&req, 0, sizeof(req));
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+ req.n.nlmsg_type = cmd;
+ argc -=1;
+ argv +=1;
+
+
+ tail = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
+
+ while (argc > 0) {
+ if (strcmp(*argv, "action") == 0 ) {
+ argc--;
+ argv++;
+ continue;
+ } else if (strcmp(*argv, "help") == 0) {
+ return -1;
+ }
+
+ strncpy(k, *argv, sizeof (k) - 1);
+ a = get_action_kind(k);
+ if (NULL == a) {
+ fprintf(stderr, "Error: non existent action: %s\n",k);
+ ret = -1;
+ goto bad_val;
+ }
+ if (strcmp(a->id, k) != 0) {
+ fprintf(stderr, "Error: non existent action: %s\n",k);
+ ret = -1;
+ goto bad_val;
+ }
+
+ argc -=1;
+ argv +=1;
+ if (argc <= 0) {
+ fprintf(stderr, "Error: no index specified action: %s\n",k);
+ ret = -1;
+ goto bad_val;
+ }
+
+ if (matches(*argv, "index") == 0) {
+ NEXT_ARG();
+ if (get_u32(&i, *argv, 10)) {
+ fprintf(stderr, "Illegal \"index\"\n");
+ ret = -1;
+ goto bad_val;
+ }
+ argc -=1;
+ argv +=1;
+ } else {
+ fprintf(stderr, "Error: no index specified action: %s\n",k);
+ ret = -1;
+ goto bad_val;
+ }
+
+ tail2 = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
+ addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
+ addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i);
+ tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
+
+ }
+
+ tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
+
+ req.n.nlmsg_seq = rth.dump = ++rth.seq;
+ if (cmd == RTM_GETACTION)
+ ans = &req.n;
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, ans, NULL, NULL) < 0) {
+ fprintf(stderr, "We have an error talking to the kernel\n");
+ return 1;
+ }
+
+ if (ans && print_action(NULL, &req.n, (void*)stdout) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ return 1;
+ }
+
+ *argc_p = argc;
+ *argv_p = argv;
+bad_val:
+ return ret;
+}
+
+int tc_action_modify(int cmd, unsigned flags, int *argc_p, char ***argv_p)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int ret = 0;
+
+ struct rtattr *tail;
+ struct {
+ struct nlmsghdr n;
+ struct tcamsg t;
+ char buf[MAX_MSG];
+ } req;
+
+ req.t.tca_family = AF_UNSPEC;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+ req.n.nlmsg_type = cmd;
+ tail = NLMSG_TAIL(&req.n);
+ argc -=1;
+ argv +=1;
+ if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
+ fprintf(stderr, "Illegal \"action\"\n");
+ return -1;
+ }
+ tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
+ fprintf(stderr, "We have an error talking to the kernel\n");
+ ret = -1;
+ }
+
+ *argc_p = argc;
+ *argv_p = argv;
+
+ return ret;
+}
+
+int tc_act_list_or_flush(int argc, char **argv, int event)
+{
+ int ret = 0, prio = 0, msg_size = 0;
+ char k[16];
+ struct rtattr *tail,*tail2;
+ struct action_util *a = NULL;
+ struct {
+ struct nlmsghdr n;
+ struct tcamsg t;
+ char buf[MAX_MSG];
+ } req;
+
+ req.t.tca_family = AF_UNSPEC;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
+
+ tail = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
+ tail2 = NLMSG_TAIL(&req.n);
+
+ strncpy(k, *argv, sizeof (k) - 1);
+#ifdef CONFIG_GACT
+ if (!gact_ld) {
+ get_action_kind("gact");
+ }
+#endif
+ a = get_action_kind(k);
+ if (NULL == a) {
+ fprintf(stderr,"bad action %s\n",k);
+ goto bad_val;
+ }
+ if (strcmp(a->id, k) != 0) {
+ fprintf(stderr,"bad action %s\n",k);
+ goto bad_val;
+ }
+ strncpy(k, *argv, sizeof (k) - 1);
+
+ addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
+ addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
+ tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
+ tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
+
+ msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr));
+
+ if (event == RTM_GETACTION) {
+ if (rtnl_dump_request(&rth, event, (void *)&req.t, msg_size) < 0) {
+ perror("Cannot send dump request");
+ return 1;
+ }
+ ret = rtnl_dump_filter(&rth, print_action, stdout, NULL, NULL);
+ }
+
+ if (event == RTM_DELACTION) {
+ req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len);
+ req.n.nlmsg_type = RTM_DELACTION;
+ req.n.nlmsg_flags |= NLM_F_ROOT;
+ req.n.nlmsg_flags |= NLM_F_REQUEST;
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
+ fprintf(stderr, "We have an error flushing\n");
+ return 1;
+ }
+
+ }
+
+bad_val:
+
+ return ret;
+}
+
+int do_action(int argc, char **argv)
+{
+
+ int ret = 0;
+
+ while (argc > 0) {
+
+ if (matches(*argv, "add") == 0) {
+ ret = tc_action_modify(RTM_NEWACTION, NLM_F_EXCL|NLM_F_CREATE, &argc, &argv);
+ } else if (matches(*argv, "change") == 0 ||
+ matches(*argv, "replace") == 0) {
+ ret = tc_action_modify(RTM_NEWACTION, NLM_F_CREATE|NLM_F_REPLACE, &argc, &argv);
+ } else if (matches(*argv, "delete") == 0) {
+ argc -=1;
+ argv +=1;
+ ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv);
+ } else if (matches(*argv, "get") == 0) {
+ argc -=1;
+ argv +=1;
+ ret = tc_action_gd(RTM_GETACTION, 0, &argc, &argv);
+ } else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
+ || matches(*argv, "lst") == 0) {
+ if (argc <= 2) {
+ act_usage();
+ return -1;
+ }
+ return tc_act_list_or_flush(argc-2, argv+2, RTM_GETACTION);
+ } else if (matches(*argv, "flush") == 0) {
+ if (argc <= 2) {
+ act_usage();
+ return -1;
+ }
+ return tc_act_list_or_flush(argc-2, argv+2, RTM_DELACTION);
+ } else if (matches(*argv, "help") == 0) {
+ act_usage();
+ return -1;
+ } else {
+
+ ret = -1;
+ }
+
+ if (ret < 0) {
+ fprintf(stderr, "Command \"%s\" is unknown, try \"tc actions help\".\n", *argv);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/tc/m_ematch.c b/tc/m_ematch.c
new file mode 100644
index 0000000..7f79a06
--- /dev/null
+++ b/tc/m_ematch.c
@@ -0,0 +1,568 @@
+/*
+ * m_ematch.c Extended Matches
+ *
+ * This program is free software; you can distribute 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: Thomas Graf <tgraf@suug.ch>
+ */
+
+#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 <dlfcn.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "utils.h"
+#include "tc_util.h"
+#include "m_ematch.h"
+
+#define EMATCH_MAP "/etc/iproute2/ematch_map"
+
+static struct ematch_util *ematch_list;
+
+/* export to bison parser */
+int ematch_argc;
+char **ematch_argv;
+char *ematch_err = NULL;
+struct ematch *ematch_root;
+
+static int begin_argc;
+static char **begin_argv;
+
+static inline void map_warning(int num, char *kind)
+{
+ fprintf(stderr,
+ "Error: Unable to find ematch \"%s\" in %s\n" \
+ "Please assign a unique ID to the ematch kind the suggested " \
+ "entry is:\n" \
+ "\t%d\t%s\n",
+ kind, EMATCH_MAP, num, kind);
+}
+
+static int lookup_map(__u16 num, char *dst, int len, const char *file)
+{
+ int err = -EINVAL;
+ char buf[512];
+ FILE *fd = fopen(file, "r");
+
+ if (fd == NULL)
+ return -errno;
+
+ while (fgets(buf, sizeof(buf), fd)) {
+ char namebuf[512], *p = buf;
+ int id;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '#' || *p == '\n' || *p == 0)
+ continue;
+
+ if (sscanf(p, "%d %s", &id, namebuf) != 2) {
+ fprintf(stderr, "ematch map %s corrupted at %s\n",
+ file, p);
+ goto out;
+ }
+
+ if (id == num) {
+ if (dst)
+ strncpy(dst, namebuf, len - 1);
+ err = 0;
+ goto out;
+ }
+ }
+
+ err = -ENOENT;
+out:
+ fclose(fd);
+ return err;
+}
+
+static int lookup_map_id(char *kind, int *dst, const char *file)
+{
+ int err = -EINVAL;
+ char buf[512];
+ FILE *fd = fopen(file, "r");
+
+ if (fd == NULL)
+ return -errno;
+
+ while (fgets(buf, sizeof(buf), fd)) {
+ char namebuf[512], *p = buf;
+ int id;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '#' || *p == '\n' || *p == 0)
+ continue;
+
+ if (sscanf(p, "%d %s", &id, namebuf) != 2) {
+ fprintf(stderr, "ematch map %s corrupted at %s\n",
+ file, p);
+ goto out;
+ }
+
+ if (!strcasecmp(namebuf, kind)) {
+ if (dst)
+ *dst = id;
+ err = 0;
+ goto out;
+ }
+ }
+
+ err = -ENOENT;
+ *dst = 0;
+out:
+ fclose(fd);
+ return err;
+}
+
+static struct ematch_util *get_ematch_kind(char *kind)
+{
+ static void *body;
+ void *dlh;
+ char buf[256];
+ struct ematch_util *e;
+
+ for (e = ematch_list; e; e = e->next) {
+ if (strcmp(e->kind, kind) == 0)
+ return e;
+ }
+
+ snprintf(buf, sizeof(buf), "em_%s.so", kind);
+ dlh = dlopen(buf, RTLD_LAZY);
+ if (dlh == NULL) {
+ dlh = body;
+ if (dlh == NULL) {
+ dlh = body = dlopen(NULL, RTLD_LAZY);
+ if (dlh == NULL)
+ return NULL;
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "%s_ematch_util", kind);
+ e = dlsym(dlh, buf);
+ if (e == NULL)
+ return NULL;
+
+ e->next = ematch_list;
+ ematch_list = e;
+
+ return e;
+}
+
+static struct ematch_util *get_ematch_kind_num(__u16 kind)
+{
+ char name[32];
+
+ if (lookup_map(kind, name, sizeof(name), EMATCH_MAP) < 0)
+ return NULL;
+
+ return get_ematch_kind(name);
+}
+
+static int parse_tree(struct nlmsghdr *n, struct ematch *tree)
+{
+ int index = 1;
+ struct ematch *t;
+
+ for (t = tree; t; t = t->next) {
+ struct rtattr *tail = NLMSG_TAIL(n);
+ struct tcf_ematch_hdr hdr = {
+ .flags = t->relation
+ };
+
+ if (t->inverted)
+ hdr.flags |= TCF_EM_INVERT;
+
+ addattr_l(n, MAX_MSG, index++, NULL, 0);
+
+ if (t->child) {
+ __u32 r = t->child_ref;
+ addraw_l(n, MAX_MSG, &hdr, sizeof(hdr));
+ addraw_l(n, MAX_MSG, &r, sizeof(r));
+ } else {
+ int num = 0, err;
+ char buf[64];
+ struct ematch_util *e;
+
+ if (t->args == NULL)
+ return -1;
+
+ strncpy(buf, (char*) t->args->data, sizeof(buf)-1);
+ e = get_ematch_kind(buf);
+ if (e == NULL) {
+ fprintf(stderr, "Unknown ematch \"%s\"\n",
+ buf);
+ return -1;
+ }
+
+ err = lookup_map_id(buf, &num, EMATCH_MAP);
+ if (err < 0) {
+ if (err == -ENOENT)
+ map_warning(e->kind_num, buf);
+ return err;
+ }
+
+ hdr.kind = num;
+ if (e->parse_eopt(n, &hdr, t->args->next) < 0)
+ return -1;
+ }
+
+ tail->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail;
+ }
+
+ return 0;
+}
+
+static int flatten_tree(struct ematch *head, struct ematch *tree)
+{
+ int i, count = 0;
+ struct ematch *t;
+
+ for (;;) {
+ count++;
+
+ if (tree->child) {
+ for (t = head; t->next; t = t->next);
+ t->next = tree->child;
+ count += flatten_tree(head, tree->child);
+ }
+
+ if (tree->relation == 0)
+ break;
+
+ tree = tree->next;
+ }
+
+ for (i = 0, t = head; t; t = t->next, i++)
+ t->index = i;
+
+ for (t = head; t; t = t->next)
+ if (t->child)
+ t->child_ref = t->child->index;
+
+ return count;
+}
+
+int em_parse_error(int err, struct bstr *args, struct bstr *carg,
+ struct ematch_util *e, char *fmt, ...)
+{
+ va_list a;
+
+ va_start(a, fmt);
+ vfprintf(stderr, fmt, a);
+ va_end(a);
+
+ if (ematch_err)
+ fprintf(stderr, ": %s\n... ", ematch_err);
+ else
+ fprintf(stderr, "\n... ");
+
+ while (ematch_argc < begin_argc) {
+ if (ematch_argc == (begin_argc - 1))
+ fprintf(stderr, ">>%s<< ", *begin_argv);
+ else
+ fprintf(stderr, "%s ", *begin_argv);
+ begin_argv++;
+ begin_argc--;
+ }
+
+ fprintf(stderr, "...\n");
+
+ if (args) {
+ fprintf(stderr, "... %s(", e->kind);
+ while (args) {
+ fprintf(stderr, "%s", args == carg ? ">>" : "");
+ bstr_print(stderr, args, 1);
+ fprintf(stderr, "%s%s", args == carg ? "<<" : "",
+ args->next ? " " : "");
+ args = args->next;
+ }
+ fprintf(stderr, ")...\n");
+
+ }
+
+ if (e == NULL) {
+ fprintf(stderr,
+ "Usage: EXPR\n" \
+ "where: EXPR := TERM [ { and | or } EXPR ]\n" \
+ " TERM := [ not ] { MATCH | '(' EXPR ')' }\n" \
+ " MATCH := module '(' ARGS ')'\n" \
+ " ARGS := ARG1 ARG2 ...\n" \
+ "\n" \
+ "Example: a(x y) and not (b(x) or c(x y z))\n");
+ } else
+ e->print_usage(stderr);
+
+ return -err;
+}
+
+static inline void free_ematch_err(void)
+{
+ if (ematch_err) {
+ free(ematch_err);
+ ematch_err = NULL;
+ }
+}
+
+extern int ematch_parse(void);
+
+int parse_ematch(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+ begin_argc = ematch_argc = *argc_p;
+ begin_argv = ematch_argv = *argv_p;
+
+ if (ematch_parse()) {
+ int err = em_parse_error(EINVAL, NULL, NULL, NULL,
+ "Parse error");
+ free_ematch_err();
+ return err;
+ }
+
+ free_ematch_err();
+
+ /* undo look ahead by parser */
+ ematch_argc++;
+ ematch_argv--;
+
+ if (ematch_root) {
+ struct rtattr *tail, *tail_list;
+
+ struct tcf_ematch_tree_hdr hdr = {
+ .nmatches = flatten_tree(ematch_root, ematch_root),
+ .progid = TCF_EM_PROG_TC
+ };
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+ addattr_l(n, MAX_MSG, TCA_EMATCH_TREE_HDR, &hdr, sizeof(hdr));
+
+ tail_list = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, TCA_EMATCH_TREE_LIST, NULL, 0);
+
+ if (parse_tree(n, ematch_root) < 0)
+ return -1;
+
+ tail_list->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail_list;
+ tail->rta_len = (void*) NLMSG_TAIL(n) - (void*) tail;
+ }
+
+ *argc_p = ematch_argc;
+ *argv_p = ematch_argv;
+
+ return 0;
+}
+
+static int print_ematch_seq(FILE *fd, struct rtattr **tb, int start,
+ int prefix)
+{
+ int n, i = start;
+ struct tcf_ematch_hdr *hdr;
+ int dlen;
+ void *data;
+
+ for (;;) {
+ if (tb[i] == NULL)
+ return -1;
+
+ dlen = RTA_PAYLOAD(tb[i]) - sizeof(*hdr);
+ data = (void *) RTA_DATA(tb[i]) + sizeof(*hdr);
+
+ if (dlen < 0)
+ return -1;
+
+ hdr = RTA_DATA(tb[i]);
+
+ if (hdr->flags & TCF_EM_INVERT)
+ fprintf(fd, "NOT ");
+
+ if (hdr->kind == 0) {
+ __u32 ref;
+
+ if (dlen < sizeof(__u32))
+ return -1;
+
+ ref = *(__u32 *) data;
+ fprintf(fd, "(\n");
+ for (n = 0; n <= prefix; n++)
+ fprintf(fd, " ");
+ if (print_ematch_seq(fd, tb, ref + 1, prefix + 1) < 0)
+ return -1;
+ for (n = 0; n < prefix; n++)
+ fprintf(fd, " ");
+ fprintf(fd, ") ");
+
+ } else {
+ struct ematch_util *e;
+
+ e = get_ematch_kind_num(hdr->kind);
+ if (e == NULL)
+ fprintf(fd, "[unknown ematch %d]\n",
+ hdr->kind);
+ else {
+ fprintf(fd, "%s(", e->kind);
+ if (e->print_eopt(fd, hdr, data, dlen) < 0)
+ return -1;
+ fprintf(fd, ")\n");
+ }
+ if (hdr->flags & TCF_EM_REL_MASK)
+ for (n = 0; n < prefix; n++)
+ fprintf(fd, " ");
+ }
+
+ switch (hdr->flags & TCF_EM_REL_MASK) {
+ case TCF_EM_REL_AND:
+ fprintf(fd, "AND ");
+ break;
+
+ case TCF_EM_REL_OR:
+ fprintf(fd, "OR ");
+ break;
+
+ default:
+ return 0;
+ }
+
+ i++;
+ }
+
+ return 0;
+}
+
+static int print_ematch_list(FILE *fd, struct tcf_ematch_tree_hdr *hdr,
+ struct rtattr *rta)
+{
+ int err = -1;
+ struct rtattr **tb;
+
+ tb = malloc((hdr->nmatches + 1) * sizeof(struct rtattr *));
+ if (tb == NULL)
+ return -1;
+
+ if (parse_rtattr_nested(tb, hdr->nmatches, rta) < 0)
+ goto errout;
+
+ fprintf(fd, "\n ");
+ if (print_ematch_seq(fd, tb, 1, 1) < 0)
+ goto errout;
+
+ err = 0;
+errout:
+ free(tb);
+ return err;
+}
+
+int print_ematch(FILE *fd, const struct rtattr *rta)
+{
+ struct rtattr *tb[TCA_EMATCH_TREE_MAX+1];
+ struct tcf_ematch_tree_hdr *hdr;
+
+ if (parse_rtattr_nested(tb, TCA_EMATCH_TREE_MAX, rta) < 0)
+ return -1;
+
+ if (tb[TCA_EMATCH_TREE_HDR] == NULL) {
+ fprintf(stderr, "Missing ematch tree header\n");
+ return -1;
+ }
+
+ if (tb[TCA_EMATCH_TREE_LIST] == NULL) {
+ fprintf(stderr, "Missing ematch tree list\n");
+ return -1;
+ }
+
+ if (RTA_PAYLOAD(tb[TCA_EMATCH_TREE_HDR]) < sizeof(*hdr)) {
+ fprintf(stderr, "Ematch tree header size mismatch\n");
+ return -1;
+ }
+
+ hdr = RTA_DATA(tb[TCA_EMATCH_TREE_HDR]);
+
+ return print_ematch_list(fd, hdr, tb[TCA_EMATCH_TREE_LIST]);
+}
+
+struct bstr * bstr_alloc(const char *text)
+{
+ struct bstr *b = calloc(1, sizeof(*b));
+
+ if (b == NULL)
+ return NULL;
+
+ b->data = strdup(text);
+ if (b->data == NULL) {
+ free(b);
+ return NULL;
+ }
+
+ b->len = strlen(text);
+
+ return b;
+}
+
+unsigned long bstrtoul(const struct bstr *b)
+{
+ char *inv = NULL;
+ unsigned long l;
+ char buf[b->len+1];
+
+ memcpy(buf, b->data, b->len);
+ buf[b->len] = '\0';
+
+ l = strtoul(buf, &inv, 0);
+ if (l == ULONG_MAX || inv == buf)
+ return ULONG_MAX;
+
+ return l;
+}
+
+void bstr_print(FILE *fd, const struct bstr *b, int ascii)
+{
+ int i;
+ char *s = b->data;
+
+ if (ascii)
+ for (i = 0; i < b->len; i++)
+ fprintf(fd, "%c", isprint(s[i]) ? s[i] : '.');
+ else {
+ for (i = 0; i < b->len; i++)
+ fprintf(fd, "%02x", s[i]);
+ fprintf(fd, "\"");
+ for (i = 0; i < b->len; i++)
+ fprintf(fd, "%c", isprint(s[i]) ? s[i] : '.');
+ fprintf(fd, "\"");
+ }
+}
+
+void print_ematch_tree(const struct ematch *tree)
+{
+ const struct ematch *t;
+
+ for (t = tree; t; t = t->next) {
+ if (t->inverted)
+ printf("NOT ");
+
+ if (t->child) {
+ printf("(");
+ print_ematch_tree(t->child);
+ printf(")");
+ } else {
+ struct bstr *b;
+ for (b = t->args; b; b = b->next)
+ printf("%s%s", b->data, b->next ? " " : "");
+ }
+
+ if (t->relation == TCF_EM_REL_AND)
+ printf(" AND ");
+ else if (t->relation == TCF_EM_REL_OR)
+ printf(" OR ");
+ }
+}
diff --git a/tc/m_ematch.h b/tc/m_ematch.h
new file mode 100644
index 0000000..5036e9b
--- /dev/null
+++ b/tc/m_ematch.h
@@ -0,0 +1,111 @@
+#ifndef __TC_EMATCH_H_
+#define __TC_EMATCH_H_
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+#define EMATCHKINDSIZ 16
+
+struct bstr
+{
+ char *data;
+ unsigned int len;
+ int quoted;
+ struct bstr *next;
+};
+
+extern struct bstr * bstr_alloc(const char *text);
+
+static inline struct bstr * bstr_new(char *data, unsigned int len)
+{
+ struct bstr *b = calloc(1, sizeof(*b));
+
+ if (b == NULL)
+ return NULL;
+
+ b->data = data;
+ b->len = len;
+
+ return b;
+}
+
+static inline int bstrcmp(struct bstr *b, const char *text)
+{
+ int len = strlen(text);
+ int d = b->len - len;
+
+ if (d == 0)
+ return strncmp(b->data, text, len);
+
+ return d;
+}
+
+static inline struct bstr *bstr_next(struct bstr *b)
+{
+ return b->next;
+}
+
+extern unsigned long bstrtoul(const struct bstr *b);
+extern void bstr_print(FILE *fd, const struct bstr *b, int ascii);
+
+
+struct ematch
+{
+ struct bstr *args;
+ int index;
+ int inverted;
+ int relation;
+ int child_ref;
+ struct ematch *child;
+ struct ematch *next;
+};
+
+static inline struct ematch * new_ematch(struct bstr *args, int inverted)
+{
+ struct ematch *e = calloc(1, sizeof(*e));
+
+ if (e == NULL)
+ return NULL;
+
+ e->args = args;
+ e->inverted = inverted;
+
+ return e;
+}
+
+extern void print_ematch_tree(const struct ematch *tree);
+
+
+struct ematch_util
+{
+ char kind[EMATCHKINDSIZ];
+ int kind_num;
+ int (*parse_eopt)(struct nlmsghdr *,struct tcf_ematch_hdr *,
+ struct bstr *);
+ int (*print_eopt)(FILE *, struct tcf_ematch_hdr *, void *, int);
+ void (*print_usage)(FILE *);
+ struct ematch_util *next;
+};
+
+static inline int parse_layer(struct bstr *b)
+{
+ if (*((char *) b->data) == 'l')
+ return TCF_LAYER_LINK;
+ else if (*((char *) b->data) == 'n')
+ return TCF_LAYER_NETWORK;
+ else if (*((char *) b->data) == 't')
+ return TCF_LAYER_TRANSPORT;
+ else
+ return INT_MAX;
+}
+
+extern int em_parse_error(int err, struct bstr *args, struct bstr *carg,
+ struct ematch_util *, char *fmt, ...);
+extern int print_ematch(FILE *, const struct rtattr *);
+extern int parse_ematch(int *, char ***, int, struct nlmsghdr *);
+
+#endif
diff --git a/tc/m_estimator.c b/tc/m_estimator.c
new file mode 100644
index 0000000..a9e5dbc
--- /dev/null
+++ b/tc/m_estimator.c
@@ -0,0 +1,64 @@
+/*
+ * m_estimator.c Parse/print estimator module options.
+ *
+ * This program is free software; you can u32istribute 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 "utils.h"
+#include "tc_util.h"
+
+static void est_help(void);
+
+static void est_help(void)
+{
+ fprintf(stderr, "Usage: ... estimator INTERVAL TIME-CONST\n");
+ fprintf(stderr, " INTERVAL is interval between measurements\n");
+ fprintf(stderr, " TIME-CONST is averaging time constant\n");
+ fprintf(stderr, "Example: ... est 1sec 8sec\n");
+ return;
+}
+
+int parse_estimator(int *p_argc, char ***p_argv, struct tc_estimator *est)
+{
+ int argc = *p_argc;
+ char **argv = *p_argv;
+ unsigned A, time_const;
+
+ NEXT_ARG();
+ if (est->ewma_log)
+ duparg("estimator", *argv);
+ if (matches(*argv, "help") == 0)
+ est_help();
+ if (get_time(&A, *argv))
+ invarg("estimator", "invalid estimator interval");
+ NEXT_ARG();
+ if (matches(*argv, "help") == 0)
+ est_help();
+ if (get_time(&time_const, *argv))
+ invarg("estimator", "invalid estimator time constant");
+ if (tc_setup_estimator(A, time_const, est) < 0) {
+ fprintf(stderr, "Error: estimator parameters are out of range.\n");
+ return -1;
+ }
+ if (show_raw)
+ fprintf(stderr, "[estimator i=%u e=%u]\n", est->interval, est->ewma_log);
+ *p_argc = argc;
+ *p_argv = argv;
+ return 0;
+}
diff --git a/tc/m_gact.c b/tc/m_gact.c
new file mode 100644
index 0000000..9f07851
--- /dev/null
+++ b/tc/m_gact.c
@@ -0,0 +1,254 @@
+/*
+ * m_gact.c generic actions module
+ *
+ * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
+ *
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+#include <linux/tc_act/tc_gact.h>
+
+/* define to turn on probablity stuff */
+
+#ifdef CONFIG_GACT_PROB
+static const char *prob_n2a(int p)
+{
+ if (p == PGACT_NONE)
+ return "none";
+ if (p == PGACT_NETRAND)
+ return "netrand";
+ if (p == PGACT_DETERM)
+ return "determ";
+ return "none";
+}
+#endif
+
+static void
+explain(void)
+{
+#ifdef CONFIG_GACT_PROB
+ fprintf(stderr, "Usage: ... gact <ACTION> [RAND] [INDEX]\n");
+ fprintf(stderr,
+ "Where: \tACTION := reclassify | drop | continue | pass \n"
+ "\tRAND := random <RANDTYPE> <ACTION> <VAL>\n"
+ "\tRANDTYPE := netrand | determ\n"
+ "\tVAL : = value not exceeding 10000\n"
+ "\tINDEX := index value used\n"
+ "\n");
+#else
+ fprintf(stderr, "Usage: ... gact <ACTION> [INDEX]\n");
+ fprintf(stderr,
+ "Where: \tACTION := reclassify | drop | continue | pass \n"
+ "\tINDEX := index value used\n"
+ "\n");
+#endif
+}
+
+
+static void
+usage(void)
+{
+ explain();
+ exit(-1);
+}
+
+int
+get_act(char ***argv_p)
+{
+ char **argv = *argv_p;
+
+ if (matches(*argv, "reclassify") == 0) {
+ return TC_ACT_RECLASSIFY;
+ } else if (matches(*argv, "drop") == 0 || matches(*argv, "shot") == 0) {
+ return TC_ACT_SHOT;
+ } else if (matches(*argv, "continue") == 0) {
+ return TC_ACT_UNSPEC;
+ } else if (matches(*argv, "pipe") == 0) {
+ return TC_ACT_PIPE;
+ } else if (matches(*argv, "pass") == 0 || matches(*argv, "ok") == 0) {
+ return TC_ACT_OK;
+ } else {
+ fprintf(stderr,"bad action type %s\n",*argv);
+ return -10;
+ }
+}
+
+int
+parse_gact(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int ok = 0;
+ int action = TC_POLICE_RECLASSIFY;
+ struct tc_gact p;
+#ifdef CONFIG_GACT_PROB
+ int rd = 0;
+ struct tc_gact_p pp;
+#endif
+ struct rtattr *tail;
+
+ memset(&p, 0, sizeof (p));
+ p.action = TC_POLICE_RECLASSIFY;
+
+ if (argc < 0)
+ return -1;
+
+
+ if (matches(*argv, "gact") == 0) {
+ ok++;
+ } else {
+ action = get_act(&argv);
+ if (action != -10) {
+ p.action = action;
+ ok++;
+ } else {
+ explain();
+ return action;
+ }
+ }
+
+ if (ok) {
+ argc--;
+ argv++;
+ }
+
+#ifdef CONFIG_GACT_PROB
+ if (ok && argc > 0) {
+ if (matches(*argv, "random") == 0) {
+ rd = 1;
+ NEXT_ARG();
+ if (matches(*argv, "netrand") == 0) {
+ NEXT_ARG();
+ pp.ptype = PGACT_NETRAND;
+ } else if (matches(*argv, "determ") == 0) {
+ NEXT_ARG();
+ pp.ptype = PGACT_DETERM;
+ } else {
+ fprintf(stderr, "Illegal \"random type\"\n");
+ return -1;
+ }
+
+ action = get_act(&argv);
+ if (action != -10) { /* FIXME */
+ pp.paction = action;
+ } else {
+ explain();
+ return -1;
+ }
+ argc--;
+ argv++;
+ if (get_u16(&pp.pval, *argv, 10)) {
+ fprintf(stderr, "Illegal probability val 0x%x\n",pp.pval);
+ return -1;
+ }
+ if (pp.pval > 10000) {
+ fprintf(stderr, "Illegal probability val 0x%x\n",pp.pval);
+ return -1;
+ }
+ argc--;
+ argv++;
+ } else if (matches(*argv, "help") == 0) {
+ usage();
+ }
+ }
+#endif
+
+ if (argc > 0) {
+ if (matches(*argv, "index") == 0) {
+ NEXT_ARG();
+ if (get_u32(&p.index, *argv, 10)) {
+ fprintf(stderr, "Illegal \"index\"\n");
+ return -1;
+ }
+ argc--;
+ argv++;
+ ok++;
+ } else if (matches(*argv, "help") == 0) {
+ usage();
+ }
+ }
+
+ if (!ok)
+ return -1;
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+ addattr_l(n, MAX_MSG, TCA_GACT_PARMS, &p, sizeof (p));
+#ifdef CONFIG_GACT_PROB
+ if (rd) {
+ addattr_l(n, MAX_MSG, TCA_GACT_PROB, &pp, sizeof (pp));
+ }
+#endif
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+}
+
+int
+print_gact(struct action_util *au,FILE * f, struct rtattr *arg)
+{
+ SPRINT_BUF(b1);
+#ifdef CONFIG_GACT_PROB
+ SPRINT_BUF(b2);
+ struct tc_gact_p *pp = NULL;
+ struct tc_gact_p pp_dummy;
+#endif
+ struct tc_gact *p = NULL;
+ struct rtattr *tb[TCA_GACT_MAX + 1];
+
+ if (arg == NULL)
+ return -1;
+
+ parse_rtattr_nested(tb, TCA_GACT_MAX, arg);
+
+ if (tb[TCA_GACT_PARMS] == NULL) {
+ fprintf(f, "[NULL gact parameters]");
+ return -1;
+ }
+ p = RTA_DATA(tb[TCA_GACT_PARMS]);
+
+ fprintf(f, "gact action %s", action_n2a(p->action, b1, sizeof (b1)));
+#ifdef CONFIG_GACT_PROB
+ if (NULL != tb[TCA_GACT_PROB]) {
+ pp = RTA_DATA(tb[TCA_GACT_PROB]);
+ } else {
+ /* need to keep consistent output */
+ memset(&pp_dummy, 0, sizeof (pp_dummy));
+ pp = &pp_dummy;
+ }
+ fprintf(f, "\n\t random type %s %s val %d",prob_n2a(pp->ptype), action_n2a(pp->paction, b2, sizeof (b2)), pp->pval);
+#endif
+ fprintf(f, "\n\t index %d ref %d bind %d",p->index, p->refcnt, p->bindcnt);
+ if (show_stats) {
+ if (tb[TCA_GACT_TM]) {
+ struct tcf_t *tm = RTA_DATA(tb[TCA_GACT_TM]);
+ print_tm(f,tm);
+ }
+ }
+ fprintf(f, "\n ");
+ return 0;
+}
+
+struct action_util gact_action_util = {
+ .id = "gact",
+ .parse_aopt = parse_gact,
+ .print_aopt = print_gact,
+};
diff --git a/tc/m_ipt.c b/tc/m_ipt.c
new file mode 100644
index 0000000..99d9965
--- /dev/null
+++ b/tc/m_ipt.c
@@ -0,0 +1,620 @@
+/*
+ * m_ipt.c iptables based targets
+ * utilities mostly ripped from iptables <duh, its the linux way>
+ *
+ * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
+ */
+
+#include <syslog.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <linux/if.h>
+#include <iptables.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include "utils.h"
+#include "tc_util.h"
+#include <linux/tc_act/tc_ipt.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <getopt.h>
+#include <errno.h>
+#include <string.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+static const char *pname = "tc-ipt";
+static const char *tname = "mangle";
+static const char *pversion = "0.1";
+
+static const char *ipthooks[] = {
+ "NF_IP_PRE_ROUTING",
+ "NF_IP_LOCAL_IN",
+ "NF_IP_FORWARD",
+ "NF_IP_LOCAL_OUT",
+ "NF_IP_POST_ROUTING",
+};
+
+static struct option original_opts[] = {
+ {"jump", 1, 0, 'j'},
+ {0, 0, 0, 0}
+};
+
+static struct iptables_target *t_list = NULL;
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+#define OPTION_OFFSET 256
+
+char *lib_dir;
+
+void
+register_target(struct iptables_target *me)
+{
+/* fprintf(stderr, "\nDummy register_target %s \n", me->name);
+*/
+ me->next = t_list;
+ t_list = me;
+
+}
+
+void
+xtables_register_target(struct iptables_target *me)
+{
+ me->next = t_list;
+ t_list = me;
+}
+
+void
+exit_tryhelp(int status)
+{
+ fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+ pname, pname);
+ exit(status);
+}
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ fprintf(stderr, "%s v%s: ", pname, pversion);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ if (status == PARAMETER_PROBLEM)
+ exit_tryhelp(status);
+ if (status == VERSION_PROBLEM)
+ fprintf(stderr,
+ "Perhaps iptables or your kernel needs to be upgraded.\n");
+ exit(status);
+}
+
+/* stolen from iptables 1.2.11
+They should really have them as a library so i can link to them
+Email them next time i remember
+*/
+
+char *
+addr_to_dotted(const struct in_addr *addrp)
+{
+ static char buf[20];
+ const unsigned char *bytep;
+
+ bytep = (const unsigned char *) &(addrp->s_addr);
+ sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
+ return buf;
+}
+
+int string_to_number_ll(const char *s, unsigned long long min,
+ unsigned long long max,
+ unsigned long long *ret)
+{
+ unsigned long long number;
+ char *end;
+
+ /* Handle hex, octal, etc. */
+ errno = 0;
+ number = strtoull(s, &end, 0);
+ if (*end == '\0' && end != s) {
+ /* we parsed a number, let's see if we want this */
+ if (errno != ERANGE && min <= number && (!max || number <= max)) {
+ *ret = number;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int string_to_number_l(const char *s, unsigned long min, unsigned long max,
+ unsigned long *ret)
+{
+ int result;
+ unsigned long long number;
+
+ result = string_to_number_ll(s, min, max, &number);
+ *ret = (unsigned long)number;
+
+ return result;
+}
+
+int string_to_number(const char *s, unsigned int min, unsigned int max,
+ unsigned int *ret)
+{
+ int result;
+ unsigned long number;
+
+ result = string_to_number_l(s, min, max, &number);
+ *ret = (unsigned int)number;
+
+ return result;
+}
+
+static void free_opts(struct option *local_opts)
+{
+ if (local_opts != original_opts) {
+ free(local_opts);
+ opts = original_opts;
+ global_option_offset = 0;
+ }
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+ unsigned int *option_offset)
+{
+ struct option *merge;
+ unsigned int num_old, num_new, i;
+
+ for (num_old = 0; oldopts[num_old].name; num_old++) ;
+ for (num_new = 0; newopts[num_new].name; num_new++) ;
+
+ *option_offset = global_option_offset + OPTION_OFFSET;
+
+ merge = malloc(sizeof (struct option) * (num_new + num_old + 1));
+ memcpy(merge, oldopts, num_old * sizeof (struct option));
+ for (i = 0; i < num_new; i++) {
+ merge[num_old + i] = newopts[i];
+ merge[num_old + i].val += *option_offset;
+ }
+ memset(merge + num_old + num_new, 0, sizeof (struct option));
+
+ return merge;
+}
+
+static void *
+fw_calloc(size_t count, size_t size)
+{
+ void *p;
+
+ if ((p = (void *) calloc(count, size)) == NULL) {
+ perror("iptables: calloc failed");
+ exit(1);
+ }
+ return p;
+}
+
+static struct iptables_target *
+find_t(char *name)
+{
+ struct iptables_target *m;
+ for (m = t_list; m; m = m->next) {
+ if (strcmp(m->name, name) == 0)
+ return m;
+ }
+
+ return NULL;
+}
+
+static struct iptables_target *
+get_target_name(const char *name)
+{
+ void *handle;
+ char *error;
+ char *new_name, *lname;
+ struct iptables_target *m;
+ char path[strlen(lib_dir) + sizeof ("/libipt_.so") + strlen(name)];
+
+#ifdef NO_SHARED_LIBS
+ return NULL;
+#endif
+
+ new_name = malloc(strlen(name) + 1);
+ lname = malloc(strlen(name) + 1);
+ if (new_name)
+ memset(new_name, '\0', strlen(name) + 1);
+ else
+ exit_error(PARAMETER_PROBLEM, "get_target_name");
+
+ if (lname)
+ memset(lname, '\0', strlen(name) + 1);
+ else
+ exit_error(PARAMETER_PROBLEM, "get_target_name");
+
+ strcpy(new_name, name);
+ strcpy(lname, name);
+
+ if (isupper(lname[0])) {
+ int i;
+ for (i = 0; i < strlen(name); i++) {
+ lname[i] = tolower(lname[i]);
+ }
+ }
+
+ if (islower(new_name[0])) {
+ int i;
+ for (i = 0; i < strlen(new_name); i++) {
+ new_name[i] = toupper(new_name[i]);
+ }
+ }
+
+ /* try libxt_xx first */
+ sprintf(path, "%s/libxt_%s.so", lib_dir, new_name);
+ handle = dlopen(path, RTLD_LAZY);
+ if (!handle) {
+ /* try libipt_xx next */
+ sprintf(path, "%s/libipt_%s.so", lib_dir, new_name);
+ handle = dlopen(path, RTLD_LAZY);
+
+ if (!handle) {
+ sprintf(path, "%s/libxt_%s.so", lib_dir , lname);
+ handle = dlopen(path, RTLD_LAZY);
+ }
+
+ if (!handle) {
+ sprintf(path, "%s/libipt_%s.so", lib_dir , lname);
+ handle = dlopen(path, RTLD_LAZY);
+ }
+ /* ok, lets give up .. */
+ if (!handle) {
+ fputs(dlerror(), stderr);
+ printf("\n");
+ free(new_name);
+ return NULL;
+ }
+ }
+
+ m = dlsym(handle, new_name);
+ if ((error = dlerror()) != NULL) {
+ m = (struct iptables_target *) dlsym(handle, lname);
+ if ((error = dlerror()) != NULL) {
+ m = find_t(new_name);
+ if (NULL == m) {
+ m = find_t(lname);
+ if (NULL == m) {
+ fputs(error, stderr);
+ fprintf(stderr, "\n");
+ dlclose(handle);
+ free(new_name);
+ return NULL;
+ }
+ }
+ }
+ }
+
+ free(new_name);
+ return m;
+}
+
+
+struct in_addr *dotted_to_addr(const char *dotted)
+{
+ static struct in_addr addr;
+ unsigned char *addrp;
+ char *p, *q;
+ unsigned int onebyte;
+ int i;
+ char buf[20];
+
+ /* copy dotted string, because we need to modify it */
+ strncpy(buf, dotted, sizeof (buf) - 1);
+ addrp = (unsigned char *) &(addr.s_addr);
+
+ p = buf;
+ for (i = 0; i < 3; i++) {
+ if ((q = strchr(p, '.')) == NULL)
+ return (struct in_addr *) NULL;
+
+ *q = '\0';
+ if (string_to_number(p, 0, 255, &onebyte) == -1)
+ return (struct in_addr *) NULL;
+
+ addrp[i] = (unsigned char) onebyte;
+ p = q + 1;
+ }
+
+ /* we've checked 3 bytes, now we check the last one */
+ if (string_to_number(p, 0, 255, &onebyte) == -1)
+ return (struct in_addr *) NULL;
+
+ addrp[3] = (unsigned char) onebyte;
+
+ return &addr;
+}
+
+static void set_revision(char *name, u_int8_t revision)
+{
+ /* Old kernel sources don't have ".revision" field,
+ * but we stole a byte from name. */
+ name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0';
+ name[IPT_FUNCTION_MAXNAMELEN - 1] = revision;
+}
+
+/*
+ * we may need to check for version mismatch
+*/
+int
+build_st(struct iptables_target *target, struct ipt_entry_target *t)
+{
+ unsigned int nfcache = 0;
+
+ if (target) {
+ size_t size;
+
+ size =
+ IPT_ALIGN(sizeof (struct ipt_entry_target)) + target->size;
+
+ if (NULL == t) {
+ target->t = fw_calloc(1, size);
+ target->t->u.target_size = size;
+
+ if (target->init != NULL)
+ target->init(target->t, &nfcache);
+ set_revision(target->t->u.user.name, target->revision);
+ } else {
+ target->t = t;
+ }
+ strcpy(target->t->u.user.name, target->name);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int parse_ipt(struct action_util *a,int *argc_p,
+ char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+ struct iptables_target *m = NULL;
+ struct ipt_entry fw;
+ struct rtattr *tail;
+ int c;
+ int rargc = *argc_p;
+ char **argv = *argv_p;
+ int argc = 0, iargc = 0;
+ char k[16];
+ int res = -1;
+ int size = 0;
+ int iok = 0, ok = 0;
+ __u32 hook = 0, index = 0;
+ res = 0;
+
+ lib_dir = getenv("IPTABLES_LIB_DIR");
+ if (!lib_dir)
+ lib_dir = IPT_LIB_DIR;
+
+ {
+ int i;
+ for (i = 0; i < rargc; i++) {
+ if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) {
+ break;
+ }
+ }
+ iargc = argc = i;
+ }
+
+ if (argc <= 2) {
+ fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc);
+ return -1;
+ }
+
+ while (1) {
+ c = getopt_long(argc, argv, "j:", opts, NULL);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'j':
+ m = get_target_name(optarg);
+ if (NULL != m) {
+
+ if (0 > build_st(m, NULL)) {
+ printf(" %s error \n", m->name);
+ return -1;
+ }
+ opts =
+ merge_options(opts, m->extra_opts,
+ &m->option_offset);
+ } else {
+ fprintf(stderr," failed to find target %s\n\n", optarg);
+ return -1;
+ }
+ ok++;
+ break;
+
+ default:
+ memset(&fw, 0, sizeof (fw));
+ if (m) {
+ m->parse(c - m->option_offset, argv, 0,
+ &m->tflags, NULL, &m->t);
+ } else {
+ fprintf(stderr," failed to find target %s\n\n", optarg);
+ return -1;
+
+ }
+ ok++;
+ break;
+
+ }
+ }
+
+ if (iargc > optind) {
+ if (matches(argv[optind], "index") == 0) {
+ if (get_u32(&index, argv[optind + 1], 10)) {
+ fprintf(stderr, "Illegal \"index\"\n");
+ free_opts(opts);
+ return -1;
+ }
+ iok++;
+
+ optind += 2;
+ }
+ }
+
+ if (!ok && !iok) {
+ fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv);
+ return -1;
+ }
+
+ /* check that we passed the correct parameters to the target */
+ if (m)
+ m->final_check(m->tflags);
+
+ {
+ struct tcmsg *t = NLMSG_DATA(n);
+ if (t->tcm_parent != TC_H_ROOT
+ && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) {
+ hook = NF_IP_PRE_ROUTING;
+ } else {
+ hook = NF_IP_POST_ROUTING;
+ }
+ }
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+ fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]);
+ fprintf(stdout, "\ttarget: ");
+
+ if (m)
+ m->print(NULL, m->t, 0);
+ fprintf(stdout, " index %d\n", index);
+
+ if (strlen(tname) > 16) {
+ size = 16;
+ k[15] = 0;
+ } else {
+ size = 1 + strlen(tname);
+ }
+ strncpy(k, tname, size);
+
+ addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size);
+ addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4);
+ addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4);
+ if (m)
+ addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size);
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+
+ argc -= optind;
+ argv += optind;
+ *argc_p = rargc - iargc;
+ *argv_p = argv;
+
+ optind = 0;
+ free_opts(opts);
+ /* Clear flags if target will be used again */
+ m->tflags=0;
+ m->used=0;
+ /* Free allocated memory */
+ if (m->t)
+ free(m->t);
+
+
+ return 0;
+
+}
+
+static int
+print_ipt(struct action_util *au,FILE * f, struct rtattr *arg)
+{
+ struct rtattr *tb[TCA_IPT_MAX + 1];
+ struct ipt_entry_target *t = NULL;
+
+ if (arg == NULL)
+ return -1;
+
+ lib_dir = getenv("IPTABLES_LIB_DIR");
+ if (!lib_dir)
+ lib_dir = IPT_LIB_DIR;
+
+ parse_rtattr_nested(tb, TCA_IPT_MAX, arg);
+
+ if (tb[TCA_IPT_TABLE] == NULL) {
+ fprintf(f, "[NULL ipt table name ] assuming mangle ");
+ } else {
+ fprintf(f, "tablename: %s ",
+ (char *) RTA_DATA(tb[TCA_IPT_TABLE]));
+ }
+
+ if (tb[TCA_IPT_HOOK] == NULL) {
+ fprintf(f, "[NULL ipt hook name ]\n ");
+ return -1;
+ } else {
+ __u32 hook;
+ hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]);
+ fprintf(f, " hook: %s \n", ipthooks[hook]);
+ }
+
+ if (tb[TCA_IPT_TARG] == NULL) {
+ fprintf(f, "\t[NULL ipt target parameters ] \n");
+ return -1;
+ } else {
+ struct iptables_target *m = NULL;
+ t = RTA_DATA(tb[TCA_IPT_TARG]);
+ m = get_target_name(t->u.user.name);
+ if (NULL != m) {
+ if (0 > build_st(m, t)) {
+ fprintf(stderr, " %s error \n", m->name);
+ return -1;
+ }
+
+ opts =
+ merge_options(opts, m->extra_opts,
+ &m->option_offset);
+ } else {
+ fprintf(stderr, " failed to find target %s\n\n",
+ t->u.user.name);
+ return -1;
+ }
+ fprintf(f, "\ttarget ");
+ m->print(NULL, m->t, 0);
+ if (tb[TCA_IPT_INDEX] == NULL) {
+ fprintf(f, " [NULL ipt target index ]\n");
+ } else {
+ __u32 index;
+ index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]);
+ fprintf(f, " \n\tindex %d", index);
+ }
+
+ if (tb[TCA_IPT_CNT]) {
+ struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);;
+ fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
+ }
+ if (show_stats) {
+ if (tb[TCA_IPT_TM]) {
+ struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
+ print_tm(f,tm);
+ }
+ }
+ fprintf(f, " \n");
+
+ }
+ free_opts(opts);
+
+ return 0;
+}
+
+struct action_util ipt_action_util = {
+ .id = "ipt",
+ .parse_aopt = parse_ipt,
+ .print_aopt = print_ipt,
+};
+
diff --git a/tc/m_mirred.c b/tc/m_mirred.c
new file mode 100644
index 0000000..226df4d
--- /dev/null
+++ b/tc/m_mirred.c
@@ -0,0 +1,301 @@
+/*
+ * m_egress.c ingress/egress packet mirror/redir actions module
+ *
+ * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
+ *
+ * TODO: Add Ingress support
+ *
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+#include "tc_common.h"
+#include <linux/tc_act/tc_mirred.h>
+
+int mirred_d = 1;
+
+static void
+explain(void)
+{
+ fprintf(stderr, "Usage: mirred <DIRECTION> <ACTION> [index INDEX] <dev DEVICENAME> \n");
+ fprintf(stderr, "where: \n");
+ fprintf(stderr, "\tDIRECTION := <ingress | egress>\n");
+ fprintf(stderr, "\tACTION := <mirror | redirect>\n");
+ fprintf(stderr, "\tINDEX is the specific policy instance id\n");
+ fprintf(stderr, "\tDEVICENAME is the devicename \n");
+
+}
+
+static void
+usage(void)
+{
+ explain();
+ exit(-1);
+}
+
+char *mirred_n2a(int action)
+{
+ switch (action) {
+ case TCA_EGRESS_REDIR:
+ return "Egress Redirect";
+ case TCA_INGRESS_REDIR:
+ return "Ingress Redirect";
+ case TCA_EGRESS_MIRROR:
+ return "Egress Mirror";
+ case TCA_INGRESS_MIRROR:
+ return "Ingress Mirror";
+ default:
+ return "unknown";
+ }
+}
+
+int
+parse_egress(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int ok = 0, iok = 0, mirror=0,redir=0;
+ struct tc_mirred p;
+ struct rtattr *tail;
+ char d[16];
+
+ memset(d,0,sizeof(d)-1);
+ memset(&p,0,sizeof(struct tc_mirred));
+
+ while (argc > 0) {
+
+ if (matches(*argv, "action") == 0) {
+ break;
+ } else if (matches(*argv, "egress") == 0) {
+ NEXT_ARG();
+ ok++;
+ continue;
+ } else {
+
+ if (matches(*argv, "index") == 0) {
+ NEXT_ARG();
+ if (get_u32(&p.index, *argv, 10)) {
+ fprintf(stderr, "Illegal \"index\"\n");
+ return -1;
+ }
+ iok++;
+ if (!ok) {
+ argc--;
+ argv++;
+ break;
+ }
+ } else if(!ok) {
+ fprintf(stderr, "was expecting egress (%s)\n", *argv);
+ break;
+
+ } else if (!mirror && matches(*argv, "mirror") == 0) {
+ mirror=1;
+ if (redir) {
+ fprintf(stderr, "Cant have both mirror and redir\n");
+ return -1;
+ }
+ p.eaction = TCA_EGRESS_MIRROR;
+ p.action = TC_ACT_PIPE;
+ ok++;
+ } else if (!redir && matches(*argv, "redirect") == 0) {
+ redir=1;
+ if (mirror) {
+ fprintf(stderr, "Cant have both mirror and redir\n");
+ return -1;
+ }
+ p.eaction = TCA_EGRESS_REDIR;
+ p.action = TC_ACT_STOLEN;
+ ok++;
+ } else if ((redir || mirror) && matches(*argv, "dev") == 0) {
+ NEXT_ARG();
+ if (strlen(d))
+ duparg("dev", *argv);
+
+ strncpy(d, *argv, sizeof(d)-1);
+ argc--;
+ argv++;
+
+ break;
+
+ }
+ }
+
+ NEXT_ARG();
+ }
+
+ if (!ok && !iok) {
+ return -1;
+ }
+
+
+
+ if (d[0]) {
+ int idx;
+ ll_init_map(&rth);
+
+ if ((idx = ll_name_to_index(d)) == 0) {
+ fprintf(stderr, "Cannot find device \"%s\"\n", d);
+ return -1;
+ }
+
+ p.ifindex = idx;
+ }
+
+
+ if (argc && p.eaction == TCA_EGRESS_MIRROR) {
+
+ if (matches(*argv, "reclassify") == 0) {
+ p.action = TC_POLICE_RECLASSIFY;
+ NEXT_ARG();
+ } else if (matches(*argv, "pipe") == 0) {
+ p.action = TC_POLICE_PIPE;
+ NEXT_ARG();
+ } else if (matches(*argv, "drop") == 0 ||
+ matches(*argv, "shot") == 0) {
+ p.action = TC_POLICE_SHOT;
+ NEXT_ARG();
+ } else if (matches(*argv, "continue") == 0) {
+ p.action = TC_POLICE_UNSPEC;
+ NEXT_ARG();
+ } else if (matches(*argv, "pass") == 0) {
+ p.action = TC_POLICE_OK;
+ NEXT_ARG();
+ }
+
+ }
+
+ if (argc) {
+ if (iok && matches(*argv, "index") == 0) {
+ fprintf(stderr, "mirred: Illegal double index\n");
+ return -1;
+ } else {
+ if (matches(*argv, "index") == 0) {
+ NEXT_ARG();
+ if (get_u32(&p.index, *argv, 10)) {
+ fprintf(stderr, "mirred: Illegal \"index\"\n");
+ return -1;
+ }
+ argc--;
+ argv++;
+ }
+ }
+ }
+
+ if (mirred_d)
+ fprintf(stdout, "Action %d device %s ifindex %d\n",p.action, d,p.ifindex);
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+ addattr_l(n, MAX_MSG, TCA_MIRRED_PARMS, &p, sizeof (p));
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+}
+
+
+int
+parse_mirred(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ if (argc < 0) {
+ fprintf(stderr,"mirred bad arguement count %d\n", argc);
+ return -1;
+ }
+
+ if (matches(*argv, "mirred") == 0) {
+ NEXT_ARG();
+ } else {
+ fprintf(stderr,"mirred bad arguement %s\n", *argv);
+ return -1;
+ }
+
+
+ if (matches(*argv, "egress") == 0 || matches(*argv, "index") == 0) {
+ int ret = parse_egress(a, &argc, &argv, tca_id, n);
+ if (ret == 0) {
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+ }
+
+ } else if (matches(*argv, "ingress") == 0) {
+ fprintf(stderr,"mirred ingress not supported at the moment\n");
+ } else if (matches(*argv, "help") == 0) {
+ usage();
+ } else {
+ fprintf(stderr,"mirred option not supported %s\n", *argv);
+ }
+
+ return -1;
+
+}
+
+int
+print_mirred(struct action_util *au,FILE * f, struct rtattr *arg)
+{
+ struct tc_mirred *p;
+ struct rtattr *tb[TCA_MIRRED_MAX + 1];
+ const char *dev;
+ SPRINT_BUF(b1);
+
+ if (arg == NULL)
+ return -1;
+
+ parse_rtattr_nested(tb, TCA_MIRRED_MAX, arg);
+
+ if (tb[TCA_MIRRED_PARMS] == NULL) {
+ fprintf(f, "[NULL mirred parameters]");
+ return -1;
+ }
+ p = RTA_DATA(tb[TCA_MIRRED_PARMS]);
+
+ /*
+ ll_init_map(&rth);
+ */
+
+
+ if ((dev = ll_index_to_name(p->ifindex)) == 0) {
+ fprintf(stderr, "Cannot find device %d\n", p->ifindex);
+ return -1;
+ }
+
+ fprintf(f, "mirred (%s to device %s) %s", mirred_n2a(p->eaction), dev,action_n2a(p->action, b1, sizeof (b1)));
+
+ fprintf(f, "\n ");
+ fprintf(f, "\tindex %d ref %d bind %d",p->index,p->refcnt,p->bindcnt);
+
+ if (show_stats) {
+ if (tb[TCA_MIRRED_TM]) {
+ struct tcf_t *tm = RTA_DATA(tb[TCA_MIRRED_TM]);
+ print_tm(f,tm);
+ }
+ }
+ fprintf(f, "\n ");
+ return 0;
+}
+
+struct action_util mirred_action_util = {
+ .id = "mirred",
+ .parse_aopt = parse_mirred,
+ .print_aopt = print_mirred,
+};
diff --git a/tc/m_nat.c b/tc/m_nat.c
new file mode 100644
index 0000000..01ec032
--- /dev/null
+++ b/tc/m_nat.c
@@ -0,0 +1,212 @@
+/*
+ * m_nat.c NAT module
+ *
+ * This program is free software; you can distribute 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: Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+#include <linux/tc_act/tc_nat.h>
+
+static void
+explain(void)
+{
+ fprintf(stderr, "Usage: ... nat NAT\n"
+ "NAT := DIRECTION OLD NEW\n"
+ "DIRECTION := { ingress | egress }\n"
+ "OLD := PREFIX\n"
+ "NEW := ADDRESS\n");
+}
+
+static void
+usage(void)
+{
+ explain();
+ exit(-1);
+}
+
+static int
+parse_nat_args(int *argc_p, char ***argv_p,struct tc_nat *sel)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ inet_prefix addr;
+
+ if (argc <= 0)
+ return -1;
+
+ if (matches(*argv, "egress") == 0)
+ sel->flags |= TCA_NAT_FLAG_EGRESS;
+ else if (matches(*argv, "ingress") != 0)
+ goto bad_val;
+
+ NEXT_ARG();
+
+ if (get_prefix_1(&addr, *argv, AF_INET))
+ goto bad_val;
+
+ sel->old_addr = addr.data[0];
+ sel->mask = htonl(~0u << (32 - addr.bitlen));
+
+ NEXT_ARG();
+
+ if (get_prefix_1(&addr, *argv, AF_INET))
+ goto bad_val;
+
+ sel->new_addr = addr.data[0];
+
+ argc--;
+ argv++;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+
+bad_val:
+ return -1;
+}
+
+static int
+parse_nat(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+ struct tc_nat sel;
+
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int ok = 0;
+ struct rtattr *tail;
+
+ memset(&sel, 0, sizeof(sel));
+
+ while (argc > 0) {
+ if (matches(*argv, "nat") == 0) {
+ NEXT_ARG();
+ if (parse_nat_args(&argc, &argv, &sel)) {
+ fprintf(stderr, "Illegal nat construct (%s) \n",
+ *argv);
+ explain();
+ return -1;
+ }
+ ok++;
+ continue;
+ } else if (matches(*argv, "help") == 0) {
+ usage();
+ } else {
+ break;
+ }
+
+ }
+
+ if (!ok) {
+ explain();
+ return -1;
+ }
+
+ if (argc) {
+ if (matches(*argv, "reclassify") == 0) {
+ sel.action = TC_ACT_RECLASSIFY;
+ argc--;
+ argv++;
+ } else if (matches(*argv, "pipe") == 0) {
+ sel.action = TC_ACT_PIPE;
+ argc--;
+ argv++;
+ } else if (matches(*argv, "drop") == 0 ||
+ matches(*argv, "shot") == 0) {
+ sel.action = TC_ACT_SHOT;
+ argc--;
+ argv++;
+ } else if (matches(*argv, "continue") == 0) {
+ sel.action = TC_ACT_UNSPEC;
+ argc--;
+ argv++;
+ } else if (matches(*argv, "pass") == 0) {
+ sel.action = TC_ACT_OK;
+ argc--;
+ argv++;
+ }
+ }
+
+ if (argc) {
+ if (matches(*argv, "index") == 0) {
+ NEXT_ARG();
+ if (get_u32(&sel.index, *argv, 10)) {
+ fprintf(stderr, "Pedit: Illegal \"index\"\n");
+ return -1;
+ }
+ argc--;
+ argv++;
+ }
+ }
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+ addattr_l(n, MAX_MSG, TCA_NAT_PARMS, &sel, sizeof(sel));
+ tail->rta_len = (char *)NLMSG_TAIL(n) - (char *)tail;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+}
+
+static int
+print_nat(struct action_util *au,FILE * f, struct rtattr *arg)
+{
+ struct tc_nat *sel;
+ struct rtattr *tb[TCA_NAT_MAX + 1];
+ char buf1[256];
+ char buf2[256];
+ SPRINT_BUF(buf3);
+ int len;
+
+ if (arg == NULL)
+ return -1;
+
+ parse_rtattr_nested(tb, TCA_NAT_MAX, arg);
+
+ if (tb[TCA_NAT_PARMS] == NULL) {
+ fprintf(f, "[NULL nat parameters]");
+ return -1;
+ }
+ sel = RTA_DATA(tb[TCA_NAT_PARMS]);
+
+ len = ffs(sel->mask);
+ len = len ? 33 - len : 0;
+
+ fprintf(f, " nat %s %s/%d %s %s", sel->flags & TCA_NAT_FLAG_EGRESS ?
+ "egress" : "ingress",
+ format_host(AF_INET, 4, &sel->old_addr, buf1, sizeof(buf1)),
+ len,
+ format_host(AF_INET, 4, &sel->new_addr, buf2, sizeof(buf2)),
+ action_n2a(sel->action, buf3, sizeof (buf3)));
+
+ if (show_stats) {
+ if (tb[TCA_NAT_TM]) {
+ struct tcf_t *tm = RTA_DATA(tb[TCA_NAT_TM]);
+ print_tm(f,tm);
+ }
+ }
+
+ return 0;
+}
+
+struct action_util nat_action_util = {
+ .id = "nat",
+ .parse_aopt = parse_nat,
+ .print_aopt = print_nat,
+};
diff --git a/tc/m_pedit.c b/tc/m_pedit.c
new file mode 100644
index 0000000..7499846
--- /dev/null
+++ b/tc/m_pedit.c
@@ -0,0 +1,608 @@
+/*
+ * m_pedit.c generic packet editor actions module
+ *
+ * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
+ *
+ * TODO:
+ * 1) Big endian broken in some spots
+ * 2) A lot of this stuff was added on the fly; get a big double-double
+ * and clean it up at some point.
+ *
+ */
+
+#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 <dlfcn.h>
+#include "utils.h"
+#include "tc_util.h"
+#include "m_pedit.h"
+
+static struct m_pedit_util *pedit_list;
+int pedit_debug = 1;
+
+static void
+explain(void)
+{
+ fprintf(stderr, "Usage: ... pedit munge <MUNGE>\n");
+ fprintf(stderr,
+ "Where: MUNGE := <RAW>|<LAYERED>\n"
+ "\t<RAW>:= <OFFSETC>[ATC]<CMD>\n "
+ "\t\tOFFSETC:= offset <offval> <u8|u16|u32>\n "
+ "\t\tATC:= at <atval> offmask <maskval> shift <shiftval>\n "
+ "\t\tNOTE: offval is byte offset, must be multiple of 4\n "
+ "\t\tNOTE: maskval is a 32 bit hex number\n "
+ "\t\tNOTE: shiftval is a is a shift value\n "
+ "\t\tCMD:= clear | invert | set <setval>| retain\n "
+ "\t<LAYERED>:= ip <ipdata> | ip6 <ip6data> \n "
+ " \t\t| udp <udpdata> | tcp <tcpdata> | icmp <icmpdata> \n"
+ "For Example usage look at the examples directory\n");
+
+}
+
+static void
+usage(void)
+{
+ explain();
+ exit(-1);
+}
+
+static int
+pedit_parse_nopopt (int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ if (argc) {
+ fprintf(stderr, "Unknown action hence option \"%s\" is unparsable\n", *argv);
+ return -1;
+ }
+
+ return 0;
+
+}
+
+struct m_pedit_util
+*get_pedit_kind(char *str)
+{
+ static void *pBODY;
+ void *dlh;
+ char buf[256];
+ struct m_pedit_util *p;
+
+ for (p = pedit_list; p; p = p->next) {
+ if (strcmp(p->id, str) == 0)
+ return p;
+ }
+
+ snprintf(buf, sizeof(buf), "p_%s.so", str);
+ dlh = dlopen(buf, RTLD_LAZY);
+ if (dlh == NULL) {
+ dlh = pBODY;
+ if (dlh == NULL) {
+ dlh = pBODY = dlopen(NULL, RTLD_LAZY);
+ if (dlh == NULL)
+ goto noexist;
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "p_pedit_%s", str);
+ p = dlsym(dlh, buf);
+ if (p == NULL)
+ goto noexist;
+
+reg:
+ p->next = pedit_list;
+ pedit_list = p;
+ return p;
+
+noexist:
+ p = malloc(sizeof(*p));
+ if (p) {
+ memset(p, 0, sizeof(*p));
+ strncpy(p->id, str, sizeof(p->id)-1);
+ p->parse_peopt = pedit_parse_nopopt;
+ goto reg;
+ }
+ return p;
+}
+
+int
+pack_key(struct tc_pedit_sel *sel,struct tc_pedit_key *tkey)
+{
+ int hwm = sel->nkeys;
+
+ if (hwm >= MAX_OFFS)
+ return -1;
+
+ if (tkey->off % 4) {
+ fprintf(stderr, "offsets MUST be in 32 bit boundaries\n");
+ return -1;
+ }
+
+ sel->keys[hwm].val = tkey->val;
+ sel->keys[hwm].mask = tkey->mask;
+ sel->keys[hwm].off = tkey->off;
+ sel->keys[hwm].at = tkey->at;
+ sel->keys[hwm].offmask = tkey->offmask;
+ sel->keys[hwm].shift = tkey->shift;
+ sel->nkeys++;
+ return 0;
+}
+
+
+int
+pack_key32(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey)
+{
+ if (tkey->off > (tkey->off & ~3)) {
+ fprintf(stderr,
+ "pack_key32: 32 bit offsets must begin in 32bit boundaries\n");
+ return -1;
+ }
+
+ tkey->val = htonl(tkey->val & retain);
+ tkey->mask = htonl(tkey->mask | ~retain);
+ /* jamal remove this - it is not necessary given the if check above */
+ tkey->off &= ~3;
+ return pack_key(sel,tkey);
+}
+
+int
+pack_key16(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey)
+{
+ int ind = 0, stride = 0;
+ __u32 m[4] = {0xFFFF0000,0xFF0000FF,0x0000FFFF};
+
+ if (0 > tkey->off) {
+ ind = tkey->off + 1;
+ if (0 > ind)
+ ind = -1*ind;
+ } else {
+ ind = tkey->off;
+ }
+
+ if (tkey->val > 0xFFFF || tkey->mask > 0xFFFF) {
+ fprintf(stderr, "pack_key16 bad value\n");
+ return -1;
+ }
+
+ ind = tkey->off & 3;
+
+ if (0 > ind || 2 < ind) {
+ fprintf(stderr, "pack_key16 bad index value %d\n",ind);
+ return -1;
+ }
+
+ stride = 8 * ind;
+ tkey->val = htons(tkey->val);
+ if (stride > 0) {
+ tkey->val <<= stride;
+ tkey->mask <<= stride;
+ retain <<= stride;
+ }
+ tkey->mask = retain|m[ind];
+
+ tkey->off &= ~3;
+
+ if (pedit_debug)
+ printf("pack_key16: Final val %08x mask %08x \n",tkey->val,tkey->mask);
+ return pack_key(sel,tkey);
+
+}
+
+int
+pack_key8(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey)
+{
+ int ind = 0, stride = 0;
+ __u32 m[4] = {0xFFFFFF00,0xFFFF00FF,0xFF00FFFF,0x00FFFFFF};
+
+ if (0 > tkey->off) {
+ ind = tkey->off + 1;
+ if (0 > ind)
+ ind = -1*ind;
+ } else {
+ ind = tkey->off;
+ }
+
+ if (tkey->val > 0xFF || tkey->mask > 0xFF) {
+ fprintf(stderr, "pack_key8 bad value (val %x mask %x\n", tkey->val, tkey->mask);
+ return -1;
+ }
+
+ ind = tkey->off & 3;
+ stride = 8 * ind;
+ tkey->val <<= stride;
+ tkey->mask <<= stride;
+ retain <<= stride;
+ tkey->mask = retain|m[ind];
+ tkey->off &= ~3;
+
+ if (pedit_debug)
+ printf("pack_key8: Final word off %d val %08x mask %08x \n",tkey->off , tkey->val,tkey->mask);
+ return pack_key(sel,tkey);
+}
+
+int
+parse_val(int *argc_p, char ***argv_p, __u32 * val, int type)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ if (argc <= 0)
+ return -1;
+
+ if (TINT == type)
+ return get_integer((int *) val, *argv, 0);
+
+ if (TU32 == type)
+ return get_u32(val, *argv, 0);
+
+ if (TIPV4 == type) {
+ inet_prefix addr;
+ if (get_prefix_1(&addr, *argv, AF_INET)) {
+ return -1;
+ }
+ *val=addr.data[0];
+ return 0;
+ }
+ if (TIPV6 == type) {
+ /* not implemented yet */
+ return -1;
+ }
+
+ return -1;
+}
+
+int
+parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type,__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey)
+{
+ __u32 mask = 0, val = 0;
+ __u32 o = 0xFF;
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ if (argc <= 0)
+ return -1;
+
+ if (pedit_debug)
+ printf("parse_cmd argc %d %s offset %d length %d\n",argc,*argv,tkey->off,len);
+
+ if (len == 2)
+ o = 0xFFFF;
+ if (len == 4)
+ o = 0xFFFFFFFF;
+
+ if (matches(*argv, "invert") == 0) {
+ retain = val = mask = o;
+ } else if (matches(*argv, "set") == 0) {
+ NEXT_ARG();
+ if (parse_val(&argc, &argv, &val, type))
+ return -1;
+ } else if (matches(*argv, "preserve") == 0) {
+ retain = mask = o;
+ } else {
+ if (matches(*argv, "clear") != 0)
+ return -1;
+ }
+
+ argc--; argv++;
+
+ if (argc && matches(*argv, "retain") == 0) {
+ NEXT_ARG();
+ if (parse_val(&argc, &argv, &retain, TU32))
+ return -1;
+ argc--; argv++;
+ }
+
+ tkey->val = val;
+
+ if (len == 1) {
+ tkey->mask = 0xFF;
+ res = pack_key8(retain,sel,tkey);
+ goto done;
+ }
+ if (len == 2) {
+ tkey->mask = mask;
+ res = pack_key16(retain,sel,tkey);
+ goto done;
+ }
+ if (len == 4) {
+ tkey->mask = mask;
+ res = pack_key32(retain,sel,tkey);
+ goto done;
+ }
+
+ return -1;
+done:
+ if (pedit_debug)
+ printf("parse_cmd done argc %d %s offset %d length %d\n",argc,*argv,tkey->off,len);
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+
+}
+
+int
+parse_offset(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey)
+{
+ int off;
+ __u32 len, retain;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int res = -1;
+
+ if (argc <= 0)
+ return -1;
+
+ if (get_integer(&off, *argv, 0))
+ return -1;
+ tkey->off = off;
+
+ argc--;
+ argv++;
+
+ if (argc <= 0)
+ return -1;
+
+
+ if (matches(*argv, "u32") == 0) {
+ len = 4;
+ retain = 0xFFFFFFFF;
+ goto done;
+ }
+ if (matches(*argv, "u16") == 0) {
+ len = 2;
+ retain = 0x0;
+ goto done;
+ }
+ if (matches(*argv, "u8") == 0) {
+ len = 1;
+ retain = 0x0;
+ goto done;
+ }
+
+ return -1;
+
+done:
+
+ NEXT_ARG();
+
+ /* [at <someval> offmask <maskval> shift <shiftval>] */
+ if (matches(*argv, "at") == 0) {
+
+ __u32 atv=0,offmask=0x0,shift=0;
+
+ NEXT_ARG();
+ if (get_u32(&atv, *argv, 0))
+ return -1;
+ tkey->at = atv;
+
+ NEXT_ARG();
+
+ if (get_u32(&offmask, *argv, 16))
+ return -1;
+ tkey->offmask = offmask;
+
+ NEXT_ARG();
+
+ if (get_u32(&shift, *argv, 0))
+ return -1;
+ tkey->shift = shift;
+
+ NEXT_ARG();
+ }
+
+ res = parse_cmd(&argc, &argv, len, TU32,retain,sel,tkey);
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+int
+parse_munge(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel)
+{
+ struct tc_pedit_key tkey;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int res = -1;
+
+ if (argc <= 0)
+ return -1;
+
+ memset(&tkey, 0, sizeof(tkey));
+
+ if (matches(*argv, "offset") == 0) {
+ NEXT_ARG();
+ res = parse_offset(&argc, &argv,sel,&tkey);
+ goto done;
+ } else {
+ char k[16];
+ struct m_pedit_util *p = NULL;
+
+ strncpy(k, *argv, sizeof (k) - 1);
+
+ if (argc > 0 ) {
+ p = get_pedit_kind(k);
+ if (NULL == p)
+ goto bad_val;
+ res = p->parse_peopt(&argc, &argv, sel,&tkey);
+ if (res < 0) {
+ fprintf(stderr,"bad pedit parsing\n");
+ goto bad_val;
+ }
+ goto done;
+ }
+ }
+
+bad_val:
+ return -1;
+
+done:
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+int
+parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+ struct {
+ struct tc_pedit_sel sel;
+ struct tc_pedit_key keys[MAX_OFFS];
+ } sel;
+
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int ok = 0, iok = 0;
+ struct rtattr *tail;
+
+ memset(&sel, 0, sizeof(sel));
+
+ while (argc > 0) {
+ if (pedit_debug > 1)
+ fprintf(stderr, "while pedit (%d:%s)\n",argc, *argv);
+ if (matches(*argv, "pedit") == 0) {
+ NEXT_ARG();
+ ok++;
+ continue;
+ } else if (matches(*argv, "help") == 0) {
+ usage();
+ } else if (matches(*argv, "munge") == 0) {
+ if (!ok) {
+ fprintf(stderr, "Illegal pedit construct (%s) \n", *argv);
+ explain();
+ return -1;
+ }
+ NEXT_ARG();
+ if (parse_munge(&argc, &argv,&sel.sel)) {
+ fprintf(stderr, "Illegal pedit construct (%s) \n", *argv);
+ explain();
+ return -1;
+ }
+ ok++;
+ } else {
+ break;
+ }
+
+ }
+
+ if (!ok) {
+ explain();
+ return -1;
+ }
+
+ if (argc) {
+ if (matches(*argv, "reclassify") == 0) {
+ sel.sel.action = TC_ACT_RECLASSIFY;
+ NEXT_ARG();
+ } else if (matches(*argv, "pipe") == 0) {
+ sel.sel.action = TC_ACT_PIPE;
+ NEXT_ARG();
+ } else if (matches(*argv, "drop") == 0 ||
+ matches(*argv, "shot") == 0) {
+ sel.sel.action = TC_ACT_SHOT;
+ NEXT_ARG();
+ } else if (matches(*argv, "continue") == 0) {
+ sel.sel.action = TC_ACT_UNSPEC;
+ NEXT_ARG();
+ } else if (matches(*argv, "pass") == 0) {
+ sel.sel.action = TC_ACT_OK;
+ NEXT_ARG();
+ }
+ }
+
+ if (argc) {
+ if (matches(*argv, "index") == 0) {
+ NEXT_ARG();
+ if (get_u32(&sel.sel.index, *argv, 10)) {
+ fprintf(stderr, "Pedit: Illegal \"index\"\n");
+ return -1;
+ }
+ argc--;
+ argv++;
+ iok++;
+ }
+ }
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+ addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS,&sel, sizeof(sel.sel)+sel.sel.nkeys*sizeof(struct tc_pedit_key));
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+}
+
+int
+print_pedit(struct action_util *au,FILE * f, struct rtattr *arg)
+{
+ struct tc_pedit_sel *sel;
+ struct rtattr *tb[TCA_PEDIT_MAX + 1];
+ SPRINT_BUF(b1);
+
+ if (arg == NULL)
+ return -1;
+
+ parse_rtattr_nested(tb, TCA_PEDIT_MAX, arg);
+
+ if (tb[TCA_PEDIT_PARMS] == NULL) {
+ fprintf(f, "[NULL pedit parameters]");
+ return -1;
+ }
+ sel = RTA_DATA(tb[TCA_PEDIT_PARMS]);
+
+ fprintf(f, " pedit action %s keys %d\n ", action_n2a(sel->action, b1, sizeof (b1)),sel->nkeys);
+ fprintf(f, "\t index %d ref %d bind %d", sel->index,sel->refcnt, sel->bindcnt);
+
+ if (show_stats) {
+ if (tb[TCA_PEDIT_TM]) {
+ struct tcf_t *tm = RTA_DATA(tb[TCA_PEDIT_TM]);
+ print_tm(f,tm);
+ }
+ }
+ if (sel->nkeys) {
+ int i;
+ struct tc_pedit_key *key = sel->keys;
+
+ for (i=0; i<sel->nkeys; i++, key++) {
+ fprintf(f, "\n\t key #%d",i);
+ fprintf(f, " at %d: val %08x mask %08x",
+ (unsigned int)key->off,
+ (unsigned int)ntohl(key->val),
+ (unsigned int)ntohl(key->mask));
+ }
+ } else {
+ fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index,sel->nkeys);
+ }
+
+
+ fprintf(f, "\n ");
+ return 0;
+}
+
+int
+pedit_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats)
+{
+ return 0;
+}
+
+struct action_util pedit_action_util = {
+ .id = "pedit",
+ .parse_aopt = parse_pedit,
+ .print_aopt = print_pedit,
+};
diff --git a/tc/m_pedit.h b/tc/m_pedit.h
new file mode 100644
index 0000000..1698c95
--- /dev/null
+++ b/tc/m_pedit.h
@@ -0,0 +1,62 @@
+/*
+ * m_pedit.h generic packet editor actions module
+ *
+ * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
+ *
+ */
+
+#ifndef _ACT_PEDIT_H_
+#define _ACT_PEDIT_H_ 1
+
+#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 "utils.h"
+#include "tc_util.h"
+#include <linux/tc_act/tc_pedit.h>
+
+#define MAX_OFFS 128
+
+#define TIPV4 1
+#define TIPV6 2
+#define TINT 3
+#define TU32 4
+
+#define RU32 0xFFFFFFFF
+#define RU16 0xFFFF
+#define RU8 0xFF
+
+#define PEDITKINDSIZ 16
+
+struct m_pedit_util
+{
+ struct m_pedit_util *next;
+ char id[PEDITKINDSIZ];
+ int (*parse_peopt)(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey);
+};
+
+
+extern int parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type,__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey);
+extern int pack_key(struct tc_pedit_sel *sel,struct tc_pedit_key *tkey);
+extern int pack_key32(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey);
+extern int pack_key16(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey);
+extern int pack_key8(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey);
+extern int parse_val(int *argc_p, char ***argv_p, __u32 * val, int type);
+extern int parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type,__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey);
+extern int parse_offset(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey);
+int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n);
+extern int print_pedit(struct action_util *au,FILE * f, struct rtattr *arg);
+extern int pedit_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats);
+
+#endif
diff --git a/tc/m_police.c b/tc/m_police.c
new file mode 100644
index 0000000..ace43b5
--- /dev/null
+++ b/tc/m_police.c
@@ -0,0 +1,371 @@
+/*
+ * m_police.c Parse/print policing module options.
+ *
+ * This program is free software; you can u32istribute 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>
+ * 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 <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 "utils.h"
+#include "tc_util.h"
+
+struct action_util police_action_util = {
+ .id = "police",
+ .parse_aopt = act_parse_police,
+ .print_aopt = print_police,
+};
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: ... police rate BPS burst BYTES[/BYTES] [ mtu BYTES[/BYTES] ]\n");
+ fprintf(stderr, " [ peakrate BPS ] [ avrate BPS ] [ overhead BYTES ]\n");
+ fprintf(stderr, " [ linklayer TYPE ] [ ACTIONTERM ]\n");
+
+ fprintf(stderr, "Old Syntax ACTIONTERM := 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");
+ exit(-1);
+}
+
+static void explain1(char *arg)
+{
+ fprintf(stderr, "Illegal \"%s\"\n", arg);
+}
+
+char *police_action_n2a(int action, char *buf, int len)
+{
+ switch (action) {
+ case -1:
+ return "continue";
+ break;
+ case TC_POLICE_OK:
+ return "pass";
+ break;
+ case TC_POLICE_SHOT:
+ return "drop";
+ break;
+ case TC_POLICE_RECLASSIFY:
+ return "reclassify";
+ case TC_POLICE_PIPE:
+ return "pipe";
+ default:
+ snprintf(buf, len, "%d", action);
+ return buf;
+ }
+}
+
+int police_action_a2n(char *arg, int *result)
+{
+ int res;
+
+ if (matches(arg, "continue") == 0)
+ res = -1;
+ else if (matches(arg, "drop") == 0)
+ res = TC_POLICE_SHOT;
+ else if (matches(arg, "shot") == 0)
+ res = TC_POLICE_SHOT;
+ else if (matches(arg, "pass") == 0)
+ res = TC_POLICE_OK;
+ else if (strcmp(arg, "ok") == 0)
+ 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 -1;
+ }
+ *result = res;
+ return 0;
+}
+
+
+int get_police_result(int *action, int *result, char *arg)
+{
+ char *p = strchr(arg, '/');
+
+ if (p)
+ *p = 0;
+
+ if (police_action_a2n(arg, action)) {
+ if (p)
+ *p = '/';
+ return -1;
+ }
+
+ if (p) {
+ *p = '/';
+ if (police_action_a2n(p+1, result))
+ return -1;
+ }
+ return 0;
+}
+
+
+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;
+ int res = -1;
+ int ok=0;
+ struct tc_police p;
+ __u32 rtab[256];
+ __u32 ptab[256];
+ __u32 avrate = 0;
+ int presult = 0;
+ unsigned buffer=0, mtu=0, mpu=0;
+ unsigned short overhead=0;
+ unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
+ int Rcell_log=-1, Pcell_log = -1;
+ struct rtattr *tail;
+
+ 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, 10)) {
+ fprintf(stderr, "Illegal \"index\"\n");
+ return -1;
+ }
+ } else if (matches(*argv, "burst") == 0 ||
+ strcmp(*argv, "buffer") == 0 ||
+ strcmp(*argv, "maxburst") == 0) {
+ NEXT_ARG();
+ if (buffer) {
+ fprintf(stderr, "Double \"buffer/burst\" spec\n");
+ return -1;
+ }
+ if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) {
+ explain1("buffer");
+ return -1;
+ }
+ } else if (strcmp(*argv, "mtu") == 0 ||
+ strcmp(*argv, "minburst") == 0) {
+ NEXT_ARG();
+ if (mtu) {
+ fprintf(stderr, "Double \"mtu/minburst\" spec\n");
+ return -1;
+ }
+ if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) {
+ explain1("mtu");
+ return -1;
+ }
+ } else if (strcmp(*argv, "mpu") == 0) {
+ NEXT_ARG();
+ if (mpu) {
+ fprintf(stderr, "Double \"mpu\" spec\n");
+ return -1;
+ }
+ if (get_size(&mpu, *argv)) {
+ explain1("mpu");
+ return -1;
+ }
+ } else if (strcmp(*argv, "rate") == 0) {
+ NEXT_ARG();
+ if (p.rate.rate) {
+ fprintf(stderr, "Double \"rate\" spec\n");
+ return -1;
+ }
+ if (get_rate(&p.rate.rate, *argv)) {
+ explain1("rate");
+ return -1;
+ }
+ } else if (strcmp(*argv, "avrate") == 0) {
+ NEXT_ARG();
+ if (avrate) {
+ fprintf(stderr, "Double \"avrate\" spec\n");
+ return -1;
+ }
+ if (get_rate(&avrate, *argv)) {
+ explain1("avrate");
+ return -1;
+ }
+ } else if (matches(*argv, "peakrate") == 0) {
+ NEXT_ARG();
+ if (p.peakrate.rate) {
+ fprintf(stderr, "Double \"peakrate\" spec\n");
+ return -1;
+ }
+ if (get_rate(&p.peakrate.rate, *argv)) {
+ explain1("peakrate");
+ return -1;
+ }
+ } else if (matches(*argv, "reclassify") == 0) {
+ p.action = TC_POLICE_RECLASSIFY;
+ } else if (matches(*argv, "drop") == 0 ||
+ matches(*argv, "shot") == 0) {
+ p.action = TC_POLICE_SHOT;
+ } else if (matches(*argv, "continue") == 0) {
+ p.action = TC_POLICE_UNSPEC;
+ } else if (matches(*argv, "pass") == 0) {
+ p.action = TC_POLICE_OK;
+ } else if (matches(*argv, "pipe") == 0) {
+ p.action = TC_POLICE_PIPE;
+ } else if (strcmp(*argv, "action") == 0 ||
+ strcmp(*argv, "conform-exceed") == 0) {
+ NEXT_ARG();
+ if (get_police_result(&p.action, &presult, *argv)) {
+ fprintf(stderr, "Illegal \"action\"\n");
+ return -1;
+ }
+ } else if (matches(*argv, "overhead") == 0) {
+ NEXT_ARG();
+ if (get_u16(&overhead, *argv, 10)) {
+ explain1("overhead"); return -1;
+ }
+ } else if (matches(*argv, "linklayer") == 0) {
+ NEXT_ARG();
+ if (get_linklayer(&linklayer, *argv)) {
+ explain1("linklayer"); return -1;
+ }
+ } else if (strcmp(*argv, "help") == 0) {
+ usage();
+ } else {
+ break;
+ }
+ ok++;
+ argc--; argv++;
+ }
+
+ if (!ok)
+ return -1;
+
+ if (p.rate.rate && !buffer) {
+ fprintf(stderr, "\"burst\" requires \"rate\".\n");
+ return -1;
+ }
+ if (p.peakrate.rate) {
+ if (!p.rate.rate) {
+ fprintf(stderr, "\"peakrate\" requires \"rate\".\n");
+ return -1;
+ }
+ if (!mtu) {
+ fprintf(stderr, "\"mtu\" is required, if \"peakrate\" is requested.\n");
+ return -1;
+ }
+ }
+
+ if (p.rate.rate) {
+ p.rate.mpu = mpu;
+ p.rate.overhead = overhead;
+ if (tc_calc_rtable(&p.rate, rtab, Rcell_log, mtu, linklayer) < 0) {
+ fprintf(stderr, "TBF: failed to calculate rate table.\n");
+ return -1;
+ }
+ p.burst = tc_calc_xmittime(p.rate.rate, buffer);
+ }
+ p.mtu = mtu;
+ if (p.peakrate.rate) {
+ p.peakrate.mpu = mpu;
+ p.peakrate.overhead = overhead;
+ if (tc_calc_rtable(&p.peakrate, ptab, Pcell_log, mtu, linklayer) < 0) {
+ fprintf(stderr, "POLICE: failed to calculate peak rate table.\n");
+ return -1;
+ }
+ }
+
+ tail = NLMSG_TAIL(n);
+ 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, MAX_MSG, TCA_POLICE_RATE, rtab, 1024);
+ if (p.peakrate.rate)
+ addattr_l(n, MAX_MSG, TCA_POLICE_PEAKRATE, ptab, 1024);
+ if (avrate)
+ addattr32(n, MAX_MSG, TCA_POLICE_AVRATE, avrate);
+ if (presult)
+ addattr32(n, MAX_MSG, TCA_POLICE_RESULT, presult);
+
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ res = 0;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ 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
+print_police(struct action_util *a, FILE *f, struct rtattr *arg)
+{
+ SPRINT_BUF(b1);
+ struct tc_police *p;
+ struct rtattr *tb[TCA_POLICE_MAX+1];
+ unsigned buffer;
+
+ if (arg == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_POLICE_MAX, arg);
+
+ if (tb[TCA_POLICE_TBF] == NULL) {
+ 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 0x%x ", p->index);
+ fprintf(f, "rate %s ", sprint_rate(p->rate.rate, b1));
+ buffer = tc_calc_xmitsize(p->rate.rate, p->burst);
+ fprintf(f, "burst %s ", sprint_size(buffer, b1));
+ fprintf(f, "mtu %s ", sprint_size(p->mtu, b1));
+ if (show_raw)
+ fprintf(f, "[%08x] ", p->burst);
+ if (p->peakrate.rate)
+ 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, "overhead %ub ", p->rate.overhead);
+ 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_action_util,f,arg);
+}
diff --git a/tc/m_skbedit.c b/tc/m_skbedit.c
new file mode 100644
index 0000000..9044353
--- /dev/null
+++ b/tc/m_skbedit.c
@@ -0,0 +1,192 @@
+/*
+ * m_skbedit.c SKB Editing module
+ *
+ * Copyright (c) 2008, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Authors: Alexander Duyck <alexander.h.duyck@intel.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "utils.h"
+#include "tc_util.h"
+#include <linux/tc_act/tc_skbedit.h>
+
+static void
+explain(void)
+{
+ fprintf(stderr, "Usage: ... skbedit "
+ "queue_mapping QUEUE_MAPPING | priority PRIORITY \n"
+ "QUEUE_MAPPING = device transmit queue to use\n"
+ "PRIORITY = classID to assign to priority field\n");
+}
+
+static void
+usage(void)
+{
+ explain();
+ exit(-1);
+}
+
+static int
+parse_skbedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
+ struct nlmsghdr *n)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int ok = 0;
+ struct rtattr *tail;
+ unsigned int tmp;
+ __u16 queue_mapping;
+ __u32 flags = 0, priority;
+ struct tc_skbedit sel = { 0 };
+
+ if (matches(*argv, "skbedit") != 0)
+ return -1;
+
+ NEXT_ARG();
+
+ while (argc > 0) {
+ if (matches(*argv, "queue_mapping") == 0) {
+ flags |= SKBEDIT_F_QUEUE_MAPPING;
+ NEXT_ARG();
+ if (get_unsigned(&tmp, *argv, 10) || tmp > 65535) {
+ fprintf(stderr, "Illegal queue_mapping\n");
+ return -1;
+ }
+ queue_mapping = tmp;
+ ok++;
+ } else if (matches(*argv, "priority") == 0) {
+ flags |= SKBEDIT_F_PRIORITY;
+ NEXT_ARG();
+ if (get_tc_classid(&priority, *argv)) {
+ fprintf(stderr, "Illegal priority\n");
+ return -1;
+ }
+ ok++;
+ } else if (matches(*argv, "help") == 0) {
+ usage();
+ } else {
+ break;
+ }
+ argc--;
+ argv++;
+ }
+
+ if (argc) {
+ if (matches(*argv, "reclassify") == 0) {
+ sel.action = TC_ACT_RECLASSIFY;
+ NEXT_ARG();
+ } else if (matches(*argv, "pipe") == 0) {
+ sel.action = TC_ACT_PIPE;
+ NEXT_ARG();
+ } else if (matches(*argv, "drop") == 0 ||
+ matches(*argv, "shot") == 0) {
+ sel.action = TC_ACT_SHOT;
+ NEXT_ARG();
+ } else if (matches(*argv, "continue") == 0) {
+ sel.action = TC_ACT_UNSPEC;
+ NEXT_ARG();
+ } else if (matches(*argv, "pass") == 0) {
+ sel.action = TC_ACT_OK;
+ NEXT_ARG();
+ }
+ }
+
+ if (argc) {
+ if (matches(*argv, "index") == 0) {
+ NEXT_ARG();
+ if (get_u32(&sel.index, *argv, 10)) {
+ fprintf(stderr, "Pedit: Illegal \"index\"\n");
+ return -1;
+ }
+ argc--;
+ argv++;
+ ok++;
+ }
+ }
+
+ if (!ok) {
+ explain();
+ return -1;
+ }
+
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+ addattr_l(n, MAX_MSG, TCA_SKBEDIT_PARMS, &sel, sizeof(sel));
+ if (flags & SKBEDIT_F_QUEUE_MAPPING)
+ addattr_l(n, MAX_MSG, TCA_SKBEDIT_QUEUE_MAPPING,
+ &queue_mapping, sizeof(queue_mapping));
+ if (flags & SKBEDIT_F_PRIORITY)
+ addattr_l(n, MAX_MSG, TCA_SKBEDIT_PRIORITY,
+ &priority, sizeof(priority));
+ tail->rta_len = (char *)NLMSG_TAIL(n) - (char *)tail;
+
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+}
+
+static int print_skbedit(struct action_util *au, FILE *f, struct rtattr *arg)
+{
+ struct tc_skbedit *sel;
+ struct rtattr *tb[TCA_SKBEDIT_MAX + 1];
+ SPRINT_BUF(b1);
+ __u32 *priority;
+ __u16 *queue_mapping;
+
+ if (arg == NULL)
+ return -1;
+
+ parse_rtattr_nested(tb, TCA_SKBEDIT_MAX, arg);
+
+ if (tb[TCA_SKBEDIT_PARMS] == NULL) {
+ fprintf(f, "[NULL skbedit parameters]");
+ return -1;
+ }
+
+ sel = RTA_DATA(tb[TCA_SKBEDIT_PARMS]);
+
+ fprintf(f, " skbedit");
+
+ if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) {
+ queue_mapping = RTA_DATA(tb[TCA_SKBEDIT_QUEUE_MAPPING]);
+ fprintf(f, " queue_mapping %u", *queue_mapping);
+ }
+ if (tb[TCA_SKBEDIT_PRIORITY] != NULL) {
+ priority = RTA_DATA(tb[TCA_SKBEDIT_PRIORITY]);
+ fprintf(f, " priority %s", sprint_tc_classid(*priority, b1));
+ }
+
+ if (show_stats) {
+ if (tb[TCA_SKBEDIT_TM]) {
+ struct tcf_t *tm = RTA_DATA(tb[TCA_SKBEDIT_TM]);
+ print_tm(f, tm);
+ }
+ }
+
+ return 0;
+}
+
+struct action_util skbedit_action_util = {
+ .id = "skbedit",
+ .parse_aopt = parse_skbedit,
+ .print_aopt = print_skbedit,
+};
diff --git a/tc/m_xt.c b/tc/m_xt.c
new file mode 100644
index 0000000..3972d2d
--- /dev/null
+++ b/tc/m_xt.c
@@ -0,0 +1,346 @@
+/*
+ * m_xt.c xtables based targets
+ * utilities mostly ripped from iptables <duh, its the linux way>
+ *
+ * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
+ */
+
+#include <syslog.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <limits.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <xtables.h>
+#include "utils.h"
+#include "tc_util.h"
+#include <linux/tc_act/tc_ipt.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <getopt.h>
+#include <errno.h>
+#include <string.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#ifndef XT_LIB_DIR
+# define XT_LIB_DIR "/lib/xtables"
+#endif
+
+static const char *tname = "mangle";
+
+char *lib_dir;
+
+static const char *ipthooks[] = {
+ "NF_IP_PRE_ROUTING",
+ "NF_IP_LOCAL_IN",
+ "NF_IP_FORWARD",
+ "NF_IP_LOCAL_OUT",
+ "NF_IP_POST_ROUTING",
+};
+
+static struct option original_opts[] = {
+ {
+ .name = "jump",
+ .has_arg = 1,
+ .val = 'j'
+ },
+ {0, 0, 0, 0}
+};
+
+static struct xtables_globals tcipt_globals = {
+ .option_offset = 0,
+ .program_name = "tc-ipt",
+ .program_version = "0.2",
+ .orig_opts = original_opts,
+ .opts = original_opts,
+ .exit_err = NULL,
+};
+
+/*
+ * we may need to check for version mismatch
+*/
+int
+build_st(struct xtables_target *target, struct xt_entry_target *t)
+{
+
+ size_t size =
+ XT_ALIGN(sizeof (struct xt_entry_target)) + target->size;
+
+ if (NULL == t) {
+ target->t = xtables_calloc(1, size);
+ target->t->u.target_size = size;
+ strcpy(target->t->u.user.name, target->name);
+ xtables_set_revision(target->t->u.user.name, target->revision);
+
+ if (target->init != NULL)
+ target->init(target->t);
+ } else {
+ target->t = t;
+ }
+ return 0;
+
+}
+
+inline void set_lib_dir(void)
+{
+
+ lib_dir = getenv("XTABLES_LIBDIR");
+ if (!lib_dir) {
+ lib_dir = getenv("IPTABLES_LIB_DIR");
+ if (lib_dir)
+ fprintf(stderr, "using deprecated IPTABLES_LIB_DIR \n");
+ }
+ if (lib_dir == NULL)
+ lib_dir = XT_LIB_DIR;
+
+}
+
+static int parse_ipt(struct action_util *a,int *argc_p,
+ char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+ struct xtables_target *m = NULL;
+ struct ipt_entry fw;
+ struct rtattr *tail;
+ int c;
+ int rargc = *argc_p;
+ char **argv = *argv_p;
+ int argc = 0, iargc = 0;
+ char k[16];
+ int res = -1;
+ int size = 0;
+ int iok = 0, ok = 0;
+ __u32 hook = 0, index = 0;
+ res = 0;
+
+ xtables_init_all(&tcipt_globals, NFPROTO_IPV4);
+ set_lib_dir();
+
+ {
+ int i;
+ for (i = 0; i < rargc; i++) {
+ if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) {
+ break;
+ }
+ }
+ iargc = argc = i;
+ }
+
+ if (argc <= 2) {
+ fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc);
+ return -1;
+ }
+
+ while (1) {
+ c = getopt_long(argc, argv, "j:", tcipt_globals.opts, NULL);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'j':
+ m = xtables_find_target(optarg, XTF_TRY_LOAD);
+ if (NULL != m) {
+
+ if (0 > build_st(m, NULL)) {
+ printf(" %s error \n", m->name);
+ return -1;
+ }
+ tcipt_globals.opts =
+ xtables_merge_options(tcipt_globals.opts,
+ m->extra_opts,
+ &m->option_offset);
+ } else {
+ fprintf(stderr," failed to find target %s\n\n", optarg);
+ return -1;
+ }
+ ok++;
+ break;
+
+ default:
+ memset(&fw, 0, sizeof (fw));
+ if (m) {
+ m->parse(c - m->option_offset, argv, 0,
+ &m->tflags, NULL, &m->t);
+ } else {
+ fprintf(stderr," failed to find target %s\n\n", optarg);
+ return -1;
+
+ }
+ ok++;
+ break;
+
+ }
+ }
+
+ if (iargc > optind) {
+ if (matches(argv[optind], "index") == 0) {
+ if (get_u32(&index, argv[optind + 1], 10)) {
+ fprintf(stderr, "Illegal \"index\"\n");
+ xtables_free_opts(1);
+ return -1;
+ }
+ iok++;
+
+ optind += 2;
+ }
+ }
+
+ if (!ok && !iok) {
+ fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv);
+ return -1;
+ }
+
+ /* check that we passed the correct parameters to the target */
+ if (m && m->final_check)
+ m->final_check(m->tflags);
+
+ {
+ struct tcmsg *t = NLMSG_DATA(n);
+ if (t->tcm_parent != TC_H_ROOT
+ && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) {
+ hook = NF_IP_PRE_ROUTING;
+ } else {
+ hook = NF_IP_POST_ROUTING;
+ }
+ }
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+ fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]);
+ fprintf(stdout, "\ttarget: ");
+
+ if (m)
+ m->print(NULL, m->t, 0);
+ fprintf(stdout, " index %d\n", index);
+
+ if (strlen(tname) > 16) {
+ size = 16;
+ k[15] = 0;
+ } else {
+ size = 1 + strlen(tname);
+ }
+ strncpy(k, tname, size);
+
+ addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size);
+ addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4);
+ addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4);
+ if (m)
+ addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size);
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+
+ argc -= optind;
+ argv += optind;
+ *argc_p = rargc - iargc;
+ *argv_p = argv;
+
+ optind = 0;
+ xtables_free_opts(1);
+ /* Clear flags if target will be used again */
+ m->tflags=0;
+ m->used=0;
+ /* Free allocated memory */
+ if (m->t)
+ free(m->t);
+
+
+ return 0;
+
+}
+
+static int
+print_ipt(struct action_util *au,FILE * f, struct rtattr *arg)
+{
+ struct rtattr *tb[TCA_IPT_MAX + 1];
+ struct xt_entry_target *t = NULL;
+
+ if (arg == NULL)
+ return -1;
+
+ xtables_init_all(&tcipt_globals, NFPROTO_IPV4);
+ set_lib_dir();
+
+ parse_rtattr_nested(tb, TCA_IPT_MAX, arg);
+
+ if (tb[TCA_IPT_TABLE] == NULL) {
+ fprintf(f, "[NULL ipt table name ] assuming mangle ");
+ } else {
+ fprintf(f, "tablename: %s ",
+ (char *) RTA_DATA(tb[TCA_IPT_TABLE]));
+ }
+
+ if (tb[TCA_IPT_HOOK] == NULL) {
+ fprintf(f, "[NULL ipt hook name ]\n ");
+ return -1;
+ } else {
+ __u32 hook;
+ hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]);
+ fprintf(f, " hook: %s \n", ipthooks[hook]);
+ }
+
+ if (tb[TCA_IPT_TARG] == NULL) {
+ fprintf(f, "\t[NULL ipt target parameters ] \n");
+ return -1;
+ } else {
+ struct xtables_target *m = NULL;
+ t = RTA_DATA(tb[TCA_IPT_TARG]);
+ m = xtables_find_target(t->u.user.name, XTF_TRY_LOAD);
+ if (NULL != m) {
+ if (0 > build_st(m, t)) {
+ fprintf(stderr, " %s error \n", m->name);
+ return -1;
+ }
+
+ tcipt_globals.opts =
+ xtables_merge_options(tcipt_globals.opts,
+ m->extra_opts,
+ &m->option_offset);
+ } else {
+ fprintf(stderr, " failed to find target %s\n\n",
+ t->u.user.name);
+ return -1;
+ }
+ fprintf(f, "\ttarget ");
+ m->print(NULL, m->t, 0);
+ if (tb[TCA_IPT_INDEX] == NULL) {
+ fprintf(f, " [NULL ipt target index ]\n");
+ } else {
+ __u32 index;
+ index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]);
+ fprintf(f, " \n\tindex %d", index);
+ }
+
+ if (tb[TCA_IPT_CNT]) {
+ struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);;
+ fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
+ }
+ if (show_stats) {
+ if (tb[TCA_IPT_TM]) {
+ struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
+ print_tm(f,tm);
+ }
+ }
+ fprintf(f, " \n");
+
+ }
+ xtables_free_opts(1);
+
+ return 0;
+}
+
+struct action_util ipt_action_util = {
+ .id = "ipt",
+ .parse_aopt = parse_ipt,
+ .print_aopt = print_ipt,
+};
+
diff --git a/tc/m_xt_old.c b/tc/m_xt_old.c
new file mode 100644
index 0000000..0c7ec60
--- /dev/null
+++ b/tc/m_xt_old.c
@@ -0,0 +1,433 @@
+/*
+ * m_xt.c xtables based targets
+ * utilities mostly ripped from iptables <duh, its the linux way>
+ *
+ * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
+ */
+
+/*XXX: in the future (xtables 1.4.3?) get rid of everything tagged
+ * as TC_CONFIG_XT_H */
+
+#include <syslog.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <xtables.h>
+#include "utils.h"
+#include "tc_util.h"
+#include <linux/tc_act/tc_ipt.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <string.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#ifdef TC_CONFIG_XT_H
+#include "xt-internal.h"
+#endif
+
+static const char *pname = "tc-ipt";
+static const char *tname = "mangle";
+static const char *pversion = "0.2";
+
+static const char *ipthooks[] = {
+ "NF_IP_PRE_ROUTING",
+ "NF_IP_LOCAL_IN",
+ "NF_IP_FORWARD",
+ "NF_IP_LOCAL_OUT",
+ "NF_IP_POST_ROUTING",
+};
+
+static struct option original_opts[] = {
+ {"jump", 1, 0, 'j'},
+ {0, 0, 0, 0}
+};
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+char *lib_dir;
+const char *program_version = XTABLES_VERSION;
+const char *program_name = "tc-ipt";
+struct afinfo afinfo = {
+ .family = AF_INET,
+ .libprefix = "libxt_",
+ .ipproto = IPPROTO_IP,
+ .kmod = "ip_tables",
+ .so_rev_target = IPT_SO_GET_REVISION_TARGET,
+};
+
+
+#define OPTION_OFFSET 256
+
+/*XXX: TC_CONFIG_XT_H */
+static void free_opts(struct option *local_opts)
+{
+ if (local_opts != original_opts) {
+ free(local_opts);
+ opts = original_opts;
+ global_option_offset = 0;
+ }
+}
+
+/*XXX: TC_CONFIG_XT_H */
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+ unsigned int *option_offset)
+{
+ struct option *merge;
+ unsigned int num_old, num_new, i;
+
+ for (num_old = 0; oldopts[num_old].name; num_old++) ;
+ for (num_new = 0; newopts[num_new].name; num_new++) ;
+
+ *option_offset = global_option_offset + OPTION_OFFSET;
+
+ merge = malloc(sizeof (struct option) * (num_new + num_old + 1));
+ memcpy(merge, oldopts, num_old * sizeof (struct option));
+ for (i = 0; i < num_new; i++) {
+ merge[num_old + i] = newopts[i];
+ merge[num_old + i].val += *option_offset;
+ }
+ memset(merge + num_old + num_new, 0, sizeof (struct option));
+
+ return merge;
+}
+
+
+/*XXX: TC_CONFIG_XT_H */
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/*XXX: TC_CONFIG_XT_H */
+int
+check_inverse(const char option[], int *invert, int *my_optind, int argc)
+{
+ if (option && strcmp(option, "!") == 0) {
+ if (*invert)
+ exit_error(PARAMETER_PROBLEM,
+ "Multiple `!' flags not allowed");
+ *invert = TRUE;
+ if (my_optind != NULL) {
+ ++*my_optind;
+ if (argc && *my_optind > argc)
+ exit_error(PARAMETER_PROBLEM,
+ "no argument following `!'");
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*XXX: TC_CONFIG_XT_H */
+void exit_error(enum exittype status, const char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ fprintf(stderr, "%s v%s: ", pname, pversion);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ /* On error paths, make sure that we don't leak memory */
+ exit(status);
+}
+
+/*XXX: TC_CONFIG_XT_H */
+static void set_revision(char *name, u_int8_t revision)
+{
+ /* Old kernel sources don't have ".revision" field,
+ * but we stole a byte from name. */
+ name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0';
+ name[IPT_FUNCTION_MAXNAMELEN - 1] = revision;
+}
+
+/*
+ * we may need to check for version mismatch
+*/
+int
+build_st(struct xtables_target *target, struct xt_entry_target *t)
+{
+
+ size_t size =
+ XT_ALIGN(sizeof (struct xt_entry_target)) + target->size;
+
+ if (NULL == t) {
+ target->t = fw_calloc(1, size);
+ target->t->u.target_size = size;
+ strcpy(target->t->u.user.name, target->name);
+ set_revision(target->t->u.user.name, target->revision);
+
+ if (target->init != NULL)
+ target->init(target->t);
+ } else {
+ target->t = t;
+ }
+ return 0;
+
+}
+
+inline void set_lib_dir(void)
+{
+
+ lib_dir = getenv("XTABLES_LIBDIR");
+ if (!lib_dir) {
+ lib_dir = getenv("IPTABLES_LIB_DIR");
+ if (lib_dir)
+ fprintf(stderr, "using deprecated IPTABLES_LIB_DIR \n");
+ }
+ if (lib_dir == NULL)
+ lib_dir = XT_LIB_DIR;
+
+}
+
+static int parse_ipt(struct action_util *a,int *argc_p,
+ char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+ struct xtables_target *m = NULL;
+ struct ipt_entry fw;
+ struct rtattr *tail;
+ int c;
+ int rargc = *argc_p;
+ char **argv = *argv_p;
+ int argc = 0, iargc = 0;
+ char k[16];
+ int res = -1;
+ int size = 0;
+ int iok = 0, ok = 0;
+ __u32 hook = 0, index = 0;
+ res = 0;
+
+ set_lib_dir();
+
+ {
+ int i;
+ for (i = 0; i < rargc; i++) {
+ if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) {
+ break;
+ }
+ }
+ iargc = argc = i;
+ }
+
+ if (argc <= 2) {
+ fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc);
+ return -1;
+ }
+
+ while (1) {
+ c = getopt_long(argc, argv, "j:", opts, NULL);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'j':
+ m = find_target(optarg, TRY_LOAD);
+ if (NULL != m) {
+
+ if (0 > build_st(m, NULL)) {
+ printf(" %s error \n", m->name);
+ return -1;
+ }
+ opts =
+ merge_options(opts, m->extra_opts,
+ &m->option_offset);
+ } else {
+ fprintf(stderr," failed to find target %s\n\n", optarg);
+ return -1;
+ }
+ ok++;
+ break;
+
+ default:
+ memset(&fw, 0, sizeof (fw));
+ if (m) {
+ m->parse(c - m->option_offset, argv, 0,
+ &m->tflags, NULL, &m->t);
+ } else {
+ fprintf(stderr," failed to find target %s\n\n", optarg);
+ return -1;
+
+ }
+ ok++;
+ break;
+
+ }
+ }
+
+ if (iargc > optind) {
+ if (matches(argv[optind], "index") == 0) {
+ if (get_u32(&index, argv[optind + 1], 10)) {
+ fprintf(stderr, "Illegal \"index\"\n");
+ free_opts(opts);
+ return -1;
+ }
+ iok++;
+
+ optind += 2;
+ }
+ }
+
+ if (!ok && !iok) {
+ fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv);
+ return -1;
+ }
+
+ /* check that we passed the correct parameters to the target */
+ if (m)
+ m->final_check(m->tflags);
+
+ {
+ struct tcmsg *t = NLMSG_DATA(n);
+ if (t->tcm_parent != TC_H_ROOT
+ && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) {
+ hook = NF_IP_PRE_ROUTING;
+ } else {
+ hook = NF_IP_POST_ROUTING;
+ }
+ }
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+ fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]);
+ fprintf(stdout, "\ttarget: ");
+
+ if (m)
+ m->print(NULL, m->t, 0);
+ fprintf(stdout, " index %d\n", index);
+
+ if (strlen(tname) > 16) {
+ size = 16;
+ k[15] = 0;
+ } else {
+ size = 1 + strlen(tname);
+ }
+ strncpy(k, tname, size);
+
+ addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size);
+ addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4);
+ addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4);
+ if (m)
+ addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size);
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+
+ argc -= optind;
+ argv += optind;
+ *argc_p = rargc - iargc;
+ *argv_p = argv;
+
+ optind = 0;
+ free_opts(opts);
+ /* Clear flags if target will be used again */
+ m->tflags=0;
+ m->used=0;
+ /* Free allocated memory */
+ if (m->t)
+ free(m->t);
+
+
+ return 0;
+
+}
+
+static int
+print_ipt(struct action_util *au,FILE * f, struct rtattr *arg)
+{
+ struct rtattr *tb[TCA_IPT_MAX + 1];
+ struct xt_entry_target *t = NULL;
+
+ if (arg == NULL)
+ return -1;
+
+ set_lib_dir();
+
+ parse_rtattr_nested(tb, TCA_IPT_MAX, arg);
+
+ if (tb[TCA_IPT_TABLE] == NULL) {
+ fprintf(f, "[NULL ipt table name ] assuming mangle ");
+ } else {
+ fprintf(f, "tablename: %s ",
+ (char *) RTA_DATA(tb[TCA_IPT_TABLE]));
+ }
+
+ if (tb[TCA_IPT_HOOK] == NULL) {
+ fprintf(f, "[NULL ipt hook name ]\n ");
+ return -1;
+ } else {
+ __u32 hook;
+ hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]);
+ fprintf(f, " hook: %s \n", ipthooks[hook]);
+ }
+
+ if (tb[TCA_IPT_TARG] == NULL) {
+ fprintf(f, "\t[NULL ipt target parameters ] \n");
+ return -1;
+ } else {
+ struct xtables_target *m = NULL;
+ t = RTA_DATA(tb[TCA_IPT_TARG]);
+ m = find_target(t->u.user.name, TRY_LOAD);
+ if (NULL != m) {
+ if (0 > build_st(m, t)) {
+ fprintf(stderr, " %s error \n", m->name);
+ return -1;
+ }
+
+ opts =
+ merge_options(opts, m->extra_opts,
+ &m->option_offset);
+ } else {
+ fprintf(stderr, " failed to find target %s\n\n",
+ t->u.user.name);
+ return -1;
+ }
+ fprintf(f, "\ttarget ");
+ m->print(NULL, m->t, 0);
+ if (tb[TCA_IPT_INDEX] == NULL) {
+ fprintf(f, " [NULL ipt target index ]\n");
+ } else {
+ __u32 index;
+ index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]);
+ fprintf(f, " \n\tindex %d", index);
+ }
+
+ if (tb[TCA_IPT_CNT]) {
+ struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);;
+ fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
+ }
+ if (show_stats) {
+ if (tb[TCA_IPT_TM]) {
+ struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
+ print_tm(f,tm);
+ }
+ }
+ fprintf(f, " \n");
+
+ }
+ free_opts(opts);
+
+ return 0;
+}
+
+struct action_util ipt_action_util = {
+ .id = "ipt",
+ .parse_aopt = parse_ipt,
+ .print_aopt = print_ipt,
+};
+
diff --git a/tc/p_icmp.c b/tc/p_icmp.c
new file mode 100644
index 0000000..a4b80c2
--- /dev/null
+++ b/tc/p_icmp.c
@@ -0,0 +1,61 @@
+/*
+ * m_pedit_icmp.c packet editor: ICMP header
+ *
+ * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
+ *
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+#include "m_pedit.h"
+
+
+static int
+parse_icmp(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey)
+{
+ int res = -1;
+#if 0
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ if (argc < 2)
+ return -1;
+
+ if (strcmp(*argv, "type") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, 0);
+ goto done;
+ }
+ if (strcmp(*argv, "code") == 0) {
+ NEXT_ARG();
+ res = parse_u8(&argc, &argv, 1);
+ goto done;
+ }
+ return -1;
+
+ done:
+ *argc_p = argc;
+ *argv_p = argv;
+#endif
+ return res;
+}
+
+struct m_pedit_util p_pedit_icmp = {
+ NULL,
+ "icmp",
+ parse_icmp,
+};
diff --git a/tc/p_ip.c b/tc/p_ip.c
new file mode 100644
index 0000000..08fdbaa
--- /dev/null
+++ b/tc/p_ip.c
@@ -0,0 +1,159 @@
+/*
+ * m_pedit.c packet editor: IPV4/6 header
+ *
+ * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
+ *
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+#include "m_pedit.h"
+
+static int
+parse_ip(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey)
+{
+ int res = -1;
+ int argc = *argc_p;
+ char **argv = *argv_p;
+
+ if (argc < 2)
+ return -1;
+
+ if (strcmp(*argv, "src") == 0) {
+ NEXT_ARG();
+ tkey->off = 12;
+ res = parse_cmd(&argc, &argv, 4, TIPV4,RU32,sel,tkey);
+ goto done;
+ }
+ if (strcmp(*argv, "dst") == 0) {
+ NEXT_ARG();
+ tkey->off = 16;
+ res = parse_cmd(&argc, &argv, 4, TIPV4,RU32,sel,tkey);
+ goto done;
+ }
+ /* jamal - look at these and make them either old or new
+ ** scheme given diffserv
+ ** dont forget the CE bit
+ */
+ if (strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) {
+ NEXT_ARG();
+ tkey->off = 1;
+ res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey);
+ goto done;
+ }
+ if (strcmp(*argv, "ihl") == 0) {
+ NEXT_ARG();
+ tkey->off = 0;
+ res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey);
+ goto done;
+ }
+ if (strcmp(*argv, "protocol") == 0) {
+ NEXT_ARG();
+ tkey->off = 9;
+ res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey);
+ goto done;
+ }
+ /* jamal - fix this */
+ if (matches(*argv, "precedence") == 0) {
+ NEXT_ARG();
+ tkey->off = 1;
+ res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey);
+ goto done;
+ }
+ /* jamal - validate this at some point */
+ if (strcmp(*argv, "nofrag") == 0) {
+ NEXT_ARG();
+ tkey->off = 6;
+ res = parse_cmd(&argc, &argv, 1, TU32,0x3F,sel,tkey);
+ goto done;
+ }
+ /* jamal - validate this at some point */
+ if (strcmp(*argv, "firstfrag") == 0) {
+ NEXT_ARG();
+ tkey->off = 6;
+ res = parse_cmd(&argc, &argv, 1, TU32,0x1F,sel,tkey);
+ goto done;
+ }
+ if (strcmp(*argv, "ce") == 0) {
+ NEXT_ARG();
+ tkey->off = 6;
+ res = parse_cmd(&argc, &argv, 1, TU32,0x80,sel,tkey);
+ goto done;
+ }
+ if (strcmp(*argv, "df") == 0) {
+ NEXT_ARG();
+ tkey->off = 6;
+ res = parse_cmd(&argc, &argv, 1, TU32,0x40,sel,tkey);
+ goto done;
+ }
+ if (strcmp(*argv, "mf") == 0) {
+ NEXT_ARG();
+ tkey->off = 6;
+ res = parse_cmd(&argc, &argv, 1, TU32,0x20,sel,tkey);
+ goto done;
+ }
+ if (strcmp(*argv, "dport") == 0) {
+ NEXT_ARG();
+ tkey->off = 22;
+ res = parse_cmd(&argc, &argv, 2, TU32,RU16,sel,tkey);
+ goto done;
+ }
+ if (strcmp(*argv, "sport") == 0) {
+ NEXT_ARG();
+ tkey->off = 20;
+ res = parse_cmd(&argc, &argv, 2, TU32,RU16,sel,tkey);
+ goto done;
+ }
+ if (strcmp(*argv, "icmp_type") == 0) {
+ NEXT_ARG();
+ tkey->off = 20;
+ res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey);
+ goto done;
+ }
+ if (strcmp(*argv, "icmp_code") == 0) {
+ NEXT_ARG();
+ tkey->off = 20;
+ res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey);
+ goto done;
+ }
+ return -1;
+
+ done:
+ *argc_p = argc;
+ *argv_p = argv;
+ return res;
+}
+
+static int
+parse_ip6(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey)
+{
+ int res = -1;
+ return res;
+}
+
+struct m_pedit_util p_pedit_ip = {
+ NULL,
+ "ip",
+ parse_ip,
+};
+
+
+struct m_pedit_util p_pedit_ip6 = {
+ NULL,
+ "ip6",
+ parse_ip6,
+};
diff --git a/tc/p_tcp.c b/tc/p_tcp.c
new file mode 100644
index 0000000..7f4b6f4
--- /dev/null
+++ b/tc/p_tcp.c
@@ -0,0 +1,38 @@
+/*
+ * m_pedit_tcp.c packet editor: TCP header
+ *
+ * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
+ *
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+#include "m_pedit.h"
+
+static int
+parse_tcp(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey)
+{
+ int res = -1;
+ return res;
+}
+struct m_pedit_util p_pedit_tcp = {
+ NULL,
+ "tcp",
+ parse_tcp,
+};
+
+
diff --git a/tc/p_udp.c b/tc/p_udp.c
new file mode 100644
index 0000000..1776289
--- /dev/null
+++ b/tc/p_udp.c
@@ -0,0 +1,38 @@
+/*
+ * m_pedit_udp.c packet editor: UDP header
+ *
+ * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
+ *
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+#include "m_pedit.h"
+
+static int
+parse_udp(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey)
+{
+ int res = -1;
+ return res;
+}
+
+struct m_pedit_util p_pedit_udp = {
+ NULL,
+ "udp",
+ parse_udp,
+};
+
diff --git a/tc/q_atm.c b/tc/q_atm.c
new file mode 100644
index 0000000..4c8dc0b
--- /dev/null
+++ b/tc/q_atm.c
@@ -0,0 +1,260 @@
+/*
+ * q_atm.c ATM.
+ *
+ * Hacked 1998-2000 by Werner Almesberger, EPFL ICA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <atm.h>
+#include <linux/atmdev.h>
+#include <linux/atmarp.h>
+
+#include "utils.h"
+#include "tc_util.h"
+
+
+#define MAX_HDR_LEN 64
+
+#define usage() return(-1)
+
+
+static int atm_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ if (argc) {
+ fprintf(stderr,"Usage: atm\n");
+ return -1;
+ }
+ return 0;
+}
+
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... atm ( pvc ADDR | svc ADDR [ sap SAP ] ) "
+ "[ qos QOS ] [ sndbuf BYTES ]\n");
+ fprintf(stderr, " [ hdr HEX... ] [ excess ( CLASSID | clp ) ] "
+ "[ clip ]\n");
+}
+
+
+static int atm_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
+ struct nlmsghdr *n)
+{
+ struct sockaddr_atmsvc addr;
+ struct atm_qos qos;
+ struct atm_sap sap;
+ unsigned char hdr[MAX_HDR_LEN];
+ __u32 excess = 0;
+ struct rtattr *tail;
+ int sndbuf = 0;
+ int hdr_len = -1;
+ int set_clip = 0;
+ int s;
+
+ memset(&addr,0,sizeof(addr));
+ (void) text2qos("aal5,ubr:sdu=9180,rx:none",&qos,0);
+ (void) text2sap("blli:l2=iso8802",&sap,0);
+ while (argc > 0) {
+ if (!strcmp(*argv,"pvc")) {
+ NEXT_ARG();
+ if (text2atm(*argv,(struct sockaddr *) &addr,
+ sizeof(addr),T2A_PVC | T2A_NAME) < 0) {
+ explain();
+ return -1;
+ }
+ }
+ else if (!strcmp(*argv,"svc")) {
+ NEXT_ARG();
+ if (text2atm(*argv,(struct sockaddr *) &addr,
+ sizeof(addr),T2A_SVC | T2A_NAME) < 0) {
+ explain();
+ return -1;
+ }
+ }
+ else if (!strcmp(*argv,"qos")) {
+ NEXT_ARG();
+ if (text2qos(*argv,&qos,0) < 0) {
+ explain();
+ return -1;
+ }
+ }
+ else if (!strcmp(*argv,"sndbuf")) {
+ char *end;
+
+ NEXT_ARG();
+ sndbuf = strtol(*argv,&end,0);
+ if (*end) {
+ explain();
+ return -1;
+ }
+ }
+ else if (!strcmp(*argv,"sap")) {
+ NEXT_ARG();
+ if (addr.sas_family != AF_ATMSVC ||
+ text2sap(*argv,&sap,T2A_NAME) < 0) {
+ explain();
+ return -1;
+ }
+ }
+ else if (!strcmp(*argv,"hdr")) {
+ unsigned char *ptr;
+ char *walk;
+
+ NEXT_ARG();
+ ptr = hdr;
+ for (walk = *argv; *walk; walk++) {
+ int tmp;
+
+ if (ptr == hdr+MAX_HDR_LEN) {
+ fprintf(stderr,"header is too long\n");
+ return -1;
+ }
+ if (*walk == '.') continue;
+ if (!isxdigit(walk[0]) || !walk[1] ||
+ !isxdigit(walk[1])) {
+ explain();
+ return -1;
+ }
+ sscanf(walk,"%2x",&tmp);
+ *ptr++ = tmp;
+ walk++;
+ }
+ hdr_len = ptr-hdr;
+ }
+ else if (!strcmp(*argv,"excess")) {
+ NEXT_ARG();
+ if (!strcmp(*argv,"clp")) excess = 0;
+ else if (get_tc_classid(&excess,*argv)) {
+ explain();
+ return -1;
+ }
+ }
+ else if (!strcmp(*argv,"clip")) {
+ set_clip = 1;
+ }
+ else {
+ explain();
+ return 1;
+ }
+ argc--;
+ argv++;
+ }
+ s = socket(addr.sas_family,SOCK_DGRAM,0);
+ if (s < 0) {
+ perror("socket");
+ return -1;
+ }
+ if (setsockopt(s,SOL_ATM,SO_ATMQOS,&qos,sizeof(qos)) < 0) {
+ perror("SO_ATMQOS");
+ return -1;
+ }
+ if (sndbuf)
+ if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
+ perror("SO_SNDBUF");
+ return -1;
+ }
+ if (addr.sas_family == AF_ATMSVC && setsockopt(s,SOL_ATM,SO_ATMSAP,
+ &sap,sizeof(sap)) < 0) {
+ perror("SO_ATMSAP");
+ return -1;
+ }
+ if (connect(s,(struct sockaddr *) &addr,addr.sas_family == AF_ATMPVC ?
+ sizeof(struct sockaddr_atmpvc) : sizeof(addr)) < 0) {
+ perror("connect");
+ return -1;
+ }
+ if (set_clip)
+ if (ioctl(s,ATMARP_MKIP,0) < 0) {
+ perror("ioctl ATMARP_MKIP");
+ return -1;
+ }
+ tail = NLMSG_TAIL(n);
+ addattr_l(n,1024,TCA_OPTIONS,NULL,0);
+ addattr_l(n,1024,TCA_ATM_FD,&s,sizeof(s));
+ if (excess) addattr_l(n,1024,TCA_ATM_EXCESS,&excess,sizeof(excess));
+ if (hdr_len != -1) addattr_l(n,1024,TCA_ATM_HDR,hdr,hdr_len);
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+
+
+static int atm_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct rtattr *tb[TCA_ATM_MAX+1];
+ char buffer[MAX_ATM_ADDR_LEN+1];
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_ATM_MAX, opt);
+ if (tb[TCA_ATM_ADDR]) {
+ if (RTA_PAYLOAD(tb[TCA_ATM_ADDR]) <
+ sizeof(struct sockaddr_atmpvc))
+ fprintf(stderr,"ATM: address too short\n");
+ else {
+ if (atm2text(buffer,MAX_ATM_ADDR_LEN,
+ RTA_DATA(tb[TCA_ATM_ADDR]),A2T_PRETTY | A2T_NAME) <
+ 0) fprintf(stderr,"atm2text error\n");
+ fprintf(f,"pvc %s ",buffer);
+ }
+ }
+ if (tb[TCA_ATM_HDR]) {
+ int i;
+
+ fprintf(f,"hdr");
+ for (i = 0; i < RTA_PAYLOAD(tb[TCA_ATM_HDR]); i++)
+ fprintf(f,"%c%02x",i ? '.' : ' ',
+ ((unsigned char *) RTA_DATA(tb[TCA_ATM_HDR]))[i]);
+ if (!i) fprintf(f," .");
+ fprintf(f," ");
+ }
+ if (tb[TCA_ATM_EXCESS]) {
+ __u32 excess;
+
+ if (RTA_PAYLOAD(tb[TCA_ATM_EXCESS]) < sizeof(excess))
+ fprintf(stderr,"ATM: excess class ID too short\n");
+ else {
+ excess = *(__u32 *) RTA_DATA(tb[TCA_ATM_EXCESS]);
+ if (!excess) fprintf(f,"excess clp ");
+ else {
+ char buf[64];
+
+ print_tc_classid(buf,sizeof(buf),excess);
+ fprintf(f,"excess %s ",buf);
+ }
+ }
+ }
+ if (tb[TCA_ATM_STATE]) {
+ static const char *map[] = { ATM_VS2TXT_MAP };
+ int state;
+
+ if (RTA_PAYLOAD(tb[TCA_ATM_STATE]) < sizeof(state))
+ fprintf(stderr,"ATM: state field too short\n");
+ else {
+ state = *(int *) RTA_DATA(tb[TCA_ATM_STATE]);
+ fprintf(f,"%s ",map[state]);
+ }
+ }
+ return 0;
+}
+
+
+struct qdisc_util atm_qdisc_util = {
+ .id = "atm",
+ .parse_qopt = atm_parse_opt,
+ .print_qopt = atm_print_opt,
+ .parse_copt = atm_parse_class_opt,
+ .print_copt = atm_print_opt,
+};
diff --git a/tc/q_cbq.c b/tc/q_cbq.c
new file mode 100644
index 0000000..c99dc3b
--- /dev/null
+++ b/tc/q_cbq.c
@@ -0,0 +1,581 @@
+/*
+ * q_cbq.c CBQ.
+ *
+ * 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 "utils.h"
+#include "tc_util.h"
+#include "tc_cbq.h"
+
+static void explain_class(void)
+{
+ fprintf(stderr, "Usage: ... cbq bandwidth BPS rate BPS maxburst PKTS [ avpkt BYTES ]\n");
+ fprintf(stderr, " [ minburst PKTS ] [ bounded ] [ isolated ]\n");
+ fprintf(stderr, " [ allot BYTES ] [ mpu BYTES ] [ weight RATE ]\n");
+ fprintf(stderr, " [ prio NUMBER ] [ cell BYTES ] [ ewma LOG ]\n");
+ fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n");
+ fprintf(stderr, " [ split CLASSID ] [ defmap MASK/CHANGE ]\n");
+ fprintf(stderr, " [ overhead BYTES ] [ linklayer TYPE ]\n");
+}
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... cbq bandwidth BPS avpkt BYTES [ mpu BYTES ]\n");
+ fprintf(stderr, " [ cell BYTES ] [ ewma LOG ]\n");
+}
+
+static void explain1(char *arg)
+{
+ fprintf(stderr, "Illegal \"%s\"\n", arg);
+}
+
+#define usage() return(-1)
+
+static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ struct tc_ratespec r;
+ struct tc_cbq_lssopt lss;
+ __u32 rtab[256];
+ unsigned mpu=0, avpkt=0, allot=0;
+ unsigned short overhead=0;
+ unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
+ int cell_log=-1;
+ int ewma_log=-1;
+ struct rtattr *tail;
+
+ memset(&lss, 0, sizeof(lss));
+ memset(&r, 0, sizeof(r));
+
+ while (argc > 0) {
+ if (matches(*argv, "bandwidth") == 0 ||
+ matches(*argv, "rate") == 0) {
+ NEXT_ARG();
+ if (get_rate(&r.rate, *argv)) {
+ explain1("bandwidth");
+ return -1;
+ }
+ } else if (matches(*argv, "ewma") == 0) {
+ NEXT_ARG();
+ if (get_integer(&ewma_log, *argv, 0)) {
+ explain1("ewma");
+ return -1;
+ }
+ if (ewma_log > 31) {
+ fprintf(stderr, "ewma_log must be < 32\n");
+ return -1;
+ }
+ } else if (matches(*argv, "cell") == 0) {
+ unsigned cell;
+ int i;
+ NEXT_ARG();
+ if (get_size(&cell, *argv)) {
+ explain1("cell");
+ return -1;
+ }
+ for (i=0; i<32; i++)
+ if ((1<<i) == cell)
+ break;
+ if (i>=32) {
+ fprintf(stderr, "cell must be 2^n\n");
+ return -1;
+ }
+ cell_log = i;
+ } else if (matches(*argv, "avpkt") == 0) {
+ NEXT_ARG();
+ if (get_size(&avpkt, *argv)) {
+ explain1("avpkt");
+ return -1;
+ }
+ } else if (matches(*argv, "mpu") == 0) {
+ NEXT_ARG();
+ if (get_size(&mpu, *argv)) {
+ explain1("mpu");
+ return -1;
+ }
+ } else if (matches(*argv, "allot") == 0) {
+ NEXT_ARG();
+ /* Accept and ignore "allot" for backward compatibility */
+ if (get_size(&allot, *argv)) {
+ explain1("allot");
+ return -1;
+ }
+ } else if (matches(*argv, "overhead") == 0) {
+ NEXT_ARG();
+ if (get_u16(&overhead, *argv, 10)) {
+ explain1("overhead"); return -1;
+ }
+ } else if (matches(*argv, "linklayer") == 0) {
+ NEXT_ARG();
+ if (get_linklayer(&linklayer, *argv)) {
+ explain1("linklayer"); return -1;
+ }
+ } else if (matches(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ /* OK. All options are parsed. */
+
+ if (r.rate == 0) {
+ fprintf(stderr, "CBQ: bandwidth is required parameter.\n");
+ return -1;
+ }
+ if (avpkt == 0) {
+ fprintf(stderr, "CBQ: \"avpkt\" is required.\n");
+ return -1;
+ }
+ if (allot < (avpkt*3)/2)
+ allot = (avpkt*3)/2;
+
+ r.mpu = mpu;
+ r.overhead = overhead;
+ if (tc_calc_rtable(&r, rtab, cell_log, allot, linklayer) < 0) {
+ fprintf(stderr, "CBQ: failed to calculate rate table.\n");
+ return -1;
+ }
+
+ if (ewma_log < 0)
+ ewma_log = TC_CBQ_DEF_EWMA;
+ lss.ewma_log = ewma_log;
+ lss.maxidle = tc_calc_xmittime(r.rate, avpkt);
+ lss.change = TCF_CBQ_LSS_MAXIDLE|TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
+ lss.avpkt = avpkt;
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r));
+ addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss));
+ addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024);
+ if (show_raw) {
+ int i;
+ for (i=0; i<256; i++)
+ printf("%u ", rtab[i]);
+ printf("\n");
+ }
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ int wrr_ok=0, fopt_ok=0;
+ struct tc_ratespec r;
+ struct tc_cbq_lssopt lss;
+ struct tc_cbq_wrropt wrr;
+ struct tc_cbq_fopt fopt;
+ struct tc_cbq_ovl ovl;
+ __u32 rtab[256];
+ unsigned mpu=0;
+ int cell_log=-1;
+ int ewma_log=-1;
+ unsigned bndw = 0;
+ unsigned minburst=0, maxburst=0;
+ unsigned short overhead=0;
+ unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
+ struct rtattr *tail;
+
+ memset(&r, 0, sizeof(r));
+ memset(&lss, 0, sizeof(lss));
+ memset(&wrr, 0, sizeof(wrr));
+ memset(&fopt, 0, sizeof(fopt));
+ memset(&ovl, 0, sizeof(ovl));
+
+ while (argc > 0) {
+ if (matches(*argv, "rate") == 0) {
+ NEXT_ARG();
+ if (get_rate(&r.rate, *argv)) {
+ explain1("rate");
+ return -1;
+ }
+ } else if (matches(*argv, "bandwidth") == 0) {
+ NEXT_ARG();
+ if (get_rate(&bndw, *argv)) {
+ explain1("bandwidth");
+ return -1;
+ }
+ } else if (matches(*argv, "minidle") == 0) {
+ NEXT_ARG();
+ if (get_u32(&lss.minidle, *argv, 0)) {
+ explain1("minidle");
+ return -1;
+ }
+ lss.change |= TCF_CBQ_LSS_MINIDLE;
+ } else if (matches(*argv, "minburst") == 0) {
+ NEXT_ARG();
+ if (get_u32(&minburst, *argv, 0)) {
+ explain1("minburst");
+ return -1;
+ }
+ lss.change |= TCF_CBQ_LSS_OFFTIME;
+ } else if (matches(*argv, "maxburst") == 0) {
+ NEXT_ARG();
+ if (get_u32(&maxburst, *argv, 0)) {
+ explain1("maxburst");
+ return -1;
+ }
+ lss.change |= TCF_CBQ_LSS_MAXIDLE;
+ } else if (matches(*argv, "bounded") == 0) {
+ lss.flags |= TCF_CBQ_LSS_BOUNDED;
+ lss.change |= TCF_CBQ_LSS_FLAGS;
+ } else if (matches(*argv, "borrow") == 0) {
+ lss.flags &= ~TCF_CBQ_LSS_BOUNDED;
+ lss.change |= TCF_CBQ_LSS_FLAGS;
+ } else if (matches(*argv, "isolated") == 0) {
+ lss.flags |= TCF_CBQ_LSS_ISOLATED;
+ lss.change |= TCF_CBQ_LSS_FLAGS;
+ } else if (matches(*argv, "sharing") == 0) {
+ lss.flags &= ~TCF_CBQ_LSS_ISOLATED;
+ lss.change |= TCF_CBQ_LSS_FLAGS;
+ } else if (matches(*argv, "ewma") == 0) {
+ NEXT_ARG();
+ if (get_integer(&ewma_log, *argv, 0)) {
+ explain1("ewma");
+ return -1;
+ }
+ if (ewma_log > 31) {
+ fprintf(stderr, "ewma_log must be < 32\n");
+ return -1;
+ }
+ lss.change |= TCF_CBQ_LSS_EWMA;
+ } else if (matches(*argv, "cell") == 0) {
+ unsigned cell;
+ int i;
+ NEXT_ARG();
+ if (get_size(&cell, *argv)) {
+ explain1("cell");
+ return -1;
+ }
+ for (i=0; i<32; i++)
+ if ((1<<i) == cell)
+ break;
+ if (i>=32) {
+ fprintf(stderr, "cell must be 2^n\n");
+ return -1;
+ }
+ cell_log = i;
+ } else if (matches(*argv, "prio") == 0) {
+ unsigned prio;
+ NEXT_ARG();
+ if (get_u32(&prio, *argv, 0)) {
+ explain1("prio");
+ return -1;
+ }
+ if (prio > TC_CBQ_MAXPRIO) {
+ fprintf(stderr, "\"prio\" must be number in the range 1...%d\n", TC_CBQ_MAXPRIO);
+ return -1;
+ }
+ wrr.priority = prio;
+ wrr_ok++;
+ } else if (matches(*argv, "allot") == 0) {
+ NEXT_ARG();
+ if (get_size(&wrr.allot, *argv)) {
+ explain1("allot");
+ return -1;
+ }
+ } else if (matches(*argv, "avpkt") == 0) {
+ NEXT_ARG();
+ if (get_size(&lss.avpkt, *argv)) {
+ explain1("avpkt");
+ return -1;
+ }
+ lss.change |= TCF_CBQ_LSS_AVPKT;
+ } else if (matches(*argv, "mpu") == 0) {
+ NEXT_ARG();
+ if (get_size(&mpu, *argv)) {
+ explain1("mpu");
+ return -1;
+ }
+ } else if (matches(*argv, "weight") == 0) {
+ NEXT_ARG();
+ if (get_size(&wrr.weight, *argv)) {
+ explain1("weight");
+ return -1;
+ }
+ wrr_ok++;
+ } else if (matches(*argv, "split") == 0) {
+ NEXT_ARG();
+ if (get_tc_classid(&fopt.split, *argv)) {
+ fprintf(stderr, "Invalid split node ID.\n");
+ usage();
+ }
+ fopt_ok++;
+ } else if (matches(*argv, "defmap") == 0) {
+ int err;
+ NEXT_ARG();
+ err = sscanf(*argv, "%08x/%08x", &fopt.defmap, &fopt.defchange);
+ if (err < 1) {
+ fprintf(stderr, "Invalid defmap, should be MASK32[/MASK]\n");
+ return -1;
+ }
+ if (err == 1)
+ fopt.defchange = ~0;
+ fopt_ok++;
+ } else if (matches(*argv, "overhead") == 0) {
+ NEXT_ARG();
+ if (get_u16(&overhead, *argv, 10)) {
+ explain1("overhead"); return -1;
+ }
+ } else if (matches(*argv, "linklayer") == 0) {
+ NEXT_ARG();
+ if (get_linklayer(&linklayer, *argv)) {
+ explain1("linklayer"); return -1;
+ }
+ } else if (matches(*argv, "help") == 0) {
+ explain_class();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain_class();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ /* OK. All options are parsed. */
+
+ /* 1. Prepare link sharing scheduler parameters */
+ if (r.rate) {
+ unsigned pktsize = wrr.allot;
+ if (wrr.allot < (lss.avpkt*3)/2)
+ wrr.allot = (lss.avpkt*3)/2;
+ r.mpu = mpu;
+ r.overhead = overhead;
+ if (tc_calc_rtable(&r, rtab, cell_log, pktsize, linklayer) < 0) {
+ fprintf(stderr, "CBQ: failed to calculate rate table.\n");
+ return -1;
+ }
+ }
+ if (ewma_log < 0)
+ ewma_log = TC_CBQ_DEF_EWMA;
+ lss.ewma_log = ewma_log;
+ if (lss.change&(TCF_CBQ_LSS_OFFTIME|TCF_CBQ_LSS_MAXIDLE)) {
+ if (lss.avpkt == 0) {
+ fprintf(stderr, "CBQ: avpkt is required for max/minburst.\n");
+ return -1;
+ }
+ if (bndw==0 || r.rate == 0) {
+ fprintf(stderr, "CBQ: bandwidth&rate are required for max/minburst.\n");
+ return -1;
+ }
+ }
+ if (wrr.priority == 0 && (n->nlmsg_flags&NLM_F_EXCL)) {
+ wrr_ok = 1;
+ wrr.priority = TC_CBQ_MAXPRIO;
+ if (wrr.allot == 0)
+ wrr.allot = (lss.avpkt*3)/2;
+ }
+ if (wrr_ok) {
+ if (wrr.weight == 0)
+ wrr.weight = (wrr.priority == TC_CBQ_MAXPRIO) ? 1 : r.rate;
+ if (wrr.allot == 0) {
+ fprintf(stderr, "CBQ: \"allot\" is required to set WRR parameters.\n");
+ return -1;
+ }
+ }
+ if (lss.change&TCF_CBQ_LSS_MAXIDLE) {
+ lss.maxidle = tc_cbq_calc_maxidle(bndw, r.rate, lss.avpkt, ewma_log, maxburst);
+ lss.change |= TCF_CBQ_LSS_MAXIDLE;
+ lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
+ }
+ if (lss.change&TCF_CBQ_LSS_OFFTIME) {
+ lss.offtime = tc_cbq_calc_offtime(bndw, r.rate, lss.avpkt, ewma_log, minburst);
+ lss.change |= TCF_CBQ_LSS_OFFTIME;
+ lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
+ }
+ if (lss.change&TCF_CBQ_LSS_MINIDLE) {
+ lss.minidle <<= lss.ewma_log;
+ lss.change |= TCF_CBQ_LSS_EWMA;
+ }
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ if (lss.change) {
+ lss.change |= TCF_CBQ_LSS_FLAGS;
+ addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss));
+ }
+ if (wrr_ok)
+ addattr_l(n, 1024, TCA_CBQ_WRROPT, &wrr, sizeof(wrr));
+ if (fopt_ok)
+ addattr_l(n, 1024, TCA_CBQ_FOPT, &fopt, sizeof(fopt));
+ if (r.rate) {
+ addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r));
+ addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024);
+ if (show_raw) {
+ int i;
+ for (i=0; i<256; i++)
+ printf("%u ", rtab[i]);
+ printf("\n");
+ }
+ }
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+
+static int cbq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct rtattr *tb[TCA_CBQ_MAX+1];
+ struct tc_ratespec *r = NULL;
+ struct tc_cbq_lssopt *lss = NULL;
+ struct tc_cbq_wrropt *wrr = NULL;
+ struct tc_cbq_fopt *fopt = NULL;
+ struct tc_cbq_ovl *ovl = NULL;
+ SPRINT_BUF(b1);
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_CBQ_MAX, opt);
+
+ if (tb[TCA_CBQ_RATE]) {
+ if (RTA_PAYLOAD(tb[TCA_CBQ_RATE]) < sizeof(*r))
+ fprintf(stderr, "CBQ: too short rate opt\n");
+ else
+ r = RTA_DATA(tb[TCA_CBQ_RATE]);
+ }
+ if (tb[TCA_CBQ_LSSOPT]) {
+ if (RTA_PAYLOAD(tb[TCA_CBQ_LSSOPT]) < sizeof(*lss))
+ fprintf(stderr, "CBQ: too short lss opt\n");
+ else
+ lss = RTA_DATA(tb[TCA_CBQ_LSSOPT]);
+ }
+ if (tb[TCA_CBQ_WRROPT]) {
+ if (RTA_PAYLOAD(tb[TCA_CBQ_WRROPT]) < sizeof(*wrr))
+ fprintf(stderr, "CBQ: too short wrr opt\n");
+ else
+ wrr = RTA_DATA(tb[TCA_CBQ_WRROPT]);
+ }
+ if (tb[TCA_CBQ_FOPT]) {
+ if (RTA_PAYLOAD(tb[TCA_CBQ_FOPT]) < sizeof(*fopt))
+ fprintf(stderr, "CBQ: too short fopt\n");
+ else
+ fopt = RTA_DATA(tb[TCA_CBQ_FOPT]);
+ }
+ if (tb[TCA_CBQ_OVL_STRATEGY]) {
+ if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl))
+ fprintf(stderr, "CBQ: too short overlimit strategy %u/%u\n",
+ (unsigned) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]),
+ (unsigned) sizeof(*ovl));
+ else
+ ovl = RTA_DATA(tb[TCA_CBQ_OVL_STRATEGY]);
+ }
+
+ if (r) {
+ char buf[64];
+ print_rate(buf, sizeof(buf), r->rate);
+ fprintf(f, "rate %s ", buf);
+ if (show_details) {
+ fprintf(f, "cell %ub ", 1<<r->cell_log);
+ if (r->mpu)
+ fprintf(f, "mpu %ub ", r->mpu);
+ if (r->overhead)
+ fprintf(f, "overhead %ub ", r->overhead);
+ }
+ }
+ if (lss && lss->flags) {
+ int comma=0;
+ fprintf(f, "(");
+ if (lss->flags&TCF_CBQ_LSS_BOUNDED) {
+ fprintf(f, "bounded");
+ comma=1;
+ }
+ if (lss->flags&TCF_CBQ_LSS_ISOLATED) {
+ if (comma)
+ fprintf(f, ",");
+ fprintf(f, "isolated");
+ }
+ fprintf(f, ") ");
+ }
+ if (wrr) {
+ if (wrr->priority != TC_CBQ_MAXPRIO)
+ fprintf(f, "prio %u", wrr->priority);
+ else
+ fprintf(f, "prio no-transmit");
+ if (show_details) {
+ char buf[64];
+ fprintf(f, "/%u ", wrr->cpriority);
+ if (wrr->weight != 1) {
+ print_rate(buf, sizeof(buf), wrr->weight);
+ fprintf(f, "weight %s ", buf);
+ }
+ if (wrr->allot)
+ fprintf(f, "allot %ub ", wrr->allot);
+ }
+ }
+ if (lss && show_details) {
+ fprintf(f, "\nlevel %u ewma %u avpkt %ub ", lss->level, lss->ewma_log, lss->avpkt);
+ if (lss->maxidle) {
+ fprintf(f, "maxidle %s ", sprint_ticks(lss->maxidle>>lss->ewma_log, b1));
+ if (show_raw)
+ fprintf(f, "[%08x] ", lss->maxidle);
+ }
+ if (lss->minidle!=0x7fffffff) {
+ fprintf(f, "minidle %s ", sprint_ticks(lss->minidle>>lss->ewma_log, b1));
+ if (show_raw)
+ fprintf(f, "[%08x] ", lss->minidle);
+ }
+ if (lss->offtime) {
+ fprintf(f, "offtime %s ", sprint_ticks(lss->offtime, b1));
+ if (show_raw)
+ fprintf(f, "[%08x] ", lss->offtime);
+ }
+ }
+ if (fopt && show_details) {
+ char buf[64];
+ print_tc_classid(buf, sizeof(buf), fopt->split);
+ fprintf(f, "\nsplit %s ", buf);
+ if (fopt->defmap) {
+ fprintf(f, "defmap %08x", fopt->defmap);
+ }
+ }
+ return 0;
+}
+
+static int cbq_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
+{
+ struct tc_cbq_xstats *st;
+
+ if (xstats == NULL)
+ return 0;
+
+ if (RTA_PAYLOAD(xstats) < sizeof(*st))
+ return -1;
+
+ st = RTA_DATA(xstats);
+ fprintf(f, " borrowed %u overactions %u avgidle %g undertime %g", st->borrows,
+ st->overactions, (double)st->avgidle, (double)st->undertime);
+ return 0;
+}
+
+struct qdisc_util cbq_qdisc_util = {
+ .id = "cbq",
+ .parse_qopt = cbq_parse_opt,
+ .print_qopt = cbq_print_opt,
+ .print_xstats = cbq_print_xstats,
+ .parse_copt = cbq_parse_class_opt,
+ .print_copt = cbq_print_opt,
+};
+
diff --git a/tc/q_drr.c b/tc/q_drr.c
new file mode 100644
index 0000000..7d2d874
--- /dev/null
+++ b/tc/q_drr.c
@@ -0,0 +1,124 @@
+/*
+ * q_drr.c DRR.
+ *
+ * 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: Patrick McHardy <kaber@trash.net>
+ *
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... drr\n");
+}
+
+static void explain2(void)
+{
+ fprintf(stderr, "Usage: ... drr quantum SIZE\n");
+}
+
+#define usage() return(-1)
+
+static int drr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ while (argc > 0) {
+ if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+ return 0;
+}
+
+static int drr_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
+ struct nlmsghdr *n)
+{
+ struct rtattr *tail;
+ __u32 tmp;
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+
+ while (argc > 0) {
+ if (strcmp(*argv, "quantum") == 0) {
+ NEXT_ARG();
+ if (get_size(&tmp, *argv)) {
+ fprintf(stderr, "Illegal \"quantum\"\n");
+ return -1;
+ }
+ addattr_l(n, 1024, TCA_DRR_QUANTUM, &tmp, sizeof(tmp));
+ } else if (strcmp(*argv, "help") == 0) {
+ explain2();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain2();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *)tail;
+ return 0;
+}
+
+static int drr_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct rtattr *tb[TCA_DRR_MAX + 1];
+ SPRINT_BUF(b1);
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_DRR_MAX, opt);
+
+ if (tb[TCA_DRR_QUANTUM])
+ fprintf(f, "quantum %s ",
+ sprint_size(*(__u32 *)RTA_DATA(tb[TCA_DRR_QUANTUM]), b1));
+ return 0;
+}
+
+static int drr_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
+{
+ struct tc_drr_stats *x;
+ SPRINT_BUF(b1);
+
+ if (xstats == NULL)
+ return 0;
+ if (RTA_PAYLOAD(xstats) < sizeof(*x))
+ return -1;
+ x = RTA_DATA(xstats);
+
+ fprintf(f, " deficit %s ", sprint_size(x->deficit, b1));
+ return 0;
+}
+
+struct qdisc_util drr_qdisc_util = {
+ .id = "drr",
+ .parse_qopt = drr_parse_opt,
+ .print_qopt = drr_print_opt,
+ .print_xstats = drr_print_xstats,
+ .parse_copt = drr_parse_class_opt,
+ .print_copt = drr_print_opt,
+};
diff --git a/tc/q_dsmark.c b/tc/q_dsmark.c
new file mode 100644
index 0000000..cdb5bf2
--- /dev/null
+++ b/tc/q_dsmark.c
@@ -0,0 +1,177 @@
+/*
+ * q_dsmark.c Differentiated Services field marking.
+ *
+ * Hacked 1998,1999 by Werner Almesberger, EPFL ICA
+ *
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+
+
+#define usage() return(-1)
+
+
+static void explain(void)
+{
+ fprintf(stderr,"Usage: dsmark indices INDICES [ default_index "
+ "DEFAULT_INDEX ] [ set_tc_index ]\n");
+}
+
+
+static int dsmark_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+ struct nlmsghdr *n)
+{
+ struct rtattr *tail;
+ __u16 ind;
+ char *end;
+ int dflt,set_tc_index;
+
+ ind = set_tc_index = 0;
+ dflt = -1;
+ while (argc > 0) {
+ if (!strcmp(*argv,"indices")) {
+ NEXT_ARG();
+ ind = strtoul(*argv,&end,0);
+ if (*end) {
+ explain();
+ return -1;
+ }
+ }
+ else if (!strcmp(*argv,"default_index") || !strcmp(*argv,
+ "default")) {
+ NEXT_ARG();
+ dflt = strtoul(*argv,&end,0);
+ if (*end) {
+ explain();
+ return -1;
+ }
+ }
+ else if (!strcmp(*argv,"set_tc_index")) {
+ set_tc_index = 1;
+ }
+ else {
+ explain();
+ return -1;
+ }
+ argc--;
+ argv++;
+ }
+ if (!ind) {
+ explain();
+ return -1;
+ }
+ tail = NLMSG_TAIL(n);
+ addattr_l(n,1024,TCA_OPTIONS,NULL,0);
+ addattr_l(n,1024,TCA_DSMARK_INDICES,&ind,sizeof(ind));
+ if (dflt != -1) {
+ __u16 tmp = dflt;
+
+ addattr_l(n,1024,TCA_DSMARK_DEFAULT_INDEX,&tmp,sizeof(tmp));
+ }
+ if (set_tc_index) addattr_l(n,1024,TCA_DSMARK_SET_TC_INDEX,NULL,0);
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+
+static void explain_class(void)
+{
+ fprintf(stderr, "Usage: ... dsmark [ mask MASK ] [ value VALUE ]\n");
+}
+
+
+static int dsmark_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
+ struct nlmsghdr *n)
+{
+ struct rtattr *tail;
+ __u8 tmp;
+ char *end;
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n,1024,TCA_OPTIONS,NULL,0);
+ while (argc > 0) {
+ if (!strcmp(*argv,"mask")) {
+ NEXT_ARG();
+ tmp = strtoul(*argv,&end,0);
+ if (*end) {
+ explain_class();
+ return -1;
+ }
+ addattr_l(n,1024,TCA_DSMARK_MASK,&tmp,1);
+ }
+ else if (!strcmp(*argv,"value")) {
+ NEXT_ARG();
+ tmp = strtoul(*argv,&end,0);
+ if (*end) {
+ explain_class();
+ return -1;
+ }
+ addattr_l(n,1024,TCA_DSMARK_VALUE,&tmp,1);
+ }
+ else {
+ explain_class();
+ return -1;
+ }
+ argc--;
+ argv++;
+ }
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+
+
+static int dsmark_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct rtattr *tb[TCA_DSMARK_MAX+1];
+
+ if (!opt) return 0;
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, TCA_DSMARK_MAX, RTA_DATA(opt), RTA_PAYLOAD(opt));
+ if (tb[TCA_DSMARK_MASK]) {
+ if (!RTA_PAYLOAD(tb[TCA_DSMARK_MASK]))
+ fprintf(stderr,"dsmark: empty mask\n");
+ else fprintf(f,"mask 0x%02x ",
+ *(__u8 *) RTA_DATA(tb[TCA_DSMARK_MASK]));
+ }
+ if (tb[TCA_DSMARK_VALUE]) {
+ if (!RTA_PAYLOAD(tb[TCA_DSMARK_VALUE]))
+ fprintf(stderr,"dsmark: empty value\n");
+ else fprintf(f,"value 0x%02x ",
+ *(__u8 *) RTA_DATA(tb[TCA_DSMARK_VALUE]));
+ }
+ if (tb[TCA_DSMARK_INDICES]) {
+ if (RTA_PAYLOAD(tb[TCA_DSMARK_INDICES]) < sizeof(__u16))
+ fprintf(stderr,"dsmark: indices too short\n");
+ else fprintf(f,"indices 0x%04x ",
+ *(__u16 *) RTA_DATA(tb[TCA_DSMARK_INDICES]));
+ }
+ if (tb[TCA_DSMARK_DEFAULT_INDEX]) {
+ if (RTA_PAYLOAD(tb[TCA_DSMARK_DEFAULT_INDEX]) < sizeof(__u16))
+ fprintf(stderr,"dsmark: default_index too short\n");
+ else fprintf(f,"default_index 0x%04x ",
+ *(__u16 *) RTA_DATA(tb[TCA_DSMARK_DEFAULT_INDEX]));
+ }
+ if (tb[TCA_DSMARK_SET_TC_INDEX]) fprintf(f,"set_tc_index ");
+ return 0;
+}
+
+
+struct qdisc_util dsmark_qdisc_util = {
+ .id = "dsmark",
+ .parse_qopt = dsmark_parse_opt,
+ .print_qopt = dsmark_print_opt,
+ .parse_copt = dsmark_parse_class_opt,
+ .print_copt = dsmark_print_opt,
+};
diff --git a/tc/q_fifo.c b/tc/q_fifo.c
new file mode 100644
index 0000000..9f3b3eb
--- /dev/null
+++ b/tc/q_fifo.c
@@ -0,0 +1,98 @@
+/*
+ * q_fifo.c FIFO.
+ *
+ * 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 "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... [p|b]fifo [ limit NUMBER ]\n");
+}
+
+#define usage() return(-1)
+
+static int fifo_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ int ok=0;
+ struct tc_fifo_qopt opt;
+ memset(&opt, 0, sizeof(opt));
+
+ while (argc > 0) {
+ if (strcmp(*argv, "limit") == 0) {
+ NEXT_ARG();
+ if (get_size(&opt.limit, *argv)) {
+ fprintf(stderr, "Illegal \"limit\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ if (ok)
+ addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
+ return 0;
+}
+
+static int fifo_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct tc_fifo_qopt *qopt;
+
+ if (opt == NULL)
+ return 0;
+
+ if (RTA_PAYLOAD(opt) < sizeof(*qopt))
+ return -1;
+ qopt = RTA_DATA(opt);
+ if (strcmp(qu->id, "bfifo") == 0) {
+ SPRINT_BUF(b1);
+ fprintf(f, "limit %s", sprint_size(qopt->limit, b1));
+ } else
+ fprintf(f, "limit %up", qopt->limit);
+ return 0;
+}
+
+
+struct qdisc_util bfifo_qdisc_util = {
+ .id = "bfifo",
+ .parse_qopt = fifo_parse_opt,
+ .print_qopt = fifo_print_opt,
+};
+
+struct qdisc_util pfifo_qdisc_util = {
+ .id = "pfifo",
+ .parse_qopt = fifo_parse_opt,
+ .print_qopt = fifo_print_opt,
+};
+
+extern int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt);
+struct qdisc_util pfifo_fast_qdisc_util = {
+ .id = "pfifo_fast",
+ .print_qopt = prio_print_opt,
+};
diff --git a/tc/q_gred.c b/tc/q_gred.c
new file mode 100644
index 0000000..ecef42e
--- /dev/null
+++ b/tc/q_gred.c
@@ -0,0 +1,317 @@
+/*
+ * q_gred.c GRED.
+ *
+ * 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: J Hadi Salim(hadi@nortelnetworks.com)
+ * code ruthlessly ripped from
+ * 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 "utils.h"
+#include "tc_util.h"
+
+#include "tc_red.h"
+
+
+#if 0
+#define DPRINTF(format,args...) fprintf(stderr,format,##args)
+#else
+#define DPRINTF(format,args...)
+#endif
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... gred DP drop-probability limit BYTES "
+ "min BYTES max BYTES\n");
+ fprintf(stderr, " avpkt BYTES burst PACKETS probability PROBABILITY "
+ "bandwidth KBPS\n");
+ fprintf(stderr, " [prio value]\n");
+ fprintf(stderr," OR ...\n");
+ fprintf(stderr," gred setup DPs <num of DPs> default <default DP> "
+ "[grio]\n");
+}
+
+#define usage() return(-1)
+
+static int init_gred(struct qdisc_util *qu, int argc, char **argv,
+ struct nlmsghdr *n)
+{
+
+ struct rtattr *tail;
+ struct tc_gred_sopt opt;
+ int dps = 0;
+ int def_dp = -1;
+
+ while (argc > 0) {
+ DPRINTF(stderr,"init_gred: invoked with %s\n",*argv);
+ if (strcmp(*argv, "DPs") == 0) {
+ NEXT_ARG();
+ DPRINTF(stderr,"init_gred: next_arg with %s\n",*argv);
+ dps = strtol(*argv, (char **)NULL, 10);
+ if (dps < 0 || dps >MAX_DPs) {
+ fprintf(stderr, "DPs =%d\n", dps);
+ fprintf(stderr, "Illegal \"DPs\"\n");
+ fprintf(stderr, "GRED: only %d DPs are "
+ "currently supported\n",MAX_DPs);
+ return -1;
+ }
+ } else if (strcmp(*argv, "default") == 0) {
+ NEXT_ARG();
+ def_dp = strtol(*argv, (char **)NULL, 10);
+ if (dps == 0) {
+ fprintf(stderr, "\"default DP\" must be "
+ "defined after DPs\n");
+ return -1;
+ }
+ if (def_dp < 0 || def_dp > dps) {
+ fprintf(stderr,
+ "\"default DP\" must be less than %d\n",
+ opt.DPs);
+ return -1;
+ }
+ } else if (strcmp(*argv, "grio") == 0) {
+ opt.grio=1;
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ if (!dps || def_dp == -1) {
+ fprintf(stderr, "Illegal gred setup parameters \n");
+ return -1;
+ }
+
+ memset(&opt, 0, sizeof(struct tc_gred_sopt));
+ opt.DPs = dps;
+ opt.def_DP = def_dp;
+
+ DPRINTF("TC_GRED: sending DPs=%d default=%d\n",opt.DPs,opt.def_DP);
+ n->nlmsg_flags|=NLM_F_CREATE;
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ addattr_l(n, 1024, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt));
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+/*
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+*/
+static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ int ok=0;
+ struct tc_gred_qopt opt;
+ unsigned burst = 0;
+ unsigned avpkt = 0;
+ double probability = 0.02;
+ unsigned rate = 0;
+ int wlog;
+ __u8 sbuf[256];
+ struct rtattr *tail;
+
+ memset(&opt, 0, sizeof(opt));
+
+ while (argc > 0) {
+ if (strcmp(*argv, "limit") == 0) {
+ NEXT_ARG();
+ if (get_size(&opt.limit, *argv)) {
+ fprintf(stderr, "Illegal \"limit\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "setup") == 0) {
+ if (ok) {
+ fprintf(stderr, "Illegal \"setup\"\n");
+ return -1;
+ }
+ return init_gred(qu,argc-1, argv+1,n);
+
+ } else if (strcmp(*argv, "min") == 0) {
+ NEXT_ARG();
+ if (get_size(&opt.qth_min, *argv)) {
+ fprintf(stderr, "Illegal \"min\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "max") == 0) {
+ NEXT_ARG();
+ if (get_size(&opt.qth_max, *argv)) {
+ fprintf(stderr, "Illegal \"max\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "DP") == 0) {
+ NEXT_ARG();
+ opt.DP=strtol(*argv, (char **)NULL, 10);
+ DPRINTF ("\n ******* DP =%u\n",opt.DP);
+ if (opt.DP >MAX_DPs) { /* need a better error check */
+ fprintf(stderr, "DP =%u \n",opt.DP);
+ fprintf(stderr, "Illegal \"DP\"\n");
+ fprintf(stderr, "GRED: only %d DPs are currently supported\n",MAX_DPs);
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "burst") == 0) {
+ NEXT_ARG();
+ if (get_unsigned(&burst, *argv, 0)) {
+ fprintf(stderr, "Illegal \"burst\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "avpkt") == 0) {
+ NEXT_ARG();
+ if (get_size(&avpkt, *argv)) {
+ fprintf(stderr, "Illegal \"avpkt\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "probability") == 0) {
+ NEXT_ARG();
+ if (sscanf(*argv, "%lg", &probability) != 1) {
+ fprintf(stderr, "Illegal \"probability\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "prio") == 0) {
+ NEXT_ARG();
+ opt.prio=strtol(*argv, (char **)NULL, 10);
+ /* some error check here */
+ ok++;
+ } else if (strcmp(*argv, "bandwidth") == 0) {
+ NEXT_ARG();
+ if (get_rate(&rate, *argv)) {
+ fprintf(stderr, "Illegal \"bandwidth\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ if (!ok)
+ return 0;
+
+ if (rate == 0)
+ get_rate(&rate, "10Mbit");
+
+ if (!opt.qth_min || !opt.qth_max || !burst || !opt.limit || !avpkt ||
+ (opt.DP<0)) {
+ fprintf(stderr, "Required parameter (min, max, burst, limit, "
+ "avpket, DP) is missing\n");
+ return -1;
+ }
+
+ if ((wlog = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) {
+ fprintf(stderr, "GRED: failed to calculate EWMA constant.\n");
+ return -1;
+ }
+ if (wlog >= 10)
+ fprintf(stderr, "GRED: WARNING. Burst %d seems to be to "
+ "large.\n", burst);
+ opt.Wlog = wlog;
+ if ((wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) {
+ fprintf(stderr, "GRED: failed to calculate probability.\n");
+ return -1;
+ }
+ opt.Plog = wlog;
+ if ((wlog = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0)
+ {
+ fprintf(stderr, "GRED: failed to calculate idle damping "
+ "table.\n");
+ return -1;
+ }
+ opt.Scell_log = wlog;
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ addattr_l(n, 1024, TCA_GRED_PARMS, &opt, sizeof(opt));
+ addattr_l(n, 1024, TCA_GRED_STAB, sbuf, 256);
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct rtattr *tb[TCA_GRED_STAB+1];
+ struct tc_gred_qopt *qopt;
+ int i;
+ SPRINT_BUF(b1);
+ SPRINT_BUF(b2);
+ SPRINT_BUF(b3);
+ SPRINT_BUF(b4);
+ SPRINT_BUF(b5);
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_GRED_STAB, opt);
+
+ if (tb[TCA_GRED_PARMS] == NULL)
+ return -1;
+
+ qopt = RTA_DATA(tb[TCA_GRED_PARMS]);
+ if (RTA_PAYLOAD(tb[TCA_GRED_PARMS]) < sizeof(*qopt)*MAX_DPs) {
+ fprintf(f,"\n GRED received message smaller than expected\n");
+ return -1;
+ }
+
+/* Bad hack! should really return a proper message as shown above*/
+
+ for (i=0;i<MAX_DPs;i++, qopt++) {
+ if (qopt->DP >= MAX_DPs) continue;
+ fprintf(f, "\n DP:%d (prio %d) Average Queue %s Measured "
+ "Queue %s ",
+ qopt->DP,
+ qopt->prio,
+ sprint_size(qopt->qave, b4),
+ sprint_size(qopt->backlog, b5));
+ fprintf(f, "\n\t Packet drops: %d (forced %d early %d) ",
+ qopt->forced+qopt->early,
+ qopt->forced,
+ qopt->early);
+ fprintf(f, "\n\t Packet totals: %u (bytes %u) ",
+ qopt->packets,
+ qopt->bytesin);
+ if (show_details)
+ fprintf(f, "\n limit %s min %s max %s ",
+ sprint_size(qopt->limit, b1),
+ sprint_size(qopt->qth_min, b2),
+ sprint_size(qopt->qth_max, b3));
+ fprintf(f, "ewma %u Plog %u Scell_log %u",
+ qopt->Wlog, qopt->Plog, qopt->Scell_log);
+ }
+ return 0;
+}
+
+struct qdisc_util gred_qdisc_util = {
+ .id = "gred",
+ .parse_qopt = gred_parse_opt,
+ .print_qopt = gred_print_opt,
+};
diff --git a/tc/q_hfsc.c b/tc/q_hfsc.c
new file mode 100644
index 0000000..b190c71
--- /dev/null
+++ b/tc/q_hfsc.c
@@ -0,0 +1,406 @@
+/*
+ * q_hfsc.c HFSC.
+ *
+ * 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: Patrick McHardy, <kaber@trash.net>
+ *
+ */
+
+#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"
+
+static int hfsc_get_sc(int *, char ***, struct tc_service_curve *);
+
+
+static void
+explain_qdisc(void)
+{
+ fprintf(stderr,
+ "Usage: ... hfsc [ default CLASSID ]\n"
+ "\n"
+ " default: default class for unclassified packets\n"
+ );
+}
+
+static void
+explain_class(void)
+{
+ fprintf(stderr,
+ "Usage: ... hfsc [ [ rt SC ] [ ls SC ] | [ sc SC ] ] [ ul SC ]\n"
+ "\n"
+ "SC := [ [ m1 BPS ] [ d SEC ] m2 BPS\n"
+ "\n"
+ " m1 : slope of first segment\n"
+ " d : x-coordinate of intersection\n"
+ " m2 : slope of second segment\n"
+ "\n"
+ "Alternative format:\n"
+ "\n"
+ "SC := [ [ umax BYTE ] dmax SEC ] rate BPS\n"
+ "\n"
+ " umax : maximum unit of work\n"
+ " dmax : maximum delay\n"
+ " rate : rate\n"
+ "\n"
+ );
+}
+
+static void
+explain1(char *arg)
+{
+ fprintf(stderr, "HFSC: Illegal \"%s\"\n", arg);
+}
+
+static int
+hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ struct tc_hfsc_qopt qopt;
+
+ memset(&qopt, 0, sizeof(qopt));
+
+ while (argc > 0) {
+ if (matches(*argv, "default") == 0) {
+ NEXT_ARG();
+ if (qopt.defcls != 0) {
+ fprintf(stderr, "HFSC: Double \"default\"\n");
+ return -1;
+ }
+ if (get_u16(&qopt.defcls, *argv, 16) < 0) {
+ explain1("default");
+ return -1;
+ }
+ } else if (matches(*argv, "help") == 0) {
+ explain_qdisc();
+ return -1;
+ } else {
+ fprintf(stderr, "HFSC: What is \"%s\" ?\n", *argv);
+ explain_qdisc();
+ return -1;
+ }
+ argc--, argv++;
+ }
+
+ addattr_l(n, 1024, TCA_OPTIONS, &qopt, sizeof(qopt));
+ return 0;
+}
+
+static int
+hfsc_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct tc_hfsc_qopt *qopt;
+
+ if (opt == NULL)
+ return 0;
+ if (RTA_PAYLOAD(opt) < sizeof(*qopt))
+ return -1;
+ qopt = RTA_DATA(opt);
+
+ if (qopt->defcls != 0)
+ fprintf(f, "default %x ", qopt->defcls);
+
+ return 0;
+}
+
+static int
+hfsc_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
+{
+ struct tc_hfsc_stats *st;
+
+ if (xstats == NULL)
+ return 0;
+ if (RTA_PAYLOAD(xstats) < sizeof(*st))
+ return -1;
+ st = RTA_DATA(xstats);
+
+ fprintf(f, " period %u ", st->period);
+ if (st->work != 0)
+ fprintf(f, "work %llu bytes ", (unsigned long long) st->work);
+ if (st->rtwork != 0)
+ fprintf(f, "rtwork %llu bytes ", (unsigned long long) st->rtwork);
+ fprintf(f, "level %u ", st->level);
+ fprintf(f, "\n");
+
+ return 0;
+}
+
+static int
+hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
+ struct nlmsghdr *n)
+{
+ struct tc_service_curve rsc, fsc, usc;
+ int rsc_ok, fsc_ok, usc_ok;
+ struct rtattr *tail;
+
+ memset(&rsc, 0, sizeof(rsc));
+ memset(&fsc, 0, sizeof(fsc));
+ memset(&usc, 0, sizeof(usc));
+ rsc_ok = fsc_ok = usc_ok = 0;
+
+ while (argc > 0) {
+ if (matches(*argv, "rt") == 0) {
+ NEXT_ARG();
+ if (hfsc_get_sc(&argc, &argv, &rsc) < 0) {
+ explain1("rt");
+ return -1;
+ }
+ rsc_ok = 1;
+ } else if (matches(*argv, "ls") == 0) {
+ NEXT_ARG();
+ if (hfsc_get_sc(&argc, &argv, &fsc) < 0) {
+ explain1("ls");
+ return -1;
+ }
+ fsc_ok = 1;
+ } else if (matches(*argv, "sc") == 0) {
+ NEXT_ARG();
+ if (hfsc_get_sc(&argc, &argv, &rsc) < 0) {
+ explain1("sc");
+ return -1;
+ }
+ memcpy(&fsc, &rsc, sizeof(fsc));
+ rsc_ok = 1;
+ fsc_ok = 1;
+ } else if (matches(*argv, "ul") == 0) {
+ NEXT_ARG();
+ if (hfsc_get_sc(&argc, &argv, &usc) < 0) {
+ explain1("ul");
+ return -1;
+ }
+ usc_ok = 1;
+ } else if (matches(*argv, "help") == 0) {
+ explain_class();
+ return -1;
+ } else {
+ fprintf(stderr, "HFSC: What is \"%s\" ?\n", *argv);
+ explain_class();
+ return -1;
+ }
+ argc--, argv++;
+ }
+
+ if (!(rsc_ok || fsc_ok || usc_ok)) {
+ fprintf(stderr, "HFSC: no parameters given\n");
+ explain_class();
+ return -1;
+ }
+ if (usc_ok && !fsc_ok) {
+ fprintf(stderr, "HFSC: Upper-limit Service Curve without "
+ "Link-Share Service Curve\n");
+ explain_class();
+ return -1;
+ }
+
+ tail = NLMSG_TAIL(n);
+
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ if (rsc_ok)
+ addattr_l(n, 1024, TCA_HFSC_RSC, &rsc, sizeof(rsc));
+ if (fsc_ok)
+ addattr_l(n, 1024, TCA_HFSC_FSC, &fsc, sizeof(fsc));
+ if (usc_ok)
+ addattr_l(n, 1024, TCA_HFSC_USC, &usc, sizeof(usc));
+
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static void
+hfsc_print_sc(FILE *f, char *name, struct tc_service_curve *sc)
+{
+ SPRINT_BUF(b1);
+
+ fprintf(f, "%s ", name);
+ fprintf(f, "m1 %s ", sprint_rate(sc->m1, b1));
+ fprintf(f, "d %s ", sprint_time(tc_core_ktime2time(sc->d), b1));
+ fprintf(f, "m2 %s ", sprint_rate(sc->m2, b1));
+}
+
+static int
+hfsc_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct rtattr *tb[TCA_HFSC_MAX+1];
+ struct tc_service_curve *rsc = NULL, *fsc = NULL, *usc = NULL;
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_HFSC_MAX, opt);
+
+ if (tb[TCA_HFSC_RSC]) {
+ if (RTA_PAYLOAD(tb[TCA_HFSC_RSC]) < sizeof(*rsc))
+ fprintf(stderr, "HFSC: truncated realtime option\n");
+ else
+ rsc = RTA_DATA(tb[TCA_HFSC_RSC]);
+ }
+ if (tb[TCA_HFSC_FSC]) {
+ if (RTA_PAYLOAD(tb[TCA_HFSC_FSC]) < sizeof(*fsc))
+ fprintf(stderr, "HFSC: truncated linkshare option\n");
+ else
+ fsc = RTA_DATA(tb[TCA_HFSC_FSC]);
+ }
+ if (tb[TCA_HFSC_USC]) {
+ if (RTA_PAYLOAD(tb[TCA_HFSC_USC]) < sizeof(*usc))
+ fprintf(stderr, "HFSC: truncated upperlimit option\n");
+ else
+ usc = RTA_DATA(tb[TCA_HFSC_USC]);
+ }
+
+
+ if (rsc != NULL && fsc != NULL &&
+ memcmp(rsc, fsc, sizeof(*rsc)) == 0)
+ hfsc_print_sc(f, "sc", rsc);
+ else {
+ if (rsc != NULL)
+ hfsc_print_sc(f, "rt", rsc);
+ if (fsc != NULL)
+ hfsc_print_sc(f, "ls", fsc);
+ }
+ if (usc != NULL)
+ hfsc_print_sc(f, "ul", usc);
+
+ return 0;
+}
+
+struct qdisc_util hfsc_qdisc_util = {
+ .id = "hfsc",
+ .parse_qopt = hfsc_parse_opt,
+ .print_qopt = hfsc_print_opt,
+ .print_xstats = hfsc_print_xstats,
+ .parse_copt = hfsc_parse_class_opt,
+ .print_copt = hfsc_print_class_opt,
+};
+
+static int
+hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc)
+{
+ char **argv = *argvp;
+ int argc = *argcp;
+ unsigned int m1 = 0, d = 0, m2 = 0;
+
+ if (matches(*argv, "m1") == 0) {
+ NEXT_ARG();
+ if (get_rate(&m1, *argv) < 0) {
+ explain1("m1");
+ return -1;
+ }
+ NEXT_ARG();
+ }
+
+ if (matches(*argv, "d") == 0) {
+ NEXT_ARG();
+ if (get_time(&d, *argv) < 0) {
+ explain1("d");
+ return -1;
+ }
+ NEXT_ARG();
+ }
+
+ if (matches(*argv, "m2") == 0) {
+ NEXT_ARG();
+ if (get_rate(&m2, *argv) < 0) {
+ explain1("m2");
+ return -1;
+ }
+ } else
+ return -1;
+
+ sc->m1 = m1;
+ sc->d = tc_core_time2ktime(d);
+ sc->m2 = m2;
+
+ *argvp = argv;
+ *argcp = argc;
+ return 0;
+}
+
+static int
+hfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc)
+{
+ char **argv = *argvp;
+ int argc = *argcp;
+ unsigned int umax = 0, dmax = 0, rate = 0;
+
+ if (matches(*argv, "umax") == 0) {
+ NEXT_ARG();
+ if (get_size(&umax, *argv) < 0) {
+ explain1("umax");
+ return -1;
+ }
+ NEXT_ARG();
+ }
+
+ if (matches(*argv, "dmax") == 0) {
+ NEXT_ARG();
+ if (get_time(&dmax, *argv) < 0) {
+ explain1("dmax");
+ return -1;
+ }
+ NEXT_ARG();
+ }
+
+ if (matches(*argv, "rate") == 0) {
+ NEXT_ARG();
+ if (get_rate(&rate, *argv) < 0) {
+ explain1("rate");
+ return -1;
+ }
+ } else
+ return -1;
+
+ if (umax != 0 && dmax == 0) {
+ fprintf(stderr, "HFSC: umax given but dmax is zero.\n");
+ return -1;
+ }
+
+ if (dmax != 0 && ceil(1.0 * umax * TIME_UNITS_PER_SEC / dmax) > rate) {
+ /*
+ * concave curve, slope of first segment is umax/dmax,
+ * intersection is at dmax
+ */
+ sc->m1 = ceil(1.0 * umax * TIME_UNITS_PER_SEC / dmax); /* in bps */
+ sc->d = tc_core_time2ktime(dmax);
+ sc->m2 = rate;
+ } else {
+ /*
+ * convex curve, slope of first segment is 0, intersection
+ * is at dmax - umax / rate
+ */
+ sc->m1 = 0;
+ sc->d = tc_core_time2ktime(ceil(dmax - umax * TIME_UNITS_PER_SEC / rate));
+ sc->m2 = rate;
+ }
+
+ *argvp = argv;
+ *argcp = argc;
+ return 0;
+}
+
+static int
+hfsc_get_sc(int *argcp, char ***argvp, struct tc_service_curve *sc)
+{
+ if (hfsc_get_sc1(argcp, argvp, sc) < 0 &&
+ hfsc_get_sc2(argcp, argvp, sc) < 0)
+ return -1;
+
+ if (sc->m1 == 0 && sc->m2 == 0) {
+ fprintf(stderr, "HFSC: Service Curve has two zero slopes\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/tc/q_htb.c b/tc/q_htb.c
new file mode 100644
index 0000000..c69e350
--- /dev/null
+++ b/tc/q_htb.c
@@ -0,0 +1,337 @@
+/*
+ * q_htb.c HTB.
+ *
+ * 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: Martin Devera, devik@cdi.cz
+ *
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+
+#define HTB_TC_VER 0x30003
+#if HTB_TC_VER >> 16 != TC_HTB_PROTOVER
+#error "Different kernel and TC HTB versions"
+#endif
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... qdisc add ... htb [default N] [r2q N]\n"
+ " default minor id of class to which unclassified packets are sent {0}\n"
+ " r2q DRR quantums are computed as rate in Bps/r2q {10}\n"
+ " debug string of 16 numbers each 0-3 {0}\n\n"
+ "... class add ... htb rate R1 [burst B1] [mpu B] [overhead O]\n"
+ " [prio P] [slot S] [pslot PS]\n"
+ " [ceil R2] [cburst B2] [mtu MTU] [quantum Q]\n"
+ " rate rate allocated to this class (class can still borrow)\n"
+ " burst max bytes burst which can be accumulated during idle period {computed}\n"
+ " mpu minimum packet size used in rate computations\n"
+ " overhead per-packet size overhead used in rate computations\n"
+ " linklay adapting to a linklayer e.g. atm\n"
+ " ceil definite upper class rate (no borrows) {rate}\n"
+ " cburst burst but for ceil {computed}\n"
+ " mtu max packet size we create rate map for {1600}\n"
+ " prio priority of leaf; lower are served first {0}\n"
+ " quantum how much bytes to serve from leaf at once {use r2q}\n"
+ "\nTC HTB version %d.%d\n",HTB_TC_VER>>16,HTB_TC_VER&0xffff
+ );
+}
+
+static void explain1(char *arg)
+{
+ fprintf(stderr, "Illegal \"%s\"\n", arg);
+ explain();
+}
+
+
+#define usage() return(-1)
+
+static int htb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ struct tc_htb_glob opt;
+ struct rtattr *tail;
+ unsigned i; char *p;
+ memset(&opt,0,sizeof(opt));
+ opt.rate2quantum = 10;
+ opt.version = 3;
+
+ while (argc > 0) {
+ if (matches(*argv, "r2q") == 0) {
+ NEXT_ARG();
+ if (get_u32(&opt.rate2quantum, *argv, 10)) {
+ explain1("r2q"); return -1;
+ }
+ } else if (matches(*argv, "default") == 0) {
+ NEXT_ARG();
+ if (get_u32(&opt.defcls, *argv, 16)) {
+ explain1("default"); return -1;
+ }
+ } else if (matches(*argv, "debug") == 0) {
+ NEXT_ARG(); p = *argv;
+ for (i=0; i<16; i++,p++) {
+ if (*p<'0' || *p>'3') break;
+ opt.debug |= (*p-'0')<<(2*i);
+ }
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ addattr_l(n, 2024, TCA_HTB_INIT, &opt, NLMSG_ALIGN(sizeof(opt)));
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ int ok=0;
+ struct tc_htb_opt opt;
+ __u32 rtab[256],ctab[256];
+ unsigned buffer=0,cbuffer=0;
+ int cell_log=-1,ccell_log = -1;
+ unsigned mtu;
+ unsigned short mpu = 0;
+ unsigned short overhead = 0;
+ unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
+ struct rtattr *tail;
+
+ memset(&opt, 0, sizeof(opt)); mtu = 1600; /* eth packet len */
+
+ while (argc > 0) {
+ if (matches(*argv, "prio") == 0) {
+ NEXT_ARG();
+ if (get_u32(&opt.prio, *argv, 10)) {
+ explain1("prio"); return -1;
+ }
+ ok++;
+ } else if (matches(*argv, "mtu") == 0) {
+ NEXT_ARG();
+ if (get_u32(&mtu, *argv, 10)) {
+ explain1("mtu"); return -1;
+ }
+ } else if (matches(*argv, "mpu") == 0) {
+ NEXT_ARG();
+ if (get_u16(&mpu, *argv, 10)) {
+ explain1("mpu"); return -1;
+ }
+ } else if (matches(*argv, "overhead") == 0) {
+ NEXT_ARG();
+ if (get_u16(&overhead, *argv, 10)) {
+ explain1("overhead"); return -1;
+ }
+ } else if (matches(*argv, "linklayer") == 0) {
+ NEXT_ARG();
+ if (get_linklayer(&linklayer, *argv)) {
+ explain1("linklayer"); return -1;
+ }
+ } else if (matches(*argv, "quantum") == 0) {
+ NEXT_ARG();
+ if (get_u32(&opt.quantum, *argv, 10)) {
+ explain1("quantum"); return -1;
+ }
+ } else if (matches(*argv, "burst") == 0 ||
+ strcmp(*argv, "buffer") == 0 ||
+ strcmp(*argv, "maxburst") == 0) {
+ NEXT_ARG();
+ if (get_size_and_cell(&buffer, &cell_log, *argv) < 0) {
+ explain1("buffer");
+ return -1;
+ }
+ ok++;
+ } else if (matches(*argv, "cburst") == 0 ||
+ strcmp(*argv, "cbuffer") == 0 ||
+ strcmp(*argv, "cmaxburst") == 0) {
+ NEXT_ARG();
+ if (get_size_and_cell(&cbuffer, &ccell_log, *argv) < 0) {
+ explain1("cbuffer");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "ceil") == 0) {
+ NEXT_ARG();
+ if (opt.ceil.rate) {
+ fprintf(stderr, "Double \"ceil\" spec\n");
+ return -1;
+ }
+ if (get_rate(&opt.ceil.rate, *argv)) {
+ explain1("ceil");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "rate") == 0) {
+ NEXT_ARG();
+ if (opt.rate.rate) {
+ fprintf(stderr, "Double \"rate\" spec\n");
+ return -1;
+ }
+ if (get_rate(&opt.rate.rate, *argv)) {
+ explain1("rate");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+/* if (!ok)
+ return 0;*/
+
+ if (opt.rate.rate == 0) {
+ fprintf(stderr, "\"rate\" is required.\n");
+ return -1;
+ }
+ /* if ceil params are missing, use the same as rate */
+ if (!opt.ceil.rate) opt.ceil = opt.rate;
+
+ /* compute minimal allowed burst from rate; mtu is added here to make
+ sute that buffer is larger than mtu and to have some safeguard space */
+ if (!buffer) buffer = opt.rate.rate / get_hz() + mtu;
+ if (!cbuffer) cbuffer = opt.ceil.rate / get_hz() + mtu;
+
+ opt.ceil.overhead = overhead;
+ opt.rate.overhead = overhead;
+
+ opt.ceil.mpu = mpu;
+ opt.rate.mpu = mpu;
+
+ if (tc_calc_rtable(&opt.rate, rtab, cell_log, mtu, linklayer) < 0) {
+ fprintf(stderr, "htb: failed to calculate rate table.\n");
+ return -1;
+ }
+ opt.buffer = tc_calc_xmittime(opt.rate.rate, buffer);
+
+ if (tc_calc_rtable(&opt.ceil, ctab, ccell_log, mtu, linklayer) < 0) {
+ fprintf(stderr, "htb: failed to calculate ceil rate table.\n");
+ return -1;
+ }
+ opt.cbuffer = tc_calc_xmittime(opt.ceil.rate, cbuffer);
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ addattr_l(n, 2024, TCA_HTB_PARMS, &opt, sizeof(opt));
+ addattr_l(n, 3024, TCA_HTB_RTAB, rtab, 1024);
+ addattr_l(n, 4024, TCA_HTB_CTAB, ctab, 1024);
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static int htb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct rtattr *tb[TCA_HTB_RTAB+1];
+ struct tc_htb_opt *hopt;
+ struct tc_htb_glob *gopt;
+ double buffer,cbuffer;
+ SPRINT_BUF(b1);
+ SPRINT_BUF(b2);
+ SPRINT_BUF(b3);
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_HTB_RTAB, opt);
+
+ if (tb[TCA_HTB_PARMS]) {
+
+ hopt = RTA_DATA(tb[TCA_HTB_PARMS]);
+ if (RTA_PAYLOAD(tb[TCA_HTB_PARMS]) < sizeof(*hopt)) return -1;
+
+ if (!hopt->level) {
+ fprintf(f, "prio %d ", (int)hopt->prio);
+ if (show_details)
+ fprintf(f, "quantum %d ", (int)hopt->quantum);
+ }
+ fprintf(f, "rate %s ", sprint_rate(hopt->rate.rate, b1));
+ buffer = tc_calc_xmitsize(hopt->rate.rate, hopt->buffer);
+ fprintf(f, "ceil %s ", sprint_rate(hopt->ceil.rate, b1));
+ cbuffer = tc_calc_xmitsize(hopt->ceil.rate, hopt->cbuffer);
+ if (show_details) {
+ fprintf(f, "burst %s/%u mpu %s overhead %s ",
+ sprint_size(buffer, b1),
+ 1<<hopt->rate.cell_log,
+ sprint_size(hopt->rate.mpu&0xFF, b2),
+ sprint_size((hopt->rate.mpu>>8)&0xFF, b3));
+ fprintf(f, "cburst %s/%u mpu %s overhead %s ",
+ sprint_size(cbuffer, b1),
+ 1<<hopt->ceil.cell_log,
+ sprint_size(hopt->ceil.mpu&0xFF, b2),
+ sprint_size((hopt->ceil.mpu>>8)&0xFF, b3));
+ fprintf(f, "level %d ", (int)hopt->level);
+ } else {
+ fprintf(f, "burst %s ", sprint_size(buffer, b1));
+ fprintf(f, "cburst %s ", sprint_size(cbuffer, b1));
+ }
+ if (show_raw)
+ fprintf(f, "buffer [%08x] cbuffer [%08x] ",
+ hopt->buffer,hopt->cbuffer);
+ }
+ if (tb[TCA_HTB_INIT]) {
+ gopt = RTA_DATA(tb[TCA_HTB_INIT]);
+ if (RTA_PAYLOAD(tb[TCA_HTB_INIT]) < sizeof(*gopt)) return -1;
+
+ fprintf(f, "r2q %d default %x direct_packets_stat %u",
+ gopt->rate2quantum,gopt->defcls,gopt->direct_pkts);
+ if (show_details)
+ fprintf(f," ver %d.%d",gopt->version >> 16,gopt->version & 0xffff);
+ }
+ return 0;
+}
+
+static int htb_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
+{
+ struct tc_htb_xstats *st;
+ if (xstats == NULL)
+ return 0;
+
+ if (RTA_PAYLOAD(xstats) < sizeof(*st))
+ return -1;
+
+ st = RTA_DATA(xstats);
+ fprintf(f, " lended: %u borrowed: %u giants: %u\n",
+ st->lends,st->borrows,st->giants);
+ fprintf(f, " tokens: %d ctokens: %d\n", st->tokens,st->ctokens);
+ return 0;
+}
+
+struct qdisc_util htb_qdisc_util = {
+ .id = "htb",
+ .parse_qopt = htb_parse_opt,
+ .print_qopt = htb_print_opt,
+ .print_xstats = htb_print_xstats,
+ .parse_copt = htb_parse_class_opt,
+ .print_copt = htb_print_opt,
+};
+
+/* for testing of old one */
+struct qdisc_util htb2_qdisc_util = {
+ .id = "htb2",
+ .parse_qopt = htb_parse_opt,
+ .print_qopt = htb_print_opt,
+ .print_xstats = htb_print_xstats,
+ .parse_copt = htb_parse_class_opt,
+ .print_copt = htb_print_opt,
+};
diff --git a/tc/q_ingress.c b/tc/q_ingress.c
new file mode 100644
index 0000000..71fbd49
--- /dev/null
+++ b/tc/q_ingress.c
@@ -0,0 +1,69 @@
+/*
+ *
+ * q_ingress.c INGRESS.
+ *
+ * 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: J Hadi Salim
+ *
+ * This is here just in case it is needed
+ * useless right now; might be useful in the future
+ *
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... ingress \n");
+}
+
+#define usage() return(-1)
+
+static int ingress_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+
+ if (argc > 0) {
+ while (argc > 0) {
+
+ if (strcmp(*argv, "handle") == 0) {
+ NEXT_ARG();
+ argc--; argv++;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ }
+ }
+
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ return 0;
+}
+
+static int ingress_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+
+ fprintf(f, "---------------- ");
+ return 0;
+}
+
+struct qdisc_util ingress_qdisc_util = {
+ .id = "ingress",
+ .parse_qopt = ingress_parse_opt,
+ .print_qopt = ingress_print_opt,
+};
diff --git a/tc/q_multiq.c b/tc/q_multiq.c
new file mode 100644
index 0000000..306e170
--- /dev/null
+++ b/tc/q_multiq.c
@@ -0,0 +1,87 @@
+/*
+ * q_multiq.c Multiqueue aware qdisc
+ *
+ * Copyright (c) 2008, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Author: Alexander Duyck <alexander.h.duyck@intel.com>
+ *
+ * Original Authors: PJ Waskiewicz, <peter.p.waskiewicz.jr@intel.com> (RR)
+ * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> (from PRIO)
+ *
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... multiq [help]\n");
+}
+
+#define usage() return(-1)
+
+static int multiq_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+ struct nlmsghdr *n)
+{
+ struct tc_multiq_qopt opt;
+
+ if (argc > 0) {
+ if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
+ return 0;
+}
+
+int multiq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct tc_multiq_qopt *qopt;
+
+ if (opt == NULL)
+ return 0;
+ if (RTA_PAYLOAD(opt) < sizeof(*qopt))
+ return 0;
+
+ qopt = RTA_DATA(opt);
+
+ fprintf(f, "bands %u/%u ", qopt->bands, qopt->max_bands);
+
+ return 0;
+}
+
+struct qdisc_util multiq_qdisc_util = {
+ .id = "multiq",
+ .parse_qopt = multiq_parse_opt,
+ .print_qopt = multiq_print_opt,
+};
diff --git a/tc/q_netem.c b/tc/q_netem.c
new file mode 100644
index 0000000..33b3d2a
--- /dev/null
+++ b/tc/q_netem.c
@@ -0,0 +1,398 @@
+/*
+ * q_netem.c NETEM.
+ *
+ * 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: Stephen Hemminger <shemminger@osdl.org>
+ *
+ */
+
+#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 <errno.h>
+
+#include "utils.h"
+#include "tc_util.h"
+#include "tc_common.h"
+
+static void explain(void)
+{
+ fprintf(stderr,
+"Usage: ... netem [ limit PACKETS ] \n" \
+" [ delay TIME [ JITTER [CORRELATION]]]\n" \
+" [ distribution {uniform|normal|pareto|paretonormal} ]\n" \
+" [ drop PERCENT [CORRELATION]] \n" \
+" [ corrupt PERCENT [CORRELATION]] \n" \
+" [ duplicate PERCENT [CORRELATION]]\n" \
+" [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n");
+}
+
+static void explain1(const char *arg)
+{
+ fprintf(stderr, "Illegal \"%s\"\n", arg);
+}
+
+#define usage() return(-1)
+
+/* Upper bound on size of distribution
+ * really (TCA_BUF_MAX - other headers) / sizeof (__s16)
+ */
+#define MAX_DIST (16*1024)
+
+/*
+ * Simplistic file parser for distrbution data.
+ * Format is:
+ * # comment line(s)
+ * data0 data1 ...
+ */
+static int get_distribution(const char *type, __s16 *data, int maxdata)
+{
+ FILE *f;
+ int n;
+ long x;
+ size_t len;
+ char *line = NULL;
+ char name[128];
+
+ snprintf(name, sizeof(name), "%s/%s.dist", get_tc_lib(), type);
+ if ((f = fopen(name, "r")) == NULL) {
+ fprintf(stderr, "No distribution data for %s (%s: %s)\n",
+ type, name, strerror(errno));
+ return -1;
+ }
+
+ n = 0;
+ while (getline(&line, &len, f) != -1) {
+ char *p, *endp;
+ if (*line == '\n' || *line == '#')
+ continue;
+
+ for (p = line; ; p = endp) {
+ x = strtol(p, &endp, 0);
+ if (endp == p)
+ break;
+
+ if (n >= maxdata) {
+ fprintf(stderr, "%s: too much data\n",
+ name);
+ n = -1;
+ goto error;
+ }
+ data[n++] = x;
+ }
+ }
+ error:
+ free(line);
+ fclose(f);
+ return n;
+}
+
+static int isnumber(const char *arg)
+{
+ char *p;
+
+ return strtod(arg, &p) != 0 || p != arg;
+}
+
+#define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isnumber(argv[1]))
+
+/* Adjust for the fact that psched_ticks aren't always usecs
+ (based on kernel PSCHED_CLOCK configuration */
+static int get_ticks(__u32 *ticks, const char *str)
+{
+ unsigned t;
+
+ if(get_time(&t, str))
+ return -1;
+
+ if (tc_core_time2big(t)) {
+ fprintf(stderr, "Illegal %u time (too large)\n", t);
+ return -1;
+ }
+
+ *ticks = tc_core_time2tick(t);
+ return 0;
+}
+
+static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
+ struct nlmsghdr *n)
+{
+ size_t dist_size = 0;
+ struct rtattr *tail;
+ struct tc_netem_qopt opt;
+ struct tc_netem_corr cor;
+ struct tc_netem_reorder reorder;
+ struct tc_netem_corrupt corrupt;
+ __s16 *dist_data = NULL;
+ int present[__TCA_NETEM_MAX];
+
+ memset(&opt, 0, sizeof(opt));
+ opt.limit = 1000;
+ memset(&cor, 0, sizeof(cor));
+ memset(&reorder, 0, sizeof(reorder));
+ memset(&corrupt, 0, sizeof(corrupt));
+ memset(present, 0, sizeof(present));
+
+ while (argc > 0) {
+ if (matches(*argv, "limit") == 0) {
+ NEXT_ARG();
+ if (get_size(&opt.limit, *argv)) {
+ explain1("limit");
+ return -1;
+ }
+ } else if (matches(*argv, "latency") == 0 ||
+ matches(*argv, "delay") == 0) {
+ NEXT_ARG();
+ if (get_ticks(&opt.latency, *argv)) {
+ explain1("latency");
+ return -1;
+ }
+
+ if (NEXT_IS_NUMBER()) {
+ NEXT_ARG();
+ if (get_ticks(&opt.jitter, *argv)) {
+ explain1("latency");
+ return -1;
+ }
+
+ if (NEXT_IS_NUMBER()) {
+ NEXT_ARG();
+ ++present[TCA_NETEM_CORR];
+ if (get_percent(&cor.delay_corr, *argv)) {
+ explain1("latency");
+ return -1;
+ }
+ }
+ }
+ } else if (matches(*argv, "loss") == 0 ||
+ matches(*argv, "drop") == 0) {
+ NEXT_ARG();
+ if (get_percent(&opt.loss, *argv)) {
+ explain1("loss");
+ return -1;
+ }
+ if (NEXT_IS_NUMBER()) {
+ NEXT_ARG();
+ ++present[TCA_NETEM_CORR];
+ if (get_percent(&cor.loss_corr, *argv)) {
+ explain1("loss");
+ return -1;
+ }
+ }
+ } else if (matches(*argv, "reorder") == 0) {
+ NEXT_ARG();
+ present[TCA_NETEM_REORDER] = 1;
+ if (get_percent(&reorder.probability, *argv)) {
+ explain1("reorder");
+ return -1;
+ }
+ if (NEXT_IS_NUMBER()) {
+ NEXT_ARG();
+ ++present[TCA_NETEM_CORR];
+ if (get_percent(&reorder.correlation, *argv)) {
+ explain1("reorder");
+ return -1;
+ }
+ }
+ } else if (matches(*argv, "corrupt") == 0) {
+ NEXT_ARG();
+ present[TCA_NETEM_CORRUPT] = 1;
+ if (get_percent(&corrupt.probability, *argv)) {
+ explain1("corrupt");
+ return -1;
+ }
+ if (NEXT_IS_NUMBER()) {
+ NEXT_ARG();
+ ++present[TCA_NETEM_CORR];
+ if (get_percent(&corrupt.correlation, *argv)) {
+ explain1("corrupt");
+ return -1;
+ }
+ }
+ } else if (matches(*argv, "gap") == 0) {
+ NEXT_ARG();
+ if (get_u32(&opt.gap, *argv, 0)) {
+ explain1("gap");
+ return -1;
+ }
+ } else if (matches(*argv, "duplicate") == 0) {
+ NEXT_ARG();
+ if (get_percent(&opt.duplicate, *argv)) {
+ explain1("duplicate");
+ return -1;
+ }
+ if (NEXT_IS_NUMBER()) {
+ NEXT_ARG();
+ if (get_percent(&cor.dup_corr, *argv)) {
+ explain1("duplicate");
+ return -1;
+ }
+ }
+ } else if (matches(*argv, "distribution") == 0) {
+ NEXT_ARG();
+ dist_data = calloc(sizeof(dist_data[0]), MAX_DIST);
+ dist_size = get_distribution(*argv, dist_data, MAX_DIST);
+ if (dist_size <= 0) {
+ free(dist_data);
+ return -1;
+ }
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ tail = NLMSG_TAIL(n);
+
+ if (reorder.probability) {
+ if (opt.latency == 0) {
+ fprintf(stderr, "reordering not possible without specifying some delay\n");
+ }
+ if (opt.gap == 0)
+ opt.gap = 1;
+ } else if (opt.gap > 0) {
+ fprintf(stderr, "gap specified without reorder probability\n");
+ explain();
+ return -1;
+ }
+
+ if (dist_data && (opt.latency == 0 || opt.jitter == 0)) {
+ fprintf(stderr, "distribution specified but no latency and jitter values\n");
+ explain();
+ return -1;
+ }
+
+ if (addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)) < 0)
+ return -1;
+
+ if (present[TCA_NETEM_CORR] &&
+ addattr_l(n, 1024, TCA_NETEM_CORR, &cor, sizeof(cor)) < 0)
+ return -1;
+
+ if (present[TCA_NETEM_REORDER] &&
+ addattr_l(n, 1024, TCA_NETEM_REORDER, &reorder, sizeof(reorder)) < 0)
+ return -1;
+
+ if (present[TCA_NETEM_CORRUPT] &&
+ addattr_l(n, 1024, TCA_NETEM_CORRUPT, &corrupt, sizeof(corrupt)) < 0)
+ return -1;
+
+ if (dist_data) {
+ if (addattr_l(n, MAX_DIST * sizeof(dist_data[0]),
+ TCA_NETEM_DELAY_DIST,
+ dist_data, dist_size * sizeof(dist_data[0])) < 0)
+ return -1;
+ free(dist_data);
+ }
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ const struct tc_netem_corr *cor = NULL;
+ const struct tc_netem_reorder *reorder = NULL;
+ const struct tc_netem_corrupt *corrupt = NULL;
+ struct tc_netem_qopt qopt;
+ int len = RTA_PAYLOAD(opt) - sizeof(qopt);
+ SPRINT_BUF(b1);
+
+ if (opt == NULL)
+ return 0;
+
+ if (len < 0) {
+ fprintf(stderr, "options size error\n");
+ return -1;
+ }
+ memcpy(&qopt, RTA_DATA(opt), sizeof(qopt));
+
+ if (len > 0) {
+ struct rtattr *tb[TCA_NETEM_MAX+1];
+ parse_rtattr(tb, TCA_NETEM_MAX, RTA_DATA(opt) + sizeof(qopt),
+ len);
+
+ if (tb[TCA_NETEM_CORR]) {
+ if (RTA_PAYLOAD(tb[TCA_NETEM_CORR]) < sizeof(*cor))
+ return -1;
+ cor = RTA_DATA(tb[TCA_NETEM_CORR]);
+ }
+ if (tb[TCA_NETEM_REORDER]) {
+ if (RTA_PAYLOAD(tb[TCA_NETEM_REORDER]) < sizeof(*reorder))
+ return -1;
+ reorder = RTA_DATA(tb[TCA_NETEM_REORDER]);
+ }
+ if (tb[TCA_NETEM_CORRUPT]) {
+ if (RTA_PAYLOAD(tb[TCA_NETEM_CORRUPT]) < sizeof(*corrupt))
+ return -1;
+ corrupt = RTA_DATA(tb[TCA_NETEM_CORRUPT]);
+ }
+ }
+
+ fprintf(f, "limit %d", qopt.limit);
+
+ if (qopt.latency) {
+ fprintf(f, " delay %s", sprint_ticks(qopt.latency, b1));
+
+ if (qopt.jitter) {
+ fprintf(f, " %s", sprint_ticks(qopt.jitter, b1));
+ if (cor && cor->delay_corr)
+ fprintf(f, " %s", sprint_percent(cor->delay_corr, b1));
+ }
+ }
+
+ if (qopt.loss) {
+ fprintf(f, " loss %s", sprint_percent(qopt.loss, b1));
+ if (cor && cor->loss_corr)
+ fprintf(f, " %s", sprint_percent(cor->loss_corr, b1));
+ }
+
+ if (qopt.duplicate) {
+ fprintf(f, " duplicate %s",
+ sprint_percent(qopt.duplicate, b1));
+ if (cor && cor->dup_corr)
+ fprintf(f, " %s", sprint_percent(cor->dup_corr, b1));
+ }
+
+ if (reorder && reorder->probability) {
+ fprintf(f, " reorder %s",
+ sprint_percent(reorder->probability, b1));
+ if (reorder->correlation)
+ fprintf(f, " %s",
+ sprint_percent(reorder->correlation, b1));
+ }
+
+ if (corrupt && corrupt->probability) {
+ fprintf(f, " corrupt %s",
+ sprint_percent(corrupt->probability, b1));
+ if (corrupt->correlation)
+ fprintf(f, " %s",
+ sprint_percent(corrupt->correlation, b1));
+ }
+
+ if (qopt.gap)
+ fprintf(f, " gap %lu", (unsigned long)qopt.gap);
+
+ return 0;
+}
+
+struct qdisc_util netem_qdisc_util = {
+ .id = "netem",
+ .parse_qopt = netem_parse_opt,
+ .print_qopt = netem_print_opt,
+};
+
diff --git a/tc/q_prio.c b/tc/q_prio.c
new file mode 100644
index 0000000..31a37cd
--- /dev/null
+++ b/tc/q_prio.c
@@ -0,0 +1,129 @@
+/*
+ * q_prio.c PRIO.
+ *
+ * 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 "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... prio bands NUMBER priomap P1 P2...[multiqueue]\n");
+}
+
+#define usage() return(-1)
+
+static int prio_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ int ok=0;
+ int pmap_mode = 0;
+ int idx = 0;
+ struct tc_prio_qopt opt={3,{ 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }};
+ struct rtattr *nest;
+ unsigned char mq = 0;
+
+ while (argc > 0) {
+ if (strcmp(*argv, "bands") == 0) {
+ if (pmap_mode)
+ explain();
+ NEXT_ARG();
+ if (get_integer(&opt.bands, *argv, 10)) {
+ fprintf(stderr, "Illegal \"bands\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "priomap") == 0) {
+ if (pmap_mode) {
+ fprintf(stderr, "Error: duplicate priomap\n");
+ return -1;
+ }
+ pmap_mode = 1;
+ } else if (strcmp(*argv, "multiqueue") == 0) {
+ mq = 1;
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ unsigned band;
+ if (!pmap_mode) {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ if (get_unsigned(&band, *argv, 10)) {
+ fprintf(stderr, "Illegal \"priomap\" element\n");
+ return -1;
+ }
+ if (band > opt.bands) {
+ fprintf(stderr, "\"priomap\" element is out of bands\n");
+ return -1;
+ }
+ if (idx > TC_PRIO_MAX) {
+ fprintf(stderr, "\"priomap\" index > TC_PRIO_MAX=%u\n", TC_PRIO_MAX);
+ return -1;
+ }
+ opt.priomap[idx++] = band;
+ }
+ argc--; argv++;
+ }
+
+/*
+ if (pmap_mode) {
+ for (; idx < TC_PRIO_MAX; idx++)
+ opt.priomap[idx] = opt.priomap[TC_PRIO_BESTEFFORT];
+ }
+*/
+ nest = addattr_nest_compat(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
+ if (mq)
+ addattr_l(n, 1024, TCA_PRIO_MQ, NULL, 0);
+ addattr_nest_compat_end(n, nest);
+ return 0;
+}
+
+int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ int i;
+ struct tc_prio_qopt *qopt;
+ struct rtattr *tb[TCA_PRIO_MAX+1];
+
+ if (opt == NULL)
+ return 0;
+
+ if (parse_rtattr_nested_compat(tb, TCA_PRIO_MAX, opt, qopt,
+ sizeof(*qopt)))
+ return -1;
+
+ fprintf(f, "bands %u priomap ", qopt->bands);
+ for (i=0; i<=TC_PRIO_MAX; i++)
+ fprintf(f, " %d", qopt->priomap[i]);
+
+ if (tb[TCA_PRIO_MQ])
+ fprintf(f, " multiqueue: %s ",
+ *(unsigned char *)RTA_DATA(tb[TCA_PRIO_MQ]) ? "on" : "off");
+
+ return 0;
+}
+
+struct qdisc_util prio_qdisc_util = {
+ .id = "prio",
+ .parse_qopt = prio_parse_opt,
+ .print_qopt = prio_print_opt,
+};
+
diff --git a/tc/q_red.c b/tc/q_red.c
new file mode 100644
index 0000000..6f93b26
--- /dev/null
+++ b/tc/q_red.c
@@ -0,0 +1,219 @@
+/*
+ * q_red.c RED.
+ *
+ * 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 "utils.h"
+#include "tc_util.h"
+
+#include "tc_red.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... red limit BYTES min BYTES max BYTES avpkt BYTES burst PACKETS\n");
+ fprintf(stderr, " probability PROBABILITY bandwidth KBPS [ ecn ]\n");
+}
+
+#define usage() return(-1)
+
+static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ int ok=0;
+ struct tc_red_qopt opt;
+ unsigned burst = 0;
+ unsigned avpkt = 0;
+ double probability = 0.02;
+ unsigned rate = 0;
+ int ecn_ok = 0;
+ int wlog;
+ __u8 sbuf[256];
+ struct rtattr *tail;
+
+ memset(&opt, 0, sizeof(opt));
+
+ while (argc > 0) {
+ if (strcmp(*argv, "limit") == 0) {
+ NEXT_ARG();
+ if (get_size(&opt.limit, *argv)) {
+ fprintf(stderr, "Illegal \"limit\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "min") == 0) {
+ NEXT_ARG();
+ if (get_size(&opt.qth_min, *argv)) {
+ fprintf(stderr, "Illegal \"min\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "max") == 0) {
+ NEXT_ARG();
+ if (get_size(&opt.qth_max, *argv)) {
+ fprintf(stderr, "Illegal \"max\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "burst") == 0) {
+ NEXT_ARG();
+ if (get_unsigned(&burst, *argv, 0)) {
+ fprintf(stderr, "Illegal \"burst\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "avpkt") == 0) {
+ NEXT_ARG();
+ if (get_size(&avpkt, *argv)) {
+ fprintf(stderr, "Illegal \"avpkt\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "probability") == 0) {
+ NEXT_ARG();
+ if (sscanf(*argv, "%lg", &probability) != 1) {
+ fprintf(stderr, "Illegal \"probability\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "bandwidth") == 0) {
+ NEXT_ARG();
+ if (get_rate(&rate, *argv)) {
+ fprintf(stderr, "Illegal \"bandwidth\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "ecn") == 0) {
+ ecn_ok = 1;
+ ok++;
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ if (!ok)
+ return 0;
+
+ if (rate == 0)
+ get_rate(&rate, "10Mbit");
+
+ if (!opt.qth_min || !opt.qth_max || !burst || !opt.limit || !avpkt) {
+ fprintf(stderr, "Required parameter (min, max, burst, limit, avpket) is missing\n");
+ return -1;
+ }
+
+ if ((wlog = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) {
+ fprintf(stderr, "RED: failed to calculate EWMA constant.\n");
+ return -1;
+ }
+ if (wlog >= 10)
+ fprintf(stderr, "RED: WARNING. Burst %d seems to be to large.\n", burst);
+ opt.Wlog = wlog;
+ if ((wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) {
+ fprintf(stderr, "RED: failed to calculate probability.\n");
+ return -1;
+ }
+ opt.Plog = wlog;
+ if ((wlog = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) {
+ fprintf(stderr, "RED: failed to calculate idle damping table.\n");
+ return -1;
+ }
+ opt.Scell_log = wlog;
+ if (ecn_ok) {
+#ifdef TC_RED_ECN
+ opt.flags |= TC_RED_ECN;
+#else
+ fprintf(stderr, "RED: ECN support is missing in this binary.\n");
+ return -1;
+#endif
+ }
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ addattr_l(n, 1024, TCA_RED_PARMS, &opt, sizeof(opt));
+ addattr_l(n, 1024, TCA_RED_STAB, sbuf, 256);
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static int red_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct rtattr *tb[TCA_RED_STAB+1];
+ struct tc_red_qopt *qopt;
+ SPRINT_BUF(b1);
+ SPRINT_BUF(b2);
+ SPRINT_BUF(b3);
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_RED_STAB, opt);
+
+ if (tb[TCA_RED_PARMS] == NULL)
+ return -1;
+ qopt = RTA_DATA(tb[TCA_RED_PARMS]);
+ if (RTA_PAYLOAD(tb[TCA_RED_PARMS]) < sizeof(*qopt))
+ return -1;
+ fprintf(f, "limit %s min %s max %s ",
+ sprint_size(qopt->limit, b1),
+ sprint_size(qopt->qth_min, b2),
+ sprint_size(qopt->qth_max, b3));
+#ifdef TC_RED_ECN
+ if (qopt->flags & TC_RED_ECN)
+ fprintf(f, "ecn ");
+#endif
+ if (show_details) {
+ fprintf(f, "ewma %u Plog %u Scell_log %u",
+ qopt->Wlog, qopt->Plog, qopt->Scell_log);
+ }
+ return 0;
+}
+
+static int red_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
+{
+#ifdef TC_RED_ECN
+ struct tc_red_xstats *st;
+
+ if (xstats == NULL)
+ return 0;
+
+ if (RTA_PAYLOAD(xstats) < sizeof(*st))
+ return -1;
+
+ st = RTA_DATA(xstats);
+ fprintf(f, " marked %u early %u pdrop %u other %u",
+ st->marked, st->early, st->pdrop, st->other);
+ return 0;
+
+#endif
+ return 0;
+}
+
+
+struct qdisc_util red_qdisc_util = {
+ .id = "red",
+ .parse_qopt = red_parse_opt,
+ .print_qopt = red_print_opt,
+ .print_xstats = red_print_xstats,
+};
diff --git a/tc/q_rr.c b/tc/q_rr.c
new file mode 100644
index 0000000..9bb8c7d
--- /dev/null
+++ b/tc/q_rr.c
@@ -0,0 +1,122 @@
+/*
+ * q_rr.c RR.
+ *
+ * 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: PJ Waskiewicz, <peter.p.waskiewicz.jr@intel.com>
+ * Original Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> (from PRIO)
+ */
+
+#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 "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... rr bands NUMBER priomap P1 P2... [multiqueue]\n");
+}
+
+#define usage() return(-1)
+
+static int rr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ int ok = 0;
+ int pmap_mode = 0;
+ int idx = 0;
+ struct tc_prio_qopt opt={3,{ 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }};
+ struct rtattr *nest;
+ unsigned char mq = 0;
+
+ while (argc > 0) {
+ if (strcmp(*argv, "bands") == 0) {
+ if (pmap_mode)
+ explain();
+ NEXT_ARG();
+ if (get_integer(&opt.bands, *argv, 10)) {
+ fprintf(stderr, "Illegal \"bands\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "priomap") == 0) {
+ if (pmap_mode) {
+ fprintf(stderr, "Error: duplicate priomap\n");
+ return -1;
+ }
+ pmap_mode = 1;
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else if (strcmp(*argv, "multiqueue") == 0) {
+ mq = 1;
+ } else {
+ unsigned band;
+ if (!pmap_mode) {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ if (get_unsigned(&band, *argv, 10)) {
+ fprintf(stderr, "Illegal \"priomap\" element\n");
+ return -1;
+ }
+ if (band > opt.bands) {
+ fprintf(stderr, "\"priomap\" element is out of bands\n");
+ return -1;
+ }
+ if (idx > TC_PRIO_MAX) {
+ fprintf(stderr, "\"priomap\" index > TC_RR_MAX=%u\n", TC_PRIO_MAX);
+ return -1;
+ }
+ opt.priomap[idx++] = band;
+ }
+ argc--; argv++;
+ }
+
+ nest = addattr_nest_compat(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
+ if (mq)
+ addattr_l(n, 1024, TCA_PRIO_MQ, NULL, 0);
+ addattr_nest_compat_end(n, nest);
+ return 0;
+}
+
+int rr_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ int i;
+ struct tc_prio_qopt *qopt;
+ struct rtattr *tb[TCA_PRIO_MAX + 1];
+
+ if (opt == NULL)
+ return 0;
+
+ if (parse_rtattr_nested_compat(tb, TCA_PRIO_MAX, opt, qopt,
+ sizeof(*qopt)))
+ return -1;
+
+ fprintf(f, "bands %u priomap ", qopt->bands);
+ for (i=0; i <= TC_PRIO_MAX; i++)
+ fprintf(f, " %d", qopt->priomap[i]);
+
+ if (tb[TCA_PRIO_MQ])
+ fprintf(f, " multiqueue: %s ",
+ *(unsigned char *)RTA_DATA(tb[TCA_PRIO_MQ]) ? "on" : "off");
+
+ return 0;
+}
+
+struct qdisc_util rr_qdisc_util = {
+ .id = "rr",
+ .parse_qopt = rr_parse_opt,
+ .print_qopt = rr_print_opt,
+};
diff --git a/tc/q_sfq.c b/tc/q_sfq.c
new file mode 100644
index 0000000..ce4dade
--- /dev/null
+++ b/tc/q_sfq.c
@@ -0,0 +1,124 @@
+/*
+ * q_sfq.c SFQ.
+ *
+ * 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 "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... sfq [ limit NUMBER ] [ perturb SECS ] [ quantum BYTES ]\n");
+}
+
+#define usage() return(-1)
+
+static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ int ok=0;
+ struct tc_sfq_qopt opt;
+
+ memset(&opt, 0, sizeof(opt));
+
+ while (argc > 0) {
+ if (strcmp(*argv, "quantum") == 0) {
+ NEXT_ARG();
+ if (get_size(&opt.quantum, *argv)) {
+ fprintf(stderr, "Illegal \"limit\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "perturb") == 0) {
+ NEXT_ARG();
+ if (get_integer(&opt.perturb_period, *argv, 0)) {
+ fprintf(stderr, "Illegal \"perturb\"\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "limit") == 0) {
+ NEXT_ARG();
+ if (get_u32(&opt.limit, *argv, 0)) {
+ fprintf(stderr, "Illegal \"limit\"\n");
+ return -1;
+ }
+ if (opt.limit < 2) {
+ fprintf(stderr, "Illegal \"limit\", must be > 1\n");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ if (ok)
+ addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
+ return 0;
+}
+
+static int sfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct tc_sfq_qopt *qopt;
+ SPRINT_BUF(b1);
+
+ if (opt == NULL)
+ return 0;
+
+ if (RTA_PAYLOAD(opt) < sizeof(*qopt))
+ return -1;
+ qopt = RTA_DATA(opt);
+ fprintf(f, "limit %up ", qopt->limit);
+ fprintf(f, "quantum %s ", sprint_size(qopt->quantum, b1));
+ if (show_details) {
+ fprintf(f, "flows %u/%u ", qopt->flows, qopt->divisor);
+ }
+ if (qopt->perturb_period)
+ fprintf(f, "perturb %dsec ", qopt->perturb_period);
+ return 0;
+}
+
+static int sfq_print_xstats(struct qdisc_util *qu, FILE *f,
+ struct rtattr *xstats)
+{
+ struct tc_sfq_xstats *st;
+
+ if (xstats == NULL)
+ return 0;
+ if (RTA_PAYLOAD(xstats) < sizeof(*st))
+ return -1;
+ st = RTA_DATA(xstats);
+
+ fprintf(f, " allot %d ", st->allot);
+ fprintf(f, "\n");
+ return 0;
+}
+
+struct qdisc_util sfq_qdisc_util = {
+ .id = "sfq",
+ .parse_qopt = sfq_parse_opt,
+ .print_qopt = sfq_print_opt,
+ .print_xstats = sfq_print_xstats,
+};
diff --git a/tc/q_tbf.c b/tc/q_tbf.c
new file mode 100644
index 0000000..dbf9586
--- /dev/null
+++ b/tc/q_tbf.c
@@ -0,0 +1,286 @@
+/*
+ * q_tbf.c TBF.
+ *
+ * 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 "utils.h"
+#include "tc_util.h"
+
+static void explain(void)
+{
+ fprintf(stderr, "Usage: ... tbf limit BYTES burst BYTES[/BYTES] rate KBPS [ mtu BYTES[/BYTES] ]\n");
+ fprintf(stderr, " [ peakrate KBPS ] [ latency TIME ] ");
+ fprintf(stderr, "[ overhead BYTES ] [ linklayer TYPE ]\n");
+}
+
+static void explain1(char *arg)
+{
+ fprintf(stderr, "Illegal \"%s\"\n", arg);
+}
+
+
+#define usage() return(-1)
+
+static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ int ok=0;
+ struct tc_tbf_qopt opt;
+ __u32 rtab[256];
+ __u32 ptab[256];
+ unsigned buffer=0, mtu=0, mpu=0, latency=0;
+ int Rcell_log=-1, Pcell_log = -1;
+ unsigned short overhead=0;
+ unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
+ struct rtattr *tail;
+
+ memset(&opt, 0, sizeof(opt));
+
+ while (argc > 0) {
+ if (matches(*argv, "limit") == 0) {
+ NEXT_ARG();
+ if (opt.limit || latency) {
+ fprintf(stderr, "Double \"limit/latency\" spec\n");
+ return -1;
+ }
+ if (get_size(&opt.limit, *argv)) {
+ explain1("limit");
+ return -1;
+ }
+ ok++;
+ } else if (matches(*argv, "latency") == 0) {
+ NEXT_ARG();
+ if (opt.limit || latency) {
+ fprintf(stderr, "Double \"limit/latency\" spec\n");
+ return -1;
+ }
+ if (get_time(&latency, *argv)) {
+ explain1("latency");
+ return -1;
+ }
+ ok++;
+ } else if (matches(*argv, "burst") == 0 ||
+ strcmp(*argv, "buffer") == 0 ||
+ strcmp(*argv, "maxburst") == 0) {
+ NEXT_ARG();
+ if (buffer) {
+ fprintf(stderr, "Double \"buffer/burst\" spec\n");
+ return -1;
+ }
+ if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) {
+ explain1("buffer");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "mtu") == 0 ||
+ strcmp(*argv, "minburst") == 0) {
+ NEXT_ARG();
+ if (mtu) {
+ fprintf(stderr, "Double \"mtu/minburst\" spec\n");
+ return -1;
+ }
+ if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) {
+ explain1("mtu");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "mpu") == 0) {
+ NEXT_ARG();
+ if (mpu) {
+ fprintf(stderr, "Double \"mpu\" spec\n");
+ return -1;
+ }
+ if (get_size(&mpu, *argv)) {
+ explain1("mpu");
+ return -1;
+ }
+ ok++;
+ } else if (strcmp(*argv, "rate") == 0) {
+ NEXT_ARG();
+ if (opt.rate.rate) {
+ fprintf(stderr, "Double \"rate\" spec\n");
+ return -1;
+ }
+ if (get_rate(&opt.rate.rate, *argv)) {
+ explain1("rate");
+ return -1;
+ }
+ ok++;
+ } else if (matches(*argv, "peakrate") == 0) {
+ NEXT_ARG();
+ if (opt.peakrate.rate) {
+ fprintf(stderr, "Double \"peakrate\" spec\n");
+ return -1;
+ }
+ if (get_rate(&opt.peakrate.rate, *argv)) {
+ explain1("peakrate");
+ return -1;
+ }
+ ok++;
+ } else if (matches(*argv, "overhead") == 0) {
+ NEXT_ARG();
+ if (overhead) {
+ fprintf(stderr, "Double \"overhead\" spec\n");
+ return -1;
+ }
+ if (get_u16(&overhead, *argv, 10)) {
+ explain1("overhead"); return -1;
+ }
+ } else if (matches(*argv, "linklayer") == 0) {
+ NEXT_ARG();
+ if (get_linklayer(&linklayer, *argv)) {
+ explain1("linklayer"); return -1;
+ }
+ } else if (strcmp(*argv, "help") == 0) {
+ explain();
+ return -1;
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ if (!ok)
+ return 0;
+
+ if (opt.rate.rate == 0 || !buffer) {
+ fprintf(stderr, "Both \"rate\" and \"burst\" are required.\n");
+ return -1;
+ }
+ if (opt.peakrate.rate) {
+ if (!mtu) {
+ fprintf(stderr, "\"mtu\" is required, if \"peakrate\" is requested.\n");
+ return -1;
+ }
+ }
+
+ if (opt.limit == 0 && latency == 0) {
+ fprintf(stderr, "Either \"limit\" or \"latency\" are required.\n");
+ return -1;
+ }
+
+ if (opt.limit == 0) {
+ double lim = opt.rate.rate*(double)latency/TIME_UNITS_PER_SEC + buffer;
+ if (opt.peakrate.rate) {
+ double lim2 = opt.peakrate.rate*(double)latency/TIME_UNITS_PER_SEC + mtu;
+ if (lim2 < lim)
+ lim = lim2;
+ }
+ opt.limit = lim;
+ }
+
+ opt.rate.mpu = mpu;
+ opt.rate.overhead = overhead;
+ if (tc_calc_rtable(&opt.rate, rtab, Rcell_log, mtu, linklayer) < 0) {
+ fprintf(stderr, "TBF: failed to calculate rate table.\n");
+ return -1;
+ }
+ opt.buffer = tc_calc_xmittime(opt.rate.rate, buffer);
+
+ if (opt.peakrate.rate) {
+ opt.peakrate.mpu = mpu;
+ opt.peakrate.overhead = overhead;
+ if (tc_calc_rtable(&opt.peakrate, ptab, Pcell_log, mtu, linklayer) < 0) {
+ fprintf(stderr, "TBF: failed to calculate peak rate table.\n");
+ return -1;
+ }
+ opt.mtu = tc_calc_xmittime(opt.peakrate.rate, mtu);
+ }
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ addattr_l(n, 2024, TCA_TBF_PARMS, &opt, sizeof(opt));
+ addattr_l(n, 3024, TCA_TBF_RTAB, rtab, 1024);
+ if (opt.peakrate.rate)
+ addattr_l(n, 4096, TCA_TBF_PTAB, ptab, 1024);
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static int tbf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
+{
+ struct rtattr *tb[TCA_TBF_PTAB+1];
+ struct tc_tbf_qopt *qopt;
+ double buffer, mtu;
+ double latency;
+ SPRINT_BUF(b1);
+ SPRINT_BUF(b2);
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_TBF_PTAB, opt);
+
+ if (tb[TCA_TBF_PARMS] == NULL)
+ return -1;
+
+ qopt = RTA_DATA(tb[TCA_TBF_PARMS]);
+ if (RTA_PAYLOAD(tb[TCA_TBF_PARMS]) < sizeof(*qopt))
+ return -1;
+ fprintf(f, "rate %s ", sprint_rate(qopt->rate.rate, b1));
+ buffer = tc_calc_xmitsize(qopt->rate.rate, qopt->buffer);
+ if (show_details) {
+ fprintf(f, "burst %s/%u mpu %s ", sprint_size(buffer, b1),
+ 1<<qopt->rate.cell_log, sprint_size(qopt->rate.mpu, b2));
+ } else {
+ fprintf(f, "burst %s ", sprint_size(buffer, b1));
+ }
+ if (show_raw)
+ fprintf(f, "[%08x] ", qopt->buffer);
+ if (qopt->peakrate.rate) {
+ fprintf(f, "peakrate %s ", sprint_rate(qopt->peakrate.rate, b1));
+ if (qopt->mtu || qopt->peakrate.mpu) {
+ mtu = tc_calc_xmitsize(qopt->peakrate.rate, qopt->mtu);
+ if (show_details) {
+ fprintf(f, "mtu %s/%u mpu %s ", sprint_size(mtu, b1),
+ 1<<qopt->peakrate.cell_log, sprint_size(qopt->peakrate.mpu, b2));
+ } else {
+ fprintf(f, "minburst %s ", sprint_size(mtu, b1));
+ }
+ if (show_raw)
+ fprintf(f, "[%08x] ", qopt->mtu);
+ }
+ }
+
+ if (show_raw)
+ fprintf(f, "limit %s ", sprint_size(qopt->limit, b1));
+
+ latency = TIME_UNITS_PER_SEC*(qopt->limit/(double)qopt->rate.rate) - tc_core_tick2time(qopt->buffer);
+ if (qopt->peakrate.rate) {
+ double lat2 = TIME_UNITS_PER_SEC*(qopt->limit/(double)qopt->peakrate.rate) - tc_core_tick2time(qopt->mtu);
+ if (lat2 > latency)
+ latency = lat2;
+ }
+ fprintf(f, "lat %s ", sprint_time(latency, b1));
+
+ if (qopt->rate.overhead) {
+ fprintf(f, "overhead %d", qopt->rate.overhead);
+ }
+
+ return 0;
+}
+
+struct qdisc_util tbf_qdisc_util = {
+ .id = "tbf",
+ .parse_qopt = tbf_parse_opt,
+ .print_qopt = tbf_print_opt,
+};
+
diff --git a/tc/static-syms.c b/tc/static-syms.c
new file mode 100644
index 0000000..1ed3a8a
--- /dev/null
+++ b/tc/static-syms.c
@@ -0,0 +1,6 @@
+#include <string.h>
+void *_dlsym(const char *sym)
+{
+#include "static-syms.h"
+ return NULL;
+}
diff --git a/tc/tc.c b/tc/tc.c
new file mode 100644
index 0000000..8e362d2
--- /dev/null
+++ b/tc/tc.c
@@ -0,0 +1,320 @@
+/*
+ * tc.c "tc" utility frontend.
+ *
+ * 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>
+ *
+ * Fixes:
+ *
+ * Petri Mattila <petri@prihateam.fi> 990308: wrong memset's resulted in faults
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <errno.h>
+
+#include "SNAPSHOT.h"
+#include "utils.h"
+#include "tc_util.h"
+#include "tc_common.h"
+
+int show_stats = 0;
+int show_details = 0;
+int show_raw = 0;
+int show_pretty = 0;
+
+int resolve_hosts = 0;
+int use_iec = 0;
+int force = 0;
+struct rtnl_handle rth;
+
+static void *BODY = NULL; /* cached handle dlopen(NULL) */
+static struct qdisc_util * qdisc_list;
+static struct filter_util * filter_list;
+
+static int print_noqopt(struct qdisc_util *qu, FILE *f,
+ struct rtattr *opt)
+{
+ if (opt && RTA_PAYLOAD(opt))
+ fprintf(f, "[Unknown qdisc, optlen=%u] ",
+ (unsigned) RTA_PAYLOAD(opt));
+ return 0;
+}
+
+static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
+{
+ if (argc) {
+ fprintf(stderr, "Unknown qdisc \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv);
+ return -1;
+ }
+ return 0;
+}
+
+static int print_nofopt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle)
+{
+ if (opt && RTA_PAYLOAD(opt))
+ fprintf(f, "fh %08x [Unknown filter, optlen=%u] ",
+ fhandle, (unsigned) RTA_PAYLOAD(opt));
+ else if (fhandle)
+ fprintf(f, "fh %08x ", fhandle);
+ return 0;
+}
+
+static int parse_nofopt(struct filter_util *qu, char *fhandle, int argc, char **argv, struct nlmsghdr *n)
+{
+ __u32 handle;
+
+ if (argc) {
+ fprintf(stderr, "Unknown filter \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv);
+ return -1;
+ }
+ if (fhandle) {
+ struct tcmsg *t = NLMSG_DATA(n);
+ if (get_u32(&handle, fhandle, 16)) {
+ fprintf(stderr, "Unparsable filter ID \"%s\"\n", fhandle);
+ return -1;
+ }
+ t->tcm_handle = handle;
+ }
+ return 0;
+}
+
+struct qdisc_util *get_qdisc_kind(const char *str)
+{
+ void *dlh;
+ char buf[256];
+ struct qdisc_util *q;
+
+ for (q = qdisc_list; q; q = q->next)
+ if (strcmp(q->id, str) == 0)
+ return q;
+
+ snprintf(buf, sizeof(buf), "%s/q_%s.so", get_tc_lib(), str);
+ dlh = dlopen(buf, RTLD_LAZY);
+ if (!dlh) {
+ /* look in current binary, only open once */
+ dlh = BODY;
+ if (dlh == NULL) {
+ dlh = BODY = dlopen(NULL, RTLD_LAZY);
+ if (dlh == NULL)
+ goto noexist;
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "%s_qdisc_util", str);
+ q = dlsym(dlh, buf);
+ if (q == NULL)
+ goto noexist;
+
+reg:
+ q->next = qdisc_list;
+ qdisc_list = q;
+ return q;
+
+noexist:
+ q = malloc(sizeof(*q));
+ if (q) {
+
+ memset(q, 0, sizeof(*q));
+ q->id = strcpy(malloc(strlen(str)+1), str);
+ q->parse_qopt = parse_noqopt;
+ q->print_qopt = print_noqopt;
+ goto reg;
+ }
+ return q;
+}
+
+
+struct filter_util *get_filter_kind(const char *str)
+{
+ void *dlh;
+ char buf[256];
+ struct filter_util *q;
+
+ for (q = filter_list; q; q = q->next)
+ if (strcmp(q->id, str) == 0)
+ return q;
+
+ snprintf(buf, sizeof(buf), "%s/f_%s.so", get_tc_lib(), str);
+ dlh = dlopen(buf, RTLD_LAZY);
+ if (dlh == NULL) {
+ dlh = BODY;
+ if (dlh == NULL) {
+ dlh = BODY = dlopen(NULL, RTLD_LAZY);
+ if (dlh == NULL)
+ goto noexist;
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "%s_filter_util", str);
+ q = dlsym(dlh, buf);
+ if (q == NULL)
+ goto noexist;
+
+reg:
+ q->next = filter_list;
+ filter_list = q;
+ return q;
+noexist:
+ q = malloc(sizeof(*q));
+ if (q) {
+ memset(q, 0, sizeof(*q));
+ strncpy(q->id, str, 15);
+ q->parse_fopt = parse_nofopt;
+ q->print_fopt = print_nofopt;
+ goto reg;
+ }
+ return q;
+}
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n"
+ " tc [-force] -batch filename\n"
+ "where OBJECT := { qdisc | class | filter | action | monitor }\n"
+ " OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -p[retty] | -b[atch] [filename] }\n");
+}
+
+static int do_cmd(int argc, char **argv)
+{
+ if (matches(*argv, "qdisc") == 0)
+ return do_qdisc(argc-1, argv+1);
+
+ if (matches(*argv, "class") == 0)
+ return do_class(argc-1, argv+1);
+
+ if (matches(*argv, "filter") == 0)
+ return do_filter(argc-1, argv+1);
+
+ if (matches(*argv, "actions") == 0)
+ return do_action(argc-1, argv+1);
+
+ if (matches(*argv, "monitor") == 0)
+ return do_tcmonitor(argc-1, argv+1);
+
+ if (matches(*argv, "help") == 0) {
+ usage();
+ return 0;
+ }
+
+ fprintf(stderr, "Object \"%s\" is unknown, try \"tc help\".\n",
+ *argv);
+ return -1;
+}
+
+static int batch(const char *name)
+{
+ char *line = NULL;
+ size_t len = 0;
+ int ret = 0;
+
+ if (name && strcmp(name, "-") != 0) {
+ if (freopen(name, "r", stdin) == NULL) {
+ fprintf(stderr, "Cannot open file \"%s\" for reading: %s\n",
+ name, strerror(errno));
+ return -1;
+ }
+ }
+
+ tc_core_init();
+
+ if (rtnl_open(&rth, 0) < 0) {
+ fprintf(stderr, "Cannot open rtnetlink\n");
+ return -1;
+ }
+
+ cmdlineno = 0;
+ while (getcmdline(&line, &len, stdin) != -1) {
+ char *largv[100];
+ int largc;
+
+ largc = makeargs(line, largv, 100);
+ if (largc == 0)
+ continue; /* blank line */
+
+ if (do_cmd(largc, largv)) {
+ fprintf(stderr, "Command failed %s:%d\n", name, cmdlineno);
+ ret = 1;
+ if (!force)
+ break;
+ }
+ }
+ if (line)
+ free(line);
+
+ rtnl_close(&rth);
+ return ret;
+}
+
+
+int main(int argc, char **argv)
+{
+ int ret;
+ int do_batching = 0;
+ char *batchfile = NULL;
+
+ while (argc > 1) {
+ if (argv[1][0] != '-')
+ break;
+ if (matches(argv[1], "-stats") == 0 ||
+ matches(argv[1], "-statistics") == 0) {
+ ++show_stats;
+ } else if (matches(argv[1], "-details") == 0) {
+ ++show_details;
+ } else if (matches(argv[1], "-raw") == 0) {
+ ++show_raw;
+ } else if (matches(argv[1], "-pretty") == 0) {
+ ++show_pretty;
+ } else if (matches(argv[1], "-Version") == 0) {
+ printf("tc utility, iproute2-ss%s\n", SNAPSHOT);
+ return 0;
+ } else if (matches(argv[1], "-iec") == 0) {
+ ++use_iec;
+ } else if (matches(argv[1], "-help") == 0) {
+ usage();
+ return 0;
+ } else if (matches(argv[1], "-force") == 0) {
+ ++force;
+ } else if (matches(argv[1], "-batch") == 0) {
+ do_batching = 1;
+ if (argc > 2)
+ batchfile = argv[2];
+ argc--; argv++;
+ } else {
+ fprintf(stderr, "Option \"%s\" is unknown, try \"tc -help\".\n", argv[1]);
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ if (do_batching)
+ return batch(batchfile);
+
+ if (argc <= 1) {
+ usage();
+ return 0;
+ }
+
+ tc_core_init();
+ if (rtnl_open(&rth, 0) < 0) {
+ fprintf(stderr, "Cannot open rtnetlink\n");
+ exit(1);
+ }
+
+ ret = do_cmd(argc-1, argv+1);
+ rtnl_close(&rth);
+
+ return ret;
+}
diff --git a/tc/tc_cbq.c b/tc/tc_cbq.c
new file mode 100644
index 0000000..0bb262e
--- /dev/null
+++ b/tc/tc_cbq.c
@@ -0,0 +1,57 @@
+/*
+ * tc_cbq.c CBQ maintanance routines.
+ *
+ * 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 <math.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "tc_core.h"
+#include "tc_cbq.h"
+
+unsigned tc_cbq_calc_maxidle(unsigned bndw, unsigned rate, unsigned avpkt,
+ int ewma_log, unsigned maxburst)
+{
+ double maxidle;
+ double g = 1.0 - 1.0/(1<<ewma_log);
+ double xmt = (double)avpkt/bndw;
+
+ maxidle = xmt*(1-g);
+ if (bndw != rate && maxburst) {
+ double vxmt = (double)avpkt/rate - xmt;
+ vxmt *= (pow(g, -(double)maxburst) - 1);
+ if (vxmt > maxidle)
+ maxidle = vxmt;
+ }
+ return tc_core_time2tick(maxidle*(1<<ewma_log)*TIME_UNITS_PER_SEC);
+}
+
+unsigned tc_cbq_calc_offtime(unsigned bndw, unsigned rate, unsigned avpkt,
+ int ewma_log, unsigned minburst)
+{
+ double g = 1.0 - 1.0/(1<<ewma_log);
+ double offtime = (double)avpkt/rate - (double)avpkt/bndw;
+
+ if (minburst == 0)
+ return 0;
+ if (minburst == 1)
+ offtime *= pow(g, -(double)minburst) - 1;
+ else
+ offtime *= 1 + (pow(g, -(double)(minburst-1)) - 1)/(1-g);
+ return tc_core_time2tick(offtime*TIME_UNITS_PER_SEC);
+}
diff --git a/tc/tc_cbq.h b/tc/tc_cbq.h
new file mode 100644
index 0000000..8f95649
--- /dev/null
+++ b/tc/tc_cbq.h
@@ -0,0 +1,9 @@
+#ifndef _TC_CBQ_H_
+#define _TC_CBQ_H_ 1
+
+unsigned tc_cbq_calc_maxidle(unsigned bndw, unsigned rate, unsigned avpkt,
+ int ewma_log, unsigned maxburst);
+unsigned tc_cbq_calc_offtime(unsigned bndw, unsigned rate, unsigned avpkt,
+ int ewma_log, unsigned minburst);
+
+#endif
diff --git a/tc/tc_class.c b/tc/tc_class.c
new file mode 100644
index 0000000..9d4eea5
--- /dev/null
+++ b/tc/tc_class.c
@@ -0,0 +1,334 @@
+/*
+ * 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"
+
+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;
+}
+
+int tc_class_modify(int cmd, unsigned flags, int argc, char **argv)
+{
+ struct {
+ struct nlmsghdr n;
+ struct tcmsg t;
+ char buf[4096];
+ } req;
+ 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;
+
+ 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(*argv, "invalid class ID");
+ 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(*argv, "invalid parent ID");
+ 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, 0, 0, NULL, NULL, NULL) < 0)
+ return 2;
+
+ return 0;
+}
+
+int filter_ifindex;
+__u32 filter_qdisc;
+__u32 filter_classid;
+
+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 (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) {
+ 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 ", (char*)RTA_DATA(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;
+}
+
+
+int tc_class_list(int argc, char **argv)
+{
+ struct tcmsg t;
+ char d[16];
+
+ memset(&t, 0, sizeof(t));
+ t.tcm_family = AF_UNSPEC;
+ memset(d, 0, sizeof(d));
+
+ 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(*argv, "invalid qdisc ID");
+ } 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");
+ } 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(*argv, "invalid parent ID");
+ 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, NULL, NULL) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ return 1;
+ }
+
+ 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;
+}
diff --git a/tc/tc_common.h b/tc/tc_common.h
new file mode 100644
index 0000000..4f88856
--- /dev/null
+++ b/tc/tc_common.h
@@ -0,0 +1,21 @@
+
+#define TCA_BUF_MAX (64*1024)
+
+extern struct rtnl_handle rth;
+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 do_tcmonitor(int argc, char **argv);
+extern int print_action(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int print_filter(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int print_qdisc(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int print_class(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern void print_size_table(FILE *fp, const char *prefix, struct rtattr *rta);
+
+struct tc_estimator;
+extern int parse_estimator(int *p_argc, char ***p_argv, struct tc_estimator *est);
+
+struct tc_sizespec;
+extern int parse_size_table(int *p_argc, char ***p_argv, struct tc_sizespec *s);
+extern int check_size_table_opts(struct tc_sizespec *s);
diff --git a/tc/tc_core.c b/tc/tc_core.c
new file mode 100644
index 0000000..9a0ff39
--- /dev/null
+++ b/tc/tc_core.c
@@ -0,0 +1,211 @@
+/*
+ * tc_core.c TC core library.
+ *
+ * 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 <math.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "tc_core.h"
+#include <linux/atm.h>
+
+static double tick_in_usec = 1;
+static double clock_factor = 1;
+
+int tc_core_time2big(unsigned time)
+{
+ __u64 t = time;
+
+ t *= tick_in_usec;
+ return (t >> 32) != 0;
+}
+
+
+unsigned tc_core_time2tick(unsigned time)
+{
+ return time*tick_in_usec;
+}
+
+unsigned tc_core_tick2time(unsigned tick)
+{
+ return tick/tick_in_usec;
+}
+
+unsigned tc_core_time2ktime(unsigned time)
+{
+ return time * clock_factor;
+}
+
+unsigned tc_core_ktime2time(unsigned ktime)
+{
+ return ktime / clock_factor;
+}
+
+unsigned tc_calc_xmittime(unsigned rate, unsigned size)
+{
+ return tc_core_time2tick(TIME_UNITS_PER_SEC*((double)size/rate));
+}
+
+unsigned tc_calc_xmitsize(unsigned rate, unsigned ticks)
+{
+ return ((double)rate*tc_core_tick2time(ticks))/TIME_UNITS_PER_SEC;
+}
+
+/*
+ * The align to ATM cells is used for determining the (ATM) SAR
+ * alignment overhead at the ATM layer. (SAR = Segmentation And
+ * Reassembly). This is for example needed when scheduling packet on
+ * an ADSL connection. Note that the extra ATM-AAL overhead is _not_
+ * included in this calculation. This overhead is added in the kernel
+ * before doing the rate table lookup, as this gives better precision
+ * (as the table will always be aligned for 48 bytes).
+ * --Hawk, d.7/11-2004. <hawk@diku.dk>
+ */
+unsigned tc_align_to_atm(unsigned size)
+{
+ int linksize, cells;
+ cells = size / ATM_CELL_PAYLOAD;
+ if ((size % ATM_CELL_PAYLOAD) > 0)
+ cells++;
+
+ linksize = cells * ATM_CELL_SIZE; /* Use full cell size to add ATM tax */
+ return linksize;
+}
+
+unsigned tc_adjust_size(unsigned sz, unsigned mpu, enum link_layer linklayer)
+{
+ if (sz < mpu)
+ sz = mpu;
+
+ switch (linklayer) {
+ case LINKLAYER_ATM:
+ return tc_align_to_atm(sz);
+ case LINKLAYER_ETHERNET:
+ default:
+ // No size adjustments on Ethernet
+ return sz;
+ }
+}
+
+/*
+ rtab[pkt_len>>cell_log] = pkt_xmit_time
+ */
+
+int tc_calc_rtable(struct tc_ratespec *r, __u32 *rtab,
+ int cell_log, unsigned mtu,
+ enum link_layer linklayer)
+{
+ int i;
+ unsigned sz;
+ unsigned bps = r->rate;
+ unsigned mpu = r->mpu;
+
+ if (mtu == 0)
+ mtu = 2047;
+
+ if (cell_log < 0) {
+ cell_log = 0;
+ while ((mtu >> cell_log) > 255)
+ cell_log++;
+ }
+
+ for (i=0; i<256; i++) {
+ sz = tc_adjust_size((i + 1) << cell_log, mpu, linklayer);
+ rtab[i] = tc_calc_xmittime(bps, sz);
+ }
+
+ r->cell_align=-1; // Due to the sz calc
+ r->cell_log=cell_log;
+ return cell_log;
+}
+
+/*
+ stab[pkt_len>>cell_log] = pkt_xmit_size>>size_log
+ */
+
+int tc_calc_size_table(struct tc_sizespec *s, __u16 **stab)
+{
+ int i;
+ enum link_layer linklayer = s->linklayer;
+ unsigned int sz;
+
+ if (linklayer <= LINKLAYER_ETHERNET && s->mpu == 0) {
+ /* don't need data table in this case (only overhead set) */
+ s->mtu = 0;
+ s->tsize = 0;
+ s->cell_log = 0;
+ s->cell_align = 0;
+ *stab = NULL;
+ return 0;
+ }
+
+ if (s->mtu == 0)
+ s->mtu = 2047;
+ if (s->tsize == 0)
+ s->tsize = 512;
+
+ s->cell_log = 0;
+ while ((s->mtu >> s->cell_log) > s->tsize - 1)
+ s->cell_log++;
+
+ *stab = malloc(s->tsize * sizeof(__u16));
+ if (!*stab)
+ return -1;
+
+again:
+ for (i = s->tsize - 1; i >= 0; i--) {
+ sz = tc_adjust_size((i + 1) << s->cell_log, s->mpu, linklayer);
+ if ((sz >> s->size_log) > UINT16_MAX) {
+ s->size_log++;
+ goto again;
+ }
+ (*stab)[i] = sz >> s->size_log;
+ }
+
+ s->cell_align = -1; // Due to the sz calc
+ return 0;
+}
+
+int tc_core_init()
+{
+ FILE *fp;
+ __u32 clock_res;
+ __u32 t2us;
+ __u32 us2t;
+
+ fp = fopen("/proc/net/psched", "r");
+ if (fp == NULL)
+ return -1;
+
+ if (fscanf(fp, "%08x%08x%08x", &t2us, &us2t, &clock_res) != 3) {
+ fclose(fp);
+ return -1;
+ }
+ fclose(fp);
+
+ /* compatibility hack: for old iproute binaries (ignoring
+ * the kernel clock resolution) the kernel advertises a
+ * tick multiplier of 1000 in case of nano-second resolution,
+ * which really is 1. */
+ if (clock_res == 1000000000)
+ t2us = us2t;
+
+ clock_factor = (double)clock_res / TIME_UNITS_PER_SEC;
+ tick_in_usec = (double)t2us / us2t * clock_factor;
+ return 0;
+}
diff --git a/tc/tc_core.h b/tc/tc_core.h
new file mode 100644
index 0000000..5a693ba
--- /dev/null
+++ b/tc/tc_core.h
@@ -0,0 +1,34 @@
+#ifndef _TC_CORE_H_
+#define _TC_CORE_H_ 1
+
+#include <asm/types.h>
+#include <linux/pkt_sched.h>
+
+#define TIME_UNITS_PER_SEC 1000000
+
+enum link_layer {
+ LINKLAYER_UNSPEC,
+ LINKLAYER_ETHERNET,
+ LINKLAYER_ATM,
+};
+
+
+int tc_core_time2big(unsigned time);
+unsigned tc_core_time2tick(unsigned time);
+unsigned tc_core_tick2time(unsigned tick);
+unsigned tc_core_time2ktime(unsigned time);
+unsigned tc_core_ktime2time(unsigned ktime);
+unsigned tc_calc_xmittime(unsigned rate, unsigned size);
+unsigned tc_calc_xmitsize(unsigned rate, unsigned ticks);
+int tc_calc_rtable(struct tc_ratespec *r, __u32 *rtab,
+ int cell_log, unsigned mtu, enum link_layer link_layer);
+int tc_calc_size_table(struct tc_sizespec *s, __u16 **stab);
+
+int tc_setup_estimator(unsigned A, unsigned time_const, struct tc_estimator *est);
+
+int tc_core_init(void);
+
+extern struct rtnl_handle g_rth;
+extern int is_batch_mode;
+
+#endif
diff --git a/tc/tc_estimator.c b/tc/tc_estimator.c
new file mode 100644
index 0000000..e559add
--- /dev/null
+++ b/tc/tc_estimator.c
@@ -0,0 +1,44 @@
+/*
+ * tc_core.c TC core library.
+ *
+ * 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 <math.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "tc_core.h"
+
+int tc_setup_estimator(unsigned A, unsigned time_const, struct tc_estimator *est)
+{
+ for (est->interval=0; est->interval<=5; est->interval++) {
+ if (A <= (1<<est->interval)*(TIME_UNITS_PER_SEC/4))
+ break;
+ }
+ if (est->interval > 5)
+ return -1;
+ est->interval -= 2;
+ for (est->ewma_log=1; est->ewma_log<32; est->ewma_log++) {
+ double w = 1.0 - 1.0/(1<<est->ewma_log);
+ if (A/(-log(w)) > time_const)
+ break;
+ }
+ est->ewma_log--;
+ if (est->ewma_log==0 || est->ewma_log >= 31)
+ return -1;
+ return 0;
+}
diff --git a/tc/tc_filter.c b/tc/tc_filter.c
new file mode 100644
index 0000000..919c57c
--- /dev/null
+++ b/tc/tc_filter.c
@@ -0,0 +1,378 @@
+/*
+ * tc_filter.c "tc filter".
+ *
+ * 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 <linux/if_ether.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "tc_util.h"
+#include "tc_common.h"
+
+static void usage(void);
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: tc filter [ add | del | change | replace | show ] dev STRING\n");
+ fprintf(stderr, " [ pref PRIO ] protocol PROTO\n");
+ fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n");
+ fprintf(stderr, " [ root | classid CLASSID ] [ handle FILTERID ]\n");
+ fprintf(stderr, " [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " tc filter show [ dev STRING ] [ root | parent CLASSID ]\n");
+ fprintf(stderr, "Where:\n");
+ fprintf(stderr, "FILTER_TYPE := { rsvp | u32 | fw | route | etc. }\n");
+ fprintf(stderr, "FILTERID := ... format depends on classifier, see there\n");
+ fprintf(stderr, "OPTIONS := ... try tc filter add <desired FILTER_KIND> help\n");
+ return;
+}
+
+
+int tc_filter_modify(int cmd, unsigned flags, int argc, char **argv)
+{
+ struct {
+ struct nlmsghdr n;
+ struct tcmsg t;
+ char buf[MAX_MSG];
+ } req;
+ struct filter_util *q = NULL;
+ __u32 prio = 0;
+ __u32 protocol = 0;
+ int protocol_set = 0;
+ char *fhandle = NULL;
+ char d[16];
+ char k[16];
+ struct tc_estimator est;
+
+ memset(&req, 0, sizeof(req));
+ memset(&est, 0, sizeof(est));
+ memset(d, 0, sizeof(d));
+ memset(k, 0, sizeof(k));
+ memset(&req, 0, sizeof(req));
+
+ 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;
+
+ if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE)
+ protocol = ETH_P_ALL;
+
+ 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, "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");
+ req.t.tcm_parent = handle;
+ } else if (strcmp(*argv, "handle") == 0) {
+ NEXT_ARG();
+ if (fhandle)
+ duparg("handle", *argv);
+ fhandle = *argv;
+ } else if (matches(*argv, "preference") == 0 ||
+ matches(*argv, "priority") == 0) {
+ NEXT_ARG();
+ if (prio)
+ duparg("priority", *argv);
+ if (get_u32(&prio, *argv, 0))
+ invarg(*argv, "invalid priority value");
+ } else if (matches(*argv, "protocol") == 0) {
+ __u16 id;
+ NEXT_ARG();
+ if (protocol_set)
+ duparg("protocol", *argv);
+ if (ll_proto_a2n(&id, *argv))
+ invarg(*argv, "invalid protocol");
+ protocol = id;
+ protocol_set = 1;
+ } else if (matches(*argv, "estimator") == 0) {
+ if (parse_estimator(&argc, &argv, &est) < 0)
+ return -1;
+ } else if (matches(*argv, "help") == 0) {
+ usage();
+ return 0;
+ } else {
+ strncpy(k, *argv, sizeof(k)-1);
+
+ q = get_filter_kind(k);
+ argc--; argv++;
+ break;
+ }
+
+ argc--; argv++;
+ }
+
+ req.t.tcm_info = TC_H_MAKE(prio<<16, protocol);
+
+ if (k[0])
+ addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
+
+ if (q) {
+ if (q->parse_fopt(q, fhandle, argc, argv, &req.n))
+ return 1;
+ } else {
+ if (fhandle) {
+ fprintf(stderr, "Must specify filter type when using "
+ "\"handle\"\n");
+ return -1;
+ }
+ if (argc) {
+ if (matches(*argv, "help") == 0)
+ usage();
+ fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n", *argv);
+ return -1;
+ }
+ }
+ if (est.ewma_log)
+ addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
+
+
+ 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, 0, 0, NULL, NULL, NULL) < 0) {
+ fprintf(stderr, "We have an error talking to the kernel\n");
+ return 2;
+ }
+
+ return 0;
+}
+
+static __u32 filter_parent;
+static int filter_ifindex;
+static __u32 filter_prio;
+static __u32 filter_protocol;
+__u16 f_proto = 0;
+
+int print_filter(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 filter_util *q;
+ char abuf[256];
+
+ if (n->nlmsg_type != RTM_NEWTFILTER && n->nlmsg_type != RTM_DELTFILTER) {
+ fprintf(stderr, "Not a filter\n");
+ return 0;
+ }
+ len -= NLMSG_LENGTH(sizeof(*t));
+ if (len < 0) {
+ fprintf(stderr, "Wrong len %d\n", len);
+ return -1;
+ }
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len);
+
+ if (tb[TCA_KIND] == NULL) {
+ fprintf(stderr, "print_filter: NULL kind\n");
+ return -1;
+ }
+
+ if (n->nlmsg_type == RTM_DELTFILTER)
+ fprintf(fp, "deleted ");
+
+ fprintf(fp, "filter ");
+ if (!filter_ifindex || filter_ifindex != t->tcm_ifindex)
+ fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex));
+
+ if (!filter_parent || filter_parent != t->tcm_parent) {
+ if (t->tcm_parent == TC_H_ROOT)
+ fprintf(fp, "root ");
+ else {
+ print_tc_classid(abuf, sizeof(abuf), t->tcm_parent);
+ fprintf(fp, "parent %s ", abuf);
+ }
+ }
+ if (t->tcm_info) {
+ f_proto = TC_H_MIN(t->tcm_info);
+ __u32 prio = TC_H_MAJ(t->tcm_info)>>16;
+ if (!filter_protocol || filter_protocol != f_proto) {
+ if (f_proto) {
+ SPRINT_BUF(b1);
+ fprintf(fp, "protocol %s ",
+ ll_proto_n2a(f_proto, b1, sizeof(b1)));
+ }
+ }
+ if (!filter_prio || filter_prio != prio) {
+ if (prio)
+ fprintf(fp, "pref %u ", prio);
+ }
+ }
+ fprintf(fp, "%s ", (char*)RTA_DATA(tb[TCA_KIND]));
+ q = get_filter_kind(RTA_DATA(tb[TCA_KIND]));
+ if (tb[TCA_OPTIONS]) {
+ if (q)
+ q->print_fopt(q, fp, tb[TCA_OPTIONS], t->tcm_handle);
+ else
+ fprintf(fp, "[cannot parse parameters]");
+ }
+ fprintf(fp, "\n");
+
+ if (show_stats && (tb[TCA_STATS] || tb[TCA_STATS2])) {
+ print_tcstats_attr(fp, tb, " ", NULL);
+ fprintf(fp, "\n");
+ }
+
+ fflush(fp);
+ return 0;
+}
+
+
+int tc_filter_list(int argc, char **argv)
+{
+ struct tcmsg t;
+ char d[16];
+ __u32 prio = 0;
+ __u32 protocol = 0;
+ char *fhandle = NULL;
+
+ memset(&t, 0, sizeof(t));
+ t.tcm_family = AF_UNSPEC;
+ memset(d, 0, sizeof(d));
+
+ 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, "root") == 0) {
+ if (t.tcm_parent) {
+ fprintf(stderr, "Error: \"root\" is duplicate parent ID\n");
+ return -1;
+ }
+ filter_parent = t.tcm_parent = TC_H_ROOT;
+ } else if (strcmp(*argv, "parent") == 0) {
+ __u32 handle;
+ NEXT_ARG();
+ if (t.tcm_parent)
+ duparg("parent", *argv);
+ if (get_tc_classid(&handle, *argv))
+ invarg(*argv, "invalid parent ID");
+ filter_parent = t.tcm_parent = handle;
+ } else if (strcmp(*argv, "handle") == 0) {
+ NEXT_ARG();
+ if (fhandle)
+ duparg("handle", *argv);
+ fhandle = *argv;
+ } else if (matches(*argv, "preference") == 0 ||
+ matches(*argv, "priority") == 0) {
+ NEXT_ARG();
+ if (prio)
+ duparg("priority", *argv);
+ if (get_u32(&prio, *argv, 0))
+ invarg(*argv, "invalid preference");
+ filter_prio = prio;
+ } else if (matches(*argv, "protocol") == 0) {
+ __u16 res;
+ NEXT_ARG();
+ if (protocol)
+ duparg("protocol", *argv);
+ if (ll_proto_a2n(&res, *argv))
+ invarg(*argv, "invalid protocol");
+ protocol = res;
+ filter_protocol = protocol;
+ } else if (matches(*argv, "help") == 0) {
+ usage();
+ } else {
+ fprintf(stderr, " What is \"%s\"? Try \"tc filter help\"\n", *argv);
+ return -1;
+ }
+
+ argc--; argv++;
+ }
+
+ t.tcm_info = TC_H_MAKE(prio<<16, protocol);
+
+ 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_GETTFILTER, &t, sizeof(t)) < 0) {
+ perror("Cannot send dump request");
+ return 1;
+ }
+
+ if (rtnl_dump_filter(&rth, print_filter, stdout, NULL, NULL) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+int do_filter(int argc, char **argv)
+{
+ if (argc < 1)
+ return tc_filter_list(0, NULL);
+ if (matches(*argv, "add") == 0)
+ return tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1);
+ if (matches(*argv, "change") == 0)
+ return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1);
+ if (matches(*argv, "replace") == 0)
+ return tc_filter_modify(RTM_NEWTFILTER, NLM_F_CREATE, argc-1, argv+1);
+ if (matches(*argv, "delete") == 0)
+ return tc_filter_modify(RTM_DELTFILTER, 0, argc-1, argv+1);
+#if 0
+ if (matches(*argv, "get") == 0)
+ return tc_filter_get(RTM_GETTFILTER, 0, argc-1, argv+1);
+#endif
+ if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
+ || matches(*argv, "lst") == 0)
+ return tc_filter_list(argc-1, argv+1);
+ if (matches(*argv, "help") == 0) {
+ usage();
+ return 0;
+ }
+ fprintf(stderr, "Command \"%s\" is unknown, try \"tc filter help\".\n", *argv);
+ return -1;
+}
+
diff --git a/tc/tc_monitor.c b/tc/tc_monitor.c
new file mode 100644
index 0000000..bf58744
--- /dev/null
+++ b/tc/tc_monitor.c
@@ -0,0 +1,110 @@
+/*
+ * tc_monitor.c "tc monitor".
+ *
+ * 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: Jamal Hadi Salim
+ *
+ */
+
+#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 <time.h>
+#include "rt_names.h"
+#include "utils.h"
+#include "tc_util.h"
+#include "tc_common.h"
+
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: tc monitor\n");
+ exit(-1);
+}
+
+
+int accept_tcmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+ FILE *fp = (FILE*)arg;
+
+ if (n->nlmsg_type == RTM_NEWTFILTER || n->nlmsg_type == RTM_DELTFILTER) {
+ print_filter(who, n, arg);
+ return 0;
+ }
+ if (n->nlmsg_type == RTM_NEWTCLASS || n->nlmsg_type == RTM_DELTCLASS) {
+ print_class(who, n, arg);
+ return 0;
+ }
+ if (n->nlmsg_type == RTM_NEWQDISC || n->nlmsg_type == RTM_DELQDISC) {
+ print_qdisc(who, n, arg);
+ return 0;
+ }
+ if (n->nlmsg_type == RTM_GETACTION || n->nlmsg_type == RTM_NEWACTION ||
+ n->nlmsg_type == RTM_DELACTION) {
+ print_action(who, n, arg);
+ return 0;
+ }
+ if (n->nlmsg_type != NLMSG_ERROR && n->nlmsg_type != NLMSG_NOOP &&
+ n->nlmsg_type != NLMSG_DONE) {
+ fprintf(fp, "Unknown message: length %08d type %08x flags %08x\n",
+ n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+ }
+ return 0;
+}
+
+int do_tcmonitor(int argc, char **argv)
+{
+ struct rtnl_handle rth;
+ char *file = NULL;
+ unsigned groups = nl_mgrp(RTNLGRP_TC);
+
+ while (argc > 0) {
+ if (matches(*argv, "file") == 0) {
+ NEXT_ARG();
+ file = *argv;
+ } else {
+ if (matches(*argv, "help") == 0) {
+ usage();
+ } else {
+ fprintf(stderr, "Argument \"%s\" is unknown, try \"tc monitor help\".\n", *argv);
+ exit(-1);
+ }
+ }
+ argc--; argv++;
+ }
+
+ if (file) {
+ FILE *fp;
+ fp = fopen(file, "r");
+ if (fp == NULL) {
+ perror("Cannot fopen");
+ exit(-1);
+ }
+ return rtnl_from_file(fp, accept_tcmsg, (void*)stdout);
+ }
+
+ if (rtnl_open(&rth, groups) < 0)
+ exit(1);
+
+ ll_init_map(&rth);
+
+ if (rtnl_listen(&rth, accept_tcmsg, (void*)stdout) < 0) {
+ rtnl_close(&rth);
+ exit(2);
+ }
+
+ rtnl_close(&rth);
+ exit(0);
+}
diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c
new file mode 100644
index 0000000..c7f2988
--- /dev/null
+++ b/tc/tc_qdisc.c
@@ -0,0 +1,361 @@
+/*
+ * tc_qdisc.c "tc qdisc".
+ *
+ * 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>
+ * J Hadi Salim: Extension to ingress
+ */
+
+#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 <malloc.h>
+
+#include "utils.h"
+#include "tc_util.h"
+#include "tc_common.h"
+
+static int usage(void);
+
+static int usage(void)
+{
+ fprintf(stderr, "Usage: tc qdisc [ add | del | replace | change | show ] dev STRING\n");
+ fprintf(stderr, " [ handle QHANDLE ] [ root | ingress | parent CLASSID ]\n");
+ fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n");
+ fprintf(stderr, " [ stab [ help | STAB_OPTIONS] ]\n");
+ fprintf(stderr, " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " tc qdisc show [ dev STRING ] [ingress]\n");
+ fprintf(stderr, "Where:\n");
+ fprintf(stderr, "QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n");
+ fprintf(stderr, "OPTIONS := ... try tc qdisc add <desired QDISC_KIND> help\n");
+ fprintf(stderr, "STAB_OPTIONS := ... try tc qdisc add stab help\n");
+ return -1;
+}
+
+int tc_qdisc_modify(int cmd, unsigned flags, int argc, char **argv)
+{
+ struct qdisc_util *q = NULL;
+ struct tc_estimator est;
+ struct {
+ struct tc_sizespec szopts;
+ __u16 *data;
+ } stab;
+ char d[16];
+ char k[16];
+ struct {
+ struct nlmsghdr n;
+ struct tcmsg t;
+ char buf[TCA_BUF_MAX];
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ memset(&stab, 0, sizeof(stab));
+ 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;
+
+ 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, "handle") == 0) {
+ __u32 handle;
+ if (req.t.tcm_handle)
+ duparg("handle", *argv);
+ NEXT_ARG();
+ if (get_qdisc_handle(&handle, *argv))
+ invarg(*argv, "invalid qdisc ID");
+ req.t.tcm_handle = handle;
+ } 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;
+#ifdef TC_H_INGRESS
+ } else if (strcmp(*argv, "ingress") == 0) {
+ if (req.t.tcm_parent) {
+ fprintf(stderr, "Error: \"ingress\" is a duplicate parent ID\n");
+ return -1;
+ }
+ req.t.tcm_parent = TC_H_INGRESS;
+ strncpy(k, "ingress", sizeof(k)-1);
+ q = get_qdisc_kind(k);
+ req.t.tcm_handle = 0xffff0000;
+
+ argc--; argv++;
+ break;
+#endif
+ } 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");
+ req.t.tcm_parent = handle;
+ } else if (matches(*argv, "estimator") == 0) {
+ if (parse_estimator(&argc, &argv, &est))
+ return -1;
+ } else if (matches(*argv, "stab") == 0) {
+ if (parse_size_table(&argc, &argv, &stab.szopts) < 0)
+ return -1;
+ continue;
+ } 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_qopt) {
+ fprintf(stderr, "qdisc '%s' does not support option parsing\n", k);
+ return -1;
+ }
+ if (q->parse_qopt(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 qdisc help\".\n", *argv);
+ return -1;
+ }
+ }
+
+ if (check_size_table_opts(&stab.szopts)) {
+ struct rtattr *tail;
+
+ if (tc_calc_size_table(&stab.szopts, &stab.data) < 0) {
+ fprintf(stderr, "failed to calculate size table.\n");
+ return -1;
+ }
+
+ tail = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, sizeof(req), TCA_STAB, NULL, 0);
+ addattr_l(&req.n, sizeof(req), TCA_STAB_BASE, &stab.szopts,
+ sizeof(stab.szopts));
+ if (stab.data)
+ addattr_l(&req.n, sizeof(req), TCA_STAB_DATA, stab.data,
+ stab.szopts.tsize * sizeof(__u16));
+ tail->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail;
+ if (stab.data)
+ free(stab.data);
+ }
+
+ if (d[0]) {
+ int idx;
+
+ ll_init_map(&rth);
+
+ if ((idx = ll_name_to_index(d)) == 0) {
+ fprintf(stderr, "Cannot find device \"%s\"\n", d);
+ return 1;
+ }
+ req.t.tcm_ifindex = idx;
+ }
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+ return 2;
+
+ return 0;
+}
+
+static int filter_ifindex;
+
+int print_qdisc(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_NEWQDISC && n->nlmsg_type != RTM_DELQDISC) {
+ fprintf(stderr, "Not a qdisc\n");
+ return 0;
+ }
+ len -= NLMSG_LENGTH(sizeof(*t));
+ if (len < 0) {
+ fprintf(stderr, "Wrong len %d\n", len);
+ return -1;
+ }
+
+ if (filter_ifindex && filter_ifindex != t->tcm_ifindex)
+ return 0;
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len);
+
+ if (tb[TCA_KIND] == NULL) {
+ fprintf(stderr, "print_qdisc: NULL kind\n");
+ return -1;
+ }
+
+ if (n->nlmsg_type == RTM_DELQDISC)
+ fprintf(fp, "deleted ");
+
+ fprintf(fp, "qdisc %s %x: ", (char*)RTA_DATA(tb[TCA_KIND]), t->tcm_handle>>16);
+ 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 (t->tcm_parent) {
+ print_tc_classid(abuf, sizeof(abuf), t->tcm_parent);
+ fprintf(fp, "parent %s ", abuf);
+ }
+ if (t->tcm_info != 1) {
+ fprintf(fp, "refcnt %d ", t->tcm_info);
+ }
+ /* 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]);
+ else
+ fprintf(fp, "[cannot parse qdisc parameters]");
+ }
+ fprintf(fp, "\n");
+ if (show_details && tb[TCA_STAB]) {
+ print_size_table(fp, " ", tb[TCA_STAB]);
+ fprintf(fp, "\n");
+ }
+ if (show_stats) {
+ struct rtattr *xstats = NULL;
+
+ if (tb[TCA_STATS] || tb[TCA_STATS2] || tb[TCA_XSTATS]) {
+ print_tcstats_attr(fp, tb, " ", &xstats);
+ fprintf(fp, "\n");
+ }
+
+ if (q && xstats && q->print_xstats) {
+ q->print_xstats(q, fp, xstats);
+ fprintf(fp, "\n");
+ }
+ }
+ fflush(fp);
+ return 0;
+}
+
+
+int tc_qdisc_list(int argc, char **argv)
+{
+ struct tcmsg t;
+ char d[16];
+
+ memset(&t, 0, sizeof(t));
+ t.tcm_family = AF_UNSPEC;
+ memset(&d, 0, sizeof(d));
+
+ while (argc > 0) {
+ if (strcmp(*argv, "dev") == 0) {
+ NEXT_ARG();
+ strncpy(d, *argv, sizeof(d)-1);
+#ifdef TC_H_INGRESS
+ } else if (strcmp(*argv, "ingress") == 0) {
+ if (t.tcm_parent) {
+ fprintf(stderr, "Duplicate parent ID\n");
+ usage();
+ }
+ t.tcm_parent = TC_H_INGRESS;
+#endif
+ } else if (matches(*argv, "help") == 0) {
+ usage();
+ } else {
+ fprintf(stderr, "What is \"%s\"? Try \"tc qdisc 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_GETQDISC, &t, sizeof(t)) < 0) {
+ perror("Cannot send dump request");
+ return 1;
+ }
+
+ if (rtnl_dump_filter(&rth, print_qdisc, stdout, NULL, NULL) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+int do_qdisc(int argc, char **argv)
+{
+ if (argc < 1)
+ return tc_qdisc_list(0, NULL);
+ if (matches(*argv, "add") == 0)
+ return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1);
+ if (matches(*argv, "change") == 0)
+ return tc_qdisc_modify(RTM_NEWQDISC, 0, argc-1, argv+1);
+ if (matches(*argv, "replace") == 0)
+ return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
+ if (matches(*argv, "link") == 0)
+ return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_REPLACE, argc-1, argv+1);
+ if (matches(*argv, "delete") == 0)
+ return tc_qdisc_modify(RTM_DELQDISC, 0, argc-1, argv+1);
+#if 0
+ if (matches(*argv, "get") == 0)
+ return tc_qdisc_get(RTM_GETQDISC, 0, argc-1, argv+1);
+#endif
+ if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
+ || matches(*argv, "lst") == 0)
+ return tc_qdisc_list(argc-1, argv+1);
+ if (matches(*argv, "help") == 0) {
+ usage();
+ return 0;
+ }
+ fprintf(stderr, "Command \"%s\" is unknown, try \"tc qdisc help\".\n", *argv);
+ return -1;
+}
diff --git a/tc/tc_red.c b/tc/tc_red.c
new file mode 100644
index 0000000..8f9bde0
--- /dev/null
+++ b/tc/tc_red.c
@@ -0,0 +1,97 @@
+/*
+ * tc_red.c RED maintanance routines.
+ *
+ * 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 <math.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "tc_core.h"
+#include "tc_red.h"
+
+/*
+ Plog = log(prob/(qmax - qmin))
+ */
+int tc_red_eval_P(unsigned qmin, unsigned qmax, double prob)
+{
+ int i = qmax - qmin;
+
+ if (i <= 0)
+ return -1;
+
+ prob /= i;
+
+ for (i=0; i<32; i++) {
+ if (prob > 1.0)
+ break;
+ prob *= 2;
+ }
+ if (i>=32)
+ return -1;
+ return i;
+}
+
+/*
+ burst + 1 - qmin/avpkt < (1-(1-W)^burst)/W
+ */
+
+int tc_red_eval_ewma(unsigned qmin, unsigned burst, unsigned avpkt)
+{
+ int wlog = 1;
+ double W = 0.5;
+ double a = (double)burst + 1 - (double)qmin/avpkt;
+
+ if (a < 1.0)
+ return -1;
+ for (wlog=1; wlog<32; wlog++, W /= 2) {
+ if (a <= (1 - pow(1-W, burst))/W)
+ return wlog;
+ }
+ return -1;
+}
+
+/*
+ Stab[t>>Scell_log] = -log(1-W) * t/xmit_time
+ */
+
+int tc_red_eval_idle_damping(int Wlog, unsigned avpkt, unsigned bps, __u8 *sbuf)
+{
+ double xmit_time = tc_calc_xmittime(bps, avpkt);
+ double lW = -log(1.0 - 1.0/(1<<Wlog))/xmit_time;
+ double maxtime = 31/lW;
+ int clog;
+ int i;
+ double tmp;
+
+ tmp = maxtime;
+ for (clog=0; clog<32; clog++) {
+ if (maxtime/(1<<clog) < 512)
+ break;
+ }
+ if (clog >= 32)
+ return -1;
+
+ sbuf[0] = 0;
+ for (i=1; i<255; i++) {
+ sbuf[i] = (i<<clog)*lW;
+ if (sbuf[i] > 31)
+ sbuf[i] = 31;
+ }
+ sbuf[255] = 31;
+ return clog;
+}
diff --git a/tc/tc_red.h b/tc/tc_red.h
new file mode 100644
index 0000000..6f6b09e
--- /dev/null
+++ b/tc/tc_red.h
@@ -0,0 +1,8 @@
+#ifndef _TC_RED_H_
+#define _TC_RED_H_ 1
+
+extern int tc_red_eval_P(unsigned qmin, unsigned qmax, double prob);
+extern int tc_red_eval_ewma(unsigned qmin, unsigned burst, unsigned avpkt);
+extern int tc_red_eval_idle_damping(int wlog, unsigned avpkt, unsigned bandwidth, __u8 *sbuf);
+
+#endif
diff --git a/tc/tc_stab.c b/tc/tc_stab.c
new file mode 100644
index 0000000..47b4e5e
--- /dev/null
+++ b/tc/tc_stab.c
@@ -0,0 +1,160 @@
+/*
+ * tc_stab.c "tc qdisc ... stab *".
+ *
+ * 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: Jussi Kivilinna, <jussi.kivilinna@mbnet.fi>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <math.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <malloc.h>
+
+#include "utils.h"
+#include "tc_util.h"
+#include "tc_core.h"
+#include "tc_common.h"
+
+static void stab_help(void)
+{
+ fprintf(stderr,
+ "Usage: ... stab [ mtu BYTES ] [ tsize SLOTS ] [ mpu BYTES ] \n"
+ " [ overhead BYTES ] [ linklayer TYPE ] ...\n"
+ " mtu : max packet size we create rate map for {2047}\n"
+ " tsize : how many slots should size table have {512}\n"
+ " mpu : minimum packet size used in rate computations\n"
+ " overhead : per-packet size overhead used in rate computations\n"
+ " linklayer : adapting to a linklayer e.g. atm\n"
+ "Example: ... stab overhead 20 linklayer atm\n");
+
+ return;
+}
+
+int check_size_table_opts(struct tc_sizespec *s)
+{
+ return s->linklayer >= LINKLAYER_ETHERNET || s->mpu != 0 ||
+ s->overhead != 0;
+}
+
+int parse_size_table(int *argcp, char ***argvp, struct tc_sizespec *sp)
+{
+ char **argv = *argvp;
+ int argc = *argcp;
+ struct tc_sizespec s;
+
+ memset(&s, 0, sizeof(s));
+
+ NEXT_ARG();
+ if (matches(*argv, "help") == 0) {
+ stab_help();
+ return -1;
+ }
+ while (argc > 0) {
+ if (matches(*argv, "mtu") == 0) {
+ NEXT_ARG();
+ if (s.mtu)
+ duparg("mtu", *argv);
+ if (get_u32(&s.mtu, *argv, 10)) {
+ invarg("mtu", "invalid mtu");
+ return -1;
+ }
+ } else if (matches(*argv, "mpu") == 0) {
+ NEXT_ARG();
+ if (s.mpu)
+ duparg("mpu", *argv);
+ if (get_u32(&s.mpu, *argv, 10)) {
+ invarg("mpu", "invalid mpu");
+ return -1;
+ }
+ } else if (matches(*argv, "overhead") == 0) {
+ NEXT_ARG();
+ if (s.overhead)
+ duparg("overhead", *argv);
+ if (get_integer(&s.overhead, *argv, 10)) {
+ invarg("overhead", "invalid overhead");
+ return -1;
+ }
+ } else if (matches(*argv, "tsize") == 0) {
+ NEXT_ARG();
+ if (s.tsize)
+ duparg("tsize", *argv);
+ if (get_u32(&s.tsize, *argv, 10)) {
+ invarg("tsize", "invalid table size");
+ return -1;
+ }
+ } else if (matches(*argv, "linklayer") == 0) {
+ NEXT_ARG();
+ if (s.linklayer != LINKLAYER_UNSPEC)
+ duparg("linklayer", *argv);
+ if (get_linklayer(&s.linklayer, *argv)) {
+ invarg("linklayer", "invalid linklayer");
+ return -1;
+ }
+ } else
+ break;
+ argc--; argv++;
+ }
+
+ if (!check_size_table_opts(&s))
+ return -1;
+
+ *sp = s;
+ *argvp = argv;
+ *argcp = argc;
+ return 0;
+}
+
+void print_size_table(FILE *fp, const char *prefix, struct rtattr *rta)
+{
+ struct rtattr *tb[TCA_STAB_MAX + 1];
+ SPRINT_BUF(b1);
+
+ parse_rtattr_nested(tb, TCA_STAB_MAX, rta);
+
+ if (tb[TCA_STAB_BASE]) {
+ struct tc_sizespec s = {0};
+ memcpy(&s, RTA_DATA(tb[TCA_STAB_BASE]),
+ MIN(RTA_PAYLOAD(tb[TCA_STAB_BASE]), sizeof(s)));
+
+ fprintf(fp, "%s", prefix);
+ if (s.linklayer)
+ fprintf(fp, "linklayer %s ",
+ sprint_linklayer(s.linklayer, b1));
+ if (s.overhead)
+ fprintf(fp, "overhead %d ", s.overhead);
+ if (s.mpu)
+ fprintf(fp, "mpu %u ", s.mpu);
+ if (s.mtu)
+ fprintf(fp, "mtu %u ", s.mtu);
+ if (s.tsize)
+ fprintf(fp, "tsize %u ", s.tsize);
+ }
+
+#if 0
+ if (tb[TCA_STAB_DATA]) {
+ unsigned i, j, dlen;
+ __u16 *data = RTA_DATA(tb[TCA_STAB_DATA]);
+ dlen = RTA_PAYLOAD(tb[TCA_STAB_DATA]) / sizeof(__u16);
+
+ fprintf(fp, "\n%sstab data:", prefix);
+ for (i = 0; i < dlen/12; i++) {
+ fprintf(fp, "\n%s %3u:", prefix, i * 12);
+ for (j = 0; i * 12 + j < dlen; j++)
+ fprintf(fp, " %05x", data[i * 12 + j]);
+ }
+ }
+#endif
+}
+
diff --git a/tc/tc_util.c b/tc/tc_util.c
new file mode 100644
index 0000000..fe2c7eb
--- /dev/null
+++ b/tc/tc_util.c
@@ -0,0 +1,580 @@
+/*
+ * tc_util.c Misc TC utility functions.
+ *
+ * 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"
+
+#ifndef LIBDIR
+#define LIBDIR "/usr/lib/"
+#endif
+
+const char *get_tc_lib(void)
+{
+ const char *lib_dir;
+
+ lib_dir = getenv("TC_LIB_DIR");
+ if (!lib_dir)
+ lib_dir = LIBDIR "/tc/";
+
+ return lib_dir;
+}
+
+int get_qdisc_handle(__u32 *h, const char *str)
+{
+ __u32 maj;
+ char *p;
+
+ maj = TC_H_UNSPEC;
+ if (strcmp(str, "none") == 0)
+ goto ok;
+ maj = strtoul(str, &p, 16);
+ if (p == str)
+ return -1;
+ maj <<= 16;
+ if (*p != ':' && *p!=0)
+ return -1;
+ok:
+ *h = maj;
+ return 0;
+}
+
+int get_tc_classid(__u32 *h, const char *str)
+{
+ __u32 maj, min;
+ char *p;
+
+ maj = TC_H_ROOT;
+ if (strcmp(str, "root") == 0)
+ goto ok;
+ maj = TC_H_UNSPEC;
+ if (strcmp(str, "none") == 0)
+ goto ok;
+ maj = strtoul(str, &p, 16);
+ if (p == str) {
+ maj = 0;
+ if (*p != ':')
+ return -1;
+ }
+ if (*p == ':') {
+ if (maj >= (1<<16))
+ return -1;
+ maj <<= 16;
+ str = p+1;
+ min = strtoul(str, &p, 16);
+ if (*p != 0)
+ return -1;
+ if (min >= (1<<16))
+ return -1;
+ maj |= min;
+ } else if (*p != 0)
+ return -1;
+
+ok:
+ *h = maj;
+ return 0;
+}
+
+int print_tc_classid(char *buf, int len, __u32 h)
+{
+ if (h == TC_H_ROOT)
+ sprintf(buf, "root");
+ else if (h == TC_H_UNSPEC)
+ snprintf(buf, len, "none");
+ else if (TC_H_MAJ(h) == 0)
+ snprintf(buf, len, ":%x", TC_H_MIN(h));
+ else if (TC_H_MIN(h) == 0)
+ snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16);
+ else
+ snprintf(buf, len, "%x:%x", TC_H_MAJ(h)>>16, TC_H_MIN(h));
+ return 0;
+}
+
+char * sprint_tc_classid(__u32 h, char *buf)
+{
+ if (print_tc_classid(buf, SPRINT_BSIZE-1, h))
+ strcpy(buf, "???");
+ return buf;
+}
+
+/* See http://physics.nist.gov/cuu/Units/binary.html */
+static const struct rate_suffix {
+ const char *name;
+ double scale;
+} suffixes[] = {
+ { "bit", 1. },
+ { "Kibit", 1024. },
+ { "kbit", 1000. },
+ { "mibit", 1024.*1024. },
+ { "mbit", 1000000. },
+ { "gibit", 1024.*1024.*1024. },
+ { "gbit", 1000000000. },
+ { "tibit", 1024.*1024.*1024.*1024. },
+ { "tbit", 1000000000000. },
+ { "Bps", 8. },
+ { "KiBps", 8.*1024. },
+ { "KBps", 8000. },
+ { "MiBps", 8.*1024*1024. },
+ { "MBps", 8000000. },
+ { "GiBps", 8.*1024.*1024.*1024. },
+ { "GBps", 8000000000. },
+ { "TiBps", 8.*1024.*1024.*1024.*1024. },
+ { "TBps", 8000000000000. },
+ { NULL }
+};
+
+
+int get_rate(unsigned *rate, const char *str)
+{
+ char *p;
+ double bps = strtod(str, &p);
+ const struct rate_suffix *s;
+
+ if (p == str)
+ return -1;
+
+ if (*p == '\0') {
+ *rate = bps / 8.; /* assume bytes/sec */
+ return 0;
+ }
+
+ for (s = suffixes; s->name; ++s) {
+ if (strcasecmp(s->name, p) == 0) {
+ *rate = (bps * s->scale) / 8.;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+int get_rate_and_cell(unsigned *rate, int *cell_log, char *str)
+{
+ char * slash = strchr(str, '/');
+
+ if (slash)
+ *slash = 0;
+
+ if (get_rate(rate, str))
+ return -1;
+
+ if (slash) {
+ int cell;
+ int i;
+
+ if (get_integer(&cell, slash+1, 0))
+ return -1;
+ *slash = '/';
+
+ for (i=0; i<32; i++) {
+ if ((1<<i) == cell) {
+ *cell_log = i;
+ return 0;
+ }
+ }
+ return -1;
+ }
+ return 0;
+}
+
+void print_rate(char *buf, int len, __u32 rate)
+{
+ double tmp = (double)rate*8;
+ extern int use_iec;
+
+ if (use_iec) {
+ if (tmp >= 1000.0*1024.0*1024.0)
+ snprintf(buf, len, "%.0fMibit", tmp/1024.0*1024.0);
+ else if (tmp >= 1000.0*1024)
+ snprintf(buf, len, "%.0fKibit", tmp/1024);
+ else
+ snprintf(buf, len, "%.0fbit", tmp);
+ } else {
+ if (tmp >= 1000.0*1000000.0)
+ snprintf(buf, len, "%.0fMbit", tmp/1000000.0);
+ else if (tmp >= 1000.0 * 1000.0)
+ snprintf(buf, len, "%.0fKbit", tmp/1000.0);
+ else
+ snprintf(buf, len, "%.0fbit", tmp);
+ }
+}
+
+char * sprint_rate(__u32 rate, char *buf)
+{
+ print_rate(buf, SPRINT_BSIZE-1, rate);
+ return buf;
+}
+
+int get_time(unsigned *time, const char *str)
+{
+ double t;
+ char *p;
+
+ t = strtod(str, &p);
+ if (p == str)
+ return -1;
+
+ if (*p) {
+ if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 ||
+ strcasecmp(p, "secs")==0)
+ t *= TIME_UNITS_PER_SEC;
+ else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 ||
+ strcasecmp(p, "msecs") == 0)
+ t *= TIME_UNITS_PER_SEC/1000;
+ else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 ||
+ strcasecmp(p, "usecs") == 0)
+ t *= TIME_UNITS_PER_SEC/1000000;
+ else
+ return -1;
+ }
+
+ *time = t;
+ return 0;
+}
+
+
+void print_time(char *buf, int len, __u32 time)
+{
+ double tmp = time;
+
+ if (tmp >= TIME_UNITS_PER_SEC)
+ snprintf(buf, len, "%.1fs", tmp/TIME_UNITS_PER_SEC);
+ else if (tmp >= TIME_UNITS_PER_SEC/1000)
+ snprintf(buf, len, "%.1fms", tmp/(TIME_UNITS_PER_SEC/1000));
+ else
+ snprintf(buf, len, "%uus", time);
+}
+
+char * sprint_time(__u32 time, char *buf)
+{
+ print_time(buf, SPRINT_BSIZE-1, time);
+ return buf;
+}
+
+char * sprint_ticks(__u32 ticks, char *buf)
+{
+ return sprint_time(tc_core_tick2time(ticks), buf);
+}
+
+int get_size(unsigned *size, const char *str)
+{
+ double sz;
+ char *p;
+
+ sz = strtod(str, &p);
+ if (p == str)
+ return -1;
+
+ if (*p) {
+ if (strcasecmp(p, "kb") == 0 || strcasecmp(p, "k")==0)
+ sz *= 1024;
+ else if (strcasecmp(p, "gb") == 0 || strcasecmp(p, "g")==0)
+ sz *= 1024*1024*1024;
+ else if (strcasecmp(p, "gbit") == 0)
+ sz *= 1024*1024*1024/8;
+ else if (strcasecmp(p, "mb") == 0 || strcasecmp(p, "m")==0)
+ sz *= 1024*1024;
+ else if (strcasecmp(p, "mbit") == 0)
+ sz *= 1024*1024/8;
+ else if (strcasecmp(p, "kbit") == 0)
+ sz *= 1024/8;
+ else if (strcasecmp(p, "b") != 0)
+ return -1;
+ }
+
+ *size = sz;
+ return 0;
+}
+
+int get_size_and_cell(unsigned *size, int *cell_log, char *str)
+{
+ char * slash = strchr(str, '/');
+
+ if (slash)
+ *slash = 0;
+
+ if (get_size(size, str))
+ return -1;
+
+ if (slash) {
+ int cell;
+ int i;
+
+ if (get_integer(&cell, slash+1, 0))
+ return -1;
+ *slash = '/';
+
+ for (i=0; i<32; i++) {
+ if ((1<<i) == cell) {
+ *cell_log = i;
+ return 0;
+ }
+ }
+ return -1;
+ }
+ return 0;
+}
+
+void print_size(char *buf, int len, __u32 sz)
+{
+ double tmp = sz;
+
+ if (sz >= 1024*1024 && fabs(1024*1024*rint(tmp/(1024*1024)) - sz) < 1024)
+ snprintf(buf, len, "%gMb", rint(tmp/(1024*1024)));
+ else if (sz >= 1024 && fabs(1024*rint(tmp/1024) - sz) < 16)
+ snprintf(buf, len, "%gKb", rint(tmp/1024));
+ else
+ snprintf(buf, len, "%ub", sz);
+}
+
+char * sprint_size(__u32 size, char *buf)
+{
+ print_size(buf, SPRINT_BSIZE-1, size);
+ return buf;
+}
+
+static const double max_percent_value = 0xffffffff;
+
+int get_percent(__u32 *percent, const char *str)
+{
+ char *p;
+ double per = strtod(str, &p) / 100.;
+
+ if (per > 1. || per < 0)
+ return -1;
+ if (*p && strcmp(p, "%"))
+ return -1;
+
+ *percent = (unsigned) rint(per * max_percent_value);
+ return 0;
+}
+
+void print_percent(char *buf, int len, __u32 per)
+{
+ snprintf(buf, len, "%g%%", 100. * (double) per / max_percent_value);
+}
+
+char * sprint_percent(__u32 per, char *buf)
+{
+ print_percent(buf, SPRINT_BSIZE-1, per);
+ return buf;
+}
+
+void print_qdisc_handle(char *buf, int len, __u32 h)
+{
+ snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16);
+}
+
+char * sprint_qdisc_handle(__u32 h, char *buf)
+{
+ print_qdisc_handle(buf, SPRINT_BSIZE-1, h);
+ 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;
+}
+
+int get_linklayer(unsigned *val, const char *arg)
+{
+ int res;
+
+ if (matches(arg, "ethernet") == 0)
+ res = LINKLAYER_ETHERNET;
+ else if (matches(arg, "atm") == 0)
+ res = LINKLAYER_ATM;
+ else if (matches(arg, "adsl") == 0)
+ res = LINKLAYER_ATM;
+ else
+ return -1; /* Indicate error */
+
+ *val = res;
+ return 0;
+}
+
+void print_linklayer(char *buf, int len, unsigned linklayer)
+{
+ switch (linklayer) {
+ case LINKLAYER_UNSPEC:
+ snprintf(buf, len, "%s", "unspec");
+ return;
+ case LINKLAYER_ETHERNET:
+ snprintf(buf, len, "%s", "ethernet");
+ return;
+ case LINKLAYER_ATM:
+ snprintf(buf, len, "%s", "atm");
+ return;
+ default:
+ snprintf(buf, len, "%s", "unknown");
+ return;
+ }
+}
+
+char *sprint_linklayer(unsigned linklayer, char *buf)
+{
+ print_linklayer(buf, SPRINT_BSIZE-1, linklayer);
+ return buf;
+}
+
+void print_tm(FILE * f, const struct tcf_t *tm)
+{
+ int hz = get_user_hz();
+ if (tm->install != 0)
+ fprintf(f, " installed %u sec", (unsigned)(tm->install/hz));
+ if (tm->lastuse != 0)
+ fprintf(f, " used %u sec", (unsigned)(tm->lastuse/hz));
+ if (tm->expires != 0)
+ fprintf(f, " expires %u sec", (unsigned)(tm->expires/hz));
+}
+
+void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtattr **xstats)
+{
+ SPRINT_BUF(b1);
+ struct rtattr *tbs[TCA_STATS_MAX + 1];
+
+ parse_rtattr_nested(tbs, TCA_STATS_MAX, rta);
+
+ if (tbs[TCA_STATS_BASIC]) {
+ struct gnet_stats_basic bs = {0};
+ memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]), sizeof(bs)));
+ fprintf(fp, "%sSent %llu bytes %u pkt",
+ prefix, (unsigned long long) bs.bytes, bs.packets);
+ }
+
+ if (tbs[TCA_STATS_QUEUE]) {
+ struct gnet_stats_queue q = {0};
+ memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q)));
+ fprintf(fp, " (dropped %u, overlimits %u requeues %u) ",
+ q.drops, q.overlimits, q.requeues);
+ }
+
+ if (tbs[TCA_STATS_RATE_EST]) {
+ struct gnet_stats_rate_est re = {0};
+ memcpy(&re, RTA_DATA(tbs[TCA_STATS_RATE_EST]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_RATE_EST]), sizeof(re)));
+ fprintf(fp, "\n%srate %s %upps ",
+ prefix, sprint_rate(re.bps, b1), re.pps);
+ }
+
+ if (tbs[TCA_STATS_QUEUE]) {
+ struct gnet_stats_queue q = {0};
+ memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q)));
+ if (!tbs[TCA_STATS_RATE_EST])
+ fprintf(fp, "\n%s", prefix);
+ fprintf(fp, "backlog %s %up requeues %u ",
+ sprint_size(q.backlog, b1), q.qlen, q.requeues);
+ }
+
+ if (xstats)
+ *xstats = tbs[TCA_STATS_APP] ? : NULL;
+}
+
+void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats)
+{
+ SPRINT_BUF(b1);
+
+ if (tb[TCA_STATS2]) {
+ print_tcstats2_attr(fp, tb[TCA_STATS2], prefix, xstats);
+ if (xstats && NULL == *xstats)
+ goto compat_xstats;
+ return;
+ }
+ /* backward compatibility */
+ if (tb[TCA_STATS]) {
+ struct tc_stats st;
+
+ /* handle case where kernel returns more/less than we know about */
+ memset(&st, 0, sizeof(st));
+ memcpy(&st, RTA_DATA(tb[TCA_STATS]), MIN(RTA_PAYLOAD(tb[TCA_STATS]), sizeof(st)));
+
+ fprintf(fp, "%sSent %llu bytes %u pkts (dropped %u, overlimits %u) ",
+ prefix, (unsigned long long)st.bytes, st.packets, st.drops,
+ st.overlimits);
+
+ if (st.bps || st.pps || st.qlen || st.backlog) {
+ fprintf(fp, "\n%s", prefix);
+ 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);
+ }
+ 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);
+ }
+ }
+ }
+
+compat_xstats:
+ if (tb[TCA_XSTATS] && xstats)
+ *xstats = tb[TCA_XSTATS];
+}
+
diff --git a/tc/tc_util.h b/tc/tc_util.h
new file mode 100644
index 0000000..d84b09a
--- /dev/null
+++ b/tc/tc_util.h
@@ -0,0 +1,104 @@
+#ifndef _TC_UTIL_H_
+#define _TC_UTIL_H_ 1
+
+#define MAX_MSG 16384
+#include <linux/pkt_sched.h>
+#include <linux/pkt_cls.h>
+#include <linux/gen_stats.h>
+#include "tc_core.h"
+
+/* This is the deprecated multiqueue interface */
+#ifndef TCA_PRIO_MAX
+enum
+{
+ TCA_PRIO_UNSPEC,
+ TCA_PRIO_MQ,
+ __TCA_PRIO_MAX
+};
+
+#define TCA_PRIO_MAX (__TCA_PRIO_MAX - 1)
+#endif
+
+struct qdisc_util
+{
+ struct qdisc_util *next;
+ const char *id;
+ 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_xstats)(struct qdisc_util *qu, FILE *f, struct rtattr *xstats);
+
+ int (*parse_copt)(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n);
+ int (*print_copt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt);
+};
+
+extern __u16 f_proto;
+struct filter_util
+{
+ struct filter_util *next;
+ char id[16];
+ int (*parse_fopt)(struct filter_util *qu, char *fhandle, int argc,
+ char **argv, struct nlmsghdr *n);
+ 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 const char *get_tc_lib(void);
+
+extern struct qdisc_util *get_qdisc_kind(const char *str);
+extern struct filter_util *get_filter_kind(const char *str);
+
+extern int get_qdisc_handle(__u32 *h, const char *str);
+extern int get_rate(unsigned *rate, const char *str);
+extern int get_percent(unsigned *percent, const char *str);
+extern int get_size(unsigned *size, const char *str);
+extern int get_size_and_cell(unsigned *size, int *cell_log, char *str);
+extern int get_time(unsigned *time, const char *str);
+extern int get_linklayer(unsigned *val, const char *arg);
+
+extern void print_rate(char *buf, int len, __u32 rate);
+extern void print_size(char *buf, int len, __u32 size);
+extern void print_percent(char *buf, int len, __u32 percent);
+extern void print_qdisc_handle(char *buf, int len, __u32 h);
+extern void print_time(char *buf, int len, __u32 time);
+extern void print_linklayer(char *buf, int len, unsigned linklayer);
+extern char * sprint_rate(__u32 rate, char *buf);
+extern char * sprint_size(__u32 size, char *buf);
+extern char * sprint_qdisc_handle(__u32 h, char *buf);
+extern char * sprint_tc_classid(__u32 h, char *buf);
+extern char * sprint_time(__u32 time, char *buf);
+extern char * sprint_ticks(__u32 ticks, char *buf);
+extern char * sprint_percent(__u32 percent, char *buf);
+extern char * sprint_linklayer(unsigned linklayer, char *buf);
+
+extern void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats);
+extern void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtattr **xstats);
+
+extern int get_tc_classid(__u32 *h, const char *str);
+extern int print_tc_classid(char *buf, int len, __u32 h);
+extern char * sprint_tc_classid(__u32 h, char *buf);
+
+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, const struct rtattr *tb);
+extern int tc_print_ipt(FILE *f, const struct rtattr *tb);
+extern int parse_action(int *, char ***, int, struct nlmsghdr *);
+extern void print_tm(FILE *f, const struct tcf_t *tm);
+
+#endif