@@ -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;
@@ -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
};
@@ -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;