nft-arp.c 16.4 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
	uint32_t op;
	int ret = 0;

	if (fw->arp.iniface[0] != '\0') {
137
		op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_VIA_IN);
138
139
140
141
		add_iniface(r, fw->arp.iniface, op);
	}

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

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

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

162
	if (fw->arp.arhln != 0 ||
163
164
	    fw->arp.invflags & IPT_INV_ARPHLN) {
		op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_ARPHLN);
165
166
167
168
169
170
		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
	if (fw->arp.arpop != 0 ||
172
173
	    fw->arp.invflags & IPT_INV_ARPOP) {
		op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_ARPOP);
174
175
176
177
178
		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
181
182
		op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_SRCDEVADDR);
		add_addr(r, NFT_PAYLOAD_NETWORK_HEADER,
			 sizeof(struct arphdr),
183
184
185
186
			 &fw->arp.src_devaddr.addr,
			 &fw->arp.src_devaddr.mask,
			 fw->arp.arhln, op);

187
188
	}

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

199
200

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

209
210
	if (fw->arp.tgt.s_addr != 0 ||
	    fw->arp.tmsk.s_addr != 0 ||
211
212
213
214
	    fw->arp.invflags & IPT_INV_DSTIP) {
		op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_DSTIP);
		add_addr(r, NFT_PAYLOAD_NETWORK_HEADER,
			 sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr) + fw->arp.arhln,
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
			 &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 void nft_arp_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
			       void *data)
{
246
247
	struct iptables_command_state *cs = data;
	struct arpt_entry *fw = &cs->arp;
248
249
250
251
252
253
	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);

254
	fw->arp.invflags |= flags;
255
256
257
258
259
}

static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto,
				    void *data)
{
260
	struct iptables_command_state *cs = data;
261
262
263
264
265
266
267
268
269

	cs->jumpto = jumpto;
}

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

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
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 {
288
289
		memset(info->mask, 0xff,
		       min(ctx->payload.len, ETH_ALEN));
290
291
292
293
294
	}

	return inv;
}

295
296
297
static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
				  struct nftnl_expr *e, void *data)
{
298
299
	struct iptables_command_state *cs = data;
	struct arpt_entry *fw = &cs->arp;
300
	struct in_addr addr;
301
302
	uint16_t ar_hrd, ar_pro, ar_op;
	uint8_t ar_hln;
303
304
305
306
307
308
309
310
	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)
311
			fw->arp.invflags |= IPT_INV_ARPHRD;
312
313
314
315
316
317
		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)
318
			fw->arp.invflags |= IPT_INV_PROTO;
319
320
321
322
323
324
		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)
325
			fw->arp.invflags |= IPT_INV_ARPOP;
326
327
		break;
	case offsetof(struct arphdr, ar_hln):
328
		get_cmp_data(e, &ar_hln, sizeof(ar_hln), &inv);
329
330
331
		fw->arp.arhln = ar_hln;
		fw->arp.arhln_mask = 0xff;
		if (inv)
332
			fw->arp.invflags |= IPT_INV_ARPOP;
333
334
		break;
	default:
335
336
		if (ctx->payload.offset == sizeof(struct arphdr)) {
			if (nft_arp_parse_devaddr(ctx, e, &fw->arp.src_devaddr))
337
				fw->arp.invflags |= IPT_INV_SRCDEVADDR;
338
		} else if (ctx->payload.offset == sizeof(struct arphdr) +
339
340
341
342
343
344
345
					   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 {
346
347
348
				memset(&fw->arp.smsk, 0xff,
				       min(ctx->payload.len,
					   sizeof(struct in_addr)));
349
350
351
			}

			if (inv)
352
				fw->arp.invflags |= IPT_INV_SRCIP;
353
354
355
		} else if (ctx->payload.offset == sizeof(struct arphdr) +
						  fw->arp.arhln +
						  sizeof(struct in_addr)) {
356
			if (nft_arp_parse_devaddr(ctx, e, &fw->arp.tgt_devaddr))
357
				fw->arp.invflags |= IPT_INV_TGTDEVADDR;
358
359
360
361
		} else if (ctx->payload.offset == sizeof(struct arphdr) +
						  fw->arp.arhln +
						  sizeof(struct in_addr) +
						  fw->arp.arhln) {
362
363
364
365
366
367
			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 {
368
369
370
				memset(&fw->arp.tmsk, 0xff,
				       min(ctx->payload.len,
					   sizeof(struct in_addr)));
371
372
373
			}

			if (inv)
374
				fw->arp.invflags |= IPT_INV_DSTIP;
375
376
377
378
379
380
381
382
		}
		break;
	}
}

static void nft_arp_print_header(unsigned int format, const char *chain,
				 const char *pol,
				 const struct xt_counters *counters,
383
384
				 bool basechain, uint32_t refs,
				 uint32_t entries)
385
386
{
	printf("Chain %s", chain);
387
	if (basechain && pol) {
388
389
390
391
392
393
394
395
396
397
398
399
400
401
		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);
	}
}

402
static void nft_arp_print_rule_details(const struct iptables_command_state *cs,
403
				       unsigned int format)
404
{
405
	const struct arpt_entry *fw = &cs->arp;
406
407
	char buf[BUFSIZ];
	char iface[IFNAMSIZ+2];
408
	const char *sep = "";
409
410
411
	int print_iface = 0;
	int i;

412
413
414
415
416
	if (strlen(cs->jumpto)) {
		printf("%s-j %s", sep, cs->jumpto);
		sep = " ";
	}

417
418
419
420
421
422
423
424
425
426
427
	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");
	}
428
	if (print_iface) {
429
		printf("%s%s-i %s", sep, fw->arp.invflags & IPT_INV_VIA_IN ?
430
				   "! " : "", iface);
431
432
		sep = " ";
	}
433
434
435
436
437
438
439
440
441
442
443
444
445

	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");
	}
446
	if (print_iface) {
447
		printf("%s%s-o %s", sep, fw->arp.invflags & IPT_INV_VIA_OUT ?
448
				   "! " : "", iface);
449
450
		sep = " ";
	}
451
452

	if (fw->arp.smsk.s_addr != 0L) {
453
		printf("%s%s", sep, fw->arp.invflags & IPT_INV_SRCIP
454
455
456
457
458
459
460
			? "! " : "");
		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);
461
462
		printf("-s %s", buf);
		sep = " ";
463
464
465
466
467
468
469
	}

	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;
470
	printf("%s%s", sep, fw->arp.invflags & IPT_INV_SRCDEVADDR
471
472
		? "! " : "");
	printf("--src-mac ");
473
474
	xtables_print_mac_and_mask((unsigned char *)fw->arp.src_devaddr.addr,
				   (unsigned char *)fw->arp.src_devaddr.mask);
475
	sep = " ";
476
477
478
after_devsrc:

	if (fw->arp.tmsk.s_addr != 0L) {
479
		printf("%s%s", sep, fw->arp.invflags & IPT_INV_DSTIP
480
481
482
483
484
485
486
			? "! " : "");
		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);
487
488
		printf("-d %s", buf);
		sep = " ";
489
490
491
492
493
494
495
	}

	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;
496
	printf("%s%s", sep, fw->arp.invflags & IPT_INV_TGTDEVADDR
497
498
		? "! " : "");
	printf("--dst-mac ");
499
500
	xtables_print_mac_and_mask((unsigned char *)fw->arp.tgt_devaddr.addr,
				   (unsigned char *)fw->arp.tgt_devaddr.mask);
501
	sep = " ";
502
503
504

after_devdst:

505
	if (fw->arp.arhln_mask != 255 || fw->arp.arhln != 6) {
506
		printf("%s%s", sep, fw->arp.invflags & IPT_INV_ARPHLN
507
508
509
510
			? "! " : "");
		printf("--h-length %d", fw->arp.arhln);
		if (fw->arp.arhln_mask != 255)
			printf("/%d", fw->arp.arhln_mask);
511
		sep = " ";
512
513
514
515
516
	}

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

517
		printf("%s%s", sep, fw->arp.invflags & IPT_INV_ARPOP
518
519
			? "! " : "");
		if (tmp <= NUMOPCODES && !(format & FMT_NUMERIC))
520
			printf("--opcode %s", arp_opcodes[tmp-1]);
521
		else
522
			printf("--opcode %d", tmp);
523
524
525

		if (fw->arp.arpop_mask != 65535)
			printf("/%d", ntohs(fw->arp.arpop_mask));
526
		sep = " ";
527
528
	}

529
	if (fw->arp.arhrd_mask != 65535 || fw->arp.arhrd != htons(1)) {
530
531
		uint16_t tmp = ntohs(fw->arp.arhrd);

532
		printf("%s%s", sep, fw->arp.invflags & IPT_INV_ARPHRD
533
534
535
536
537
538
539
			? "! " : "");
		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));
540
		sep = " ";
541
542
543
544
545
	}

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

546
		printf("%s%s", sep, fw->arp.invflags & IPT_INV_PROTO
547
548
549
550
551
552
553
			? "! " : "");
		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));
554
		sep = " ";
555
556
557
558
	}
}

static void
559
nft_arp_save_rule(const void *data, unsigned int format)
560
{
561
	const struct iptables_command_state *cs = data;
562

563
564
	format |= FMT_NUMERIC;

565
566
567
568
	nft_arp_print_rule_details(cs, format);
	if (cs->target && cs->target->save)
		cs->target->save(&cs->fw, cs->target->t);
	printf("\n");
569
570
}

571
static void
572
573
nft_arp_print_rule(struct nft_handle *h, struct nftnl_rule *r,
		   unsigned int num, unsigned int format)
574
575
576
577
578
579
{
	struct iptables_command_state cs = {};

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

580
	nft_rule_to_iptables_command_state(h, r, &cs);
581

582
583
	nft_arp_print_rule_details(&cs, format);
	print_matches_and_target(&cs, format);
584
585

	if (!(format & FMT_NOCOUNTS)) {
586
587
		printf(" , pcnt=");
		xtables_print_num(cs.counters.pcnt, format | FMT_NOTABLE);
588
		printf("-- bcnt=");
589
		xtables_print_num(cs.counters.bcnt, format | FMT_NOTABLE);
590
591
592
593
	}

	if (!(format & FMT_NONEWLINE))
		fputc('\n', stdout);
594
595

	nft_clear_iptables_command_state(&cs);
596
597
}

598
599
600
601
602
603
604
605
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
606
607
	    || a->arp.smsk.s_addr != b->arp.smsk.s_addr
	    || a->arp.tmsk.s_addr != b->arp.tmsk.s_addr
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
	    || 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);
}

625
626
627
628
629
630
631
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 ?: "-");
}

632
633
634
635
636
637
638
639
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,
640
641
642
	.print_rule		= nft_arp_print_rule,
	.save_rule		= nft_arp_save_rule,
	.save_chain		= nft_arp_save_chain,
643
	.post_parse		= NULL,
644
	.rule_to_cs		= nft_rule_to_iptables_command_state,
645
646
	.clear_cs		= nft_clear_iptables_command_state,
	.parse_target		= nft_ipv46_parse_target,
647
};