struct gnet_stats_basic_cpu __percpu *cpu_bstats;
        struct gnet_stats_queue __percpu *cpu_qstats;
        struct tc_cookie        *act_cookie;
+       struct tcf_chain        *goto_chain;
 };
 #define tcf_head       common.tcfa_head
 #define tcf_index      common.tcfa_index
 
 #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);
                kfree(p->act_cookie->data);
                kfree(p->act_cookie);
        }
+       if (p->goto_chain)
+               tcf_action_goto_chain_fini(p);
 
        kfree(p);
 }
                                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)
        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:
 
 
                err = tp->classify(skb, tp, res);
 #ifdef CONFIG_NET_CLS_ACT
-               if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode))
+               if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode)) {
                        goto reset;
+               } else if (unlikely(TC_ACT_EXT_CMP(err, TC_ACT_GOTO_CHAIN))) {
+                       old_tp = res->goto_tp;
+                       goto reset;
+               }
 #endif
                if (err >= 0)
                        return err;