nft: consolidate nft_rule_find for ARP, IPv4 and IPv6

This patch kills nft_arp_rule_find, which is almost a copy and paste
of the original nft_rule_find function. Refactor this function to
move specific protocol parts to the corresponding nft-{ipv4,ipv6,arp}.c
files.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
index 494d2c2..0e6d9f9 100644
--- a/iptables/nft-arp.c
+++ b/iptables/nft-arp.c
@@ -564,6 +564,49 @@
 				  (unsigned char*)b->arp.tgt_devaddr.mask);
 }
 
+static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nft_rule *r,
+			      void *data)
+{
+	struct arpt_entry *fw = data;
+	struct xt_entry_target *t_fw, *t_this;
+	char *targname_fw, *targname_this;
+	struct xtables_target *target_fw, *target_this;
+	struct arpt_entry this = {};
+
+	/* Delete by matching rule case */
+	nft_rule_to_arpt_entry(r, &this);
+
+	DEBUGP("comparing with... ");
+
+/*	nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0); */
+
+	if (!ops->is_same(fw, &this))
+		return false;
+
+	t_fw = nft_arp_get_target(fw);
+	t_this = nft_arp_get_target(&this);
+
+	targname_fw = t_fw->u.user.name;
+	targname_this = t_this->u.user.name;
+
+	target_fw = xtables_find_target(targname_fw, XTF_TRY_LOAD);
+	target_this = xtables_find_target(targname_this, XTF_TRY_LOAD);
+
+	if (target_fw != NULL && target_this != NULL) {
+		if (!compare_targets(target_fw, target_this)) {
+			DEBUGP("Different target\n");
+			return false;
+		}
+	} else {
+		if (strcmp(targname_fw, targname_this) != 0) {
+			DEBUGP("Different verdict\n");
+			return false;
+		}
+	}
+
+	return true;
+}
+
 struct nft_family_ops nft_family_ops_arp = {
 	.add			= nft_arp_add,
 	.is_same		= nft_arp_is_same,
@@ -573,4 +616,5 @@
 	.parse_immediate	= nft_arp_parse_immediate,
 	.print_firewall		= nft_arp_print_firewall,
 	.post_parse		= NULL,
+	.rule_find		= nft_arp_rule_find,
 };
diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c
index 71fbc83..2142a2e 100644
--- a/iptables/nft-ipv4.c
+++ b/iptables/nft-ipv4.c
@@ -371,6 +371,14 @@
 	cs->target = t;
 }
 
+static bool nft_ipv4_rule_find(struct nft_family_ops *ops,
+			       struct nft_rule *r, void *data)
+{
+	struct iptables_command_state *cs = data;
+
+	return nft_ipv46_rule_find(ops, r, cs);
+}
+
 struct nft_family_ops nft_family_ops_ipv4 = {
 	.add			= nft_ipv4_add,
 	.is_same		= nft_ipv4_is_same,
@@ -381,4 +389,5 @@
 	.save_firewall		= nft_ipv4_save_firewall,
 	.post_parse		= nft_ipv4_post_parse,
 	.parse_target		= nft_ipv4_parse_target,
+	.rule_find		= nft_ipv4_rule_find,
 };
diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c
index 3d35290..dbb148a 100644
--- a/iptables/nft-ipv6.c
+++ b/iptables/nft-ipv6.c
@@ -294,6 +294,14 @@
 	cs->target = t;
 }
 
+static bool nft_ipv6_rule_find(struct nft_family_ops *ops,
+			       struct nft_rule *r, void *data)
+{
+	struct iptables_command_state *cs = data;
+
+	return nft_ipv46_rule_find(ops, r, cs);
+}
+
 struct nft_family_ops nft_family_ops_ipv6 = {
 	.add			= nft_ipv6_add,
 	.is_same		= nft_ipv6_is_same,
@@ -304,4 +312,5 @@
 	.save_firewall		= nft_ipv6_save_firewall,
 	.post_parse		= nft_ipv6_post_parse,
 	.parse_target		= nft_ipv6_parse_target,
+	.rule_find		= nft_ipv6_rule_find,
 };
diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index c1cb5a7..acf352d 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -658,3 +658,89 @@
 	return NULL;
 }
 
+static bool
+compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2)
+{
+	struct xtables_rule_match *mp1;
+	struct xtables_rule_match *mp2;
+
+	for (mp1 = mt1, mp2 = mt2; mp1 && mp2; mp1 = mp1->next, mp2 = mp2->next) {
+		struct xt_entry_match *m1 = mp1->match->m;
+		struct xt_entry_match *m2 = mp2->match->m;
+
+		if (strcmp(m1->u.user.name, m2->u.user.name) != 0) {
+			DEBUGP("mismatching match name\n");
+			return false;
+		}
+
+		if (m1->u.user.match_size != m2->u.user.match_size) {
+			DEBUGP("mismatching match size\n");
+			return false;
+		}
+
+		if (memcmp(m1->data, m2->data,
+			   m1->u.user.match_size - sizeof(*m1)) != 0) {
+			DEBUGP("mismatch match data\n");
+			return false;
+		}
+	}
+
+	/* Both cursors should be NULL */
+	if (mp1 != mp2) {
+		DEBUGP("mismatch matches amount\n");
+		return false;
+	}
+
+	return true;
+}
+
+bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2)
+{
+	if (tg1 == NULL && tg2 == NULL)
+		return true;
+
+	if ((tg1 == NULL && tg2 != NULL) || (tg1 != NULL && tg2 == NULL))
+		return false;
+
+	if (strcmp(tg1->t->u.user.name, tg2->t->u.user.name) != 0)
+		return false;
+
+	if (memcmp(tg1->t->data, tg2->t->data,
+		   tg1->t->u.user.target_size - sizeof(*tg1->t)) != 0) {
+		return false;
+	}
+
+	return true;
+}
+
+bool nft_ipv46_rule_find(struct nft_family_ops *ops,
+			 struct nft_rule *r, struct iptables_command_state *cs)
+{
+	struct iptables_command_state this = {};
+
+	nft_rule_to_iptables_command_state(r, &this);
+
+	DEBUGP("comparing with... ");
+#ifdef DEBUG_DEL
+	nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0);
+#endif
+	if (!ops->is_same(cs, &this))
+		return false;
+
+	if (!compare_matches(cs->matches, this.matches)) {
+		DEBUGP("Different matches\n");
+		return false;
+	}
+
+	if (!compare_targets(cs->target, this.target)) {
+		DEBUGP("Different target\n");
+		return false;
+	}
+
+	if (strcmp(cs->jumpto, this.jumpto) != 0) {
+		DEBUGP("Different verdict\n");
+		return false;
+	}
+
+	return true;
+}
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
index 80f2bc6..3d1f433 100644
--- a/iptables/nft-shared.h
+++ b/iptables/nft-shared.h
@@ -54,6 +54,8 @@
 	void (*post_parse)(int command, struct iptables_command_state *cs,
 			   struct xtables_args *args);
 	void (*parse_target)(struct xtables_target *t, void *data);
+	bool (*rule_find)(struct nft_family_ops *ops, struct nft_rule *r,
+			  void *data);
 };
 
 void add_meta(struct nft_rule *r, uint32_t key);
@@ -117,6 +119,12 @@
 
 struct nft_family_ops *nft_family_ops_lookup(int family);
 
+struct nft_handle;
+bool nft_ipv46_rule_find(struct nft_family_ops *ops, struct nft_rule *r,
+			 struct iptables_command_state *cs);
+
+bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2);
+
 struct addr_mask {
 	union {
 		struct in_addr	*v4;
diff --git a/iptables/nft.c b/iptables/nft.c
index 8ac43c5..d08a513 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -1487,62 +1487,6 @@
 	return 0;
 }
 
-static bool
-compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2)
-{
-	struct xtables_rule_match *mp1;
-	struct xtables_rule_match *mp2;
-
-	for (mp1 = mt1, mp2 = mt2; mp1 && mp2; mp1 = mp1->next, mp2 = mp2->next) {
-		struct xt_entry_match *m1 = mp1->match->m;
-		struct xt_entry_match *m2 = mp2->match->m;
-
-		if (strcmp(m1->u.user.name, m2->u.user.name) != 0) {
-			DEBUGP("mismatching match name\n");
-			return false;
-		}
-
-		if (m1->u.user.match_size != m2->u.user.match_size) {
-			DEBUGP("mismatching match size\n");
-			return false;
-		}
-
-		if (memcmp(m1->data, m2->data,
-			   m1->u.user.match_size - sizeof(*m1)) != 0) {
-			DEBUGP("mismatch match data\n");
-			return false;
-		}
-	}
-
-	/* Both cursors should be NULL */
-	if (mp1 != mp2) {
-		DEBUGP("mismatch matches amount\n");
-		return false;
-	}
-
-	return true;
-}
-
-static bool
-compare_targets(struct xtables_target *tg1, struct xtables_target *tg2)
-{
-	if (tg1 == NULL && tg2 == NULL)
-		return true;
-
-	if ((tg1 == NULL && tg2 != NULL) || (tg1 != NULL && tg2 == NULL))
-		return false;
-
-	if (strcmp(tg1->t->u.user.name, tg2->t->u.user.name) != 0)
-		return false;
-
-	if (memcmp(tg1->t->data, tg2->t->data,
-		   tg1->t->u.user.target_size - sizeof(*tg1->t)) != 0) {
-		return false;
-	}
-
-	return true;
-}
-
 static void
 __nft_rule_del(struct nft_handle *h, struct nft_rule *r)
 {
@@ -1572,8 +1516,8 @@
 }
 
 static struct nft_rule *
-nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table,
-	      struct iptables_command_state *cs, int rulenum)
+nft_rule_find(struct nft_handle *h, struct nft_rule_list *list,
+	      const char *chain, const char *table, void *data, int rulenum)
 {
 	struct nft_rule *r;
 	struct nft_rule_list_iter *iter;
@@ -1590,9 +1534,6 @@
 			nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE);
 		const char *rule_chain =
 			nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN);
-		const struct nft_family_ops *ops = nft_family_ops_lookup(
-				nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY));
-		struct iptables_command_state this = {};
 
 		if (strcmp(table, rule_table) != 0 ||
 		    strcmp(chain, rule_chain) != 0) {
@@ -1607,33 +1548,9 @@
 			found = true;
 			break;
 		} else {
-			/* Delete by matching rule case */
-			nft_rule_to_iptables_command_state(r, &this);
-
-			DEBUGP("comparing with... ");
-#ifdef DEBUG_DEL
-			nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0);
-#endif
-			if (!ops->is_same(cs, &this))
-				goto next;
-
-			if (!compare_matches(cs->matches, this.matches)) {
-				DEBUGP("Different matches\n");
-				goto next;
-			}
-
-			if (!compare_targets(cs->target, this.target)) {
-				DEBUGP("Different target\n");
-				goto next;
-			}
-
-			if (strcmp(cs->jumpto, this.jumpto) != 0) {
-				DEBUGP("Different verdict\n");
-				goto next;
-			}
-
-			found = true;
-			break;
+			found = h->ops->rule_find(h->ops, r, data);
+			if (found)
+				break;
 		}
 next:
 		rule_ctr++;
@@ -1658,7 +1575,7 @@
 	if (list == NULL)
 		return 0;
 
-	ret = nft_rule_find(list, chain, table, e, -1) ? 1 : 0;
+	ret = nft_rule_find(h, list, chain, table, e, -1) ? 1 : 0;
 	if (ret == 0)
 		errno = ENOENT;
 
@@ -1681,7 +1598,7 @@
 	if (list == NULL)
 		return 0;
 
-	r = nft_rule_find(list, chain, table, cs, -1);
+	r = nft_rule_find(h, list, chain, table, cs, -1);
 	if (r != NULL) {
 		ret = 1;
 
@@ -1755,7 +1672,7 @@
 		if (list == NULL)
 			goto err;
 
-		r = nft_rule_find(list, chain, table, cs, rulenum);
+		r = nft_rule_find(h, list, chain, table, cs, rulenum);
 		if (r == NULL) {
 			errno = ENOENT;
 			goto err;
@@ -1786,7 +1703,7 @@
 	if (list == NULL)
 		return 0;
 
-	r = nft_rule_find(list, chain, table, NULL, rulenum);
+	r = nft_rule_find(h, list, chain, table, NULL, rulenum);
 	if (r != NULL) {
 		ret = 1;
 
@@ -1818,7 +1735,7 @@
 	if (list == NULL)
 		return 0;
 
-	r = nft_rule_find(list, chain, table, cs, rulenum);
+	r = nft_rule_find(h, list, chain, table, cs, rulenum);
 	if (r != NULL) {
 		DEBUGP("replacing rule with handle=%llu\n",
 			(unsigned long long)
@@ -2136,7 +2053,7 @@
 	if (list == NULL)
 		return 0;
 
-	r = nft_rule_find(list, chain, table, NULL, rulenum);
+	r = nft_rule_find(h, list, chain, table, NULL, rulenum);
 	if (r == NULL) {
 		errno = ENOENT;
 		ret = 1;
@@ -2624,91 +2541,6 @@
 	return ret == 0 ? 1 : 0;
 }
 
-static struct nft_rule *
-nft_arp_rule_find(struct nft_rule_list *list, const char *chain,
-		  const char *table, struct arpt_entry *fw, int rulenum)
-{
-	struct nft_rule *r;
-	struct nft_rule_list_iter *iter;
-	int rule_ctr = 0;
-	bool found = false;
-
-	iter = nft_rule_list_iter_create(list);
-	if (iter == NULL)
-		return 0;
-
-	r = nft_rule_list_iter_next(iter);
-	while (r != NULL) {
-		const char *rule_table =
-			nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE);
-		const char *rule_chain =
-			nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN);
-		const struct nft_family_ops *ops = nft_family_ops_lookup(
-				nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY));
-		struct arpt_entry this = {};
-
-		if (strcmp(table, rule_table) != 0 ||
-		    strcmp(chain, rule_chain) != 0) {
-			DEBUGP("different chain / table\n");
-			goto next;
-		}
-
-		if (rulenum >= 0) {
-			/* Delete by rule number case */
-			if (rule_ctr != rulenum)
-				goto next;
-			found = true;
-			break;
-		} else {
-			struct xt_entry_target *t_fw, *t_this;
-			char *targname_fw, *targname_this;
-			struct xtables_target *target_fw, *target_this;
-
-			/* Delete by matching rule case */
-			nft_rule_to_arpt_entry(r, &this);
-
-			DEBUGP("comparing with... ");
-#ifdef DEBUG_DEL
-			nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0);
-#endif
-
-			if (!ops->is_same(fw, &this))
-				goto next;
-
-			t_fw = nft_arp_get_target(fw);
-			t_this = nft_arp_get_target(&this);
-
-			targname_fw = t_fw->u.user.name;
-			targname_this = t_this->u.user.name;
-
-			target_fw = xtables_find_target(targname_fw, XTF_TRY_LOAD);
-			target_this = xtables_find_target(targname_this, XTF_TRY_LOAD);
-
-			if (target_fw != NULL && target_this != NULL) {
-				if (!compare_targets(target_fw, target_this)) {
-					DEBUGP("Different target\n");
-					goto next;
-				}
-			} else {
-				if (strcmp(targname_fw, targname_this) != 0) {
-					DEBUGP("Different verdict\n");
-					goto next;
-				}
-			}
-
-			found = true;
-			break;
-		}
-next:
-		rule_ctr++;
-		r = nft_rule_list_iter_next(iter);
-	}
-
-	nft_rule_list_iter_destroy(iter);
-
-	return found ? r : NULL;
-}
-
 int nft_arp_rule_insert(struct nft_handle *h, const char *chain,
 			const char *table, struct arpt_entry *fw,
 			int rulenum, bool verbose)
@@ -2728,7 +2560,7 @@
 		if (list == NULL)
 			goto err;
 
-		r = nft_arp_rule_find(list, chain, table, fw, rulenum);
+		r = nft_rule_find(h, list, chain, table, fw, rulenum);
 		if (r == NULL) {
 			errno = ENOENT;
 			goto err;