[PATCH nf-next,v2 3/7] netfilter: nf_tables: track register store and load operations

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add basic register tracking for the combo infrastructure.

Track registers to detect an expression that recycles data from an
expression that has been merged into a combo expression. Skip track
and merge into combo logic in such case. Otherwise userspace might
trigger a combo expression then try to access a register that is
uninitialized.

Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx>
---
v2: new in this series.

 include/net/netfilter/nf_tables.h        | 35 +++++++++++++++++++++++
 include/net/netfilter/nft_fib.h          |  4 +++
 include/net/netfilter/nft_meta.h         |  8 ++++++
 net/bridge/netfilter/nft_meta_bridge.c   |  2 ++
 net/bridge/netfilter/nft_reject_bridge.c |  1 +
 net/ipv4/netfilter/nft_dup_ipv4.c        | 14 +++++++++
 net/ipv4/netfilter/nft_fib_ipv4.c        |  2 ++
 net/ipv4/netfilter/nft_reject_ipv4.c     |  1 +
 net/ipv6/netfilter/nft_dup_ipv6.c        | 14 +++++++++
 net/ipv6/netfilter/nft_fib_ipv6.c        |  2 ++
 net/ipv6/netfilter/nft_reject_ipv6.c     |  1 +
 net/netfilter/nft_bitwise.c              | 13 +++++++++
 net/netfilter/nft_byteorder.c            | 13 +++++++++
 net/netfilter/nft_cmp.c                  | 24 ++++++++++++++++
 net/netfilter/nft_compat.c               |  1 +
 net/netfilter/nft_connlimit.c            |  1 +
 net/netfilter/nft_counter.c              |  1 +
 net/netfilter/nft_ct.c                   | 27 ++++++++++++++++++
 net/netfilter/nft_dup_netdev.c           | 12 ++++++++
 net/netfilter/nft_dynset.c               | 14 +++++++++
 net/netfilter/nft_exthdr.c               | 29 +++++++++++++++++++
 net/netfilter/nft_fib.c                  | 12 ++++++++
 net/netfilter/nft_fib_inet.c             |  1 +
 net/netfilter/nft_fib_netdev.c           |  1 +
 net/netfilter/nft_flow_offload.c         |  1 +
 net/netfilter/nft_fwd_netdev.c           | 36 ++++++++++++++++++++++++
 net/netfilter/nft_hash.c                 | 25 ++++++++++++++++
 net/netfilter/nft_immediate.c            | 12 ++++++++
 net/netfilter/nft_inner.c                | 10 +++++++
 net/netfilter/nft_last.c                 |  1 +
 net/netfilter/nft_limit.c                |  2 ++
 net/netfilter/nft_log.c                  |  1 +
 net/netfilter/nft_lookup.c               | 14 +++++++++
 net/netfilter/nft_masq.c                 | 17 +++++++++++
 net/netfilter/nft_meta.c                 | 26 +++++++++++++++++
 net/netfilter/nft_nat.c                  | 32 +++++++++++++++++++++
 net/netfilter/nft_numgen.c               | 24 ++++++++++++++++
 net/netfilter/nft_objref.c               | 13 +++++++++
 net/netfilter/nft_osf.c                  | 12 ++++++++
 net/netfilter/nft_payload.c              | 26 +++++++++++++++++
 net/netfilter/nft_queue.c                | 14 +++++++++
 net/netfilter/nft_quota.c                |  1 +
 net/netfilter/nft_range.c                | 12 ++++++++
 net/netfilter/nft_redir.c                | 17 +++++++++++
 net/netfilter/nft_reject_inet.c          |  1 +
 net/netfilter/nft_reject_netdev.c        |  1 +
 net/netfilter/nft_rt.c                   | 14 +++++++++
 net/netfilter/nft_socket.c               | 12 ++++++++
 net/netfilter/nft_synproxy.c             |  1 +
 net/netfilter/nft_tproxy.c               | 27 ++++++++++++++++++
 net/netfilter/nft_tunnel.c               | 12 ++++++++
 net/netfilter/nft_xfrm.c                 | 12 ++++++++
 52 files changed, 609 insertions(+)

diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 588b1904e411..81adf294da25 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -123,6 +123,38 @@ struct nft_regs {
 	};
 };
 
+struct nft_expr_track_ctx {
+	u32	reg_bitmap;
+	bool	cancel;
+};
+
+static inline u32 nft_expr_track_bitmask(u8 reg, u8 len)
+{
+	return ((1 << DIV_ROUND_UP(len, 4)) - 1) << reg;
+}
+
+static inline void nft_expr_track_dreg(struct nft_expr_track_ctx *ctx, u8 dreg, u8 len)
+{
+	ctx->reg_bitmap |= nft_expr_track_bitmask(dreg, len);
+}
+
+static inline void nft_expr_track_sreg(struct nft_expr_track_ctx *ctx, u8 sreg, u8 len)
+{
+	if (!(ctx->reg_bitmap & nft_expr_track_bitmask(sreg, len)))
+		ctx->cancel = true;
+}
+
+static inline void nft_expr_track_reset_dreg(struct nft_expr_track_ctx *ctx, u8 dreg, u8 len)
+{
+	ctx->reg_bitmap &= ~nft_expr_track_bitmask(dreg, len);
+}
+
+#define __NFT_TRACK_GENERIC	1UL
+#define NFT_TRACK_GENERIC	(void *)__NFT_TRACK_GENERIC
+
+struct nft_expr_track {
+};
+
 /* Store/load an u8, u16 or u64 integer to/from the u32 data register.
  *
  * Note, when using concatenations, register allocation happens at 32-bit
@@ -934,6 +966,9 @@ struct nft_expr_ops {
 	int				(*validate)(const struct nft_ctx *ctx,
 						    const struct nft_expr *expr,
 						    const struct nft_data **data);
+	int				(*track)(struct nft_expr_track_ctx *ctx,
+						 struct nft_expr_track *track,
+						 const struct nft_expr *expr);
 	bool				(*gc)(struct net *net,
 					      const struct nft_expr *expr);
 	int				(*offload)(struct nft_offload_ctx *ctx,
diff --git a/include/net/netfilter/nft_fib.h b/include/net/netfilter/nft_fib.h
index 2e434ba41b97..7045e7106611 100644
--- a/include/net/netfilter/nft_fib.h
+++ b/include/net/netfilter/nft_fib.h
@@ -7,6 +7,7 @@
 struct nft_fib {
 	u8			dreg;
 	u8			result;
+	u8			len;
 	u32			flags;
 };
 
@@ -38,4 +39,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,
 			  const struct net_device *dev);
 
+int nft_fib_track(struct nft_expr_track_ctx *ctx, struct nft_expr_track *track,
+		  const struct nft_expr *expr);
+
 #endif
diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h
index 690f6245026c..e03e008cbd51 100644
--- a/include/net/netfilter/nft_meta.h
+++ b/include/net/netfilter/nft_meta.h
@@ -44,6 +44,14 @@ int nft_meta_set_validate(const struct nft_ctx *ctx,
 			  const struct nft_expr *expr,
 			  const struct nft_data **data);
 
+int nft_meta_get_track(struct nft_expr_track_ctx *ctx,
+		       struct nft_expr_track *track,
+		       const struct nft_expr *expr);
+
+int nft_meta_set_track(struct nft_expr_track_ctx *ctx,
+		       struct nft_expr_track *track,
+		       const struct nft_expr *expr);
+
 struct nft_inner_tun_ctx;
 void nft_meta_inner_eval(const struct nft_expr *expr,
 			 struct nft_regs *regs, const struct nft_pktinfo *pkt,
diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c
index 93e8a8dadd80..3964f0a0f328 100644
--- a/net/bridge/netfilter/nft_meta_bridge.c
+++ b/net/bridge/netfilter/nft_meta_bridge.c
@@ -102,6 +102,7 @@ static const struct nft_expr_ops nft_meta_bridge_get_ops = {
 	.eval		= nft_meta_bridge_get_eval,
 	.init		= nft_meta_bridge_get_init,
 	.dump		= nft_meta_get_dump,
+	.track		= nft_meta_get_track,
 };
 
 static void nft_meta_bridge_set_eval(const struct nft_expr *expr,
@@ -173,6 +174,7 @@ static const struct nft_expr_ops nft_meta_bridge_set_ops = {
 	.init		= nft_meta_bridge_set_init,
 	.destroy	= nft_meta_set_destroy,
 	.dump		= nft_meta_set_dump,
+	.track		= nft_meta_set_track,
 	.validate	= nft_meta_bridge_set_validate,
 };
 
diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c
index fbf858ddec35..eb94369d24bf 100644
--- a/net/bridge/netfilter/nft_reject_bridge.c
+++ b/net/bridge/netfilter/nft_reject_bridge.c
@@ -184,6 +184,7 @@ static const struct nft_expr_ops nft_reject_bridge_ops = {
 	.eval		= nft_reject_bridge_eval,
 	.init		= nft_reject_init,
 	.dump		= nft_reject_dump,
+	.track		= NFT_TRACK_GENERIC,
 	.validate	= nft_reject_bridge_validate,
 };
 
diff --git a/net/ipv4/netfilter/nft_dup_ipv4.c b/net/ipv4/netfilter/nft_dup_ipv4.c
index cae5b38335b3..b7b0cbe3115b 100644
--- a/net/ipv4/netfilter/nft_dup_ipv4.c
+++ b/net/ipv4/netfilter/nft_dup_ipv4.c
@@ -69,6 +69,19 @@ static int nft_dup_ipv4_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_dup_ipv4_track(struct nft_expr_track_ctx *ctx,
+			      struct nft_expr_track *track,
+			      const struct nft_expr *expr)
+{
+	const struct nft_dup_ipv4 *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg_addr, sizeof(struct in_addr));
+	if (priv->sreg_dev)
+		nft_expr_track_dreg(ctx, priv->sreg_dev, sizeof(int));
+
+       return 1;
+}
+
 static struct nft_expr_type nft_dup_ipv4_type;
 static const struct nft_expr_ops nft_dup_ipv4_ops = {
 	.type		= &nft_dup_ipv4_type,
@@ -76,6 +89,7 @@ static const struct nft_expr_ops nft_dup_ipv4_ops = {
 	.eval		= nft_dup_ipv4_eval,
 	.init		= nft_dup_ipv4_init,
 	.dump		= nft_dup_ipv4_dump,
+	.track		= nft_dup_ipv4_track,
 };
 
 static const struct nla_policy nft_dup_ipv4_policy[NFTA_DUP_MAX + 1] = {
diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c
index 55c4b73265ed..258a3ffd3294 100644
--- a/net/ipv4/netfilter/nft_fib_ipv4.c
+++ b/net/ipv4/netfilter/nft_fib_ipv4.c
@@ -158,6 +158,7 @@ static const struct nft_expr_ops nft_fib4_type_ops = {
 	.eval		= nft_fib4_eval_type,
 	.init		= nft_fib_init,
 	.dump		= nft_fib_dump,
+	.track		= nft_fib_track,
 	.validate	= nft_fib_validate,
 };
 
@@ -167,6 +168,7 @@ static const struct nft_expr_ops nft_fib4_ops = {
 	.eval		= nft_fib4_eval,
 	.init		= nft_fib_init,
 	.dump		= nft_fib_dump,
+	.track		= nft_fib_track,
 	.validate	= nft_fib_validate,
 };
 
diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c
index 55fc23a8f7a7..56e18609df8c 100644
--- a/net/ipv4/netfilter/nft_reject_ipv4.c
+++ b/net/ipv4/netfilter/nft_reject_ipv4.c
@@ -44,6 +44,7 @@ static const struct nft_expr_ops nft_reject_ipv4_ops = {
 	.eval		= nft_reject_ipv4_eval,
 	.init		= nft_reject_init,
 	.dump		= nft_reject_dump,
+	.track		= NFT_TRACK_GENERIC,
 	.validate	= nft_reject_validate,
 };
 
diff --git a/net/ipv6/netfilter/nft_dup_ipv6.c b/net/ipv6/netfilter/nft_dup_ipv6.c
index e859beb29bb1..0527a6f064b7 100644
--- a/net/ipv6/netfilter/nft_dup_ipv6.c
+++ b/net/ipv6/netfilter/nft_dup_ipv6.c
@@ -67,6 +67,19 @@ static int nft_dup_ipv6_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_dup_ipv6_track(struct nft_expr_track_ctx *ctx,
+			      struct nft_expr_track *track,
+			      const struct nft_expr *expr)
+{
+	const struct nft_dup_ipv6 *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg_addr, sizeof(struct in6_addr));
+	if (priv->sreg_dev)
+		nft_expr_track_dreg(ctx, priv->sreg_dev, sizeof(int));
+
+	return 1;
+}
+
 static struct nft_expr_type nft_dup_ipv6_type;
 static const struct nft_expr_ops nft_dup_ipv6_ops = {
 	.type		= &nft_dup_ipv6_type,
@@ -74,6 +87,7 @@ static const struct nft_expr_ops nft_dup_ipv6_ops = {
 	.eval		= nft_dup_ipv6_eval,
 	.init		= nft_dup_ipv6_init,
 	.dump		= nft_dup_ipv6_dump,
+	.track		= nft_dup_ipv6_track,
 };
 
 static const struct nla_policy nft_dup_ipv6_policy[NFTA_DUP_MAX + 1] = {
diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c
index 6ae17f530994..bf46d169cd15 100644
--- a/net/ipv6/netfilter/nft_fib_ipv6.c
+++ b/net/ipv6/netfilter/nft_fib_ipv6.c
@@ -219,6 +219,7 @@ static const struct nft_expr_ops nft_fib6_type_ops = {
 	.eval		= nft_fib6_eval_type,
 	.init		= nft_fib_init,
 	.dump		= nft_fib_dump,
+	.track		= nft_fib_track,
 	.validate	= nft_fib_validate,
 };
 
@@ -228,6 +229,7 @@ static const struct nft_expr_ops nft_fib6_ops = {
 	.eval		= nft_fib6_eval,
 	.init		= nft_fib_init,
 	.dump		= nft_fib_dump,
+	.track		= nft_fib_track,
 	.validate	= nft_fib_validate,
 };
 
diff --git a/net/ipv6/netfilter/nft_reject_ipv6.c b/net/ipv6/netfilter/nft_reject_ipv6.c
index ed69c768797e..0cb315f57c74 100644
--- a/net/ipv6/netfilter/nft_reject_ipv6.c
+++ b/net/ipv6/netfilter/nft_reject_ipv6.c
@@ -45,6 +45,7 @@ static const struct nft_expr_ops nft_reject_ipv6_ops = {
 	.eval		= nft_reject_ipv6_eval,
 	.init		= nft_reject_init,
 	.dump		= nft_reject_dump,
+	.track		= NFT_TRACK_GENERIC,
 	.validate	= nft_reject_validate,
 };
 
diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c
index b358c03bdb04..0ab2d281f245 100644
--- a/net/netfilter/nft_bitwise.c
+++ b/net/netfilter/nft_bitwise.c
@@ -262,6 +262,18 @@ static int nft_bitwise_dump(struct sk_buff *skb,
 
 static struct nft_data zero;
 
+static int nft_bitwise_track(struct nft_expr_track_ctx *ctx,
+			     struct nft_expr_track *track,
+			     const struct nft_expr *expr)
+{
+	const struct nft_bitwise *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg, priv->len);
+	nft_expr_track_dreg(ctx, priv->dreg, priv->len);
+
+	return 1;
+}
+
 static int nft_bitwise_offload(struct nft_offload_ctx *ctx,
 			       struct nft_flow_rule *flow,
 			       const struct nft_expr *expr)
@@ -287,6 +299,7 @@ static const struct nft_expr_ops nft_bitwise_ops = {
 	.eval		= nft_bitwise_eval,
 	.init		= nft_bitwise_init,
 	.dump		= nft_bitwise_dump,
+	.track		= nft_bitwise_track,
 	.offload	= nft_bitwise_offload,
 };
 
diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c
index a42d03741bb3..3d7ed9c8b611 100644
--- a/net/netfilter/nft_byteorder.c
+++ b/net/netfilter/nft_byteorder.c
@@ -169,12 +169,25 @@ static int nft_byteorder_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_byteorder_track(struct nft_expr_track_ctx *ctx,
+			       struct nft_expr_track *track,
+			       const struct nft_expr *expr)
+{
+	const struct nft_byteorder *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg, priv->len);
+	nft_expr_track_dreg(ctx, priv->dreg, priv->len);
+
+	return 1;
+}
+
 static const struct nft_expr_ops nft_byteorder_ops = {
 	.type		= &nft_byteorder_type,
 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_byteorder)),
 	.eval		= nft_byteorder_eval,
 	.init		= nft_byteorder_init,
 	.dump		= nft_byteorder_dump,
+	.track		= nft_byteorder_track,
 };
 
 struct nft_expr_type nft_byteorder_type __read_mostly = {
diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c
index 7f3f87446683..dc026cd4458d 100644
--- a/net/netfilter/nft_cmp.c
+++ b/net/netfilter/nft_cmp.c
@@ -184,12 +184,24 @@ static int nft_cmp_offload(struct nft_offload_ctx *ctx,
 	return __nft_cmp_offload(ctx, flow, priv);
 }
 
+static int nft_cmp_track(struct nft_expr_track_ctx *ctx,
+			 struct nft_expr_track *track,
+			 const struct nft_expr *expr)
+{
+	const struct nft_cmp_expr *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg, priv->len);
+
+	return 1;
+}
+
 static const struct nft_expr_ops nft_cmp_ops = {
 	.type		= &nft_cmp_type,
 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)),
 	.eval		= nft_cmp_eval,
 	.init		= nft_cmp_init,
 	.dump		= nft_cmp_dump,
+	.track		= nft_cmp_track,
 	.offload	= nft_cmp_offload,
 };
 
@@ -275,12 +287,24 @@ static int nft_cmp_fast_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_cmp_fast_track(struct nft_expr_track_ctx *ctx,
+			      struct nft_expr_track *track,
+			      const struct nft_expr *expr)
+{
+	const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg, sizeof(u32));
+
+	return 1;
+}
+
 const struct nft_expr_ops nft_cmp_fast_ops = {
 	.type		= &nft_cmp_type,
 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)),
 	.eval		= NULL,	/* inlined */
 	.init		= nft_cmp_fast_init,
 	.dump		= nft_cmp_fast_dump,
+	.track		= nft_cmp_fast_track,
 	.offload	= nft_cmp_fast_offload,
 };
 
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index e178b479dfaf..af88078caf99 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -864,6 +864,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
 	ops->destroy = nft_target_destroy;
 	ops->dump = nft_target_dump;
 	ops->validate = nft_target_validate;
+	ops->track = NFT_TRACK_GENERIC;
 	ops->data = target;
 
 	if (family == NFPROTO_BRIDGE)
diff --git a/net/netfilter/nft_connlimit.c b/net/netfilter/nft_connlimit.c
index 53ef6854f8e6..029414111cdb 100644
--- a/net/netfilter/nft_connlimit.c
+++ b/net/netfilter/nft_connlimit.c
@@ -257,6 +257,7 @@ static const struct nft_expr_ops nft_connlimit_ops = {
 	.clone		= nft_connlimit_clone,
 	.destroy_clone	= nft_connlimit_destroy_clone,
 	.dump		= nft_connlimit_dump,
+	.track		= NFT_TRACK_GENERIC,
 	.gc		= nft_connlimit_gc,
 };
 
diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c
index 446406631c9a..04fdc9378087 100644
--- a/net/netfilter/nft_counter.c
+++ b/net/netfilter/nft_counter.c
@@ -293,6 +293,7 @@ static const struct nft_expr_ops nft_counter_ops = {
 	.destroy	= nft_counter_destroy,
 	.destroy_clone	= nft_counter_destroy,
 	.dump		= nft_counter_dump,
+	.track		= NFT_TRACK_GENERIC,
 	.clone		= nft_counter_clone,
 	.offload	= nft_counter_offload,
 	.offload_stats	= nft_counter_offload_stats,
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
index fe25ce6fa0bd..0cd628cc2282 100644
--- a/net/netfilter/nft_ct.c
+++ b/net/netfilter/nft_ct.c
@@ -697,6 +697,28 @@ static int nft_ct_set_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_ct_get_track(struct nft_expr_track_ctx *ctx,
+			    struct nft_expr_track *track,
+			    const struct nft_expr *expr)
+{
+	const struct nft_ct *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, priv->len);
+
+	return 1;
+}
+
+static int nft_ct_set_track(struct nft_expr_track_ctx *ctx,
+			    struct nft_expr_track *track,
+			    const struct nft_expr *expr)
+{
+	const struct nft_ct *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg, priv->len);
+
+	return 1;
+}
+
 static struct nft_expr_type nft_ct_type;
 static const struct nft_expr_ops nft_ct_get_ops = {
 	.type		= &nft_ct_type,
@@ -705,6 +727,7 @@ static const struct nft_expr_ops nft_ct_get_ops = {
 	.init		= nft_ct_get_init,
 	.destroy	= nft_ct_get_destroy,
 	.dump		= nft_ct_get_dump,
+	.track		= nft_ct_get_track,
 };
 
 #ifdef CONFIG_RETPOLINE
@@ -715,6 +738,7 @@ static const struct nft_expr_ops nft_ct_get_fast_ops = {
 	.init		= nft_ct_get_init,
 	.destroy	= nft_ct_get_destroy,
 	.dump		= nft_ct_get_dump,
+	.track		= nft_ct_get_track,
 };
 #endif
 
@@ -725,6 +749,7 @@ static const struct nft_expr_ops nft_ct_set_ops = {
 	.init		= nft_ct_set_init,
 	.destroy	= nft_ct_set_destroy,
 	.dump		= nft_ct_set_dump,
+	.track		= nft_ct_set_track,
 };
 
 #ifdef CONFIG_NF_CONNTRACK_ZONES
@@ -735,6 +760,7 @@ static const struct nft_expr_ops nft_ct_set_zone_ops = {
 	.init		= nft_ct_set_init,
 	.destroy	= nft_ct_set_destroy,
 	.dump		= nft_ct_set_dump,
+	.track		= nft_ct_set_track,
 };
 #endif
 
@@ -804,6 +830,7 @@ static const struct nft_expr_ops nft_notrack_ops = {
 	.type		= &nft_notrack_type,
 	.size		= NFT_EXPR_SIZE(0),
 	.eval		= nft_notrack_eval,
+	.track		= NFT_TRACK_GENERIC,
 };
 
 static struct nft_expr_type nft_notrack_type __read_mostly = {
diff --git a/net/netfilter/nft_dup_netdev.c b/net/netfilter/nft_dup_netdev.c
index 2007700bef8e..e2f1ac2a3bb7 100644
--- a/net/netfilter/nft_dup_netdev.c
+++ b/net/netfilter/nft_dup_netdev.c
@@ -58,6 +58,17 @@ static int nft_dup_netdev_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_dup_netdev_track(struct nft_expr_track_ctx *ctx,
+				struct nft_expr_track *track,
+				const struct nft_expr *expr)
+{
+	const struct nft_dup_netdev *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg_dev, IFNAMSIZ);
+
+	return 1;
+}
+
 static int nft_dup_netdev_offload(struct nft_offload_ctx *ctx,
 				  struct nft_flow_rule *flow,
 				  const struct nft_expr *expr)
@@ -80,6 +91,7 @@ static const struct nft_expr_ops nft_dup_netdev_ops = {
 	.eval		= nft_dup_netdev_eval,
 	.init		= nft_dup_netdev_init,
 	.dump		= nft_dup_netdev_dump,
+	.track		= nft_dup_netdev_track,
 	.offload	= nft_dup_netdev_offload,
 	.offload_action	= nft_dup_netdev_offload_action,
 };
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
index c3bd57be2ee8..4b2ff0b25dfe 100644
--- a/net/netfilter/nft_dynset.c
+++ b/net/netfilter/nft_dynset.c
@@ -405,6 +405,19 @@ static int nft_dynset_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_dynset_track(struct nft_expr_track_ctx *ctx,
+			    struct nft_expr_track *track,
+			    const struct nft_expr *expr)
+{
+	const struct nft_dynset *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg_key, priv->set->klen);
+	if (priv->set->flags & NFT_SET_MAP)
+		nft_expr_track_sreg(ctx, priv->sreg_data, priv->set->dlen);
+
+	return 1;
+}
+
 static const struct nft_expr_ops nft_dynset_ops = {
 	.type		= &nft_dynset_type,
 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_dynset)),
@@ -414,6 +427,7 @@ static const struct nft_expr_ops nft_dynset_ops = {
 	.activate	= nft_dynset_activate,
 	.deactivate	= nft_dynset_deactivate,
 	.dump		= nft_dynset_dump,
+	.track		= nft_dynset_track,
 };
 
 struct nft_expr_type nft_dynset_type __read_mostly = {
diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
index 41e8ae77b823..83f783e8b7b7 100644
--- a/net/netfilter/nft_exthdr.c
+++ b/net/netfilter/nft_exthdr.c
@@ -698,12 +698,35 @@ static int nft_exthdr_dump_strip(struct sk_buff *skb,
 	return nft_exthdr_dump_common(skb, priv);
 }
 
+static int nft_exthdr_track(struct nft_expr_track_ctx *ctx,
+			    struct nft_expr_track *track,
+			    const struct nft_expr *expr)
+{
+	const struct nft_exthdr *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, priv->len);
+
+	return 1;
+}
+
+static int nft_exthdr_set_track(struct nft_expr_track_ctx *ctx,
+				struct nft_expr_track *track,
+				const struct nft_expr *expr)
+{
+	const struct nft_exthdr *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg, priv->len);
+
+	return 1;
+}
+
 static const struct nft_expr_ops nft_exthdr_ipv6_ops = {
 	.type		= &nft_exthdr_type,
 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
 	.eval		= nft_exthdr_ipv6_eval,
 	.init		= nft_exthdr_init,
 	.dump		= nft_exthdr_dump,
+	.track		= nft_exthdr_track,
 };
 
 static const struct nft_expr_ops nft_exthdr_ipv4_ops = {
@@ -712,6 +735,7 @@ static const struct nft_expr_ops nft_exthdr_ipv4_ops = {
 	.eval		= nft_exthdr_ipv4_eval,
 	.init		= nft_exthdr_ipv4_init,
 	.dump		= nft_exthdr_dump,
+	.track		= nft_exthdr_track,
 };
 
 static const struct nft_expr_ops nft_exthdr_tcp_ops = {
@@ -720,6 +744,7 @@ static const struct nft_expr_ops nft_exthdr_tcp_ops = {
 	.eval		= nft_exthdr_tcp_eval,
 	.init		= nft_exthdr_init,
 	.dump		= nft_exthdr_dump,
+	.track		= nft_exthdr_track,
 };
 
 static const struct nft_expr_ops nft_exthdr_tcp_set_ops = {
@@ -728,6 +753,7 @@ static const struct nft_expr_ops nft_exthdr_tcp_set_ops = {
 	.eval		= nft_exthdr_tcp_set_eval,
 	.init		= nft_exthdr_tcp_set_init,
 	.dump		= nft_exthdr_dump_set,
+	.track		= nft_exthdr_set_track,
 };
 
 static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = {
@@ -736,6 +762,7 @@ static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = {
 	.eval		= nft_exthdr_tcp_strip_eval,
 	.init		= nft_exthdr_tcp_strip_init,
 	.dump		= nft_exthdr_dump_strip,
+	.track		= NFT_TRACK_GENERIC,
 };
 
 static const struct nft_expr_ops nft_exthdr_sctp_ops = {
@@ -744,6 +771,7 @@ static const struct nft_expr_ops nft_exthdr_sctp_ops = {
 	.eval		= nft_exthdr_sctp_eval,
 	.init		= nft_exthdr_init,
 	.dump		= nft_exthdr_dump,
+	.track		= nft_exthdr_track,
 };
 
 static const struct nft_expr_ops nft_exthdr_dccp_ops = {
@@ -752,6 +780,7 @@ static const struct nft_expr_ops nft_exthdr_dccp_ops = {
 	.eval		= nft_exthdr_dccp_eval,
 	.init		= nft_exthdr_dccp_init,
 	.dump		= nft_exthdr_dump,
+	.track		= nft_exthdr_track,
 };
 
 static const struct nft_expr_ops *
diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c
index e7cb42c9c175..12eff4ba05bd 100644
--- a/net/netfilter/nft_fib.c
+++ b/net/netfilter/nft_fib.c
@@ -108,6 +108,7 @@ int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
 	default:
 		return -EINVAL;
 	}
+	priv->len = len;
 
 	err = nft_parse_register_store(ctx, tb[NFTA_FIB_DREG], &priv->dreg,
 				       NULL, NFT_DATA_VALUE, len);
@@ -135,6 +136,17 @@ int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr, bool reset)
 }
 EXPORT_SYMBOL_GPL(nft_fib_dump);
 
+int nft_fib_track(struct nft_expr_track_ctx *ctx, struct nft_expr_track *track,
+		  const struct nft_expr *expr)
+{
+	const struct nft_fib *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, priv->len);
+
+	return 1;
+}
+EXPORT_SYMBOL_GPL(nft_fib_track);
+
 void nft_fib_store_result(void *reg, const struct nft_fib *priv,
 			  const struct net_device *dev)
 {
diff --git a/net/netfilter/nft_fib_inet.c b/net/netfilter/nft_fib_inet.c
index a88d44e163d1..9dc81538815f 100644
--- a/net/netfilter/nft_fib_inet.c
+++ b/net/netfilter/nft_fib_inet.c
@@ -48,6 +48,7 @@ static const struct nft_expr_ops nft_fib_inet_ops = {
 	.eval		= nft_fib_inet_eval,
 	.init		= nft_fib_init,
 	.dump		= nft_fib_dump,
+	.track		= nft_fib_track,
 	.validate	= nft_fib_validate,
 };
 
diff --git a/net/netfilter/nft_fib_netdev.c b/net/netfilter/nft_fib_netdev.c
index 3f3478abd845..69448f630fdb 100644
--- a/net/netfilter/nft_fib_netdev.c
+++ b/net/netfilter/nft_fib_netdev.c
@@ -57,6 +57,7 @@ static const struct nft_expr_ops nft_fib_netdev_ops = {
 	.eval		= nft_fib_netdev_eval,
 	.init		= nft_fib_init,
 	.dump		= nft_fib_dump,
+	.track		= nft_fib_track,
 	.validate	= nft_fib_validate,
 };
 
diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
index 59c468261134..84fbe808644a 100644
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -462,6 +462,7 @@ static const struct nft_expr_ops nft_flow_offload_ops = {
 	.destroy	= nft_flow_offload_destroy,
 	.validate	= nft_flow_offload_validate,
 	.dump		= nft_flow_offload_dump,
+	.track		= NFT_TRACK_GENERIC,
 };
 
 static struct nft_expr_type nft_flow_offload_type __read_mostly = {
diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c
index a534d060ce1b..e535b27d05ae 100644
--- a/net/netfilter/nft_fwd_netdev.c
+++ b/net/netfilter/nft_fwd_netdev.c
@@ -70,6 +70,17 @@ static int nft_fwd_netdev_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_fwd_netdev_track(struct nft_expr_track_ctx *ctx,
+				struct nft_expr_track *track,
+				const struct nft_expr *expr)
+{
+	const struct nft_fwd_netdev *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg_dev, IFNAMSIZ);
+
+	return 1;
+}
+
 static int nft_fwd_netdev_offload(struct nft_offload_ctx *ctx,
 				  struct nft_flow_rule *flow,
 				  const struct nft_expr *expr)
@@ -203,6 +214,29 @@ static int nft_fwd_neigh_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_fwd_neigh_track(struct nft_expr_track_ctx *ctx,
+			       struct nft_expr_track *track,
+			       const struct nft_expr *expr)
+{
+	const struct nft_fwd_neigh *priv = nft_expr_priv(expr);
+	u8 addr_len;
+
+	switch (priv->nfproto) {
+	case NFPROTO_IPV4:
+		addr_len = sizeof(struct in_addr);
+		break;
+	case NFPROTO_IPV6:
+		addr_len = sizeof(struct in6_addr);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+	nft_expr_track_sreg(ctx, priv->sreg_dev, IFNAMSIZ);
+	nft_expr_track_sreg(ctx, priv->sreg_addr, addr_len);
+
+	return 1;
+}
+
 static int nft_fwd_validate(const struct nft_ctx *ctx,
 			    const struct nft_expr *expr,
 			    const struct nft_data **data)
@@ -219,6 +253,7 @@ static const struct nft_expr_ops nft_fwd_neigh_netdev_ops = {
 	.init		= nft_fwd_neigh_init,
 	.dump		= nft_fwd_neigh_dump,
 	.validate	= nft_fwd_validate,
+	.track		= nft_fwd_neigh_track,
 };
 
 static const struct nft_expr_ops nft_fwd_netdev_ops = {
@@ -228,6 +263,7 @@ static const struct nft_expr_ops nft_fwd_netdev_ops = {
 	.init		= nft_fwd_netdev_init,
 	.dump		= nft_fwd_netdev_dump,
 	.validate	= nft_fwd_validate,
+	.track		= nft_fwd_netdev_track,
 	.offload	= nft_fwd_netdev_offload,
 	.offload_action	= nft_fwd_netdev_offload_action,
 };
diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c
index 4fc99c80b28e..f64394e0327e 100644
--- a/net/netfilter/nft_hash.c
+++ b/net/netfilter/nft_hash.c
@@ -165,6 +165,18 @@ static int nft_jhash_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_jhash_track(struct nft_expr_track_ctx *ctx,
+			   struct nft_expr_track *track,
+			   const struct nft_expr *expr)
+{
+	const struct nft_jhash *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg, priv->len);
+	nft_expr_track_dreg(ctx, priv->dreg, sizeof(u32));
+
+	return 1;
+}
+
 static int nft_symhash_dump(struct sk_buff *skb,
 			    const struct nft_expr *expr, bool reset)
 {
@@ -185,6 +197,17 @@ static int nft_symhash_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_symhash_track(struct nft_expr_track_ctx *ctx,
+			     struct nft_expr_track *track,
+			     const struct nft_expr *expr)
+{
+	const struct nft_symhash *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, sizeof(u32));
+
+	return 1;
+}
+
 static struct nft_expr_type nft_hash_type;
 static const struct nft_expr_ops nft_jhash_ops = {
 	.type		= &nft_hash_type,
@@ -192,6 +215,7 @@ static const struct nft_expr_ops nft_jhash_ops = {
 	.eval		= nft_jhash_eval,
 	.init		= nft_jhash_init,
 	.dump		= nft_jhash_dump,
+	.track		= nft_jhash_track,
 };
 
 static const struct nft_expr_ops nft_symhash_ops = {
@@ -200,6 +224,7 @@ static const struct nft_expr_ops nft_symhash_ops = {
 	.eval		= nft_symhash_eval,
 	.init		= nft_symhash_init,
 	.dump		= nft_symhash_dump,
+	.track		= nft_symhash_track,
 };
 
 static const struct nft_expr_ops *
diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c
index 0d744c3f73b3..4b977538ecdd 100644
--- a/net/netfilter/nft_immediate.c
+++ b/net/netfilter/nft_immediate.c
@@ -162,6 +162,17 @@ static int nft_immediate_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_immediate_track(struct nft_expr_track_ctx *ctx,
+			       struct nft_expr_track *track,
+			       const struct nft_expr *expr)
+{
+	const struct nft_immediate_expr *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, priv->dlen);
+
+	return 1;
+}
+
 static int nft_immediate_validate(const struct nft_ctx *ctx,
 				  const struct nft_expr *expr,
 				  const struct nft_data **d)
@@ -249,6 +260,7 @@ static const struct nft_expr_ops nft_imm_ops = {
 	.deactivate	= nft_immediate_deactivate,
 	.destroy	= nft_immediate_destroy,
 	.dump		= nft_immediate_dump,
+	.track		= nft_immediate_track,
 	.validate	= nft_immediate_validate,
 	.offload	= nft_immediate_offload,
 	.offload_action	= nft_immediate_offload_action,
diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c
index 28e2873ba24e..470ddf59b1ea 100644
--- a/net/netfilter/nft_inner.c
+++ b/net/netfilter/nft_inner.c
@@ -368,12 +368,22 @@ static int nft_inner_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_inner_track(struct nft_expr_track_ctx *ctx,
+			   struct nft_expr_track *track,
+			   const struct nft_expr *expr)
+{
+	struct nft_inner *priv = nft_expr_priv(expr);
+
+	return priv->expr.ops->track(ctx, track, (struct nft_expr *)&priv->expr);
+}
+
 static const struct nft_expr_ops nft_inner_ops = {
 	.type		= &nft_inner_type,
 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_inner)),
 	.eval		= nft_inner_eval,
 	.init		= nft_inner_init,
 	.dump		= nft_inner_dump,
+	.track		= nft_inner_track,
 };
 
 struct nft_expr_type nft_inner_type __read_mostly = {
diff --git a/net/netfilter/nft_last.c b/net/netfilter/nft_last.c
index 5d44d45ca4ef..ac6facafff94 100644
--- a/net/netfilter/nft_last.c
+++ b/net/netfilter/nft_last.c
@@ -125,6 +125,7 @@ static const struct nft_expr_ops nft_last_ops = {
 	.destroy	= nft_last_destroy,
 	.clone		= nft_last_clone,
 	.dump		= nft_last_dump,
+	.track		= NFT_TRACK_GENERIC,
 };
 
 struct nft_expr_type nft_last_type __read_mostly = {
diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c
index 10752da5bd16..074de96c4d13 100644
--- a/net/netfilter/nft_limit.c
+++ b/net/netfilter/nft_limit.c
@@ -228,6 +228,7 @@ static const struct nft_expr_ops nft_limit_pkts_ops = {
 	.destroy	= nft_limit_pkts_destroy,
 	.clone		= nft_limit_pkts_clone,
 	.dump		= nft_limit_pkts_dump,
+	.track		= NFT_TRACK_GENERIC,
 };
 
 static void nft_limit_bytes_eval(const struct nft_expr *expr,
@@ -280,6 +281,7 @@ static const struct nft_expr_ops nft_limit_bytes_ops = {
 	.eval		= nft_limit_bytes_eval,
 	.init		= nft_limit_bytes_init,
 	.dump		= nft_limit_bytes_dump,
+	.track		= NFT_TRACK_GENERIC,
 	.clone		= nft_limit_bytes_clone,
 	.destroy	= nft_limit_bytes_destroy,
 };
diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c
index 84e374411877..c72ce995cb55 100644
--- a/net/netfilter/nft_log.c
+++ b/net/netfilter/nft_log.c
@@ -291,6 +291,7 @@ static const struct nft_expr_ops nft_log_ops = {
 	.init		= nft_log_init,
 	.destroy	= nft_log_destroy,
 	.dump		= nft_log_dump,
+	.track		= NFT_TRACK_GENERIC,
 };
 
 static struct nft_expr_type nft_log_type __read_mostly = {
diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c
index 5ba799a7722d..efb45b2ed405 100644
--- a/net/netfilter/nft_lookup.c
+++ b/net/netfilter/nft_lookup.c
@@ -206,6 +206,19 @@ static int nft_lookup_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_lookup_track(struct nft_expr_track_ctx *ctx,
+			    struct nft_expr_track *track,
+			    const struct nft_expr *expr)
+{
+	const struct nft_lookup *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg, priv->set->klen);
+	if (priv->dreg_set)
+		nft_expr_track_dreg(ctx, priv->dreg, priv->set->dlen);
+
+	return 1;
+}
+
 static int nft_lookup_validate(const struct nft_ctx *ctx,
 			       const struct nft_expr *expr,
 			       const struct nft_data **d)
@@ -243,6 +256,7 @@ static const struct nft_expr_ops nft_lookup_ops = {
 	.destroy	= nft_lookup_destroy,
 	.dump		= nft_lookup_dump,
 	.validate	= nft_lookup_validate,
+	.track		= nft_lookup_track,
 };
 
 struct nft_expr_type nft_lookup_type __read_mostly = {
diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c
index b6cd11fd596b..b21e8634d1e0 100644
--- a/net/netfilter/nft_masq.c
+++ b/net/netfilter/nft_masq.c
@@ -96,6 +96,20 @@ static int nft_masq_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_masq_track(struct nft_expr_track_ctx *ctx,
+			  struct nft_expr_track *track,
+			  const struct nft_expr *expr)
+{
+	const struct nft_masq *priv = nft_expr_priv(expr);
+
+	if (priv->sreg_proto_min) {
+		nft_expr_track_sreg(ctx, priv->sreg_proto_min, sizeof(u16));
+		nft_expr_track_sreg(ctx, priv->sreg_proto_max, sizeof(u16));
+	}
+
+	return 1;
+}
+
 static void nft_masq_eval(const struct nft_expr *expr,
 			  struct nft_regs *regs,
 			  const struct nft_pktinfo *pkt)
@@ -145,6 +159,7 @@ static const struct nft_expr_ops nft_masq_ipv4_ops = {
 	.init		= nft_masq_init,
 	.destroy	= nft_masq_ipv4_destroy,
 	.dump		= nft_masq_dump,
+	.track		= nft_masq_track,
 	.validate	= nft_masq_validate,
 };
 
@@ -172,6 +187,7 @@ static const struct nft_expr_ops nft_masq_ipv6_ops = {
 	.init		= nft_masq_init,
 	.destroy	= nft_masq_ipv6_destroy,
 	.dump		= nft_masq_dump,
+	.track		= nft_masq_track,
 	.validate	= nft_masq_validate,
 };
 
@@ -213,6 +229,7 @@ static const struct nft_expr_ops nft_masq_inet_ops = {
 	.init		= nft_masq_init,
 	.destroy	= nft_masq_inet_destroy,
 	.dump		= nft_masq_dump,
+	.track		= nft_masq_track,
 	.validate	= nft_masq_validate,
 };
 
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
index 73128d73fc99..db445f8c9f9f 100644
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -744,6 +744,30 @@ static int nft_meta_get_offload(struct nft_offload_ctx *ctx,
 	return 0;
 }
 
+int nft_meta_get_track(struct nft_expr_track_ctx *ctx,
+		       struct nft_expr_track *track,
+		       const struct nft_expr *expr)
+{
+	const struct nft_meta *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, priv->len);
+
+	return 1;
+}
+EXPORT_SYMBOL_GPL(nft_meta_get_track);
+
+int nft_meta_set_track(struct nft_expr_track_ctx *ctx,
+		       struct nft_expr_track *track,
+		       const struct nft_expr *expr)
+{
+	const struct nft_meta *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg, priv->len);
+
+	return 1;
+}
+EXPORT_SYMBOL_GPL(nft_meta_set_track);
+
 static const struct nft_expr_ops nft_meta_get_ops = {
 	.type		= &nft_meta_type,
 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)),
@@ -761,6 +785,7 @@ static const struct nft_expr_ops nft_meta_set_ops = {
 	.init		= nft_meta_set_init,
 	.destroy	= nft_meta_set_destroy,
 	.dump		= nft_meta_set_dump,
+	.track		= nft_meta_set_track,
 	.validate	= nft_meta_set_validate,
 };
 
@@ -845,6 +870,7 @@ static const struct nft_expr_ops nft_meta_inner_ops = {
 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)),
 	.init		= nft_meta_inner_init,
 	.dump		= nft_meta_get_dump,
+	.track		= nft_meta_get_track,
 	/* direct call to nft_meta_inner_eval(). */
 };
 
diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c
index 296667e25420..e12f4e75f247 100644
--- a/net/netfilter/nft_nat.c
+++ b/net/netfilter/nft_nat.c
@@ -301,6 +301,36 @@ static int nft_nat_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_nat_track(struct nft_expr_track_ctx *ctx,
+			 struct nft_expr_track *track,
+			 const struct nft_expr *expr)
+{
+	const struct nft_nat *priv = nft_expr_priv(expr);
+	u8 alen;
+
+	if (priv->sreg_addr_min) {
+		switch (priv->family) {
+		case NFPROTO_IPV4:
+			alen = sizeof_field(struct nf_nat_range, min_addr.ip);
+			break;
+		case NFPROTO_IPV6:
+			alen = sizeof_field(struct nf_nat_range, min_addr.ip6);
+			break;
+		default:
+			break;
+		}
+
+		nft_expr_track_sreg(ctx, priv->sreg_addr_min, alen);
+		nft_expr_track_sreg(ctx, priv->sreg_addr_max, alen);
+	}
+	if (priv->sreg_proto_min) {
+		nft_expr_track_sreg(ctx, priv->sreg_proto_min, sizeof(u16));
+		nft_expr_track_sreg(ctx, priv->sreg_proto_max, sizeof(u16));
+	}
+
+	return 1;
+}
+
 static void
 nft_nat_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
 {
@@ -317,6 +347,7 @@ static const struct nft_expr_ops nft_nat_ops = {
 	.init           = nft_nat_init,
 	.destroy        = nft_nat_destroy,
 	.dump           = nft_nat_dump,
+	.track		= nft_nat_track,
 	.validate	= nft_nat_validate,
 };
 
@@ -347,6 +378,7 @@ static const struct nft_expr_ops nft_nat_inet_ops = {
 	.init           = nft_nat_init,
 	.destroy        = nft_nat_destroy,
 	.dump           = nft_nat_dump,
+	.track		= nft_nat_track,
 	.validate	= nft_nat_validate,
 };
 
diff --git a/net/netfilter/nft_numgen.c b/net/netfilter/nft_numgen.c
index c46534347405..0cd44b561492 100644
--- a/net/netfilter/nft_numgen.c
+++ b/net/netfilter/nft_numgen.c
@@ -111,6 +111,17 @@ static int nft_ng_inc_dump(struct sk_buff *skb,
 			   priv->offset);
 }
 
+static int nft_ng_inc_track(struct nft_expr_track_ctx *ctx,
+			    struct nft_expr_track *track,
+			    const struct nft_expr *expr)
+{
+	struct nft_ng_inc *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, sizeof(u32));
+
+	return 1;
+}
+
 static void nft_ng_inc_destroy(const struct nft_ctx *ctx,
 			       const struct nft_expr *expr)
 {
@@ -168,6 +179,17 @@ static int nft_ng_random_dump(struct sk_buff *skb,
 			   priv->offset);
 }
 
+static int nft_ng_random_track(struct nft_expr_track_ctx *ctx,
+			       struct nft_expr_track *track,
+			       const struct nft_expr *expr)
+{
+	struct nft_ng_random *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, sizeof(u32));
+
+	return 1;
+}
+
 static struct nft_expr_type nft_ng_type;
 static const struct nft_expr_ops nft_ng_inc_ops = {
 	.type		= &nft_ng_type,
@@ -176,6 +198,7 @@ static const struct nft_expr_ops nft_ng_inc_ops = {
 	.init		= nft_ng_inc_init,
 	.destroy	= nft_ng_inc_destroy,
 	.dump		= nft_ng_inc_dump,
+	.track		= nft_ng_inc_track,
 };
 
 static const struct nft_expr_ops nft_ng_random_ops = {
@@ -184,6 +207,7 @@ static const struct nft_expr_ops nft_ng_random_ops = {
 	.eval		= nft_ng_random_eval,
 	.init		= nft_ng_random_init,
 	.dump		= nft_ng_random_dump,
+	.track		= nft_ng_random_track,
 };
 
 static const struct nft_expr_ops *
diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c
index 0db12fe25d16..e3c3154cbf6f 100644
--- a/net/netfilter/nft_objref.c
+++ b/net/netfilter/nft_objref.c
@@ -91,6 +91,7 @@ static const struct nft_expr_ops nft_objref_ops = {
 	.activate	= nft_objref_activate,
 	.deactivate	= nft_objref_deactivate,
 	.dump		= nft_objref_dump,
+	.track		= NFT_TRACK_GENERIC,
 };
 
 struct nft_objref_map {
@@ -170,6 +171,17 @@ static int nft_objref_map_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_objref_map_track(struct nft_expr_track_ctx *ctx,
+				struct nft_expr_track *track,
+				const struct nft_expr *expr)
+{
+	const struct nft_objref_map *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg, priv->set->klen);
+
+	return 1;
+}
+
 static void nft_objref_map_deactivate(const struct nft_ctx *ctx,
 				      const struct nft_expr *expr,
 				      enum nft_trans_phase phase)
@@ -204,6 +216,7 @@ static const struct nft_expr_ops nft_objref_map_ops = {
 	.deactivate	= nft_objref_map_deactivate,
 	.destroy	= nft_objref_map_destroy,
 	.dump		= nft_objref_map_dump,
+	.track		= nft_objref_map_track,
 };
 
 static const struct nft_expr_ops *
diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c
index 093ec7f96a77..3717b041ed37 100644
--- a/net/netfilter/nft_osf.c
+++ b/net/netfilter/nft_osf.c
@@ -112,6 +112,17 @@ static int nft_osf_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_osf_track(struct nft_expr_track_ctx *ctx,
+			 struct nft_expr_track *track,
+			 const struct nft_expr *expr)
+{
+	const struct nft_osf *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, NFT_OSF_MAXGENRELEN);
+
+	return 1;
+}
+
 static int nft_osf_validate(const struct nft_ctx *ctx,
 			    const struct nft_expr *expr,
 			    const struct nft_data **data)
@@ -139,6 +150,7 @@ static const struct nft_expr_ops nft_osf_op = {
 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_osf)),
 	.init		= nft_osf_init,
 	.dump		= nft_osf_dump,
+	.track		= nft_osf_track,
 	.type		= &nft_osf_type,
 	.validate	= nft_osf_validate,
 };
diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
index 2c06aca301cb..e5929aea685f 100644
--- a/net/netfilter/nft_payload.c
+++ b/net/netfilter/nft_payload.c
@@ -247,6 +247,17 @@ static int nft_payload_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_payload_track(struct nft_expr_track_ctx *ctx,
+			     struct nft_expr_track *track,
+			     const struct nft_expr *expr)
+{
+	const struct nft_payload *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, priv->len);
+
+	return 1;
+}
+
 static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
 				     u32 priv_len, u32 field_len)
 {
@@ -550,6 +561,7 @@ static const struct nft_expr_ops nft_payload_ops = {
 	.eval		= nft_payload_eval,
 	.init		= nft_payload_init,
 	.dump		= nft_payload_dump,
+	.track		= nft_payload_track,
 	.offload	= nft_payload_offload,
 };
 
@@ -559,6 +571,7 @@ const struct nft_expr_ops nft_payload_fast_ops = {
 	.eval		= nft_payload_eval,
 	.init		= nft_payload_init,
 	.dump		= nft_payload_dump,
+	.track		= nft_payload_track,
 	.offload	= nft_payload_offload,
 };
 
@@ -645,6 +658,7 @@ static const struct nft_expr_ops nft_payload_inner_ops = {
 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_payload)),
 	.init		= nft_payload_inner_init,
 	.dump		= nft_payload_dump,
+	.track		= nft_payload_track,
 	/* direct call to nft_payload_inner_eval(). */
 };
 
@@ -913,12 +927,24 @@ static int nft_payload_set_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_payload_set_track(struct nft_expr_track_ctx *ctx,
+				 struct nft_expr_track *track,
+				 const struct nft_expr *expr)
+{
+	const struct nft_payload_set *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg, priv->len);
+
+	return 1;
+}
+
 static const struct nft_expr_ops nft_payload_set_ops = {
 	.type		= &nft_payload_type,
 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_payload_set)),
 	.eval		= nft_payload_set_eval,
 	.init		= nft_payload_set_init,
 	.dump		= nft_payload_set_dump,
+	.track		= nft_payload_set_track,
 };
 
 static const struct nft_expr_ops *
diff --git a/net/netfilter/nft_queue.c b/net/netfilter/nft_queue.c
index 160541791939..62fe8cfa322d 100644
--- a/net/netfilter/nft_queue.c
+++ b/net/netfilter/nft_queue.c
@@ -184,6 +184,18 @@ nft_queue_sreg_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_queue_track(struct nft_expr_track_ctx *ctx,
+			   struct nft_expr_track *track,
+			   const struct nft_expr *expr)
+{
+	const struct nft_queue *priv = nft_expr_priv(expr);
+
+	if (priv->sreg_qnum)
+		nft_expr_track_sreg(ctx, priv->sreg_qnum, sizeof(u32));
+
+	return 1;
+}
+
 static struct nft_expr_type nft_queue_type;
 static const struct nft_expr_ops nft_queue_ops = {
 	.type		= &nft_queue_type,
@@ -191,6 +203,7 @@ static const struct nft_expr_ops nft_queue_ops = {
 	.eval		= nft_queue_eval,
 	.init		= nft_queue_init,
 	.dump		= nft_queue_dump,
+	.track		= nft_queue_track,
 	.validate	= nft_queue_validate,
 };
 
@@ -200,6 +213,7 @@ static const struct nft_expr_ops nft_queue_sreg_ops = {
 	.eval		= nft_queue_sreg_eval,
 	.init		= nft_queue_sreg_init,
 	.dump		= nft_queue_sreg_dump,
+	.track		= nft_queue_track,
 	.validate	= nft_queue_validate,
 };
 
diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c
index 5453056f07df..eb2ae8a7a6a5 100644
--- a/net/netfilter/nft_quota.c
+++ b/net/netfilter/nft_quota.c
@@ -259,6 +259,7 @@ static const struct nft_expr_ops nft_quota_ops = {
 	.destroy	= nft_quota_destroy,
 	.clone		= nft_quota_clone,
 	.dump		= nft_quota_dump,
+	.track		= NFT_TRACK_GENERIC,
 };
 
 static struct nft_expr_type nft_quota_type __read_mostly = {
diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c
index f8258d520229..c75acc1995fc 100644
--- a/net/netfilter/nft_range.c
+++ b/net/netfilter/nft_range.c
@@ -132,12 +132,24 @@ static int nft_range_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_range_track(struct nft_expr_track_ctx *ctx,
+			   struct nft_expr_track *track,
+			   const struct nft_expr *expr)
+{
+	const struct nft_range_expr *priv = nft_expr_priv(expr);
+
+	nft_expr_track_sreg(ctx, priv->sreg, priv->len);
+
+	return 1;
+}
+
 static const struct nft_expr_ops nft_range_ops = {
 	.type		= &nft_range_type,
 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_range_expr)),
 	.eval		= nft_range_eval,
 	.init		= nft_range_init,
 	.dump		= nft_range_dump,
+	.track		= nft_range_track,
 };
 
 struct nft_expr_type nft_range_type __read_mostly = {
diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c
index ab8dfaa34230..824aaf1abdf3 100644
--- a/net/netfilter/nft_redir.c
+++ b/net/netfilter/nft_redir.c
@@ -101,6 +101,20 @@ static int nft_redir_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_redir_track(struct nft_expr_track_ctx *ctx,
+			   struct nft_expr_track *track,
+			   const struct nft_expr *expr)
+{
+	const struct nft_redir *priv = nft_expr_priv(expr);
+
+	if (priv->sreg_proto_min) {
+		nft_expr_track_sreg(ctx, priv->sreg_proto_min, sizeof(u16));
+		nft_expr_track_sreg(ctx, priv->sreg_proto_max, sizeof(u16));
+	}
+
+	return 1;
+}
+
 static void nft_redir_eval(const struct nft_expr *expr,
 			   struct nft_regs *regs,
 			   const struct nft_pktinfo *pkt)
@@ -148,6 +162,7 @@ static const struct nft_expr_ops nft_redir_ipv4_ops = {
 	.init		= nft_redir_init,
 	.destroy	= nft_redir_ipv4_destroy,
 	.dump		= nft_redir_dump,
+	.track		= nft_redir_track,
 	.validate	= nft_redir_validate,
 };
 
@@ -175,6 +190,7 @@ static const struct nft_expr_ops nft_redir_ipv6_ops = {
 	.init		= nft_redir_init,
 	.destroy	= nft_redir_ipv6_destroy,
 	.dump		= nft_redir_dump,
+	.track		= nft_redir_track,
 	.validate	= nft_redir_validate,
 };
 
@@ -203,6 +219,7 @@ static const struct nft_expr_ops nft_redir_inet_ops = {
 	.init		= nft_redir_init,
 	.destroy	= nft_redir_inet_destroy,
 	.dump		= nft_redir_dump,
+	.track		= nft_redir_track,
 	.validate	= nft_redir_validate,
 };
 
diff --git a/net/netfilter/nft_reject_inet.c b/net/netfilter/nft_reject_inet.c
index 554caf967baa..8be0bda077f9 100644
--- a/net/netfilter/nft_reject_inet.c
+++ b/net/netfilter/nft_reject_inet.c
@@ -79,6 +79,7 @@ static const struct nft_expr_ops nft_reject_inet_ops = {
 	.eval		= nft_reject_inet_eval,
 	.init		= nft_reject_init,
 	.dump		= nft_reject_dump,
+	.track		= NFT_TRACK_GENERIC,
 	.validate	= nft_reject_inet_validate,
 };
 
diff --git a/net/netfilter/nft_reject_netdev.c b/net/netfilter/nft_reject_netdev.c
index 61cd8c4ac385..d4f6cfa5c34a 100644
--- a/net/netfilter/nft_reject_netdev.c
+++ b/net/netfilter/nft_reject_netdev.c
@@ -158,6 +158,7 @@ static const struct nft_expr_ops nft_reject_netdev_ops = {
 	.eval		= nft_reject_netdev_eval,
 	.init		= nft_reject_init,
 	.dump		= nft_reject_dump,
+	.track		= NFT_TRACK_GENERIC,
 	.validate	= nft_reject_netdev_validate,
 };
 
diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c
index 63a6069aa02b..478276db8868 100644
--- a/net/netfilter/nft_rt.c
+++ b/net/netfilter/nft_rt.c
@@ -16,6 +16,7 @@
 struct nft_rt {
 	enum nft_rt_keys	key:8;
 	u8			dreg;
+	u8			len;
 };
 
 static u16 get_tcpmss(const struct nft_pktinfo *pkt, const struct dst_entry *skbdst)
@@ -140,6 +141,7 @@ static int nft_rt_get_init(const struct nft_ctx *ctx,
 	default:
 		return -EOPNOTSUPP;
 	}
+	priv->len = len;
 
 	return nft_parse_register_store(ctx, tb[NFTA_RT_DREG], &priv->dreg,
 					NULL, NFT_DATA_VALUE, len);
@@ -160,6 +162,17 @@ static int nft_rt_get_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_rt_get_track(struct nft_expr_track_ctx *ctx,
+			    struct nft_expr_track *track,
+			    const struct nft_expr *expr)
+{
+	const struct nft_rt *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, priv->len);
+
+	return 1;
+}
+
 static int nft_rt_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
 			   const struct nft_data **data)
 {
@@ -190,6 +203,7 @@ static const struct nft_expr_ops nft_rt_get_ops = {
 	.eval		= nft_rt_get_eval,
 	.init		= nft_rt_get_init,
 	.dump		= nft_rt_get_dump,
+	.track		= nft_rt_get_track,
 	.validate	= nft_rt_validate,
 };
 
diff --git a/net/netfilter/nft_socket.c b/net/netfilter/nft_socket.c
index 3bfcfe3ee5aa..bb714dc19247 100644
--- a/net/netfilter/nft_socket.c
+++ b/net/netfilter/nft_socket.c
@@ -213,6 +213,17 @@ static int nft_socket_dump(struct sk_buff *skb,
 	return 0;
 }
 
+static int nft_socket_track(struct nft_expr_track_ctx *ctx,
+			    struct nft_expr_track *track,
+			    const struct nft_expr *expr)
+{
+	const struct nft_socket *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, priv->len);
+
+	return 1;
+}
+
 static int nft_socket_validate(const struct nft_ctx *ctx,
 			       const struct nft_expr *expr,
 			       const struct nft_data **data)
@@ -230,6 +241,7 @@ static const struct nft_expr_ops nft_socket_ops = {
 	.eval		= nft_socket_eval,
 	.init		= nft_socket_init,
 	.dump		= nft_socket_dump,
+	.track		= nft_socket_track,
 	.validate	= nft_socket_validate,
 };
 
diff --git a/net/netfilter/nft_synproxy.c b/net/netfilter/nft_synproxy.c
index bf7268908154..81707c8844d4 100644
--- a/net/netfilter/nft_synproxy.c
+++ b/net/netfilter/nft_synproxy.c
@@ -287,6 +287,7 @@ static const struct nft_expr_ops nft_synproxy_ops = {
 	.init		= nft_synproxy_init,
 	.destroy	= nft_synproxy_destroy,
 	.dump		= nft_synproxy_dump,
+	.track		= NFT_TRACK_GENERIC,
 	.type		= &nft_synproxy_type,
 	.validate	= nft_synproxy_validate,
 };
diff --git a/net/netfilter/nft_tproxy.c b/net/netfilter/nft_tproxy.c
index 089d608f6b32..d7b4ed7f5009 100644
--- a/net/netfilter/nft_tproxy.c
+++ b/net/netfilter/nft_tproxy.c
@@ -312,6 +312,32 @@ static int nft_tproxy_dump(struct sk_buff *skb,
 	return 0;
 }
 
+static int nft_tproxy_track(struct nft_expr_track_ctx *ctx,
+			    struct nft_expr_track *track,
+			    const struct nft_expr *expr)
+{
+	const struct nft_tproxy *priv = nft_expr_priv(expr);
+	u8 alen = 0;
+
+	switch (priv->family) {
+	case NFPROTO_IPV4:
+		alen = sizeof_field(union nf_inet_addr, in);
+		break;
+#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
+	case NFPROTO_IPV6:
+		alen = sizeof_field(union nf_inet_addr, in6);
+		break;
+#endif
+	}
+
+	if (priv->sreg_addr)
+		nft_expr_track_sreg(ctx, priv->sreg_addr, alen);
+	if (priv->sreg_port)
+		nft_expr_track_sreg(ctx, priv->sreg_port, sizeof(u16));
+
+	return 1;
+}
+
 static int nft_tproxy_validate(const struct nft_ctx *ctx,
 			       const struct nft_expr *expr,
 			       const struct nft_data **data)
@@ -327,6 +353,7 @@ static const struct nft_expr_ops nft_tproxy_ops = {
 	.init		= nft_tproxy_init,
 	.destroy	= nft_tproxy_destroy,
 	.dump		= nft_tproxy_dump,
+	.track		= nft_tproxy_track,
 	.validate	= nft_tproxy_validate,
 };
 
diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c
index ddd698332730..9ed4e65899f3 100644
--- a/net/netfilter/nft_tunnel.c
+++ b/net/netfilter/nft_tunnel.c
@@ -124,6 +124,17 @@ static int nft_tunnel_get_dump(struct sk_buff *skb,
 	return -1;
 }
 
+static int nft_tunnel_get_track(struct nft_expr_track_ctx *ctx,
+				struct nft_expr_track *track,
+				const struct nft_expr *expr)
+{
+	const struct nft_tunnel *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, priv->len);
+
+	return 1;
+}
+
 static struct nft_expr_type nft_tunnel_type;
 static const struct nft_expr_ops nft_tunnel_get_ops = {
 	.type		= &nft_tunnel_type,
@@ -131,6 +142,7 @@ static const struct nft_expr_ops nft_tunnel_get_ops = {
 	.eval		= nft_tunnel_get_eval,
 	.init		= nft_tunnel_get_init,
 	.dump		= nft_tunnel_get_dump,
+	.track		= nft_tunnel_get_track,
 };
 
 static struct nft_expr_type nft_tunnel_type __read_mostly = {
diff --git a/net/netfilter/nft_xfrm.c b/net/netfilter/nft_xfrm.c
index 6252e1d9def8..f75fb1a2be80 100644
--- a/net/netfilter/nft_xfrm.c
+++ b/net/netfilter/nft_xfrm.c
@@ -229,6 +229,17 @@ static int nft_xfrm_get_dump(struct sk_buff *skb,
 	return 0;
 }
 
+static int nft_xfrm_get_track(struct nft_expr_track_ctx *ctx,
+			      struct nft_expr_track *track,
+			      const struct nft_expr *expr)
+{
+	const struct nft_xfrm *priv = nft_expr_priv(expr);
+
+	nft_expr_track_dreg(ctx, priv->dreg, priv->len);
+
+	return 1;
+}
+
 static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
 			     const struct nft_data **data)
 {
@@ -261,6 +272,7 @@ static const struct nft_expr_ops nft_xfrm_get_ops = {
 	.eval		= nft_xfrm_get_eval,
 	.init		= nft_xfrm_get_init,
 	.dump		= nft_xfrm_get_dump,
+	.track		= nft_xfrm_get_track,
 	.validate	= nft_xfrm_validate,
 };
 
-- 
2.30.2




[Index of Archives]     [Netfitler Users]     [Berkeley Packet Filter]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux