diff options
Diffstat (limited to 'iptables/nft-arp.c')
-rw-r--r-- | iptables/nft-arp.c | 883 |
1 files changed, 535 insertions, 348 deletions
diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c index d4a86610..5d66e271 100644 --- a/iptables/nft-arp.c +++ b/iptables/nft-arp.c @@ -18,6 +18,7 @@ #include <xtables.h> #include <libiptc/libxtc.h> +#include <arpa/inet.h> #include <net/if_arp.h> #include <netinet/if_ether.h> @@ -25,94 +26,8 @@ #include <linux/netfilter/nf_tables.h> #include "nft-shared.h" -#include "nft-arp.h" #include "nft.h" - -/* a few names */ -char *arp_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[22]; - 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 */ - snprintf(buf, sizeof(buf), "/%s", addr_to_dotted(mask)); - - return buf; -} +#include "xshared.h" static bool need_devaddr(struct arpt_devaddr_info *info) { @@ -126,59 +41,81 @@ static bool need_devaddr(struct arpt_devaddr_info *info) return false; } -static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, void *data) +static int nft_arp_add(struct nft_handle *h, struct nft_rule_ctx *ctx, + struct nftnl_rule *r, struct iptables_command_state *cs) { - struct iptables_command_state *cs = data; struct arpt_entry *fw = &cs->arp; 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); + op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_VIA_IN); + add_iface(h, r, fw->arp.iniface, NFT_META_IIFNAME, op); } if (fw->arp.outiface[0] != '\0') { - op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_VIA_OUT); - add_outiface(r, fw->arp.outiface, op); + op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_VIA_OUT); + add_iface(h, r, fw->arp.outiface, NFT_META_OIFNAME, op); } if (fw->arp.arhrd != 0 || - fw->arp.invflags & ARPT_INV_ARPHRD) { - 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); + fw->arp.arhrd_mask != 0xffff || + fw->arp.invflags & IPT_INV_ARPHRD) { + uint8_t reg; + + op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_ARPHRD); + add_payload(h, r, offsetof(struct arphdr, ar_hrd), 2, + NFT_PAYLOAD_NETWORK_HEADER, ®); + if (fw->arp.arhrd_mask != 0xffff) + add_bitwise_u16(h, r, fw->arp.arhrd_mask, 0, reg, ®); + add_cmp_u16(r, fw->arp.arhrd, op, reg); } if (fw->arp.arpro != 0 || - fw->arp.invflags & ARPT_INV_ARPPRO) { - 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); + fw->arp.arpro_mask != 0xffff || + fw->arp.invflags & IPT_INV_PROTO) { + uint8_t reg; + + op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_PROTO); + add_payload(h, r, offsetof(struct arphdr, ar_pro), 2, + NFT_PAYLOAD_NETWORK_HEADER, ®); + if (fw->arp.arpro_mask != 0xffff) + add_bitwise_u16(h, r, fw->arp.arpro_mask, 0, reg, ®); + add_cmp_u16(r, fw->arp.arpro, op, reg); } if (fw->arp.arhln != 0 || - fw->arp.invflags & ARPT_INV_ARPHLN) { - op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPHLN); - add_proto(r, offsetof(struct arphdr, ar_hln), 1, - fw->arp.arhln, op); + fw->arp.arhln_mask != 255 || + fw->arp.invflags & IPT_INV_ARPHLN) { + uint8_t reg; + + op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_ARPHLN); + add_payload(h, r, offsetof(struct arphdr, ar_hln), 1, + NFT_PAYLOAD_NETWORK_HEADER, ®); + if (fw->arp.arhln_mask != 255) + add_bitwise(h, r, &fw->arp.arhln_mask, 1, reg, ®); + add_cmp_u8(r, fw->arp.arhln, op, reg); } - add_proto(r, offsetof(struct arphdr, ar_pln), 1, 4, NFT_CMP_EQ); + add_proto(h, r, offsetof(struct arphdr, ar_pln), 1, 4, NFT_CMP_EQ); if (fw->arp.arpop != 0 || - fw->arp.invflags & ARPT_INV_ARPOP) { - 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); + fw->arp.arpop_mask != 0xffff || + fw->arp.invflags & IPT_INV_ARPOP) { + uint8_t reg; + + op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_ARPOP); + add_payload(h, r, offsetof(struct arphdr, ar_op), 2, + NFT_PAYLOAD_NETWORK_HEADER, ®); + if (fw->arp.arpop_mask != 0xffff) + add_bitwise_u16(h, r, fw->arp.arpop_mask, 0, reg, ®); + add_cmp_u16(r, fw->arp.arpop, op, reg); } if (need_devaddr(&fw->arp.src_devaddr)) { - op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCDEVADDR); - add_addr(r, sizeof(struct arphdr), + op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_SRCDEVADDR); + add_addr(h, r, NFT_PAYLOAD_NETWORK_HEADER, + sizeof(struct arphdr), &fw->arp.src_devaddr.addr, &fw->arp.src_devaddr.mask, fw->arp.arhln, op); @@ -187,17 +124,19 @@ static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, void *data) if (fw->arp.src.s_addr != 0 || fw->arp.smsk.s_addr != 0 || - fw->arp.invflags & ARPT_INV_SRCIP) { - op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCIP); - add_addr(r, sizeof(struct arphdr) + fw->arp.arhln, + fw->arp.invflags & IPT_INV_SRCIP) { + op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_SRCIP); + add_addr(h, r, NFT_PAYLOAD_NETWORK_HEADER, + sizeof(struct arphdr) + fw->arp.arhln, &fw->arp.src.s_addr, &fw->arp.smsk.s_addr, sizeof(struct in_addr), op); } if (need_devaddr(&fw->arp.tgt_devaddr)) { - op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTDEVADDR); - add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr), + op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_TGTDEVADDR); + add_addr(h, r, NFT_PAYLOAD_NETWORK_HEADER, + sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr), &fw->arp.tgt_devaddr.addr, &fw->arp.tgt_devaddr.mask, fw->arp.arhln, op); @@ -205,9 +144,10 @@ static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, void *data) if (fw->arp.tgt.s_addr != 0 || fw->arp.tmsk.s_addr != 0 || - fw->arp.invflags & ARPT_INV_TGTIP) { - op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTIP); - add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr) + fw->arp.arhln, + fw->arp.invflags & IPT_INV_DSTIP) { + op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_DSTIP); + add_addr(h, r, NFT_PAYLOAD_NETWORK_HEADER, + sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr) + fw->arp.arhln, &fw->arp.tgt.s_addr, &fw->arp.tmsk.s_addr, sizeof(struct in_addr), op); } @@ -236,168 +176,13 @@ static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, void *data) 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) -{ - struct iptables_command_state *cs = data; - struct arpt_entry *fw = &cs->arp; - 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) -{ - struct iptables_command_state *cs = data; - - cs->jumpto = jumpto; -} - -static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask) -{ - mask->s_addr = ctx->bitwise.mask[0]; -} - -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; -} - -static void nft_arp_parse_payload(struct nft_xt_ctx *ctx, - struct nftnl_expr *e, void *data) -{ - struct iptables_command_state *cs = data; - struct arpt_entry *fw = &cs->arp; - struct in_addr addr; - uint16_t ar_hrd, ar_pro, ar_op; - uint8_t ar_hln; - 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): - get_cmp_data(e, &ar_hln, sizeof(ar_hln), &inv); - fw->arp.arhln = ar_hln; - fw->arp.arhln_mask = 0xff; - if (inv) - fw->arp.invflags |= ARPT_INV_ARPOP; - break; - default: - 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) + - 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)) { - 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) { - 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, - bool basechain, uint32_t refs, - uint32_t entries) + int refs, uint32_t entries) { printf("Chain %s", chain); - if (basechain && pol) { + if (pol) { printf(" (policy %s", pol); if (!(format & FMT_NOCOUNTS)) { fputc(' ', stdout); @@ -408,7 +193,7 @@ static void nft_arp_print_header(unsigned int format, const char *chain, } printf(")\n"); } else { - printf(" (%u references)\n", refs); + printf(" (%d references)\n", refs); } } @@ -416,7 +201,6 @@ static void nft_arp_print_rule_details(const struct iptables_command_state *cs, unsigned int format) { const struct arpt_entry *fw = &cs->arp; - char buf[BUFSIZ]; char iface[IFNAMSIZ+2]; const char *sep = ""; int print_iface = 0; @@ -439,7 +223,7 @@ static void nft_arp_print_rule_details(const struct iptables_command_state *cs, else strcat(iface, "any"); } if (print_iface) { - printf("%s%s-i %s", sep, fw->arp.invflags & ARPT_INV_VIA_IN ? + printf("%s%s-i %s", sep, fw->arp.invflags & IPT_INV_VIA_IN ? "! " : "", iface); sep = " "; } @@ -457,21 +241,16 @@ static void nft_arp_print_rule_details(const struct iptables_command_state *cs, else strcat(iface, "any"); } if (print_iface) { - printf("%s%s-o %s", sep, fw->arp.invflags & ARPT_INV_VIA_OUT ? + printf("%s%s-o %s", sep, fw->arp.invflags & IPT_INV_VIA_OUT ? "! " : "", iface); sep = " "; } if (fw->arp.smsk.s_addr != 0L) { - printf("%s%s", sep, 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))); - strncat(buf, mask_to_dotted(&(fw->arp.smsk)), - sizeof(buf) - strlen(buf) - 1); - printf("-s %s", buf); + printf("%s%s-s %s", sep, + fw->arp.invflags & IPT_INV_SRCIP ? "! " : "", + ipv4_addr_to_string(&fw->arp.src, + &fw->arp.smsk, format)); sep = " "; } @@ -480,7 +259,7 @@ static void nft_arp_print_rule_details(const struct iptables_command_state *cs, break; if (i == ARPT_DEV_ADDR_LEN_MAX) goto after_devsrc; - printf("%s%s", sep, fw->arp.invflags & ARPT_INV_SRCDEVADDR + printf("%s%s", sep, fw->arp.invflags & IPT_INV_SRCDEVADDR ? "! " : ""); printf("--src-mac "); xtables_print_mac_and_mask((unsigned char *)fw->arp.src_devaddr.addr, @@ -489,15 +268,10 @@ static void nft_arp_print_rule_details(const struct iptables_command_state *cs, after_devsrc: if (fw->arp.tmsk.s_addr != 0L) { - printf("%s%s", sep, 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))); - strncat(buf, mask_to_dotted(&(fw->arp.tmsk)), - sizeof(buf) - strlen(buf) - 1); - printf("-d %s", buf); + printf("%s%s-d %s", sep, + fw->arp.invflags & IPT_INV_DSTIP ? "! " : "", + ipv4_addr_to_string(&fw->arp.tgt, + &fw->arp.tmsk, format)); sep = " "; } @@ -506,7 +280,7 @@ after_devsrc: break; if (i == ARPT_DEV_ADDR_LEN_MAX) goto after_devdst; - printf("%s%s", sep, fw->arp.invflags & ARPT_INV_TGTDEVADDR + printf("%s%s", sep, fw->arp.invflags & IPT_INV_TGTDEVADDR ? "! " : ""); printf("--dst-mac "); xtables_print_mac_and_mask((unsigned char *)fw->arp.tgt_devaddr.addr, @@ -515,8 +289,9 @@ after_devsrc: after_devdst: - if (fw->arp.arhln_mask != 255 || fw->arp.arhln != 6) { - printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPHLN + if (fw->arp.arhln_mask != 255 || fw->arp.arhln != 6 || + fw->arp.invflags & IPT_INV_ARPHLN) { + printf("%s%s", sep, fw->arp.invflags & IPT_INV_ARPHLN ? "! " : ""); printf("--h-length %d", fw->arp.arhln); if (fw->arp.arhln_mask != 255) @@ -527,9 +302,9 @@ after_devdst: if (fw->arp.arpop_mask != 0) { int tmp = ntohs(fw->arp.arpop); - printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPOP + printf("%s%s", sep, fw->arp.invflags & IPT_INV_ARPOP ? "! " : ""); - if (tmp <= NUMOPCODES && !(format & FMT_NUMERIC)) + if (tmp <= ARP_NUMOPCODES && !(format & FMT_NUMERIC)) printf("--opcode %s", arp_opcodes[tmp-1]); else printf("--opcode %d", tmp); @@ -539,42 +314,42 @@ after_devdst: sep = " "; } - if (fw->arp.arhrd_mask != 65535 || fw->arp.arhrd != htons(1)) { + if (fw->arp.arhrd_mask != 65535 || fw->arp.arhrd != htons(1) || + fw->arp.invflags & IPT_INV_ARPHRD) { uint16_t tmp = ntohs(fw->arp.arhrd); - printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPHRD + printf("%s%s", sep, fw->arp.invflags & IPT_INV_ARPHRD ? "! " : ""); if (tmp == 1 && !(format & FMT_NUMERIC)) printf("--h-type %s", "Ethernet"); else - printf("--h-type %u", tmp); + printf("--h-type 0x%x", tmp); if (fw->arp.arhrd_mask != 65535) - printf("/%d", ntohs(fw->arp.arhrd_mask)); + printf("/0x%x", ntohs(fw->arp.arhrd_mask)); sep = " "; } if (fw->arp.arpro_mask != 0) { int tmp = ntohs(fw->arp.arpro); - printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPPRO + printf("%s%s", sep, fw->arp.invflags & IPT_INV_PROTO ? "! " : ""); 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("/0x%x", ntohs(fw->arp.arpro_mask)); sep = " "; } } static void -nft_arp_save_rule(const void *data, unsigned int format) +nft_arp_save_rule(const struct iptables_command_state *cs, unsigned int format) { - const struct iptables_command_state *cs = data; - format |= FMT_NUMERIC; + printf(" "); nft_arp_print_rule_details(cs, format); if (cs->target && cs->target->save) cs->target->save(&cs->fw, cs->target->t); @@ -604,13 +379,15 @@ nft_arp_print_rule(struct nft_handle *h, struct nftnl_rule *r, if (!(format & FMT_NONEWLINE)) fputc('\n', stdout); + + xtables_clear_iptables_command_state(&cs); } -static bool nft_arp_is_same(const void *data_a, - const void *data_b) +static bool nft_arp_is_same(const struct iptables_command_state *cs_a, + const struct iptables_command_state *cs_b) { - const struct arpt_entry *a = data_a; - const struct arpt_entry *b = data_b; + const struct arpt_entry *a = &cs_a->arp; + const struct arpt_entry *b = &cs_b->arp; if (a->arp.src.s_addr != b->arp.src.s_addr || a->arp.tgt.s_addr != b->arp.tgt.s_addr @@ -633,53 +410,463 @@ static bool nft_arp_is_same(const void *data_a, (unsigned char *)b->arp.outiface_mask); } -static bool nft_arp_rule_find(struct nft_handle *h, struct nftnl_rule *r, - void *data) +static void nft_arp_save_chain(const struct nftnl_chain *c, const char *policy) { - const struct iptables_command_state *cs = data; - struct iptables_command_state this = {}; - bool ret = false; + const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME); - /* Delete by matching rule case */ - nft_rule_to_iptables_command_state(h, r, &this); + printf(":%s %s\n", chain, policy ?: "-"); +} - if (!nft_arp_is_same(&cs->arp, &this.arp)) - goto out; +static int getlength_and_mask(const char *from, uint8_t *to, uint8_t *mask) +{ + char *dup = strdup(from); + char *p, *buffer; + int i, ret = -1; - if (!compare_targets(cs->target, this.target)) - goto out; + if (!dup) + return -1; - if (this.jumpto && strcmp(cs->jumpto, this.jumpto) != 0) - goto out; + if ( (p = strrchr(dup, '/')) != NULL) { + *p = '\0'; + i = strtol(p+1, &buffer, 10); + if (*buffer != '\0' || i < 0 || i > 255) + goto out_err; + *mask = (uint8_t)i; + } else + *mask = 255; + i = strtol(dup, &buffer, 10); + if (*buffer != '\0' || i < 0 || i > 255) + goto out_err; + *to = (uint8_t)i; + ret = 0; +out_err: + free(dup); + return ret; - ret = true; -out: - h->ops->clear_cs(&this); +} + +static int get16_and_mask(const char *from, uint16_t *to, + uint16_t *mask, int base) +{ + char *dup = strdup(from); + char *p, *buffer; + int i, ret = -1; + + if (!dup) + return -1; + + if ( (p = strrchr(dup, '/')) != NULL) { + *p = '\0'; + i = strtol(p+1, &buffer, base); + if (*buffer != '\0' || i < 0 || i > 65535) + goto out_err; + *mask = htons((uint16_t)i); + } else + *mask = 65535; + i = strtol(dup, &buffer, base); + if (*buffer != '\0' || i < 0 || i > 65535) + goto out_err; + *to = htons((uint16_t)i); + ret = 0; +out_err: + free(dup); return ret; } -static void nft_arp_save_chain(const struct nftnl_chain *c, const char *policy) +static void nft_arp_post_parse(int command, + struct iptables_command_state *cs, + struct xtables_args *args) { - const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME); + cs->arp.arp.invflags = args->invflags; - printf(":%s %s\n", chain, policy ?: "-"); + memcpy(cs->arp.arp.iniface, args->iniface, IFNAMSIZ); + memcpy(cs->arp.arp.iniface_mask, args->iniface_mask, IFNAMSIZ); + + memcpy(cs->arp.arp.outiface, args->outiface, IFNAMSIZ); + memcpy(cs->arp.arp.outiface_mask, args->outiface_mask, IFNAMSIZ); + + cs->arp.counters.pcnt = args->pcnt_cnt; + cs->arp.counters.bcnt = args->bcnt_cnt; + + if (command & (CMD_REPLACE | CMD_INSERT | + CMD_DELETE | CMD_APPEND | CMD_CHECK)) { + if (!(cs->options & OPT_DESTINATION)) + args->dhostnetworkmask = "0.0.0.0/0"; + if (!(cs->options & OPT_SOURCE)) + args->shostnetworkmask = "0.0.0.0/0"; + } + + if (args->shostnetworkmask) + xtables_ipparse_multiple(args->shostnetworkmask, + &args->s.addr.v4, &args->s.mask.v4, + &args->s.naddrs); + if (args->dhostnetworkmask) + xtables_ipparse_multiple(args->dhostnetworkmask, + &args->d.addr.v4, &args->d.mask.v4, + &args->d.naddrs); + + if ((args->s.naddrs > 1 || args->d.naddrs > 1) && + (cs->arp.arp.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) + xtables_error(PARAMETER_PROBLEM, + "! not allowed with multiple" + " source or destination IP addresses"); + + if (args->src_mac && + xtables_parse_mac_and_mask(args->src_mac, + cs->arp.arp.src_devaddr.addr, + cs->arp.arp.src_devaddr.mask)) + xtables_error(PARAMETER_PROBLEM, + "Problem with specified source mac"); + if (args->dst_mac && + xtables_parse_mac_and_mask(args->dst_mac, + cs->arp.arp.tgt_devaddr.addr, + cs->arp.arp.tgt_devaddr.mask)) + xtables_error(PARAMETER_PROBLEM, + "Problem with specified destination mac"); + if (args->arp_hlen) { + getlength_and_mask(args->arp_hlen, &cs->arp.arp.arhln, + &cs->arp.arp.arhln_mask); + + if (cs->arp.arp.arhln != 6) + xtables_error(PARAMETER_PROBLEM, + "Only hardware address length of 6 is supported currently."); + } + if (args->arp_opcode) { + if (get16_and_mask(args->arp_opcode, &cs->arp.arp.arpop, + &cs->arp.arp.arpop_mask, 10)) { + int i; + + for (i = 0; i < ARP_NUMOPCODES; i++) + if (!strcasecmp(arp_opcodes[i], + args->arp_opcode)) + break; + if (i == ARP_NUMOPCODES) + xtables_error(PARAMETER_PROBLEM, + "Problem with specified opcode"); + cs->arp.arp.arpop = htons(i+1); + } + } + if (args->arp_htype) { + if (get16_and_mask(args->arp_htype, &cs->arp.arp.arhrd, + &cs->arp.arp.arhrd_mask, 16)) { + if (strcasecmp(args->arp_htype, "Ethernet")) + xtables_error(PARAMETER_PROBLEM, + "Problem with specified hardware type"); + cs->arp.arp.arhrd = htons(1); + } + } + if (args->arp_ptype) { + if (get16_and_mask(args->arp_ptype, &cs->arp.arp.arpro, + &cs->arp.arp.arpro_mask, 0)) { + if (strcasecmp(args->arp_ptype, "ipv4")) + xtables_error(PARAMETER_PROBLEM, + "Problem with specified protocol type"); + cs->arp.arp.arpro = htons(0x800); + } + } +} + +static void nft_arp_init_cs(struct iptables_command_state *cs) +{ + cs->arp.arp.arhln = 6; + cs->arp.arp.arhln_mask = 255; + cs->arp.arp.arhrd = htons(ARPHRD_ETHER); + cs->arp.arp.arhrd_mask = 65535; + cs->arp.arp.arpop_mask = 65535; + cs->arp.arp.arpro_mask = 65535; +} + +static int +nft_arp_add_entry(struct nft_handle *h, + const char *chain, const char *table, + struct iptables_command_state *cs, + struct xtables_args *args, bool verbose, + bool append, int rulenum) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < args->s.naddrs; i++) { + cs->arp.arp.src.s_addr = args->s.addr.v4[i].s_addr; + cs->arp.arp.smsk.s_addr = args->s.mask.v4[i].s_addr; + for (j = 0; j < args->d.naddrs; j++) { + cs->arp.arp.tgt.s_addr = args->d.addr.v4[j].s_addr; + cs->arp.arp.tmsk.s_addr = args->d.mask.v4[j].s_addr; + if (append) { + ret = nft_cmd_rule_append(h, chain, table, cs, + verbose); + } else { + ret = nft_cmd_rule_insert(h, chain, table, cs, + rulenum, verbose); + } + } + } + + return ret; +} + +static int +nft_arp_delete_entry(struct nft_handle *h, + const char *chain, const char *table, + struct iptables_command_state *cs, + struct xtables_args *args, bool verbose) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < args->s.naddrs; i++) { + cs->arp.arp.src.s_addr = args->s.addr.v4[i].s_addr; + cs->arp.arp.smsk.s_addr = args->s.mask.v4[i].s_addr; + for (j = 0; j < args->d.naddrs; j++) { + cs->arp.arp.tgt.s_addr = args->d.addr.v4[j].s_addr; + cs->arp.arp.tmsk.s_addr = args->d.mask.v4[j].s_addr; + ret = nft_cmd_rule_delete(h, chain, table, cs, verbose); + } + } + + return ret; +} + +static int +nft_arp_check_entry(struct nft_handle *h, + const char *chain, const char *table, + struct iptables_command_state *cs, + struct xtables_args *args, bool verbose) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < args->s.naddrs; i++) { + cs->arp.arp.src.s_addr = args->s.addr.v4[i].s_addr; + cs->arp.arp.smsk.s_addr = args->s.mask.v4[i].s_addr; + for (j = 0; j < args->d.naddrs; j++) { + cs->arp.arp.tgt.s_addr = args->d.addr.v4[j].s_addr; + cs->arp.arp.tmsk.s_addr = args->d.mask.v4[j].s_addr; + ret = nft_cmd_rule_check(h, chain, table, cs, verbose); + } + } + + return ret; +} + +static int +nft_arp_replace_entry(struct nft_handle *h, + const char *chain, const char *table, + struct iptables_command_state *cs, + struct xtables_args *args, bool verbose, + int rulenum) +{ + cs->arp.arp.src.s_addr = args->s.addr.v4->s_addr; + cs->arp.arp.tgt.s_addr = args->d.addr.v4->s_addr; + cs->arp.arp.smsk.s_addr = args->s.mask.v4->s_addr; + cs->arp.arp.tmsk.s_addr = args->d.mask.v4->s_addr; + + return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose); +} + +static void nft_arp_xlate_mac_and_mask(const struct arpt_devaddr_info *devaddr, + const char *addr, + bool invert, + struct xt_xlate *xl) +{ + unsigned int i; + + for (i = 0; i < 6; ++i) { + if (devaddr->mask[i]) + break; + } + + if (i == 6) + return; + + xt_xlate_add(xl, "arp %s ether ", addr); + if (invert) + xt_xlate_add(xl, "!= "); + + xt_xlate_add(xl, "%02x", (uint8_t)devaddr->addr[0]); + for (i = 1; i < 6; ++i) + xt_xlate_add(xl, ":%02x", (uint8_t)devaddr->addr[i]); + + for (i = 0; i < 6; ++i) { + int j; + + if ((uint8_t)devaddr->mask[i] == 0xff) + continue; + + xt_xlate_add(xl, "/%02x", (uint8_t)devaddr->mask[0]); + + for (j = 1; j < 6; ++j) + xt_xlate_add(xl, ":%02x", (uint8_t)devaddr->mask[j]); + return; + } +} + +static void nft_arp_xlate16(uint16_t v, uint16_t m, const char *what, + bool hex, bool inverse, + struct xt_xlate *xl) +{ + const char *fmt = hex ? "0x%x " : "%d "; + + if (m) { + xt_xlate_add(xl, "arp %s ", what); + if (inverse) + xt_xlate_add(xl, " !="); + if (m != 0xffff) { + xt_xlate_add(xl, "& "); + xt_xlate_add(xl, fmt, ntohs(m));; + + } + xt_xlate_add(xl, fmt, ntohs(v)); + } +} + +static void nft_arp_xlate_ipv4_addr(const char *what, const struct in_addr *addr, + const struct in_addr *mask, + bool inv, struct xt_xlate *xl) +{ + char mbuf[INET_ADDRSTRLEN], abuf[INET_ADDRSTRLEN]; + const char *op = inv ? "!= " : ""; + int cidr; + + if (!inv && !addr->s_addr && !mask->s_addr) + return; + + inet_ntop(AF_INET, addr, abuf, sizeof(abuf)); + + cidr = xtables_ipmask_to_cidr(mask); + switch (cidr) { + case -1: + xt_xlate_add(xl, "arp %s ip & %s %s %s ", what, + inet_ntop(AF_INET, mask, mbuf, sizeof(mbuf)), + inv ? "!=" : "==", abuf); + break; + case 32: + xt_xlate_add(xl, "arp %s ip %s%s ", what, op, abuf); + break; + default: + xt_xlate_add(xl, "arp %s ip %s%s/%d ", what, op, abuf, cidr); + } +} + +static int nft_arp_xlate(const struct iptables_command_state *cs, + struct xt_xlate *xl) +{ + const struct arpt_entry *fw = &cs->arp; + int ret; + + xlate_ifname(xl, "iifname", fw->arp.iniface, + fw->arp.invflags & IPT_INV_VIA_IN); + xlate_ifname(xl, "oifname", fw->arp.outiface, + fw->arp.invflags & IPT_INV_VIA_OUT); + + if (fw->arp.arhrd || + fw->arp.arhrd_mask != 0xffff || + fw->arp.invflags & IPT_INV_ARPHRD) + nft_arp_xlate16(fw->arp.arhrd, fw->arp.arhrd_mask, + "htype", false, + fw->arp.invflags & IPT_INV_ARPHRD, xl); + + if (fw->arp.arhln_mask != 255 || fw->arp.arhln || + fw->arp.invflags & IPT_INV_ARPHLN) { + xt_xlate_add(xl, "arp hlen "); + if (fw->arp.invflags & IPT_INV_ARPHLN) + xt_xlate_add(xl, " !="); + if (fw->arp.arhln_mask != 255) + xt_xlate_add(xl, "& %d ", fw->arp.arhln_mask); + xt_xlate_add(xl, "%d ", fw->arp.arhln); + } + + /* added implicitly by arptables-nft */ + xt_xlate_add(xl, "arp plen %d", 4); + + if (fw->arp.arpop_mask != 65535 || + fw->arp.arpop != 0 || + fw->arp.invflags & IPT_INV_ARPOP) + nft_arp_xlate16(fw->arp.arpop, fw->arp.arpop_mask, + "operation", false, + fw->arp.invflags & IPT_INV_ARPOP, xl); + + if (fw->arp.arpro_mask != 65535 || + fw->arp.invflags & IPT_INV_PROTO || + fw->arp.arpro) + nft_arp_xlate16(fw->arp.arpro, fw->arp.arpro_mask, + "ptype", true, + fw->arp.invflags & IPT_INV_PROTO, xl); + + if (fw->arp.smsk.s_addr != 0L) + nft_arp_xlate_ipv4_addr("saddr", &fw->arp.src, &fw->arp.smsk, + fw->arp.invflags & IPT_INV_SRCIP, xl); + + if (fw->arp.tmsk.s_addr != 0L) + nft_arp_xlate_ipv4_addr("daddr", &fw->arp.tgt, &fw->arp.tmsk, + fw->arp.invflags & IPT_INV_DSTIP, xl); + + nft_arp_xlate_mac_and_mask(&fw->arp.src_devaddr, "saddr", + fw->arp.invflags & IPT_INV_SRCDEVADDR, xl); + nft_arp_xlate_mac_and_mask(&fw->arp.tgt_devaddr, "daddr", + fw->arp.invflags & IPT_INV_TGTDEVADDR, xl); + + ret = xlate_matches(cs, xl); + if (!ret) + return ret; + + /* Always add counters per rule, as in iptables */ + xt_xlate_add(xl, "counter"); + return xlate_action(cs, false, xl); +} + +static const char *nft_arp_option_name(int option) +{ + switch (option) { + default: return ip46t_option_name(option); + /* different name than iptables */ + case OPT_SOURCE: return "--source-ip"; + case OPT_DESTINATION: return "--destination-ip"; + /* arptables specific ones */ + case OPT_S_MAC: return "--source-mac"; + case OPT_D_MAC: return "--destination-mac"; + case OPT_H_LENGTH: return "--h-length"; + case OPT_OPCODE: return "--opcode"; + case OPT_H_TYPE: return "--h-type"; + case OPT_P_TYPE: return "--proto-type"; + } +} + +static int nft_arp_option_invert(int option) +{ + switch (option) { + case OPT_S_MAC: return IPT_INV_SRCDEVADDR; + case OPT_D_MAC: return IPT_INV_TGTDEVADDR; + case OPT_H_LENGTH: return IPT_INV_ARPHLN; + case OPT_OPCODE: return IPT_INV_ARPOP; + case OPT_H_TYPE: return IPT_INV_ARPHRD; + case OPT_P_TYPE: return IPT_INV_PROTO; + default: return ip46t_option_invert(option); + } } 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, .print_rule = nft_arp_print_rule, .save_rule = nft_arp_save_rule, - .save_counters = save_counters, .save_chain = nft_arp_save_chain, - .post_parse = NULL, + .rule_parse = &nft_ruleparse_ops_arp, + .cmd_parse = { + .post_parse = nft_arp_post_parse, + .option_name = nft_arp_option_name, + .option_invert = nft_arp_option_invert, + .command_default = command_default, + .print_help = xtables_printhelp, + }, .rule_to_cs = nft_rule_to_iptables_command_state, - .clear_cs = nft_clear_iptables_command_state, - .rule_find = nft_arp_rule_find, - .parse_target = nft_ipv46_parse_target, + .init_cs = nft_arp_init_cs, + .clear_cs = xtables_clear_iptables_command_state, + .xlate = nft_arp_xlate, + .add_entry = nft_arp_add_entry, + .delete_entry = nft_arp_delete_entry, + .check_entry = nft_arp_check_entry, + .replace_entry = nft_arp_replace_entry, }; |