]> granicus.if.org Git - libnl/commitdiff
- Reworked the classifier interface.
authorThomas Graf <tgr@lsx.localdomain>
Wed, 2 Sep 2009 16:31:14 +0000 (18:31 +0200)
committerThomas Graf <tgr@lsx.localdomain>
Wed, 2 Sep 2009 16:31:14 +0000 (18:31 +0200)
- Added initial ematch support
- Added support for the basic classifier
- Added support for the cgroup classifier

31 files changed:
include/linux/pkt_cls.h
include/netlink-types.h
include/netlink/attr.h
include/netlink/list.h
include/netlink/route/classifier-modules.h
include/netlink/route/classifier.h
include/netlink/route/cls/basic.h [new file with mode: 0644]
include/netlink/route/cls/cgroup.h [new file with mode: 0644]
include/netlink/route/cls/ematch.h [new file with mode: 0644]
include/netlink/route/cls/ematch/cmp.h [new file with mode: 0644]
lib/Makefile
lib/route/cls.c [moved from lib/route/classifier.c with 95% similarity]
lib/route/cls/basic.c [new file with mode: 0644]
lib/route/cls/cgroup.c [new file with mode: 0644]
lib/route/cls/ematch.c [new file with mode: 0644]
lib/route/cls/ematch/cmp.c [new file with mode: 0644]
lib/route/cls/ematch/container.c [new file with mode: 0644]
lib/route/cls/fw.c
lib/route/cls/u32.c
lib/route/cls_obj.c
src/Makefile
src/cls/basic.c [new file with mode: 0644]
src/cls/cgroup.c [new file with mode: 0644]
src/cls/utils.c [new file with mode: 0644]
src/cls/utils.h [new file with mode: 0644]
src/nl-cls-add.c [new file with mode: 0644]
src/nl-cls-delete.c [new file with mode: 0644]
src/nl-cls-list.c [new file with mode: 0644]
src/nl-list-caches.c
src/utils.h
tests/Makefile

index 30b8571e6b34c25b2311291ad460e2d1754e7862..3c842edff388c90ef76bab969322cddfa7e076d1 100644 (file)
@@ -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
 {
index 263e5d713ca86a77541f81f60f76be9850f3bf1f..ace5ddec3c1f842165d36856963f8783c5f19452 100644 (file)
@@ -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;
index b3a350b58adf07d12c61d0c5bb669f81d16ec819..8479c2337d41947b65dde62d918ab0df1afb60e0 100644 (file)
@@ -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))
+
 /** @} */
 
 /**
index c6876a77f76c287cc3fbe37ec7c902ccddd517c0..28712edad212962dc55aff4cb7edb05c0be97030 100644 (file)
@@ -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,
index 02715ed9433330017e9dc1593ae0e8f676b9b7a8..35cb06e46a1f9d297f5c30025c236984ef29a989 100644 (file)
@@ -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
index b43400012b002ee177362edc42e6168f0a71013f..d9c3d211ab38b885019c31e0829ddc9c73bbaaf5 100644 (file)
@@ -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 (file)
index 0000000..7003124
--- /dev/null
@@ -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 (file)
index 0000000..7b0e3d3
--- /dev/null
@@ -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 (file)
index 0000000..c4292bf
--- /dev/null
@@ -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 (file)
index 0000000..b4ad03a
--- /dev/null
@@ -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
index 80dd1ca95b5d71550199de5866347dfaf596b4be..a811942d49bdc057e71debbb2e8606de1a2f1aca 100644 (file)
@@ -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)
similarity index 95%
rename from lib/route/classifier.c
rename to lib/route/cls.c
index 4b5ce9d2ee0889c52ef1d53daa48b58d69b3ecd8..cbf034563b6034869c304ea9fc5e9b77cc6ad8a7 100644 (file)
@@ -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 (file)
index 0000000..1460b72
--- /dev/null
@@ -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 (file)
index 0000000..e5f38b8
--- /dev/null
@@ -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 (file)
index 0000000..cb77b16
--- /dev/null
@@ -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 (file)
index 0000000..ec25320
--- /dev/null
@@ -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 (file)
index 0000000..54d836f
--- /dev/null
@@ -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);
+}
index d18d3f8f6e0200967eedda0ff4e7e9f9fbf69472..8cf25b978ca4f24f2b5eb75090592f529950c59f 100644 (file)
@@ -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
  */
 #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,
        },
 };
 
index cf02cdfe74b255cfe8ab0d69166980a91f372ff7..46d502bc8233249d2f46d260c51038def6617eaf 100644 (file)
@@ -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
  */
 #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,
index 217b6d0810665e10976ef1760da11dfe9b9db58e..c8218c0708ec4df45f953c20906aa3288f3a6afa 100644 (file)
@@ -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 = {
index 6a7bfd2bfe10cb361ac9948df4a03b4a0f44d46e..fb6ae9c1096082e31d559670de94ebf38d6ba477 100644 (file)
@@ -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 (file)
index 0000000..df1c112
--- /dev/null
@@ -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 (file)
index 0000000..ad0392f
--- /dev/null
@@ -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 (file)
index 0000000..ef6603b
--- /dev/null
@@ -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 (file)
index 0000000..1a8ee9b
--- /dev/null
@@ -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 (file)
index 0000000..997f02f
--- /dev/null
@@ -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 (file)
index 0000000..cfdc170
--- /dev/null
@@ -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 (file)
index 0000000..9121d52
--- /dev/null
@@ -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;
+}
index e9e81c5b744fb09aaf974df94154877013cb8bcb..abd5ff3f6facb521029defcbf1487e7250c5fead 100644 (file)
@@ -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)
 {
index a5c94e6e8b298f768752dc62f86a49ee949c7d73..69b6fdcbb59d6514022bb9f074d527c96b4221b6 100644 (file)
@@ -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>
 #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);
index 168eeeb695931c6d2cdddd110adcbd76f9c03bf9..8494eea0d5a79f5fd9c3b8fbc8559ff1f9940997 100644 (file)
@@ -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"; \