1 /*
2 * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
3 * (C) 2013 by Giuseppe Longo <giuseppelng@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 *arp_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 *
addr_to_dotted(const struct in_addr * addrp)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 *
addr_to_host(const struct in_addr * addr)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 *
addr_to_network(const struct in_addr * addr)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 *
addr_to_anyname(const struct in_addr * addr)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 *
mask_to_dotted(const struct in_addr * mask)92 mask_to_dotted(const struct in_addr *mask)
93 {
94 int i;
95 static char buf[22];
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 snprintf(buf, sizeof(buf), "/%s", addr_to_dotted(mask));
113
114 return buf;
115 }
116
need_devaddr(struct arpt_devaddr_info * info)117 static bool need_devaddr(struct arpt_devaddr_info *info)
118 {
119 int i;
120
121 for (i = 0; i < ETH_ALEN; i++) {
122 if (info->addr[i] || info->mask[i])
123 return true;
124 }
125
126 return false;
127 }
128
nft_arp_add(struct nft_handle * h,struct nftnl_rule * r,void * data)129 static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, void *data)
130 {
131 struct iptables_command_state *cs = data;
132 struct arpt_entry *fw = &cs->arp;
133 uint32_t op;
134 int ret = 0;
135
136 if (fw->arp.iniface[0] != '\0') {
137 op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_VIA_IN);
138 add_iniface(r, fw->arp.iniface, op);
139 }
140
141 if (fw->arp.outiface[0] != '\0') {
142 op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_VIA_OUT);
143 add_outiface(r, fw->arp.outiface, op);
144 }
145
146 if (fw->arp.arhrd != 0 ||
147 fw->arp.invflags & IPT_INV_ARPHRD) {
148 op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_ARPHRD);
149 add_payload(r, offsetof(struct arphdr, ar_hrd), 2,
150 NFT_PAYLOAD_NETWORK_HEADER);
151 add_cmp_u16(r, fw->arp.arhrd, op);
152 }
153
154 if (fw->arp.arpro != 0 ||
155 fw->arp.invflags & IPT_INV_PROTO) {
156 op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_PROTO);
157 add_payload(r, offsetof(struct arphdr, ar_pro), 2,
158 NFT_PAYLOAD_NETWORK_HEADER);
159 add_cmp_u16(r, fw->arp.arpro, op);
160 }
161
162 if (fw->arp.arhln != 0 ||
163 fw->arp.invflags & IPT_INV_ARPHLN) {
164 op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_ARPHLN);
165 add_proto(r, offsetof(struct arphdr, ar_hln), 1,
166 fw->arp.arhln, op);
167 }
168
169 add_proto(r, offsetof(struct arphdr, ar_pln), 1, 4, NFT_CMP_EQ);
170
171 if (fw->arp.arpop != 0 ||
172 fw->arp.invflags & IPT_INV_ARPOP) {
173 op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_ARPOP);
174 add_payload(r, offsetof(struct arphdr, ar_op), 2,
175 NFT_PAYLOAD_NETWORK_HEADER);
176 add_cmp_u16(r, fw->arp.arpop, op);
177 }
178
179 if (need_devaddr(&fw->arp.src_devaddr)) {
180 op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_SRCDEVADDR);
181 add_addr(r, NFT_PAYLOAD_NETWORK_HEADER,
182 sizeof(struct arphdr),
183 &fw->arp.src_devaddr.addr,
184 &fw->arp.src_devaddr.mask,
185 fw->arp.arhln, op);
186
187 }
188
189 if (fw->arp.src.s_addr != 0 ||
190 fw->arp.smsk.s_addr != 0 ||
191 fw->arp.invflags & IPT_INV_SRCIP) {
192 op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_SRCIP);
193 add_addr(r, NFT_PAYLOAD_NETWORK_HEADER,
194 sizeof(struct arphdr) + fw->arp.arhln,
195 &fw->arp.src.s_addr, &fw->arp.smsk.s_addr,
196 sizeof(struct in_addr), op);
197 }
198
199
200 if (need_devaddr(&fw->arp.tgt_devaddr)) {
201 op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_TGTDEVADDR);
202 add_addr(r, NFT_PAYLOAD_NETWORK_HEADER,
203 sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr),
204 &fw->arp.tgt_devaddr.addr,
205 &fw->arp.tgt_devaddr.mask,
206 fw->arp.arhln, op);
207 }
208
209 if (fw->arp.tgt.s_addr != 0 ||
210 fw->arp.tmsk.s_addr != 0 ||
211 fw->arp.invflags & IPT_INV_DSTIP) {
212 op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_DSTIP);
213 add_addr(r, NFT_PAYLOAD_NETWORK_HEADER,
214 sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr) + fw->arp.arhln,
215 &fw->arp.tgt.s_addr, &fw->arp.tmsk.s_addr,
216 sizeof(struct in_addr), op);
217 }
218
219 /* Counters need to me added before the target, otherwise they are
220 * increased for each rule because of the way nf_tables works.
221 */
222 if (add_counters(r, fw->counters.pcnt, fw->counters.bcnt) < 0)
223 return -1;
224
225 if (cs->target != NULL) {
226 /* Standard target? */
227 if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
228 ret = add_verdict(r, NF_ACCEPT);
229 else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
230 ret = add_verdict(r, NF_DROP);
231 else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
232 ret = add_verdict(r, NFT_RETURN);
233 else
234 ret = add_target(r, cs->target->t);
235 } else if (strlen(cs->jumpto) > 0) {
236 /* No goto in arptables */
237 ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
238 }
239
240 return ret;
241 }
242
nft_arp_parse_meta(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)243 static void nft_arp_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
244 void *data)
245 {
246 struct iptables_command_state *cs = data;
247 struct arpt_entry *fw = &cs->arp;
248 uint8_t flags = 0;
249
250 parse_meta(e, ctx->meta.key, fw->arp.iniface, fw->arp.iniface_mask,
251 fw->arp.outiface, fw->arp.outiface_mask,
252 &flags);
253
254 fw->arp.invflags |= flags;
255 }
256
nft_arp_parse_immediate(const char * jumpto,bool nft_goto,void * data)257 static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto,
258 void *data)
259 {
260 struct iptables_command_state *cs = data;
261
262 cs->jumpto = jumpto;
263 }
264
parse_mask_ipv4(struct nft_xt_ctx * ctx,struct in_addr * mask)265 static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask)
266 {
267 mask->s_addr = ctx->bitwise.mask[0];
268 }
269
nft_arp_parse_devaddr(struct nft_xt_ctx * ctx,struct nftnl_expr * e,struct arpt_devaddr_info * info)270 static bool nft_arp_parse_devaddr(struct nft_xt_ctx *ctx,
271 struct nftnl_expr *e,
272 struct arpt_devaddr_info *info)
273 {
274 uint32_t hlen;
275 bool inv;
276
277 nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &hlen);
278
279 if (hlen != ETH_ALEN)
280 return false;
281
282 get_cmp_data(e, info->addr, ETH_ALEN, &inv);
283
284 if (ctx->flags & NFT_XT_CTX_BITWISE) {
285 memcpy(info->mask, ctx->bitwise.mask, ETH_ALEN);
286 ctx->flags &= ~NFT_XT_CTX_BITWISE;
287 } else {
288 memset(info->mask, 0xff,
289 min(ctx->payload.len, ETH_ALEN));
290 }
291
292 return inv;
293 }
294
nft_arp_parse_payload(struct nft_xt_ctx * ctx,struct nftnl_expr * e,void * data)295 static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
296 struct nftnl_expr *e, void *data)
297 {
298 struct iptables_command_state *cs = data;
299 struct arpt_entry *fw = &cs->arp;
300 struct in_addr addr;
301 uint16_t ar_hrd, ar_pro, ar_op;
302 uint8_t ar_hln;
303 bool inv;
304
305 switch (ctx->payload.offset) {
306 case offsetof(struct arphdr, ar_hrd):
307 get_cmp_data(e, &ar_hrd, sizeof(ar_hrd), &inv);
308 fw->arp.arhrd = ar_hrd;
309 fw->arp.arhrd_mask = 0xffff;
310 if (inv)
311 fw->arp.invflags |= IPT_INV_ARPHRD;
312 break;
313 case offsetof(struct arphdr, ar_pro):
314 get_cmp_data(e, &ar_pro, sizeof(ar_pro), &inv);
315 fw->arp.arpro = ar_pro;
316 fw->arp.arpro_mask = 0xffff;
317 if (inv)
318 fw->arp.invflags |= IPT_INV_PROTO;
319 break;
320 case offsetof(struct arphdr, ar_op):
321 get_cmp_data(e, &ar_op, sizeof(ar_op), &inv);
322 fw->arp.arpop = ar_op;
323 fw->arp.arpop_mask = 0xffff;
324 if (inv)
325 fw->arp.invflags |= IPT_INV_ARPOP;
326 break;
327 case offsetof(struct arphdr, ar_hln):
328 get_cmp_data(e, &ar_hln, sizeof(ar_hln), &inv);
329 fw->arp.arhln = ar_hln;
330 fw->arp.arhln_mask = 0xff;
331 if (inv)
332 fw->arp.invflags |= IPT_INV_ARPOP;
333 break;
334 default:
335 if (ctx->payload.offset == sizeof(struct arphdr)) {
336 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 fw->arp.arhln) {
340 get_cmp_data(e, &addr, sizeof(addr), &inv);
341 fw->arp.src.s_addr = addr.s_addr;
342 if (ctx->flags & NFT_XT_CTX_BITWISE) {
343 parse_mask_ipv4(ctx, &fw->arp.smsk);
344 ctx->flags &= ~NFT_XT_CTX_BITWISE;
345 } else {
346 memset(&fw->arp.smsk, 0xff,
347 min(ctx->payload.len,
348 sizeof(struct in_addr)));
349 }
350
351 if (inv)
352 fw->arp.invflags |= IPT_INV_SRCIP;
353 } else if (ctx->payload.offset == sizeof(struct arphdr) +
354 fw->arp.arhln +
355 sizeof(struct in_addr)) {
356 if (nft_arp_parse_devaddr(ctx, e, &fw->arp.tgt_devaddr))
357 fw->arp.invflags |= IPT_INV_TGTDEVADDR;
358 } else if (ctx->payload.offset == sizeof(struct arphdr) +
359 fw->arp.arhln +
360 sizeof(struct in_addr) +
361 fw->arp.arhln) {
362 get_cmp_data(e, &addr, sizeof(addr), &inv);
363 fw->arp.tgt.s_addr = addr.s_addr;
364 if (ctx->flags & NFT_XT_CTX_BITWISE) {
365 parse_mask_ipv4(ctx, &fw->arp.tmsk);
366 ctx->flags &= ~NFT_XT_CTX_BITWISE;
367 } else {
368 memset(&fw->arp.tmsk, 0xff,
369 min(ctx->payload.len,
370 sizeof(struct in_addr)));
371 }
372
373 if (inv)
374 fw->arp.invflags |= IPT_INV_DSTIP;
375 }
376 break;
377 }
378 }
379
nft_arp_print_header(unsigned int format,const char * chain,const char * pol,const struct xt_counters * counters,bool basechain,uint32_t refs,uint32_t entries)380 static void nft_arp_print_header(unsigned int format, const char *chain,
381 const char *pol,
382 const struct xt_counters *counters,
383 bool basechain, uint32_t refs,
384 uint32_t entries)
385 {
386 printf("Chain %s", chain);
387 if (basechain && pol) {
388 printf(" (policy %s", pol);
389 if (!(format & FMT_NOCOUNTS)) {
390 fputc(' ', stdout);
391 xtables_print_num(counters->pcnt, (format|FMT_NOTABLE));
392 fputs("packets, ", stdout);
393 xtables_print_num(counters->bcnt, (format|FMT_NOTABLE));
394 fputs("bytes", stdout);
395 }
396 printf(")\n");
397 } else {
398 printf(" (%u references)\n", refs);
399 }
400 }
401
nft_arp_print_rule_details(const struct iptables_command_state * cs,unsigned int format)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 char buf[BUFSIZ];
407 char iface[IFNAMSIZ+2];
408 const char *sep = "";
409 int print_iface = 0;
410 int i;
411
412 if (strlen(cs->jumpto)) {
413 printf("%s-j %s", sep, cs->jumpto);
414 sep = " ";
415 }
416
417 iface[0] = '\0';
418
419 if (fw->arp.iniface[0] != '\0') {
420 strcat(iface, fw->arp.iniface);
421 print_iface = 1;
422 }
423 else if (format & FMT_VIA) {
424 print_iface = 1;
425 if (format & FMT_NUMERIC) strcat(iface, "*");
426 else strcat(iface, "any");
427 }
428 if (print_iface) {
429 printf("%s%s-i %s", sep, fw->arp.invflags & IPT_INV_VIA_IN ?
430 "! " : "", iface);
431 sep = " ";
432 }
433
434 print_iface = 0;
435 iface[0] = '\0';
436
437 if (fw->arp.outiface[0] != '\0') {
438 strcat(iface, fw->arp.outiface);
439 print_iface = 1;
440 }
441 else if (format & FMT_VIA) {
442 print_iface = 1;
443 if (format & FMT_NUMERIC) strcat(iface, "*");
444 else strcat(iface, "any");
445 }
446 if (print_iface) {
447 printf("%s%s-o %s", sep, fw->arp.invflags & IPT_INV_VIA_OUT ?
448 "! " : "", iface);
449 sep = " ";
450 }
451
452 if (fw->arp.smsk.s_addr != 0L) {
453 printf("%s%s", sep, fw->arp.invflags & IPT_INV_SRCIP
454 ? "! " : "");
455 if (format & FMT_NUMERIC)
456 sprintf(buf, "%s", addr_to_dotted(&(fw->arp.src)));
457 else
458 sprintf(buf, "%s", addr_to_anyname(&(fw->arp.src)));
459 strncat(buf, mask_to_dotted(&(fw->arp.smsk)),
460 sizeof(buf) - strlen(buf) - 1);
461 printf("-s %s", buf);
462 sep = " ";
463 }
464
465 for (i = 0; i < ARPT_DEV_ADDR_LEN_MAX; i++)
466 if (fw->arp.src_devaddr.mask[i] != 0)
467 break;
468 if (i == ARPT_DEV_ADDR_LEN_MAX)
469 goto after_devsrc;
470 printf("%s%s", sep, fw->arp.invflags & IPT_INV_SRCDEVADDR
471 ? "! " : "");
472 printf("--src-mac ");
473 xtables_print_mac_and_mask((unsigned char *)fw->arp.src_devaddr.addr,
474 (unsigned char *)fw->arp.src_devaddr.mask);
475 sep = " ";
476 after_devsrc:
477
478 if (fw->arp.tmsk.s_addr != 0L) {
479 printf("%s%s", sep, fw->arp.invflags & IPT_INV_DSTIP
480 ? "! " : "");
481 if (format & FMT_NUMERIC)
482 sprintf(buf, "%s", addr_to_dotted(&(fw->arp.tgt)));
483 else
484 sprintf(buf, "%s", addr_to_anyname(&(fw->arp.tgt)));
485 strncat(buf, mask_to_dotted(&(fw->arp.tmsk)),
486 sizeof(buf) - strlen(buf) - 1);
487 printf("-d %s", buf);
488 sep = " ";
489 }
490
491 for (i = 0; i <ARPT_DEV_ADDR_LEN_MAX; i++)
492 if (fw->arp.tgt_devaddr.mask[i] != 0)
493 break;
494 if (i == ARPT_DEV_ADDR_LEN_MAX)
495 goto after_devdst;
496 printf("%s%s", sep, fw->arp.invflags & IPT_INV_TGTDEVADDR
497 ? "! " : "");
498 printf("--dst-mac ");
499 xtables_print_mac_and_mask((unsigned char *)fw->arp.tgt_devaddr.addr,
500 (unsigned char *)fw->arp.tgt_devaddr.mask);
501 sep = " ";
502
503 after_devdst:
504
505 if (fw->arp.arhln_mask != 255 || fw->arp.arhln != 6) {
506 printf("%s%s", sep, fw->arp.invflags & IPT_INV_ARPHLN
507 ? "! " : "");
508 printf("--h-length %d", fw->arp.arhln);
509 if (fw->arp.arhln_mask != 255)
510 printf("/%d", fw->arp.arhln_mask);
511 sep = " ";
512 }
513
514 if (fw->arp.arpop_mask != 0) {
515 int tmp = ntohs(fw->arp.arpop);
516
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 if (fw->arp.arpop_mask != 65535)
525 printf("/%d", ntohs(fw->arp.arpop_mask));
526 sep = " ";
527 }
528
529 if (fw->arp.arhrd_mask != 65535 || fw->arp.arhrd != htons(1)) {
530 uint16_t tmp = ntohs(fw->arp.arhrd);
531
532 printf("%s%s", sep, fw->arp.invflags & IPT_INV_ARPHRD
533 ? "! " : "");
534 if (tmp == 1 && !(format & FMT_NUMERIC))
535 printf("--h-type %s", "Ethernet");
536 else
537 printf("--h-type %u", tmp);
538 if (fw->arp.arhrd_mask != 65535)
539 printf("/%d", ntohs(fw->arp.arhrd_mask));
540 sep = " ";
541 }
542
543 if (fw->arp.arpro_mask != 0) {
544 int tmp = ntohs(fw->arp.arpro);
545
546 printf("%s%s", sep, fw->arp.invflags & IPT_INV_PROTO
547 ? "! " : "");
548 if (tmp == 0x0800 && !(format & FMT_NUMERIC))
549 printf("--proto-type %s", "IPv4");
550 else
551 printf("--proto-type 0x%x", tmp);
552 if (fw->arp.arpro_mask != 65535)
553 printf("/%x", ntohs(fw->arp.arpro_mask));
554 sep = " ";
555 }
556 }
557
558 static void
nft_arp_save_rule(const void * data,unsigned int format)559 nft_arp_save_rule(const void *data, unsigned int format)
560 {
561 const struct iptables_command_state *cs = data;
562
563 format |= FMT_NUMERIC;
564
565 nft_arp_print_rule_details(cs, format);
566 if (cs->target && cs->target->save)
567 cs->target->save(&cs->fw, cs->target->t);
568 printf("\n");
569 }
570
571 static void
nft_arp_print_rule(struct nft_handle * h,struct nftnl_rule * r,unsigned int num,unsigned int format)572 nft_arp_print_rule(struct nft_handle *h, struct nftnl_rule *r,
573 unsigned int num, unsigned int format)
574 {
575 struct iptables_command_state cs = {};
576
577 if (format & FMT_LINENUMBERS)
578 printf("%u ", num);
579
580 nft_rule_to_iptables_command_state(h, r, &cs);
581
582 nft_arp_print_rule_details(&cs, format);
583 print_matches_and_target(&cs, format);
584
585 if (!(format & FMT_NOCOUNTS)) {
586 printf(" , pcnt=");
587 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 if (!(format & FMT_NONEWLINE))
593 fputc('\n', stdout);
594
595 nft_clear_iptables_command_state(&cs);
596 }
597
nft_arp_is_same(const void * data_a,const void * data_b)598 static bool nft_arp_is_same(const void *data_a,
599 const void *data_b)
600 {
601 const struct arpt_entry *a = data_a;
602 const struct arpt_entry *b = data_b;
603
604 if (a->arp.src.s_addr != b->arp.src.s_addr
605 || a->arp.tgt.s_addr != b->arp.tgt.s_addr
606 || a->arp.smsk.s_addr != b->arp.smsk.s_addr
607 || a->arp.tmsk.s_addr != b->arp.tmsk.s_addr
608 || a->arp.arpro != b->arp.arpro
609 || a->arp.flags != b->arp.flags
610 || a->arp.invflags != b->arp.invflags) {
611 DEBUGP("different src/dst/proto/flags/invflags\n");
612 return false;
613 }
614
615 return is_same_interfaces(a->arp.iniface,
616 a->arp.outiface,
617 (unsigned char *)a->arp.iniface_mask,
618 (unsigned char *)a->arp.outiface_mask,
619 b->arp.iniface,
620 b->arp.outiface,
621 (unsigned char *)b->arp.iniface_mask,
622 (unsigned char *)b->arp.outiface_mask);
623 }
624
nft_arp_save_chain(const struct nftnl_chain * c,const char * policy)625 static void nft_arp_save_chain(const struct nftnl_chain *c, const char *policy)
626 {
627 const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
628
629 printf(":%s %s\n", chain, policy ?: "-");
630 }
631
632 struct nft_family_ops nft_family_ops_arp = {
633 .add = nft_arp_add,
634 .is_same = nft_arp_is_same,
635 .print_payload = NULL,
636 .parse_meta = nft_arp_parse_meta,
637 .parse_payload = nft_arp_parse_payload,
638 .parse_immediate = nft_arp_parse_immediate,
639 .print_header = nft_arp_print_header,
640 .print_rule = nft_arp_print_rule,
641 .save_rule = nft_arp_save_rule,
642 .save_chain = nft_arp_save_chain,
643 .post_parse = NULL,
644 .rule_to_cs = nft_rule_to_iptables_command_state,
645 .clear_cs = nft_clear_iptables_command_state,
646 .parse_target = nft_ipv46_parse_target,
647 };
648