From 84909d171585d77fe769f03e2b1b96eab0aa0213 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Mon, 9 Sep 2013 12:54:04 +0200 Subject: xtables: bootstrap ARP compatibility layer for nftables This patch bootstraps ARP support for the compatibility layer: 1) copy original arptables code into xtables-arp.c 2) adapt it to fit into the existing nft infrastructure. 3) add the builtin table/chains for ARP. 4) add necessary parts so xtables-multi can provide xtables-arp. 5) add basic support for rule addition (-A), insertion (-I) and listing (-L). [ This was originally posted in a series of patches with interdependencies that I have collapsed to leave the repository in consistent state. This patch includes the following changes I made: * Rename from xtables-arptables to xtables-arp, previous name too long. * Remove nft-arptables.c, now we have one single nft-arp.c file. Moved specific ARP functions to nft.c. Those should go away at some point as some refactorization should allow to accomodate those functions to the existing infrastructure. * Fix --opcode Request/Reply, so we can do something useful with this like dropping ARP request/replies. --pablo ] Signed-off-by: Giuseppe Longo Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft-arp.c | 549 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 549 insertions(+) create mode 100644 iptables/nft-arp.c (limited to 'iptables/nft-arp.c') 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 + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#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 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, +}; -- cgit v1.2.3