summaryrefslogtreecommitdiffstats
path: root/iptables/nft-arp.c
diff options
context:
space:
mode:
Diffstat (limited to 'iptables/nft-arp.c')
-rw-r--r--iptables/nft-arp.c549
1 files changed, 549 insertions, 0 deletions
diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
new file mode 100644
index 00000000..09b939d0
--- /dev/null
+++ b/iptables/nft-arp.c
@@ -0,0 +1,549 @@
+/*
+ * (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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <net/if_arp.h>
+
+#include <xtables.h>
+#include <net/if_arp.h>
+#include <if_ether.h>
+
+#include <linux/netfilter_arp/arp_tables.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "nft-shared.h"
+
+/* a few names */
+char *opcodes[] =
+{
+ "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;
+ static char buf[20];
+ 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 */
+ sprintf(buf, "/%s", addr_to_dotted(mask));
+
+ return buf;
+}
+
+static void print_mac(const unsigned char *mac, int l)
+{
+ int j;
+
+ for (j = 0; j < l; j++)
+ printf("%02x%s", mac[j],
+ (j==l-1) ? "" : ":");
+}
+
+static void print_mac_and_mask(const unsigned char *mac, const unsigned char *mask, int l)
+{
+ int i;
+
+ print_mac(mac, l);
+ for (i = 0; i < l ; i++)
+ if (mask[i] != 255)
+ break;
+ if (i == l)
+ return;
+ printf("/");
+ print_mac(mask, l);
+}
+
+static uint8_t arpt_to_ipt_flags(uint16_t invflags)
+{
+ uint8_t result = 0;
+
+ if (invflags & ARPT_INV_VIA_IN)
+ result |= IPT_INV_VIA_IN;
+
+ if (invflags & ARPT_INV_VIA_OUT)
+ result |= IPT_INV_VIA_OUT;
+
+ if (invflags & ARPT_INV_SRCIP)
+ result |= IPT_INV_SRCIP;
+
+ if (invflags & ARPT_INV_TGTIP)
+ result |= IPT_INV_DSTIP;
+
+ if (invflags & ARPT_INV_ARPPRO)
+ result |= IPT_INV_PROTO;
+
+ if (invflags & ARPT_INV_MASK)
+ result |= IPT_INV_MASK;
+
+ return result;
+}
+
+static int nft_arp_add(struct nft_rule *r, void *data)
+{
+ struct arpt_entry *fw = data;
+ uint8_t flags = arpt_to_ipt_flags(fw->arp.invflags);
+
+ if (fw->arp.iniface[0] != '\0')
+ add_iniface(r, fw->arp.iniface, flags);
+
+ if (fw->arp.outiface[0] != '\0')
+ add_outiface(r, fw->arp.outiface, flags);
+
+ if (fw->arp.arhrd != 0) {
+ add_payload(r, offsetof(struct arphdr, ar_hrd), 2);
+ add_cmp_u16(r, fw->arp.arhrd, NFT_CMP_EQ);
+ }
+
+ if (fw->arp.arpro != 0) {
+ add_payload(r, offsetof(struct arphdr, ar_pro), 2);
+ add_cmp_u16(r, fw->arp.arpro, NFT_CMP_EQ);
+ }
+
+ if (fw->arp.arhln != 0)
+ add_proto(r, offsetof(struct arphdr, ar_hln), 1,
+ fw->arp.arhln, flags);
+
+ add_proto(r, offsetof(struct arphdr, ar_pln), 1, 4, 0);
+
+ if (fw->arp.arpop != 0) {
+ add_payload(r, offsetof(struct arphdr, ar_op), 2);
+ add_cmp_u16(r, fw->arp.arpop, NFT_CMP_EQ);
+ }
+
+ if (fw->arp.src_devaddr.addr[0] != '\0') {
+ add_payload(r, sizeof(struct arphdr), fw->arp.arhln);
+ add_cmp_ptr(r, NFT_CMP_EQ, fw->arp.src_devaddr.addr, fw->arp.arhln);
+ }
+
+ if (fw->arp.src.s_addr != 0)
+ add_addr(r, sizeof(struct arphdr) + fw->arp.arhln,
+ &fw->arp.src.s_addr, 4, flags);
+
+ if (fw->arp.tgt_devaddr.addr[0] != '\0') {
+ add_payload(r, sizeof(struct arphdr) + fw->arp.arhln + 4, fw->arp.arhln);
+ add_cmp_ptr(r, NFT_CMP_EQ, fw->arp.tgt_devaddr.addr, fw->arp.arhln);
+ }
+
+ if (fw->arp.tgt.s_addr != 0)
+ add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr),
+ &fw->arp.tgt.s_addr, 4, flags);
+
+ return 0;
+}
+
+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;
+
+ if (invflags & IPT_INV_MASK)
+ result |= ARPT_INV_MASK;
+
+ return result;
+}
+
+static void nft_arp_parse_meta(struct nft_rule_expr *e, uint8_t key,
+ void *data)
+{
+ struct arpt_entry *fw = data;
+ uint8_t flags;
+
+ parse_meta(e, 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_target(struct xtables_target *t, void *data)
+{
+ struct arpt_entry *fw = data;
+ size_t size = sizeof(struct arpt_entry);
+ struct xt_entry_target **target;
+
+ fw->target_offset = size;
+ fw->next_offset = size + t->t->u.target_size;
+
+ target = (void *) fw + fw->target_offset;
+ *target = t->t;
+}
+
+static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto,
+ void *data)
+{
+ struct xtables_target *target;
+ size_t size;
+
+ target = xtables_find_target(XT_STANDARD_TARGET,
+ XTF_LOAD_MUST_SUCCEED);
+ size = sizeof(struct xt_entry_target) + target->size;
+ target->t = xtables_calloc(1, size);
+ target->t->u.target_size = size;
+ strcpy(target->t->u.user.name, jumpto);
+
+ nft_arp_parse_target(target, data);
+}
+
+static void nft_arp_parse_payload(struct nft_rule_expr_iter *iter,
+ uint32_t offset, void *data)
+{
+ struct arpt_entry *fw = data;
+ struct in_addr addr;
+ unsigned short int ar_hrd, ar_pro, ar_op, ar_hln;
+ bool inv;
+
+ switch (offset) {
+ case offsetof(struct arphdr, ar_hrd):
+ get_cmp_data(iter, &ar_hrd, sizeof(ar_hrd), &inv);
+ fw->arp.arhrd = htons(ar_hrd);
+ fw->arp.arhrd_mask = htons(0xffff);
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_ARPHRD;
+ break;
+ case offsetof(struct arphdr, ar_pro):
+ get_cmp_data(iter, &ar_pro, sizeof(ar_pro), &inv);
+ fw->arp.arpro = htons(ar_pro);
+ fw->arp.arpro_mask = htons(0xffff);
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_ARPPRO;
+ break;
+ case offsetof(struct arphdr, ar_op):
+ get_cmp_data(iter, &ar_op, sizeof(ar_op), &inv);
+ fw->arp.arpop = htons(ar_op);
+ fw->arp.arpop_mask = htons(0xffff);
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_ARPOP;
+ break;
+ case offsetof(struct arphdr, ar_hln):
+ get_cmp_data(iter, &ar_hln, sizeof(ar_op), &inv);
+ fw->arp.arhln = ar_hln;
+ fw->arp.arhln_mask = 0xff;
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_ARPOP;
+ break;
+ default:
+ if (!fw->arp.arhln)
+ break;
+
+ if (offset == sizeof(struct arphdr) + fw->arp.arhln) {
+ get_cmp_data(iter, &addr, sizeof(addr), &inv);
+ fw->arp.src.s_addr = addr.s_addr;
+ fw->arp.smsk.s_addr = 0xffffffff;
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_SRCIP;
+ } else if (offset == sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr)) {
+ get_cmp_data(iter, &addr, sizeof(addr), &inv);
+ fw->arp.tgt.s_addr = addr.s_addr;
+ fw->arp.tmsk.s_addr = 0xffffffff;
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_TGTIP;
+ }
+ break;
+ }
+}
+
+static 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;
+ int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY);
+
+ iter = nft_rule_expr_iter_create(r);
+ if (iter == NULL)
+ return;
+
+ expr = nft_rule_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name =
+ nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
+
+ if (strcmp(name, "counter") == 0)
+ nft_parse_counter(expr, iter, &fw->counters);
+ else if (strcmp(name, "payload") == 0)
+ nft_parse_payload(expr, iter, family, fw);
+ else if (strcmp(name, "meta") == 0)
+ nft_parse_meta(expr, iter, family, fw);
+ else if (strcmp(name, "immediate") == 0)
+ nft_parse_immediate(expr, iter, family, fw);
+ else if (strcmp(name, "target") == 0)
+ nft_parse_target(expr, iter, family, fw);
+
+ expr = nft_rule_expr_iter_next(iter);
+ }
+
+ nft_rule_expr_iter_destroy(iter);
+}
+
+static void
+nft_arp_print_firewall(struct nft_rule *r, unsigned int num,
+ unsigned int format)
+{
+ struct arpt_entry fw = {};
+ const char *targname;
+ struct xtables_target *target = NULL;
+ const struct xt_entry_target *t;
+ char buf[BUFSIZ];
+ int i;
+ char iface[IFNAMSIZ+2];
+ int print_iface = 0;
+
+ nft_rule_to_arpt_entry(r, &fw);
+
+ if (format & FMT_LINENUMBERS)
+ printf("%u ", num);
+
+ if (fw.target_offset) {
+ t = nft_arp_get_target(&fw);
+ targname = t->u.user.name;
+ target = xtables_find_target(targname, XTF_TRY_LOAD);
+ if (!(format & FMT_NOTARGET))
+ printf("-j %s ", targname);
+ }
+
+ 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");
+ }
+ if (print_iface)
+ printf("%s-i %s ", fw.arp.invflags & ARPT_INV_VIA_IN ? "! ": "", iface);
+
+ 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");
+ }
+ if (print_iface)
+ printf("%s-o %s ", fw.arp.invflags & ARPT_INV_VIA_OUT ? "! " : "", iface);
+
+ if (fw.arp.smsk.s_addr != 0L) {
+ printf("%s", fw.arp.invflags & ARPT_INV_SRCIP
+ ? "! " : "");
+ if (format & FMT_NUMERIC)
+ sprintf(buf, "%s", addr_to_dotted(&(fw.arp.src)));
+ else
+ sprintf(buf, "%s", addr_to_anyname(&(fw.arp.src)));
+ strcat(buf, mask_to_dotted(&(fw.arp.smsk)));
+ printf("-s %s ", buf);
+ }
+
+ 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;
+ printf("%s", fw.arp.invflags & ARPT_INV_SRCDEVADDR
+ ? "! " : "");
+ printf("--src-mac ");
+ print_mac_and_mask((unsigned char *)fw.arp.src_devaddr.addr,
+ (unsigned char *)fw.arp.src_devaddr.mask, ETH_ALEN);
+ printf(" ");
+after_devsrc:
+
+ if (fw.arp.tmsk.s_addr != 0L) {
+ printf("%s",fw.arp.invflags & ARPT_INV_TGTIP
+ ? "! " : "");
+ if (format & FMT_NUMERIC)
+ sprintf(buf, "%s", addr_to_dotted(&(fw.arp.tgt)));
+ else
+ sprintf(buf, "%s", addr_to_anyname(&(fw.arp.tgt)));
+ strcat(buf, mask_to_dotted(&(fw.arp.tmsk)));
+ printf("-d %s ", buf);
+ }
+
+ 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;
+ printf("%s",fw.arp.invflags & ARPT_INV_TGTDEVADDR
+ ? "! " : "");
+ printf("--dst-mac ");
+ print_mac_and_mask((unsigned char *)fw.arp.tgt_devaddr.addr,
+ (unsigned char *)fw.arp.tgt_devaddr.mask, ETH_ALEN);
+ printf(" ");
+after_devdst:
+
+ if (fw.arp.arhln_mask != 0) {
+ printf("%s",fw.arp.invflags & ARPT_INV_ARPHLN
+ ? "! " : "");
+ printf("--h-length %d", fw.arp.arhln);
+ if (fw.arp.arhln_mask != 255)
+ printf("/%d", fw.arp.arhln_mask);
+ printf(" ");
+ }
+
+ if (fw.arp.arpop_mask != 0) {
+ int tmp = ntohs(fw.arp.arpop);
+
+ printf("%s",fw.arp.invflags & ARPT_INV_ARPOP
+ ? "! " : "");
+ if (tmp <= NUMOPCODES && !(format & FMT_NUMERIC))
+ printf("--opcode %s", opcodes[tmp-1]);
+ else
+ printf("--opcode %d", tmp);
+ if (fw.arp.arpop_mask != 65535)
+ printf("/%d", ntohs(fw.arp.arpop_mask));
+ printf(" ");
+ }
+
+ if (fw.arp.arhrd_mask != 0) {
+ uint16_t tmp = ntohs(fw.arp.arhrd);
+
+ printf("%s", fw.arp.invflags & ARPT_INV_ARPHRD
+ ? "! " : "");
+ 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));
+ printf(" ");
+ }
+
+ if (fw.arp.arpro_mask != 0) {
+ int tmp = ntohs(fw.arp.arpro);
+
+ printf("%s", fw.arp.invflags & ARPT_INV_ARPPRO
+ ? "! " : "");
+ 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));
+ printf(" ");
+ }
+
+ if (target) {
+ if (target->print)
+ /* Print the target information. */
+ target->print(&fw.arp, t, format & FMT_NUMERIC);
+ }
+
+ if (!(format & FMT_NOCOUNTS)) {
+ printf(", pcnt=");
+ xtables_print_num(fw.counters.pcnt, format);
+ printf("-- bcnt=");
+ xtables_print_num(fw.counters.bcnt, format);
+ }
+
+ if (!(format & FMT_NONEWLINE))
+ fputc('\n', stdout);
+}
+
+struct nft_family_ops nft_family_ops_arp = {
+ .add = nft_arp_add,
+ .is_same = NULL,
+ .print_payload = NULL,
+ .parse_meta = nft_arp_parse_meta,
+ .parse_payload = nft_arp_parse_payload,
+ .parse_immediate = nft_arp_parse_immediate,
+ .print_firewall = nft_arp_print_firewall,
+ .post_parse = NULL,
+};