]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
net: sched: cls_flower: implement chain templates
authorJiri Pirko <jiri@mellanox.com>
Mon, 23 Jul 2018 07:23:10 +0000 (09:23 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 24 Jul 2018 03:44:12 +0000 (20:44 -0700)
Use the previously introduced template extension and implement
callback to create, destroy and dump chain template. The existing
parsing and dumping functions are re-used. Also, check if newly added
filters fit the template if it is set.

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/sched/cls_flower.c

index bb7aa1e9d281a9c93c5614b9636d02e0ee9eefdb..f0c80758a594056bc1087b7166dd40484c867dc5 100644 (file)
@@ -72,6 +72,13 @@ struct fl_flow_mask {
        struct list_head list;
 };
 
+struct fl_flow_tmplt {
+       struct fl_flow_key dummy_key;
+       struct fl_flow_key mask;
+       struct flow_dissector dissector;
+       struct tcf_chain *chain;
+};
+
 struct cls_fl_head {
        struct rhashtable ht;
        struct list_head masks;
@@ -147,6 +154,23 @@ static void fl_set_masked_key(struct fl_flow_key *mkey, struct fl_flow_key *key,
                *lmkey++ = *lkey++ & *lmask++;
 }
 
+static bool fl_mask_fits_tmplt(struct fl_flow_tmplt *tmplt,
+                              struct fl_flow_mask *mask)
+{
+       const long *lmask = fl_key_get_start(&mask->key, mask);
+       const long *ltmplt;
+       int i;
+
+       if (!tmplt)
+               return true;
+       ltmplt = fl_key_get_start(&tmplt->mask, mask);
+       for (i = 0; i < fl_mask_range(mask); i += sizeof(long)) {
+               if (~*ltmplt++ & *lmask++)
+                       return false;
+       }
+       return true;
+}
+
 static void fl_clear_masked_range(struct fl_flow_key *key,
                                  struct fl_flow_mask *mask)
 {
@@ -939,6 +963,7 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp,
                        struct cls_fl_filter *f, struct fl_flow_mask *mask,
                        unsigned long base, struct nlattr **tb,
                        struct nlattr *est, bool ovr,
+                       struct fl_flow_tmplt *tmplt,
                        struct netlink_ext_ack *extack)
 {
        int err;
@@ -959,6 +984,11 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp,
        fl_mask_update_range(mask);
        fl_set_masked_key(&f->mkey, &f->key, mask);
 
+       if (!fl_mask_fits_tmplt(tmplt, mask)) {
+               NL_SET_ERR_MSG_MOD(extack, "Mask does not fit the template");
+               return -EINVAL;
+       }
+
        return 0;
 }
 
@@ -1024,7 +1054,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
        }
 
        err = fl_set_parms(net, tp, fnew, &mask, base, tb, tca[TCA_RATE], ovr,
-                          extack);
+                          tp->chain->tmplt_priv, extack);
        if (err)
                goto errout_idr;
 
@@ -1164,6 +1194,52 @@ static int fl_reoffload(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb,
        return 0;
 }
 
+static void *fl_tmplt_create(struct net *net, struct tcf_chain *chain,
+                            struct nlattr **tca,
+                            struct netlink_ext_ack *extack)
+{
+       struct fl_flow_tmplt *tmplt;
+       struct nlattr **tb;
+       int err;
+
+       if (!tca[TCA_OPTIONS])
+               return ERR_PTR(-EINVAL);
+
+       tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL);
+       if (!tb)
+               return ERR_PTR(-ENOBUFS);
+       err = nla_parse_nested(tb, TCA_FLOWER_MAX, tca[TCA_OPTIONS],
+                              fl_policy, NULL);
+       if (err)
+               goto errout_tb;
+
+       tmplt = kzalloc(sizeof(*tmplt), GFP_KERNEL);
+       if (!tmplt)
+               goto errout_tb;
+       tmplt->chain = chain;
+       err = fl_set_key(net, tb, &tmplt->dummy_key, &tmplt->mask, extack);
+       if (err)
+               goto errout_tmplt;
+       kfree(tb);
+
+       fl_init_dissector(&tmplt->dissector, &tmplt->mask);
+
+       return tmplt;
+
+errout_tmplt:
+       kfree(tmplt);
+errout_tb:
+       kfree(tb);
+       return ERR_PTR(err);
+}
+
+static void fl_tmplt_destroy(void *tmplt_priv)
+{
+       struct fl_flow_tmplt *tmplt = tmplt_priv;
+
+       kfree(tmplt);
+}
+
 static int fl_dump_key_val(struct sk_buff *skb,
                           void *val, int val_type,
                           void *mask, int mask_type, int len)
@@ -1536,6 +1612,31 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, void *fh,
        return -1;
 }
 
+static int fl_tmplt_dump(struct sk_buff *skb, struct net *net, void *tmplt_priv)
+{
+       struct fl_flow_tmplt *tmplt = tmplt_priv;
+       struct fl_flow_key *key, *mask;
+       struct nlattr *nest;
+
+       nest = nla_nest_start(skb, TCA_OPTIONS);
+       if (!nest)
+               goto nla_put_failure;
+
+       key = &tmplt->dummy_key;
+       mask = &tmplt->mask;
+
+       if (fl_dump_key(skb, net, key, mask))
+               goto nla_put_failure;
+
+       nla_nest_end(skb, nest);
+
+       return skb->len;
+
+nla_put_failure:
+       nla_nest_cancel(skb, nest);
+       return -EMSGSIZE;
+}
+
 static void fl_bind_class(void *fh, u32 classid, unsigned long cl)
 {
        struct cls_fl_filter *f = fh;
@@ -1556,6 +1657,9 @@ static struct tcf_proto_ops cls_fl_ops __read_mostly = {
        .reoffload      = fl_reoffload,
        .dump           = fl_dump,
        .bind_class     = fl_bind_class,
+       .tmplt_create   = fl_tmplt_create,
+       .tmplt_destroy  = fl_tmplt_destroy,
+       .tmplt_dump     = fl_tmplt_dump,
        .owner          = THIS_MODULE,
 };