summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/pkt_cls.h78
-rw-r--r--include/netlink-types.h32
-rw-r--r--include/netlink/attr.h10
-rw-r--r--include/netlink/list.h5
-rw-r--r--include/netlink/route/classifier-modules.h12
-rw-r--r--include/netlink/route/classifier.h6
-rw-r--r--include/netlink/route/cls/basic.h33
-rw-r--r--include/netlink/route/cls/cgroup.h31
-rw-r--r--include/netlink/route/cls/ematch.h73
-rw-r--r--include/netlink/route/cls/ematch/cmp.h31
-rw-r--r--lib/Makefile1
-rw-r--r--lib/route/cls.c (renamed from lib/route/classifier.c)33
-rw-r--r--lib/route/cls/basic.c211
-rw-r--r--lib/route/cls/cgroup.c141
-rw-r--r--lib/route/cls/ematch.c410
-rw-r--r--lib/route/cls/ematch/cmp.c116
-rw-r--r--lib/route/cls/ematch/container.c39
-rw-r--r--lib/route/cls/fw.c96
-rw-r--r--lib/route/cls/u32.c127
-rw-r--r--lib/route/cls_obj.c59
-rw-r--r--src/Makefile4
-rw-r--r--src/cls/basic.c90
-rw-r--r--src/cls/cgroup.c78
-rw-r--r--src/cls/utils.c105
-rw-r--r--src/cls/utils.h51
-rw-r--r--src/nl-cls-add.c117
-rw-r--r--src/nl-cls-delete.c133
-rw-r--r--src/nl-cls-list.c113
-rw-r--r--src/nl-list-caches.c2
-rw-r--r--src/utils.h9
-rw-r--r--tests/Makefile2
31 files changed, 2047 insertions, 201 deletions
diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
index 30b8571..3c842ed 100644
--- a/include/linux/pkt_cls.h
+++ b/include/linux/pkt_cls.h
@@ -1,6 +1,7 @@
#ifndef __LINUX_PKT_CLS_H
#define __LINUX_PKT_CLS_H
+#include <linux/types.h>
#include <linux/pkt_sched.h>
/* I think i could have done better macros ; for now this is stolen from
@@ -201,8 +202,8 @@ enum
struct tc_u32_key
{
- __u32 mask;
- __u32 val;
+ __be32 mask;
+ __be32 val;
int off;
int offmask;
};
@@ -213,12 +214,12 @@ struct tc_u32_sel
unsigned char offshift;
unsigned char nkeys;
- __u16 offmask;
+ __be16 offmask;
__u16 off;
short offoff;
short hoff;
- __u32 hmask;
+ __be32 hmask;
struct tc_u32_key keys[0];
};
@@ -328,6 +329,58 @@ enum
#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1)
+/* Flow filter */
+
+enum
+{
+ FLOW_KEY_SRC,
+ FLOW_KEY_DST,
+ FLOW_KEY_PROTO,
+ FLOW_KEY_PROTO_SRC,
+ FLOW_KEY_PROTO_DST,
+ FLOW_KEY_IIF,
+ FLOW_KEY_PRIORITY,
+ FLOW_KEY_MARK,
+ FLOW_KEY_NFCT,
+ FLOW_KEY_NFCT_SRC,
+ FLOW_KEY_NFCT_DST,
+ FLOW_KEY_NFCT_PROTO_SRC,
+ FLOW_KEY_NFCT_PROTO_DST,
+ FLOW_KEY_RTCLASSID,
+ FLOW_KEY_SKUID,
+ FLOW_KEY_SKGID,
+ FLOW_KEY_VLAN_TAG,
+ __FLOW_KEY_MAX,
+};
+
+#define FLOW_KEY_MAX (__FLOW_KEY_MAX - 1)
+
+enum
+{
+ FLOW_MODE_MAP,
+ FLOW_MODE_HASH,
+};
+
+enum
+{
+ TCA_FLOW_UNSPEC,
+ TCA_FLOW_KEYS,
+ TCA_FLOW_MODE,
+ TCA_FLOW_BASECLASS,
+ TCA_FLOW_RSHIFT,
+ TCA_FLOW_ADDEND,
+ TCA_FLOW_MASK,
+ TCA_FLOW_XOR,
+ TCA_FLOW_DIVISOR,
+ TCA_FLOW_ACT,
+ TCA_FLOW_POLICE,
+ TCA_FLOW_EMATCHES,
+ TCA_FLOW_PERTURB,
+ __TCA_FLOW_MAX
+};
+
+#define TCA_FLOW_MAX (__TCA_FLOW_MAX - 1)
+
/* Basic filter */
enum
@@ -342,6 +395,20 @@ enum
#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1)
+
+/* Cgroup classifier */
+
+enum
+{
+ TCA_CGROUP_UNSPEC,
+ TCA_CGROUP_ACT,
+ TCA_CGROUP_POLICE,
+ TCA_CGROUP_EMATCHES,
+ __TCA_CGROUP_MAX,
+};
+
+#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1)
+
/* Extended Matches */
struct tcf_ematch_tree_hdr
@@ -409,7 +476,8 @@ enum
#define TCF_EM_U32 3
#define TCF_EM_META 4
#define TCF_EM_TEXT 5
-#define TCF_EM_MAX 5
+#define TCF_EM_VLAN 6
+#define TCF_EM_MAX 6
enum
{
diff --git a/include/netlink-types.h b/include/netlink-types.h
index 263e5d7..ace5dde 100644
--- a/include/netlink-types.h
+++ b/include/netlink-types.h
@@ -453,7 +453,7 @@ struct rtnl_tstats
struct nl_data * pre ##_opts; \
uint64_t pre ##_stats[RTNL_TC_STATS_MAX+1]; \
struct nl_data * pre ##_xstats; \
- void * pre ##_subdata; \
+ struct nl_data * pre ##_subdata; \
struct rtnl_tca
@@ -476,8 +476,8 @@ struct rtnl_class
struct rtnl_cls
{
NL_TCA_GENERIC(c);
- uint16_t c_prio;
- uint16_t c_protocol;
+ uint16_t c_prio;
+ uint16_t c_protocol;
struct rtnl_cls_ops *c_ops;
};
@@ -495,6 +495,12 @@ struct rtnl_u32
int cu_mask;
};
+struct rtnl_cgroup
+{
+ struct rtnl_ematch_tree *cg_ematch;
+ int cg_mask;
+};
+
struct rtnl_fw
{
uint32_t cf_classid;
@@ -504,6 +510,26 @@ struct rtnl_fw
int cf_mask;
};
+struct rtnl_ematch
+{
+ uint16_t e_id;
+ uint16_t e_kind;
+ uint16_t e_flags;
+
+ struct nl_list_head e_childs;
+ struct nl_list_head e_list;
+ struct rtnl_ematch_ops *e_ops;
+
+ char e_data[0];
+};
+
+struct rtnl_ematch_tree
+{
+ uint16_t et_progid;
+ struct nl_list_head et_list;
+
+};
+
struct rtnl_dsmark_qdisc
{
uint16_t qdm_indices;
diff --git a/include/netlink/attr.h b/include/netlink/attr.h
index b3a350b..8479c23 100644
--- a/include/netlink/attr.h
+++ b/include/netlink/attr.h
@@ -232,6 +232,16 @@ extern int nla_parse_nested(struct nlattr **, int, struct nlattr *,
NLA_PUT(msg, attrtype, nl_addr_get_len(addr), \
nl_addr_get_binary_addr(addr))
+/**
+ * Add abstract data attribute to netlink message.
+ * @arg msg Netlink message.
+ * @arg attrtype Attribute type.
+ * @arg data Abstract data object.
+ */
+#define NLA_PUT_DATA(msg, attrtype, data) \
+ NLA_PUT(msg, attrtype, nl_data_get_size(data), \
+ nl_data_get(data))
+
/** @} */
/**
diff --git a/include/netlink/list.h b/include/netlink/list.h
index c6876a7..28712ed 100644
--- a/include/netlink/list.h
+++ b/include/netlink/list.h
@@ -18,6 +18,11 @@ struct nl_list_head
struct nl_list_head * prev;
};
+static inline void NL_INIT_LIST_HEAD(struct nl_list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
static inline void __nl_list_add(struct nl_list_head *obj,
struct nl_list_head *prev,
diff --git a/include/netlink/route/classifier-modules.h b/include/netlink/route/classifier-modules.h
index 02715ed..35cb06e 100644
--- a/include/netlink/route/classifier-modules.h
+++ b/include/netlink/route/classifier-modules.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_CLASS_MODULES_H_
@@ -25,10 +25,16 @@ extern "C" {
struct rtnl_cls_ops
{
/**
- * Kind/Name of classifier
+ * Name of classifier module
*/
char co_kind[32];
+
+ /**
+ * Size of private classifier data
+ */
+ size_t co_size;
+
/**
* Dump callbacks
*/
@@ -37,7 +43,7 @@ struct rtnl_cls_ops
/**
* Must return the contents supposed to be in TCA_OPTIONS
*/
- struct nl_msg *(*co_get_opts)(struct rtnl_cls *);
+ int (*co_get_opts)(struct rtnl_cls *, struct nl_msg *);
/**
* TCA_OPTIONS message parser
diff --git a/include/netlink/route/classifier.h b/include/netlink/route/classifier.h
index b434000..d9c3d21 100644
--- a/include/netlink/route/classifier.h
+++ b/include/netlink/route/classifier.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_CLASSIFIER_H_
@@ -40,8 +40,10 @@ extern int rtnl_cls_build_delete_request(struct rtnl_cls *, int,
extern int rtnl_cls_delete(struct nl_sock *, struct rtnl_cls *, int);
extern void rtnl_cls_set_ifindex(struct rtnl_cls *, int);
+extern int rtnl_cls_get_ifindex(struct rtnl_cls *);
extern void rtnl_cls_set_handle(struct rtnl_cls *, uint32_t);
extern void rtnl_cls_set_parent(struct rtnl_cls *, uint32_t);
+extern uint32_t rtnl_cls_get_parent(struct rtnl_cls *);
extern int rtnl_cls_set_kind(struct rtnl_cls *, const char *);
extern struct rtnl_cls_ops *rtnl_cls_get_ops(struct rtnl_cls *);
@@ -51,6 +53,8 @@ extern uint16_t rtnl_cls_get_prio(struct rtnl_cls *);
extern void rtnl_cls_set_protocol(struct rtnl_cls *, uint16_t);
extern uint16_t rtnl_cls_get_protocol(struct rtnl_cls *);
+extern void *rtnl_cls_data(struct rtnl_cls *);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/netlink/route/cls/basic.h b/include/netlink/route/cls/basic.h
new file mode 100644
index 0000000..7003124
--- /dev/null
+++ b/include/netlink/route/cls/basic.h
@@ -0,0 +1,33 @@
+/*
+ * netlink/route/cls/basic.h Basic Classifier
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_BASIC_H_
+#define NETLINK_BASIC_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct rtnl_cls_ops *rtnl_basic_get_ops(void);
+extern int rtnl_basic_set_classid(struct rtnl_cls *, uint32_t);
+extern uint32_t rtnl_basic_get_classid(struct rtnl_cls *);
+extern int rtnl_basic_set_ematch(struct rtnl_cls *,
+ struct rtnl_ematch_tree *);
+extern struct rtnl_ematch_tree *
+ rtnl_basic_get_ematch(struct rtnl_cls *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/cls/cgroup.h b/include/netlink/route/cls/cgroup.h
new file mode 100644
index 0000000..7b0e3d3
--- /dev/null
+++ b/include/netlink/route/cls/cgroup.h
@@ -0,0 +1,31 @@
+/*
+ * netlink/route/cls/cgroup.h Control Groups Classifier
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_CLS_CGROUP_H_
+#define NETLINK_CLS_CGROUP_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int rtnl_cgroup_set_ematch(struct rtnl_cls *,
+ struct rtnl_ematch_tree *);
+extern struct rtnl_ematch_tree *
+ rtnl_cgroup_get_ematch(struct rtnl_cls *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/cls/ematch.h b/include/netlink/route/cls/ematch.h
new file mode 100644
index 0000000..c4292bf
--- /dev/null
+++ b/include/netlink/route/cls/ematch.h
@@ -0,0 +1,73 @@
+/*
+ * netlink/route/cls/ematch.h Extended Matches
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_CLS_EMATCH_H_
+#define NETLINK_CLS_EMATCH_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/classifier.h>
+#include <linux/pkt_cls.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rtnl_ematch;
+struct rtnl_ematch_tree;
+
+struct rtnl_ematch_ops
+{
+ int eo_kind;
+ const char * eo_name;
+ size_t eo_datalen;
+
+ int (*eo_parse)(struct rtnl_ematch *,
+ void *, size_t);
+ void (*eo_dump)(struct rtnl_ematch *,
+ struct nl_dump_params *);
+ struct nl_list_head eo_list;
+};
+
+extern int rtnl_ematch_register(struct rtnl_ematch_ops *);
+extern int rtnl_ematch_unregister(struct rtnl_ematch_ops *);
+
+extern struct rtnl_ematch_ops *
+ rtnl_ematch_lookup_ops(int);
+extern struct rtnl_ematch_ops *
+ rtnl_ematch_lookup_ops_name(const char *);
+
+extern struct rtnl_ematch *
+ rtnl_ematch_alloc(struct rtnl_ematch_ops *);
+extern void rtnl_ematch_add_child(struct rtnl_ematch *,
+ struct rtnl_ematch *);
+extern void rtnl_ematch_unlink(struct rtnl_ematch *);
+extern void rtnl_ematch_free(struct rtnl_ematch *);
+
+extern void * rtnl_ematch_data(struct rtnl_ematch *);
+extern void rtnl_ematch_set_flags(struct rtnl_ematch *, uint16_t);
+extern void rtnl_ematch_unset_flags(struct rtnl_ematch *, uint16_t);
+extern uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *);
+
+extern struct rtnl_ematch_tree *
+ rtnl_ematch_tree_alloc(uint16_t);
+extern void rtnl_ematch_tree_free(struct rtnl_ematch_tree *);
+
+extern int rtnl_ematch_parse(struct nlattr *, struct rtnl_ematch_tree **);
+extern void rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *,
+ struct rtnl_ematch *);
+extern void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *,
+ struct nl_dump_params *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/cls/ematch/cmp.h b/include/netlink/route/cls/ematch/cmp.h
new file mode 100644
index 0000000..b4ad03a
--- /dev/null
+++ b/include/netlink/route/cls/ematch/cmp.h
@@ -0,0 +1,31 @@
+/*
+ * netlink/route/cls/ematch/cmp.h Simple Comparison
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_CLS_EMATCH_CMP_H_
+#define NETLINK_CLS_EMATCH_CMP_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void rtnl_ematch_cmp_set(struct rtnl_ematch *,
+ struct tcf_em_cmp *);
+extern struct tcf_em_cmp *
+ rtnl_ematch_cmp_get(struct rtnl_ematch *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/Makefile b/lib/Makefile
index 80dd1ca..a811942 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -18,6 +18,7 @@ CORE_OBJ := $(CORE_C:%.c=%.o)
ROUTE_C := $(wildcard route/*.c)
ROUTE_C += $(wildcard route/cls/*.c)
+ROUTE_C += $(wildcard route/cls/ematch/*.c)
ROUTE_C += $(wildcard route/sch/*.c)
ROUTE_C += $(wildcard route/link/*.c)
ROUTE_C += $(wildcard fib_lookup/*.c)
diff --git a/lib/route/classifier.c b/lib/route/cls.c
index 4b5ce9d..cbf0345 100644
--- a/lib/route/classifier.c
+++ b/lib/route/cls.c
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
*/
/**
@@ -38,9 +38,9 @@ static struct nl_cache_ops rtnl_cls_ops;
static int cls_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
struct nlmsghdr *nlh, struct nl_parser_param *pp)
{
- int err;
- struct rtnl_cls *cls;
struct rtnl_cls_ops *cops;
+ struct rtnl_cls *cls;
+ int err;
cls = rtnl_cls_alloc();
if (!cls) {
@@ -57,11 +57,8 @@ static int cls_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
cls->c_protocol = ntohs(TC_H_MIN(cls->c_info));
cops = rtnl_cls_lookup_ops(cls);
- if (cops && cops->co_msg_parser) {
- err = cops->co_msg_parser(cls);
- if (err < 0)
- goto errout_free;
- }
+ if (cops && cops->co_msg_parser && (err = cops->co_msg_parser(cls)) < 0)
+ goto errout_free;
err = pp->pp_cb((struct nl_object *) cls, pp);
errout_free:
@@ -97,19 +94,23 @@ static int cls_build(struct rtnl_cls *cls, int type, int flags,
tchdr = nlmsg_data(nlmsg_hdr(*result));
prio = rtnl_cls_get_prio(cls);
proto = rtnl_cls_get_protocol(cls);
- tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto)),
+ tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto));
cops = rtnl_cls_lookup_ops(cls);
if (cops && cops->co_get_opts) {
struct nl_msg *opts;
-
- opts = cops->co_get_opts(cls);
- if (opts) {
- err = nla_put_nested(*result, TCA_OPTIONS, opts);
- nlmsg_free(opts);
- if (err < 0)
- goto errout;
+
+ if (!(opts = nlmsg_alloc())) {
+ err = -NLE_NOMEM;
+ goto errout;
}
+
+ if (!(err = cops->co_get_opts(cls, opts)))
+ err = nla_put_nested(*result, TCA_OPTIONS, opts);
+
+ nlmsg_free(opts);
+ if (err < 0)
+ goto errout;
}
return 0;
diff --git a/lib/route/cls/basic.c b/lib/route/cls/basic.c
new file mode 100644
index 0000000..1460b72
--- /dev/null
+++ b/lib/route/cls/basic.c
@@ -0,0 +1,211 @@
+/*
+ * lib/route/cls/basic.c Basic Classifier
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cls
+ * @defgroup basic Basic Classifier
+ *
+ * @par Introduction
+ * The basic classifier is the simplest form of a classifier. It does
+ * not have any special classification capabilities, instead it can be
+ * used to classify exclusively based on extended matches or to
+ * create a "catch-all" filter.
+ *
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/classifier-modules.h>
+#include <netlink/route/cls/basic.h>
+#include <netlink/route/cls/ematch.h>
+
+struct rtnl_basic
+{
+ uint32_t b_classid;
+ struct rtnl_ematch_tree * b_ematch;
+ int b_mask;
+};
+
+/** @cond SKIP */
+#define BASIC_ATTR_CLASSID 0x001
+#define BASIC_ATTR_EMATCH 0x002
+/** @endcond */
+
+static struct nla_policy basic_policy[TCA_FW_MAX+1] = {
+ [TCA_BASIC_CLASSID] = { .type = NLA_U32 },
+ [TCA_BASIC_EMATCHES] = { .type = NLA_NESTED },
+ [TCA_BASIC_ACT] = { .type = NLA_NESTED },
+ [TCA_BASIC_POLICE] = { .type = NLA_NESTED },
+};
+
+static int basic_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
+{
+ return -NLE_OPNOTSUPP;
+}
+
+static void basic_free_data(struct rtnl_cls *cls)
+{
+ struct rtnl_basic *basic = rtnl_cls_data(cls);
+
+ rtnl_ematch_tree_free(basic->b_ematch);
+}
+
+static int basic_msg_parser(struct rtnl_cls *cls)
+{
+ struct nlattr *tb[TCA_BASIC_MAX + 1];
+ struct rtnl_basic *basic = rtnl_cls_data(cls);
+ int err;
+
+ err = tca_parse(tb, TCA_BASIC_MAX, (struct rtnl_tca *) cls, basic_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[TCA_BASIC_CLASSID]) {
+ basic->b_classid = nla_get_u32(tb[TCA_BASIC_CLASSID]);
+ basic->b_mask |= BASIC_ATTR_CLASSID;
+ }
+
+ if (tb[TCA_BASIC_EMATCHES]) {
+ if ((err = rtnl_ematch_parse(tb[TCA_BASIC_EMATCHES],
+ &basic->b_ematch)) < 0)
+ return err;
+
+ if (basic->b_ematch)
+ basic->b_mask |= BASIC_ATTR_EMATCH;
+ }
+
+ if (tb[TCA_BASIC_ACT]) {
+ /* XXX */
+ }
+
+ if (tb[TCA_BASIC_POLICE]) {
+ /* XXX */
+ }
+
+ return 0;
+}
+
+static void basic_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
+{
+ struct rtnl_basic *b = rtnl_cls_data(cls);
+ char buf[32];
+
+ if (b->b_mask & BASIC_ATTR_EMATCH)
+ nl_dump(p, " ematch");
+ else
+ nl_dump(p, " match-all");
+
+ if (b->b_mask & BASIC_ATTR_CLASSID)
+ nl_dump(p, " classify-to %s",
+ rtnl_tc_handle2str(b->b_classid, buf, sizeof(buf)));
+}
+
+static void basic_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
+{
+ struct rtnl_basic *b = rtnl_cls_data(cls);
+
+ if (b->b_mask & BASIC_ATTR_EMATCH) {
+ nl_dump(p, "\n");
+ nl_dump_line(p, " ematch ");
+ rtnl_ematch_tree_dump(b->b_ematch, p);
+ } else
+ nl_dump(p, "no options.\n");
+}
+
+static int basic_get_opts(struct rtnl_cls *cls, struct nl_msg *msg)
+{
+ struct rtnl_basic *b = rtnl_cls_data(cls);
+
+ if (!(b->b_mask & BASIC_ATTR_CLASSID))
+ return -NLE_MISSING_ATTR;
+
+ NLA_PUT_U32(msg, TCA_BASIC_CLASSID, b->b_classid);
+
+ return 0;
+
+nla_put_failure:
+ return -NLE_NOMEM;
+}
+
+/**
+ * @name Attribute Modifications
+ * @{
+ */
+
+int rtnl_basic_set_classid(struct rtnl_cls *cls, uint32_t classid)
+{
+ struct rtnl_basic *b = rtnl_cls_data(cls);
+
+ b->b_classid = classid;
+ b->b_mask |= BASIC_ATTR_CLASSID;
+
+ return 0;
+}
+
+uint32_t rtnl_basic_get_classid(struct rtnl_cls *cls)
+{
+ struct rtnl_basic *b = rtnl_cls_data(cls);
+
+ return b->b_classid;
+}
+
+int rtnl_basic_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree)
+{
+ struct rtnl_basic *b = rtnl_cls_data(cls);
+
+ if (b->b_ematch) {
+ rtnl_ematch_tree_free(b->b_ematch);
+ b->b_mask &= ~BASIC_ATTR_EMATCH;
+ }
+
+ b->b_ematch = tree;
+
+ if (tree)
+ b->b_mask |= BASIC_ATTR_EMATCH;
+
+ return 0;
+}
+
+struct rtnl_ematch_tree *rtnl_basic_get_ematch(struct rtnl_cls *cls)
+{
+ struct rtnl_basic *b = rtnl_cls_data(cls);
+ return b->b_ematch;
+}
+
+/** @} */
+
+static struct rtnl_cls_ops basic_ops = {
+ .co_kind = "basic",
+ .co_size = sizeof(struct rtnl_basic),
+ .co_msg_parser = basic_msg_parser,
+ .co_clone = basic_clone,
+ .co_free_data = basic_free_data,
+ .co_get_opts = basic_get_opts,
+ .co_dump = {
+ [NL_DUMP_LINE] = basic_dump_line,
+ [NL_DUMP_DETAILS] = basic_dump_details,
+ },
+};
+
+static void __init basic_init(void)
+{
+ rtnl_cls_register(&basic_ops);
+}
+
+static void __exit basic_exit(void)
+{
+ rtnl_cls_unregister(&basic_ops);
+}
+
+/** @} */
diff --git a/lib/route/cls/cgroup.c b/lib/route/cls/cgroup.c
new file mode 100644
index 0000000..e5f38b8
--- /dev/null
+++ b/lib/route/cls/cgroup.c
@@ -0,0 +1,141 @@
+/*
+ * lib/route/cls/cgroup.c Control Groups Classifier
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cls_api
+ * @defgroup cgroup Control Groups Classifier
+ *
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/classifier-modules.h>
+#include <netlink/route/cls/cgroup.h>
+#include <netlink/route/cls/ematch.h>
+
+/** @cond SKIP */
+#define CGROUP_ATTR_EMATCH 0x001
+/** @endcond */
+
+static struct nla_policy cgroup_policy[TCA_CGROUP_MAX+1] = {
+ [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED },
+};
+
+static void cgroup_free_data(struct rtnl_cls *cls)
+{
+ struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+
+ rtnl_ematch_tree_free(cg->cg_ematch);
+}
+
+static int cgroup_msg_parser(struct rtnl_cls *cls)
+{
+ struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+ struct nlattr *tb[TCA_CGROUP_MAX + 1];
+ int err;
+
+ err = tca_parse(tb, TCA_CGROUP_MAX, (struct rtnl_tca *) cls,
+ cgroup_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[TCA_CGROUP_EMATCHES]) {
+ if ((err = rtnl_ematch_parse(tb[TCA_CGROUP_EMATCHES],
+ &cg->cg_ematch)) < 0)
+ return err;
+ cg->cg_mask |= CGROUP_ATTR_EMATCH;
+ }
+
+#if 0
+ TODO:
+ TCA_CGROUP_ACT,
+ TCA_CGROUP_POLICE,
+#endif
+
+ return 0;
+}
+
+static void cgroup_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
+{
+ struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+
+ if (cg->cg_mask & CGROUP_ATTR_EMATCH)
+ nl_dump(p, " ematch");
+ else
+ nl_dump(p, " match-all");
+}
+
+static void cgroup_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
+{
+ struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+
+ if (cg->cg_mask & CGROUP_ATTR_EMATCH) {
+ nl_dump(p, "\n");
+ nl_dump_line(p, " ematch ");
+ rtnl_ematch_tree_dump(cg->cg_ematch, p);
+ }
+}
+
+/**
+ * @name Attribute Modifications
+ * @{
+ */
+
+int rtnl_cgroup_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree)
+{
+ struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+
+ if (cg->cg_ematch) {
+ rtnl_ematch_tree_free(cg->cg_ematch);
+ cg->cg_mask &= ~CGROUP_ATTR_EMATCH;
+ }
+
+ cg->cg_ematch = tree;
+
+ if (tree)
+ cg->cg_mask |= CGROUP_ATTR_EMATCH;
+
+ return 0;
+}
+
+struct rtnl_ematch_tree *rtnl_cgroup_get_ematch(struct rtnl_cls *cls)
+{
+ struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+ return cg->cg_ematch;
+}
+
+static struct rtnl_cls_ops cgroup_ops = {
+ .co_kind = "cgroup",
+ .co_size = sizeof(struct rtnl_cgroup),
+ .co_msg_parser = cgroup_msg_parser,
+ .co_free_data = cgroup_free_data,
+ .co_dump = {
+ [NL_DUMP_LINE] = cgroup_dump_line,
+ [NL_DUMP_DETAILS] = cgroup_dump_details,
+ },
+};
+
+static void __init cgroup_init(void)
+{
+ rtnl_cls_register(&cgroup_ops);
+}
+
+static void __exit cgroup_exit(void)
+{
+ rtnl_cls_unregister(&cgroup_ops);
+}
+
+/** @} */
diff --git a/lib/route/cls/ematch.c b/lib/route/cls/ematch.c
new file mode 100644
index 0000000..cb77b16
--- /dev/null
+++ b/lib/route/cls/ematch.c
@@ -0,0 +1,410 @@
+/*
+ * lib/route/cls/ematch.c Extended Matches
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cls
+ * @defgroup ematch Extended Match
+ *
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/classifier-modules.h>
+#include <netlink/route/cls/ematch.h>
+
+/**
+ * @name Module Registration
+ * @{
+ */
+
+static NL_LIST_HEAD(ematch_ops_list);
+
+/**
+ * Register ematch module
+ * @arg ops Module operations.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_ematch_register(struct rtnl_ematch_ops *ops)
+{
+ if (rtnl_ematch_lookup_ops(ops->eo_kind))
+ return -NLE_EXIST;
+
+ nl_list_add_tail(&ops->eo_list, &ematch_ops_list);
+
+ return 0;
+}
+
+/**
+ * Unregister ematch module
+ * @arg ops Module operations.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_ematch_unregister(struct rtnl_ematch_ops *ops)
+{
+ struct rtnl_ematch_ops *o;
+
+ nl_list_for_each_entry(o, &ematch_ops_list, eo_list) {
+ if (ops->eo_kind == o->eo_kind) {
+ nl_list_del(&o->eo_list);
+ return 0;
+ }
+ }
+
+ return -NLE_OBJ_NOTFOUND;
+}
+
+/**
+ * Lookup ematch module by kind
+ * @arg kind Module kind.
+ *
+ * @return Module operations or NULL if not found.
+ */
+struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind)
+{
+ struct rtnl_ematch_ops *ops;
+
+ nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
+ if (ops->eo_kind == kind)
+ return ops;
+
+ return NULL;
+}
+
+/**
+ * Lookup ematch module by name
+ * @arg name Name of ematch module.
+ *
+ * @return Module operations or NULL if not fuond.
+ */
+struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_name(const char *name)
+{
+ struct rtnl_ematch_ops *ops;
+
+ nl_list_for_each_entry(ops, &ematch_ops_list, eo_list)
+ if (!strcasecmp(ops->eo_name, name))
+ return ops;
+
+ return NULL;
+}
+
+/** @} */
+
+/**
+ * @name Match
+ */
+
+struct rtnl_ematch *rtnl_ematch_alloc(struct rtnl_ematch_ops *ops)
+{
+ struct rtnl_ematch *e;
+ size_t len = sizeof(*e) + (ops ? ops->eo_datalen : 0);
+
+ if (!(e = calloc(1, len)))
+ return NULL;
+
+ NL_INIT_LIST_HEAD(&e->e_list);
+ NL_INIT_LIST_HEAD(&e->e_childs);
+
+ if (ops) {
+ e->e_ops = ops;
+ e->e_kind = ops->eo_kind;
+ }
+
+ return e;
+}
+
+/**
+ * Add ematch to the end of the parent's list of children.
+ * @arg parent Parent ematch.
+ * @arg child Ematch to be added as new child of parent.
+ */
+void rtnl_ematch_add_child(struct rtnl_ematch *parent,
+ struct rtnl_ematch *child)
+{
+ nl_list_add_tail(&child->e_list, &parent->e_childs);
+}
+
+/**
+ * Remove ematch from the list it is linked to.
+ * @arg ematch Ematch to be unlinked.
+ */
+void rtnl_ematch_unlink(struct rtnl_ematch *ematch)
+{
+ nl_list_del(&ematch->e_list);
+}
+
+void rtnl_ematch_free(struct rtnl_ematch *ematch)
+{
+ if (!ematch)
+ return;
+
+ free(ematch);
+}
+
+void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags)
+{
+ ematch->e_flags |= flags;
+}
+
+void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags)
+{
+ ematch->e_flags &= ~flags;
+}
+
+uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch)
+{
+ return ematch->e_flags;
+}
+
+void *rtnl_ematch_data(struct rtnl_ematch *ematch)
+{
+ return ematch->e_data;
+}
+
+/** @} */
+
+/**
+ * @name Tree
+ */
+
+struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid)
+{
+ struct rtnl_ematch_tree *tree;
+
+ if (!(tree = calloc(1, sizeof(*tree))))
+ return NULL;
+
+ NL_INIT_LIST_HEAD(&tree->et_list);
+ tree->et_progid = progid;
+
+ return tree;
+}
+
+static void free_ematch_list(struct nl_list_head *head)
+{
+ struct rtnl_ematch *pos, *next;
+
+ nl_list_for_each_entry_safe(pos, next, head, e_list) {
+ if (!nl_list_empty(&pos->e_childs))
+ free_ematch_list(&pos->e_childs);
+ rtnl_ematch_free(pos);
+ }
+}
+
+void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree)
+{
+ if (!tree)
+ return;
+
+ free_ematch_list(&tree->et_list);
+ free(tree);
+}
+
+void rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *tree,
+ struct rtnl_ematch *ematch)
+{
+ nl_list_add_tail(&ematch->e_list, &tree->et_list);
+}
+
+static inline uint32_t container_ref(struct rtnl_ematch *ematch)
+{
+ return *((uint32_t *) rtnl_ematch_data(ematch));
+}
+
+static int link_tree(struct rtnl_ematch *index[], int nmatches, int pos,
+ struct nl_list_head *root)
+{
+ struct rtnl_ematch *ematch;
+ int i;
+
+ for (i = pos; i < nmatches; i++) {
+ ematch = index[i];
+
+ nl_list_add_tail(&ematch->e_list, root);
+
+ if (ematch->e_kind == TCF_EM_CONTAINER)
+ link_tree(index, nmatches, container_ref(ematch),
+ &ematch->e_childs);
+
+ if (!(ematch->e_flags & TCF_EM_REL_MASK))
+ return 0;
+ }
+
+ /* Last entry in chain can't possibly have no relation */
+ return -NLE_INVAL;
+}
+
+static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = {
+ [TCA_EMATCH_TREE_HDR] = { .minlen=sizeof(struct tcf_ematch_tree_hdr) },
+ [TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED },
+};
+
+/**
+ * Parse ematch netlink attributes
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result)
+{
+ struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1];
+ struct tcf_ematch_tree_hdr *thdr;
+ struct rtnl_ematch_tree *tree;
+ struct rtnl_ematch **index;
+ int nmatches = 0, err, remaining;
+
+ err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy);
+ if (err < 0)
+ return err;
+
+ if (!tb[TCA_EMATCH_TREE_HDR])
+ return -NLE_MISSING_ATTR;
+
+ thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]);
+
+ /* Ignore empty trees */
+ if (thdr->nmatches == 0)
+ return 0;
+
+ if (!tb[TCA_EMATCH_TREE_LIST])
+ return -NLE_MISSING_ATTR;
+
+ if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) /
+ nla_total_size(sizeof(struct tcf_ematch_hdr))))
+ return -NLE_INVAL;
+
+ if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *))))
+ return -NLE_NOMEM;
+
+ if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) {
+ err = -NLE_NOMEM;
+ goto errout;
+ }
+
+ nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) {
+ struct rtnl_ematch_ops *ops;
+ struct tcf_ematch_hdr *hdr;
+ struct rtnl_ematch *ematch;
+ void *data;
+ size_t len;
+
+ if (nla_len(a) < sizeof(*hdr)) {
+ err = -NLE_INVAL;
+ goto errout;
+ }
+
+ if (nmatches >= thdr->nmatches) {
+ err = -NLE_RANGE;
+ goto errout;
+ }
+
+ hdr = nla_data(a);
+ data = nla_data(a) + NLA_ALIGN(sizeof(*hdr));
+ len = nla_len(a) - NLA_ALIGN(sizeof(*hdr));
+
+ ops = rtnl_ematch_lookup_ops(hdr->kind);
+ if (ops && ops->eo_datalen && len < ops->eo_datalen) {
+ err = -NLE_INVAL;
+ goto errout;
+ }
+
+ if (!(ematch = rtnl_ematch_alloc(ops))) {
+ err = -NLE_NOMEM;
+ goto errout;
+ }
+
+ ematch->e_id = hdr->matchid;
+ ematch->e_kind = hdr->kind;
+ ematch->e_flags = hdr->flags;
+
+ if (ops && (err = ops->eo_parse(ematch, data, len)) < 0)
+ goto errout;
+
+ if (hdr->kind == TCF_EM_CONTAINER &&
+ container_ref(ematch) >= thdr->nmatches) {
+ err = -NLE_INVAL;
+ goto errout;
+ }
+
+ index[nmatches++] = ematch;
+ }
+
+ if (nmatches != thdr->nmatches) {
+ err = -NLE_INVAL;
+ goto errout;
+ }
+
+ err = link_tree(index, nmatches, 0, &tree->et_list);
+ if (err < 0)
+ goto errout;
+
+ free(index);
+ *result = tree;
+
+ return 0;
+
+errout:
+ rtnl_ematch_tree_free(tree);
+ free(index);
+ return err;
+}
+
+static void dump_ematch_sequence(struct nl_list_head *head,
+ struct nl_dump_params *p)
+{
+ struct rtnl_ematch *match;
+
+ nl_list_for_each_entry(match, head, e_list) {
+ if (match->e_flags & TCF_EM_INVERT)
+ nl_dump(p, "NOT ");
+
+ if (match->e_kind == TCF_EM_CONTAINER) {
+ nl_dump(p, "(");
+ dump_ematch_sequence(&match->e_childs, p);
+ nl_dump(p, ")");
+ } else if (!match->e_ops) {
+ nl_dump(p, "[unknown ematch %d]", match->e_kind);
+ } else {
+ nl_dump(p, "%s(", match->e_ops->eo_name);
+
+ if (match->e_ops->eo_dump)
+ match->e_ops->eo_dump(match, p);
+
+ nl_dump(p, ")");
+ }
+
+ switch (match->e_flags & TCF_EM_REL_MASK) {
+ case TCF_EM_REL_AND:
+ nl_dump(p, " AND ");
+ break;
+ case TCF_EM_REL_OR:
+ nl_dump(p, " OR ");
+ break;
+ default:
+ /* end of first level ematch sequence */
+ return;
+ }
+ }
+}
+
+void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree,
+ struct nl_dump_params *p)
+{
+ dump_ematch_sequence(&tree->et_list, p);
+ nl_dump(p, "\n");
+}
+
+/** @} */
+
+/** @} */
diff --git a/lib/route/cls/ematch/cmp.c b/lib/route/cls/ematch/cmp.c
new file mode 100644
index 0000000..ec25320
--- /dev/null
+++ b/lib/route/cls/ematch/cmp.c
@@ -0,0 +1,116 @@
+/*
+ * lib/route/cls/ematch/cmp.c Simple packet data comparison ematch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup ematch
+ * @defgroup em_cmp Simple packet data comparison
+ *
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+#include <linux/tc_ematch/tc_em_cmp.h>
+
+void rtnl_ematch_cmp_set(struct rtnl_ematch *ematch,
+ struct tcf_em_cmp *cfg)
+{
+ memcpy(rtnl_ematch_data(ematch), cfg, sizeof(*cfg));
+}
+
+struct tcf_em_cmp *rtnl_ematch_cmp_get(struct rtnl_ematch *ematch)
+{
+ return rtnl_ematch_data(ematch);
+}
+
+static const char *align_txt(struct tcf_em_cmp *cmp)
+{
+ switch (cmp->align) {
+ case TCF_EM_ALIGN_U8:
+ return "u8";
+ case TCF_EM_ALIGN_U16:
+ return (cmp->flags & TCF_EM_CMP_TRANS) ? "h16" : "u16";
+ case TCF_EM_ALIGN_U32:
+ return (cmp->flags & TCF_EM_CMP_TRANS) ? "h32" : "u32";
+ default:
+ return (cmp->flags & TCF_EM_CMP_TRANS) ? "h?" : "u?";
+ }
+}
+
+static const char *layer_txt(struct tcf_em_cmp *cmp)
+{
+ switch (cmp->layer) {
+ case TCF_LAYER_LINK:
+ return "link";
+ case TCF_LAYER_NETWORK:
+ return "network";
+ case TCF_LAYER_TRANSPORT:
+ return "transport";
+ default:
+ return "?";
+ }
+}
+
+static const char *relation_txt(struct tcf_em_cmp *cmp)
+{
+ switch (cmp->opnd) {
+ case TCF_EM_OPND_EQ:
+ return "eq";
+ case TCF_EM_OPND_LT:
+ return "lt";
+ case TCF_EM_OPND_GT:
+ return "gt";
+ default:
+ return "?";
+ }
+}
+
+static int cmp_parse(struct rtnl_ematch *m, void *data, size_t len)
+{
+ memcpy(rtnl_ematch_data(m), data, len);
+
+ return 0;
+}
+
+static void cmp_dump(struct rtnl_ematch *m, struct nl_dump_params *p)
+{
+ struct tcf_em_cmp *cmp = rtnl_ematch_data(m);
+
+ nl_dump(p, "%s at %s+%u ",
+ align_txt(cmp), layer_txt(cmp), cmp->off);
+
+ if (cmp->mask)
+ nl_dump(p, "& 0x%x ", cmp->mask);
+
+ nl_dump(p, "%s %u", relation_txt(cmp), cmp->val);
+}
+
+static struct rtnl_ematch_ops cmp_ops = {
+ .eo_kind = TCF_EM_CMP,
+ .eo_name = "cmp",
+ .eo_datalen = sizeof(struct tcf_em_cmp),
+ .eo_parse = cmp_parse,
+ .eo_dump = cmp_dump,
+};
+
+static void __init cmp_init(void)
+{
+ rtnl_ematch_register(&cmp_ops);
+}
+
+static void __exit cmp_exit(void)
+{
+ rtnl_ematch_unregister(&cmp_ops);
+}
+
+/** @} */
diff --git a/lib/route/cls/ematch/container.c b/lib/route/cls/ematch/container.c
new file mode 100644
index 0000000..54d836f
--- /dev/null
+++ b/lib/route/cls/ematch/container.c
@@ -0,0 +1,39 @@
+/*
+ * lib/route/cls/ematch/container.c Container Ematch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+
+static int container_parse(struct rtnl_ematch *m, void *data, size_t len)
+{
+ memcpy(m->e_data, data, sizeof(uint32_t));
+
+ return 0;
+}
+
+static struct rtnl_ematch_ops container_ops = {
+ .eo_kind = TCF_EM_CONTAINER,
+ .eo_name = "container",
+ .eo_datalen = sizeof(uint32_t),
+ .eo_parse = container_parse,
+};
+
+static void __init container_init(void)
+{
+ rtnl_ematch_register(&container_ops);
+}
+
+static void __exit container_exit(void)
+{
+ rtnl_ematch_unregister(&container_ops);
+}
diff --git a/lib/route/cls/fw.c b/lib/route/cls/fw.c
index d18d3f8..8cf25b9 100644
--- a/lib/route/cls/fw.c
+++ b/lib/route/cls/fw.c
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2006 Petr Gotthard <petr.gotthard@siemens.com>
* Copyright (c) 2006 Siemens AG Oesterreich
*/
@@ -32,19 +32,6 @@
#define FW_ATTR_INDEV 0x008
/** @endcond */
-static inline struct rtnl_fw *fw_cls(struct rtnl_cls *cls)
-{
- return (struct rtnl_fw *) cls->c_subdata;
-}
-
-static inline struct rtnl_fw *fw_alloc(struct rtnl_cls *cls)
-{
- if (!cls->c_subdata)
- cls->c_subdata = calloc(1, sizeof(struct rtnl_fw));
-
- return fw_cls(cls);
-}
-
static struct nla_policy fw_policy[TCA_FW_MAX+1] = {
[TCA_FW_CLASSID] = { .type = NLA_U32 },
[TCA_FW_INDEV] = { .type = NLA_STRING,
@@ -53,18 +40,14 @@ static struct nla_policy fw_policy[TCA_FW_MAX+1] = {
static int fw_msg_parser(struct rtnl_cls *cls)
{
- int err;
+ struct rtnl_fw *f = rtnl_cls_data(cls);
struct nlattr *tb[TCA_FW_MAX + 1];
- struct rtnl_fw *f;
+ int err;
err = tca_parse(tb, TCA_FW_MAX, (struct rtnl_tca *) cls, fw_policy);
if (err < 0)
return err;
- f = fw_alloc(cls);
- if (!f)
- return -NLE_NOMEM;
-
if (tb[TCA_FW_CLASSID]) {
f->cf_classid = nla_get_u32(tb[TCA_FW_CLASSID]);
f->cf_mask |= FW_ATTR_CLASSID;
@@ -94,47 +77,31 @@ static int fw_msg_parser(struct rtnl_cls *cls)
static void fw_free_data(struct rtnl_cls *cls)
{
- struct rtnl_fw *f = fw_cls(cls);
-
- if (!f)
- return;
+ struct rtnl_fw *f = rtnl_cls_data(cls);
nl_data_free(f->cf_act);
nl_data_free(f->cf_police);
-
- free(cls->c_subdata);
}
static int fw_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
{
- struct rtnl_fw *dst, *src = fw_cls(_src);
-
- if (!src)
- return 0;
+ struct rtnl_fw *dst = rtnl_cls_data(_dst);
+ struct rtnl_fw *src = rtnl_cls_data(_src);
- dst = fw_alloc(_dst);
- if (!dst)
+ if (src->cf_act && !(dst->cf_act = nl_data_clone(src->cf_act)))
return -NLE_NOMEM;
-
- if (src->cf_act)
- if (!(dst->cf_act = nl_data_clone(src->cf_act)))
- return -NLE_NOMEM;
- if (src->cf_police)
- if (!(dst->cf_police = nl_data_clone(src->cf_police)))
- return -NLE_NOMEM;
+ if (src->cf_police && !(dst->cf_police = nl_data_clone(src->cf_police)))
+ return -NLE_NOMEM;
return 0;
}
static void fw_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
{
- struct rtnl_fw *f = fw_cls(cls);
+ struct rtnl_fw *f = rtnl_cls_data(cls);
char buf[32];
- if (!f)
- return;
-
if (f->cf_mask & FW_ATTR_CLASSID)
nl_dump(p, " target %s",
rtnl_tc_handle2str(f->cf_classid, buf, sizeof(buf)));
@@ -142,45 +109,32 @@ static void fw_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
static void fw_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
{
- struct rtnl_fw *f = fw_cls(cls);
-
- if (!f)
- return;
+ struct rtnl_fw *f = rtnl_cls_data(cls);
if (f->cf_mask & FW_ATTR_INDEV)
nl_dump(p, "indev %s ", f->cf_indev);
}
-static void fw_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p)
-{
-}
-
-static struct nl_msg *fw_get_opts(struct rtnl_cls *cls)
+static int fw_get_opts(struct rtnl_cls *cls, struct nl_msg *msg)
{
- struct rtnl_fw *f;
- struct nl_msg *msg;
+ struct rtnl_fw *f = rtnl_cls_data(cls);
- f = fw_cls(cls);
- if (!f)
- return NULL;
-
- msg = nlmsg_alloc();
- if (!msg)
- return NULL;
-
if (f->cf_mask & FW_ATTR_CLASSID)
- nla_put_u32(msg, TCA_FW_CLASSID, f->cf_classid);
+ NLA_PUT_U32(msg, TCA_FW_CLASSID, f->cf_classid);
if (f->cf_mask & FW_ATTR_ACTION)
- nla_put_data(msg, TCA_FW_ACT, f->cf_act);
+ NLA_PUT_DATA(msg, TCA_FW_ACT, f->cf_act);
if (f->cf_mask & FW_ATTR_POLICE)
- nla_put_data(msg, TCA_FW_POLICE, f->cf_police);
+ NLA_PUT_DATA(msg, TCA_FW_POLICE, f->cf_police);
if (f->cf_mask & FW_ATTR_INDEV)
- nla_put_string(msg, TCA_FW_INDEV, f->cf_indev);
+ NLA_PUT_STRING(msg, TCA_FW_INDEV, f->cf_indev);
- return msg;
+ return 0;
+
+nla_put_failure:
+ return -NLE_NOMEM;
}
/**
@@ -190,12 +144,8 @@ static struct nl_msg *fw_get_opts(struct rtnl_cls *cls)
int rtnl_fw_set_classid(struct rtnl_cls *cls, uint32_t classid)
{
- struct rtnl_fw *f;
+ struct rtnl_fw *f = rtnl_cls_data(cls);
- f = fw_alloc(cls);
- if (!f)
- return -NLE_NOMEM;
-
f->cf_classid = classid;
f->cf_mask |= FW_ATTR_CLASSID;
@@ -206,6 +156,7 @@ int rtnl_fw_set_classid(struct rtnl_cls *cls, uint32_t classid)
static struct rtnl_cls_ops fw_ops = {
.co_kind = "fw",
+ .co_size = sizeof(struct rtnl_fw),
.co_msg_parser = fw_msg_parser,
.co_free_data = fw_free_data,
.co_clone = fw_clone,
@@ -213,7 +164,6 @@ static struct rtnl_cls_ops fw_ops = {
.co_dump = {
[NL_DUMP_LINE] = fw_dump_line,
[NL_DUMP_DETAILS] = fw_dump_details,
- [NL_DUMP_STATS] = fw_dump_stats,
},
};
diff --git a/lib/route/cls/u32.c b/lib/route/cls/u32.c
index cf02cdf..46d502b 100644
--- a/lib/route/cls/u32.c
+++ b/lib/route/cls/u32.c
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
* Copyright (c) 2005-2006 Siemens AG Oesterreich
*/
@@ -40,19 +40,6 @@
#define U32_ATTR_INDEV 0x100
/** @endcond */
-static inline struct rtnl_u32 *u32_cls(struct rtnl_cls *cls)
-{
- return (struct rtnl_u32 *) cls->c_subdata;
-}
-
-static inline struct rtnl_u32 *u32_alloc(struct rtnl_cls *cls)
-{
- if (!cls->c_subdata)
- cls->c_subdata = calloc(1, sizeof(struct rtnl_u32));
-
- return u32_cls(cls);
-}
-
static inline struct tc_u32_sel *u32_selector(struct rtnl_u32 *u)
{
return (struct tc_u32_sel *) u->cu_selector->d_data;
@@ -79,18 +66,14 @@ static struct nla_policy u32_policy[TCA_U32_MAX+1] = {
static int u32_msg_parser(struct rtnl_cls *cls)
{
- int err;
+ struct rtnl_u32 *u = rtnl_cls_data(cls);
struct nlattr *tb[TCA_U32_MAX + 1];
- struct rtnl_u32 *u;
+ int err;
err = tca_parse(tb, TCA_U32_MAX, (struct rtnl_tca *) cls, u32_policy);
if (err < 0)
return err;
- u = u32_alloc(cls);
- if (!u)
- goto errout_nomem;
-
if (tb[TCA_U32_DIVISOR]) {
u->cu_divisor = nla_get_u32(tb[TCA_U32_DIVISOR]);
u->cu_mask |= U32_ATTR_DIVISOR;
@@ -170,57 +153,40 @@ errout:
static void u32_free_data(struct rtnl_cls *cls)
{
- struct rtnl_u32 *u = u32_cls(cls);
-
- if (!u)
- return;
+ struct rtnl_u32 *u = rtnl_cls_data(cls);
nl_data_free(u->cu_selector);
nl_data_free(u->cu_act);
nl_data_free(u->cu_police);
nl_data_free(u->cu_pcnt);
-
- free(cls->c_subdata);
}
static int u32_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
{
- struct rtnl_u32 *dst, *src = u32_cls(_src);
+ struct rtnl_u32 *dst = rtnl_cls_data(_dst);
+ struct rtnl_u32 *src = rtnl_cls_data(_src);
- if (!src)
- return 0;
-
- dst = u32_alloc(_dst);
- if (!dst)
+ if (src->cu_selector &&
+ !(dst->cu_selector = nl_data_clone(src->cu_selector)))
return -NLE_NOMEM;
- if (src->cu_selector)
- if (!(dst->cu_selector = nl_data_clone(src->cu_selector)))
- return -NLE_NOMEM;
-
- if (src->cu_act)
- if (!(dst->cu_act = nl_data_clone(src->cu_act)))
- return -NLE_NOMEM;
+ if (src->cu_act && !(dst->cu_act = nl_data_clone(src->cu_act)))
+ return -NLE_NOMEM;
- if (src->cu_police)
- if (!(dst->cu_police = nl_data_clone(src->cu_police)))
- return -NLE_NOMEM;
+ if (src->cu_police && !(dst->cu_police = nl_data_clone(src->cu_police)))
+ return -NLE_NOMEM;
- if (src->cu_pcnt)
- if (!(dst->cu_pcnt = nl_data_clone(src->cu_pcnt)))
- return -NLE_NOMEM;
+ if (src->cu_pcnt && !(dst->cu_pcnt = nl_data_clone(src->cu_pcnt)))
+ return -NLE_NOMEM;
return 0;
}
static void u32_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
{
- struct rtnl_u32 *u = u32_cls(cls);
+ struct rtnl_u32 *u = rtnl_cls_data(cls);
char buf[32];
- if (!u)
- return;
-
if (u->cu_mask & U32_ATTR_DIVISOR)
nl_dump(p, " divisor %u", u->cu_divisor);
else if (u->cu_mask & U32_ATTR_CLASSID)
@@ -289,12 +255,9 @@ static void print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel,
static void u32_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
{
- struct rtnl_u32 *u = u32_cls(cls);
+ struct rtnl_u32 *u = rtnl_cls_data(cls);
struct tc_u32_sel *s;
- if (!u)
- return;
-
if (!(u->cu_mask & U32_ATTR_SELECTOR)) {
nl_dump(p, "no-selector\n");
return;
@@ -328,10 +291,7 @@ static void u32_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
static void u32_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p)
{
- struct rtnl_u32 *u = u32_cls(cls);
-
- if (!u)
- return;
+ struct rtnl_u32 *u = rtnl_cls_data(cls);
if (u->cu_mask & U32_ATTR_PCNT) {
struct tc_u32_pcnt *pc = u->cu_pcnt->d_data;
@@ -342,44 +302,38 @@ static void u32_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p)
}
}
-static struct nl_msg *u32_get_opts(struct rtnl_cls *cls)
+static int u32_get_opts(struct rtnl_cls *cls, struct nl_msg *msg)
{
- struct rtnl_u32 *u;
- struct nl_msg *msg;
+ struct rtnl_u32 *u = rtnl_cls_data(cls);
- u = u32_cls(cls);
- if (!u)
- return NULL;
-
- msg = nlmsg_alloc();
- if (!msg)
- return NULL;
-
if (u->cu_mask & U32_ATTR_DIVISOR)
- nla_put_u32(msg, TCA_U32_DIVISOR, u->cu_divisor);
+ NLA_PUT_U32(msg, TCA_U32_DIVISOR, u->cu_divisor);
if (u->cu_mask & U32_ATTR_HASH)
- nla_put_u32(msg, TCA_U32_HASH, u->cu_hash);
+ NLA_PUT_U32(msg, TCA_U32_HASH, u->cu_hash);
if (u->cu_mask & U32_ATTR_CLASSID)
- nla_put_u32(msg, TCA_U32_CLASSID, u->cu_classid);
+ NLA_PUT_U32(msg, TCA_U32_CLASSID, u->cu_classid);
if (u->cu_mask & U32_ATTR_LINK)
- nla_put_u32(msg, TCA_U32_LINK, u->cu_link);
+ NLA_PUT_U32(msg, TCA_U32_LINK, u->cu_link);
if (u->cu_mask & U32_ATTR_SELECTOR)
- nla_put_data(msg, TCA_U32_SEL, u->cu_selector);
+ NLA_PUT_DATA(msg, TCA_U32_SEL, u->cu_selector);
if (u->cu_mask & U32_ATTR_ACTION)
- nla_put_data(msg, TCA_U32_ACT, u->cu_act);
+ NLA_PUT_DATA(msg, TCA_U32_ACT, u->cu_act);
if (u->cu_mask & U32_ATTR_POLICE)
- nla_put_data(msg, TCA_U32_POLICE, u->cu_police);
+ NLA_PUT_DATA(msg, TCA_U32_POLICE, u->cu_police);
if (u->cu_mask & U32_ATTR_INDEV)
- nla_put_string(msg, TCA_U32_INDEV, u->cu_indev);
+ NLA_PUT_STRING(msg, TCA_U32_INDEV, u->cu_indev);
- return msg;
+ return 0;
+
+nla_put_failure:
+ return -NLE_NOMEM;
}
/**
@@ -397,12 +351,8 @@ void rtnl_u32_set_handle(struct rtnl_cls *cls, int htid, int hash,
int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
{
- struct rtnl_u32 *u;
+ struct rtnl_u32 *u = rtnl_cls_data(cls);
- u = u32_alloc(cls);
- if (!u)
- return -NLE_NOMEM;
-
u->cu_classid = classid;
u->cu_mask |= U32_ATTR_CLASSID;
@@ -419,11 +369,7 @@ int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags)
{
struct tc_u32_sel *sel;
- struct rtnl_u32 *u;
-
- u = u32_alloc(cls);
- if (!u)
- return -NLE_NOMEM;
+ struct rtnl_u32 *u = rtnl_cls_data(cls);
sel = u32_selector_alloc(u);
if (!sel)
@@ -453,13 +399,9 @@ int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
int off, int offmask)
{
struct tc_u32_sel *sel;
- struct rtnl_u32 *u;
+ struct rtnl_u32 *u = rtnl_cls_data(cls);
int err;
- u = u32_alloc(cls);
- if (!u)
- return -NLE_NOMEM;
-
sel = u32_selector_alloc(u);
if (!sel)
return -NLE_NOMEM;
@@ -562,6 +504,7 @@ int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, struct in6_addr *addr,
static struct rtnl_cls_ops u32_ops = {
.co_kind = "u32",
+ .co_size = sizeof(struct rtnl_u32),
.co_msg_parser = u32_msg_parser,
.co_free_data = u32_free_data,
.co_clone = u32_clone,
diff --git a/lib/route/cls_obj.c b/lib/route/cls_obj.c
index 217b6d0..c8218c0 100644
--- a/lib/route/cls_obj.c
+++ b/lib/route/cls_obj.c
@@ -39,6 +39,8 @@ static void cls_free_data(struct nl_object *obj)
cops = rtnl_cls_lookup_ops(cls);
if (cops && cops->co_free_data)
cops->co_free_data(cls);
+
+ nl_data_free(cls->c_subdata);
}
static int cls_clone(struct nl_object *_dst, struct nl_object *_src)
@@ -52,6 +54,13 @@ static int cls_clone(struct nl_object *_dst, struct nl_object *_src)
if (err < 0)
goto errout;
+ if (src->c_subdata) {
+ if (!(dst->c_subdata = nl_data_clone(src->c_subdata))) {
+ err = -NLE_NOMEM;
+ goto errout;
+ }
+ }
+
cops = rtnl_cls_lookup_ops(src);
if (cops && cops->co_clone)
err = cops->co_clone(dst, src);
@@ -133,6 +142,11 @@ void rtnl_cls_set_ifindex(struct rtnl_cls *f, int ifindex)
tca_set_ifindex((struct rtnl_tca *) f, ifindex);
}
+int rtnl_cls_get_ifindex(struct rtnl_cls *cls)
+{
+ return cls->c_ifindex;
+}
+
void rtnl_cls_set_handle(struct rtnl_cls *f, uint32_t handle)
{
tca_set_handle((struct rtnl_tca *) f, handle);
@@ -143,14 +157,21 @@ void rtnl_cls_set_parent(struct rtnl_cls *f, uint32_t parent)
tca_set_parent((struct rtnl_tca *) f, parent);
}
-int rtnl_cls_set_kind(struct rtnl_cls *f, const char *kind)
+uint32_t rtnl_cls_get_parent(struct rtnl_cls *cls)
{
- tca_set_kind((struct rtnl_tca *) f, kind);
+ return cls->c_parent;
+}
+
+int rtnl_cls_set_kind(struct rtnl_cls *cls, const char *kind)
+{
+ if (cls->ce_mask & TCA_ATTR_KIND)
+ return -NLE_EXIST;
+
+ tca_set_kind((struct rtnl_tca *) cls, kind);
+
+ /* Force allocation of data */
+ rtnl_cls_data(cls);
- f->c_ops = __rtnl_cls_lookup_ops(kind);
- if (f->c_ops == NULL)
- return -NLE_OBJ_NOTFOUND;
-
return 0;
}
@@ -187,6 +208,32 @@ uint16_t rtnl_cls_get_protocol(struct rtnl_cls *cls)
return ETH_P_ALL;
}
+void *rtnl_cls_data(struct rtnl_cls *cls)
+{
+ if (!cls->c_subdata) {
+ struct rtnl_cls_ops *ops = cls->c_ops;
+
+ if (!ops) {
+ if (!cls->c_kind[0])
+ BUG();
+
+ ops = __rtnl_cls_lookup_ops(cls->c_kind);
+ if (ops == NULL)
+ return NULL;
+
+ cls->c_ops = ops;
+ }
+
+ if (!ops->co_size)
+ BUG();
+
+ if (!(cls->c_subdata = nl_data_alloc(NULL, ops->co_size)))
+ return NULL;
+ }
+
+ return nl_data_get(cls->c_subdata);
+}
+
/** @} */
struct nl_object_ops cls_obj_ops = {
diff --git a/src/Makefile b/src/Makefile
index 6a7bfd2..fb6ae9c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -17,6 +17,9 @@ LDFLAGS += -L../lib -lnl
CIN := $(wildcard nl-*.c) $(wildcard genl-*.c) $(wildcard nf-*.c)
TOOLS := $(CIN:%.c=%)
+CLS := $(wildcard cls/*.c)
+CLS_OBJ := $(CLS:%.c=%.o)
+
all: $(TOOLS)
$(TOOLS): utils.o
@@ -31,6 +34,7 @@ nl-rule-list: rule-utils.o rtnl-utils.o
nl-neightbl-list: rtnl-utils.o
nl-monitor: rtnl-utils.o
nl-tctree-list: rtnl-utils.o
+nl-cls-add nl-cls-delete nl-cls-list: rtnl-utils.o cls/utils.o $(CLS_OBJ)
genl-ctrl-list: ctrl-utils.o
diff --git a/src/cls/basic.c b/src/cls/basic.c
new file mode 100644
index 0000000..df1c112
--- /dev/null
+++ b/src/cls/basic.c
@@ -0,0 +1,90 @@
+/*
+ * src/cls/basic.c Basic Classifier
+ *
+ * This library 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 version 2 of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "utils.h"
+#include <netlink/route/cls/basic.h>
+#include <netlink/route/cls/ematch.h>
+
+static void print_usage(void)
+{
+ printf(
+"Usage: ... basic [OPTIONS]...\n"
+"\n"
+"Options\n"
+" -h, --help Show this help.\n"
+" -e, --ematch=MATCH Extended match (See --ematch help).\n"
+" -c, --classid=HANDLE Target class to classify matching packets to.\n"
+ );
+ exit(0);
+}
+
+static void basic_parse_argv(struct rtnl_cls *cls, int argc, char **argv)
+{
+ uint32_t classid;
+
+ for (;;) {
+ int c, optidx = 0, err;
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "ematch", 1, 0, 'e' },
+ { "classid", 1, 0, 'c' },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "he:c:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '?':
+ exit(NLE_INVAL);
+
+ case 'h':
+ print_usage();
+
+ case 'e':
+#if 0
+ if ((err = parse_ematch_syntax(optarg, &tree)) < 0)
+ fatal(err, "Error while parsing ematch: %s",
+ nl_geterror(err));
+
+ if ((err = rtnl_basic_set_ematch(cls, tree)) < 0)
+ fatal(err, "Unable to set ematch: %s",
+ nl_geterror(err));
+#endif
+ break;
+
+ case 'c':
+ if ((err = rtnl_tc_str2handle(optarg, &classid)) < 0)
+ fatal(err, "Invalid classid \"%s\": %s",
+ optarg, nl_geterror(err));
+
+ if ((err = rtnl_basic_set_classid(cls, classid)) < 0)
+ fatal(err, "Unable to set classid: %s",
+ nl_geterror(err));
+ break;
+ }
+ }
+}
+
+static struct cls_module basic_module = {
+ .name = "basic",
+ .parse_argv = basic_parse_argv,
+};
+
+static void __attribute__ ((constructor)) basic_init(void)
+{
+ register_cls_module(&basic_module);
+}
+
+static void __attribute__ ((destructor)) basic_exit(void)
+{
+ unregister_cls_module(&basic_module);
+}
diff --git a/src/cls/cgroup.c b/src/cls/cgroup.c
new file mode 100644
index 0000000..ad0392f
--- /dev/null
+++ b/src/cls/cgroup.c
@@ -0,0 +1,78 @@
+/*
+ * src/cls/cgroup.c Control Groups Classifier
+ *
+ * This library 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 version 2 of the License.
+ *
+ * Copyright (c) 2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "utils.h"
+#include <netlink/route/cls/cgroup.h>
+#include <netlink/route/cls/ematch.h>
+
+static void print_usage(void)
+{
+ printf(
+"Usage: ... cgroup [OPTIONS]...\n"
+"\n"
+"Options\n"
+" -h, --help Show this help.\n"
+" -e, --ematch=MATCH Extended match (See --ematch help).\n"
+" -c, --classid=HANDLE Target class to classify matching packets to.\n"
+ );
+ exit(0);
+}
+
+static void basic_parse_argv(struct rtnl_cls *cls, int argc, char **argv)
+{
+ for (;;) {
+ int c, optidx = 0;
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "ematch", 1, 0, 'e' },
+ { "classid", 1, 0, 'c' },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "he:c:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '?':
+ exit(NLE_INVAL);
+
+ case 'h':
+ print_usage();
+
+#if 0
+ case 'e':
+ if ((err = parse_ematch_syntax(optarg, &tree)) < 0)
+ fatal(err, "Error while parsing ematch: %s",
+ nl_geterror(err));
+
+ if ((err = rtnl_basic_set_ematch(cls, tree)) < 0)
+ fatal(err, "Unable to set ematch: %s",
+ nl_geterror(err));
+ break;
+#endif
+ }
+ }
+}
+
+static struct cls_module cgroup_module = {
+ .name = "cgroup",
+ .parse_argv = basic_parse_argv,
+};
+
+static void __init cgroup_init(void)
+{
+ register_cls_module(&cgroup_module);
+}
+
+static void __exit cgroup_exit(void)
+{
+ unregister_cls_module(&cgroup_module);
+}
diff --git a/src/cls/utils.c b/src/cls/utils.c
new file mode 100644
index 0000000..ef6603b
--- /dev/null
+++ b/src/cls/utils.c
@@ -0,0 +1,105 @@
+/*
+ * src/cls-utils.c Classifier Helpers
+ *
+ * This library 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 version 2 of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "utils.h"
+
+struct rtnl_cls *nlt_alloc_cls(void)
+{
+ struct rtnl_cls *cls;
+
+ cls = rtnl_cls_alloc();
+ if (!cls)
+ fatal(ENOMEM, "Unable to allocate classifier object");
+
+ return cls;
+}
+
+void parse_dev(struct rtnl_cls *cls, struct nl_cache *link_cache, char *arg)
+{
+ int ival;
+
+ if (!(ival = rtnl_link_name2i(link_cache, arg)))
+ fatal(ENOENT, "Link \"%s\" does not exist", arg);
+
+ rtnl_cls_set_ifindex(cls, ival);
+}
+
+void parse_prio(struct rtnl_cls *cls, char *arg)
+{
+ uint32_t prio = parse_u32(arg);
+ rtnl_cls_set_prio(cls, prio);
+}
+
+void parse_parent(struct rtnl_cls *cls, char *arg)
+{
+ uint32_t parent;
+ int err;
+
+ if ((err = rtnl_tc_str2handle(arg, &parent)) < 0)
+ fatal(err, "Unable to parse handle \"%s\": %s",
+ arg, nl_geterror(err));
+
+ rtnl_cls_set_parent(cls, parent);
+}
+
+void parse_handle(struct rtnl_cls *cls, char *arg)
+{
+ uint32_t handle;
+ int err;
+
+ if ((err = rtnl_tc_str2handle(arg, &handle)) < 0)
+ fatal(err, "Unable to parse handle \"%s\": %s",
+ arg, nl_geterror(err));
+
+ rtnl_cls_set_handle(cls, handle);
+}
+
+void parse_proto(struct rtnl_cls *cls, char *arg)
+{
+ int proto = nl_str2ether_proto(arg);
+ if (proto < 0)
+ fatal(proto, "Unable to parse protocol \"%s\": %s",
+ arg, nl_geterror(proto));
+ rtnl_cls_set_protocol(cls, proto);
+}
+
+static NL_LIST_HEAD(cls_modules);
+
+struct cls_module *lookup_cls_mod(struct rtnl_cls_ops *ops)
+{
+ struct cls_module *mod;
+
+ nl_list_for_each_entry(mod, &cls_modules, list) {
+ if (mod->ops == ops)
+ return mod;
+ }
+
+ return NULL;
+}
+
+void register_cls_module(struct cls_module *mod)
+{
+ struct rtnl_cls_ops *ops;
+
+ if (!(ops = __rtnl_cls_lookup_ops(mod->name)))
+ fatal(ENOENT, "Could not locate classifier module \"%s\"",
+ mod->name);
+
+ if (lookup_cls_mod(ops) != NULL)
+ fatal(EEXIST, "Duplicate classifier module registration.");
+
+ mod->ops = ops;
+ nl_list_add_tail(&mod->list, &cls_modules);
+}
+
+void unregister_cls_module(struct cls_module *mod)
+{
+ nl_list_del(&mod->list);
+}
diff --git a/src/cls/utils.h b/src/cls/utils.h
new file mode 100644
index 0000000..1a8ee9b
--- /dev/null
+++ b/src/cls/utils.h
@@ -0,0 +1,51 @@
+/*
+ * src/cls-utils.h Classifier Helpers
+ *
+ * This library 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 version 2 of the License.
+ *
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __CLS_UTILS_H_
+#define __CLS_UTILS_H_
+
+#include "../utils.h"
+#include <netlink/route/classifier-modules.h>
+#include <netlink/route/cls/ematch.h>
+
+struct cls_module
+{
+ const char * name;
+ struct rtnl_cls_ops * ops;
+ void (*parse_argv)(struct rtnl_cls *, int, char **);
+ struct nl_list_head list;
+};
+
+extern struct cls_module *lookup_cls_mod(struct rtnl_cls_ops *);
+extern void register_cls_module(struct cls_module *);
+extern void unregister_cls_module(struct cls_module *);
+
+struct ematch_module
+{
+ int kind;
+ struct rtnl_ematch_ops *ops;
+ void (*parse_argv)(struct rtnl_ematch *, int, char **);
+ struct nl_list_head list;
+};
+
+extern struct ematch_module *lookup_ematch_mod(struct rtnl_ematch_ops *);
+extern void register_ematch_module(struct ematch_module *);
+extern void unregister_ematch_module(struct ematch_module *);
+
+extern struct rtnl_cls *nlt_alloc_cls(void);
+extern void parse_dev(struct rtnl_cls *, struct nl_cache *, char *);
+extern void parse_prio(struct rtnl_cls *, char *);
+extern void parse_parent(struct rtnl_cls *, char *);
+extern void parse_handle(struct rtnl_cls *, char *);
+extern void parse_proto(struct rtnl_cls *, char *);
+
+extern int parse_ematch_syntax(const char *, struct rtnl_ematch_tree **);
+
+#endif
diff --git a/src/nl-cls-add.c b/src/nl-cls-add.c
new file mode 100644
index 0000000..997f02f
--- /dev/null
+++ b/src/nl-cls-add.c
@@ -0,0 +1,117 @@
+/*
+ * src/nl-cls-add.c Add classifier
+ *
+ * This library 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 version 2 of the License.
+ *
+ * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "cls/utils.h"
+
+static int quiet = 0;
+
+static void print_usage(void)
+{
+ printf(
+"Usage: nl-cls-add [OPTION]... [CLASSIFIER] TYPE [TYPE OPTIONS]...\n"
+"\n"
+"Options\n"
+" -q, --quiet Do not print informal notifications.\n"
+" -h, --help Show this help.\n"
+" -v, --version Show versioning information.\n"
+"\n"
+"Classifier Options\n"
+" -d, --dev=DEV Device the classifier should be assigned to.\n"
+" -p, --parent=HANDLE Parent QDisc\n"
+" --proto=PROTO Protocol (default=IPv4)\n"
+" --prio=NUM Priority (0..256)\n"
+" --id=HANDLE Unique identifier\n"
+ );
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct rtnl_cls *cls;
+ struct nl_cache *link_cache;
+ struct rtnl_cls_ops *ops;
+ struct cls_module *mod;
+ struct nl_dump_params dp = {
+ .dp_type = NL_DUMP_DETAILS,
+ .dp_fd = stdout,
+ };
+ char *kind;
+ int err, nlflags = NLM_F_CREATE;
+
+ sock = nlt_alloc_socket();
+ nlt_connect(sock, NETLINK_ROUTE);
+ link_cache = nlt_alloc_link_cache(sock);
+ cls = nlt_alloc_cls();
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_PROTO = 257,
+ ARG_PRIO = 258,
+ ARG_ID,
+ };
+ static struct option long_opts[] = {
+ { "quiet", 0, 0, 'q' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "dev", 1, 0, 'd' },
+ { "parent", 1, 0, 'p' },
+ { "proto", 1, 0, ARG_PROTO },
+ { "prio", 1, 0, ARG_PRIO },
+ { "id", 1, 0, ARG_ID },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "+qhva:d:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '?': exit(NLE_INVAL);
+ case 'q': quiet = 1; break;
+ case 'h': print_usage(); break;
+ case 'v': nlt_print_version(); break;
+ case 'd': parse_dev(cls, link_cache, optarg); break;
+ case 'p': parse_parent(cls, optarg); break;
+ case ARG_PRIO: parse_prio(cls, optarg); break;
+ case ARG_ID: parse_handle(cls, optarg); break;
+ case ARG_PROTO: parse_proto(cls, optarg); break;
+ }
+ }
+
+ if (optind >= argc) {
+ print_usage();
+ fatal(EINVAL, "Missing classifier type");
+ }
+
+ kind = argv[optind++];
+ if ((err = rtnl_cls_set_kind(cls, kind)) < 0)
+ fatal(ENOENT, "Unknown classifier type \"%s\".", kind);
+
+ ops = rtnl_cls_get_ops(cls);
+ if (!(mod = lookup_cls_mod(ops)))
+ fatal(ENOTSUP, "Classifier type \"%s\" not supported.", kind);
+
+ mod->parse_argv(cls, argc, argv);
+
+ printf("Adding ");
+ nl_object_dump(OBJ_CAST(cls), &dp);
+
+ if ((err = rtnl_cls_add(sock, cls, nlflags)) < 0)
+ fatal(err, "Unable to add classifier: %s", nl_geterror(err));
+
+ if (!quiet) {
+ printf("Added ");
+ nl_object_dump(OBJ_CAST(cls), &dp);
+ }
+
+ return 0;
+}
diff --git a/src/nl-cls-delete.c b/src/nl-cls-delete.c
new file mode 100644
index 0000000..cfdc170
--- /dev/null
+++ b/src/nl-cls-delete.c
@@ -0,0 +1,133 @@
+/*
+ * src/nl-cls-delete.c Delete Classifier
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "cls/utils.h"
+
+static int interactive = 0, default_yes = 0, quiet = 0;
+static int deleted = 0;
+static struct nl_sock *sock;
+
+static void print_usage(void)
+{
+ printf(
+ "Usage: nl-cls-list [OPTION]... [CLASSIFIER]\n"
+ "\n"
+ "Options\n"
+ " -i, --interactive Run interactively\n"
+ " --yes Set default answer to yes\n"
+ " -q, --quiet Do not print informal notifications\n"
+ " -h, --help Show this help\n"
+ " -v, --version Show versioning information\n"
+ "\n"
+ "Classifier Options\n"
+ " -d, --dev=DEV Device the classifier should be assigned to.\n"
+ " -p, --parent=HANDLE Parent qdisc/class\n"
+ " --proto=PROTO Protocol\n"
+ " --prio=NUM Priority (0..256)\n"
+ " --id=HANDLE Unique identifier\n"
+ );
+ exit(0);
+}
+
+static void delete_cb(struct nl_object *obj, void *arg)
+{
+ struct rtnl_cls *cls = (struct rtnl_cls *) obj;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ int err;
+
+ if (interactive && !nlt_confirm(obj, &params, default_yes))
+ return;
+
+ if ((err = rtnl_cls_delete(sock, cls, 0)) < 0)
+ fatal(err, "Unable to delete classifier: %s",
+ nl_geterror(err));
+
+ if (!quiet) {
+ printf("Deleted ");
+ nl_object_dump(obj, &params);
+ }
+
+ deleted++;
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_cache *link_cache, *cls_cache;
+ struct rtnl_cls *cls;
+ int nf = 0, err;
+
+ sock = nlt_alloc_socket();
+ nlt_connect(sock, NETLINK_ROUTE);
+ link_cache = nlt_alloc_link_cache(sock);
+ cls = nlt_alloc_cls();
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_PRIO = 257,
+ ARG_PROTO = 258,
+ ARG_ID,
+ ARG_YES,
+ };
+ static struct option long_opts[] = {
+ { "interactive", 0, 0, 'i' },
+ { "yes", 0, 0, ARG_YES },
+ { "quiet", 0, 0, 'q' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "dev", 1, 0, 'd' },
+ { "parent", 1, 0, 'p' },
+ { "proto", 1, 0, ARG_PROTO },
+ { "prio", 1, 0, ARG_PRIO },
+ { "id", 1, 0, ARG_ID },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "iqhvd:p:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'i': interactive = 1; break;
+ case ARG_YES: default_yes = 1; break;
+ case 'q': quiet = 1; break;
+ case 'h': print_usage(); break;
+ case 'v': nlt_print_version(); break;
+ case 'd': nf++; parse_dev(cls, link_cache, optarg); break;
+ case 'p': nf++; parse_parent(cls, optarg); break;
+ case ARG_PRIO: nf++; parse_prio(cls, optarg); break;
+ case ARG_ID: nf++; parse_handle(cls, optarg); break;
+ case ARG_PROTO: nf++; parse_proto(cls, optarg); break;
+ }
+ }
+
+ if (nf == 0 && !interactive && !default_yes) {
+ fprintf(stderr, "You attempted to delete all classifiers in "
+ "non-interactive mode, aborting.\n");
+ exit(0);
+ }
+
+ err = rtnl_cls_alloc_cache(sock, rtnl_cls_get_ifindex(cls),
+ rtnl_cls_get_parent(cls), &cls_cache);
+ if (err < 0)
+ fatal(err, "Unable to allocate classifier cache: %s",
+ nl_geterror(err));
+
+ nl_cache_foreach_filter(cls_cache, OBJ_CAST(cls), delete_cb, NULL);
+
+ if (!quiet)
+ printf("Deleted %d classifiers\n", deleted);
+
+ return 0;
+}
diff --git a/src/nl-cls-list.c b/src/nl-cls-list.c
new file mode 100644
index 0000000..9121d52
--- /dev/null
+++ b/src/nl-cls-list.c
@@ -0,0 +1,113 @@
+/*
+ * src/nl-cls-list.c List classifiers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "cls/utils.h"
+
+static struct nl_sock *sock;
+static struct rtnl_cls *cls;
+static struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+};
+
+static void print_usage(void)
+{
+ printf(
+ "Usage: nl-cls-list [OPTION]... [CLASSIFIER]\n"
+ "\n"
+ "Options\n"
+ " -f, --format=TYPE Output format { brief | details | stats }\n"
+ " -h, --help Show this help text.\n"
+ " -v, --version Show versioning information.\n"
+ "\n"
+ "Classifier Options\n"
+ " -d, --dev=DEV Device the classifier should be assigned to.\n"
+ " -p, --parent=HANDLE Parent qdisc/class\n"
+ " --proto=PROTO Protocol\n"
+ " --prio=NUM Priority\n"
+ " --id=NUM Identifier\n"
+ );
+ exit(0);
+}
+
+static void print_cls(struct nl_object *obj, void *arg)
+{
+ struct nl_cache *cls_cache;
+ int err, ifindex;
+
+ if (obj)
+ ifindex = rtnl_link_get_ifindex((struct rtnl_link *) obj);
+ else
+ ifindex = rtnl_cls_get_ifindex(cls);
+
+ err = rtnl_cls_alloc_cache(sock, ifindex, rtnl_cls_get_parent(cls),
+ &cls_cache);
+ if (err < 0)
+ fatal(err, "Unable to allocate classifier cache: %s",
+ nl_geterror(err));
+
+ nl_cache_dump_filter(cls_cache, &params, OBJ_CAST(cls));
+ nl_cache_free(cls_cache);
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_cache *link_cache;
+ int dev = 0;
+
+ params.dp_fd = stdout;
+ sock = nlt_alloc_socket();
+ nlt_connect(sock, NETLINK_ROUTE);
+ link_cache = nlt_alloc_link_cache(sock);
+ cls = nlt_alloc_cls();
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_PROTO = 257,
+ ARG_PRIO = 258,
+ ARG_ID,
+ };
+ static struct option long_opts[] = {
+ { "format", 1, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "dev", 1, 0, 'd' },
+ { "parent", 1, 0, 'p' },
+ { "proto", 1, 0, ARG_PROTO },
+ { "prio", 1, 0, ARG_PRIO },
+ { "id", 1, 0, ARG_ID },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "+f:qhva:d:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '?': exit(NLE_INVAL);
+ case 'f': params.dp_type = nlt_parse_dumptype(optarg); break;
+ case 'h': print_usage(); break;
+ case 'v': nlt_print_version(); break;
+ case 'd': dev = 1; parse_dev(cls, link_cache, optarg); break;
+ case 'p': parse_parent(cls, optarg); break;
+ case ARG_PRIO: parse_prio(cls, optarg); break;
+ case ARG_ID: parse_handle(cls, optarg); break;
+ case ARG_PROTO: parse_proto(cls, optarg); break;
+ }
+ }
+
+ if (!dev)
+ nl_cache_foreach(link_cache, print_cls, NULL);
+ else
+ print_cls(NULL, NULL);
+
+ return 0;
+}
diff --git a/src/nl-list-caches.c b/src/nl-list-caches.c
index e9e81c5..abd5ff3 100644
--- a/src/nl-list-caches.c
+++ b/src/nl-list-caches.c
@@ -9,8 +9,8 @@
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
*/
-#include "utils.h"
#include <netlink-local.h>
+#include "utils.h"
static void print_usage(void)
{
diff --git a/src/utils.h b/src/utils.h
index a5c94e6..69b6fdc 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -36,6 +36,7 @@
#include <netlink/route/qdisc.h>
#include <netlink/route/class.h>
#include <netlink/route/classifier.h>
+#include <netlink/route/cls/ematch.h>
#include <netlink/fib_lookup/lookup.h>
#include <netlink/fib_lookup/request.h>
#include <netlink/genl/genl.h>
@@ -43,6 +44,14 @@
#include <netlink/genl/mngt.h>
#include <netlink/netfilter/ct.h>
+#ifndef __init
+#define __init __attribute__((constructor))
+#endif
+
+#ifndef __exit
+#define __exit __attribute__((destructor))
+#endif
+
extern uint32_t parse_u32(const char *);
extern void nlt_print_version(void);
diff --git a/tests/Makefile b/tests/Makefile
index 168eeeb..8494eea 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -23,7 +23,7 @@ $(TOOLS): ../src/utils.o
test-%: test-%.c
@echo " LD $@"; \
- $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -lnl-genl
+ $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -lnl-genl -lnl-route
clean:
@echo " CLEAN src"; \