/*
* (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>
#include <net/netns/generic.h>
struct nft_xt {
struct list_head head;
struct nft_expr_ops ops;
refcount_t refcnt;
/* used only when transaction mutex is locked */
unsigned int listcnt;
/* Unlike other expressions, ops doesn't have static storage duration.
* nft core assumes they do. We use kfree_rcu so that nft core can
* can check expr->ops->size even after nft_compat->destroy() frees
* the nft_xt struct that holds the ops structure.
*/
struct rcu_head rcu_head;
};
/* Used for matches where *info is larger than X byte */
#define NFT_MATCH_LARGE_THRESH 192
struct nft_xt_match_priv {
void *info;
};
struct nft_compat_net {
struct list_head nft_target_list;
struct list_head nft_match_list;
};
static unsigned int nft_compat_net_id __read_mostly;
static struct nft_expr_type nft_match_type;
static struct nft_expr_type nft_target_type;
static struct nft_compat_net *nft_compat_pernet(struct net *net)
{
return net_generic(net, nft_compat_net_id);
}
static void nft_xt_get(struct nft_xt *xt)
{
/* refcount_inc() warns on 0 -> 1 transition, but we can't
* init the reference count to 1 in .select_ops -- we can't
* undo such an increase when another expression inside the same
* rule fails afterwards.
*/
if (xt->listcnt == 0)
refcount_set(&xt->refcnt, 1);
else
refcount_inc(&xt->refcnt);
xt->listcnt++;
}
static bool nft_xt_put(struct nft_xt *xt)
{
if (refcount_dec_and_test(&xt->refcnt)) {
WARN_ON_ONCE(!list_empty(&xt->head));
kfree_rcu(xt, rcu_head);
return true;
}
return false;
}
static int nft_compat_chain_validate_dependency(const struct nft_ctx *ctx,
const char *tablename)
{
enum nft_chain_types type = NFT_CHAIN_T_DEFAULT;
const struct nft_chain *chain = ctx->chain;
const struct nft_base_chain *basechain;
if (!tablename ||
!nft_is_base_chain(chain))
return 0;
basechain = nft_base_chain(chain);
if (strcmp(tablename, "nat") == 0) {
if (ctx->family != NFPROTO_BRIDGE)
type = NFT_CHAIN_T_NAT;
if (basechain->type->type != type)
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: