/*
* (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This software has been sponsored by Sophos Astaro <http://www.sophos.com>
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_tables_compat.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_arp/arp_tables.h>
#include <net/netfilter/nf_tables.h>
struct nft_xt {
struct list_head head;
struct nft_expr_ops ops;
unsigned int refcnt;
};
static void nft_xt_put(struct nft_xt *xt)
{
if (--xt->refcnt == 0) {
list_del(&xt->head);
kfree(xt);
}
}
static int nft_compat_chain_validate_dependency(const char *tablename,
const struct nft_chain *chain)
{
const struct nft_base_chain *basechain;
if (!tablename || !(chain->flags & NFT_BASE_CHAIN))
return 0;
basechain = nft_base_chain(chain);
if (strcmp(tablename, "nat") == 0 &&
basechain->type->type != NFT_CHAIN_T_NAT)
return -EINVAL;
return 0;
}
union nft_entry {
struct ipt_entry e4;
struct ip6t_entry e6;
struct ebt_entry ebt;
struct arpt_entry arp;
};
static inline void
nft_compat_set_par(struct xt_action_param *par, void *xt, const void *xt_info)
{
par->target = xt;
par->targinfo = xt_info;
par->hotdrop = false;
}
static void nft_target_eval_xt(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
void *info = nft_expr_priv(expr);
struct xt_target *target = expr->ops->data;
struct sk_buff *skb = pkt->skb;
int ret;
nft_compat_set_par((struct xt_action_param *)&pkt->xt, target, info);
ret = target->target(skb, &pkt->xt);
if (pkt->xt.hotdrop)
ret = NF_DROP;
switch (ret) {
case XT_CONTINUE:
regs->verdict.code = NFT_CONTINUE;
break;
default:
regs->verdict.code = ret;
break;
}
}
static void nft_target_eval_bridge(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
void *info = nft_expr_priv(expr);
struct xt_target *target = expr->ops->data;
struct sk_buff *skb = pkt->skb;
int ret;
nft_compat_set_par((struct xt_action_param *)&pkt->xt, target, info);
ret = target->target(skb, &pkt->xt);
if (pkt->xt.hotdrop)
ret = NF_DROP;
switch (ret) {
case EBT_ACCEPT:
regs->verdict.code = NF_ACCEPT;
break;
case EBT_DROP:
regs->verdict.code = NF_DROP;
break;
case EBT_CONTINUE:
regs->verdict.code = NFT_CONTINUE;
break;
case EBT_RETURN:
regs->verdict.code = NFT_RETURN;
break;
default:
regs->verdict.code = ret;
break;
}
}
static const struct nla_policy nft_target_policy[NFTA_TARGET_MAX + 1] = {
[NFTA_TARGET_NAME] = { .type = NLA_NUL_STRING },
[NFTA_TARGET_REV] = { .type = NLA_U32 },
[NFTA_TARGET_INFO] = { .type = NLA_BINARY },
};
static void
nft_target_set_tgchk_param(struct xt_tgchk_param *par,
const struct nft_ctx *ctx,
struct xt_target *target, void *info,
union nft_entry *entry, u16 proto, bool inv)
{
par->net = ctx->net;
par->table = ctx->table->name;
switch (ctx->afi->family) {
case AF_INET:
entry->e4.ip.proto = proto;
entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0;
break;
case AF_INET6:
if (proto)
entry->e6.ipv6.flags |= IP6T_F_PROTO;
entry->e6.ipv6.proto = proto;
entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0;
break;
case NFPROTO_BRIDGE:
entry->ebt.ethproto = (__force __be16)proto;
entry->ebt.invflags = inv ? EBT_IPROTO