|
@@ -28,6 +28,31 @@
|
|
|
#include <net/act_api.h>
|
|
|
#include <net/netlink.h>
|
|
|
|
|
|
+static int tcf_action_goto_chain_init(struct tc_action *a, struct tcf_proto *tp)
|
|
|
+{
|
|
|
+ u32 chain_index = a->tcfa_action & TC_ACT_EXT_VAL_MASK;
|
|
|
+
|
|
|
+ if (!tp)
|
|
|
+ return -EINVAL;
|
|
|
+ a->goto_chain = tcf_chain_get(tp->chain->block, chain_index);
|
|
|
+ if (!a->goto_chain)
|
|
|
+ return -ENOMEM;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void tcf_action_goto_chain_fini(struct tc_action *a)
|
|
|
+{
|
|
|
+ tcf_chain_put(a->goto_chain);
|
|
|
+}
|
|
|
+
|
|
|
+static void tcf_action_goto_chain_exec(const struct tc_action *a,
|
|
|
+ struct tcf_result *res)
|
|
|
+{
|
|
|
+ const struct tcf_chain *chain = a->goto_chain;
|
|
|
+
|
|
|
+ res->goto_tp = rcu_dereference_bh(chain->filter_chain);
|
|
|
+}
|
|
|
+
|
|
|
static void free_tcf(struct rcu_head *head)
|
|
|
{
|
|
|
struct tc_action *p = container_of(head, struct tc_action, tcfa_rcu);
|
|
@@ -39,6 +64,8 @@ static void free_tcf(struct rcu_head *head)
|
|
|
kfree(p->act_cookie->data);
|
|
|
kfree(p->act_cookie);
|
|
|
}
|
|
|
+ if (p->goto_chain)
|
|
|
+ tcf_action_goto_chain_fini(p);
|
|
|
|
|
|
kfree(p);
|
|
|
}
|
|
@@ -465,6 +492,8 @@ repeat:
|
|
|
else /* faulty graph, stop pipeline */
|
|
|
return TC_ACT_OK;
|
|
|
}
|
|
|
+ } else if (TC_ACT_EXT_CMP(ret, TC_ACT_GOTO_CHAIN)) {
|
|
|
+ tcf_action_goto_chain_exec(a, res);
|
|
|
}
|
|
|
|
|
|
if (ret != TC_ACT_PIPE)
|
|
@@ -657,6 +686,17 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
|
|
|
if (err != ACT_P_CREATED)
|
|
|
module_put(a_o->owner);
|
|
|
|
|
|
+ if (TC_ACT_EXT_CMP(a->tcfa_action, TC_ACT_GOTO_CHAIN)) {
|
|
|
+ err = tcf_action_goto_chain_init(a, tp);
|
|
|
+ if (err) {
|
|
|
+ LIST_HEAD(actions);
|
|
|
+
|
|
|
+ list_add_tail(&a->list, &actions);
|
|
|
+ tcf_action_destroy(&actions, bind);
|
|
|
+ return ERR_PTR(err);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return a;
|
|
|
|
|
|
err_mod:
|