* (c) 2011 Volkswagen Group Research
* Authors: Michal Sojka <sojkam1@fel.cvut.cz>
* Pavel Pisa <pisa@cmp.felk.cvut.cz>
- * Rostislav Lisovy <lisovy@kormus.cz>
+ * Rostislav Lisovy <lisovy@gmail.cz>
* Funded by: Volkswagen Group Research
*
* Some function descriptions are heavily inspired by article "Linux Network
#include <linux/rcupdate.h>
#include <linux/can.h>
-#ifndef CAN_SFF_ID_BITS
- #define CAN_SFF_ID_BITS 11
- #define CAN_EFF_ID_BITS 29
-#endif
-
-#define SFF_BITMAP 1
-
-/* Definition of Netlink messages */
+/* Definition of Netlink message parts */
enum {
TCA_CANFLTR_UNSPEC,
TCA_CANFLTR_CLASSID,
- TCA_CANFLTR_RULES, /* Array of can_filter structs; We are able
+ TCA_CANFLTR_RULES, /* Array of can_filter structs; We are able
to determine the length after receiving */
__TCA_CANFLTR_MAX
};
#define TCA_CANFLTR_MAX (__TCA_CANFLTR_MAX - 1)
static const struct nla_policy canfltr_policy[TCA_CANFLTR_MAX + 1] = {
- [TCA_CANFLTR_CLASSID] = { .type = NLA_U32 },
- /* FIXME Be aware of possible problems with 64bit kernel and
- 32bit userspace etc. */
- [TCA_CANFLTR_RULES] = { /*.len = (sizeof(struct can_filter))*/ } /* FIXME */
+ [TCA_CANFLTR_CLASSID] = { .type = NLA_U32 }, /* Be aware of possible
+ problems with 64bit kernel and
+ 32bit userspace etc. */
+ [TCA_CANFLTR_RULES] = { .type = NLA_NESTED }
};
struct canfltr_rules {
- struct can_filter *rules_raw; /* Raw rules copied from netlink message;
- Used for sending information to userspace
- (when 'tc filter show' is invoked) AND
- when matching EFF frames*/
-#ifdef SFF_BITMAP
- DECLARE_BITMAP(match_sff, (1 << CAN_SFF_ID_BITS)); /* For each SFF Can
+ struct can_filter *rules_raw; /* Raw rules copied from netlink
+ message; Used for sending information
+ to userspace (when 'tc filter show' is
+ invoked) AND when matching EFF frames*/
+ DECLARE_BITMAP(match_sff, (1 << CAN_SFF_ID_BITS)); /* For each SFF CAN
ID (11 bit) there is one record in this
bitfield */
-#endif
- int inv_match_en; /* Inverted match flag */
int rules_count;
int eff_rules_count;
int sff_rules_count;
struct canfltr_state {
u32 handle;
- struct canfltr_rules *rules; /* All rules necessary for classification */
+ struct canfltr_rules *rules; /* All rules necessary for
+ classification */
struct tcf_result res; /* Class ID (flow id) the instance
of a filter is bound to */
struct list_head link;
* ----------------------------------------------------------------------------
*/
-static void canfltr_sff_match_add(struct canfltr_rules *rls, u32 can_id, u32 can_mask)
+static void canfltr_sff_match_add(struct canfltr_rules *rls,
+ u32 can_id, u32 can_mask)
{
-#ifdef SFF_BITMAP
int i;
/* Limit can_mask and can_id to SFF range to
}
/* individual frame filter */
- /* Add record (set bit to 1) for each ID that conforms particular rule */
+ /* Add record (set bit to 1) for each ID that
+ conforms particular rule */
for (i = 0; i < (1 << CAN_SFF_ID_BITS); i++) {
if ((i & can_mask) == can_id)
set_bit(i, rls->match_sff);
}
-#endif
}
-/*
- * Extracts Can ID out of the sk_buff structure.
+/**
+ * canfltr_get_id() - Extracts Can ID out of the sk_buff structure.
*/
static canid_t canfltr_get_id(struct sk_buff *skb)
{
return cf->can_id;
}
-/*
- * Performs the classification. Iterates over all instances of filter,
- * checking for CAN ID match.
+/**
+ * canfltr_classify() - Performs the classification.
*
* @skb: Socket buffer
* @tp:
* @res: Is used for setting Class ID as a result of classification
*
+ * Iterates over all instances of filter, checking for CAN ID match.
+ *
* Returns value relevant for policing. Used return values:
* TC_POLICE_OK if succesfully classified (without regard to policing rules)
* TC_POLICE_UNSPEC if no matching rule was found
*/
-static int canfltr_classify(struct sk_buff *skb, struct tcf_proto *tp,
+static int canfltr_classify(struct sk_buff *skb, const struct tcf_proto *tp,
struct tcf_result *res)
{
struct canfltr_head *head = (struct canfltr_head *)tp->root;
can_id &= CAN_EFF_MASK;
for (i = 0; i < r->eff_rules_count; i++) {
- /* XXX: Why aren't these & performed "off-line"? */
- if ((r->rules_raw[i].can_id & r->rules_raw[i].can_mask & CAN_EFF_MASK) ==
- (can_id & r->rules_raw[i].can_mask & CAN_EFF_MASK)) {
+ if (!(((r->rules_raw[i].can_id ^ can_id) &
+ r->rules_raw[i].can_mask) & CAN_EFF_MASK)) {
match = true;
break;
}
}
- } else {
+ } else { /* SFF */
can_id &= CAN_SFF_MASK;
-#ifdef SFF_BITMAP
match = test_bit(can_id, r->match_sff);
-#else
- for (i = r->eff_rules_count; i < r->eff_rules_count + r->sff_rules_count; i++) {
- if ((r->rules_raw[i].can_id & r->rules_raw[i].can_mask & CAN_SFF_MASK) == /* XXX: dtto */
- (can_id & r->rules_raw[i].can_mask & CAN_SFF_MASK)) {
- match = true;
- break;
- }
- }
-#endif
}
if (match) {
return TC_POLICE_UNSPEC;
}
-/*
- * Looks up a filter element by its handle and returns the internal
- * filter ID (i.e. pointer)
+/**
+ * canfltr_get() - Looks up a filter element by its handle and returns the
+ * internal filter ID (i.e. pointer)
*/
static unsigned long canfltr_get(struct tcf_proto *tp, u32 handle)
{
return 0UL;
}
-/*
- * Is invoked when a filter element previously referenced
+/**
+ * canfltr_put() - Is invoked when a filter element previously referenced
* with get() is no longer used
*/
static void canfltr_put(struct tcf_proto *tp, unsigned long f)
{
}
+/**
+ * canfltr_gen_handle() - Generate handle for newly created filter
+ *
+ * This code is heavily inspired by handle generator in cls_basic.c
+ */
static unsigned int canfltr_gen_handle(struct tcf_proto *tp)
{
struct canfltr_head *head = (struct canfltr_head *)tp->root;
- int i = 0xFFFF;
+ unsigned int i = 0x80000000;
- while (i-- > 0) {
- u32 h;
+ do {
+ if (++head->hgenerator == 0x7FFFFFFF)
+ head->hgenerator = 1;
+ } while (--i > 0 && canfltr_get(tp, head->hgenerator));
- head->hgenerator += 0x10000;
- if (head->hgenerator == 0)
- head->hgenerator = 0x10000;
+ if (i == 0)
+ return 0;
- h = head->hgenerator;
- if (canfltr_get(tp, h) == 0)
- return h;
- }
- return 0;
+ return head->hgenerator;
}
static void canfltr_rules_free_rcu(struct rcu_head *rcu)
int i;
rules_tmp = kzalloc(sizeof(*rules_tmp), GFP_KERNEL);
- if (rules_tmp == NULL)
- return -ENOBUFS; /* XXX: Why not -ENOMEM? */
+ if (!rules_tmp)
+ return -ENOBUFS;
err = -EINVAL;
if (tb[TCA_CANFLTR_CLASSID] == NULL)
canfltr_nl_rules = nla_data(tb[TCA_CANFLTR_RULES]);
rules_tmp->sff_rules_count = 0;
rules_tmp->eff_rules_count = 0;
- rules_tmp->rules_count =
- (nla_len(tb[TCA_CANFLTR_RULES]) / sizeof(struct can_filter));
+ rules_tmp->rules_count = (nla_len(tb[TCA_CANFLTR_RULES]) /
+ sizeof(struct can_filter));
- rules_tmp->rules_raw =
- kzalloc(sizeof(struct can_filter) * rules_tmp->rules_count, GFP_KERNEL);
+ rules_tmp->rules_raw = kzalloc(sizeof(struct can_filter) *
+ rules_tmp->rules_count, GFP_KERNEL);
err = -ENOMEM;
if (rules_tmp->rules_raw == NULL)
goto errout;
- /* We need two for() loops for copying rules into two contiguous areas in rules_raw */
+ /* We need two for() loops for copying rules into
+ two contiguous areas in rules_raw */
/* Process EFF frame rules*/
for (i = 0; i < rules_tmp->rules_count; i++) {
if ((canfltr_nl_rules[i].can_id & CAN_EFF_FLAG) &&
(canfltr_nl_rules[i].can_mask & CAN_EFF_FLAG)) {
- memcpy(rules_tmp->rules_raw + rules_tmp->eff_rules_count,
- &canfltr_nl_rules[i], sizeof(struct can_filter));
+ memcpy(rules_tmp->rules_raw +
+ rules_tmp->eff_rules_count,
+ &canfltr_nl_rules[i],
+ sizeof(struct can_filter));
rules_tmp->eff_rules_count++;
} else {
continue;
(canfltr_nl_rules[i].can_mask & CAN_EFF_FLAG)) {
continue;
} else {
- memcpy(rules_tmp->rules_raw + rules_tmp->eff_rules_count +
+ memcpy(rules_tmp->rules_raw +
+ rules_tmp->eff_rules_count +
rules_tmp->sff_rules_count,
- &canfltr_nl_rules[i], sizeof(struct can_filter));
+ &canfltr_nl_rules[i],
+ sizeof(struct can_filter));
rules_tmp->sff_rules_count++;
- canfltr_sff_match_add(rules_tmp, canfltr_nl_rules[i].can_id,
- canfltr_nl_rules[i].can_mask);
+ canfltr_sff_match_add(rules_tmp,
+ canfltr_nl_rules[i].can_id,
+ canfltr_nl_rules[i].can_mask);
}
}
}
- if (f->rules == NULL) { /* Setting parameters for newly created filter */
+ /* Setting parameters for newly created filter */
+ if (f->rules == NULL) {
rcu_assign_pointer(f->rules, rules_tmp);
} else { /* Changing existing filter */
struct canfltr_rules *rules_old;
return err;
}
-/*
- * Called for changing properties of an existing filter or after addition
- * of a new filter to a class (by calling bind_tcf which binds an instance
- * of a filter to the class).
+/**
+ * canfltr_change() - Called for changing properties of an existing filter or
+ * after addition of a new filter to a class (by calling bind_tcf which binds
+ * an instance of a filter to the class).
*
* @tp: Structure representing instance of a filter.
* Part of a linked list of all filters.
f->handle = handle;
else {
f->handle = canfltr_gen_handle(tp);
+ if (f->handle == 0)
+ goto errout;
}
/* Configure filter */
}
-static void canfltr_delete_filter(struct tcf_proto *tp, struct canfltr_state *f)
+static void canfltr_delete_filter(struct tcf_proto *tp,
+ struct canfltr_state *f)
{
tcf_unbind_filter(tp, &f->res);
kfree(f);
}
-/*
- * Remove whole filter.
+/**
+ * canfltr_destroy() - Remove whole filter.
*/
static void canfltr_destroy(struct tcf_proto *tp)
{
kfree(head);
}
-/*
- * Delete one instance of a filter.
+/**
+ * canfltr_delete() - Delete one instance of a filter.
*/
static int canfltr_delete(struct tcf_proto *tp, unsigned long arg)
{
}
-/*
- * Initialize filter
+/**
+ * canfltr_init() - Initialize filter
*/
static int canfltr_init(struct tcf_proto *tp)
{
struct canfltr_head *head;
+ if ((tp->protocol != htons(ETH_P_ALL)) &&
+ (tp->protocol != htons(ETH_P_CAN)))
+ return -1;
+
+ /* Work only on CAN frames */
+ if (tp->protocol == htons(ETH_P_ALL))
+ tp->protocol = htons(ETH_P_CAN);
+
head = kzalloc(sizeof(*head), GFP_KERNEL);
if (head == NULL)
return -ENOBUFS;
INIT_LIST_HEAD(&head->flist);
tp->root = head;
- tp->protocol = htons(ETH_P_CAN); /* Work only on AF_CAN packets - not tested! */
return 0;
}
-/*
- * Iterates over all elements of a filter and invokes a callback function
- * for each of them. This is used to obtain diagnostic data
+/**
+ * canfltr_walk() - Iterates over all elements of a filter and invokes a
+ * callback function for each of them. This is used to obtain diagnostic data.
*/
static void canfltr_walk(struct tcf_proto *tp, struct tcf_walker *arg)
{
}
}
-/*
- * Returns diagnostic data for a filter or one of its elements.
+/**
+ * canfltr_dump() - Returns diagnostic data for a filter or one of its elements.
*/
static int canfltr_dump(struct tcf_proto *tp, unsigned long fh,
struct sk_buff *skb, struct tcmsg *t)
static int __init init_canfltr(void)
{
pr_debug("canfltr: CAN filter loaded\n");
-#ifdef SFF_BITMAP
- pr_debug("canfltr: SFF rules stored in bitmap\n");
-#else
- pr_debug("canfltr: SFF rules stored in array\n");
-#endif
return register_tcf_proto_ops(&cls_canfltr_ops);
}
module_init(init_canfltr);
module_exit(exit_canfltr);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR(""); /* FIXME */
+MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.cz>");
MODULE_DESCRIPTION("Controller Area Network classifier");