xtables: nft-arp: implements is_same op for ARP family

The following patch implements the is_same operation
for ARP family needed for searching arp rule.

Signed-off-by: Giuseppe Longo <giuseppelng@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
index 6ec8a45..494d2c2 100644
--- a/iptables/nft-arp.c
+++ b/iptables/nft-arp.c
@@ -15,12 +15,13 @@
 
 #include <xtables.h>
 #include <net/if_arp.h>
-#include <if_ether.h>
+#include <netinet/if_ether.h>
 
 #include <linux/netfilter_arp/arp_tables.h>
 #include <linux/netfilter/nf_tables.h>
 
 #include "nft-shared.h"
+#include "nft.h"
 
 /* a few names */
 char *opcodes[] =
@@ -334,7 +335,7 @@
 	}
 }
 
-static void nft_rule_to_arpt_entry(struct nft_rule *r, struct arpt_entry *fw)
+void nft_rule_to_arpt_entry(struct nft_rule *r, struct arpt_entry *fw)
 {
 	struct nft_rule_expr_iter *iter;
 	struct nft_rule_expr *expr;
@@ -537,9 +538,35 @@
 		fputc('\n', stdout);
 }
 
+static bool nft_arp_is_same(const void *data_a,
+			    const void *data_b)
+{
+	const struct arpt_entry *a = data_a;
+	const struct arpt_entry *b = data_b;
+
+	if (a->arp.src.s_addr != b->arp.src.s_addr
+	    || a->arp.tgt.s_addr != b->arp.tgt.s_addr
+	    || a->arp.smsk.s_addr != b->arp.tmsk.s_addr
+	    || a->arp.arpro != b->arp.arpro
+	    || a->arp.flags != b->arp.flags
+	    || a->arp.invflags != b->arp.invflags) {
+		DEBUGP("different src/dst/proto/flags/invflags\n");
+		return false;
+	}
+
+	return is_same_interfaces(a->arp.src_devaddr.addr,
+				  a->arp.tgt_devaddr.addr,
+				  (unsigned char*)a->arp.src_devaddr.mask,
+				  (unsigned char*)a->arp.tgt_devaddr.mask,
+				  b->arp.src_devaddr.addr,
+				  a->arp.tgt_devaddr.addr,
+				  (unsigned char*)b->arp.src_devaddr.mask,
+				  (unsigned char*)b->arp.tgt_devaddr.mask);
+}
+
 struct nft_family_ops nft_family_ops_arp = {
 	.add			= nft_arp_add,
-	.is_same		= NULL,
+	.is_same		= nft_arp_is_same,
 	.print_payload		= NULL,
 	.parse_meta		= nft_arp_parse_meta,
 	.parse_payload		= nft_arp_parse_payload,
diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c
index 4034098..71fbc83 100644
--- a/iptables/nft-ipv4.c
+++ b/iptables/nft-ipv4.c
@@ -66,9 +66,12 @@
 	return cs->fw.ip.flags;
 }
 
-static bool nft_ipv4_is_same(const struct iptables_command_state *a,
-			     const struct iptables_command_state *b)
+static bool nft_ipv4_is_same(const void *data_a,
+			     const void *data_b)
 {
+	const struct iptables_command_state *a = data_a;
+	const struct iptables_command_state *b = data_b;
+
 	if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr
 	    || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr
 	    || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr
diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c
index 2efe95e..3d35290 100644
--- a/iptables/nft-ipv6.c
+++ b/iptables/nft-ipv6.c
@@ -49,9 +49,12 @@
 	return cs->fw6.ipv6.flags;
 }
 
-static bool nft_ipv6_is_same(const struct iptables_command_state *a,
-			     const struct iptables_command_state *b)
+static bool nft_ipv6_is_same(const void *data_a,
+			     const void *data_b)
 {
+	const struct iptables_command_state *a = data_a;
+	const struct iptables_command_state *b = data_b;
+
 	if (memcmp(a->fw6.ipv6.src.s6_addr, b->fw6.ipv6.src.s6_addr,
 		   sizeof(struct in6_addr)) != 0
 	    || memcmp(a->fw6.ipv6.dst.s6_addr, b->fw6.ipv6.dst.s6_addr,
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
index 375c714..80f2bc6 100644
--- a/iptables/nft-shared.h
+++ b/iptables/nft-shared.h
@@ -38,8 +38,8 @@
 
 struct nft_family_ops {
 	int (*add)(struct nft_rule *r, void *data);
-	bool (*is_same)(const struct iptables_command_state *a,
-			const struct iptables_command_state *b);
+	bool (*is_same)(const void *data_a,
+			const void *data_b);
 	void (*print_payload)(struct nft_rule_expr *e,
 			      struct nft_rule_expr_iter *iter);
 	void (*parse_meta)(struct nft_rule_expr *e, uint8_t key,
diff --git a/iptables/nft.c b/iptables/nft.c
index 28c5473..8ac43c5 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -2660,10 +2660,41 @@
 			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;
diff --git a/iptables/nft.h b/iptables/nft.h
index 25275ce..09d3e0c 100644
--- a/iptables/nft.h
+++ b/iptables/nft.h
@@ -162,4 +162,6 @@
 			const char *table, struct arpt_entry *fw,
 			int rulenum, bool verbose);
 
+void nft_rule_to_arpt_entry(struct nft_rule *r, struct arpt_entry *fw);
+
 #endif
diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c
index 68687c0..4b3b046 100644
--- a/iptables/xtables-arp.c
+++ b/iptables/xtables-arp.c
@@ -1402,7 +1402,7 @@
 					handle);*/
 		break;
 	case CMD_INSERT:
-		ret = append_entry(h, chain, *table, e, rulenum,
+		ret = append_entry(h, chain, *table, e, rulenum - 1,
 				   nsaddrs, saddrs, ndaddrs, daddrs,
 				   options&OPT_VERBOSE, false);
 		break;