nft-arp.c 16.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
 * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
 * (C) 2013 by Giuseppe Longo <giuseppelng@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <net/if_arp.h>

#include <xtables.h>
#include <libiptc/libxtc.h>
#include <net/if_arp.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-arp.h"
#include "nft.h"

/* a few names */
32
char *arp_opcodes[] =
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
{
	"Request",
	"Reply",
	"Request_Reverse",
	"Reply_Reverse",
	"DRARP_Request",
	"DRARP_Reply",
	"DRARP_Error",
	"InARP_Request",
	"ARP_NAK",
};

static char *
addr_to_dotted(const struct in_addr *addrp)
{
	static char buf[20];
	const unsigned char *bytep;

	bytep = (const unsigned char *) &(addrp->s_addr);
	sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
	return buf;
}

static char *
addr_to_host(const struct in_addr *addr)
{
	struct hostent *host;

	if ((host = gethostbyaddr((char *) addr,
					sizeof(struct in_addr), AF_INET)) != NULL)
		return (char *) host->h_name;

	return (char *) NULL;
}

static char *
addr_to_network(const struct in_addr *addr)
{
	struct netent *net;

	if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
		return (char *) net->n_name;

	return (char *) NULL;
}

static char *
addr_to_anyname(const struct in_addr *addr)
{
	char *name;

	if ((name = addr_to_host(addr)) != NULL ||
		(name = addr_to_network(addr)) != NULL)
		return name;

	return addr_to_dotted(addr);
}

static char *
mask_to_dotted(const struct in_addr *mask)
{
	int i;
95
	static char buf[22];
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
	u_int32_t maskaddr, bits;

	maskaddr = ntohl(mask->s_addr);

	if (maskaddr == 0xFFFFFFFFL)
		/* we don't want to see "/32" */
		return "";

	i = 32;
	bits = 0xFFFFFFFEL;
	while (--i >= 0 && maskaddr != bits)
		bits <<= 1;
	if (i >= 0)
		sprintf(buf, "/%d", i);
	else
		/* mask was not a decent combination of 1's and 0's */
112
		snprintf(buf, sizeof(buf), "/%s", addr_to_dotted(mask));
113
114
115
116

	return buf;
}

117
118
119
120
121
122
123
124
125
126
127
128
static bool need_devaddr(struct arpt_devaddr_info *info)
{
	int i;

	for (i = 0; i < ETH_ALEN; i++) {
		if (info->addr[i] || info->mask[i])
			return true;
	}

	return false;
}

129
static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, void *data)
130
{
131
132
	struct iptables_command_state *cs = data;
	struct arpt_entry *fw = &cs->arp;
133
134
135
136
137
138
139
140
141
142
143
144
145
	uint32_t op;
	int ret = 0;

	if (fw->arp.iniface[0] != '\0') {
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_VIA_IN);
		add_iniface(r, fw->arp.iniface, op);
	}

	if (fw->arp.outiface[0] != '\0') {
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_VIA_OUT);
		add_outiface(r, fw->arp.outiface, op);
	}

146
147
	if (fw->arp.arhrd != 0 ||
	    fw->arp.invflags & ARPT_INV_ARPHRD) {
148
149
150
151
152
153
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPHRD);
		add_payload(r, offsetof(struct arphdr, ar_hrd), 2,
			    NFT_PAYLOAD_NETWORK_HEADER);
		add_cmp_u16(r, fw->arp.arhrd, op);
	}

154
155
	if (fw->arp.arpro != 0 ||
	    fw->arp.invflags & ARPT_INV_ARPPRO) {
156
157
158
159
160
161
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPPRO);
	        add_payload(r, offsetof(struct arphdr, ar_pro), 2,
			    NFT_PAYLOAD_NETWORK_HEADER);
		add_cmp_u16(r, fw->arp.arpro, op);
	}

162
163
	if (fw->arp.arhln != 0 ||
	    fw->arp.invflags & ARPT_INV_ARPHLN) {
164
165
166
167
168
169
170
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPHLN);
		add_proto(r, offsetof(struct arphdr, ar_hln), 1,
			  fw->arp.arhln, op);
	}

	add_proto(r, offsetof(struct arphdr, ar_pln), 1, 4, NFT_CMP_EQ);

171
172
	if (fw->arp.arpop != 0 ||
	    fw->arp.invflags & ARPT_INV_ARPOP) {
173
174
175
176
177
178
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPOP);
		add_payload(r, offsetof(struct arphdr, ar_op), 2,
			    NFT_PAYLOAD_NETWORK_HEADER);
		add_cmp_u16(r, fw->arp.arpop, op);
	}

179
	if (need_devaddr(&fw->arp.src_devaddr)) {
180
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCDEVADDR);
181
182
183
184
185
		add_addr(r, sizeof(struct arphdr),
			 &fw->arp.src_devaddr.addr,
			 &fw->arp.src_devaddr.mask,
			 fw->arp.arhln, op);

186
187
	}

188
189
190
	if (fw->arp.src.s_addr != 0 ||
	    fw->arp.smsk.s_addr != 0 ||
	    fw->arp.invflags & ARPT_INV_SRCIP) {
191
192
193
194
195
196
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCIP);
		add_addr(r, sizeof(struct arphdr) + fw->arp.arhln,
			 &fw->arp.src.s_addr, &fw->arp.smsk.s_addr,
			 sizeof(struct in_addr), op);
	}

197
198

	if (need_devaddr(&fw->arp.tgt_devaddr)) {
199
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTDEVADDR);
200
201
202
203
		add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr),
			 &fw->arp.tgt_devaddr.addr,
			 &fw->arp.tgt_devaddr.mask,
			 fw->arp.arhln, op);
204
205
	}

206
207
208
	if (fw->arp.tgt.s_addr != 0 ||
	    fw->arp.tmsk.s_addr != 0 ||
	    fw->arp.invflags & ARPT_INV_TGTIP) {
209
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTIP);
210
		add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr) + fw->arp.arhln,
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
			 &fw->arp.tgt.s_addr, &fw->arp.tmsk.s_addr,
			 sizeof(struct in_addr), op);
	}

	/* Counters need to me added before the target, otherwise they are
	 * increased for each rule because of the way nf_tables works.
	 */
	if (add_counters(r, fw->counters.pcnt, fw->counters.bcnt) < 0)
		return -1;

	if (cs->target != NULL) {
		/* Standard target? */
		if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
			ret = add_verdict(r, NF_ACCEPT);
		else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
			ret = add_verdict(r, NF_DROP);
		else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
			ret = add_verdict(r, NFT_RETURN);
		else
			ret = add_target(r, cs->target->t);
	} else if (strlen(cs->jumpto) > 0) {
		/* No goto in arptables */
		ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
	}

	return ret;
}

static uint16_t ipt_to_arpt_flags(uint8_t invflags)
{
	uint16_t result = 0;

	if (invflags & IPT_INV_VIA_IN)
		result |= ARPT_INV_VIA_IN;

	if (invflags & IPT_INV_VIA_OUT)
		result |= ARPT_INV_VIA_OUT;

	if (invflags & IPT_INV_SRCIP)
		result |= ARPT_INV_SRCIP;

	if (invflags & IPT_INV_DSTIP)
		result |= ARPT_INV_TGTIP;

	if (invflags & IPT_INV_PROTO)
		result |= ARPT_INV_ARPPRO;

	return result;
}

static void nft_arp_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
			       void *data)
{
264
265
	struct iptables_command_state *cs = data;
	struct arpt_entry *fw = &cs->arp;
266
267
268
269
270
271
272
273
274
275
276
277
	uint8_t flags = 0;

	parse_meta(e, ctx->meta.key, fw->arp.iniface, fw->arp.iniface_mask,
		   fw->arp.outiface, fw->arp.outiface_mask,
		   &flags);

	fw->arp.invflags |= ipt_to_arpt_flags(flags);
}

static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto,
				    void *data)
{
278
	struct iptables_command_state *cs = data;
279
280
281
282
283
284
285
286
287

	cs->jumpto = jumpto;
}

static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask)
{
	mask->s_addr = ctx->bitwise.mask[0];
}

288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
static bool nft_arp_parse_devaddr(struct nft_xt_ctx *ctx,
				  struct nftnl_expr *e,
				  struct arpt_devaddr_info *info)
{
	uint32_t hlen;
	bool inv;

	nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &hlen);

	if (hlen != ETH_ALEN)
		return false;

	get_cmp_data(e, info->addr, ETH_ALEN, &inv);

	if (ctx->flags & NFT_XT_CTX_BITWISE) {
		memcpy(info->mask, ctx->bitwise.mask, ETH_ALEN);
		ctx->flags &= ~NFT_XT_CTX_BITWISE;
	} else {
		memset(info->mask, 0xff, ETH_ALEN);
	}

	return inv;
}

312
313
314
static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
				  struct nftnl_expr *e, void *data)
{
315
316
	struct iptables_command_state *cs = data;
	struct arpt_entry *fw = &cs->arp;
317
	struct in_addr addr;
318
319
	uint16_t ar_hrd, ar_pro, ar_op;
	uint8_t ar_hln;
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
	bool inv;

	switch (ctx->payload.offset) {
	case offsetof(struct arphdr, ar_hrd):
		get_cmp_data(e, &ar_hrd, sizeof(ar_hrd), &inv);
		fw->arp.arhrd = ar_hrd;
		fw->arp.arhrd_mask = 0xffff;
		if (inv)
			fw->arp.invflags |= ARPT_INV_ARPHRD;
		break;
	case offsetof(struct arphdr, ar_pro):
		get_cmp_data(e, &ar_pro, sizeof(ar_pro), &inv);
		fw->arp.arpro = ar_pro;
		fw->arp.arpro_mask = 0xffff;
		if (inv)
			fw->arp.invflags |= ARPT_INV_ARPPRO;
		break;
	case offsetof(struct arphdr, ar_op):
		get_cmp_data(e, &ar_op, sizeof(ar_op), &inv);
		fw->arp.arpop = ar_op;
		fw->arp.arpop_mask = 0xffff;
		if (inv)
			fw->arp.invflags |= ARPT_INV_ARPOP;
		break;
	case offsetof(struct arphdr, ar_hln):
345
		get_cmp_data(e, &ar_hln, sizeof(ar_hln), &inv);
346
347
348
349
350
351
		fw->arp.arhln = ar_hln;
		fw->arp.arhln_mask = 0xff;
		if (inv)
			fw->arp.invflags |= ARPT_INV_ARPOP;
		break;
	default:
352
353
354
355
		if (ctx->payload.offset == sizeof(struct arphdr)) {
			if (nft_arp_parse_devaddr(ctx, e, &fw->arp.src_devaddr))
				fw->arp.invflags |= ARPT_INV_SRCDEVADDR;
		} else if (ctx->payload.offset == sizeof(struct arphdr) +
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
					   fw->arp.arhln) {
			get_cmp_data(e, &addr, sizeof(addr), &inv);
			fw->arp.src.s_addr = addr.s_addr;
			if (ctx->flags & NFT_XT_CTX_BITWISE) {
				parse_mask_ipv4(ctx, &fw->arp.smsk);
				ctx->flags &= ~NFT_XT_CTX_BITWISE;
			} else {
				fw->arp.smsk.s_addr = 0xffffffff;
			}

			if (inv)
				fw->arp.invflags |= ARPT_INV_SRCIP;
		} else if (ctx->payload.offset == sizeof(struct arphdr) +
						  fw->arp.arhln +
						  sizeof(struct in_addr)) {
371
372
373
374
375
376
			if (nft_arp_parse_devaddr(ctx, e, &fw->arp.tgt_devaddr))
				fw->arp.invflags |= ARPT_INV_TGTDEVADDR;
		} else if (ctx->payload.offset == sizeof(struct arphdr) +
						  fw->arp.arhln +
						  sizeof(struct in_addr) +
						  fw->arp.arhln) {
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
			get_cmp_data(e, &addr, sizeof(addr), &inv);
			fw->arp.tgt.s_addr = addr.s_addr;
			if (ctx->flags & NFT_XT_CTX_BITWISE) {
				parse_mask_ipv4(ctx, &fw->arp.tmsk);
				ctx->flags &= ~NFT_XT_CTX_BITWISE;
			} else {
				fw->arp.tmsk.s_addr = 0xffffffff;
			}

			if (inv)
				fw->arp.invflags |= ARPT_INV_TGTIP;
		}
		break;
	}
}

static void nft_arp_print_header(unsigned int format, const char *chain,
				 const char *pol,
				 const struct xt_counters *counters,
396
397
				 bool basechain, uint32_t refs,
				 uint32_t entries)
398
399
{
	printf("Chain %s", chain);
400
	if (basechain && pol) {
401
402
403
404
405
406
407
408
409
410
411
412
413
414
		printf(" (policy %s", pol);
		if (!(format & FMT_NOCOUNTS)) {
			fputc(' ', stdout);
			xtables_print_num(counters->pcnt, (format|FMT_NOTABLE));
			fputs("packets, ", stdout);
			xtables_print_num(counters->bcnt, (format|FMT_NOTABLE));
			fputs("bytes", stdout);
		}
		printf(")\n");
	} else {
		printf(" (%u references)\n", refs);
	}
}

415
static void nft_arp_print_rule_details(const struct iptables_command_state *cs,
416
				       unsigned int format)
417
{
418
	const struct arpt_entry *fw = &cs->arp;
419
420
	char buf[BUFSIZ];
	char iface[IFNAMSIZ+2];
421
	const char *sep = "";
422
423
424
	int print_iface = 0;
	int i;

425
426
427
428
429
	if (strlen(cs->jumpto)) {
		printf("%s-j %s", sep, cs->jumpto);
		sep = " ";
	}

430
431
432
433
434
435
436
437
438
439
440
	iface[0] = '\0';

	if (fw->arp.iniface[0] != '\0') {
		strcat(iface, fw->arp.iniface);
		print_iface = 1;
	}
	else if (format & FMT_VIA) {
		print_iface = 1;
		if (format & FMT_NUMERIC) strcat(iface, "*");
		else strcat(iface, "any");
	}
441
442
	if (print_iface) {
		printf("%s%s-i %s", sep, fw->arp.invflags & ARPT_INV_VIA_IN ?
443
				   "! " : "", iface);
444
445
		sep = " ";
	}
446
447
448
449
450
451
452
453
454
455
456
457
458

	print_iface = 0;
	iface[0] = '\0';

	if (fw->arp.outiface[0] != '\0') {
		strcat(iface, fw->arp.outiface);
		print_iface = 1;
	}
	else if (format & FMT_VIA) {
		print_iface = 1;
		if (format & FMT_NUMERIC) strcat(iface, "*");
		else strcat(iface, "any");
	}
459
460
	if (print_iface) {
		printf("%s%s-o %s", sep, fw->arp.invflags & ARPT_INV_VIA_OUT ?
461
				   "! " : "", iface);
462
463
		sep = " ";
	}
464
465

	if (fw->arp.smsk.s_addr != 0L) {
466
		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_SRCIP
467
468
469
470
471
472
473
			? "! " : "");
		if (format & FMT_NUMERIC)
			sprintf(buf, "%s", addr_to_dotted(&(fw->arp.src)));
		else
			sprintf(buf, "%s", addr_to_anyname(&(fw->arp.src)));
		strncat(buf, mask_to_dotted(&(fw->arp.smsk)),
			sizeof(buf) - strlen(buf) - 1);
474
475
		printf("-s %s", buf);
		sep = " ";
476
477
478
479
480
481
482
	}

	for (i = 0; i < ARPT_DEV_ADDR_LEN_MAX; i++)
		if (fw->arp.src_devaddr.mask[i] != 0)
			break;
	if (i == ARPT_DEV_ADDR_LEN_MAX)
		goto after_devsrc;
483
	printf("%s%s", sep, fw->arp.invflags & ARPT_INV_SRCDEVADDR
484
485
		? "! " : "");
	printf("--src-mac ");
486
487
	xtables_print_mac_and_mask((unsigned char *)fw->arp.src_devaddr.addr,
				   (unsigned char *)fw->arp.src_devaddr.mask);
488
	sep = " ";
489
490
491
after_devsrc:

	if (fw->arp.tmsk.s_addr != 0L) {
492
		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_TGTIP
493
494
495
496
497
498
499
			? "! " : "");
		if (format & FMT_NUMERIC)
			sprintf(buf, "%s", addr_to_dotted(&(fw->arp.tgt)));
		else
			sprintf(buf, "%s", addr_to_anyname(&(fw->arp.tgt)));
		strncat(buf, mask_to_dotted(&(fw->arp.tmsk)),
			sizeof(buf) - strlen(buf) - 1);
500
501
		printf("-d %s", buf);
		sep = " ";
502
503
504
505
506
507
508
	}

	for (i = 0; i <ARPT_DEV_ADDR_LEN_MAX; i++)
		if (fw->arp.tgt_devaddr.mask[i] != 0)
			break;
	if (i == ARPT_DEV_ADDR_LEN_MAX)
		goto after_devdst;
509
	printf("%s%s", sep, fw->arp.invflags & ARPT_INV_TGTDEVADDR
510
511
		? "! " : "");
	printf("--dst-mac ");
512
513
	xtables_print_mac_and_mask((unsigned char *)fw->arp.tgt_devaddr.addr,
				   (unsigned char *)fw->arp.tgt_devaddr.mask);
514
	sep = " ";
515
516
517

after_devdst:

518
519
	if (fw->arp.arhln_mask != 255 || fw->arp.arhln != 6) {
		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPHLN
520
521
522
523
			? "! " : "");
		printf("--h-length %d", fw->arp.arhln);
		if (fw->arp.arhln_mask != 255)
			printf("/%d", fw->arp.arhln_mask);
524
		sep = " ";
525
526
527
528
529
	}

	if (fw->arp.arpop_mask != 0) {
		int tmp = ntohs(fw->arp.arpop);

530
		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPOP
531
532
			? "! " : "");
		if (tmp <= NUMOPCODES && !(format & FMT_NUMERIC))
533
			printf("--opcode %s", arp_opcodes[tmp-1]);
534
		else
535
			printf("--opcode %d", tmp);
536
537
538

		if (fw->arp.arpop_mask != 65535)
			printf("/%d", ntohs(fw->arp.arpop_mask));
539
		sep = " ";
540
541
	}

542
	if (fw->arp.arhrd_mask != 65535 || fw->arp.arhrd != htons(1)) {
543
544
		uint16_t tmp = ntohs(fw->arp.arhrd);

545
		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPHRD
546
547
548
549
550
551
552
			? "! " : "");
		if (tmp == 1 && !(format & FMT_NUMERIC))
			printf("--h-type %s", "Ethernet");
		else
			printf("--h-type %u", tmp);
		if (fw->arp.arhrd_mask != 65535)
			printf("/%d", ntohs(fw->arp.arhrd_mask));
553
		sep = " ";
554
555
556
557
558
	}

	if (fw->arp.arpro_mask != 0) {
		int tmp = ntohs(fw->arp.arpro);

559
		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPPRO
560
561
562
563
564
565
566
			? "! " : "");
		if (tmp == 0x0800 && !(format & FMT_NUMERIC))
			printf("--proto-type %s", "IPv4");
		else
			printf("--proto-type 0x%x", tmp);
		if (fw->arp.arpro_mask != 65535)
			printf("/%x", ntohs(fw->arp.arpro_mask));
567
		sep = " ";
568
569
570
571
	}
}

static void
572
nft_arp_save_rule(const void *data, unsigned int format)
573
{
574
	const struct iptables_command_state *cs = data;
575

576
577
	format |= FMT_NUMERIC;

578
579
580
581
	nft_arp_print_rule_details(cs, format);
	if (cs->target && cs->target->save)
		cs->target->save(&cs->fw, cs->target->t);
	printf("\n");
582
583
}

584
static void
585
586
nft_arp_print_rule(struct nft_handle *h, struct nftnl_rule *r,
		   unsigned int num, unsigned int format)
587
588
589
590
591
592
{
	struct iptables_command_state cs = {};

	if (format & FMT_LINENUMBERS)
		printf("%u ", num);

593
	nft_rule_to_iptables_command_state(h, r, &cs);
594

595
596
	nft_arp_print_rule_details(&cs, format);
	print_matches_and_target(&cs, format);
597
598

	if (!(format & FMT_NOCOUNTS)) {
599
600
		printf(" , pcnt=");
		xtables_print_num(cs.counters.pcnt, format | FMT_NOTABLE);
601
		printf("-- bcnt=");
602
		xtables_print_num(cs.counters.bcnt, format | FMT_NOTABLE);
603
604
605
606
	}

	if (!(format & FMT_NONEWLINE))
		fputc('\n', stdout);
607
608

	nft_clear_iptables_command_state(&cs);
609
610
}

611
612
613
614
615
616
617
618
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
619
620
	    || a->arp.smsk.s_addr != b->arp.smsk.s_addr
	    || a->arp.tmsk.s_addr != b->arp.tmsk.s_addr
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
	    || 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.iniface,
				  a->arp.outiface,
				  (unsigned char *)a->arp.iniface_mask,
				  (unsigned char *)a->arp.outiface_mask,
				  b->arp.iniface,
				  b->arp.outiface,
				  (unsigned char *)b->arp.iniface_mask,
				  (unsigned char *)b->arp.outiface_mask);
}

638
639
640
641
642
643
644
static void nft_arp_save_chain(const struct nftnl_chain *c, const char *policy)
{
	const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);

	printf(":%s %s\n", chain, policy ?: "-");
}

645
646
647
648
649
650
651
652
struct nft_family_ops nft_family_ops_arp = {
	.add			= nft_arp_add,
	.is_same		= nft_arp_is_same,
	.print_payload		= NULL,
	.parse_meta		= nft_arp_parse_meta,
	.parse_payload		= nft_arp_parse_payload,
	.parse_immediate	= nft_arp_parse_immediate,
	.print_header		= nft_arp_print_header,
653
654
655
	.print_rule		= nft_arp_print_rule,
	.save_rule		= nft_arp_save_rule,
	.save_chain		= nft_arp_save_chain,
656
	.post_parse		= NULL,
657
	.rule_to_cs		= nft_rule_to_iptables_command_state,
658
659
	.clear_cs		= nft_clear_iptables_command_state,
	.parse_target		= nft_ipv46_parse_target,
660
};