summaryrefslogtreecommitdiffstats
path: root/iptables
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2018-11-06 17:52:10 +0100
committerFlorian Westphal <fw@strlen.de>2018-11-12 14:53:24 +0100
commit9677ed12a93f165fa9840fd9f8fa183390a3bd04 (patch)
tree02152da925a75a2468b2b60641d118ce28f34281 /iptables
parentab0b6d508caafc519b0d86a8c019c61c418240f7 (diff)
arptables: fix src/dst mac handling
1. check both address and mask, not just first byte of mac 2. use add_addr() for this so mask is also handled via bitwise expr. 3. use the correct offsets. 4. add dissector so we can reverse translate the payload expressions generated for this. Signed-off-by: Florian Westphal <fw@strlen.de>
Diffstat (limited to 'iptables')
-rw-r--r--iptables/nft-arp.c66
1 files changed, 57 insertions, 9 deletions
diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
index c8b52ae0..1a98996f 100644
--- a/iptables/nft-arp.c
+++ b/iptables/nft-arp.c
@@ -137,6 +137,18 @@ static void print_mac_and_mask(const unsigned char *mac, const unsigned char *ma
print_mac(mask, l);
}
+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;
+}
+
static int nft_arp_add(struct nftnl_rule *r, void *data)
{
struct iptables_command_state *cs = data;
@@ -187,11 +199,13 @@ static int nft_arp_add(struct nftnl_rule *r, void *data)
add_cmp_u16(r, fw->arp.arpop, op);
}
- if (fw->arp.src_devaddr.addr[0] != '\0') {
+ if (need_devaddr(&fw->arp.src_devaddr)) {
op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCDEVADDR);
- add_payload(r, sizeof(struct arphdr), fw->arp.arhln,
- NFT_PAYLOAD_NETWORK_HEADER);
- add_cmp_ptr(r, op, fw->arp.src_devaddr.addr, fw->arp.arhln);
+ add_addr(r, sizeof(struct arphdr),
+ &fw->arp.src_devaddr.addr,
+ &fw->arp.src_devaddr.mask,
+ fw->arp.arhln, op);
+
}
if (fw->arp.src.s_addr != 0 ||
@@ -203,11 +217,13 @@ static int nft_arp_add(struct nftnl_rule *r, void *data)
sizeof(struct in_addr), op);
}
- if (fw->arp.tgt_devaddr.addr[0] != '\0') {
+
+ if (need_devaddr(&fw->arp.tgt_devaddr)) {
op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTDEVADDR);
- add_payload(r, sizeof(struct arphdr) + fw->arp.arhln + 4,
- fw->arp.arhln, NFT_PAYLOAD_NETWORK_HEADER);
- add_cmp_ptr(r, op, fw->arp.tgt_devaddr.addr, fw->arp.arhln);
+ add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr),
+ &fw->arp.tgt_devaddr.addr,
+ &fw->arp.tgt_devaddr.mask,
+ fw->arp.arhln, op);
}
if (fw->arp.tgt.s_addr != 0 ||
@@ -292,6 +308,30 @@ 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)
{
@@ -331,7 +371,10 @@ static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
fw->arp.invflags |= ARPT_INV_ARPOP;
break;
default:
- if (ctx->payload.offset == sizeof(struct arphdr) +
+ 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;
@@ -346,6 +389,11 @@ static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
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);