diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index 5b55c7c0a7052..871037d21a7a6 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -22,6 +22,7 @@
 
 #include <linux/netfilter/nf_tables.h>
 #include <linux/netfilter/xt_comment.h>
+#include <linux/netfilter/xt_limit.h>
 
 #include <libmnl/libmnl.h>
 #include <libnftnl/rule.h>
@@ -549,6 +550,49 @@ void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 	ops->parse_immediate(jumpto, nft_goto, data);
 }
 
+static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	__u32 burst = nftnl_expr_get_u32(e, NFTNL_EXPR_LIMIT_BURST);
+	__u64 unit = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_UNIT);
+	__u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE);
+	struct xtables_rule_match **matches;
+	struct xtables_match *match;
+	struct nft_family_ops *ops;
+	struct xt_rateinfo *rinfo;
+	size_t size;
+
+	switch (ctx->family) {
+	case NFPROTO_IPV4:
+	case NFPROTO_IPV6:
+	case NFPROTO_BRIDGE:
+		matches = &ctx->cs->matches;
+		break;
+	default:
+		fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
+			ctx->family);
+		exit(EXIT_FAILURE);
+	}
+
+	match = xtables_find_match("limit", XTF_TRY_LOAD, matches);
+	if (match == NULL)
+		return;
+
+	size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
+	match->m = xtables_calloc(1, size);
+	match->m->u.match_size = size;
+	strcpy(match->m->u.user.name, match->name);
+	match->m->u.user.revision = match->revision;
+	xs_init_match(match);
+
+	rinfo = (void *)match->m->data;
+	rinfo->avg = XT_LIMIT_SCALE * unit / rate;
+	rinfo->burst = burst;
+
+	ops = nft_family_ops_lookup(ctx->family);
+	if (ops->parse_match != NULL)
+		ops->parse_match(match, ctx->cs);
+}
+
 void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
 					struct iptables_command_state *cs)
 {
@@ -586,6 +630,8 @@ void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
 			nft_parse_match(&ctx, expr);
 		else if (strcmp(name, "target") == 0)
 			nft_parse_target(&ctx, expr);
+		else if (strcmp(name, "limit") == 0)
+			nft_parse_limit(&ctx, expr);
 
 		expr = nftnl_expr_iter_next(iter);
 	}
diff --git a/iptables/nft.c b/iptables/nft.c
index 120039ed6aac8..8c0746dd94b87 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -38,6 +38,8 @@
 #include <linux/netfilter/nf_tables.h>
 #include <linux/netfilter/nf_tables_compat.h>
 
+#include <linux/netfilter/xt_limit.h>
+
 #include <libmnl/libmnl.h>
 #include <libnftnl/table.h>
 #include <libnftnl/chain.h>
@@ -896,11 +898,50 @@ static int __add_match(struct nftnl_expr *e, struct xt_entry_match *m)
 	return 0;
 }
 
+static int add_nft_limit(struct nftnl_rule *r, struct xt_entry_match *m)
+{
+	struct xt_rateinfo *rinfo = (void *)m->data;
+	static const uint32_t mult[] = {
+		XT_LIMIT_SCALE*24*60*60,	/* day */
+		XT_LIMIT_SCALE*60*60,		/* hour */
+		XT_LIMIT_SCALE*60,		/* min */
+		XT_LIMIT_SCALE,			/* sec */
+	};
+	struct nftnl_expr *expr;
+	int i;
+
+	expr = nftnl_expr_alloc("limit");
+	if (!expr)
+		return -ENOMEM;
+
+	for (i = 1; i < ARRAY_SIZE(mult); i++) {
+		if (rinfo->avg > mult[i] ||
+		    mult[i] / rinfo->avg < mult[i] % rinfo->avg)
+			break;
+	}
+
+	nftnl_expr_set_u32(expr, NFTNL_EXPR_LIMIT_TYPE, NFT_LIMIT_PKTS);
+	nftnl_expr_set_u32(expr, NFTNL_EXPR_LIMIT_FLAGS, 0);
+
+	nftnl_expr_set_u64(expr, NFTNL_EXPR_LIMIT_RATE,
+			   mult[i - 1] / rinfo->avg);
+        nftnl_expr_set_u64(expr, NFTNL_EXPR_LIMIT_UNIT,
+			   mult[i - 1] / XT_LIMIT_SCALE);
+
+	nftnl_expr_set_u32(expr, NFTNL_EXPR_LIMIT_BURST, rinfo->burst);
+
+	nftnl_rule_add_expr(r, expr);
+	return 0;
+}
+
 int add_match(struct nftnl_rule *r, struct xt_entry_match *m)
 {
 	struct nftnl_expr *expr;
 	int ret;
 
+	if (!strcmp(m->u.user.name, "limit"))
+		return add_nft_limit(r, m);
+
 	expr = nftnl_expr_alloc("match");
 	if (expr == NULL)
 		return -ENOMEM;
@@ -2996,8 +3037,9 @@ static const char *supported_exprs[NFT_COMPAT_EXPR_MAX] = {
 };
 
 
-static int nft_is_expr_compatible(const char *name)
+static int nft_is_expr_compatible(const struct nftnl_expr *expr)
 {
+	const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
 	int i;
 
 	for (i = 0; i < NFT_COMPAT_EXPR_MAX; i++) {
@@ -3005,6 +3047,11 @@ static int nft_is_expr_compatible(const char *name)
 			return 0;
 	}
 
+	if (!strcmp(name, "limit") &&
+	    nftnl_expr_get_u32(expr, NFTNL_EXPR_LIMIT_TYPE) == NFT_LIMIT_PKTS &&
+	    nftnl_expr_get_u32(expr, NFTNL_EXPR_LIMIT_FLAGS) == 0)
+		return 0;
+
 	return 1;
 }
 
@@ -3020,9 +3067,7 @@ static bool nft_is_rule_compatible(struct nftnl_rule *rule)
 
 	expr = nftnl_expr_iter_next(iter);
 	while (expr != NULL) {
-		const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
-
-		if (nft_is_expr_compatible(name) == 0) {
+		if (nft_is_expr_compatible(expr) == 0) {
 			expr = nftnl_expr_iter_next(iter);
 			continue;
 		}
