Commit 4b45e079 authored by David S. Miller's avatar David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next

Pablo Neira Ayuso says:

====================
Netfilter updates for net-next

The following patchset contains Netfilter updates for net-next.
This patchset contains updates for the nf_tables register tracking
infrastructure, disable bogus warning when attaching ct helpers,
one namespace pollution fix and few cleanups for the flowtable.

1) Revisit conntrack gc routine to reduce chances of overruning
   the netlink buffer from the event path. From Florian Westphal.

2) Disable warning on explicit ct helper assignment, from Phil Sutter.

3) Read-only expressions do not update registers, mark them as
   NFT_REDUCE_READONLY. Add helper functions to update the register
   tracking information. This patch re-enables the register tracking
   infrastructure.

4) Cancel register tracking in case an expression fully/partially
   clobbers existing data.

5) Add register tracking support for remaining expressions: ct,
   lookup, meta, numgen, osf, hash, immediate, socket, xfrm, tunnel,
   fib, exthdr.

6) Rename init and exit functions for the conntrack h323 helper,
   from Randy Dunlap.

7) Remove redundant field in struct flow_offload_work.

8) Update nf_flow_table_iterate() to pass flowtable to callback.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents e6980b57 217cff36
...@@ -177,4 +177,5 @@ void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat); ...@@ -177,4 +177,5 @@ void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat);
int nf_nat_helper_try_module_get(const char *name, u16 l3num, int nf_nat_helper_try_module_get(const char *name, u16 l3num,
u8 protonum); u8 protonum);
void nf_nat_helper_put(struct nf_conntrack_helper *helper); void nf_nat_helper_put(struct nf_conntrack_helper *helper);
void nf_ct_set_auto_assign_helper_warned(struct net *net);
#endif /*_NF_CONNTRACK_HELPER_H*/ #endif /*_NF_CONNTRACK_HELPER_H*/
...@@ -126,6 +126,7 @@ struct nft_regs_track { ...@@ -126,6 +126,7 @@ struct nft_regs_track {
struct { struct {
const struct nft_expr *selector; const struct nft_expr *selector;
const struct nft_expr *bitwise; const struct nft_expr *bitwise;
u8 num_reg;
} regs[NFT_REG32_NUM]; } regs[NFT_REG32_NUM];
const struct nft_expr *cur; const struct nft_expr *cur;
...@@ -1633,4 +1634,25 @@ static inline struct nftables_pernet *nft_pernet(const struct net *net) ...@@ -1633,4 +1634,25 @@ static inline struct nftables_pernet *nft_pernet(const struct net *net)
return net_generic(net, nf_tables_net_id); return net_generic(net, nf_tables_net_id);
} }
#define __NFT_REDUCE_READONLY 1UL
#define NFT_REDUCE_READONLY (void *)__NFT_REDUCE_READONLY
static inline bool nft_reduce_is_readonly(const struct nft_expr *expr)
{
return expr->ops->reduce == NFT_REDUCE_READONLY;
}
void nft_reg_track_update(struct nft_regs_track *track,
const struct nft_expr *expr, u8 dreg, u8 len);
void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len);
void __nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg);
static inline bool nft_reg_track_cmp(struct nft_regs_track *track,
const struct nft_expr *expr, u8 dreg)
{
return track->regs[dreg].selector &&
track->regs[dreg].selector->ops == expr->ops &&
track->regs[dreg].num_reg == 0;
}
#endif /* _NET_NF_TABLES_H */ #endif /* _NET_NF_TABLES_H */
...@@ -37,4 +37,7 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs, ...@@ -37,4 +37,7 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs,
void nft_fib_store_result(void *reg, const struct nft_fib *priv, void nft_fib_store_result(void *reg, const struct nft_fib *priv,
const struct net_device *dev); const struct net_device *dev);
bool nft_fib_reduce(struct nft_regs_track *track,
const struct nft_expr *expr);
#endif #endif
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
struct nft_meta { struct nft_meta {
enum nft_meta_keys key:8; enum nft_meta_keys key:8;
u8 len;
union { union {
u8 dreg; u8 dreg;
u8 sreg; u8 sreg;
...@@ -43,4 +44,6 @@ int nft_meta_set_validate(const struct nft_ctx *ctx, ...@@ -43,4 +44,6 @@ int nft_meta_set_validate(const struct nft_ctx *ctx,
const struct nft_expr *expr, const struct nft_expr *expr,
const struct nft_data **data); const struct nft_data **data);
bool nft_meta_get_reduce(struct nft_regs_track *track,
const struct nft_expr *expr);
#endif #endif
...@@ -87,6 +87,7 @@ static int nft_meta_bridge_get_init(const struct nft_ctx *ctx, ...@@ -87,6 +87,7 @@ static int nft_meta_bridge_get_init(const struct nft_ctx *ctx,
return nft_meta_get_init(ctx, expr, tb); return nft_meta_get_init(ctx, expr, tb);
} }
priv->len = len;
return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg, return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg,
NULL, NFT_DATA_VALUE, len); NULL, NFT_DATA_VALUE, len);
} }
...@@ -98,6 +99,7 @@ static const struct nft_expr_ops nft_meta_bridge_get_ops = { ...@@ -98,6 +99,7 @@ static const struct nft_expr_ops nft_meta_bridge_get_ops = {
.eval = nft_meta_bridge_get_eval, .eval = nft_meta_bridge_get_eval,
.init = nft_meta_bridge_get_init, .init = nft_meta_bridge_get_init,
.dump = nft_meta_get_dump, .dump = nft_meta_get_dump,
.reduce = nft_meta_get_reduce,
}; };
static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track, static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track,
...@@ -112,8 +114,7 @@ static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track, ...@@ -112,8 +114,7 @@ static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track,
if (track->regs[i].selector->ops != &nft_meta_bridge_get_ops) if (track->regs[i].selector->ops != &nft_meta_bridge_get_ops)
continue; continue;
track->regs[i].selector = NULL; __nft_reg_track_cancel(track, i);
track->regs[i].bitwise = NULL;
} }
return false; return false;
......
...@@ -185,6 +185,7 @@ static const struct nft_expr_ops nft_reject_bridge_ops = { ...@@ -185,6 +185,7 @@ static const struct nft_expr_ops nft_reject_bridge_ops = {
.init = nft_reject_init, .init = nft_reject_init,
.dump = nft_reject_dump, .dump = nft_reject_dump,
.validate = nft_reject_bridge_validate, .validate = nft_reject_bridge_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_reject_bridge_type __read_mostly = { static struct nft_expr_type nft_reject_bridge_type __read_mostly = {
......
...@@ -580,7 +580,7 @@ static struct nf_ct_helper_expectfn callforwarding_nat = { ...@@ -580,7 +580,7 @@ static struct nf_ct_helper_expectfn callforwarding_nat = {
}; };
/****************************************************************************/ /****************************************************************************/
static int __init init(void) static int __init nf_nat_h323_init(void)
{ {
BUG_ON(set_h245_addr_hook != NULL); BUG_ON(set_h245_addr_hook != NULL);
BUG_ON(set_h225_addr_hook != NULL); BUG_ON(set_h225_addr_hook != NULL);
...@@ -607,7 +607,7 @@ static int __init init(void) ...@@ -607,7 +607,7 @@ static int __init init(void)
} }
/****************************************************************************/ /****************************************************************************/
static void __exit fini(void) static void __exit nf_nat_h323_fini(void)
{ {
RCU_INIT_POINTER(set_h245_addr_hook, NULL); RCU_INIT_POINTER(set_h245_addr_hook, NULL);
RCU_INIT_POINTER(set_h225_addr_hook, NULL); RCU_INIT_POINTER(set_h225_addr_hook, NULL);
...@@ -624,8 +624,8 @@ static void __exit fini(void) ...@@ -624,8 +624,8 @@ static void __exit fini(void)
} }
/****************************************************************************/ /****************************************************************************/
module_init(init); module_init(nf_nat_h323_init);
module_exit(fini); module_exit(nf_nat_h323_fini);
MODULE_AUTHOR("Jing Min Zhao <zhaojingmin@users.sourceforge.net>"); MODULE_AUTHOR("Jing Min Zhao <zhaojingmin@users.sourceforge.net>");
MODULE_DESCRIPTION("H.323 NAT helper"); MODULE_DESCRIPTION("H.323 NAT helper");
......
...@@ -75,6 +75,7 @@ static const struct nft_expr_ops nft_dup_ipv4_ops = { ...@@ -75,6 +75,7 @@ static const struct nft_expr_ops nft_dup_ipv4_ops = {
.eval = nft_dup_ipv4_eval, .eval = nft_dup_ipv4_eval,
.init = nft_dup_ipv4_init, .init = nft_dup_ipv4_init,
.dump = nft_dup_ipv4_dump, .dump = nft_dup_ipv4_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
static const struct nla_policy nft_dup_ipv4_policy[NFTA_DUP_MAX + 1] = { static const struct nla_policy nft_dup_ipv4_policy[NFTA_DUP_MAX + 1] = {
......
...@@ -152,6 +152,7 @@ static const struct nft_expr_ops nft_fib4_type_ops = { ...@@ -152,6 +152,7 @@ static const struct nft_expr_ops nft_fib4_type_ops = {
.init = nft_fib_init, .init = nft_fib_init,
.dump = nft_fib_dump, .dump = nft_fib_dump,
.validate = nft_fib_validate, .validate = nft_fib_validate,
.reduce = nft_fib_reduce,
}; };
static const struct nft_expr_ops nft_fib4_ops = { static const struct nft_expr_ops nft_fib4_ops = {
...@@ -161,6 +162,7 @@ static const struct nft_expr_ops nft_fib4_ops = { ...@@ -161,6 +162,7 @@ static const struct nft_expr_ops nft_fib4_ops = {
.init = nft_fib_init, .init = nft_fib_init,
.dump = nft_fib_dump, .dump = nft_fib_dump,
.validate = nft_fib_validate, .validate = nft_fib_validate,
.reduce = nft_fib_reduce,
}; };
static const struct nft_expr_ops * static const struct nft_expr_ops *
......
...@@ -45,6 +45,7 @@ static const struct nft_expr_ops nft_reject_ipv4_ops = { ...@@ -45,6 +45,7 @@ static const struct nft_expr_ops nft_reject_ipv4_ops = {
.init = nft_reject_init, .init = nft_reject_init,
.dump = nft_reject_dump, .dump = nft_reject_dump,
.validate = nft_reject_validate, .validate = nft_reject_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_reject_ipv4_type __read_mostly = { static struct nft_expr_type nft_reject_ipv4_type __read_mostly = {
......
...@@ -73,6 +73,7 @@ static const struct nft_expr_ops nft_dup_ipv6_ops = { ...@@ -73,6 +73,7 @@ static const struct nft_expr_ops nft_dup_ipv6_ops = {
.eval = nft_dup_ipv6_eval, .eval = nft_dup_ipv6_eval,
.init = nft_dup_ipv6_init, .init = nft_dup_ipv6_init,
.dump = nft_dup_ipv6_dump, .dump = nft_dup_ipv6_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
static const struct nla_policy nft_dup_ipv6_policy[NFTA_DUP_MAX + 1] = { static const struct nla_policy nft_dup_ipv6_policy[NFTA_DUP_MAX + 1] = {
......
...@@ -211,6 +211,7 @@ static const struct nft_expr_ops nft_fib6_type_ops = { ...@@ -211,6 +211,7 @@ static const struct nft_expr_ops nft_fib6_type_ops = {
.init = nft_fib_init, .init = nft_fib_init,
.dump = nft_fib_dump, .dump = nft_fib_dump,
.validate = nft_fib_validate, .validate = nft_fib_validate,
.reduce = nft_fib_reduce,
}; };
static const struct nft_expr_ops nft_fib6_ops = { static const struct nft_expr_ops nft_fib6_ops = {
...@@ -220,6 +221,7 @@ static const struct nft_expr_ops nft_fib6_ops = { ...@@ -220,6 +221,7 @@ static const struct nft_expr_ops nft_fib6_ops = {
.init = nft_fib_init, .init = nft_fib_init,
.dump = nft_fib_dump, .dump = nft_fib_dump,
.validate = nft_fib_validate, .validate = nft_fib_validate,
.reduce = nft_fib_reduce,
}; };
static const struct nft_expr_ops * static const struct nft_expr_ops *
......
...@@ -46,6 +46,7 @@ static const struct nft_expr_ops nft_reject_ipv6_ops = { ...@@ -46,6 +46,7 @@ static const struct nft_expr_ops nft_reject_ipv6_ops = {
.init = nft_reject_init, .init = nft_reject_init,
.dump = nft_reject_dump, .dump = nft_reject_dump,
.validate = nft_reject_validate, .validate = nft_reject_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_reject_ipv6_type __read_mostly = { static struct nft_expr_type nft_reject_ipv6_type __read_mostly = {
......
...@@ -66,6 +66,8 @@ EXPORT_SYMBOL_GPL(nf_conntrack_hash); ...@@ -66,6 +66,8 @@ EXPORT_SYMBOL_GPL(nf_conntrack_hash);
struct conntrack_gc_work { struct conntrack_gc_work {
struct delayed_work dwork; struct delayed_work dwork;
u32 next_bucket; u32 next_bucket;
u32 avg_timeout;
u32 start_time;
bool exiting; bool exiting;
bool early_drop; bool early_drop;
}; };
...@@ -77,8 +79,19 @@ static __read_mostly bool nf_conntrack_locks_all; ...@@ -77,8 +79,19 @@ static __read_mostly bool nf_conntrack_locks_all;
/* serialize hash resizes and nf_ct_iterate_cleanup */ /* serialize hash resizes and nf_ct_iterate_cleanup */
static DEFINE_MUTEX(nf_conntrack_mutex); static DEFINE_MUTEX(nf_conntrack_mutex);
#define GC_SCAN_INTERVAL (120u * HZ) #define GC_SCAN_INTERVAL_MAX (60ul * HZ)
#define GC_SCAN_INTERVAL_MIN (1ul * HZ)
/* clamp timeouts to this value (TCP unacked) */
#define GC_SCAN_INTERVAL_CLAMP (300ul * HZ)
/* large initial bias so that we don't scan often just because we have
* three entries with a 1s timeout.
*/
#define GC_SCAN_INTERVAL_INIT INT_MAX
#define GC_SCAN_MAX_DURATION msecs_to_jiffies(10) #define GC_SCAN_MAX_DURATION msecs_to_jiffies(10)
#define GC_SCAN_EXPIRED_MAX (64000u / HZ)
#define MIN_CHAINLEN 8u #define MIN_CHAINLEN 8u
#define MAX_CHAINLEN (32u - MIN_CHAINLEN) #define MAX_CHAINLEN (32u - MIN_CHAINLEN)
...@@ -1420,16 +1433,28 @@ static bool gc_worker_can_early_drop(const struct nf_conn *ct) ...@@ -1420,16 +1433,28 @@ static bool gc_worker_can_early_drop(const struct nf_conn *ct)
static void gc_worker(struct work_struct *work) static void gc_worker(struct work_struct *work)
{ {
unsigned long end_time = jiffies + GC_SCAN_MAX_DURATION;
unsigned int i, hashsz, nf_conntrack_max95 = 0; unsigned int i, hashsz, nf_conntrack_max95 = 0;
unsigned long next_run = GC_SCAN_INTERVAL; u32 end_time, start_time = nfct_time_stamp;
struct conntrack_gc_work *gc_work; struct conntrack_gc_work *gc_work;
unsigned int expired_count = 0;
unsigned long next_run;
s32 delta_time;
gc_work = container_of(work, struct conntrack_gc_work, dwork.work); gc_work = container_of(work, struct conntrack_gc_work, dwork.work);
i = gc_work->next_bucket; i = gc_work->next_bucket;
if (gc_work->early_drop) if (gc_work->early_drop)
nf_conntrack_max95 = nf_conntrack_max / 100u * 95u; nf_conntrack_max95 = nf_conntrack_max / 100u * 95u;
if (i == 0) {
gc_work->avg_timeout = GC_SCAN_INTERVAL_INIT;
gc_work->start_time = start_time;
}
next_run = gc_work->avg_timeout;
end_time = start_time + GC_SCAN_MAX_DURATION;
do { do {
struct nf_conntrack_tuple_hash *h; struct nf_conntrack_tuple_hash *h;
struct hlist_nulls_head *ct_hash; struct hlist_nulls_head *ct_hash;
...@@ -1446,6 +1471,7 @@ static void gc_worker(struct work_struct *work) ...@@ -1446,6 +1471,7 @@ static void gc_worker(struct work_struct *work)
hlist_nulls_for_each_entry_rcu(h, n, &ct_hash[i], hnnode) { hlist_nulls_for_each_entry_rcu(h, n, &ct_hash[i], hnnode) {
struct nf_conntrack_net *cnet; struct nf_conntrack_net *cnet;
unsigned long expires;
struct net *net; struct net *net;
tmp = nf_ct_tuplehash_to_ctrack(h); tmp = nf_ct_tuplehash_to_ctrack(h);
...@@ -1455,11 +1481,29 @@ static void gc_worker(struct work_struct *work) ...@@ -1455,11 +1481,29 @@ static void gc_worker(struct work_struct *work)
continue; continue;
} }
if (expired_count > GC_SCAN_EXPIRED_MAX) {
rcu_read_unlock();
gc_work->next_bucket = i;
gc_work->avg_timeout = next_run;
delta_time = nfct_time_stamp - gc_work->start_time;
/* re-sched immediately if total cycle time is exceeded */
next_run = delta_time < (s32)GC_SCAN_INTERVAL_MAX;
goto early_exit;
}
if (nf_ct_is_expired(tmp)) { if (nf_ct_is_expired(tmp)) {
nf_ct_gc_expired(tmp); nf_ct_gc_expired(tmp);
expired_count++;
continue; continue;
} }
expires = clamp(nf_ct_expires(tmp), GC_SCAN_INTERVAL_MIN, GC_SCAN_INTERVAL_CLAMP);
next_run += expires;
next_run /= 2u;
if (nf_conntrack_max95 == 0 || gc_worker_skip_ct(tmp)) if (nf_conntrack_max95 == 0 || gc_worker_skip_ct(tmp))
continue; continue;
...@@ -1477,8 +1521,10 @@ static void gc_worker(struct work_struct *work) ...@@ -1477,8 +1521,10 @@ static void gc_worker(struct work_struct *work)
continue; continue;
} }
if (gc_worker_can_early_drop(tmp)) if (gc_worker_can_early_drop(tmp)) {
nf_ct_kill(tmp); nf_ct_kill(tmp);
expired_count++;
}
nf_ct_put(tmp); nf_ct_put(tmp);
} }
...@@ -1491,33 +1537,38 @@ static void gc_worker(struct work_struct *work) ...@@ -1491,33 +1537,38 @@ static void gc_worker(struct work_struct *work)
cond_resched(); cond_resched();
i++; i++;
if (time_after(jiffies, end_time) && i < hashsz) { delta_time = nfct_time_stamp - end_time;
if (delta_time > 0 && i < hashsz) {
gc_work->avg_timeout = next_run;
gc_work->next_bucket = i; gc_work->next_bucket = i;
next_run = 0; next_run = 0;
break; goto early_exit;
} }
} while (i < hashsz); } while (i < hashsz);
gc_work->next_bucket = 0;
next_run = clamp(next_run, GC_SCAN_INTERVAL_MIN, GC_SCAN_INTERVAL_MAX);
delta_time = max_t(s32, nfct_time_stamp - gc_work->start_time, 1);
if (next_run > (unsigned long)delta_time)
next_run -= delta_time;
else
next_run = 1;
early_exit:
if (gc_work->exiting) if (gc_work->exiting)
return; return;
/* if (next_run)
* Eviction will normally happen from the packet path, and not
* from this gc worker.
*
* This worker is only here to reap expired entries when system went
* idle after a busy period.
*/
if (next_run) {
gc_work->early_drop = false; gc_work->early_drop = false;
gc_work->next_bucket = 0;
}
queue_delayed_work(system_power_efficient_wq, &gc_work->dwork, next_run); queue_delayed_work(system_power_efficient_wq, &gc_work->dwork, next_run);
} }
static void conntrack_gc_work_init(struct conntrack_gc_work *gc_work) static void conntrack_gc_work_init(struct conntrack_gc_work *gc_work)
{ {
INIT_DEFERRABLE_WORK(&gc_work->dwork, gc_worker); INIT_DELAYED_WORK(&gc_work->dwork, gc_worker);
gc_work->exiting = false; gc_work->exiting = false;
} }
......
...@@ -550,6 +550,12 @@ void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat) ...@@ -550,6 +550,12 @@ void nf_nat_helper_unregister(struct nf_conntrack_nat_helper *nat)
} }
EXPORT_SYMBOL_GPL(nf_nat_helper_unregister); EXPORT_SYMBOL_GPL(nf_nat_helper_unregister);
void nf_ct_set_auto_assign_helper_warned(struct net *net)
{
nf_ct_pernet(net)->auto_assign_helper_warned = true;
}
EXPORT_SYMBOL_GPL(nf_ct_set_auto_assign_helper_warned);
void nf_conntrack_helper_pernet_init(struct net *net) void nf_conntrack_helper_pernet_init(struct net *net)
{ {
struct nf_conntrack_net *cnet = nf_ct_pernet(net); struct nf_conntrack_net *cnet = nf_ct_pernet(net);
......
...@@ -405,7 +405,8 @@ EXPORT_SYMBOL_GPL(flow_offload_lookup); ...@@ -405,7 +405,8 @@ EXPORT_SYMBOL_GPL(flow_offload_lookup);
static int static int
nf_flow_table_iterate(struct nf_flowtable *flow_table, nf_flow_table_iterate(struct nf_flowtable *flow_table,
void (*iter)(struct flow_offload *flow, void *data), void (*iter)(struct nf_flowtable *flowtable,
struct flow_offload *flow, void *data),
void *data) void *data)
{ {
struct flow_offload_tuple_rhash *tuplehash; struct flow_offload_tuple_rhash *tuplehash;
...@@ -429,7 +430,7 @@ nf_flow_table_iterate(struct nf_flowtable *flow_table, ...@@ -429,7 +430,7 @@ nf_flow_table_iterate(struct nf_flowtable *flow_table,
flow = container_of(tuplehash, struct flow_offload, tuplehash[0]); flow = container_of(tuplehash, struct flow_offload, tuplehash[0]);
iter(flow, data); iter(flow_table, flow, data);
} }
rhashtable_walk_stop(&hti); rhashtable_walk_stop(&hti);
rhashtable_walk_exit(&hti); rhashtable_walk_exit(&hti);
...@@ -457,10 +458,9 @@ static bool nf_flow_has_stale_dst(struct flow_offload *flow) ...@@ -457,10 +458,9 @@ static bool nf_flow_has_stale_dst(struct flow_offload *flow)
flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple); flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple);
} }
static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data) static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table,
struct flow_offload *flow, void *data)
{ {
struct nf_flowtable *flow_table = data;
if (nf_flow_has_expired(flow) || if (nf_flow_has_expired(flow) ||
nf_ct_is_dying(flow->ct) || nf_ct_is_dying(flow->ct) ||
nf_flow_has_stale_dst(flow)) nf_flow_has_stale_dst(flow))
...@@ -485,7 +485,7 @@ static void nf_flow_offload_work_gc(struct work_struct *work) ...@@ -485,7 +485,7 @@ static void nf_flow_offload_work_gc(struct work_struct *work)
struct nf_flowtable *flow_table; struct nf_flowtable *flow_table;
flow_table = container_of(work, struct nf_flowtable, gc_work.work); flow_table = container_of(work, struct nf_flowtable, gc_work.work);
nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, flow_table); nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, NULL);
queue_delayed_work(system_power_efficient_wq, &flow_table->gc_work, HZ); queue_delayed_work(system_power_efficient_wq, &flow_table->gc_work, HZ);
} }
...@@ -601,7 +601,8 @@ int nf_flow_table_init(struct nf_flowtable *flowtable) ...@@ -601,7 +601,8 @@ int nf_flow_table_init(struct nf_flowtable *flowtable)
} }
EXPORT_SYMBOL_GPL(nf_flow_table_init); EXPORT_SYMBOL_GPL(nf_flow_table_init);
static void nf_flow_table_do_cleanup(struct flow_offload *flow, void *data) static void nf_flow_table_do_cleanup(struct nf_flowtable *flow_table,
struct flow_offload *flow, void *data)
{ {
struct net_device *dev = data; struct net_device *dev = data;
...@@ -643,11 +644,10 @@ void nf_flow_table_free(struct nf_flowtable *flow_table) ...@@ -643,11 +644,10 @@ void nf_flow_table_free(struct nf_flowtable *flow_table)
cancel_delayed_work_sync(&flow_table->gc_work); cancel_delayed_work_sync(&flow_table->gc_work);
nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL); nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL);
nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, flow_table); nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, NULL);
nf_flow_table_offload_flush(flow_table); nf_flow_table_offload_flush(flow_table);
if (nf_flowtable_hw_offload(flow_table)) if (nf_flowtable_hw_offload(flow_table))
nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, NULL);
flow_table);
rhashtable_destroy(&flow_table->rhashtable); rhashtable_destroy(&flow_table->rhashtable);
} }
EXPORT_SYMBOL_GPL(nf_flow_table_free); EXPORT_SYMBOL_GPL(nf_flow_table_free);
......
...@@ -20,7 +20,6 @@ static struct workqueue_struct *nf_flow_offload_stats_wq; ...@@ -20,7 +20,6 @@ static struct workqueue_struct *nf_flow_offload_stats_wq;
struct flow_offload_work { struct flow_offload_work {
struct list_head list; struct list_head list;
enum flow_cls_command cmd; enum flow_cls_command cmd;
int priority;
struct nf_flowtable *flowtable; struct nf_flowtable *flowtable;
struct flow_offload *flow; struct flow_offload *flow;
struct work_struct work; struct work_struct work;
...@@ -874,7 +873,8 @@ static int flow_offload_tuple_add(struct flow_offload_work *offload, ...@@ -874,7 +873,8 @@ static int flow_offload_tuple_add(struct flow_offload_work *offload,
enum flow_offload_tuple_dir dir) enum flow_offload_tuple_dir dir)
{ {
return nf_flow_offload_tuple(offload->flowtable, offload->flow, return nf_flow_offload_tuple(offload->flowtable, offload->flow,
flow_rule, dir, offload->priority, flow_rule, dir,
offload->flowtable->priority,
FLOW_CLS_REPLACE, NULL, FLOW_CLS_REPLACE, NULL,
&offload->flowtable->flow_block.cb_list); &offload->flowtable->flow_block.cb_list);
} }
...@@ -883,7 +883,8 @@ static void flow_offload_tuple_del(struct flow_offload_work *offload, ...@@ -883,7 +883,8 @@ static void flow_offload_tuple_del(struct flow_offload_work *offload,
enum flow_offload_tuple_dir dir) enum flow_offload_tuple_dir dir)
{ {
nf_flow_offload_tuple(offload->flowtable, offload->flow, NULL, dir, nf_flow_offload_tuple(offload->flowtable, offload->flow, NULL, dir,
offload->priority, FLOW_CLS_DESTROY, NULL, offload->flowtable->priority,
FLOW_CLS_DESTROY, NULL,
&offload->flowtable->flow_block.cb_list); &offload->flowtable->flow_block.cb_list);
} }
...@@ -934,7 +935,8 @@ static void flow_offload_tuple_stats(struct flow_offload_work *offload, ...@@ -934,7 +935,8 @@ static void flow_offload_tuple_stats(struct flow_offload_work *offload,
struct flow_stats *stats) struct flow_stats *stats)
{ {
nf_flow_offload_tuple(offload->flowtable, offload->flow, NULL, dir, nf_flow_offload_tuple(offload->flowtable, offload->flow, NULL, dir,
offload->priority, FLOW_CLS_STATS, stats, offload->flowtable->priority,
FLOW_CLS_STATS, stats,
&offload->flowtable->flow_block.cb_list); &offload->flowtable->flow_block.cb_list);
} }
...@@ -1012,7 +1014,6 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable, ...@@ -1012,7 +1014,6 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable,
offload->cmd = cmd; offload->cmd = cmd;
offload->flow = flow; offload->flow = flow;
offload->priority = flowtable->priority;
offload->flowtable = flowtable; offload->flowtable = flowtable;
INIT_WORK(&offload->work, flow_offload_work_handler); INIT_WORK(&offload->work, flow_offload_work_handler);
......
...@@ -550,6 +550,58 @@ static int nft_delflowtable(struct nft_ctx *ctx, ...@@ -550,6 +550,58 @@ static int nft_delflowtable(struct nft_ctx *ctx,
return err; return err;
} }
static void __nft_reg_track_clobber(struct nft_regs_track *track, u8 dreg)
{
int i;
for (i = track->regs[dreg].num_reg; i > 0; i--)
__nft_reg_track_cancel(track, dreg - i);
}
static void __nft_reg_track_update(struct nft_regs_track *track,
const struct nft_expr *expr,
u8 dreg, u8 num_reg)
{
track->regs[dreg].selector = expr;
track->regs[dreg].bitwise = NULL;
track->regs[dreg].num_reg = num_reg;
}
void nft_reg_track_update(struct nft_regs_track *track,
const struct nft_expr *expr, u8 dreg, u8 len)
{
unsigned int regcount;
int i;
__nft_reg_track_clobber(track, dreg);
regcount = DIV_ROUND_UP(len, NFT_REG32_SIZE);
for (i = 0; i < regcount; i++, dreg++)
__nft_reg_track_update(track, expr, dreg, i);
}
EXPORT_SYMBOL_GPL(nft_reg_track_update);
void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len)
{
unsigned int regcount;
int i;
__nft_reg_track_clobber(track, dreg);
regcount = DIV_ROUND_UP(len, NFT_REG32_SIZE);
for (i = 0; i < regcount; i++, dreg++)
__nft_reg_track_cancel(track, dreg);
}
EXPORT_SYMBOL_GPL(nft_reg_track_cancel);
void __nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg)
{
track->regs[dreg].selector = NULL;
track->regs[dreg].bitwise = NULL;
track->regs[dreg].num_reg = 0;
}
EXPORT_SYMBOL_GPL(__nft_reg_track_cancel);
/* /*
* Tables * Tables
*/ */
...@@ -8290,7 +8342,16 @@ EXPORT_SYMBOL_GPL(nf_tables_trans_destroy_flush_work); ...@@ -8290,7 +8342,16 @@ EXPORT_SYMBOL_GPL(nf_tables_trans_destroy_flush_work);
static bool nft_expr_reduce(struct nft_regs_track *track, static bool nft_expr_reduce(struct nft_regs_track *track,
const struct nft_expr *expr) const struct nft_expr *expr)
{ {
if (!expr->ops->reduce) {
pr_warn_once("missing reduce for expression %s ",
expr->ops->type->name);
return false;
}
if (nft_reduce_is_readonly(expr))
return false; return false;
return expr->ops->reduce(track, expr);
} }
static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *chain) static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *chain)
......
...@@ -283,12 +283,16 @@ static bool nft_bitwise_reduce(struct nft_regs_track *track, ...@@ -283,12 +283,16 @@ static bool nft_bitwise_reduce(struct nft_regs_track *track,
{ {
const struct nft_bitwise *priv = nft_expr_priv(expr); const struct nft_bitwise *priv = nft_expr_priv(expr);
const struct nft_bitwise *bitwise; const struct nft_bitwise *bitwise;
unsigned int regcount;
u8 dreg;
int i;
if (!track->regs[priv->sreg].selector) if (!track->regs[priv->sreg].selector)
return false; return false;
bitwise = nft_expr_priv(expr); bitwise = nft_expr_priv(expr);
if (track->regs[priv->sreg].selector == track->regs[priv->dreg].selector && if (track->regs[priv->sreg].selector == track->regs[priv->dreg].selector &&
track->regs[priv->sreg].num_reg == 0 &&
track->regs[priv->dreg].bitwise && track->regs[priv->dreg].bitwise &&
track->regs[priv->dreg].bitwise->ops == expr->ops && track->regs[priv->dreg].bitwise->ops == expr->ops &&
priv->sreg == bitwise->sreg && priv->sreg == bitwise->sreg &&
...@@ -302,16 +306,20 @@ static bool nft_bitwise_reduce(struct nft_regs_track *track, ...@@ -302,16 +306,20 @@ static bool nft_bitwise_reduce(struct nft_regs_track *track,
return true; return true;
} }
if (track->regs[priv->sreg].bitwise) { if (track->regs[priv->sreg].bitwise ||
track->regs[priv->dreg].selector = NULL; track->regs[priv->sreg].num_reg != 0) {
track->regs[priv->dreg].bitwise = NULL; nft_reg_track_cancel(track, priv->dreg, priv->len);
return false; return false;
} }
if (priv->sreg != priv->dreg) { if (priv->sreg != priv->dreg) {
track->regs[priv->dreg].selector = nft_reg_track_update(track, track->regs[priv->sreg].selector,
track->regs[priv->sreg].selector; priv->dreg, priv->len);
} }
dreg = priv->dreg;
regcount = DIV_ROUND_UP(priv->len, NFT_REG32_SIZE);
for (i = 0; i < regcount; i++, dreg++)
track->regs[priv->dreg].bitwise = expr; track->regs[priv->dreg].bitwise = expr;
return false; return false;
...@@ -447,8 +455,7 @@ static bool nft_bitwise_fast_reduce(struct nft_regs_track *track, ...@@ -447,8 +455,7 @@ static bool nft_bitwise_fast_reduce(struct nft_regs_track *track,
} }
if (track->regs[priv->sreg].bitwise) { if (track->regs[priv->sreg].bitwise) {
track->regs[priv->dreg].selector = NULL; nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE);
track->regs[priv->dreg].bitwise = NULL;
return false; return false;
} }
...@@ -522,3 +529,4 @@ bool nft_expr_reduce_bitwise(struct nft_regs_track *track, ...@@ -522,3 +529,4 @@ bool nft_expr_reduce_bitwise(struct nft_regs_track *track,
return false; return false;
} }
EXPORT_SYMBOL_GPL(nft_expr_reduce_bitwise);
...@@ -172,8 +172,7 @@ static bool nft_byteorder_reduce(struct nft_regs_track *track, ...@@ -172,8 +172,7 @@ static bool nft_byteorder_reduce(struct nft_regs_track *track,
{ {
struct nft_byteorder *priv = nft_expr_priv(expr); struct nft_byteorder *priv = nft_expr_priv(expr);
track->regs[priv->dreg].selector = NULL; nft_reg_track_cancel(track, priv->dreg, priv->len);
track->regs[priv->dreg].bitwise = NULL;
return false; return false;
} }
......
...@@ -193,6 +193,7 @@ static const struct nft_expr_ops nft_cmp_ops = { ...@@ -193,6 +193,7 @@ static const struct nft_expr_ops nft_cmp_ops = {
.eval = nft_cmp_eval, .eval = nft_cmp_eval,
.init = nft_cmp_init, .init = nft_cmp_init,
.dump = nft_cmp_dump, .dump = nft_cmp_dump,
.reduce = NFT_REDUCE_READONLY,
.offload = nft_cmp_offload, .offload = nft_cmp_offload,
}; };
...@@ -269,6 +270,7 @@ const struct nft_expr_ops nft_cmp_fast_ops = { ...@@ -269,6 +270,7 @@ const struct nft_expr_ops nft_cmp_fast_ops = {
.eval = NULL, /* inlined */ .eval = NULL, /* inlined */
.init = nft_cmp_fast_init, .init = nft_cmp_fast_init,
.dump = nft_cmp_fast_dump, .dump = nft_cmp_fast_dump,
.reduce = NFT_REDUCE_READONLY,
.offload = nft_cmp_fast_offload, .offload = nft_cmp_fast_offload,
}; };
...@@ -359,6 +361,7 @@ const struct nft_expr_ops nft_cmp16_fast_ops = { ...@@ -359,6 +361,7 @@ const struct nft_expr_ops nft_cmp16_fast_ops = {
.eval = NULL, /* inlined */ .eval = NULL, /* inlined */
.init = nft_cmp16_fast_init, .init = nft_cmp16_fast_init,
.dump = nft_cmp16_fast_dump, .dump = nft_cmp16_fast_dump,
.reduce = NFT_REDUCE_READONLY,
.offload = nft_cmp16_fast_offload, .offload = nft_cmp16_fast_offload,
}; };
......
...@@ -871,6 +871,7 @@ nft_target_select_ops(const struct nft_ctx *ctx, ...@@ -871,6 +871,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
ops->dump = nft_target_dump; ops->dump = nft_target_dump;
ops->validate = nft_target_validate; ops->validate = nft_target_validate;
ops->data = target; ops->data = target;
ops->reduce = NFT_REDUCE_READONLY;
if (family == NFPROTO_BRIDGE) if (family == NFPROTO_BRIDGE)
ops->eval = nft_target_eval_bridge; ops->eval = nft_target_eval_bridge;
......
...@@ -257,6 +257,7 @@ static const struct nft_expr_ops nft_connlimit_ops = { ...@@ -257,6 +257,7 @@ static const struct nft_expr_ops nft_connlimit_ops = {
.destroy_clone = nft_connlimit_destroy_clone, .destroy_clone = nft_connlimit_destroy_clone,
.dump = nft_connlimit_dump, .dump = nft_connlimit_dump,
.gc = nft_connlimit_gc, .gc = nft_connlimit_gc,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_connlimit_type __read_mostly = { static struct nft_expr_type nft_connlimit_type __read_mostly = {
......
...@@ -293,6 +293,7 @@ static const struct nft_expr_ops nft_counter_ops = { ...@@ -293,6 +293,7 @@ static const struct nft_expr_ops nft_counter_ops = {
.destroy_clone = nft_counter_destroy, .destroy_clone = nft_counter_destroy,
.dump = nft_counter_dump, .dump = nft_counter_dump,
.clone = nft_counter_clone, .clone = nft_counter_clone,
.reduce = NFT_REDUCE_READONLY,
.offload = nft_counter_offload, .offload = nft_counter_offload,
.offload_stats = nft_counter_offload_stats, .offload_stats = nft_counter_offload_stats,
}; };
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
struct nft_ct { struct nft_ct {
enum nft_ct_keys key:8; enum nft_ct_keys key:8;
enum ip_conntrack_dir dir:8; enum ip_conntrack_dir dir:8;
u8 len;
union { union {
u8 dreg; u8 dreg;
u8 sreg; u8 sreg;
...@@ -500,6 +501,7 @@ static int nft_ct_get_init(const struct nft_ctx *ctx, ...@@ -500,6 +501,7 @@ static int nft_ct_get_init(const struct nft_ctx *ctx,
} }
} }
priv->len = len;
err = nft_parse_register_store(ctx, tb[NFTA_CT_DREG], &priv->dreg, NULL, err = nft_parse_register_store(ctx, tb[NFTA_CT_DREG], &priv->dreg, NULL,
NFT_DATA_VALUE, len); NFT_DATA_VALUE, len);
if (err < 0) if (err < 0)
...@@ -608,6 +610,7 @@ static int nft_ct_set_init(const struct nft_ctx *ctx, ...@@ -608,6 +610,7 @@ static int nft_ct_set_init(const struct nft_ctx *ctx,
} }
} }
priv->len = len;
err = nft_parse_register_load(tb[NFTA_CT_SREG], &priv->sreg, len); err = nft_parse_register_load(tb[NFTA_CT_SREG], &priv->sreg, len);
if (err < 0) if (err < 0)
goto err1; goto err1;
...@@ -677,6 +680,29 @@ static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr) ...@@ -677,6 +680,29 @@ static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
return -1; return -1;
} }
static bool nft_ct_get_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
const struct nft_ct *priv = nft_expr_priv(expr);
const struct nft_ct *ct;
if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}
ct = nft_expr_priv(track->regs[priv->dreg].selector);
if (priv->key != ct->key) {
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}
if (!track->regs[priv->dreg].bitwise)
return true;
return nft_expr_reduce_bitwise(track, expr);
}
static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr) static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
{ {
const struct nft_ct *priv = nft_expr_priv(expr); const struct nft_ct *priv = nft_expr_priv(expr);
...@@ -710,8 +736,27 @@ static const struct nft_expr_ops nft_ct_get_ops = { ...@@ -710,8 +736,27 @@ static const struct nft_expr_ops nft_ct_get_ops = {
.init = nft_ct_get_init, .init = nft_ct_get_init,
.destroy = nft_ct_get_destroy, .destroy = nft_ct_get_destroy,
.dump = nft_ct_get_dump, .dump = nft_ct_get_dump,
.reduce = nft_ct_get_reduce,
}; };
static bool nft_ct_set_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
int i;
for (i = 0; i < NFT_REG32_NUM; i++) {
if (!track->regs[i].selector)
continue;
if (track->regs[i].selector->ops != &nft_ct_get_ops)
continue;
__nft_reg_track_cancel(track, i);
}
return false;
}
static const struct nft_expr_ops nft_ct_set_ops = { static const struct nft_expr_ops nft_ct_set_ops = {
.type = &nft_ct_type, .type = &nft_ct_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
...@@ -719,6 +764,7 @@ static const struct nft_expr_ops nft_ct_set_ops = { ...@@ -719,6 +764,7 @@ static const struct nft_expr_ops nft_ct_set_ops = {
.init = nft_ct_set_init, .init = nft_ct_set_init,
.destroy = nft_ct_set_destroy, .destroy = nft_ct_set_destroy,
.dump = nft_ct_set_dump, .dump = nft_ct_set_dump,
.reduce = nft_ct_set_reduce,
}; };
#ifdef CONFIG_NF_CONNTRACK_ZONES #ifdef CONFIG_NF_CONNTRACK_ZONES
...@@ -729,6 +775,7 @@ static const struct nft_expr_ops nft_ct_set_zone_ops = { ...@@ -729,6 +775,7 @@ static const struct nft_expr_ops nft_ct_set_zone_ops = {
.init = nft_ct_set_init, .init = nft_ct_set_init,
.destroy = nft_ct_set_destroy, .destroy = nft_ct_set_destroy,
.dump = nft_ct_set_dump, .dump = nft_ct_set_dump,
.reduce = nft_ct_set_reduce,
}; };
#endif #endif
...@@ -785,6 +832,7 @@ static const struct nft_expr_ops nft_notrack_ops = { ...@@ -785,6 +832,7 @@ static const struct nft_expr_ops nft_notrack_ops = {
.type = &nft_notrack_type, .type = &nft_notrack_type,
.size = NFT_EXPR_SIZE(0), .size = NFT_EXPR_SIZE(0),
.eval = nft_notrack_eval, .eval = nft_notrack_eval,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_notrack_type __read_mostly = { static struct nft_expr_type nft_notrack_type __read_mostly = {
...@@ -1041,6 +1089,9 @@ static int nft_ct_helper_obj_init(const struct nft_ctx *ctx, ...@@ -1041,6 +1089,9 @@ static int nft_ct_helper_obj_init(const struct nft_ctx *ctx,
if (err < 0) if (err < 0)
goto err_put_helper; goto err_put_helper;
/* Avoid the bogus warning, helper will be assigned after CT init */
nf_ct_set_auto_assign_helper_warned(ctx->net);
return 0; return 0;
err_put_helper: err_put_helper:
......
...@@ -79,6 +79,7 @@ static const struct nft_expr_ops nft_dup_netdev_ops = { ...@@ -79,6 +79,7 @@ static const struct nft_expr_ops nft_dup_netdev_ops = {
.eval = nft_dup_netdev_eval, .eval = nft_dup_netdev_eval,
.init = nft_dup_netdev_init, .init = nft_dup_netdev_init,
.dump = nft_dup_netdev_dump, .dump = nft_dup_netdev_dump,
.reduce = NFT_REDUCE_READONLY,
.offload = nft_dup_netdev_offload, .offload = nft_dup_netdev_offload,
.offload_action = nft_dup_netdev_offload_action, .offload_action = nft_dup_netdev_offload_action,
}; };
......
...@@ -413,6 +413,7 @@ static const struct nft_expr_ops nft_dynset_ops = { ...@@ -413,6 +413,7 @@ static const struct nft_expr_ops nft_dynset_ops = {
.activate = nft_dynset_activate, .activate = nft_dynset_activate,
.deactivate = nft_dynset_deactivate, .deactivate = nft_dynset_deactivate,
.dump = nft_dynset_dump, .dump = nft_dynset_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
struct nft_expr_type nft_dynset_type __read_mostly = { struct nft_expr_type nft_dynset_type __read_mostly = {
......
...@@ -603,12 +603,40 @@ static int nft_exthdr_dump_strip(struct sk_buff *skb, const struct nft_expr *exp ...@@ -603,12 +603,40 @@ static int nft_exthdr_dump_strip(struct sk_buff *skb, const struct nft_expr *exp
return nft_exthdr_dump_common(skb, priv); return nft_exthdr_dump_common(skb, priv);
} }
static bool nft_exthdr_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
const struct nft_exthdr *priv = nft_expr_priv(expr);
const struct nft_exthdr *exthdr;
if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}
exthdr = nft_expr_priv(track->regs[priv->dreg].selector);
if (priv->type != exthdr->type ||
priv->op != exthdr->op ||
priv->flags != exthdr->flags ||
priv->offset != exthdr->offset ||
priv->len != exthdr->len) {
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}
if (!track->regs[priv->dreg].bitwise)
return true;
return nft_expr_reduce_bitwise(track, expr);
}
static const struct nft_expr_ops nft_exthdr_ipv6_ops = { static const struct nft_expr_ops nft_exthdr_ipv6_ops = {
.type = &nft_exthdr_type, .type = &nft_exthdr_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
.eval = nft_exthdr_ipv6_eval, .eval = nft_exthdr_ipv6_eval,
.init = nft_exthdr_init, .init = nft_exthdr_init,
.dump = nft_exthdr_dump, .dump = nft_exthdr_dump,
.reduce = nft_exthdr_reduce,
}; };
static const struct nft_expr_ops nft_exthdr_ipv4_ops = { static const struct nft_expr_ops nft_exthdr_ipv4_ops = {
...@@ -617,6 +645,7 @@ static const struct nft_expr_ops nft_exthdr_ipv4_ops = { ...@@ -617,6 +645,7 @@ static const struct nft_expr_ops nft_exthdr_ipv4_ops = {
.eval = nft_exthdr_ipv4_eval, .eval = nft_exthdr_ipv4_eval,
.init = nft_exthdr_ipv4_init, .init = nft_exthdr_ipv4_init,
.dump = nft_exthdr_dump, .dump = nft_exthdr_dump,
.reduce = nft_exthdr_reduce,
}; };
static const struct nft_expr_ops nft_exthdr_tcp_ops = { static const struct nft_expr_ops nft_exthdr_tcp_ops = {
...@@ -625,6 +654,7 @@ static const struct nft_expr_ops nft_exthdr_tcp_ops = { ...@@ -625,6 +654,7 @@ static const struct nft_expr_ops nft_exthdr_tcp_ops = {
.eval = nft_exthdr_tcp_eval, .eval = nft_exthdr_tcp_eval,
.init = nft_exthdr_init, .init = nft_exthdr_init,
.dump = nft_exthdr_dump, .dump = nft_exthdr_dump,
.reduce = nft_exthdr_reduce,
}; };
static const struct nft_expr_ops nft_exthdr_tcp_set_ops = { static const struct nft_expr_ops nft_exthdr_tcp_set_ops = {
...@@ -633,6 +663,7 @@ static const struct nft_expr_ops nft_exthdr_tcp_set_ops = { ...@@ -633,6 +663,7 @@ static const struct nft_expr_ops nft_exthdr_tcp_set_ops = {
.eval = nft_exthdr_tcp_set_eval, .eval = nft_exthdr_tcp_set_eval,
.init = nft_exthdr_tcp_set_init, .init = nft_exthdr_tcp_set_init,
.dump = nft_exthdr_dump_set, .dump = nft_exthdr_dump_set,
.reduce = NFT_REDUCE_READONLY,
}; };
static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = { static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = {
...@@ -641,6 +672,7 @@ static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = { ...@@ -641,6 +672,7 @@ static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = {
.eval = nft_exthdr_tcp_strip_eval, .eval = nft_exthdr_tcp_strip_eval,
.init = nft_exthdr_tcp_strip_init, .init = nft_exthdr_tcp_strip_init,
.dump = nft_exthdr_dump_strip, .dump = nft_exthdr_dump_strip,
.reduce = NFT_REDUCE_READONLY,
}; };
static const struct nft_expr_ops nft_exthdr_sctp_ops = { static const struct nft_expr_ops nft_exthdr_sctp_ops = {
...@@ -649,6 +681,7 @@ static const struct nft_expr_ops nft_exthdr_sctp_ops = { ...@@ -649,6 +681,7 @@ static const struct nft_expr_ops nft_exthdr_sctp_ops = {
.eval = nft_exthdr_sctp_eval, .eval = nft_exthdr_sctp_eval,
.init = nft_exthdr_init, .init = nft_exthdr_init,
.dump = nft_exthdr_dump, .dump = nft_exthdr_dump,
.reduce = nft_exthdr_reduce,
}; };
static const struct nft_expr_ops * static const struct nft_expr_ops *
......
...@@ -156,5 +156,47 @@ void nft_fib_store_result(void *reg, const struct nft_fib *priv, ...@@ -156,5 +156,47 @@ void nft_fib_store_result(void *reg, const struct nft_fib *priv,
} }
EXPORT_SYMBOL_GPL(nft_fib_store_result); EXPORT_SYMBOL_GPL(nft_fib_store_result);
bool nft_fib_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
const struct nft_fib *priv = nft_expr_priv(expr);
unsigned int len = NFT_REG32_SIZE;
const struct nft_fib *fib;
switch (priv->result) {
case NFT_FIB_RESULT_OIF:
break;
case NFT_FIB_RESULT_OIFNAME:
if (priv->flags & NFTA_FIB_F_PRESENT)
len = NFT_REG32_SIZE;
else
len = IFNAMSIZ;
break;
case NFT_FIB_RESULT_ADDRTYPE:
break;
default:
WARN_ON_ONCE(1);
break;
}
if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
nft_reg_track_update(track, expr, priv->dreg, len);
return false;
}
fib = nft_expr_priv(track->regs[priv->dreg].selector);
if (priv->result != fib->result ||
priv->flags != fib->flags) {
nft_reg_track_update(track, expr, priv->dreg, len);
return false;
}
if (!track->regs[priv->dreg].bitwise)
return true;
return false;
}
EXPORT_SYMBOL_GPL(nft_fib_reduce);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
...@@ -49,6 +49,7 @@ static const struct nft_expr_ops nft_fib_inet_ops = { ...@@ -49,6 +49,7 @@ static const struct nft_expr_ops nft_fib_inet_ops = {
.init = nft_fib_init, .init = nft_fib_init,
.dump = nft_fib_dump, .dump = nft_fib_dump,
.validate = nft_fib_validate, .validate = nft_fib_validate,
.reduce = nft_fib_reduce,
}; };
static struct nft_expr_type nft_fib_inet_type __read_mostly = { static struct nft_expr_type nft_fib_inet_type __read_mostly = {
......
...@@ -58,6 +58,7 @@ static const struct nft_expr_ops nft_fib_netdev_ops = { ...@@ -58,6 +58,7 @@ static const struct nft_expr_ops nft_fib_netdev_ops = {
.init = nft_fib_init, .init = nft_fib_init,
.dump = nft_fib_dump, .dump = nft_fib_dump,
.validate = nft_fib_validate, .validate = nft_fib_validate,
.reduce = nft_fib_reduce,
}; };
static struct nft_expr_type nft_fib_netdev_type __read_mostly = { static struct nft_expr_type nft_fib_netdev_type __read_mostly = {
......
...@@ -441,6 +441,7 @@ static const struct nft_expr_ops nft_flow_offload_ops = { ...@@ -441,6 +441,7 @@ static const struct nft_expr_ops nft_flow_offload_ops = {
.destroy = nft_flow_offload_destroy, .destroy = nft_flow_offload_destroy,
.validate = nft_flow_offload_validate, .validate = nft_flow_offload_validate,
.dump = nft_flow_offload_dump, .dump = nft_flow_offload_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_flow_offload_type __read_mostly = { static struct nft_expr_type nft_flow_offload_type __read_mostly = {
......
...@@ -217,6 +217,7 @@ static const struct nft_expr_ops nft_fwd_neigh_netdev_ops = { ...@@ -217,6 +217,7 @@ static const struct nft_expr_ops nft_fwd_neigh_netdev_ops = {
.init = nft_fwd_neigh_init, .init = nft_fwd_neigh_init,
.dump = nft_fwd_neigh_dump, .dump = nft_fwd_neigh_dump,
.validate = nft_fwd_validate, .validate = nft_fwd_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static const struct nft_expr_ops nft_fwd_netdev_ops = { static const struct nft_expr_ops nft_fwd_netdev_ops = {
...@@ -226,6 +227,7 @@ static const struct nft_expr_ops nft_fwd_netdev_ops = { ...@@ -226,6 +227,7 @@ static const struct nft_expr_ops nft_fwd_netdev_ops = {
.init = nft_fwd_netdev_init, .init = nft_fwd_netdev_init,
.dump = nft_fwd_netdev_dump, .dump = nft_fwd_netdev_dump,
.validate = nft_fwd_validate, .validate = nft_fwd_validate,
.reduce = NFT_REDUCE_READONLY,
.offload = nft_fwd_netdev_offload, .offload = nft_fwd_netdev_offload,
.offload_action = nft_fwd_netdev_offload_action, .offload_action = nft_fwd_netdev_offload_action,
}; };
......
...@@ -165,6 +165,16 @@ static int nft_jhash_dump(struct sk_buff *skb, ...@@ -165,6 +165,16 @@ static int nft_jhash_dump(struct sk_buff *skb,
return -1; return -1;
} }
static bool nft_jhash_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
const struct nft_jhash *priv = nft_expr_priv(expr);
nft_reg_track_cancel(track, priv->dreg, sizeof(u32));
return false;
}
static int nft_symhash_dump(struct sk_buff *skb, static int nft_symhash_dump(struct sk_buff *skb,
const struct nft_expr *expr) const struct nft_expr *expr)
{ {
...@@ -185,6 +195,30 @@ static int nft_symhash_dump(struct sk_buff *skb, ...@@ -185,6 +195,30 @@ static int nft_symhash_dump(struct sk_buff *skb,
return -1; return -1;
} }
static bool nft_symhash_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
struct nft_symhash *priv = nft_expr_priv(expr);
struct nft_symhash *symhash;
if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
nft_reg_track_update(track, expr, priv->dreg, sizeof(u32));
return false;
}
symhash = nft_expr_priv(track->regs[priv->dreg].selector);
if (priv->offset != symhash->offset ||
priv->modulus != symhash->modulus) {
nft_reg_track_update(track, expr, priv->dreg, sizeof(u32));
return false;
}
if (!track->regs[priv->dreg].bitwise)
return true;
return false;
}
static struct nft_expr_type nft_hash_type; static struct nft_expr_type nft_hash_type;
static const struct nft_expr_ops nft_jhash_ops = { static const struct nft_expr_ops nft_jhash_ops = {
.type = &nft_hash_type, .type = &nft_hash_type,
...@@ -192,6 +226,7 @@ static const struct nft_expr_ops nft_jhash_ops = { ...@@ -192,6 +226,7 @@ static const struct nft_expr_ops nft_jhash_ops = {
.eval = nft_jhash_eval, .eval = nft_jhash_eval,
.init = nft_jhash_init, .init = nft_jhash_init,
.dump = nft_jhash_dump, .dump = nft_jhash_dump,
.reduce = nft_jhash_reduce,
}; };
static const struct nft_expr_ops nft_symhash_ops = { static const struct nft_expr_ops nft_symhash_ops = {
...@@ -200,6 +235,7 @@ static const struct nft_expr_ops nft_symhash_ops = { ...@@ -200,6 +235,7 @@ static const struct nft_expr_ops nft_symhash_ops = {
.eval = nft_symhash_eval, .eval = nft_symhash_eval,
.init = nft_symhash_init, .init = nft_symhash_init,
.dump = nft_symhash_dump, .dump = nft_symhash_dump,
.reduce = nft_symhash_reduce,
}; };
static const struct nft_expr_ops * static const struct nft_expr_ops *
......
...@@ -223,6 +223,17 @@ static bool nft_immediate_offload_action(const struct nft_expr *expr) ...@@ -223,6 +223,17 @@ static bool nft_immediate_offload_action(const struct nft_expr *expr)
return false; return false;
} }
static bool nft_immediate_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
const struct nft_immediate_expr *priv = nft_expr_priv(expr);
if (priv->dreg != NFT_REG_VERDICT)
nft_reg_track_cancel(track, priv->dreg, priv->dlen);
return false;
}
static const struct nft_expr_ops nft_imm_ops = { static const struct nft_expr_ops nft_imm_ops = {
.type = &nft_imm_type, .type = &nft_imm_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)), .size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)),
...@@ -233,6 +244,7 @@ static const struct nft_expr_ops nft_imm_ops = { ...@@ -233,6 +244,7 @@ static const struct nft_expr_ops nft_imm_ops = {
.destroy = nft_immediate_destroy, .destroy = nft_immediate_destroy,
.dump = nft_immediate_dump, .dump = nft_immediate_dump,
.validate = nft_immediate_validate, .validate = nft_immediate_validate,
.reduce = nft_immediate_reduce,
.offload = nft_immediate_offload, .offload = nft_immediate_offload,
.offload_action = nft_immediate_offload_action, .offload_action = nft_immediate_offload_action,
}; };
......
...@@ -120,6 +120,7 @@ static const struct nft_expr_ops nft_last_ops = { ...@@ -120,6 +120,7 @@ static const struct nft_expr_ops nft_last_ops = {
.destroy = nft_last_destroy, .destroy = nft_last_destroy,
.clone = nft_last_clone, .clone = nft_last_clone,
.dump = nft_last_dump, .dump = nft_last_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
struct nft_expr_type nft_last_type __read_mostly = { struct nft_expr_type nft_last_type __read_mostly = {
......
...@@ -225,6 +225,7 @@ static const struct nft_expr_ops nft_limit_pkts_ops = { ...@@ -225,6 +225,7 @@ static const struct nft_expr_ops nft_limit_pkts_ops = {
.destroy = nft_limit_pkts_destroy, .destroy = nft_limit_pkts_destroy,
.clone = nft_limit_pkts_clone, .clone = nft_limit_pkts_clone,
.dump = nft_limit_pkts_dump, .dump = nft_limit_pkts_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
static void nft_limit_bytes_eval(const struct nft_expr *expr, static void nft_limit_bytes_eval(const struct nft_expr *expr,
...@@ -279,6 +280,7 @@ static const struct nft_expr_ops nft_limit_bytes_ops = { ...@@ -279,6 +280,7 @@ static const struct nft_expr_ops nft_limit_bytes_ops = {
.dump = nft_limit_bytes_dump, .dump = nft_limit_bytes_dump,
.clone = nft_limit_bytes_clone, .clone = nft_limit_bytes_clone,
.destroy = nft_limit_bytes_destroy, .destroy = nft_limit_bytes_destroy,
.reduce = NFT_REDUCE_READONLY,
}; };
static const struct nft_expr_ops * static const struct nft_expr_ops *
......
...@@ -290,6 +290,7 @@ static const struct nft_expr_ops nft_log_ops = { ...@@ -290,6 +290,7 @@ static const struct nft_expr_ops nft_log_ops = {
.init = nft_log_init, .init = nft_log_init,
.destroy = nft_log_destroy, .destroy = nft_log_destroy,
.dump = nft_log_dump, .dump = nft_log_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_log_type __read_mostly = { static struct nft_expr_type nft_log_type __read_mostly = {
......
...@@ -253,6 +253,17 @@ static int nft_lookup_validate(const struct nft_ctx *ctx, ...@@ -253,6 +253,17 @@ static int nft_lookup_validate(const struct nft_ctx *ctx,
return 0; return 0;
} }
static bool nft_lookup_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
const struct nft_lookup *priv = nft_expr_priv(expr);
if (priv->set->flags & NFT_SET_MAP)
nft_reg_track_cancel(track, priv->dreg, priv->set->dlen);
return false;
}
static const struct nft_expr_ops nft_lookup_ops = { static const struct nft_expr_ops nft_lookup_ops = {
.type = &nft_lookup_type, .type = &nft_lookup_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)), .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)),
...@@ -263,6 +274,7 @@ static const struct nft_expr_ops nft_lookup_ops = { ...@@ -263,6 +274,7 @@ static const struct nft_expr_ops nft_lookup_ops = {
.destroy = nft_lookup_destroy, .destroy = nft_lookup_destroy,
.dump = nft_lookup_dump, .dump = nft_lookup_dump,
.validate = nft_lookup_validate, .validate = nft_lookup_validate,
.reduce = nft_lookup_reduce,
}; };
struct nft_expr_type nft_lookup_type __read_mostly = { struct nft_expr_type nft_lookup_type __read_mostly = {
......
...@@ -129,6 +129,7 @@ static const struct nft_expr_ops nft_masq_ipv4_ops = { ...@@ -129,6 +129,7 @@ static const struct nft_expr_ops nft_masq_ipv4_ops = {
.destroy = nft_masq_ipv4_destroy, .destroy = nft_masq_ipv4_destroy,
.dump = nft_masq_dump, .dump = nft_masq_dump,
.validate = nft_masq_validate, .validate = nft_masq_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_masq_ipv4_type __read_mostly = { static struct nft_expr_type nft_masq_ipv4_type __read_mostly = {
...@@ -175,6 +176,7 @@ static const struct nft_expr_ops nft_masq_ipv6_ops = { ...@@ -175,6 +176,7 @@ static const struct nft_expr_ops nft_masq_ipv6_ops = {
.destroy = nft_masq_ipv6_destroy, .destroy = nft_masq_ipv6_destroy,
.dump = nft_masq_dump, .dump = nft_masq_dump,
.validate = nft_masq_validate, .validate = nft_masq_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_masq_ipv6_type __read_mostly = { static struct nft_expr_type nft_masq_ipv6_type __read_mostly = {
...@@ -230,6 +232,7 @@ static const struct nft_expr_ops nft_masq_inet_ops = { ...@@ -230,6 +232,7 @@ static const struct nft_expr_ops nft_masq_inet_ops = {
.destroy = nft_masq_inet_destroy, .destroy = nft_masq_inet_destroy,
.dump = nft_masq_dump, .dump = nft_masq_dump,
.validate = nft_masq_validate, .validate = nft_masq_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_masq_inet_type __read_mostly = { static struct nft_expr_type nft_masq_inet_type __read_mostly = {
......
...@@ -539,6 +539,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx, ...@@ -539,6 +539,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
priv->len = len;
return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg, return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg,
NULL, NFT_DATA_VALUE, len); NULL, NFT_DATA_VALUE, len);
} }
...@@ -664,6 +665,7 @@ int nft_meta_set_init(const struct nft_ctx *ctx, ...@@ -664,6 +665,7 @@ int nft_meta_set_init(const struct nft_ctx *ctx,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
priv->len = len;
err = nft_parse_register_load(tb[NFTA_META_SREG], &priv->sreg, len); err = nft_parse_register_load(tb[NFTA_META_SREG], &priv->sreg, len);
if (err < 0) if (err < 0)
return err; return err;
...@@ -750,24 +752,21 @@ static int nft_meta_get_offload(struct nft_offload_ctx *ctx, ...@@ -750,24 +752,21 @@ static int nft_meta_get_offload(struct nft_offload_ctx *ctx,
return 0; return 0;
} }
static bool nft_meta_get_reduce(struct nft_regs_track *track, bool nft_meta_get_reduce(struct nft_regs_track *track,
const struct nft_expr *expr) const struct nft_expr *expr)
{ {
const struct nft_meta *priv = nft_expr_priv(expr); const struct nft_meta *priv = nft_expr_priv(expr);
const struct nft_meta *meta; const struct nft_meta *meta;
if (!track->regs[priv->dreg].selector || if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
track->regs[priv->dreg].selector->ops != expr->ops) { nft_reg_track_update(track, expr, priv->dreg, priv->len);
track->regs[priv->dreg].selector = expr;
track->regs[priv->dreg].bitwise = NULL;
return false; return false;
} }
meta = nft_expr_priv(track->regs[priv->dreg].selector); meta = nft_expr_priv(track->regs[priv->dreg].selector);
if (priv->key != meta->key || if (priv->key != meta->key ||
priv->dreg != meta->dreg) { priv->dreg != meta->dreg) {
track->regs[priv->dreg].selector = expr; nft_reg_track_update(track, expr, priv->dreg, priv->len);
track->regs[priv->dreg].bitwise = NULL;
return false; return false;
} }
...@@ -776,6 +775,7 @@ static bool nft_meta_get_reduce(struct nft_regs_track *track, ...@@ -776,6 +775,7 @@ static bool nft_meta_get_reduce(struct nft_regs_track *track,
return nft_expr_reduce_bitwise(track, expr); return nft_expr_reduce_bitwise(track, expr);
} }
EXPORT_SYMBOL_GPL(nft_meta_get_reduce);
static const struct nft_expr_ops nft_meta_get_ops = { static const struct nft_expr_ops nft_meta_get_ops = {
.type = &nft_meta_type, .type = &nft_meta_type,
...@@ -800,8 +800,7 @@ static bool nft_meta_set_reduce(struct nft_regs_track *track, ...@@ -800,8 +800,7 @@ static bool nft_meta_set_reduce(struct nft_regs_track *track,
if (track->regs[i].selector->ops != &nft_meta_get_ops) if (track->regs[i].selector->ops != &nft_meta_get_ops)
continue; continue;
track->regs[i].selector = NULL; __nft_reg_track_cancel(track, i);
track->regs[i].bitwise = NULL;
} }
return false; return false;
......
...@@ -317,6 +317,7 @@ static const struct nft_expr_ops nft_nat_ops = { ...@@ -317,6 +317,7 @@ static const struct nft_expr_ops nft_nat_ops = {
.destroy = nft_nat_destroy, .destroy = nft_nat_destroy,
.dump = nft_nat_dump, .dump = nft_nat_dump,
.validate = nft_nat_validate, .validate = nft_nat_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_nat_type __read_mostly = { static struct nft_expr_type nft_nat_type __read_mostly = {
...@@ -346,6 +347,7 @@ static const struct nft_expr_ops nft_nat_inet_ops = { ...@@ -346,6 +347,7 @@ static const struct nft_expr_ops nft_nat_inet_ops = {
.destroy = nft_nat_destroy, .destroy = nft_nat_destroy,
.dump = nft_nat_dump, .dump = nft_nat_dump,
.validate = nft_nat_validate, .validate = nft_nat_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_inet_nat_type __read_mostly = { static struct nft_expr_type nft_inet_nat_type __read_mostly = {
......
...@@ -85,6 +85,16 @@ static int nft_ng_inc_init(const struct nft_ctx *ctx, ...@@ -85,6 +85,16 @@ static int nft_ng_inc_init(const struct nft_ctx *ctx,
return err; return err;
} }
static bool nft_ng_inc_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
const struct nft_ng_inc *priv = nft_expr_priv(expr);
nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE);
return false;
}
static int nft_ng_dump(struct sk_buff *skb, enum nft_registers dreg, static int nft_ng_dump(struct sk_buff *skb, enum nft_registers dreg,
u32 modulus, enum nft_ng_types type, u32 offset) u32 modulus, enum nft_ng_types type, u32 offset)
{ {
...@@ -172,6 +182,16 @@ static int nft_ng_random_dump(struct sk_buff *skb, const struct nft_expr *expr) ...@@ -172,6 +182,16 @@ static int nft_ng_random_dump(struct sk_buff *skb, const struct nft_expr *expr)
priv->offset); priv->offset);
} }
static bool nft_ng_random_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
const struct nft_ng_random *priv = nft_expr_priv(expr);
nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE);
return false;
}
static struct nft_expr_type nft_ng_type; static struct nft_expr_type nft_ng_type;
static const struct nft_expr_ops nft_ng_inc_ops = { static const struct nft_expr_ops nft_ng_inc_ops = {
.type = &nft_ng_type, .type = &nft_ng_type,
...@@ -180,6 +200,7 @@ static const struct nft_expr_ops nft_ng_inc_ops = { ...@@ -180,6 +200,7 @@ static const struct nft_expr_ops nft_ng_inc_ops = {
.init = nft_ng_inc_init, .init = nft_ng_inc_init,
.destroy = nft_ng_inc_destroy, .destroy = nft_ng_inc_destroy,
.dump = nft_ng_inc_dump, .dump = nft_ng_inc_dump,
.reduce = nft_ng_inc_reduce,
}; };
static const struct nft_expr_ops nft_ng_random_ops = { static const struct nft_expr_ops nft_ng_random_ops = {
...@@ -188,6 +209,7 @@ static const struct nft_expr_ops nft_ng_random_ops = { ...@@ -188,6 +209,7 @@ static const struct nft_expr_ops nft_ng_random_ops = {
.eval = nft_ng_random_eval, .eval = nft_ng_random_eval,
.init = nft_ng_random_init, .init = nft_ng_random_init,
.dump = nft_ng_random_dump, .dump = nft_ng_random_dump,
.reduce = nft_ng_random_reduce,
}; };
static const struct nft_expr_ops * static const struct nft_expr_ops *
......
...@@ -91,6 +91,7 @@ static const struct nft_expr_ops nft_objref_ops = { ...@@ -91,6 +91,7 @@ static const struct nft_expr_ops nft_objref_ops = {
.activate = nft_objref_activate, .activate = nft_objref_activate,
.deactivate = nft_objref_deactivate, .deactivate = nft_objref_deactivate,
.dump = nft_objref_dump, .dump = nft_objref_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
struct nft_objref_map { struct nft_objref_map {
...@@ -204,6 +205,7 @@ static const struct nft_expr_ops nft_objref_map_ops = { ...@@ -204,6 +205,7 @@ static const struct nft_expr_ops nft_objref_map_ops = {
.deactivate = nft_objref_map_deactivate, .deactivate = nft_objref_map_deactivate,
.destroy = nft_objref_map_destroy, .destroy = nft_objref_map_destroy,
.dump = nft_objref_map_dump, .dump = nft_objref_map_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
static const struct nft_expr_ops * static const struct nft_expr_ops *
......
...@@ -120,6 +120,30 @@ static int nft_osf_validate(const struct nft_ctx *ctx, ...@@ -120,6 +120,30 @@ static int nft_osf_validate(const struct nft_ctx *ctx,
(1 << NF_INET_FORWARD)); (1 << NF_INET_FORWARD));
} }
static bool nft_osf_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
struct nft_osf *priv = nft_expr_priv(expr);
struct nft_osf *osf;
if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
nft_reg_track_update(track, expr, priv->dreg, NFT_OSF_MAXGENRELEN);
return false;
}
osf = nft_expr_priv(track->regs[priv->dreg].selector);
if (priv->flags != osf->flags ||
priv->ttl != osf->ttl) {
nft_reg_track_update(track, expr, priv->dreg, NFT_OSF_MAXGENRELEN);
return false;
}
if (!track->regs[priv->dreg].bitwise)
return true;
return false;
}
static struct nft_expr_type nft_osf_type; static struct nft_expr_type nft_osf_type;
static const struct nft_expr_ops nft_osf_op = { static const struct nft_expr_ops nft_osf_op = {
.eval = nft_osf_eval, .eval = nft_osf_eval,
...@@ -128,6 +152,7 @@ static const struct nft_expr_ops nft_osf_op = { ...@@ -128,6 +152,7 @@ static const struct nft_expr_ops nft_osf_op = {
.dump = nft_osf_dump, .dump = nft_osf_dump,
.type = &nft_osf_type, .type = &nft_osf_type,
.validate = nft_osf_validate, .validate = nft_osf_validate,
.reduce = nft_osf_reduce,
}; };
static struct nft_expr_type nft_osf_type __read_mostly = { static struct nft_expr_type nft_osf_type __read_mostly = {
......
...@@ -216,10 +216,8 @@ static bool nft_payload_reduce(struct nft_regs_track *track, ...@@ -216,10 +216,8 @@ static bool nft_payload_reduce(struct nft_regs_track *track,
const struct nft_payload *priv = nft_expr_priv(expr); const struct nft_payload *priv = nft_expr_priv(expr);
const struct nft_payload *payload; const struct nft_payload *payload;
if (!track->regs[priv->dreg].selector || if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
track->regs[priv->dreg].selector->ops != expr->ops) { nft_reg_track_update(track, expr, priv->dreg, priv->len);
track->regs[priv->dreg].selector = expr;
track->regs[priv->dreg].bitwise = NULL;
return false; return false;
} }
...@@ -227,8 +225,7 @@ static bool nft_payload_reduce(struct nft_regs_track *track, ...@@ -227,8 +225,7 @@ static bool nft_payload_reduce(struct nft_regs_track *track,
if (priv->base != payload->base || if (priv->base != payload->base ||
priv->offset != payload->offset || priv->offset != payload->offset ||
priv->len != payload->len) { priv->len != payload->len) {
track->regs[priv->dreg].selector = expr; nft_reg_track_update(track, expr, priv->dreg, priv->len);
track->regs[priv->dreg].bitwise = NULL;
return false; return false;
} }
...@@ -815,8 +812,7 @@ static bool nft_payload_set_reduce(struct nft_regs_track *track, ...@@ -815,8 +812,7 @@ static bool nft_payload_set_reduce(struct nft_regs_track *track,
track->regs[i].selector->ops != &nft_payload_fast_ops) track->regs[i].selector->ops != &nft_payload_fast_ops)
continue; continue;
track->regs[i].selector = NULL; __nft_reg_track_cancel(track, i);
track->regs[i].bitwise = NULL;
} }
return false; return false;
......
...@@ -164,6 +164,7 @@ static const struct nft_expr_ops nft_queue_ops = { ...@@ -164,6 +164,7 @@ static const struct nft_expr_ops nft_queue_ops = {
.eval = nft_queue_eval, .eval = nft_queue_eval,
.init = nft_queue_init, .init = nft_queue_init,
.dump = nft_queue_dump, .dump = nft_queue_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
static const struct nft_expr_ops nft_queue_sreg_ops = { static const struct nft_expr_ops nft_queue_sreg_ops = {
...@@ -172,6 +173,7 @@ static const struct nft_expr_ops nft_queue_sreg_ops = { ...@@ -172,6 +173,7 @@ static const struct nft_expr_ops nft_queue_sreg_ops = {
.eval = nft_queue_sreg_eval, .eval = nft_queue_sreg_eval,
.init = nft_queue_sreg_init, .init = nft_queue_sreg_init,
.dump = nft_queue_sreg_dump, .dump = nft_queue_sreg_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
static const struct nft_expr_ops * static const struct nft_expr_ops *
......
...@@ -254,6 +254,7 @@ static const struct nft_expr_ops nft_quota_ops = { ...@@ -254,6 +254,7 @@ static const struct nft_expr_ops nft_quota_ops = {
.destroy = nft_quota_destroy, .destroy = nft_quota_destroy,
.clone = nft_quota_clone, .clone = nft_quota_clone,
.dump = nft_quota_dump, .dump = nft_quota_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_quota_type __read_mostly = { static struct nft_expr_type nft_quota_type __read_mostly = {
......
...@@ -140,6 +140,7 @@ static const struct nft_expr_ops nft_range_ops = { ...@@ -140,6 +140,7 @@ static const struct nft_expr_ops nft_range_ops = {
.eval = nft_range_eval, .eval = nft_range_eval,
.init = nft_range_init, .init = nft_range_init,
.dump = nft_range_dump, .dump = nft_range_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
struct nft_expr_type nft_range_type __read_mostly = { struct nft_expr_type nft_range_type __read_mostly = {
......
...@@ -134,6 +134,7 @@ static const struct nft_expr_ops nft_redir_ipv4_ops = { ...@@ -134,6 +134,7 @@ static const struct nft_expr_ops nft_redir_ipv4_ops = {
.destroy = nft_redir_ipv4_destroy, .destroy = nft_redir_ipv4_destroy,
.dump = nft_redir_dump, .dump = nft_redir_dump,
.validate = nft_redir_validate, .validate = nft_redir_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_redir_ipv4_type __read_mostly = { static struct nft_expr_type nft_redir_ipv4_type __read_mostly = {
...@@ -183,6 +184,7 @@ static const struct nft_expr_ops nft_redir_ipv6_ops = { ...@@ -183,6 +184,7 @@ static const struct nft_expr_ops nft_redir_ipv6_ops = {
.destroy = nft_redir_ipv6_destroy, .destroy = nft_redir_ipv6_destroy,
.dump = nft_redir_dump, .dump = nft_redir_dump,
.validate = nft_redir_validate, .validate = nft_redir_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_redir_ipv6_type __read_mostly = { static struct nft_expr_type nft_redir_ipv6_type __read_mostly = {
...@@ -225,6 +227,7 @@ static const struct nft_expr_ops nft_redir_inet_ops = { ...@@ -225,6 +227,7 @@ static const struct nft_expr_ops nft_redir_inet_ops = {
.destroy = nft_redir_inet_destroy, .destroy = nft_redir_inet_destroy,
.dump = nft_redir_dump, .dump = nft_redir_dump,
.validate = nft_redir_validate, .validate = nft_redir_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_redir_inet_type __read_mostly = { static struct nft_expr_type nft_redir_inet_type __read_mostly = {
......
...@@ -80,6 +80,7 @@ static const struct nft_expr_ops nft_reject_inet_ops = { ...@@ -80,6 +80,7 @@ static const struct nft_expr_ops nft_reject_inet_ops = {
.init = nft_reject_init, .init = nft_reject_init,
.dump = nft_reject_dump, .dump = nft_reject_dump,
.validate = nft_reject_inet_validate, .validate = nft_reject_inet_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_reject_inet_type __read_mostly = { static struct nft_expr_type nft_reject_inet_type __read_mostly = {
......
...@@ -159,6 +159,7 @@ static const struct nft_expr_ops nft_reject_netdev_ops = { ...@@ -159,6 +159,7 @@ static const struct nft_expr_ops nft_reject_netdev_ops = {
.init = nft_reject_init, .init = nft_reject_init,
.dump = nft_reject_dump, .dump = nft_reject_dump,
.validate = nft_reject_netdev_validate, .validate = nft_reject_netdev_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_reject_netdev_type __read_mostly = { static struct nft_expr_type nft_reject_netdev_type __read_mostly = {
......
...@@ -191,6 +191,7 @@ static const struct nft_expr_ops nft_rt_get_ops = { ...@@ -191,6 +191,7 @@ static const struct nft_expr_ops nft_rt_get_ops = {
.init = nft_rt_get_init, .init = nft_rt_get_init,
.dump = nft_rt_get_dump, .dump = nft_rt_get_dump,
.validate = nft_rt_validate, .validate = nft_rt_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
struct nft_expr_type nft_rt_type __read_mostly = { struct nft_expr_type nft_rt_type __read_mostly = {
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
struct nft_socket { struct nft_socket {
enum nft_socket_keys key:8; enum nft_socket_keys key:8;
u8 level; u8 level;
u8 len;
union { union {
u8 dreg; u8 dreg;
}; };
...@@ -179,6 +180,7 @@ static int nft_socket_init(const struct nft_ctx *ctx, ...@@ -179,6 +180,7 @@ static int nft_socket_init(const struct nft_ctx *ctx,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
priv->len = len;
return nft_parse_register_store(ctx, tb[NFTA_SOCKET_DREG], &priv->dreg, return nft_parse_register_store(ctx, tb[NFTA_SOCKET_DREG], &priv->dreg,
NULL, NFT_DATA_VALUE, len); NULL, NFT_DATA_VALUE, len);
} }
...@@ -198,6 +200,31 @@ static int nft_socket_dump(struct sk_buff *skb, ...@@ -198,6 +200,31 @@ static int nft_socket_dump(struct sk_buff *skb,
return 0; return 0;
} }
static bool nft_socket_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
const struct nft_socket *priv = nft_expr_priv(expr);
const struct nft_socket *socket;
if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}
socket = nft_expr_priv(track->regs[priv->dreg].selector);
if (priv->key != socket->key ||
priv->dreg != socket->dreg ||
priv->level != socket->level) {
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}
if (!track->regs[priv->dreg].bitwise)
return true;
return nft_expr_reduce_bitwise(track, expr);
}
static struct nft_expr_type nft_socket_type; static struct nft_expr_type nft_socket_type;
static const struct nft_expr_ops nft_socket_ops = { static const struct nft_expr_ops nft_socket_ops = {
.type = &nft_socket_type, .type = &nft_socket_type,
...@@ -205,6 +232,7 @@ static const struct nft_expr_ops nft_socket_ops = { ...@@ -205,6 +232,7 @@ static const struct nft_expr_ops nft_socket_ops = {
.eval = nft_socket_eval, .eval = nft_socket_eval,
.init = nft_socket_init, .init = nft_socket_init,
.dump = nft_socket_dump, .dump = nft_socket_dump,
.reduce = nft_socket_reduce,
}; };
static struct nft_expr_type nft_socket_type __read_mostly = { static struct nft_expr_type nft_socket_type __read_mostly = {
......
...@@ -288,6 +288,7 @@ static const struct nft_expr_ops nft_synproxy_ops = { ...@@ -288,6 +288,7 @@ static const struct nft_expr_ops nft_synproxy_ops = {
.dump = nft_synproxy_dump, .dump = nft_synproxy_dump,
.type = &nft_synproxy_type, .type = &nft_synproxy_type,
.validate = nft_synproxy_validate, .validate = nft_synproxy_validate,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_synproxy_type __read_mostly = { static struct nft_expr_type nft_synproxy_type __read_mostly = {
......
...@@ -320,6 +320,7 @@ static const struct nft_expr_ops nft_tproxy_ops = { ...@@ -320,6 +320,7 @@ static const struct nft_expr_ops nft_tproxy_ops = {
.init = nft_tproxy_init, .init = nft_tproxy_init,
.destroy = nft_tproxy_destroy, .destroy = nft_tproxy_destroy,
.dump = nft_tproxy_dump, .dump = nft_tproxy_dump,
.reduce = NFT_REDUCE_READONLY,
}; };
static struct nft_expr_type nft_tproxy_type __read_mostly = { static struct nft_expr_type nft_tproxy_type __read_mostly = {
......
...@@ -17,6 +17,7 @@ struct nft_tunnel { ...@@ -17,6 +17,7 @@ struct nft_tunnel {
enum nft_tunnel_keys key:8; enum nft_tunnel_keys key:8;
u8 dreg; u8 dreg;
enum nft_tunnel_mode mode:8; enum nft_tunnel_mode mode:8;
u8 len;
}; };
static void nft_tunnel_get_eval(const struct nft_expr *expr, static void nft_tunnel_get_eval(const struct nft_expr *expr,
...@@ -101,6 +102,7 @@ static int nft_tunnel_get_init(const struct nft_ctx *ctx, ...@@ -101,6 +102,7 @@ static int nft_tunnel_get_init(const struct nft_ctx *ctx,
priv->mode = NFT_TUNNEL_MODE_NONE; priv->mode = NFT_TUNNEL_MODE_NONE;
} }
priv->len = len;
return nft_parse_register_store(ctx, tb[NFTA_TUNNEL_DREG], &priv->dreg, return nft_parse_register_store(ctx, tb[NFTA_TUNNEL_DREG], &priv->dreg,
NULL, NFT_DATA_VALUE, len); NULL, NFT_DATA_VALUE, len);
} }
...@@ -122,6 +124,31 @@ static int nft_tunnel_get_dump(struct sk_buff *skb, ...@@ -122,6 +124,31 @@ static int nft_tunnel_get_dump(struct sk_buff *skb,
return -1; return -1;
} }
static bool nft_tunnel_get_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
const struct nft_tunnel *priv = nft_expr_priv(expr);
const struct nft_tunnel *tunnel;
if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}
tunnel = nft_expr_priv(track->regs[priv->dreg].selector);
if (priv->key != tunnel->key ||
priv->dreg != tunnel->dreg ||
priv->mode != tunnel->mode) {
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}
if (!track->regs[priv->dreg].bitwise)
return true;
return false;
}
static struct nft_expr_type nft_tunnel_type; static struct nft_expr_type nft_tunnel_type;
static const struct nft_expr_ops nft_tunnel_get_ops = { static const struct nft_expr_ops nft_tunnel_get_ops = {
.type = &nft_tunnel_type, .type = &nft_tunnel_type,
...@@ -129,6 +156,7 @@ static const struct nft_expr_ops nft_tunnel_get_ops = { ...@@ -129,6 +156,7 @@ static const struct nft_expr_ops nft_tunnel_get_ops = {
.eval = nft_tunnel_get_eval, .eval = nft_tunnel_get_eval,
.init = nft_tunnel_get_init, .init = nft_tunnel_get_init,
.dump = nft_tunnel_get_dump, .dump = nft_tunnel_get_dump,
.reduce = nft_tunnel_get_reduce,
}; };
static struct nft_expr_type nft_tunnel_type __read_mostly = { static struct nft_expr_type nft_tunnel_type __read_mostly = {
......
...@@ -27,6 +27,7 @@ struct nft_xfrm { ...@@ -27,6 +27,7 @@ struct nft_xfrm {
u8 dreg; u8 dreg;
u8 dir; u8 dir;
u8 spnum; u8 spnum;
u8 len;
}; };
static int nft_xfrm_get_init(const struct nft_ctx *ctx, static int nft_xfrm_get_init(const struct nft_ctx *ctx,
...@@ -86,6 +87,7 @@ static int nft_xfrm_get_init(const struct nft_ctx *ctx, ...@@ -86,6 +87,7 @@ static int nft_xfrm_get_init(const struct nft_ctx *ctx,
priv->spnum = spnum; priv->spnum = spnum;
priv->len = len;
return nft_parse_register_store(ctx, tb[NFTA_XFRM_DREG], &priv->dreg, return nft_parse_register_store(ctx, tb[NFTA_XFRM_DREG], &priv->dreg,
NULL, NFT_DATA_VALUE, len); NULL, NFT_DATA_VALUE, len);
} }
...@@ -252,6 +254,31 @@ static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *e ...@@ -252,6 +254,31 @@ static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *e
return nft_chain_validate_hooks(ctx->chain, hooks); return nft_chain_validate_hooks(ctx->chain, hooks);
} }
static bool nft_xfrm_reduce(struct nft_regs_track *track,
const struct nft_expr *expr)
{
const struct nft_xfrm *priv = nft_expr_priv(expr);
const struct nft_xfrm *xfrm;
if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}
xfrm = nft_expr_priv(track->regs[priv->dreg].selector);
if (priv->key != xfrm->key ||
priv->dreg != xfrm->dreg ||
priv->dir != xfrm->dir ||
priv->spnum != xfrm->spnum) {
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}
if (!track->regs[priv->dreg].bitwise)
return true;
return nft_expr_reduce_bitwise(track, expr);
}
static struct nft_expr_type nft_xfrm_type; static struct nft_expr_type nft_xfrm_type;
static const struct nft_expr_ops nft_xfrm_get_ops = { static const struct nft_expr_ops nft_xfrm_get_ops = {
...@@ -261,6 +288,7 @@ static const struct nft_expr_ops nft_xfrm_get_ops = { ...@@ -261,6 +288,7 @@ static const struct nft_expr_ops nft_xfrm_get_ops = {
.init = nft_xfrm_get_init, .init = nft_xfrm_get_init,
.dump = nft_xfrm_get_dump, .dump = nft_xfrm_get_dump,
.validate = nft_xfrm_validate, .validate = nft_xfrm_validate,
.reduce = nft_xfrm_reduce,
}; };
static struct nft_expr_type nft_xfrm_type __read_mostly = { static struct nft_expr_type nft_xfrm_type __read_mostly = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment