]> rtime.felk.cvut.cz Git - lisovros/linux_canprio.git/blobdiff - net/sched/cls_can.c
cls_can: Stop using NLA_PUT*()
[lisovros/linux_canprio.git] / net / sched / cls_can.c
index 0b956c532882fcb72287050fcd4326964860d040..dcb27920be92768ecee759a4069953626c92bbe9 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
  * cls_can.c  -- Controller Area Network classifier.
  * Makes decisions according to Controller Area Network identifiers (can_id).
  *
@@ -6,14 +6,14 @@
  *             modify it under the terms of the GNU General Public License
  *             as published by the Free Software Foundation; version 2 of
  *             the License.
- * 
- * Idea:       Oliver Hartkopp <oliver.hartkopp@volkswagen.de> 
+ *
+ * Idea:       Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
  * Copyright:  (c) 2011 Czech Technical University in Prague
  *             (c) 2011 Volkswagen Group Research
  * Authors:    Michal Sojka <sojkam1@fel.cvut.cz>
  *             Pavel Pisa <pisa@cmp.felk.cvut.cz>
- *             Rostislav Lisovy <lisovy@kormus.cz>
- * Founded by: Volkswagen Group Research
+ *             Rostislav Lisovy <lisovy@gmail.cz>
+ * Funded by:  Volkswagen Group Research
  *
  * Some function descriptions are heavily inspired by article "Linux Network
  * Traffic Control -- Implementation Overview" by Werner Almesberger
 #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 ID (11 bit) 
-                                       there is one record in this bitfield */
-#endif
-       int inv_match_en;               /* Inverted match flag */
+       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 */
        int rules_count;
        int eff_rules_count;
        int sff_rules_count;
@@ -82,8 +74,9 @@ struct canfltr_head {
 
 struct canfltr_state {
        u32 handle;
-       struct canfltr_rules *rules;    /* All rules necessary for classification */
-       struct tcf_result res;          /* Class ID (flow id) the instance 
+       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;
 };
@@ -92,17 +85,18 @@ struct canfltr_state {
  * ----------------------------------------------------------------------------
  */
 
-#ifdef SFF_BITMAP
-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)
 {
        int i;
 
-       /* Limit can_mask and can_id to SFF range to protect against write after end of array */
+       /* Limit can_mask and can_id to SFF range to
+       protect against write after end of array */
        can_mask &= CAN_SFF_MASK;
        can_id &= can_mask;
 
        /* single frame */
-       if (can_mask == CAN_SFF_MASK) {
+       if (can_mask == CAN_SFF_MASK) {
                set_bit(can_id, rls->match_sff);
                return;
        }
@@ -114,16 +108,16 @@ static void canfltr_sff_match_add(struct canfltr_rules *rls, u32 can_id, u32 can
        }
 
        /* 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 ot ouf 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)
 {
@@ -133,60 +127,49 @@ 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 
- * 
+ * @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 filter was found
+ *   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;
        struct canfltr_state *f;
        struct canfltr_rules *r;
-       u32 can_id;
+       canid_t can_id;
        int i;
 
-       can_id = (u32)canfltr_get_id(skb);
+       can_id = canfltr_get_id(skb);
 
        rcu_read_lock();
        list_for_each_entry(f, &head->flist, link) {
                bool match = false;
                r = rcu_dereference(f->rules);
-               
+
 
                if (can_id & CAN_EFF_FLAG) {
                        can_id &= CAN_EFF_MASK;
-                       
-                       for (i = 0; i < r->eff_rules_count; i++) {
-                               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)) {
 
+                       for (i = 0; i < r->eff_rules_count; i++) {
+                               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) ==
-                                       (can_id & r->rules_raw[i].can_mask & CAN_SFF_MASK)) {
-
-                                       match = true;
-                                       break;
-                               }
-                       }
-#endif
                }
 
                if (match) {
@@ -200,9 +183,9 @@ static int canfltr_classify(struct sk_buff *skb, struct tcf_proto *tp,
        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)
 {
@@ -213,42 +196,42 @@ static unsigned long canfltr_get(struct tcf_proto *tp, u32 handle)
                return 0UL;
 
        list_for_each_entry(f, &head->flist, link) {
-               if (f->handle == handle) {
+               if (f->handle == handle)
                        return (unsigned long) f;
-               }
        }
 
        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));
 
-               if ((head->hgenerator += 0x10000) == 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)
 {
        kfree(container_of(rcu, struct canfltr_rules, rcu));
@@ -264,7 +247,7 @@ static int canfltr_set_parms(struct tcf_proto *tp, struct canfltr_state *f,
        int i;
 
        rules_tmp = kzalloc(sizeof(*rules_tmp), GFP_KERNEL);
-       if (rules_tmp == NULL)
+       if (!rules_tmp)
                return -ENOBUFS;
 
        err = -EINVAL;
@@ -275,50 +258,54 @@ static int canfltr_set_parms(struct tcf_proto *tp, struct canfltr_state *f,
                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;
 
-               /* Process EFF frames */
+               /* 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));
-                               rules_tmp->eff_rules_count ++;
+                           (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));
+                               rules_tmp->eff_rules_count++;
                        } else {
                                continue;
                        }
                }
 
-               /* Process SFF frames 
-                  We need two for() loops for copying rules into two contiguous areas in rules_raw */
+               /* Process SFF 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)) {
-                               
+                           (canfltr_nl_rules[i].can_mask & CAN_EFF_FLAG)) {
                                continue;
                        } else {
-                               memcpy(rules_tmp->rules_raw + rules_tmp->eff_rules_count + rules_tmp->sff_rules_count, 
-                                       &canfltr_nl_rules[i], sizeof(struct can_filter));
-                               rules_tmp->sff_rules_count ++;
-
-#ifdef SFF_BITMAP
-                               canfltr_sff_match_add(rules_tmp, canfltr_nl_rules[i].can_id, 
-                                                     canfltr_nl_rules[i].can_mask);
-#endif
+                               memcpy(rules_tmp->rules_raw +
+                                       rules_tmp->eff_rules_count +
+                                       rules_tmp->sff_rules_count,
+                                       &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);
                        }
                }
        }
 
 
-       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;
@@ -334,15 +321,15 @@ errout:
        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. 
+ * @tp:     Structure representing instance of a filter.
  *          Part of a linked list of all filters.
  * @base:
- * @handle:  
+ * @handle:
  * @tca:    Messages passed through the Netlink from userspace.
  * @arg:
  */
@@ -353,17 +340,18 @@ static int canfltr_change(struct tcf_proto *tp, unsigned long base, u32 handle,
        struct canfltr_state *f = (struct canfltr_state *)*arg;
        struct nlattr *tb[TCA_CANFLTR_MAX + 1];
        int err;
-       
+
        if (tca[TCA_OPTIONS] == NULL)
                return -EINVAL;
 
-       /* Parses a stream of attributes and stores a pointer to each attribute in
-       the tb array accessible via the attribute type. Policy may be set to NULL 
-       if no validation is required.*/
-       err = nla_parse_nested(tb, TCA_CANFLTR_MAX, tca[TCA_OPTIONS], canfltr_policy);
-       if (err < 0)                
+       /* Parses a stream of attributes and stores a pointer to each
+       attribute in the tb array accessible via the attribute type.
+       Policy may be set to NULL if no validation is required.*/
+       err = nla_parse_nested(tb, TCA_CANFLTR_MAX, tca[TCA_OPTIONS],
+               canfltr_policy);
+       if (err < 0)
                return err;
-       /* Change existing filter (remove all settings and add 
+       /* Change existing filter (remove all settings and add
        them thereafter as if filter was newly created) */
        if (f != NULL) {
                if (handle && f->handle != handle)
@@ -388,12 +376,14 @@ static int canfltr_change(struct tcf_proto *tp, unsigned long base, u32 handle,
                f->handle = handle;
        else {
                f->handle = canfltr_gen_handle(tp);
+               if (f->handle == 0)
+                       goto errout;
        }
 
        /* Configure filter */
        err = canfltr_set_parms(tp, f, base, tb, tca[TCA_RATE]);
        if (err < 0)
-               goto errout;    
+               goto errout;
 
        /* Add newly created filter to list of all filters */
        tcf_tree_lock(tp);
@@ -411,33 +401,34 @@ errout:
 }
 
 
-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);
 
        rcu_barrier();
        kfree(f->rules->rules_raw);
        kfree(f->rules);
-        kfree(f);
+       kfree(f);
 }
 
-/*
- * Remove whole filter.
+/**
+ * canfltr_destroy() - Remove whole filter.
  */
 static void canfltr_destroy(struct tcf_proto *tp)
 {
-        struct canfltr_head *head = tp->root;
-        struct canfltr_state *f, *n;
-
-        list_for_each_entry_safe(f, n, &head->flist, link) {
-                list_del(&f->link);
-                canfltr_delete_filter(tp, f);
-        }
-        kfree(head);
+       struct canfltr_head *head = tp->root;
+       struct canfltr_state *f, *n;
+
+       list_for_each_entry_safe(f, n, &head->flist, link) {
+               list_del(&f->link);
+               canfltr_delete_filter(tp, f);
+       }
+       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)
 {
@@ -460,33 +451,40 @@ 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)
 {
        struct canfltr_head *head = (struct canfltr_head *) tp->root;
        struct canfltr_state *f;
-       
+
        list_for_each_entry(f, &head->flist, link) {
                if (arg->count < arg->skip)
                        goto skip;
@@ -495,16 +493,16 @@ static void canfltr_walk(struct tcf_proto *tp, struct tcf_walker *arg)
                        arg->stop = 1;
                        break;
                }
-skip:   
+skip:
                arg->count++;
        }
 }
 
-/* 
- * 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)
+                       struct sk_buff *skb, struct tcmsg *t)
 {
        struct canfltr_state *f = (struct canfltr_state *) fh;
        struct nlattr *nest;
@@ -521,12 +519,13 @@ static int canfltr_dump(struct tcf_proto *tp, unsigned long fh,
        if (nest == NULL)
                goto nla_put_failure;
 
-       if (f->res.classid)
-               NLA_PUT_U32(skb, TCA_CANFLTR_CLASSID, f->res.classid);
-
-       NLA_PUT(skb, TCA_CANFLTR_RULES, r->rules_count * 
-               sizeof(struct can_filter), r->rules_raw);
+       if (f->res.classid &&
+           nla_put_u32(skb, TCA_CANFLTR_CLASSID, f->res.classid))
+               goto nla_put_failure;
 
+       if (nla_put(skb, TCA_CANFLTR_RULES, r->rules_count *
+           sizeof(struct can_filter), r->rules_raw)
+               goto nla_put_failure;
 
        nla_nest_end(skb, nest);
 
@@ -557,11 +556,6 @@ static struct tcf_proto_ops cls_canfltr_ops __read_mostly = {
 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);
 }
 
@@ -574,6 +568,5 @@ static void __exit exit_canfltr(void)
 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");
-