Blame bootstrap_ver/iptables/nft-arp.c

Packit Service 2cd632
/*
Packit Service 2cd632
 * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
Packit Service 2cd632
 * (C) 2013 by Giuseppe Longo <giuseppelng@gmail.com>
Packit Service 2cd632
 *
Packit Service 2cd632
 * This program is free software; you can redistribute it and/or modify
Packit Service 2cd632
 * it under the terms of the GNU General Public License as published by
Packit Service 2cd632
 * the Free Software Foundation; either version 2 of the License, or
Packit Service 2cd632
 * (at your option) any later version.
Packit Service 2cd632
 *
Packit Service 2cd632
 * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
Packit Service 2cd632
 */
Packit Service 2cd632
Packit Service 2cd632
#include <stdio.h>
Packit Service 2cd632
#include <stdlib.h>
Packit Service 2cd632
#include <string.h>
Packit Service 2cd632
#include <netdb.h>
Packit Service 2cd632
#include <net/if_arp.h>
Packit Service 2cd632
Packit Service 2cd632
#include <xtables.h>
Packit Service 2cd632
#include <libiptc/libxtc.h>
Packit Service 2cd632
#include <net/if_arp.h>
Packit Service 2cd632
#include <netinet/if_ether.h>
Packit Service 2cd632
Packit Service 2cd632
#include <linux/netfilter_arp/arp_tables.h>
Packit Service 2cd632
#include <linux/netfilter/nf_tables.h>
Packit Service 2cd632
Packit Service 2cd632
#include "nft-shared.h"
Packit Service 2cd632
#include "nft-arp.h"
Packit Service 2cd632
#include "nft.h"
Packit Service 2cd632
Packit Service 2cd632
/* a few names */
Packit Service 2cd632
char *opcodes[] =
Packit Service 2cd632
{
Packit Service 2cd632
	"Request",
Packit Service 2cd632
	"Reply",
Packit Service 2cd632
	"Request_Reverse",
Packit Service 2cd632
	"Reply_Reverse",
Packit Service 2cd632
	"DRARP_Request",
Packit Service 2cd632
	"DRARP_Reply",
Packit Service 2cd632
	"DRARP_Error",
Packit Service 2cd632
	"InARP_Request",
Packit Service 2cd632
	"ARP_NAK",
Packit Service 2cd632
};
Packit Service 2cd632
Packit Service 2cd632
static char *
Packit Service 2cd632
addr_to_dotted(const struct in_addr *addrp)
Packit Service 2cd632
{
Packit Service 2cd632
	static char buf[20];
Packit Service 2cd632
	const unsigned char *bytep;
Packit Service 2cd632
Packit Service 2cd632
	bytep = (const unsigned char *) &(addrp->s_addr);
Packit Service 2cd632
	sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
Packit Service 2cd632
	return buf;
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static char *
Packit Service 2cd632
addr_to_host(const struct in_addr *addr)
Packit Service 2cd632
{
Packit Service 2cd632
	struct hostent *host;
Packit Service 2cd632
Packit Service 2cd632
	if ((host = gethostbyaddr((char *) addr,
Packit Service 2cd632
					sizeof(struct in_addr), AF_INET)) != NULL)
Packit Service 2cd632
		return (char *) host->h_name;
Packit Service 2cd632
Packit Service 2cd632
	return (char *) NULL;
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static char *
Packit Service 2cd632
addr_to_network(const struct in_addr *addr)
Packit Service 2cd632
{
Packit Service 2cd632
	struct netent *net;
Packit Service 2cd632
Packit Service 2cd632
	if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
Packit Service 2cd632
		return (char *) net->n_name;
Packit Service 2cd632
Packit Service 2cd632
	return (char *) NULL;
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static char *
Packit Service 2cd632
addr_to_anyname(const struct in_addr *addr)
Packit Service 2cd632
{
Packit Service 2cd632
	char *name;
Packit Service 2cd632
Packit Service 2cd632
	if ((name = addr_to_host(addr)) != NULL ||
Packit Service 2cd632
		(name = addr_to_network(addr)) != NULL)
Packit Service 2cd632
		return name;
Packit Service 2cd632
Packit Service 2cd632
	return addr_to_dotted(addr);
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static char *
Packit Service 2cd632
mask_to_dotted(const struct in_addr *mask)
Packit Service 2cd632
{
Packit Service 2cd632
	int i;
Packit Service 2cd632
	static char buf[22];
Packit Service 2cd632
	u_int32_t maskaddr, bits;
Packit Service 2cd632
Packit Service 2cd632
	maskaddr = ntohl(mask->s_addr);
Packit Service 2cd632
Packit Service 2cd632
	if (maskaddr == 0xFFFFFFFFL)
Packit Service 2cd632
		/* we don't want to see "/32" */
Packit Service 2cd632
		return "";
Packit Service 2cd632
Packit Service 2cd632
	i = 32;
Packit Service 2cd632
	bits = 0xFFFFFFFEL;
Packit Service 2cd632
	while (--i >= 0 && maskaddr != bits)
Packit Service 2cd632
		bits <<= 1;
Packit Service 2cd632
	if (i >= 0)
Packit Service 2cd632
		sprintf(buf, "/%d", i);
Packit Service 2cd632
	else
Packit Service 2cd632
		/* mask was not a decent combination of 1's and 0's */
Packit Service 2cd632
		snprintf(buf, sizeof(buf), "/%s", addr_to_dotted(mask));
Packit Service 2cd632
Packit Service 2cd632
	return buf;
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void print_mac(const unsigned char *mac, int l)
Packit Service 2cd632
{
Packit Service 2cd632
	int j;
Packit Service 2cd632
Packit Service 2cd632
	for (j = 0; j < l; j++)
Packit Service 2cd632
		printf("%02x%s", mac[j],
Packit Service 2cd632
			(j==l-1) ? "" : ":");
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void print_mac_and_mask(const unsigned char *mac, const unsigned char *mask, int l)
Packit Service 2cd632
{
Packit Service 2cd632
	int i;
Packit Service 2cd632
Packit Service 2cd632
	print_mac(mac, l);
Packit Service 2cd632
	for (i = 0; i < l ; i++)
Packit Service 2cd632
		if (mask[i] != 255)
Packit Service 2cd632
			break;
Packit Service 2cd632
	if (i == l)
Packit Service 2cd632
		return;
Packit Service 2cd632
	printf("/");
Packit Service 2cd632
	print_mac(mask, l);
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static bool need_devaddr(struct arpt_devaddr_info *info)
Packit Service 2cd632
{
Packit Service 2cd632
	int i;
Packit Service 2cd632
Packit Service 2cd632
	for (i = 0; i < ETH_ALEN; i++) {
Packit Service 2cd632
		if (info->addr[i] || info->mask[i])
Packit Service 2cd632
			return true;
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	return false;
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static int nft_arp_add(struct nftnl_rule *r, void *data)
Packit Service 2cd632
{
Packit Service 2cd632
	struct iptables_command_state *cs = data;
Packit Service 2cd632
	struct arpt_entry *fw = &cs->arp;
Packit Service 2cd632
	uint32_t op;
Packit Service 2cd632
	int ret = 0;
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.iniface[0] != '\0') {
Packit Service 2cd632
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_VIA_IN);
Packit Service 2cd632
		add_iniface(r, fw->arp.iniface, op);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.outiface[0] != '\0') {
Packit Service 2cd632
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_VIA_OUT);
Packit Service 2cd632
		add_outiface(r, fw->arp.outiface, op);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.arhrd != 0 ||
Packit Service 2cd632
	    fw->arp.invflags & ARPT_INV_ARPHRD) {
Packit Service 2cd632
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPHRD);
Packit Service 2cd632
		add_payload(r, offsetof(struct arphdr, ar_hrd), 2,
Packit Service 2cd632
			    NFT_PAYLOAD_NETWORK_HEADER);
Packit Service 2cd632
		add_cmp_u16(r, fw->arp.arhrd, op);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.arpro != 0 ||
Packit Service 2cd632
	    fw->arp.invflags & ARPT_INV_ARPPRO) {
Packit Service 2cd632
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPPRO);
Packit Service 2cd632
	        add_payload(r, offsetof(struct arphdr, ar_pro), 2,
Packit Service 2cd632
			    NFT_PAYLOAD_NETWORK_HEADER);
Packit Service 2cd632
		add_cmp_u16(r, fw->arp.arpro, op);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.arhln != 0 ||
Packit Service 2cd632
	    fw->arp.invflags & ARPT_INV_ARPHLN) {
Packit Service 2cd632
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPHLN);
Packit Service 2cd632
		add_proto(r, offsetof(struct arphdr, ar_hln), 1,
Packit Service 2cd632
			  fw->arp.arhln, op);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	add_proto(r, offsetof(struct arphdr, ar_pln), 1, 4, NFT_CMP_EQ);
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.arpop != 0 ||
Packit Service 2cd632
	    fw->arp.invflags & ARPT_INV_ARPOP) {
Packit Service 2cd632
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_ARPOP);
Packit Service 2cd632
		add_payload(r, offsetof(struct arphdr, ar_op), 2,
Packit Service 2cd632
			    NFT_PAYLOAD_NETWORK_HEADER);
Packit Service 2cd632
		add_cmp_u16(r, fw->arp.arpop, op);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (need_devaddr(&fw->arp.src_devaddr)) {
Packit Service 2cd632
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCDEVADDR);
Packit Service 2cd632
		add_addr(r, sizeof(struct arphdr),
Packit Service 2cd632
			 &fw->arp.src_devaddr.addr,
Packit Service 2cd632
			 &fw->arp.src_devaddr.mask,
Packit Service 2cd632
			 fw->arp.arhln, op);
Packit Service 2cd632
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.src.s_addr != 0 ||
Packit Service 2cd632
	    fw->arp.smsk.s_addr != 0 ||
Packit Service 2cd632
	    fw->arp.invflags & ARPT_INV_SRCIP) {
Packit Service 2cd632
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCIP);
Packit Service 2cd632
		add_addr(r, sizeof(struct arphdr) + fw->arp.arhln,
Packit Service 2cd632
			 &fw->arp.src.s_addr, &fw->arp.smsk.s_addr,
Packit Service 2cd632
			 sizeof(struct in_addr), op);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
Packit Service 2cd632
	if (need_devaddr(&fw->arp.tgt_devaddr)) {
Packit Service 2cd632
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTDEVADDR);
Packit Service 2cd632
		add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr),
Packit Service 2cd632
			 &fw->arp.tgt_devaddr.addr,
Packit Service 2cd632
			 &fw->arp.tgt_devaddr.mask,
Packit Service 2cd632
			 fw->arp.arhln, op);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.tgt.s_addr != 0 ||
Packit Service 2cd632
	    fw->arp.tmsk.s_addr != 0 ||
Packit Service 2cd632
	    fw->arp.invflags & ARPT_INV_TGTIP) {
Packit Service 2cd632
		op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTIP);
Packit Service 2cd632
		add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr) + fw->arp.arhln,
Packit Service 2cd632
			 &fw->arp.tgt.s_addr, &fw->arp.tmsk.s_addr,
Packit Service 2cd632
			 sizeof(struct in_addr), op);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	/* Counters need to me added before the target, otherwise they are
Packit Service 2cd632
	 * increased for each rule because of the way nf_tables works.
Packit Service 2cd632
	 */
Packit Service 2cd632
	if (add_counters(r, fw->counters.pcnt, fw->counters.bcnt) < 0)
Packit Service 2cd632
		return -1;
Packit Service 2cd632
Packit Service 2cd632
	if (cs->target != NULL) {
Packit Service 2cd632
		/* Standard target? */
Packit Service 2cd632
		if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
Packit Service 2cd632
			ret = add_verdict(r, NF_ACCEPT);
Packit Service 2cd632
		else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
Packit Service 2cd632
			ret = add_verdict(r, NF_DROP);
Packit Service 2cd632
		else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
Packit Service 2cd632
			ret = add_verdict(r, NFT_RETURN);
Packit Service 2cd632
		else
Packit Service 2cd632
			ret = add_target(r, cs->target->t);
Packit Service 2cd632
	} else if (strlen(cs->jumpto) > 0) {
Packit Service 2cd632
		/* No goto in arptables */
Packit Service 2cd632
		ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	return ret;
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static uint16_t ipt_to_arpt_flags(uint8_t invflags)
Packit Service 2cd632
{
Packit Service 2cd632
	uint16_t result = 0;
Packit Service 2cd632
Packit Service 2cd632
	if (invflags & IPT_INV_VIA_IN)
Packit Service 2cd632
		result |= ARPT_INV_VIA_IN;
Packit Service 2cd632
Packit Service 2cd632
	if (invflags & IPT_INV_VIA_OUT)
Packit Service 2cd632
		result |= ARPT_INV_VIA_OUT;
Packit Service 2cd632
Packit Service 2cd632
	if (invflags & IPT_INV_SRCIP)
Packit Service 2cd632
		result |= ARPT_INV_SRCIP;
Packit Service 2cd632
Packit Service 2cd632
	if (invflags & IPT_INV_DSTIP)
Packit Service 2cd632
		result |= ARPT_INV_TGTIP;
Packit Service 2cd632
Packit Service 2cd632
	if (invflags & IPT_INV_PROTO)
Packit Service 2cd632
		result |= ARPT_INV_ARPPRO;
Packit Service 2cd632
Packit Service 2cd632
	return result;
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void nft_arp_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
Packit Service 2cd632
			       void *data)
Packit Service 2cd632
{
Packit Service 2cd632
	struct iptables_command_state *cs = data;
Packit Service 2cd632
	struct arpt_entry *fw = &cs->arp;
Packit Service 2cd632
	uint8_t flags = 0;
Packit Service 2cd632
Packit Service 2cd632
	parse_meta(e, ctx->meta.key, fw->arp.iniface, fw->arp.iniface_mask,
Packit Service 2cd632
		   fw->arp.outiface, fw->arp.outiface_mask,
Packit Service 2cd632
		   &flags);
Packit Service 2cd632
Packit Service 2cd632
	fw->arp.invflags |= ipt_to_arpt_flags(flags);
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto,
Packit Service 2cd632
				    void *data)
Packit Service 2cd632
{
Packit Service 2cd632
	struct iptables_command_state *cs = data;
Packit Service 2cd632
Packit Service 2cd632
	cs->jumpto = jumpto;
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask)
Packit Service 2cd632
{
Packit Service 2cd632
	mask->s_addr = ctx->bitwise.mask[0];
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static bool nft_arp_parse_devaddr(struct nft_xt_ctx *ctx,
Packit Service 2cd632
				  struct nftnl_expr *e,
Packit Service 2cd632
				  struct arpt_devaddr_info *info)
Packit Service 2cd632
{
Packit Service 2cd632
	uint32_t hlen;
Packit Service 2cd632
	bool inv;
Packit Service 2cd632
Packit Service 2cd632
	nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &hlen);
Packit Service 2cd632
Packit Service 2cd632
	if (hlen != ETH_ALEN)
Packit Service 2cd632
		return false;
Packit Service 2cd632
Packit Service 2cd632
	get_cmp_data(e, info->addr, ETH_ALEN, &inv;;
Packit Service 2cd632
Packit Service 2cd632
	if (ctx->flags & NFT_XT_CTX_BITWISE) {
Packit Service 2cd632
		memcpy(info->mask, ctx->bitwise.mask, ETH_ALEN);
Packit Service 2cd632
		ctx->flags &= ~NFT_XT_CTX_BITWISE;
Packit Service 2cd632
	} else {
Packit Service 2cd632
		memset(info->mask, 0xff, ETH_ALEN);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	return inv;
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
Packit Service 2cd632
				  struct nftnl_expr *e, void *data)
Packit Service 2cd632
{
Packit Service 2cd632
	struct iptables_command_state *cs = data;
Packit Service 2cd632
	struct arpt_entry *fw = &cs->arp;
Packit Service 2cd632
	struct in_addr addr;
Packit Service 2cd632
	unsigned short int ar_hrd, ar_pro, ar_op, ar_hln;
Packit Service 2cd632
	bool inv;
Packit Service 2cd632
Packit Service 2cd632
	switch (ctx->payload.offset) {
Packit Service 2cd632
	case offsetof(struct arphdr, ar_hrd):
Packit Service 2cd632
		get_cmp_data(e, &ar_hrd, sizeof(ar_hrd), &inv;;
Packit Service 2cd632
		fw->arp.arhrd = ar_hrd;
Packit Service 2cd632
		fw->arp.arhrd_mask = 0xffff;
Packit Service 2cd632
		if (inv)
Packit Service 2cd632
			fw->arp.invflags |= ARPT_INV_ARPHRD;
Packit Service 2cd632
		break;
Packit Service 2cd632
	case offsetof(struct arphdr, ar_pro):
Packit Service 2cd632
		get_cmp_data(e, &ar_pro, sizeof(ar_pro), &inv;;
Packit Service 2cd632
		fw->arp.arpro = ar_pro;
Packit Service 2cd632
		fw->arp.arpro_mask = 0xffff;
Packit Service 2cd632
		if (inv)
Packit Service 2cd632
			fw->arp.invflags |= ARPT_INV_ARPPRO;
Packit Service 2cd632
		break;
Packit Service 2cd632
	case offsetof(struct arphdr, ar_op):
Packit Service 2cd632
		get_cmp_data(e, &ar_op, sizeof(ar_op), &inv;;
Packit Service 2cd632
		fw->arp.arpop = ar_op;
Packit Service 2cd632
		fw->arp.arpop_mask = 0xffff;
Packit Service 2cd632
		if (inv)
Packit Service 2cd632
			fw->arp.invflags |= ARPT_INV_ARPOP;
Packit Service 2cd632
		break;
Packit Service 2cd632
	case offsetof(struct arphdr, ar_hln):
Packit Service 2cd632
		get_cmp_data(e, &ar_hln, sizeof(ar_op), &inv;;
Packit Service 2cd632
		fw->arp.arhln = ar_hln;
Packit Service 2cd632
		fw->arp.arhln_mask = 0xff;
Packit Service 2cd632
		if (inv)
Packit Service 2cd632
			fw->arp.invflags |= ARPT_INV_ARPOP;
Packit Service 2cd632
		break;
Packit Service 2cd632
	default:
Packit Service 2cd632
		if (ctx->payload.offset == sizeof(struct arphdr)) {
Packit Service 2cd632
			if (nft_arp_parse_devaddr(ctx, e, &fw->arp.src_devaddr))
Packit Service 2cd632
				fw->arp.invflags |= ARPT_INV_SRCDEVADDR;
Packit Service 2cd632
		} else if (ctx->payload.offset == sizeof(struct arphdr) +
Packit Service 2cd632
					   fw->arp.arhln) {
Packit Service 2cd632
			get_cmp_data(e, &addr, sizeof(addr), &inv;;
Packit Service 2cd632
			fw->arp.src.s_addr = addr.s_addr;
Packit Service 2cd632
			if (ctx->flags & NFT_XT_CTX_BITWISE) {
Packit Service 2cd632
				parse_mask_ipv4(ctx, &fw->arp.smsk);
Packit Service 2cd632
				ctx->flags &= ~NFT_XT_CTX_BITWISE;
Packit Service 2cd632
			} else {
Packit Service 2cd632
				fw->arp.smsk.s_addr = 0xffffffff;
Packit Service 2cd632
			}
Packit Service 2cd632
Packit Service 2cd632
			if (inv)
Packit Service 2cd632
				fw->arp.invflags |= ARPT_INV_SRCIP;
Packit Service 2cd632
		} else if (ctx->payload.offset == sizeof(struct arphdr) +
Packit Service 2cd632
						  fw->arp.arhln +
Packit Service 2cd632
						  sizeof(struct in_addr)) {
Packit Service 2cd632
			if (nft_arp_parse_devaddr(ctx, e, &fw->arp.tgt_devaddr))
Packit Service 2cd632
				fw->arp.invflags |= ARPT_INV_TGTDEVADDR;
Packit Service 2cd632
		} else if (ctx->payload.offset == sizeof(struct arphdr) +
Packit Service 2cd632
						  fw->arp.arhln +
Packit Service 2cd632
						  sizeof(struct in_addr) +
Packit Service 2cd632
						  fw->arp.arhln) {
Packit Service 2cd632
			get_cmp_data(e, &addr, sizeof(addr), &inv;;
Packit Service 2cd632
			fw->arp.tgt.s_addr = addr.s_addr;
Packit Service 2cd632
			if (ctx->flags & NFT_XT_CTX_BITWISE) {
Packit Service 2cd632
				parse_mask_ipv4(ctx, &fw->arp.tmsk);
Packit Service 2cd632
				ctx->flags &= ~NFT_XT_CTX_BITWISE;
Packit Service 2cd632
			} else {
Packit Service 2cd632
				fw->arp.tmsk.s_addr = 0xffffffff;
Packit Service 2cd632
			}
Packit Service 2cd632
Packit Service 2cd632
			if (inv)
Packit Service 2cd632
				fw->arp.invflags |= ARPT_INV_TGTIP;
Packit Service 2cd632
		}
Packit Service 2cd632
		break;
Packit Service 2cd632
	}
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void nft_arp_rule_to_cs(const struct nftnl_rule *r,
Packit Service 2cd632
			       struct iptables_command_state *cs)
Packit Service 2cd632
{
Packit Service 2cd632
	struct nftnl_expr_iter *iter;
Packit Service 2cd632
	struct nftnl_expr *expr;
Packit Service 2cd632
	int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
Packit Service 2cd632
	struct nft_xt_ctx ctx = {
Packit Service 2cd632
		.cs = cs,
Packit Service 2cd632
		.family = family,
Packit Service 2cd632
	};
Packit Service 2cd632
Packit Service 2cd632
	iter = nftnl_expr_iter_create(r);
Packit Service 2cd632
	if (iter == NULL)
Packit Service 2cd632
		return;
Packit Service 2cd632
Packit Service 2cd632
	ctx.iter = iter;
Packit Service 2cd632
	expr = nftnl_expr_iter_next(iter);
Packit Service 2cd632
	while (expr != NULL) {
Packit Service 2cd632
		const char *name =
Packit Service 2cd632
			nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
Packit Service 2cd632
Packit Service 2cd632
		if (strcmp(name, "counter") == 0)
Packit Service 2cd632
			nft_parse_counter(expr, &ctx.cs->arp.counters);
Packit Service 2cd632
		else if (strcmp(name, "payload") == 0)
Packit Service 2cd632
			nft_parse_payload(&ctx, expr);
Packit Service 2cd632
		else if (strcmp(name, "meta") == 0)
Packit Service 2cd632
			nft_parse_meta(&ctx, expr);
Packit Service 2cd632
		else if (strcmp(name, "bitwise") == 0)
Packit Service 2cd632
			nft_parse_bitwise(&ctx, expr);
Packit Service 2cd632
		else if (strcmp(name, "cmp") == 0)
Packit Service 2cd632
			nft_parse_cmp(&ctx, expr);
Packit Service 2cd632
		else if (strcmp(name, "immediate") == 0)
Packit Service 2cd632
			nft_parse_immediate(&ctx, expr);
Packit Service 2cd632
		else if (strcmp(name, "target") == 0)
Packit Service 2cd632
			nft_parse_target(&ctx, expr);
Packit Service 2cd632
Packit Service 2cd632
		expr = nftnl_expr_iter_next(iter);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	nftnl_expr_iter_destroy(iter);
Packit Service 2cd632
Packit Service 2cd632
	if (cs->jumpto != NULL)
Packit Service 2cd632
		return;
Packit Service 2cd632
Packit Service 2cd632
	if (cs->target != NULL && cs->target->name != NULL)
Packit Service 2cd632
		cs->target = xtables_find_target(cs->target->name, XTF_TRY_LOAD);
Packit Service 2cd632
	else
Packit Service 2cd632
		cs->jumpto = "";
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void nft_arp_print_header(unsigned int format, const char *chain,
Packit Service 2cd632
				 const char *pol,
Packit Service 2cd632
				 const struct xt_counters *counters,
Packit Service 2cd632
				 bool basechain, uint32_t refs,
Packit Service 2cd632
				 uint32_t entries)
Packit Service 2cd632
{
Packit Service 2cd632
	printf("Chain %s", chain);
Packit Service 2cd632
	if (basechain && pol) {
Packit Service 2cd632
		printf(" (policy %s", pol);
Packit Service 2cd632
		if (!(format & FMT_NOCOUNTS)) {
Packit Service 2cd632
			fputc(' ', stdout);
Packit Service 2cd632
			xtables_print_num(counters->pcnt, (format|FMT_NOTABLE));
Packit Service 2cd632
			fputs("packets, ", stdout);
Packit Service 2cd632
			xtables_print_num(counters->bcnt, (format|FMT_NOTABLE));
Packit Service 2cd632
			fputs("bytes", stdout);
Packit Service 2cd632
		}
Packit Service 2cd632
		printf(")\n");
Packit Service 2cd632
	} else {
Packit Service 2cd632
		printf(" (%u references)\n", refs);
Packit Service 2cd632
	}
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void nft_arp_print_rule_details(const struct arpt_entry *fw,
Packit Service 2cd632
				       unsigned int format)
Packit Service 2cd632
{
Packit Service 2cd632
	char buf[BUFSIZ];
Packit Service 2cd632
	char iface[IFNAMSIZ+2];
Packit Service 2cd632
	int print_iface = 0;
Packit Service 2cd632
	int i;
Packit Service 2cd632
Packit Service 2cd632
	iface[0] = '\0';
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.iniface[0] != '\0') {
Packit Service 2cd632
		strcat(iface, fw->arp.iniface);
Packit Service 2cd632
		print_iface = 1;
Packit Service 2cd632
	}
Packit Service 2cd632
	else if (format & FMT_VIA) {
Packit Service 2cd632
		print_iface = 1;
Packit Service 2cd632
		if (format & FMT_NUMERIC) strcat(iface, "*");
Packit Service 2cd632
		else strcat(iface, "any");
Packit Service 2cd632
	}
Packit Service 2cd632
	if (print_iface)
Packit Service 2cd632
		printf("%s-i %s ", fw->arp.invflags & ARPT_INV_VIA_IN ?
Packit Service 2cd632
				   "! " : "", iface);
Packit Service 2cd632
Packit Service 2cd632
	print_iface = 0;
Packit Service 2cd632
	iface[0] = '\0';
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.outiface[0] != '\0') {
Packit Service 2cd632
		strcat(iface, fw->arp.outiface);
Packit Service 2cd632
		print_iface = 1;
Packit Service 2cd632
	}
Packit Service 2cd632
	else if (format & FMT_VIA) {
Packit Service 2cd632
		print_iface = 1;
Packit Service 2cd632
		if (format & FMT_NUMERIC) strcat(iface, "*");
Packit Service 2cd632
		else strcat(iface, "any");
Packit Service 2cd632
	}
Packit Service 2cd632
	if (print_iface)
Packit Service 2cd632
		printf("%s-o %s ", fw->arp.invflags & ARPT_INV_VIA_OUT ?
Packit Service 2cd632
				   "! " : "", iface);
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.smsk.s_addr != 0L) {
Packit Service 2cd632
		printf("%s", fw->arp.invflags & ARPT_INV_SRCIP
Packit Service 2cd632
			? "! " : "");
Packit Service 2cd632
		if (format & FMT_NUMERIC)
Packit Service 2cd632
			sprintf(buf, "%s", addr_to_dotted(&(fw->arp.src)));
Packit Service 2cd632
		else
Packit Service 2cd632
			sprintf(buf, "%s", addr_to_anyname(&(fw->arp.src)));
Packit Service 2cd632
		strncat(buf, mask_to_dotted(&(fw->arp.smsk)),
Packit Service 2cd632
			sizeof(buf) - strlen(buf) - 1);
Packit Service 2cd632
		printf("-s %s ", buf);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	for (i = 0; i < ARPT_DEV_ADDR_LEN_MAX; i++)
Packit Service 2cd632
		if (fw->arp.src_devaddr.mask[i] != 0)
Packit Service 2cd632
			break;
Packit Service 2cd632
	if (i == ARPT_DEV_ADDR_LEN_MAX)
Packit Service 2cd632
		goto after_devsrc;
Packit Service 2cd632
	printf("%s", fw->arp.invflags & ARPT_INV_SRCDEVADDR
Packit Service 2cd632
		? "! " : "");
Packit Service 2cd632
	printf("--src-mac ");
Packit Service 2cd632
	print_mac_and_mask((unsigned char *)fw->arp.src_devaddr.addr,
Packit Service 2cd632
		(unsigned char *)fw->arp.src_devaddr.mask, ETH_ALEN);
Packit Service 2cd632
	printf(" ");
Packit Service 2cd632
after_devsrc:
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.tmsk.s_addr != 0L) {
Packit Service 2cd632
		printf("%s", fw->arp.invflags & ARPT_INV_TGTIP
Packit Service 2cd632
			? "! " : "");
Packit Service 2cd632
		if (format & FMT_NUMERIC)
Packit Service 2cd632
			sprintf(buf, "%s", addr_to_dotted(&(fw->arp.tgt)));
Packit Service 2cd632
		else
Packit Service 2cd632
			sprintf(buf, "%s", addr_to_anyname(&(fw->arp.tgt)));
Packit Service 2cd632
		strncat(buf, mask_to_dotted(&(fw->arp.tmsk)),
Packit Service 2cd632
			sizeof(buf) - strlen(buf) - 1);
Packit Service 2cd632
		printf("-d %s ", buf);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	for (i = 0; i 
Packit Service 2cd632
		if (fw->arp.tgt_devaddr.mask[i] != 0)
Packit Service 2cd632
			break;
Packit Service 2cd632
	if (i == ARPT_DEV_ADDR_LEN_MAX)
Packit Service 2cd632
		goto after_devdst;
Packit Service 2cd632
	printf("%s", fw->arp.invflags & ARPT_INV_TGTDEVADDR
Packit Service 2cd632
		? "! " : "");
Packit Service 2cd632
	printf("--dst-mac ");
Packit Service 2cd632
	print_mac_and_mask((unsigned char *)fw->arp.tgt_devaddr.addr,
Packit Service 2cd632
		(unsigned char *)fw->arp.tgt_devaddr.mask, ETH_ALEN);
Packit Service 2cd632
	printf(" ");
Packit Service 2cd632
Packit Service 2cd632
after_devdst:
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.arhln_mask != 0) {
Packit Service 2cd632
		printf("%s", fw->arp.invflags & ARPT_INV_ARPHLN
Packit Service 2cd632
			? "! " : "");
Packit Service 2cd632
		printf("--h-length %d", fw->arp.arhln);
Packit Service 2cd632
		if (fw->arp.arhln_mask != 255)
Packit Service 2cd632
			printf("/%d", fw->arp.arhln_mask);
Packit Service 2cd632
		printf(" ");
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.arpop_mask != 0) {
Packit Service 2cd632
		int tmp = ntohs(fw->arp.arpop);
Packit Service 2cd632
Packit Service 2cd632
		printf("%s", fw->arp.invflags & ARPT_INV_ARPOP
Packit Service 2cd632
			? "! " : "");
Packit Service 2cd632
		if (tmp <= NUMOPCODES && !(format & FMT_NUMERIC))
Packit Service 2cd632
			printf("--opcode %s", opcodes[tmp-1]);
Packit Service 2cd632
		else
Packit Service 2cd632
			printf("--opcode %d", tmp);
Packit Service 2cd632
Packit Service 2cd632
		if (fw->arp.arpop_mask != 65535)
Packit Service 2cd632
			printf("/%d", ntohs(fw->arp.arpop_mask));
Packit Service 2cd632
		printf(" ");
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.arhrd_mask != 0) {
Packit Service 2cd632
		uint16_t tmp = ntohs(fw->arp.arhrd);
Packit Service 2cd632
Packit Service 2cd632
		printf("%s", fw->arp.invflags & ARPT_INV_ARPHRD
Packit Service 2cd632
			? "! " : "");
Packit Service 2cd632
		if (tmp == 1 && !(format & FMT_NUMERIC))
Packit Service 2cd632
			printf("--h-type %s", "Ethernet");
Packit Service 2cd632
		else
Packit Service 2cd632
			printf("--h-type %u", tmp);
Packit Service 2cd632
		if (fw->arp.arhrd_mask != 65535)
Packit Service 2cd632
			printf("/%d", ntohs(fw->arp.arhrd_mask));
Packit Service 2cd632
		printf(" ");
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (fw->arp.arpro_mask != 0) {
Packit Service 2cd632
		int tmp = ntohs(fw->arp.arpro);
Packit Service 2cd632
Packit Service 2cd632
		printf("%s", fw->arp.invflags & ARPT_INV_ARPPRO
Packit Service 2cd632
			? "! " : "");
Packit Service 2cd632
		if (tmp == 0x0800 && !(format & FMT_NUMERIC))
Packit Service 2cd632
			printf("--proto-type %s", "IPv4");
Packit Service 2cd632
		else
Packit Service 2cd632
			printf("--proto-type 0x%x", tmp);
Packit Service 2cd632
		if (fw->arp.arpro_mask != 65535)
Packit Service 2cd632
			printf("/%x", ntohs(fw->arp.arpro_mask));
Packit Service 2cd632
		printf(" ");
Packit Service 2cd632
	}
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void nft_arp_save_counters(const void *data)
Packit Service 2cd632
{
Packit Service 2cd632
	const struct iptables_command_state *cs = data;
Packit Service 2cd632
Packit Service 2cd632
	printf("[%llu:%llu] ", (unsigned long long)cs->arp.counters.pcnt,
Packit Service 2cd632
			       (unsigned long long)cs->arp.counters.bcnt);
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void
Packit Service 2cd632
nft_arp_save_rule(const void *data, unsigned int format)
Packit Service 2cd632
{
Packit Service 2cd632
	const struct iptables_command_state *cs = data;
Packit Service 2cd632
Packit Service 2cd632
	format |= FMT_NUMERIC;
Packit Service 2cd632
Packit Service 2cd632
	nft_arp_print_rule_details(&cs->arp, format);
Packit Service 2cd632
Packit Service 2cd632
	if (cs->jumpto != NULL && strcmp(cs->jumpto, "") != 0) {
Packit Service 2cd632
		printf("-j %s", cs->jumpto);
Packit Service 2cd632
	} else if (cs->target) {
Packit Service 2cd632
		printf("-j %s", cs->target->name);
Packit Service 2cd632
		if (cs->target->save != NULL)
Packit Service 2cd632
			cs->target->save(&cs->arp, cs->target->t);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (!(format & FMT_NONEWLINE))
Packit Service 2cd632
		fputc('\n', stdout);
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void
Packit Service 2cd632
nft_arp_print_rule(struct nftnl_rule *r, unsigned int num, unsigned int format)
Packit Service 2cd632
{
Packit Service 2cd632
	struct iptables_command_state cs = {};
Packit Service 2cd632
Packit Service 2cd632
	if (format & FMT_LINENUMBERS)
Packit Service 2cd632
		printf("%u ", num);
Packit Service 2cd632
Packit Service 2cd632
	nft_arp_rule_to_cs(r, &cs);
Packit Service 2cd632
Packit Service 2cd632
	nft_arp_print_rule_details(&cs.arp, format);
Packit Service 2cd632
Packit Service 2cd632
	if (cs.jumpto != NULL && strcmp(cs.jumpto, "") != 0) {
Packit Service 2cd632
		printf("-j %s", cs.jumpto);
Packit Service 2cd632
	} else if (cs.target) {
Packit Service 2cd632
		printf("-j %s", cs.target->name);
Packit Service 2cd632
		cs.target->print(&cs.arp, cs.target->t, format & FMT_NUMERIC);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (!(format & FMT_NOCOUNTS)) {
Packit Service 2cd632
		printf(", pcnt=");
Packit Service 2cd632
		xtables_print_num(cs.arp.counters.pcnt, format);
Packit Service 2cd632
		printf("-- bcnt=");
Packit Service 2cd632
		xtables_print_num(cs.arp.counters.bcnt, format);
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	if (!(format & FMT_NONEWLINE))
Packit Service 2cd632
		fputc('\n', stdout);
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static bool nft_arp_is_same(const void *data_a,
Packit Service 2cd632
			    const void *data_b)
Packit Service 2cd632
{
Packit Service 2cd632
	const struct arpt_entry *a = data_a;
Packit Service 2cd632
	const struct arpt_entry *b = data_b;
Packit Service 2cd632
Packit Service 2cd632
	if (a->arp.src.s_addr != b->arp.src.s_addr
Packit Service 2cd632
	    || a->arp.tgt.s_addr != b->arp.tgt.s_addr
Packit Service 2cd632
	    || a->arp.smsk.s_addr != b->arp.smsk.s_addr
Packit Service 2cd632
	    || a->arp.tmsk.s_addr != b->arp.tmsk.s_addr
Packit Service 2cd632
	    || a->arp.arpro != b->arp.arpro
Packit Service 2cd632
	    || a->arp.flags != b->arp.flags
Packit Service 2cd632
	    || a->arp.invflags != b->arp.invflags) {
Packit Service 2cd632
		DEBUGP("different src/dst/proto/flags/invflags\n");
Packit Service 2cd632
		return false;
Packit Service 2cd632
	}
Packit Service 2cd632
Packit Service 2cd632
	return is_same_interfaces(a->arp.iniface,
Packit Service 2cd632
				  a->arp.outiface,
Packit Service 2cd632
				  (unsigned char *)a->arp.iniface_mask,
Packit Service 2cd632
				  (unsigned char *)a->arp.outiface_mask,
Packit Service 2cd632
				  b->arp.iniface,
Packit Service 2cd632
				  b->arp.outiface,
Packit Service 2cd632
				  (unsigned char *)b->arp.iniface_mask,
Packit Service 2cd632
				  (unsigned char *)b->arp.outiface_mask);
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
Packit Service 2cd632
			      void *data)
Packit Service 2cd632
{
Packit Service 2cd632
	const struct iptables_command_state *cs = data;
Packit Service 2cd632
	struct iptables_command_state this = {};
Packit Service 2cd632
Packit Service 2cd632
	/* Delete by matching rule case */
Packit Service 2cd632
	nft_arp_rule_to_cs(r, &this;;
Packit Service 2cd632
Packit Service 2cd632
	if (!nft_arp_is_same(&cs->arp, &this.arp))
Packit Service 2cd632
		return false;
Packit Service 2cd632
Packit Service 2cd632
	if (!compare_targets(cs->target, this.target))
Packit Service 2cd632
		return false;
Packit Service 2cd632
Packit Service 2cd632
	if (this.jumpto && strcmp(cs->jumpto, this.jumpto) != 0)
Packit Service 2cd632
		return false;
Packit Service 2cd632
Packit Service 2cd632
	return true;
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
static void nft_arp_save_chain(const struct nftnl_chain *c, const char *policy)
Packit Service 2cd632
{
Packit Service 2cd632
	const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
Packit Service 2cd632
Packit Service 2cd632
	printf(":%s %s\n", chain, policy ?: "-");
Packit Service 2cd632
}
Packit Service 2cd632
Packit Service 2cd632
struct nft_family_ops nft_family_ops_arp = {
Packit Service 2cd632
	.add			= nft_arp_add,
Packit Service 2cd632
	.is_same		= nft_arp_is_same,
Packit Service 2cd632
	.print_payload		= NULL,
Packit Service 2cd632
	.parse_meta		= nft_arp_parse_meta,
Packit Service 2cd632
	.parse_payload		= nft_arp_parse_payload,
Packit Service 2cd632
	.parse_immediate	= nft_arp_parse_immediate,
Packit Service 2cd632
	.print_header		= nft_arp_print_header,
Packit Service 2cd632
	.print_rule		= nft_arp_print_rule,
Packit Service 2cd632
	.save_rule		= nft_arp_save_rule,
Packit Service 2cd632
	.save_counters		= nft_arp_save_counters,
Packit Service 2cd632
	.save_chain		= nft_arp_save_chain,
Packit Service 2cd632
	.post_parse		= NULL,
Packit Service 2cd632
	.rule_to_cs		= nft_arp_rule_to_cs,
Packit Service 2cd632
	.clear_cs		= nft_clear_iptables_command_state,
Packit Service 2cd632
	.rule_find		= nft_arp_rule_find,
Packit Service 2cd632
	.parse_target		= nft_ipv46_parse_target,
Packit Service 2cd632
};