Use netfilter packet accounting to add support for tc user
specifying per zone policy_pkts, which determines after how
many packets a tuple will be offloaded to the zone's flow table.

To avoid conflicting policies, the same policy must be given for all
act ct instances of the same zone.

Usage example:
$ tc filter add dev ens1f0_0 ingress chain 0 flower ct_state -trk \
action ct policy_pkts 10 pipe action goto chain 1

$ tc filter add dev ens1f0_0 ingress chain 1 flower ct_state \
action ct commit policy_pkts 10 pipe \
action mirred egress redirect dev ens1f0_1

Signed-off-by: Paul Blakey <[email protected]>
Reviewed-by: Oz Shlomo <[email protected]>
Reviewed-by: Jiri Pirko <[email protected]>
---
 include/net/tc_act/tc_ct.h        |  4 +++
 include/uapi/linux/tc_act/tc_ct.h |  1 +
 net/sched/act_ct.c                | 74 +++++++++++++++++++++++++++++++++++++--
 3 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/include/net/tc_act/tc_ct.h b/include/net/tc_act/tc_ct.h
index 79654bc..9d9fdcc 100644
--- a/include/net/tc_act/tc_ct.h
+++ b/include/net/tc_act/tc_ct.h
@@ -19,6 +19,10 @@ struct tcf_ct_params {
        u32 labels[NF_CT_LABELS_MAX_SIZE / sizeof(u32)];
        u32 labels_mask[NF_CT_LABELS_MAX_SIZE / sizeof(u32)];
 
+       struct {
+               u32 pkts;
+       } offload_policy;
+
        struct nf_nat_range2 range;
        bool ipv4_range;
 
diff --git a/include/uapi/linux/tc_act/tc_ct.h 
b/include/uapi/linux/tc_act/tc_ct.h
index 5fb1d7a..a97e740 100644
--- a/include/uapi/linux/tc_act/tc_ct.h
+++ b/include/uapi/linux/tc_act/tc_ct.h
@@ -21,6 +21,7 @@ enum {
        TCA_CT_NAT_IPV6_MAX,    /* struct in6_addr */
        TCA_CT_NAT_PORT_MIN,    /* be16 */
        TCA_CT_NAT_PORT_MAX,    /* be16 */
+       TCA_CT_OFFLOAD_POLICY_PKTS,     /* u32 */
        TCA_CT_PAD,
        __TCA_CT_MAX
 };
diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c
index 9adff83..5b18d62 100644
--- a/net/sched/act_ct.c
+++ b/net/sched/act_ct.c
@@ -27,6 +27,7 @@
 
 #include <net/netfilter/nf_flow_table.h>
 #include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_acct.h>
 #include <net/netfilter/nf_conntrack_core.h>
 #include <net/netfilter/nf_conntrack_zones.h>
 #include <net/netfilter/nf_conntrack_helper.h>
@@ -46,6 +47,10 @@ struct tcf_ct_flow_table {
        refcount_t ref;
        u16 zone;
 
+       struct {
+               u32 pkts;
+       } offload_policy;
+
        bool dying;
 };
 
@@ -267,12 +272,57 @@ static int tcf_ct_flow_table_fill_actions(struct net *net,
        return err;
 }
 
+static int tcf_ct_flow_table_policy_set(struct net *net,
+                                       struct tcf_ct_params *params,
+                                       struct netlink_ext_ack *extack,
+                                       bool ct_ft_created)
+{
+       struct tcf_ct_flow_table *ct_ft = params->ct_ft;
+
+       if (!ct_ft_created) {
+               if (params->offload_policy.pkts != ct_ft->offload_policy.pkts) {
+                       NL_SET_ERR_MSG_MOD(extack, "Policy pkts must match 
previous action ct instance");
+                       return -EOPNOTSUPP;
+               }
+               return 0;
+       }
+
+       if (params->offload_policy.pkts) {
+               ct_ft->offload_policy.pkts = params->offload_policy.pkts;
+               if (!nf_ct_acct_enabled(net))
+                       nf_ct_set_acct(net, true);
+       }
+       return 0;
+}
+
+static bool tcf_ct_check_offload_policy(struct tcf_ct_flow_table *ct_ft,
+                                       struct nf_conn *ct)
+{
+       struct nf_conn_counter *counter;
+       struct nf_conn_acct *acct;
+       u64 pkts;
+
+       if (!ct_ft->offload_policy.pkts)
+               return true;
+
+       acct = nf_conn_acct_find(ct);
+       if (!acct)
+               return false;
+
+       counter = acct->counter;
+       pkts = atomic64_read(&counter[IP_CT_DIR_ORIGINAL].packets) +
+              atomic64_read(&counter[IP_CT_DIR_REPLY].packets);
+
+       return pkts >= ct_ft->offload_policy.pkts;
+}
+
 static struct nf_flowtable_type flowtable_ct = {
        .action         = tcf_ct_flow_table_fill_actions,
        .owner          = THIS_MODULE,
 };
 
-static int tcf_ct_flow_table_get(struct tcf_ct_params *params)
+static int tcf_ct_flow_table_get(struct tcf_ct_params *params,
+                                bool *ct_ft_created)
 {
        struct tcf_ct_flow_table *ct_ft;
        int err = -ENOMEM;
@@ -299,6 +349,7 @@ static int tcf_ct_flow_table_get(struct tcf_ct_params 
*params)
                goto err_init;
 
        __module_get(THIS_MODULE);
+       *ct_ft_created = true;
 out_unlock:
        params->ct_ft = ct_ft;
        params->nf_ft = &ct_ft->nf_ft;
@@ -345,6 +396,9 @@ static void tcf_ct_flow_table_add(struct tcf_ct_flow_table 
*ct_ft,
        struct flow_offload *entry;
        int err;
 
+       if (!tcf_ct_check_offload_policy(ct_ft, ct))
+               return;
+
        if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status))
                return;
 
@@ -1034,6 +1088,7 @@ static int tcf_ct_act(struct sk_buff *skb, const struct 
tc_action *a,
                                   .len = sizeof(struct in6_addr) },
        [TCA_CT_NAT_PORT_MIN] = { .type = NLA_U16 },
        [TCA_CT_NAT_PORT_MAX] = { .type = NLA_U16 },
+       [TCA_CT_OFFLOAD_POLICY_PKTS] = { .type = NLA_U32 },
 };
 
 static int tcf_ct_fill_params_nat(struct tcf_ct_params *p,
@@ -1179,6 +1234,10 @@ static int tcf_ct_fill_params(struct net *net,
                                   sizeof(p->zone));
        }
 
+       if (tb[TCA_CT_OFFLOAD_POLICY_PKTS])
+               p->offload_policy.pkts =
+                       nla_get_u32(tb[TCA_CT_OFFLOAD_POLICY_PKTS]);
+
        if (p->zone == NF_CT_DEFAULT_ZONE_ID)
                return 0;
 
@@ -1205,6 +1264,7 @@ static int tcf_ct_init(struct net *net, struct nlattr 
*nla,
        struct tcf_ct_params *params = NULL;
        struct nlattr *tb[TCA_CT_MAX + 1];
        struct tcf_chain *goto_ch = NULL;
+       bool ct_ft_created = false;
        struct tc_ct *parm;
        struct tcf_ct *c;
        int err, res = 0;
@@ -1262,7 +1322,11 @@ static int tcf_ct_init(struct net *net, struct nlattr 
*nla,
        if (err)
                goto cleanup;
 
-       err = tcf_ct_flow_table_get(params);
+       err = tcf_ct_flow_table_get(params, &ct_ft_created);
+       if (err)
+               goto cleanup;
+
+       err = tcf_ct_flow_table_policy_set(net, params, extack, ct_ft_created);
        if (err)
                goto cleanup;
 
@@ -1282,6 +1346,8 @@ static int tcf_ct_init(struct net *net, struct nlattr 
*nla,
        return res;
 
 cleanup:
+       if (params->ct_ft)
+               tcf_ct_flow_table_put(params);
        if (goto_ch)
                tcf_chain_put_by_act(goto_ch);
        kfree(params);
@@ -1414,6 +1480,10 @@ static inline int tcf_ct_dump(struct sk_buff *skb, 
struct tc_action *a,
        if (tcf_ct_dump_nat(skb, p))
                goto nla_put_failure;
 
+       if (nla_put_u32(skb, TCA_CT_OFFLOAD_POLICY_PKTS,
+                       p->offload_policy.pkts))
+               goto nla_put_failure;
+
 skip_dump:
        if (nla_put(skb, TCA_CT_PARMS, sizeof(opt), &opt))
                goto nla_put_failure;
-- 
1.8.3.1

Reply via email to