From ef858fb492dfe98e3ae194264fbc73649cf8493a Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 2 Sep 2009 18:31:14 +0200 Subject: - Reworked the classifier interface. - Added initial ematch support - Added support for the basic classifier - Added support for the cgroup classifier --- include/linux/pkt_cls.h | 78 +++++- include/netlink-types.h | 32 ++- include/netlink/attr.h | 10 + include/netlink/list.h | 5 + include/netlink/route/classifier-modules.h | 12 +- include/netlink/route/classifier.h | 6 +- include/netlink/route/cls/basic.h | 33 +++ include/netlink/route/cls/cgroup.h | 31 +++ include/netlink/route/cls/ematch.h | 73 +++++ include/netlink/route/cls/ematch/cmp.h | 31 +++ lib/Makefile | 1 + lib/route/classifier.c | 340 ------------------------ lib/route/cls.c | 341 ++++++++++++++++++++++++ lib/route/cls/basic.c | 211 +++++++++++++++ lib/route/cls/cgroup.c | 141 ++++++++++ lib/route/cls/ematch.c | 410 +++++++++++++++++++++++++++++ lib/route/cls/ematch/cmp.c | 116 ++++++++ lib/route/cls/ematch/container.c | 39 +++ lib/route/cls/fw.c | 96 ++----- lib/route/cls/u32.c | 127 +++------ lib/route/cls_obj.c | 59 ++++- src/Makefile | 4 + src/cls/basic.c | 90 +++++++ src/cls/cgroup.c | 78 ++++++ src/cls/utils.c | 105 ++++++++ src/cls/utils.h | 51 ++++ src/nl-cls-add.c | 117 ++++++++ src/nl-cls-delete.c | 133 ++++++++++ src/nl-cls-list.c | 113 ++++++++ src/nl-list-caches.c | 2 +- src/utils.h | 9 + tests/Makefile | 2 +- 32 files changed, 2371 insertions(+), 525 deletions(-) create mode 100644 include/netlink/route/cls/basic.h create mode 100644 include/netlink/route/cls/cgroup.h create mode 100644 include/netlink/route/cls/ematch.h create mode 100644 include/netlink/route/cls/ematch/cmp.h delete mode 100644 lib/route/classifier.c create mode 100644 lib/route/cls.c create mode 100644 lib/route/cls/basic.c create mode 100644 lib/route/cls/cgroup.c create mode 100644 lib/route/cls/ematch.c create mode 100644 lib/route/cls/ematch/cmp.c create mode 100644 lib/route/cls/ematch/container.c create mode 100644 src/cls/basic.c create mode 100644 src/cls/cgroup.c create mode 100644 src/cls/utils.c create mode 100644 src/cls/utils.h create mode 100644 src/nl-cls-add.c create mode 100644 src/nl-cls-delete.c create mode 100644 src/nl-cls-list.c 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 #include /* 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 + * Copyright (c) 2003-2009 Thomas Graf */ #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 + * Copyright (c) 2003-2009 Thomas Graf */ #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 + */ + +#ifndef NETLINK_BASIC_H_ +#define NETLINK_BASIC_H_ + +#include + +#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 + */ + +#ifndef NETLINK_CLS_CGROUP_H_ +#define NETLINK_CLS_CGROUP_H_ + +#include +#include + +#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 + */ + +#ifndef NETLINK_CLS_EMATCH_H_ +#define NETLINK_CLS_EMATCH_H_ + +#include +#include +#include + +#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 + */ + +#ifndef NETLINK_CLS_EMATCH_CMP_H_ +#define NETLINK_CLS_EMATCH_CMP_H_ + +#include +#include + +#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/classifier.c deleted file mode 100644 index 4b5ce9d..0000000 --- a/lib/route/classifier.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * lib/route/classifier.c 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) 2003-2008 Thomas Graf - */ - -/** - * @ingroup tc - * @defgroup cls Classifiers - * - * @par Classifier Identification - * - protocol - * - priority - * - parent - * - interface - * - kind - * - handle - * - * @{ - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -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; - - cls = rtnl_cls_alloc(); - if (!cls) { - err = -NLE_NOMEM; - goto errout; - } - cls->ce_msgtype = nlh->nlmsg_type; - - err = tca_msg_parser(nlh, (struct rtnl_tca *) cls); - if (err < 0) - goto errout_free; - - cls->c_prio = TC_H_MAJ(cls->c_info) >> 16; - 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; - } - - err = pp->pp_cb((struct nl_object *) cls, pp); -errout_free: - rtnl_cls_put(cls); -errout: - return err; -} - -static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk) -{ - struct tcmsg tchdr = { - .tcm_family = AF_UNSPEC, - .tcm_ifindex = cache->c_iarg1, - .tcm_parent = cache->c_iarg2, - }; - - return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr, - sizeof(tchdr)); -} - - -static int cls_build(struct rtnl_cls *cls, int type, int flags, - struct nl_msg **result) -{ - struct rtnl_cls_ops *cops; - int err, prio, proto; - struct tcmsg *tchdr; - - err = tca_build_msg((struct rtnl_tca *) cls, type, flags, result); - if (err < 0) - return err; - - 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)), - - 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; - } - } - - return 0; -errout: - nlmsg_free(*result); - return err; -} - -/** - * @name Classifier Addition/Modification/Deletion - * @{ - */ - -/** - * Build a netlink message to add a new classifier - * @arg cls classifier to add - * @arg flags additional netlink message flags - * @arg result Pointer to store resulting message. - * - * Builds a new netlink message requesting an addition of a classifier - * The netlink message header isn't fully equipped with all relevant - * fields and must be sent out via nl_send_auto_complete() or - * supplemented as needed. \a classifier must contain the attributes of - * the new classifier set via \c rtnl_cls_set_* functions. \a opts - * may point to the clsasifier specific options. - * - * @return 0 on success or a negative error code. - */ -int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags, - struct nl_msg **result) -{ - return cls_build(cls, RTM_NEWTFILTER, NLM_F_CREATE | flags, result); -} - -/** - * Add a new classifier - * @arg sk Netlink socket. - * @arg cls classifier to add - * @arg flags additional netlink message flags - * - * Builds a netlink message by calling rtnl_cls_build_add_request(), - * sends the request to the kernel and waits for the next ACK to be - * received and thus blocks until the request has been processed. - * - * @return 0 on sucess or a negative error if an error occured. - */ -int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags) -{ - struct nl_msg *msg; - int err; - - if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0) - return err; - - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return nl_wait_for_ack(sk); -} - -/** - * Build a netlink message to change classifier attributes - * @arg cls classifier to change - * @arg flags additional netlink message flags - * @arg result Pointer to store resulting message. - * - * Builds a new netlink message requesting a change of a neigh - * attributes. The netlink message header isn't fully equipped with - * all relevant fields and must thus be sent out via nl_send_auto_complete() - * or supplemented as needed. - * - * @return 0 on success or a negative error code. - */ -int rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags, - struct nl_msg **result) -{ - return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags, result); -} - -/** - * Change a classifier - * @arg sk Netlink socket. - * @arg cls classifier to change - * @arg flags additional netlink message flags - * - * Builds a netlink message by calling rtnl_cls_build_change_request(), - * sends the request to the kernel and waits for the next ACK to be - * received and thus blocks until the request has been processed. - * - * @return 0 on sucess or a negative error if an error occured. - */ -int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags) -{ - struct nl_msg *msg; - int err; - - if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0) - return err; - - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return nl_wait_for_ack(sk); -} - -/** - * Build a netlink request message to delete a classifier - * @arg cls classifier to delete - * @arg flags additional netlink message flags - * @arg result Pointer to store resulting message. - * - * Builds a new netlink message requesting a deletion of a classifier. - * The netlink message header isn't fully equipped with all relevant - * fields and must thus be sent out via nl_send_auto_complete() - * or supplemented as needed. - * - * @return 0 on success or a negative error code. - */ -int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags, - struct nl_msg **result) -{ - return cls_build(cls, RTM_DELTFILTER, flags, result); -} - - -/** - * Delete a classifier - * @arg sk Netlink socket. - * @arg cls classifier to delete - * @arg flags additional netlink message flags - * - * Builds a netlink message by calling rtnl_cls_build_delete_request(), - * sends the request to the kernel and waits for the next ACK to be - * received and thus blocks until the request has been processed. - * - * @return 0 on sucess or a negative error if an error occured. - */ -int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags) -{ - struct nl_msg *msg; - int err; - - if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0) - return err; - - err = nl_send_auto_complete(sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return nl_wait_for_ack(sk); -} - -/** @} */ - -/** - * @name Cache Management - * @{ - */ - -/** - * Build a classifier cache including all classifiers attached to the - * specified class/qdisc on eht specified interface. - * @arg sk Netlink socket. - * @arg ifindex interface index of the link the classes are - * attached to. - * @arg parent parent qdisc/class - * @arg result Pointer to store resulting cache. - * - * Allocates a new cache, initializes it properly and updates it to - * include all classes attached to the specified interface. - * - * @note The caller is responsible for destroying and freeing the - * cache after using it. - * @return 0 on success or a negative error code. - */ -int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent, struct nl_cache **result) -{ - struct nl_cache * cache; - int err; - - if (!(cache = nl_cache_alloc(&rtnl_cls_ops))) - return -NLE_NOMEM; - - cache->c_iarg1 = ifindex; - cache->c_iarg2 = parent; - - if (sk && (err = nl_cache_refill(sk, cache)) < 0) { - nl_cache_free(cache); - return err; - } - - *result = cache; - return 0; -} - -/** @} */ - -static struct nl_cache_ops rtnl_cls_ops = { - .co_name = "route/cls", - .co_hdrsize = sizeof(struct tcmsg), - .co_msgtypes = { - { RTM_NEWTFILTER, NL_ACT_NEW, "new" }, - { RTM_DELTFILTER, NL_ACT_DEL, "del" }, - { RTM_GETTFILTER, NL_ACT_GET, "get" }, - END_OF_MSGTYPES_LIST, - }, - .co_protocol = NETLINK_ROUTE, - .co_request_update = cls_request_update, - .co_msg_parser = cls_msg_parser, - .co_obj_ops = &cls_obj_ops, -}; - -static void __init cls_init(void) -{ - nl_cache_mngt_register(&rtnl_cls_ops); -} - -static void __exit cls_exit(void) -{ - nl_cache_mngt_unregister(&rtnl_cls_ops); -} - -/** @} */ diff --git a/lib/route/cls.c b/lib/route/cls.c new file mode 100644 index 0000000..cbf0345 --- /dev/null +++ b/lib/route/cls.c @@ -0,0 +1,341 @@ +/* + * lib/route/classifier.c 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) 2003-2009 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup cls Classifiers + * + * @par Classifier Identification + * - protocol + * - priority + * - parent + * - interface + * - kind + * - handle + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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) +{ + struct rtnl_cls_ops *cops; + struct rtnl_cls *cls; + int err; + + cls = rtnl_cls_alloc(); + if (!cls) { + err = -NLE_NOMEM; + goto errout; + } + cls->ce_msgtype = nlh->nlmsg_type; + + err = tca_msg_parser(nlh, (struct rtnl_tca *) cls); + if (err < 0) + goto errout_free; + + cls->c_prio = TC_H_MAJ(cls->c_info) >> 16; + 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)) < 0) + goto errout_free; + + err = pp->pp_cb((struct nl_object *) cls, pp); +errout_free: + rtnl_cls_put(cls); +errout: + return err; +} + +static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = cache->c_iarg1, + .tcm_parent = cache->c_iarg2, + }; + + return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr, + sizeof(tchdr)); +} + + +static int cls_build(struct rtnl_cls *cls, int type, int flags, + struct nl_msg **result) +{ + struct rtnl_cls_ops *cops; + int err, prio, proto; + struct tcmsg *tchdr; + + err = tca_build_msg((struct rtnl_tca *) cls, type, flags, result); + if (err < 0) + return err; + + 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)); + + cops = rtnl_cls_lookup_ops(cls); + if (cops && cops->co_get_opts) { + struct nl_msg *opts; + + 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; +errout: + nlmsg_free(*result); + return err; +} + +/** + * @name Classifier Addition/Modification/Deletion + * @{ + */ + +/** + * Build a netlink message to add a new classifier + * @arg cls classifier to add + * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting an addition of a classifier + * The netlink message header isn't fully equipped with all relevant + * fields and must be sent out via nl_send_auto_complete() or + * supplemented as needed. \a classifier must contain the attributes of + * the new classifier set via \c rtnl_cls_set_* functions. \a opts + * may point to the clsasifier specific options. + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags, + struct nl_msg **result) +{ + return cls_build(cls, RTM_NEWTFILTER, NLM_F_CREATE | flags, result); +} + +/** + * Add a new classifier + * @arg sk Netlink socket. + * @arg cls classifier to add + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_cls_build_add_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(sk); +} + +/** + * Build a netlink message to change classifier attributes + * @arg cls classifier to change + * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a change of a neigh + * attributes. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags, + struct nl_msg **result) +{ + return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags, result); +} + +/** + * Change a classifier + * @arg sk Netlink socket. + * @arg cls classifier to change + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_cls_build_change_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(sk); +} + +/** + * Build a netlink request message to delete a classifier + * @arg cls classifier to delete + * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a deletion of a classifier. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags, + struct nl_msg **result) +{ + return cls_build(cls, RTM_DELTFILTER, flags, result); +} + + +/** + * Delete a classifier + * @arg sk Netlink socket. + * @arg cls classifier to delete + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_cls_build_delete_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a classifier cache including all classifiers attached to the + * specified class/qdisc on eht specified interface. + * @arg sk Netlink socket. + * @arg ifindex interface index of the link the classes are + * attached to. + * @arg parent parent qdisc/class + * @arg result Pointer to store resulting cache. + * + * Allocates a new cache, initializes it properly and updates it to + * include all classes attached to the specified interface. + * + * @note The caller is responsible for destroying and freeing the + * cache after using it. + * @return 0 on success or a negative error code. + */ +int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent, struct nl_cache **result) +{ + struct nl_cache * cache; + int err; + + if (!(cache = nl_cache_alloc(&rtnl_cls_ops))) + return -NLE_NOMEM; + + cache->c_iarg1 = ifindex; + cache->c_iarg2 = parent; + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** @} */ + +static struct nl_cache_ops rtnl_cls_ops = { + .co_name = "route/cls", + .co_hdrsize = sizeof(struct tcmsg), + .co_msgtypes = { + { RTM_NEWTFILTER, NL_ACT_NEW, "new" }, + { RTM_DELTFILTER, NL_ACT_DEL, "del" }, + { RTM_GETTFILTER, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = cls_request_update, + .co_msg_parser = cls_msg_parser, + .co_obj_ops = &cls_obj_ops, +}; + +static void __init cls_init(void) +{ + nl_cache_mngt_register(&rtnl_cls_ops); +} + +static void __exit cls_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_cls_ops); +} + +/** @} */ 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 + */ + +/** + * @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 +#include +#include +#include +#include +#include +#include + +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 + */ + +/** + * @ingroup cls_api + * @defgroup cgroup Control Groups Classifier + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @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 + */ + +/** + * @ingroup cls + * @defgroup ematch Extended Match + * + * @{ + */ + +#include +#include +#include +#include +#include +#include + +/** + * @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 + */ + +/** + * @ingroup ematch + * @defgroup em_cmp Simple packet data comparison + * + * @{ + */ + +#include +#include +#include +#include +#include + +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 + */ + +#include +#include +#include +#include + +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 + * Copyright (c) 2003-2009 Thomas Graf * Copyright (c) 2006 Petr Gotthard * 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 + * Copyright (c) 2003-2009 Thomas Graf * Copyright (c) 2005-2006 Petr Gotthard * 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 + */ + +#include "utils.h" +#include +#include + +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 + */ + +#include "utils.h" +#include +#include + +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 + */ + +#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 + */ + +#ifndef __CLS_UTILS_H_ +#define __CLS_UTILS_H_ + +#include "../utils.h" +#include +#include + +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 + */ + +#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 + */ + +#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, ¶ms, 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, ¶ms); + } + + 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 + */ + +#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, ¶ms, 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 */ -#include "utils.h" #include +#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 #include #include +#include #include #include #include @@ -43,6 +44,14 @@ #include #include +#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"; \ -- cgit v0.12