Home | History | Annotate | Download | only in iptables
      1 /*
      2  * (C) 2013 by Pablo Neira Ayuso <pablo (at) netfilter.org>
      3  * (C) 2013 by Giuseppe Longo <giuseppelng (at) gmail.com>
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 2 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
     11  */
     12 
     13 #include <stdio.h>
     14 #include <stdlib.h>
     15 #include <string.h>
     16 #include <netdb.h>
     17 #include <net/if_arp.h>
     18 
     19 #include <xtables.h>
     20 #include <libiptc/libxtc.h>
     21 #include <net/if_arp.h>
     22 #include <netinet/if_ether.h>
     23 
     24 #include <linux/netfilter_arp/arp_tables.h>
     25 #include <linux/netfilter/nf_tables.h>
     26 
     27 #include "nft-shared.h"
     28 #include "nft-arp.h"
     29 #include "nft.h"
     30 
     31 /* a few names */
     32 char *opcodes[] =
     33 {
     34 	"Request",
     35 	"Reply",
     36 	"Request_Reverse",
     37 	"Reply_Reverse",
     38 	"DRARP_Request",
     39 	"DRARP_Reply",
     40 	"DRARP_Error",
     41 	"InARP_Request",
     42 	"ARP_NAK",
     43 };
     44 
     45 static char *
     46 addr_to_dotted(const struct in_addr *addrp)
     47 {
     48 	static char buf[20];
     49 	const unsigned char *bytep;
     50 
     51 	bytep = (const unsigned char *) &(addrp->s_addr);
     52 	sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
     53 	return buf;
     54 }
     55 
     56 static char *
     57 addr_to_host(const struct in_addr *addr)
     58 {
     59 	struct hostent *host;
     60 
     61 	if ((host = gethostbyaddr((char *) addr,
     62 					sizeof(struct in_addr), AF_INET)) != NULL)
     63 		return (char *) host->h_name;
     64 
     65 	return (char *) NULL;
     66 }
     67 
     68 static char *
     69 addr_to_network(const struct in_addr *addr)
     70 {
     71 	struct netent *net;
     72 
     73 	if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
     74 		return (char *) net->n_name;
     75 
     76 	return (char *) NULL;
     77 }
     78 
     79 static char *
     80 addr_to_anyname(const struct in_addr *addr)
     81 {
     82 	char *name;
     83 
     84 	if ((name = addr_to_host(addr)) != NULL ||
     85 		(name = addr_to_network(addr)) != NULL)
     86 		return name;
     87 
     88 	return addr_to_dotted(addr);
     89 }
     90 
     91 static char *
     92 mask_to_dotted(const struct in_addr *mask)
     93 {
     94 	int i;
     95 	static char buf[20];
     96 	u_int32_t maskaddr, bits;
     97 
     98 	maskaddr = ntohl(mask->s_addr);
     99 
    100 	if (maskaddr == 0xFFFFFFFFL)
    101 		/* we don't want to see "/32" */
    102 		return "";
    103 
    104 	i = 32;
    105 	bits = 0xFFFFFFFEL;
    106 	while (--i >= 0 && maskaddr != bits)
    107 		bits <<= 1;
    108 	if (i >= 0)
    109 		sprintf(buf, "/%d", i);
    110 	else
    111 		/* mask was not a decent combination of 1's and 0's */
    112 		sprintf(buf, "/%s", addr_to_dotted(mask));
    113 
    114 	return buf;
    115 }
    116 
    117 static void print_mac(const unsigned char *mac, int l)
    118 {
    119 	int j;
    120 
    121 	for (j = 0; j < l; j++)
    122 		printf("%02x%s", mac[j],
    123 			(j==l-1) ? "" : ":");
    124 }
    125 
    126 static void print_mac_and_mask(const unsigned char *mac, const unsigned char *mask, int l)
    127 {
    128 	int i;
    129 
    130 	print_mac(mac, l);
    131 	for (i = 0; i < l ; i++)
    132 		if (mask[i] != 255)
    133 			break;
    134 	if (i == l)
    135 		return;
    136 	printf("/");
    137 	print_mac(mask, l);
    138 }
    139 
    140 static int nft_arp_add(struct nftnl_rule *r, void *data)
    141 {
    142 	struct arptables_command_state *cs = data;
    143 	struct arpt_entry *fw = &cs->fw;
    144 	uint32_t op;
    145 	int ret = 0;
    146 
    147 	if (fw->arp.iniface[0] != '\0') {
    148 		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_VIA_IN);
    149 		add_iniface(r, fw->arp.iniface, op);
    150 	}
    151 
    152 	if (fw->arp.outiface[0] != '\0') {
    153 		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_VIA_OUT);
    154 		add_outiface(r, fw->arp.outiface, op);
    155 	}
    156 
    157 	if (fw->arp.arhrd != 0) {
    158 		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPHRD);
    159 		add_payload(r, offsetof(struct arphdr, ar_hrd), 2,
    160 			    NFT_PAYLOAD_NETWORK_HEADER);
    161 		add_cmp_u16(r, fw->arp.arhrd, op);
    162 	}
    163 
    164 	if (fw->arp.arpro != 0) {
    165 		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPPRO);
    166 	        add_payload(r, offsetof(struct arphdr, ar_pro), 2,
    167 			    NFT_PAYLOAD_NETWORK_HEADER);
    168 		add_cmp_u16(r, fw->arp.arpro, op);
    169 	}
    170 
    171 	if (fw->arp.arhln != 0) {
    172 		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPHLN);
    173 		add_proto(r, offsetof(struct arphdr, ar_hln), 1,
    174 			  fw->arp.arhln, op);
    175 	}
    176 
    177 	add_proto(r, offsetof(struct arphdr, ar_pln), 1, 4, NFT_CMP_EQ);
    178 
    179 	if (fw->arp.arpop != 0) {
    180 		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPOP);
    181 		add_payload(r, offsetof(struct arphdr, ar_op), 2,
    182 			    NFT_PAYLOAD_NETWORK_HEADER);
    183 		add_cmp_u16(r, fw->arp.arpop, op);
    184 	}
    185 
    186 	if (fw->arp.src_devaddr.addr[0] != '\0') {
    187 		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCDEVADDR);
    188 		add_payload(r, sizeof(struct arphdr), fw->arp.arhln,
    189 			    NFT_PAYLOAD_NETWORK_HEADER);
    190 		add_cmp_ptr(r, op, fw->arp.src_devaddr.addr, fw->arp.arhln);
    191 	}
    192 
    193 	if (fw->arp.src.s_addr != 0) {
    194 		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCIP);
    195 		add_addr(r, sizeof(struct arphdr) + fw->arp.arhln,
    196 			 &fw->arp.src.s_addr, &fw->arp.smsk.s_addr,
    197 			 sizeof(struct in_addr), op);
    198 	}
    199 
    200 	if (fw->arp.tgt_devaddr.addr[0] != '\0') {
    201 		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTDEVADDR);
    202 		add_payload(r, sizeof(struct arphdr) + fw->arp.arhln + 4,
    203 			    fw->arp.arhln, NFT_PAYLOAD_NETWORK_HEADER);
    204 		add_cmp_ptr(r, op, fw->arp.tgt_devaddr.addr, fw->arp.arhln);
    205 	}
    206 
    207 	if (fw->arp.tgt.s_addr != 0) {
    208 		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTIP);
    209 		add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr),
    210 			 &fw->arp.tgt.s_addr, &fw->arp.tmsk.s_addr,
    211 			 sizeof(struct in_addr), op);
    212 	}
    213 
    214 	/* Counters need to me added before the target, otherwise they are
    215 	 * increased for each rule because of the way nf_tables works.
    216 	 */
    217 	if (add_counters(r, fw->counters.pcnt, fw->counters.bcnt) < 0)
    218 		return -1;
    219 
    220 	if (cs->target != NULL) {
    221 		/* Standard target? */
    222 		if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
    223 			ret = add_verdict(r, NF_ACCEPT);
    224 		else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
    225 			ret = add_verdict(r, NF_DROP);
    226 		else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
    227 			ret = add_verdict(r, NFT_RETURN);
    228 		else
    229 			ret = add_target(r, cs->target->t);
    230 	} else if (strlen(cs->jumpto) > 0) {
    231 		/* No goto in arptables */
    232 		ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
    233 	}
    234 
    235 	return ret;
    236 }
    237 
    238 static uint16_t ipt_to_arpt_flags(uint8_t invflags)
    239 {
    240 	uint16_t result = 0;
    241 
    242 	if (invflags & IPT_INV_VIA_IN)
    243 		result |= ARPT_INV_VIA_IN;
    244 
    245 	if (invflags & IPT_INV_VIA_OUT)
    246 		result |= ARPT_INV_VIA_OUT;
    247 
    248 	if (invflags & IPT_INV_SRCIP)
    249 		result |= ARPT_INV_SRCIP;
    250 
    251 	if (invflags & IPT_INV_DSTIP)
    252 		result |= ARPT_INV_TGTIP;
    253 
    254 	if (invflags & IPT_INV_PROTO)
    255 		result |= ARPT_INV_ARPPRO;
    256 
    257 	return result;
    258 }
    259 
    260 static void nft_arp_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
    261 			       void *data)
    262 {
    263 	struct arptables_command_state *cs = data;
    264 	struct arpt_entry *fw = &cs->fw;
    265 	uint8_t flags = 0;
    266 
    267 	parse_meta(e, ctx->meta.key, fw->arp.iniface, fw->arp.iniface_mask,
    268 		   fw->arp.outiface, fw->arp.outiface_mask,
    269 		   &flags);
    270 
    271 	fw->arp.invflags |= ipt_to_arpt_flags(flags);
    272 }
    273 
    274 static void nft_arp_parse_target(struct xtables_target *target, void *data)
    275 {
    276 	struct arptables_command_state *cs = data;
    277 
    278 	cs->target = target;
    279 }
    280 
    281 static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto,
    282 				    void *data)
    283 {
    284 	struct arptables_command_state *cs = data;
    285 
    286 	cs->jumpto = jumpto;
    287 }
    288 
    289 static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask)
    290 {
    291 	mask->s_addr = ctx->bitwise.mask[0];
    292 }
    293 
    294 static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
    295 				  struct nftnl_expr *e, void *data)
    296 {
    297 	struct arptables_command_state *cs = data;
    298 	struct arpt_entry *fw = &cs->fw;
    299 	struct in_addr addr;
    300 	unsigned short int ar_hrd, ar_pro, ar_op, ar_hln;
    301 	bool inv;
    302 
    303 	switch (ctx->payload.offset) {
    304 	case offsetof(struct arphdr, ar_hrd):
    305 		get_cmp_data(e, &ar_hrd, sizeof(ar_hrd), &inv);
    306 		fw->arp.arhrd = ar_hrd;
    307 		fw->arp.arhrd_mask = 0xffff;
    308 		if (inv)
    309 			fw->arp.invflags |= ARPT_INV_ARPHRD;
    310 		break;
    311 	case offsetof(struct arphdr, ar_pro):
    312 		get_cmp_data(e, &ar_pro, sizeof(ar_pro), &inv);
    313 		fw->arp.arpro = ar_pro;
    314 		fw->arp.arpro_mask = 0xffff;
    315 		if (inv)
    316 			fw->arp.invflags |= ARPT_INV_ARPPRO;
    317 		break;
    318 	case offsetof(struct arphdr, ar_op):
    319 		get_cmp_data(e, &ar_op, sizeof(ar_op), &inv);
    320 		fw->arp.arpop = ar_op;
    321 		fw->arp.arpop_mask = 0xffff;
    322 		if (inv)
    323 			fw->arp.invflags |= ARPT_INV_ARPOP;
    324 		break;
    325 	case offsetof(struct arphdr, ar_hln):
    326 		get_cmp_data(e, &ar_hln, sizeof(ar_op), &inv);
    327 		fw->arp.arhln = ar_hln;
    328 		fw->arp.arhln_mask = 0xff;
    329 		if (inv)
    330 			fw->arp.invflags |= ARPT_INV_ARPOP;
    331 		break;
    332 	default:
    333 		if (fw->arp.arhln < 0)
    334 			break;
    335 
    336 		if (ctx->payload.offset == sizeof(struct arphdr) +
    337 					   fw->arp.arhln) {
    338 			get_cmp_data(e, &addr, sizeof(addr), &inv);
    339 			fw->arp.src.s_addr = addr.s_addr;
    340 			if (ctx->flags & NFT_XT_CTX_BITWISE) {
    341 				parse_mask_ipv4(ctx, &fw->arp.smsk);
    342 				ctx->flags &= ~NFT_XT_CTX_BITWISE;
    343 			} else {
    344 				fw->arp.smsk.s_addr = 0xffffffff;
    345 			}
    346 
    347 			if (inv)
    348 				fw->arp.invflags |= ARPT_INV_SRCIP;
    349 		} else if (ctx->payload.offset == sizeof(struct arphdr) +
    350 						  fw->arp.arhln +
    351 						  sizeof(struct in_addr)) {
    352 			get_cmp_data(e, &addr, sizeof(addr), &inv);
    353 			fw->arp.tgt.s_addr = addr.s_addr;
    354 			if (ctx->flags & NFT_XT_CTX_BITWISE) {
    355 				parse_mask_ipv4(ctx, &fw->arp.tmsk);
    356 				ctx->flags &= ~NFT_XT_CTX_BITWISE;
    357 			} else {
    358 				fw->arp.tmsk.s_addr = 0xffffffff;
    359 			}
    360 
    361 			if (inv)
    362 				fw->arp.invflags |= ARPT_INV_TGTIP;
    363 		}
    364 		break;
    365 	}
    366 }
    367 
    368 void nft_rule_to_arptables_command_state(struct nftnl_rule *r,
    369 					 struct arptables_command_state *cs)
    370 {
    371 	struct nftnl_expr_iter *iter;
    372 	struct nftnl_expr *expr;
    373 	int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
    374 	struct nft_xt_ctx ctx = {
    375 		.state.cs_arp = cs,
    376 		.family = family,
    377 	};
    378 
    379 	iter = nftnl_expr_iter_create(r);
    380 	if (iter == NULL)
    381 		return;
    382 
    383 	ctx.iter = iter;
    384 	expr = nftnl_expr_iter_next(iter);
    385 	while (expr != NULL) {
    386 		const char *name =
    387 			nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
    388 
    389 		if (strcmp(name, "counter") == 0)
    390 			nft_parse_counter(expr, &ctx.state.cs_arp->fw.counters);
    391 		else if (strcmp(name, "payload") == 0)
    392 			nft_parse_payload(&ctx, expr);
    393 		else if (strcmp(name, "meta") == 0)
    394 			nft_parse_meta(&ctx, expr);
    395 		else if (strcmp(name, "bitwise") == 0)
    396 			nft_parse_bitwise(&ctx, expr);
    397 		else if (strcmp(name, "cmp") == 0)
    398 			nft_parse_cmp(&ctx, expr);
    399 		else if (strcmp(name, "immediate") == 0)
    400 			nft_parse_immediate(&ctx, expr);
    401 		else if (strcmp(name, "target") == 0)
    402 			nft_parse_target(&ctx, expr);
    403 
    404 		expr = nftnl_expr_iter_next(iter);
    405 	}
    406 
    407 	nftnl_expr_iter_destroy(iter);
    408 
    409 	if (cs->jumpto != NULL)
    410 		return;
    411 
    412 	if (cs->target != NULL && cs->target->name != NULL)
    413 		cs->target = xtables_find_target(cs->target->name, XTF_TRY_LOAD);
    414 	else
    415 		cs->jumpto = "";
    416 }
    417 
    418 static void nft_arp_print_header(unsigned int format, const char *chain,
    419 				 const char *pol,
    420 				 const struct xt_counters *counters,
    421 				 bool basechain, uint32_t refs)
    422 {
    423 	printf("Chain %s", chain);
    424 	if (pol) {
    425 		printf(" (policy %s", pol);
    426 		if (!(format & FMT_NOCOUNTS)) {
    427 			fputc(' ', stdout);
    428 			xtables_print_num(counters->pcnt, (format|FMT_NOTABLE));
    429 			fputs("packets, ", stdout);
    430 			xtables_print_num(counters->bcnt, (format|FMT_NOTABLE));
    431 			fputs("bytes", stdout);
    432 		}
    433 		printf(")\n");
    434 	} else {
    435 		printf(" (%u references)\n", refs);
    436 	}
    437 }
    438 
    439 static void print_fw_details(struct arpt_entry *fw, unsigned int format)
    440 {
    441 	char buf[BUFSIZ];
    442 	char iface[IFNAMSIZ+2];
    443 	int print_iface = 0;
    444 	int i;
    445 
    446 	iface[0] = '\0';
    447 
    448 	if (fw->arp.iniface[0] != '\0') {
    449 		strcat(iface, fw->arp.iniface);
    450 		print_iface = 1;
    451 	}
    452 	else if (format & FMT_VIA) {
    453 		print_iface = 1;
    454 		if (format & FMT_NUMERIC) strcat(iface, "*");
    455 		else strcat(iface, "any");
    456 	}
    457 	if (print_iface)
    458 		printf("%s-i %s ", fw->arp.invflags & ARPT_INV_VIA_IN ?
    459 				   "! " : "", iface);
    460 
    461 	print_iface = 0;
    462 	iface[0] = '\0';
    463 
    464 	if (fw->arp.outiface[0] != '\0') {
    465 		strcat(iface, fw->arp.outiface);
    466 		print_iface = 1;
    467 	}
    468 	else if (format & FMT_VIA) {
    469 		print_iface = 1;
    470 		if (format & FMT_NUMERIC) strcat(iface, "*");
    471 		else strcat(iface, "any");
    472 	}
    473 	if (print_iface)
    474 		printf("%s-o %s ", fw->arp.invflags & ARPT_INV_VIA_OUT ?
    475 				   "! " : "", iface);
    476 
    477 	if (fw->arp.smsk.s_addr != 0L) {
    478 		printf("%s", fw->arp.invflags & ARPT_INV_SRCIP
    479 			? "! " : "");
    480 		if (format & FMT_NUMERIC)
    481 			sprintf(buf, "%s", addr_to_dotted(&(fw->arp.src)));
    482 		else
    483 			sprintf(buf, "%s", addr_to_anyname(&(fw->arp.src)));
    484 		strncat(buf, mask_to_dotted(&(fw->arp.smsk)),
    485 			sizeof(buf) - strlen(buf) - 1);
    486 		printf("-s %s ", buf);
    487 	}
    488 
    489 	for (i = 0; i < ARPT_DEV_ADDR_LEN_MAX; i++)
    490 		if (fw->arp.src_devaddr.mask[i] != 0)
    491 			break;
    492 	if (i == ARPT_DEV_ADDR_LEN_MAX)
    493 		goto after_devsrc;
    494 	printf("%s", fw->arp.invflags & ARPT_INV_SRCDEVADDR
    495 		? "! " : "");
    496 	printf("--src-mac ");
    497 	print_mac_and_mask((unsigned char *)fw->arp.src_devaddr.addr,
    498 		(unsigned char *)fw->arp.src_devaddr.mask, ETH_ALEN);
    499 	printf(" ");
    500 after_devsrc:
    501 
    502 	if (fw->arp.tmsk.s_addr != 0L) {
    503 		printf("%s", fw->arp.invflags & ARPT_INV_TGTIP
    504 			? "! " : "");
    505 		if (format & FMT_NUMERIC)
    506 			sprintf(buf, "%s", addr_to_dotted(&(fw->arp.tgt)));
    507 		else
    508 			sprintf(buf, "%s", addr_to_anyname(&(fw->arp.tgt)));
    509 		strncat(buf, mask_to_dotted(&(fw->arp.tmsk)),
    510 			sizeof(buf) - strlen(buf) - 1);
    511 		printf("-d %s ", buf);
    512 	}
    513 
    514 	for (i = 0; i <ARPT_DEV_ADDR_LEN_MAX; i++)
    515 		if (fw->arp.tgt_devaddr.mask[i] != 0)
    516 			break;
    517 	if (i == ARPT_DEV_ADDR_LEN_MAX)
    518 		goto after_devdst;
    519 	printf("%s", fw->arp.invflags & ARPT_INV_TGTDEVADDR
    520 		? "! " : "");
    521 	printf("--dst-mac ");
    522 	print_mac_and_mask((unsigned char *)fw->arp.tgt_devaddr.addr,
    523 		(unsigned char *)fw->arp.tgt_devaddr.mask, ETH_ALEN);
    524 	printf(" ");
    525 
    526 after_devdst:
    527 
    528 	if (fw->arp.arhln_mask != 0) {
    529 		printf("%s", fw->arp.invflags & ARPT_INV_ARPHLN
    530 			? "! " : "");
    531 		printf("--h-length %d", fw->arp.arhln);
    532 		if (fw->arp.arhln_mask != 255)
    533 			printf("/%d", fw->arp.arhln_mask);
    534 		printf(" ");
    535 	}
    536 
    537 	if (fw->arp.arpop_mask != 0) {
    538 		int tmp = ntohs(fw->arp.arpop);
    539 
    540 		printf("%s", fw->arp.invflags & ARPT_INV_ARPOP
    541 			? "! " : "");
    542 		if (tmp <= NUMOPCODES && !(format & FMT_NUMERIC))
    543 			printf("--opcode %s", opcodes[tmp-1]);
    544 		else
    545 
    546 		if (fw->arp.arpop_mask != 65535)
    547 			printf("/%d", ntohs(fw->arp.arpop_mask));
    548 		printf(" ");
    549 	}
    550 
    551 	if (fw->arp.arhrd_mask != 0) {
    552 		uint16_t tmp = ntohs(fw->arp.arhrd);
    553 
    554 		printf("%s", fw->arp.invflags & ARPT_INV_ARPHRD
    555 			? "! " : "");
    556 		if (tmp == 1 && !(format & FMT_NUMERIC))
    557 			printf("--h-type %s", "Ethernet");
    558 		else
    559 			printf("--h-type %u", tmp);
    560 		if (fw->arp.arhrd_mask != 65535)
    561 			printf("/%d", ntohs(fw->arp.arhrd_mask));
    562 		printf(" ");
    563 	}
    564 
    565 	if (fw->arp.arpro_mask != 0) {
    566 		int tmp = ntohs(fw->arp.arpro);
    567 
    568 		printf("%s", fw->arp.invflags & ARPT_INV_ARPPRO
    569 			? "! " : "");
    570 		if (tmp == 0x0800 && !(format & FMT_NUMERIC))
    571 			printf("--proto-type %s", "IPv4");
    572 		else
    573 			printf("--proto-type 0x%x", tmp);
    574 		if (fw->arp.arpro_mask != 65535)
    575 			printf("/%x", ntohs(fw->arp.arpro_mask));
    576 		printf(" ");
    577 	}
    578 }
    579 
    580 static void
    581 nft_arp_print_firewall(struct nftnl_rule *r, unsigned int num,
    582 		       unsigned int format)
    583 {
    584 	struct arptables_command_state cs = {};
    585 
    586 	nft_rule_to_arptables_command_state(r, &cs);
    587 
    588 	if (format & FMT_LINENUMBERS)
    589 		printf("%u ", num);
    590 
    591 	print_fw_details(&cs.fw, format);
    592 
    593 	if (cs.jumpto != NULL && strcmp(cs.jumpto, "") != 0) {
    594 		printf("-j %s", cs.jumpto);
    595 	} else if (cs.target) {
    596 		printf("-j %s", cs.target->name);
    597 		cs.target->print(&cs.fw, cs.target->t, format & FMT_NUMERIC);
    598 	}
    599 
    600 	if (!(format & FMT_NOCOUNTS)) {
    601 		printf(", pcnt=");
    602 		xtables_print_num(cs.fw.counters.pcnt, format);
    603 		printf("-- bcnt=");
    604 		xtables_print_num(cs.fw.counters.bcnt, format);
    605 	}
    606 
    607 	if (!(format & FMT_NONEWLINE))
    608 		fputc('\n', stdout);
    609 }
    610 
    611 static bool nft_arp_is_same(const void *data_a,
    612 			    const void *data_b)
    613 {
    614 	const struct arpt_entry *a = data_a;
    615 	const struct arpt_entry *b = data_b;
    616 
    617 	if (a->arp.src.s_addr != b->arp.src.s_addr
    618 	    || a->arp.tgt.s_addr != b->arp.tgt.s_addr
    619 	    || a->arp.smsk.s_addr != b->arp.tmsk.s_addr
    620 	    || a->arp.arpro != b->arp.arpro
    621 	    || a->arp.flags != b->arp.flags
    622 	    || a->arp.invflags != b->arp.invflags) {
    623 		DEBUGP("different src/dst/proto/flags/invflags\n");
    624 		return false;
    625 	}
    626 
    627 	return is_same_interfaces(a->arp.iniface,
    628 				  a->arp.outiface,
    629 				  (unsigned char *)a->arp.iniface_mask,
    630 				  (unsigned char *)a->arp.outiface_mask,
    631 				  b->arp.iniface,
    632 				  b->arp.outiface,
    633 				  (unsigned char *)b->arp.iniface_mask,
    634 				  (unsigned char *)b->arp.outiface_mask);
    635 }
    636 
    637 static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
    638 			      void *data)
    639 {
    640 	const struct arptables_command_state *cs = data;
    641 	struct arptables_command_state this = {};
    642 
    643 	/* Delete by matching rule case */
    644 	nft_rule_to_arptables_command_state(r, &this);
    645 
    646 	if (!nft_arp_is_same(cs, &this))
    647 		return false;
    648 
    649 	if (!compare_targets(cs->target, this.target))
    650 		return false;
    651 
    652 	if (strcmp(cs->jumpto, this.jumpto) != 0)
    653 		return false;
    654 
    655 	return true;
    656 }
    657 
    658 struct nft_family_ops nft_family_ops_arp = {
    659 	.add			= nft_arp_add,
    660 	.is_same		= nft_arp_is_same,
    661 	.print_payload		= NULL,
    662 	.parse_meta		= nft_arp_parse_meta,
    663 	.parse_payload		= nft_arp_parse_payload,
    664 	.parse_immediate	= nft_arp_parse_immediate,
    665 	.print_header		= nft_arp_print_header,
    666 	.print_firewall		= nft_arp_print_firewall,
    667 	.save_firewall		= NULL,
    668 	.save_counters		= NULL,
    669 	.post_parse		= NULL,
    670 	.rule_find		= nft_arp_rule_find,
    671 	.parse_target		= nft_arp_parse_target,
    672 };
    673