2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Packet matching code.
|
|
|
|
*
|
|
|
|
* Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
* Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2009-07-09 20:54:53 +00:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/cache.h>
|
2006-01-11 20:17:47 +00:00
|
|
|
#include <linux/capability.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/kmod.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/icmp.h>
|
|
|
|
#include <net/ip.h>
|
2006-04-01 10:25:19 +00:00
|
|
|
#include <net/compat.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <asm/uaccess.h>
|
2006-03-21 06:35:41 +00:00
|
|
|
#include <linux/mutex.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/proc_fs.h>
|
|
|
|
#include <linux/err.h>
|
2005-10-13 21:41:23 +00:00
|
|
|
#include <linux/cpumask.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
#include <linux/netfilter/x_tables.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/netfilter_ipv4/ip_tables.h>
|
2007-12-18 06:38:49 +00:00
|
|
|
#include <net/netfilter/nf_log.h>
|
2009-06-17 20:14:54 +00:00
|
|
|
#include "../../netfilter/xt_repldata.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
|
|
|
|
MODULE_DESCRIPTION("IPv4 packet filter");
|
|
|
|
|
|
|
|
/*#define DEBUG_IP_FIREWALL*/
|
|
|
|
/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
|
|
|
|
/*#define DEBUG_IP_FIREWALL_USER*/
|
|
|
|
|
|
|
|
#ifdef DEBUG_IP_FIREWALL
|
2010-03-19 20:08:16 +00:00
|
|
|
#define dprintf(format, args...) pr_info(format , ## args)
|
2005-04-16 22:20:36 +00:00
|
|
|
#else
|
|
|
|
#define dprintf(format, args...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DEBUG_IP_FIREWALL_USER
|
2010-03-19 20:08:16 +00:00
|
|
|
#define duprintf(format, args...) pr_info(format , ## args)
|
2005-04-16 22:20:36 +00:00
|
|
|
#else
|
|
|
|
#define duprintf(format, args...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_NETFILTER_DEBUG
|
2010-05-13 13:00:20 +00:00
|
|
|
#define IP_NF_ASSERT(x) WARN_ON(!(x))
|
2005-04-16 22:20:36 +00:00
|
|
|
#else
|
|
|
|
#define IP_NF_ASSERT(x)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* All the better to debug you with... */
|
|
|
|
#define static
|
|
|
|
#define inline
|
|
|
|
#endif
|
|
|
|
|
2009-06-17 20:14:54 +00:00
|
|
|
void *ipt_alloc_initial_table(const struct xt_table *info)
|
|
|
|
{
|
|
|
|
return xt_alloc_initial_table(ipt, IPT);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ipt_alloc_initial_table);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
We keep a set of rules for each CPU, so we can avoid write-locking
|
|
|
|
them in the softirq when updating the counters and therefore
|
|
|
|
only need to read-lock in the softirq; doing a write_lock_bh() in user
|
|
|
|
context stops packets coming through and allows user context to read
|
|
|
|
the counters or update the rules.
|
|
|
|
|
|
|
|
Hence the start of any table is given by get_table() below. */
|
|
|
|
|
|
|
|
/* Returns whether matches rule or not. */
|
2008-01-15 07:44:05 +00:00
|
|
|
/* Performance critical - called for every packet */
|
2007-12-18 05:52:00 +00:00
|
|
|
static inline bool
|
2005-04-16 22:20:36 +00:00
|
|
|
ip_packet_match(const struct iphdr *ip,
|
|
|
|
const char *indev,
|
|
|
|
const char *outdev,
|
|
|
|
const struct ipt_ip *ipinfo,
|
|
|
|
int isfrag)
|
|
|
|
{
|
|
|
|
unsigned long ret;
|
|
|
|
|
2007-12-18 06:44:06 +00:00
|
|
|
#define FWINV(bool, invflg) ((bool) ^ !!(ipinfo->invflags & (invflg)))
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr,
|
2009-11-23 22:17:06 +00:00
|
|
|
IPT_INV_SRCIP) ||
|
|
|
|
FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr,
|
|
|
|
IPT_INV_DSTIP)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
dprintf("Source or dest mismatch.\n");
|
|
|
|
|
2008-10-31 07:53:08 +00:00
|
|
|
dprintf("SRC: %pI4. Mask: %pI4. Target: %pI4.%s\n",
|
|
|
|
&ip->saddr, &ipinfo->smsk.s_addr, &ipinfo->src.s_addr,
|
2005-04-16 22:20:36 +00:00
|
|
|
ipinfo->invflags & IPT_INV_SRCIP ? " (INV)" : "");
|
2008-10-31 07:53:08 +00:00
|
|
|
dprintf("DST: %pI4 Mask: %pI4 Target: %pI4.%s\n",
|
|
|
|
&ip->daddr, &ipinfo->dmsk.s_addr, &ipinfo->dst.s_addr,
|
2005-04-16 22:20:36 +00:00
|
|
|
ipinfo->invflags & IPT_INV_DSTIP ? " (INV)" : "");
|
2007-12-18 05:52:00 +00:00
|
|
|
return false;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2009-03-25 16:31:52 +00:00
|
|
|
ret = ifname_compare_aligned(indev, ipinfo->iniface, ipinfo->iniface_mask);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
|
|
|
|
dprintf("VIA in mismatch (%s vs %s).%s\n",
|
|
|
|
indev, ipinfo->iniface,
|
|
|
|
ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
|
2007-12-18 05:52:00 +00:00
|
|
|
return false;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2009-03-25 16:31:52 +00:00
|
|
|
ret = ifname_compare_aligned(outdev, ipinfo->outiface, ipinfo->outiface_mask);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
|
|
|
|
dprintf("VIA out mismatch (%s vs %s).%s\n",
|
|
|
|
outdev, ipinfo->outiface,
|
|
|
|
ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
|
2007-12-18 05:52:00 +00:00
|
|
|
return false;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check specific protocol */
|
2009-11-23 22:17:06 +00:00
|
|
|
if (ipinfo->proto &&
|
|
|
|
FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
dprintf("Packet protocol %hi does not match %hi.%s\n",
|
|
|
|
ip->protocol, ipinfo->proto,
|
|
|
|
ipinfo->invflags&IPT_INV_PROTO ? " (INV)":"");
|
2007-12-18 05:52:00 +00:00
|
|
|
return false;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If we have a fragment rule but the packet is not a fragment
|
|
|
|
* then we return zero */
|
|
|
|
if (FWINV((ipinfo->flags&IPT_F_FRAG) && !isfrag, IPT_INV_FRAG)) {
|
|
|
|
dprintf("Fragment rule but not fragment.%s\n",
|
|
|
|
ipinfo->invflags & IPT_INV_FRAG ? " (INV)" : "");
|
2007-12-18 05:52:00 +00:00
|
|
|
return false;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-12-18 05:52:00 +00:00
|
|
|
return true;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-01-15 07:44:05 +00:00
|
|
|
static bool
|
2005-04-16 22:20:36 +00:00
|
|
|
ip_checkentry(const struct ipt_ip *ip)
|
|
|
|
{
|
|
|
|
if (ip->flags & ~IPT_F_MASK) {
|
|
|
|
duprintf("Unknown flag bits set: %08X\n",
|
|
|
|
ip->flags & ~IPT_F_MASK);
|
2007-07-08 05:16:00 +00:00
|
|
|
return false;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
if (ip->invflags & ~IPT_INV_MASK) {
|
|
|
|
duprintf("Unknown invflag bits set: %08X\n",
|
|
|
|
ip->invflags & ~IPT_INV_MASK);
|
2007-07-08 05:16:00 +00:00
|
|
|
return false;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2007-07-08 05:16:00 +00:00
|
|
|
return true;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
2009-07-05 17:43:26 +00:00
|
|
|
ipt_error(struct sk_buff *skb, const struct xt_action_param *par)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
if (net_ratelimit())
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_info("error: `%s'\n", (const char *)par->targinfo);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return NF_DROP;
|
|
|
|
}
|
|
|
|
|
2008-01-15 07:44:05 +00:00
|
|
|
/* Performance critical */
|
2005-04-16 22:20:36 +00:00
|
|
|
static inline struct ipt_entry *
|
2009-06-26 05:51:59 +00:00
|
|
|
get_entry(const void *base, unsigned int offset)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
return (struct ipt_entry *)(base + offset);
|
|
|
|
}
|
|
|
|
|
2007-07-08 05:21:23 +00:00
|
|
|
/* All zeroes == unconditional rule. */
|
2008-01-15 07:44:05 +00:00
|
|
|
/* Mildly perf critical (only if packet tracing is on) */
|
2009-07-09 21:00:19 +00:00
|
|
|
static inline bool unconditional(const struct ipt_ip *ip)
|
2007-07-08 05:21:23 +00:00
|
|
|
{
|
2009-07-09 21:00:19 +00:00
|
|
|
static const struct ipt_ip uncond;
|
2007-07-08 05:21:23 +00:00
|
|
|
|
2009-07-09 21:00:19 +00:00
|
|
|
return memcmp(ip, &uncond, sizeof(uncond)) == 0;
|
2007-12-18 06:44:06 +00:00
|
|
|
#undef FWINV
|
2007-07-08 05:21:23 +00:00
|
|
|
}
|
|
|
|
|
2009-06-26 05:51:59 +00:00
|
|
|
/* for const-correctness */
|
2010-10-13 14:11:22 +00:00
|
|
|
static inline const struct xt_entry_target *
|
2009-06-26 05:51:59 +00:00
|
|
|
ipt_get_target_c(const struct ipt_entry *e)
|
|
|
|
{
|
|
|
|
return ipt_get_target((struct ipt_entry *)e);
|
|
|
|
}
|
|
|
|
|
2007-07-08 05:21:23 +00:00
|
|
|
#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
|
|
|
|
defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
|
2008-01-15 07:44:05 +00:00
|
|
|
static const char *const hooknames[] = {
|
2007-11-20 02:53:30 +00:00
|
|
|
[NF_INET_PRE_ROUTING] = "PREROUTING",
|
|
|
|
[NF_INET_LOCAL_IN] = "INPUT",
|
2007-12-18 05:52:00 +00:00
|
|
|
[NF_INET_FORWARD] = "FORWARD",
|
2007-11-20 02:53:30 +00:00
|
|
|
[NF_INET_LOCAL_OUT] = "OUTPUT",
|
|
|
|
[NF_INET_POST_ROUTING] = "POSTROUTING",
|
2007-07-08 05:21:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum nf_ip_trace_comments {
|
|
|
|
NF_IP_TRACE_COMMENT_RULE,
|
|
|
|
NF_IP_TRACE_COMMENT_RETURN,
|
|
|
|
NF_IP_TRACE_COMMENT_POLICY,
|
|
|
|
};
|
|
|
|
|
2008-01-15 07:44:05 +00:00
|
|
|
static const char *const comments[] = {
|
2007-07-08 05:21:23 +00:00
|
|
|
[NF_IP_TRACE_COMMENT_RULE] = "rule",
|
|
|
|
[NF_IP_TRACE_COMMENT_RETURN] = "return",
|
|
|
|
[NF_IP_TRACE_COMMENT_POLICY] = "policy",
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct nf_loginfo trace_loginfo = {
|
|
|
|
.type = NF_LOG_TYPE_LOG,
|
|
|
|
.u = {
|
|
|
|
.log = {
|
|
|
|
.level = 4,
|
|
|
|
.logflags = NF_LOG_MASK,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2008-01-15 07:44:05 +00:00
|
|
|
/* Mildly perf critical (only if packet tracing is on) */
|
2007-07-08 05:21:23 +00:00
|
|
|
static inline int
|
2009-06-26 05:51:59 +00:00
|
|
|
get_chainname_rulenum(const struct ipt_entry *s, const struct ipt_entry *e,
|
2009-04-15 18:31:13 +00:00
|
|
|
const char *hookname, const char **chainname,
|
|
|
|
const char **comment, unsigned int *rulenum)
|
2007-07-08 05:21:23 +00:00
|
|
|
{
|
2010-10-13 14:11:22 +00:00
|
|
|
const struct xt_standard_target *t = (void *)ipt_get_target_c(s);
|
2007-07-08 05:21:23 +00:00
|
|
|
|
2010-10-13 14:28:00 +00:00
|
|
|
if (strcmp(t->target.u.kernel.target->name, XT_ERROR_TARGET) == 0) {
|
2007-07-08 05:21:23 +00:00
|
|
|
/* Head of user chain: ERROR target with chainname */
|
|
|
|
*chainname = t->target.data;
|
|
|
|
(*rulenum) = 0;
|
|
|
|
} else if (s == e) {
|
|
|
|
(*rulenum)++;
|
|
|
|
|
2009-11-23 22:17:06 +00:00
|
|
|
if (s->target_offset == sizeof(struct ipt_entry) &&
|
|
|
|
strcmp(t->target.u.kernel.target->name,
|
2010-10-13 14:28:00 +00:00
|
|
|
XT_STANDARD_TARGET) == 0 &&
|
2009-11-23 22:17:06 +00:00
|
|
|
t->verdict < 0 &&
|
|
|
|
unconditional(&s->ip)) {
|
2007-07-08 05:21:23 +00:00
|
|
|
/* Tail of chains: STANDARD target (return/policy) */
|
|
|
|
*comment = *chainname == hookname
|
2009-04-15 18:31:13 +00:00
|
|
|
? comments[NF_IP_TRACE_COMMENT_POLICY]
|
|
|
|
: comments[NF_IP_TRACE_COMMENT_RETURN];
|
2007-07-08 05:21:23 +00:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
} else
|
|
|
|
(*rulenum)++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-26 05:51:59 +00:00
|
|
|
static void trace_packet(const struct sk_buff *skb,
|
2007-07-08 05:21:23 +00:00
|
|
|
unsigned int hook,
|
|
|
|
const struct net_device *in,
|
|
|
|
const struct net_device *out,
|
2008-01-31 11:54:47 +00:00
|
|
|
const char *tablename,
|
2009-06-26 05:51:59 +00:00
|
|
|
const struct xt_table_info *private,
|
|
|
|
const struct ipt_entry *e)
|
2007-07-08 05:21:23 +00:00
|
|
|
{
|
2009-06-26 05:51:59 +00:00
|
|
|
const void *table_base;
|
2008-04-14 09:15:35 +00:00
|
|
|
const struct ipt_entry *root;
|
2009-04-15 18:31:13 +00:00
|
|
|
const char *hookname, *chainname, *comment;
|
2010-02-24 17:32:59 +00:00
|
|
|
const struct ipt_entry *iter;
|
2007-07-08 05:21:23 +00:00
|
|
|
unsigned int rulenum = 0;
|
|
|
|
|
2009-04-15 18:27:03 +00:00
|
|
|
table_base = private->entries[smp_processor_id()];
|
2007-07-08 05:21:23 +00:00
|
|
|
root = get_entry(table_base, private->hook_entry[hook]);
|
|
|
|
|
2009-04-15 18:31:13 +00:00
|
|
|
hookname = chainname = hooknames[hook];
|
|
|
|
comment = comments[NF_IP_TRACE_COMMENT_RULE];
|
2007-07-08 05:21:23 +00:00
|
|
|
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter, root, private->size - private->hook_entry[hook])
|
|
|
|
if (get_chainname_rulenum(iter, e, hookname,
|
|
|
|
&chainname, &comment, &rulenum) != 0)
|
|
|
|
break;
|
2007-07-08 05:21:23 +00:00
|
|
|
|
|
|
|
nf_log_packet(AF_INET, hook, skb, in, out, &trace_loginfo,
|
|
|
|
"TRACE: %s:%s:%s:%u ",
|
|
|
|
tablename, chainname, comment, rulenum);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-04-15 19:06:05 +00:00
|
|
|
static inline __pure
|
|
|
|
struct ipt_entry *ipt_next_entry(const struct ipt_entry *entry)
|
|
|
|
{
|
|
|
|
return (void *)entry + entry->next_offset;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Returns one of the generic firewall policies, like NF_ACCEPT. */
|
|
|
|
unsigned int
|
2007-10-15 07:53:15 +00:00
|
|
|
ipt_do_table(struct sk_buff *skb,
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned int hook,
|
|
|
|
const struct net_device *in,
|
|
|
|
const struct net_device *out,
|
2007-02-07 23:12:33 +00:00
|
|
|
struct xt_table *table)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
|
2008-04-14 09:15:35 +00:00
|
|
|
const struct iphdr *ip;
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Initializing verdict to NF_DROP keeps gcc happy. */
|
|
|
|
unsigned int verdict = NF_DROP;
|
|
|
|
const char *indev, *outdev;
|
2009-06-26 05:51:59 +00:00
|
|
|
const void *table_base;
|
2010-04-19 14:05:10 +00:00
|
|
|
struct ipt_entry *e, **jumpstack;
|
|
|
|
unsigned int *stackptr, origptr, cpu;
|
2009-06-26 05:51:59 +00:00
|
|
|
const struct xt_table_info *private;
|
2009-07-05 16:26:37 +00:00
|
|
|
struct xt_action_param acpar;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Initialization */
|
2007-10-15 07:53:15 +00:00
|
|
|
ip = ip_hdr(skb);
|
2005-04-16 22:20:36 +00:00
|
|
|
indev = in ? in->name : nulldevname;
|
|
|
|
outdev = out ? out->name : nulldevname;
|
|
|
|
/* We handle fragments by dealing with the first fragment as
|
|
|
|
* if it was a normal packet. All other fragments are treated
|
|
|
|
* normally, except that they will NEVER match rules that ask
|
|
|
|
* things we don't know, ie. tcp syn flag or ports). If the
|
|
|
|
* rule is also a fragment-specific rule, non-fragments won't
|
|
|
|
* match it. */
|
2009-07-05 16:26:37 +00:00
|
|
|
acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
|
|
|
|
acpar.thoff = ip_hdrlen(skb);
|
2009-07-07 18:54:30 +00:00
|
|
|
acpar.hotdrop = false;
|
2009-07-05 16:26:37 +00:00
|
|
|
acpar.in = in;
|
|
|
|
acpar.out = out;
|
|
|
|
acpar.family = NFPROTO_IPV4;
|
|
|
|
acpar.hooknum = hook;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
IP_NF_ASSERT(table->valid_hooks & (1 << hook));
|
2009-04-29 05:36:33 +00:00
|
|
|
xt_info_rdlock_bh();
|
|
|
|
private = table->private;
|
2010-04-19 14:05:10 +00:00
|
|
|
cpu = smp_processor_id();
|
|
|
|
table_base = private->entries[cpu];
|
|
|
|
jumpstack = (struct ipt_entry **)private->jumpstack[cpu];
|
2010-05-31 14:41:35 +00:00
|
|
|
stackptr = per_cpu_ptr(private->stackptr, cpu);
|
2010-04-19 14:05:10 +00:00
|
|
|
origptr = *stackptr;
|
2009-02-20 09:35:32 +00:00
|
|
|
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
e = get_entry(table_base, private->hook_entry[hook]);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-04-22 11:03:24 +00:00
|
|
|
pr_debug("Entering %s(hook %u); sp at %u (UF %p)\n",
|
2010-04-19 14:05:10 +00:00
|
|
|
table->name, hook, origptr,
|
|
|
|
get_entry(table_base, private->underflow[hook]));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
do {
|
2010-10-13 14:11:22 +00:00
|
|
|
const struct xt_entry_target *t;
|
2010-02-24 17:34:48 +00:00
|
|
|
const struct xt_entry_match *ematch;
|
2009-04-15 19:28:39 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
IP_NF_ASSERT(e);
|
2009-04-15 19:28:39 +00:00
|
|
|
if (!ip_packet_match(ip, indev, outdev,
|
2009-07-05 16:26:37 +00:00
|
|
|
&e->ip, acpar.fragoff)) {
|
2010-02-24 17:34:48 +00:00
|
|
|
no_match:
|
2009-04-15 19:28:39 +00:00
|
|
|
e = ipt_next_entry(e);
|
|
|
|
continue;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-07-09 17:14:18 +00:00
|
|
|
xt_ematch_foreach(ematch, e) {
|
2009-07-05 16:26:37 +00:00
|
|
|
acpar.match = ematch->u.kernel.match;
|
|
|
|
acpar.matchinfo = ematch->data;
|
|
|
|
if (!acpar.match->match(skb, &acpar))
|
2010-02-24 17:34:48 +00:00
|
|
|
goto no_match;
|
2009-07-09 17:14:18 +00:00
|
|
|
}
|
2010-02-24 17:34:48 +00:00
|
|
|
|
2010-07-23 14:25:11 +00:00
|
|
|
ADD_COUNTER(e->counters, skb->len, 1);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-04-15 19:28:39 +00:00
|
|
|
t = ipt_get_target(e);
|
|
|
|
IP_NF_ASSERT(t->u.kernel.target);
|
2007-07-08 05:21:23 +00:00
|
|
|
|
|
|
|
#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
|
|
|
|
defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
|
2009-04-15 19:28:39 +00:00
|
|
|
/* The packet is traced: log it */
|
|
|
|
if (unlikely(skb->nf_trace))
|
|
|
|
trace_packet(skb, hook, in, out,
|
|
|
|
table->name, private, e);
|
2007-07-08 05:21:23 +00:00
|
|
|
#endif
|
2009-04-15 19:28:39 +00:00
|
|
|
/* Standard target? */
|
|
|
|
if (!t->u.kernel.target->target) {
|
|
|
|
int v;
|
|
|
|
|
2010-10-13 14:11:22 +00:00
|
|
|
v = ((struct xt_standard_target *)t)->verdict;
|
2009-04-15 19:28:39 +00:00
|
|
|
if (v < 0) {
|
|
|
|
/* Pop from stack? */
|
2010-10-13 14:28:00 +00:00
|
|
|
if (v != XT_RETURN) {
|
2009-04-15 19:28:39 +00:00
|
|
|
verdict = (unsigned)(-v) - 1;
|
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2011-03-20 14:40:06 +00:00
|
|
|
if (*stackptr <= origptr) {
|
2010-04-19 14:05:10 +00:00
|
|
|
e = get_entry(table_base,
|
|
|
|
private->underflow[hook]);
|
2010-04-22 11:03:24 +00:00
|
|
|
pr_debug("Underflow (this is normal) "
|
2010-04-19 14:05:10 +00:00
|
|
|
"to %p\n", e);
|
|
|
|
} else {
|
|
|
|
e = jumpstack[--*stackptr];
|
2010-04-22 11:03:24 +00:00
|
|
|
pr_debug("Pulled %p out from pos %u\n",
|
2010-04-19 14:05:10 +00:00
|
|
|
e, *stackptr);
|
|
|
|
e = ipt_next_entry(e);
|
|
|
|
}
|
2009-04-15 19:28:39 +00:00
|
|
|
continue;
|
|
|
|
}
|
2009-11-23 22:17:06 +00:00
|
|
|
if (table_base + v != ipt_next_entry(e) &&
|
|
|
|
!(e->ip.flags & IPT_F_GOTO)) {
|
2010-04-19 14:05:10 +00:00
|
|
|
if (*stackptr >= private->stacksize) {
|
|
|
|
verdict = NF_DROP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
jumpstack[(*stackptr)++] = e;
|
2010-04-22 11:03:24 +00:00
|
|
|
pr_debug("Pushed %p into pos %u\n",
|
2010-04-19 14:05:10 +00:00
|
|
|
e, *stackptr - 1);
|
2009-04-15 19:28:39 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-04-15 19:28:39 +00:00
|
|
|
e = get_entry(table_base, v);
|
2009-04-15 19:35:33 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-07-05 16:26:37 +00:00
|
|
|
acpar.target = t->u.kernel.target;
|
|
|
|
acpar.targinfo = t->data;
|
2009-04-15 19:40:13 +00:00
|
|
|
|
2009-07-05 16:26:37 +00:00
|
|
|
verdict = t->u.kernel.target->target(skb, &acpar);
|
2009-04-15 19:35:33 +00:00
|
|
|
/* Target might have changed stuff. */
|
|
|
|
ip = ip_hdr(skb);
|
2010-10-13 14:28:00 +00:00
|
|
|
if (verdict == XT_CONTINUE)
|
2009-04-15 19:35:33 +00:00
|
|
|
e = ipt_next_entry(e);
|
|
|
|
else
|
|
|
|
/* Verdict */
|
|
|
|
break;
|
2009-07-07 18:54:30 +00:00
|
|
|
} while (!acpar.hotdrop);
|
2010-04-22 11:03:24 +00:00
|
|
|
pr_debug("Exiting %s; resetting sp from %u to %u\n",
|
2010-04-19 14:05:10 +00:00
|
|
|
__func__, *stackptr, origptr);
|
|
|
|
*stackptr = origptr;
|
2011-03-20 14:40:06 +00:00
|
|
|
xt_info_rdunlock_bh();
|
2005-04-16 22:20:36 +00:00
|
|
|
#ifdef DEBUG_ALLOW_ALL
|
|
|
|
return NF_ACCEPT;
|
|
|
|
#else
|
2009-07-07 18:54:30 +00:00
|
|
|
if (acpar.hotdrop)
|
2005-04-16 22:20:36 +00:00
|
|
|
return NF_DROP;
|
|
|
|
else return verdict;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Figures out from what hook each rule can be called: returns 0 if
|
|
|
|
there are loops. Puts hook bitmask in comefrom. */
|
|
|
|
static int
|
2009-06-26 05:51:59 +00:00
|
|
|
mark_source_chains(const struct xt_table_info *newinfo,
|
2005-12-14 07:13:48 +00:00
|
|
|
unsigned int valid_hooks, void *entry0)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
unsigned int hook;
|
|
|
|
|
|
|
|
/* No recursion; use packet counter to save back ptrs (reset
|
|
|
|
to 0 as we leave), and comefrom to save source hook bitmask */
|
2007-11-20 02:53:30 +00:00
|
|
|
for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned int pos = newinfo->hook_entry[hook];
|
2007-12-18 05:52:00 +00:00
|
|
|
struct ipt_entry *e = (struct ipt_entry *)(entry0 + pos);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (!(valid_hooks & (1 << hook)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Set initial back pointer. */
|
|
|
|
e->counters.pcnt = pos;
|
|
|
|
|
|
|
|
for (;;) {
|
2010-10-13 14:11:22 +00:00
|
|
|
const struct xt_standard_target *t
|
2009-06-26 05:51:59 +00:00
|
|
|
= (void *)ipt_get_target_c(e);
|
2006-12-12 08:29:52 +00:00
|
|
|
int visited = e->comefrom & (1 << hook);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-11-20 02:53:30 +00:00
|
|
|
if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
|
2010-05-13 13:02:08 +00:00
|
|
|
pr_err("iptables: loop hook %u pos %u %08X.\n",
|
2005-04-16 22:20:36 +00:00
|
|
|
hook, pos, e->comefrom);
|
|
|
|
return 0;
|
|
|
|
}
|
2007-12-18 05:52:00 +00:00
|
|
|
e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Unconditional return/END. */
|
2009-11-23 22:17:06 +00:00
|
|
|
if ((e->target_offset == sizeof(struct ipt_entry) &&
|
|
|
|
(strcmp(t->target.u.user.name,
|
2010-10-13 14:28:00 +00:00
|
|
|
XT_STANDARD_TARGET) == 0) &&
|
2009-11-23 22:17:06 +00:00
|
|
|
t->verdict < 0 && unconditional(&e->ip)) ||
|
|
|
|
visited) {
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned int oldpos, size;
|
|
|
|
|
2009-03-25 18:26:35 +00:00
|
|
|
if ((strcmp(t->target.u.user.name,
|
2010-10-13 14:28:00 +00:00
|
|
|
XT_STANDARD_TARGET) == 0) &&
|
2009-03-25 18:26:35 +00:00
|
|
|
t->verdict < -NF_MAX_VERDICT - 1) {
|
2006-12-05 21:43:50 +00:00
|
|
|
duprintf("mark_source_chains: bad "
|
|
|
|
"negative verdict (%i)\n",
|
|
|
|
t->verdict);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Return: backtrack through the last
|
|
|
|
big jump. */
|
|
|
|
do {
|
2007-11-20 02:53:30 +00:00
|
|
|
e->comefrom ^= (1<<NF_INET_NUMHOOKS);
|
2005-04-16 22:20:36 +00:00
|
|
|
#ifdef DEBUG_IP_FIREWALL_USER
|
|
|
|
if (e->comefrom
|
2007-11-20 02:53:30 +00:00
|
|
|
& (1 << NF_INET_NUMHOOKS)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
duprintf("Back unset "
|
|
|
|
"on hook %u "
|
|
|
|
"rule %u\n",
|
|
|
|
hook, pos);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
oldpos = pos;
|
|
|
|
pos = e->counters.pcnt;
|
|
|
|
e->counters.pcnt = 0;
|
|
|
|
|
|
|
|
/* We're at the start. */
|
|
|
|
if (pos == oldpos)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
e = (struct ipt_entry *)
|
2005-12-14 07:13:48 +00:00
|
|
|
(entry0 + pos);
|
2005-04-16 22:20:36 +00:00
|
|
|
} while (oldpos == pos + e->next_offset);
|
|
|
|
|
|
|
|
/* Move along one */
|
|
|
|
size = e->next_offset;
|
|
|
|
e = (struct ipt_entry *)
|
2005-12-14 07:13:48 +00:00
|
|
|
(entry0 + pos + size);
|
2005-04-16 22:20:36 +00:00
|
|
|
e->counters.pcnt = pos;
|
|
|
|
pos += size;
|
|
|
|
} else {
|
|
|
|
int newpos = t->verdict;
|
|
|
|
|
|
|
|
if (strcmp(t->target.u.user.name,
|
2010-10-13 14:28:00 +00:00
|
|
|
XT_STANDARD_TARGET) == 0 &&
|
2009-11-23 22:17:06 +00:00
|
|
|
newpos >= 0) {
|
2006-12-05 21:43:50 +00:00
|
|
|
if (newpos > newinfo->size -
|
|
|
|
sizeof(struct ipt_entry)) {
|
|
|
|
duprintf("mark_source_chains: "
|
|
|
|
"bad verdict (%i)\n",
|
|
|
|
newpos);
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
/* This a jump; chase it. */
|
|
|
|
duprintf("Jump rule %u -> %u\n",
|
|
|
|
pos, newpos);
|
|
|
|
} else {
|
|
|
|
/* ... this is a fallthru */
|
|
|
|
newpos = pos + e->next_offset;
|
|
|
|
}
|
|
|
|
e = (struct ipt_entry *)
|
2005-12-14 07:13:48 +00:00
|
|
|
(entry0 + newpos);
|
2005-04-16 22:20:36 +00:00
|
|
|
e->counters.pcnt = pos;
|
|
|
|
pos = newpos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
next:
|
|
|
|
duprintf("Finished chain %u\n", hook);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-10-13 14:11:22 +00:00
|
|
|
static void cleanup_match(struct xt_entry_match *m, struct net *net)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-10-08 09:35:19 +00:00
|
|
|
struct xt_mtdtor_param par;
|
|
|
|
|
2010-01-18 07:25:47 +00:00
|
|
|
par.net = net;
|
2008-10-08 09:35:19 +00:00
|
|
|
par.match = m->u.kernel.match;
|
|
|
|
par.matchinfo = m->data;
|
2008-10-08 09:35:20 +00:00
|
|
|
par.family = NFPROTO_IPV4;
|
2008-10-08 09:35:19 +00:00
|
|
|
if (par.match->destroy != NULL)
|
|
|
|
par.match->destroy(&par);
|
|
|
|
module_put(par.match->me);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-01-15 07:44:05 +00:00
|
|
|
static int
|
2009-06-26 05:51:59 +00:00
|
|
|
check_entry(const struct ipt_entry *e, const char *name)
|
2006-12-12 08:29:26 +00:00
|
|
|
{
|
2010-10-13 14:11:22 +00:00
|
|
|
const struct xt_entry_target *t;
|
2006-12-12 08:29:26 +00:00
|
|
|
|
|
|
|
if (!ip_checkentry(&e->ip)) {
|
2010-05-02 11:55:21 +00:00
|
|
|
duprintf("ip check failed %p %s.\n", e, par->match->name);
|
2006-12-12 08:29:26 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-10-13 14:11:22 +00:00
|
|
|
if (e->target_offset + sizeof(struct xt_entry_target) >
|
2007-12-18 05:52:00 +00:00
|
|
|
e->next_offset)
|
2006-12-12 08:29:26 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2009-06-26 05:51:59 +00:00
|
|
|
t = ipt_get_target_c(e);
|
2006-12-12 08:29:26 +00:00
|
|
|
if (e->target_offset + t->u.target_size > e->next_offset)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-15 07:44:05 +00:00
|
|
|
static int
|
2010-10-13 14:11:22 +00:00
|
|
|
check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
|
2006-12-12 08:29:26 +00:00
|
|
|
{
|
2008-10-08 09:35:18 +00:00
|
|
|
const struct ipt_ip *ip = par->entryinfo;
|
2006-12-12 08:29:26 +00:00
|
|
|
int ret;
|
|
|
|
|
2008-10-08 09:35:18 +00:00
|
|
|
par->match = m->u.kernel.match;
|
|
|
|
par->matchinfo = m->data;
|
|
|
|
|
2008-10-08 09:35:20 +00:00
|
|
|
ret = xt_check_match(par, m->u.match_size - sizeof(*m),
|
2008-10-08 09:35:18 +00:00
|
|
|
ip->proto, ip->invflags & IPT_INV_PROTO);
|
2008-10-08 09:35:17 +00:00
|
|
|
if (ret < 0) {
|
2010-05-02 11:55:21 +00:00
|
|
|
duprintf("check failed for `%s'.\n", par->match->name);
|
2008-10-08 09:35:17 +00:00
|
|
|
return ret;
|
2006-12-12 08:29:26 +00:00
|
|
|
}
|
2008-10-08 09:35:17 +00:00
|
|
|
return 0;
|
2006-12-12 08:29:26 +00:00
|
|
|
}
|
|
|
|
|
2008-01-15 07:44:05 +00:00
|
|
|
static int
|
2010-10-13 14:11:22 +00:00
|
|
|
find_check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-02-07 23:11:19 +00:00
|
|
|
struct xt_match *match;
|
2006-03-21 02:00:36 +00:00
|
|
|
int ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-07-10 17:27:47 +00:00
|
|
|
match = xt_request_find_match(NFPROTO_IPV4, m->u.user.name,
|
|
|
|
m->u.user.revision);
|
|
|
|
if (IS_ERR(match)) {
|
2006-12-12 08:29:26 +00:00
|
|
|
duprintf("find_check_match: `%s' not found\n", m->u.user.name);
|
2009-07-10 17:27:47 +00:00
|
|
|
return PTR_ERR(match);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
m->u.kernel.match = match;
|
|
|
|
|
2010-02-24 17:35:37 +00:00
|
|
|
ret = check_match(m, par);
|
2006-03-21 02:00:36 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
2006-03-21 02:00:36 +00:00
|
|
|
err:
|
|
|
|
module_put(m->u.kernel.match->me);
|
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2010-02-03 12:45:12 +00:00
|
|
|
static int check_target(struct ipt_entry *e, struct net *net, const char *name)
|
2006-12-12 08:29:26 +00:00
|
|
|
{
|
2010-10-13 14:11:22 +00:00
|
|
|
struct xt_entry_target *t = ipt_get_target(e);
|
2008-10-08 09:35:19 +00:00
|
|
|
struct xt_tgchk_param par = {
|
2010-02-03 12:45:12 +00:00
|
|
|
.net = net,
|
2008-10-08 09:35:19 +00:00
|
|
|
.table = name,
|
|
|
|
.entryinfo = e,
|
|
|
|
.target = t->u.kernel.target,
|
|
|
|
.targinfo = t->data,
|
|
|
|
.hook_mask = e->comefrom,
|
2008-10-08 09:35:20 +00:00
|
|
|
.family = NFPROTO_IPV4,
|
2008-10-08 09:35:19 +00:00
|
|
|
};
|
2007-02-09 14:24:47 +00:00
|
|
|
int ret;
|
2006-12-12 08:29:26 +00:00
|
|
|
|
2008-10-08 09:35:20 +00:00
|
|
|
ret = xt_check_target(&par, t->u.target_size - sizeof(*t),
|
2008-10-08 09:35:19 +00:00
|
|
|
e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
|
2008-10-08 09:35:17 +00:00
|
|
|
if (ret < 0) {
|
2010-03-19 20:08:16 +00:00
|
|
|
duprintf("check failed for `%s'.\n",
|
2006-12-12 08:29:26 +00:00
|
|
|
t->u.kernel.target->name);
|
2008-10-08 09:35:17 +00:00
|
|
|
return ret;
|
2006-12-12 08:29:26 +00:00
|
|
|
}
|
2008-10-08 09:35:17 +00:00
|
|
|
return 0;
|
2006-12-12 08:29:26 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-01-15 07:44:05 +00:00
|
|
|
static int
|
2010-01-18 07:21:13 +00:00
|
|
|
find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
|
2010-02-24 17:33:43 +00:00
|
|
|
unsigned int size)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2010-10-13 14:11:22 +00:00
|
|
|
struct xt_entry_target *t;
|
2007-02-07 23:11:19 +00:00
|
|
|
struct xt_target *target;
|
2005-04-16 22:20:36 +00:00
|
|
|
int ret;
|
|
|
|
unsigned int j;
|
2008-10-08 09:35:18 +00:00
|
|
|
struct xt_mtchk_param mtpar;
|
2010-02-24 17:34:48 +00:00
|
|
|
struct xt_entry_match *ematch;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-12-12 08:29:26 +00:00
|
|
|
ret = check_entry(e, name);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
[NETFILTER]: Missed and reordered checks in {arp,ip,ip6}_tables
There is a number of issues in parsing user-provided table in
translate_table(). Malicious user with CAP_NET_ADMIN may crash system by
passing special-crafted table to the *_tables.
The first issue is that mark_source_chains() function is called before entry
content checks. In case of standard target, mark_source_chains() function
uses t->verdict field in order to determine new position. But the check, that
this field leads no further, than the table end, is in check_entry(), which
is called later, than mark_source_chains().
The second issue, that there is no check that target_offset points inside
entry. If so, *_ITERATE_MATCH macro will follow further, than the entry
ends. As a result, we'll have oops or memory disclosure.
And the third issue, that there is no check that the target is completely
inside entry. Results are the same, as in previous issue.
Signed-off-by: Dmitry Mishin <dim@openvz.org>
Acked-by: Kirill Korotaev <dev@openvz.org>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-10-30 23:12:55 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
j = 0;
|
2010-01-18 07:21:13 +00:00
|
|
|
mtpar.net = net;
|
2008-10-08 09:35:18 +00:00
|
|
|
mtpar.table = name;
|
|
|
|
mtpar.entryinfo = &e->ip;
|
|
|
|
mtpar.hook_mask = e->comefrom;
|
2008-10-08 09:35:20 +00:00
|
|
|
mtpar.family = NFPROTO_IPV4;
|
2010-02-24 17:34:48 +00:00
|
|
|
xt_ematch_foreach(ematch, e) {
|
2010-02-24 17:35:37 +00:00
|
|
|
ret = find_check_match(ematch, &mtpar);
|
2010-02-24 17:34:48 +00:00
|
|
|
if (ret != 0)
|
2010-02-24 17:35:37 +00:00
|
|
|
goto cleanup_matches;
|
|
|
|
++j;
|
2010-02-24 17:34:48 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
t = ipt_get_target(e);
|
2009-07-10 16:55:11 +00:00
|
|
|
target = xt_request_find_target(NFPROTO_IPV4, t->u.user.name,
|
|
|
|
t->u.user.revision);
|
|
|
|
if (IS_ERR(target)) {
|
2006-12-12 08:29:26 +00:00
|
|
|
duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
|
2009-07-10 16:55:11 +00:00
|
|
|
ret = PTR_ERR(target);
|
2005-04-16 22:20:36 +00:00
|
|
|
goto cleanup_matches;
|
|
|
|
}
|
|
|
|
t->u.kernel.target = target;
|
|
|
|
|
2010-02-03 12:45:12 +00:00
|
|
|
ret = check_target(e, net, name);
|
2006-03-21 02:00:36 +00:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
2006-03-21 02:00:36 +00:00
|
|
|
err:
|
|
|
|
module_put(t->u.kernel.target->me);
|
2005-04-16 22:20:36 +00:00
|
|
|
cleanup_matches:
|
2010-02-24 17:35:37 +00:00
|
|
|
xt_ematch_foreach(ematch, e) {
|
|
|
|
if (j-- == 0)
|
2010-02-24 17:34:48 +00:00
|
|
|
break;
|
2010-02-24 17:35:37 +00:00
|
|
|
cleanup_match(ematch, net);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-06-26 05:51:59 +00:00
|
|
|
static bool check_underflow(const struct ipt_entry *e)
|
2009-07-18 13:22:30 +00:00
|
|
|
{
|
2010-10-13 14:11:22 +00:00
|
|
|
const struct xt_entry_target *t;
|
2009-07-18 13:22:30 +00:00
|
|
|
unsigned int verdict;
|
|
|
|
|
|
|
|
if (!unconditional(&e->ip))
|
|
|
|
return false;
|
2009-06-26 05:51:59 +00:00
|
|
|
t = ipt_get_target_c(e);
|
2009-07-18 13:22:30 +00:00
|
|
|
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
|
|
|
|
return false;
|
2010-10-13 14:11:22 +00:00
|
|
|
verdict = ((struct xt_standard_target *)t)->verdict;
|
2009-07-18 13:22:30 +00:00
|
|
|
verdict = -verdict - 1;
|
|
|
|
return verdict == NF_DROP || verdict == NF_ACCEPT;
|
|
|
|
}
|
|
|
|
|
2008-01-15 07:44:05 +00:00
|
|
|
static int
|
2005-04-16 22:20:36 +00:00
|
|
|
check_entry_size_and_hooks(struct ipt_entry *e,
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
struct xt_table_info *newinfo,
|
2009-06-26 05:51:59 +00:00
|
|
|
const unsigned char *base,
|
|
|
|
const unsigned char *limit,
|
2005-04-16 22:20:36 +00:00
|
|
|
const unsigned int *hook_entries,
|
|
|
|
const unsigned int *underflows,
|
2010-02-24 17:33:43 +00:00
|
|
|
unsigned int valid_hooks)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
unsigned int h;
|
|
|
|
|
2009-11-23 22:17:06 +00:00
|
|
|
if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 ||
|
|
|
|
(unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
|
2005-04-16 22:20:36 +00:00
|
|
|
duprintf("Bad offset %p\n", e);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e->next_offset
|
2010-10-13 14:11:22 +00:00
|
|
|
< sizeof(struct ipt_entry) + sizeof(struct xt_entry_target)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
duprintf("checking: element %p size %u\n",
|
|
|
|
e, e->next_offset);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check hooks & underflows */
|
2007-11-20 02:53:30 +00:00
|
|
|
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
|
2009-07-18 12:52:58 +00:00
|
|
|
if (!(valid_hooks & (1 << h)))
|
|
|
|
continue;
|
2005-04-16 22:20:36 +00:00
|
|
|
if ((unsigned char *)e - base == hook_entries[h])
|
|
|
|
newinfo->hook_entry[h] = hook_entries[h];
|
2009-07-09 20:54:53 +00:00
|
|
|
if ((unsigned char *)e - base == underflows[h]) {
|
2009-07-18 13:22:30 +00:00
|
|
|
if (!check_underflow(e)) {
|
|
|
|
pr_err("Underflows must be unconditional and "
|
|
|
|
"use the STANDARD target with "
|
|
|
|
"ACCEPT/DROP\n");
|
2009-07-09 20:54:53 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
newinfo->underflow[h] = underflows[h];
|
2009-07-09 20:54:53 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear counters and comefrom */
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
e->counters = ((struct xt_counters) { 0, 0 });
|
2005-04-16 22:20:36 +00:00
|
|
|
e->comefrom = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-24 17:33:43 +00:00
|
|
|
static void
|
|
|
|
cleanup_entry(struct ipt_entry *e, struct net *net)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-10-08 09:35:19 +00:00
|
|
|
struct xt_tgdtor_param par;
|
2010-10-13 14:11:22 +00:00
|
|
|
struct xt_entry_target *t;
|
2010-02-24 17:34:48 +00:00
|
|
|
struct xt_entry_match *ematch;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Cleanup all matches */
|
2010-02-24 17:34:48 +00:00
|
|
|
xt_ematch_foreach(ematch, e)
|
2010-02-24 17:35:37 +00:00
|
|
|
cleanup_match(ematch, net);
|
2005-04-16 22:20:36 +00:00
|
|
|
t = ipt_get_target(e);
|
2008-10-08 09:35:19 +00:00
|
|
|
|
2010-02-03 12:45:12 +00:00
|
|
|
par.net = net;
|
2008-10-08 09:35:19 +00:00
|
|
|
par.target = t->u.kernel.target;
|
|
|
|
par.targinfo = t->data;
|
2008-10-08 09:35:20 +00:00
|
|
|
par.family = NFPROTO_IPV4;
|
2008-10-08 09:35:19 +00:00
|
|
|
if (par.target->destroy != NULL)
|
|
|
|
par.target->destroy(&par);
|
|
|
|
module_put(par.target->me);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Checks and translates the user-supplied table segment (held in
|
|
|
|
newinfo) */
|
|
|
|
static int
|
2010-02-24 17:36:04 +00:00
|
|
|
translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
|
|
|
|
const struct ipt_replace *repl)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2010-02-24 17:32:59 +00:00
|
|
|
struct ipt_entry *iter;
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned int i;
|
2010-02-24 17:32:59 +00:00
|
|
|
int ret = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-02-24 17:36:04 +00:00
|
|
|
newinfo->size = repl->size;
|
|
|
|
newinfo->number = repl->num_entries;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Init all hooks to impossible value. */
|
2007-11-20 02:53:30 +00:00
|
|
|
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
2005-04-16 22:20:36 +00:00
|
|
|
newinfo->hook_entry[i] = 0xFFFFFFFF;
|
|
|
|
newinfo->underflow[i] = 0xFFFFFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
duprintf("translate_table: size %u\n", newinfo->size);
|
|
|
|
i = 0;
|
|
|
|
/* Walk through entries, checking offsets. */
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter, entry0, newinfo->size) {
|
|
|
|
ret = check_entry_size_and_hooks(iter, newinfo, entry0,
|
2010-02-26 16:53:31 +00:00
|
|
|
entry0 + repl->size,
|
|
|
|
repl->hook_entry,
|
|
|
|
repl->underflow,
|
|
|
|
repl->valid_hooks);
|
2010-02-24 17:32:59 +00:00
|
|
|
if (ret != 0)
|
2010-02-24 17:33:43 +00:00
|
|
|
return ret;
|
|
|
|
++i;
|
2010-04-19 14:05:10 +00:00
|
|
|
if (strcmp(ipt_get_target(iter)->u.user.name,
|
|
|
|
XT_ERROR_TARGET) == 0)
|
|
|
|
++newinfo->stacksize;
|
2010-02-24 17:32:59 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-02-24 17:36:04 +00:00
|
|
|
if (i != repl->num_entries) {
|
2005-04-16 22:20:36 +00:00
|
|
|
duprintf("translate_table: %u not %u entries\n",
|
2010-02-24 17:36:04 +00:00
|
|
|
i, repl->num_entries);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check hooks all assigned */
|
2007-11-20 02:53:30 +00:00
|
|
|
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Only hooks which are valid */
|
2010-02-24 17:36:04 +00:00
|
|
|
if (!(repl->valid_hooks & (1 << i)))
|
2005-04-16 22:20:36 +00:00
|
|
|
continue;
|
|
|
|
if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
|
|
|
|
duprintf("Invalid hook entry %u %u\n",
|
2010-02-24 17:36:04 +00:00
|
|
|
i, repl->hook_entry[i]);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (newinfo->underflow[i] == 0xFFFFFFFF) {
|
|
|
|
duprintf("Invalid underflow %u %u\n",
|
2010-02-24 17:36:04 +00:00
|
|
|
i, repl->underflow[i]);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-24 17:36:04 +00:00
|
|
|
if (!mark_source_chains(newinfo, repl->valid_hooks, entry0))
|
2006-12-05 21:43:50 +00:00
|
|
|
return -ELOOP;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Finally, each sanity check must pass */
|
|
|
|
i = 0;
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter, entry0, newinfo->size) {
|
2010-02-24 17:36:04 +00:00
|
|
|
ret = find_check_entry(iter, net, repl->name, repl->size);
|
2010-02-24 17:32:59 +00:00
|
|
|
if (ret != 0)
|
|
|
|
break;
|
2010-02-24 17:33:43 +00:00
|
|
|
++i;
|
2010-02-24 17:32:59 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-12-05 21:43:50 +00:00
|
|
|
if (ret != 0) {
|
2010-02-24 17:33:43 +00:00
|
|
|
xt_entry_foreach(iter, entry0, newinfo->size) {
|
|
|
|
if (i-- == 0)
|
2010-02-24 17:32:59 +00:00
|
|
|
break;
|
2010-02-24 17:33:43 +00:00
|
|
|
cleanup_entry(iter, net);
|
|
|
|
}
|
2006-12-05 21:43:50 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* And one copy for every other CPU */
|
2006-04-11 05:52:50 +00:00
|
|
|
for_each_possible_cpu(i) {
|
2005-12-14 07:13:48 +00:00
|
|
|
if (newinfo->entries[i] && newinfo->entries[i] != entry0)
|
|
|
|
memcpy(newinfo->entries[i], entry0, newinfo->size);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
get_counters(const struct xt_table_info *t,
|
|
|
|
struct xt_counters counters[])
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2010-02-24 17:32:59 +00:00
|
|
|
struct ipt_entry *iter;
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned int cpu;
|
|
|
|
unsigned int i;
|
|
|
|
|
2006-04-11 05:52:50 +00:00
|
|
|
for_each_possible_cpu(cpu) {
|
2011-01-10 19:11:38 +00:00
|
|
|
seqlock_t *lock = &per_cpu(xt_info_locks, cpu).lock;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
i = 0;
|
2010-02-24 17:33:43 +00:00
|
|
|
xt_entry_foreach(iter, t->entries[cpu], t->size) {
|
2011-01-10 19:11:38 +00:00
|
|
|
u64 bcnt, pcnt;
|
|
|
|
unsigned int start;
|
|
|
|
|
|
|
|
do {
|
|
|
|
start = read_seqbegin(lock);
|
|
|
|
bcnt = iter->counters.bcnt;
|
|
|
|
pcnt = iter->counters.pcnt;
|
|
|
|
} while (read_seqretry(lock, start));
|
|
|
|
|
|
|
|
ADD_COUNTER(counters[i], bcnt, pcnt);
|
2010-02-24 17:33:43 +00:00
|
|
|
++i; /* macro does multi eval of i */
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2009-02-20 09:35:32 +00:00
|
|
|
}
|
|
|
|
|
2009-06-26 05:51:59 +00:00
|
|
|
static struct xt_counters *alloc_counters(const struct xt_table *table)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2006-04-01 10:25:19 +00:00
|
|
|
unsigned int countersize;
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
struct xt_counters *counters;
|
2009-06-26 05:51:59 +00:00
|
|
|
const struct xt_table_info *private = table->private;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* We need atomic snapshot of counters: rest doesn't change
|
|
|
|
(other than comefrom, which userspace doesn't care
|
|
|
|
about). */
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
countersize = sizeof(struct xt_counters) * private->number;
|
2011-01-10 19:11:38 +00:00
|
|
|
counters = vzalloc(countersize);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (counters == NULL)
|
2009-04-29 05:36:33 +00:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2009-02-20 09:35:32 +00:00
|
|
|
|
2009-04-29 05:36:33 +00:00
|
|
|
get_counters(private, counters);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
return counters;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
copy_entries_to_user(unsigned int total_size,
|
2009-06-26 05:51:59 +00:00
|
|
|
const struct xt_table *table,
|
2006-04-01 10:25:19 +00:00
|
|
|
void __user *userptr)
|
|
|
|
{
|
|
|
|
unsigned int off, num;
|
2009-06-26 05:51:59 +00:00
|
|
|
const struct ipt_entry *e;
|
2006-04-01 10:25:19 +00:00
|
|
|
struct xt_counters *counters;
|
2008-04-14 09:15:35 +00:00
|
|
|
const struct xt_table_info *private = table->private;
|
2006-04-01 10:25:19 +00:00
|
|
|
int ret = 0;
|
2008-04-14 09:15:35 +00:00
|
|
|
const void *loc_cpu_entry;
|
2006-04-01 10:25:19 +00:00
|
|
|
|
|
|
|
counters = alloc_counters(table);
|
|
|
|
if (IS_ERR(counters))
|
|
|
|
return PTR_ERR(counters);
|
|
|
|
|
2005-12-14 07:13:48 +00:00
|
|
|
/* choose the copy that is on our node/cpu, ...
|
|
|
|
* This choice is lazy (because current thread is
|
|
|
|
* allowed to migrate to another cpu)
|
|
|
|
*/
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
loc_cpu_entry = private->entries[raw_smp_processor_id()];
|
2005-12-14 07:13:48 +00:00
|
|
|
if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
|
2005-04-16 22:20:36 +00:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto free_counters;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: use iterator macros --RR */
|
|
|
|
/* ... then go back and fix counters and names */
|
|
|
|
for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
|
|
|
|
unsigned int i;
|
2010-10-13 14:11:22 +00:00
|
|
|
const struct xt_entry_match *m;
|
|
|
|
const struct xt_entry_target *t;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-12-14 07:13:48 +00:00
|
|
|
e = (struct ipt_entry *)(loc_cpu_entry + off);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (copy_to_user(userptr + off
|
|
|
|
+ offsetof(struct ipt_entry, counters),
|
|
|
|
&counters[num],
|
|
|
|
sizeof(counters[num])) != 0) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto free_counters;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = sizeof(struct ipt_entry);
|
|
|
|
i < e->target_offset;
|
|
|
|
i += m->u.match_size) {
|
|
|
|
m = (void *)e + i;
|
|
|
|
|
|
|
|
if (copy_to_user(userptr + off + i
|
2010-10-13 14:11:22 +00:00
|
|
|
+ offsetof(struct xt_entry_match,
|
2005-04-16 22:20:36 +00:00
|
|
|
u.user.name),
|
|
|
|
m->u.kernel.match->name,
|
|
|
|
strlen(m->u.kernel.match->name)+1)
|
|
|
|
!= 0) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto free_counters;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-26 05:51:59 +00:00
|
|
|
t = ipt_get_target_c(e);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (copy_to_user(userptr + off + e->target_offset
|
2010-10-13 14:11:22 +00:00
|
|
|
+ offsetof(struct xt_entry_target,
|
2005-04-16 22:20:36 +00:00
|
|
|
u.user.name),
|
|
|
|
t->u.kernel.target->name,
|
|
|
|
strlen(t->u.kernel.target->name)+1) != 0) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto free_counters;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free_counters:
|
|
|
|
vfree(counters);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
#ifdef CONFIG_COMPAT
|
2009-06-26 06:23:19 +00:00
|
|
|
static void compat_standard_from_user(void *dst, const void *src)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
2006-09-20 19:05:37 +00:00
|
|
|
int v = *(compat_int_t *)src;
|
2006-04-01 10:25:19 +00:00
|
|
|
|
2006-09-20 19:05:37 +00:00
|
|
|
if (v > 0)
|
2007-12-18 05:47:48 +00:00
|
|
|
v += xt_compat_calc_jump(AF_INET, v);
|
2006-09-20 19:05:37 +00:00
|
|
|
memcpy(dst, &v, sizeof(v));
|
|
|
|
}
|
2006-05-02 03:12:22 +00:00
|
|
|
|
2009-06-26 06:23:19 +00:00
|
|
|
static int compat_standard_to_user(void __user *dst, const void *src)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
2006-09-20 19:05:37 +00:00
|
|
|
compat_int_t cv = *(int *)src;
|
2006-04-01 10:25:19 +00:00
|
|
|
|
2006-09-20 19:05:37 +00:00
|
|
|
if (cv > 0)
|
2007-12-18 05:47:48 +00:00
|
|
|
cv -= xt_compat_calc_jump(AF_INET, cv);
|
2006-09-20 19:05:37 +00:00
|
|
|
return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
|
2006-04-01 10:25:19 +00:00
|
|
|
}
|
|
|
|
|
2009-06-26 05:51:59 +00:00
|
|
|
static int compat_calc_entry(const struct ipt_entry *e,
|
2007-12-18 05:46:15 +00:00
|
|
|
const struct xt_table_info *info,
|
2009-06-26 05:51:59 +00:00
|
|
|
const void *base, struct xt_table_info *newinfo)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
2010-02-24 17:34:48 +00:00
|
|
|
const struct xt_entry_match *ematch;
|
2010-10-13 14:11:22 +00:00
|
|
|
const struct xt_entry_target *t;
|
2007-01-04 20:14:41 +00:00
|
|
|
unsigned int entry_offset;
|
2006-04-01 10:25:19 +00:00
|
|
|
int off, i, ret;
|
|
|
|
|
2007-12-18 05:47:14 +00:00
|
|
|
off = sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
|
2006-04-01 10:25:19 +00:00
|
|
|
entry_offset = (void *)e - base;
|
2010-02-24 17:34:48 +00:00
|
|
|
xt_ematch_foreach(ematch, e)
|
2010-02-24 17:35:37 +00:00
|
|
|
off += xt_compat_match_offset(ematch->u.kernel.match);
|
2009-06-26 05:51:59 +00:00
|
|
|
t = ipt_get_target_c(e);
|
2006-09-20 19:05:37 +00:00
|
|
|
off += xt_compat_target_offset(t->u.kernel.target);
|
2006-04-01 10:25:19 +00:00
|
|
|
newinfo->size -= off;
|
2007-12-18 05:47:48 +00:00
|
|
|
ret = xt_compat_add_offset(AF_INET, entry_offset, off);
|
2006-04-01 10:25:19 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2007-11-20 02:53:30 +00:00
|
|
|
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
2007-12-18 05:46:15 +00:00
|
|
|
if (info->hook_entry[i] &&
|
|
|
|
(e < (struct ipt_entry *)(base + info->hook_entry[i])))
|
2006-04-01 10:25:19 +00:00
|
|
|
newinfo->hook_entry[i] -= off;
|
2007-12-18 05:46:15 +00:00
|
|
|
if (info->underflow[i] &&
|
|
|
|
(e < (struct ipt_entry *)(base + info->underflow[i])))
|
2006-04-01 10:25:19 +00:00
|
|
|
newinfo->underflow[i] -= off;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-12-05 07:24:56 +00:00
|
|
|
static int compat_table_info(const struct xt_table_info *info,
|
2007-12-18 05:46:15 +00:00
|
|
|
struct xt_table_info *newinfo)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
2010-02-24 17:32:59 +00:00
|
|
|
struct ipt_entry *iter;
|
2006-04-01 10:25:19 +00:00
|
|
|
void *loc_cpu_entry;
|
2010-02-24 17:33:43 +00:00
|
|
|
int ret;
|
2006-04-01 10:25:19 +00:00
|
|
|
|
|
|
|
if (!newinfo || !info)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2007-12-05 07:24:56 +00:00
|
|
|
/* we dont care about newinfo->entries[] */
|
|
|
|
memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
|
|
|
|
newinfo->initial_entries = 0;
|
2006-04-01 10:25:19 +00:00
|
|
|
loc_cpu_entry = info->entries[raw_smp_processor_id()];
|
2010-12-18 17:35:15 +00:00
|
|
|
xt_compat_init_offsets(AF_INET, info->number);
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter, loc_cpu_entry, info->size) {
|
|
|
|
ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
|
|
|
|
if (ret != 0)
|
2010-02-24 17:33:43 +00:00
|
|
|
return ret;
|
2010-02-24 17:32:59 +00:00
|
|
|
}
|
2010-02-24 17:33:43 +00:00
|
|
|
return 0;
|
2006-04-01 10:25:19 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-06-26 05:51:59 +00:00
|
|
|
static int get_info(struct net *net, void __user *user,
|
|
|
|
const int *len, int compat)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
2010-10-13 13:56:56 +00:00
|
|
|
char name[XT_TABLE_MAXNAMELEN];
|
2007-02-07 23:12:33 +00:00
|
|
|
struct xt_table *t;
|
2006-04-01 10:25:19 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (*len != sizeof(struct ipt_getinfo)) {
|
2007-12-18 05:52:15 +00:00
|
|
|
duprintf("length %u != %zu\n", *len,
|
|
|
|
sizeof(struct ipt_getinfo));
|
2006-04-01 10:25:19 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (copy_from_user(name, user, sizeof(name)) != 0)
|
|
|
|
return -EFAULT;
|
|
|
|
|
2010-10-13 13:56:56 +00:00
|
|
|
name[XT_TABLE_MAXNAMELEN-1] = '\0';
|
2006-04-01 10:25:19 +00:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
if (compat)
|
|
|
|
xt_compat_lock(AF_INET);
|
|
|
|
#endif
|
2008-01-31 12:03:03 +00:00
|
|
|
t = try_then_request_module(xt_find_table_lock(net, AF_INET, name),
|
2007-12-18 05:46:15 +00:00
|
|
|
"iptable_%s", name);
|
2006-04-01 10:25:19 +00:00
|
|
|
if (t && !IS_ERR(t)) {
|
|
|
|
struct ipt_getinfo info;
|
2008-04-14 09:15:35 +00:00
|
|
|
const struct xt_table_info *private = t->private;
|
2006-04-01 10:25:19 +00:00
|
|
|
#ifdef CONFIG_COMPAT
|
2010-02-08 19:17:43 +00:00
|
|
|
struct xt_table_info tmp;
|
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
if (compat) {
|
|
|
|
ret = compat_table_info(private, &tmp);
|
2007-12-18 05:47:48 +00:00
|
|
|
xt_compat_flush_offsets(AF_INET);
|
2007-12-18 05:46:15 +00:00
|
|
|
private = &tmp;
|
2006-04-01 10:25:19 +00:00
|
|
|
}
|
|
|
|
#endif
|
2010-11-03 07:45:06 +00:00
|
|
|
memset(&info, 0, sizeof(info));
|
2006-04-01 10:25:19 +00:00
|
|
|
info.valid_hooks = t->valid_hooks;
|
|
|
|
memcpy(info.hook_entry, private->hook_entry,
|
2007-12-18 05:46:15 +00:00
|
|
|
sizeof(info.hook_entry));
|
2006-04-01 10:25:19 +00:00
|
|
|
memcpy(info.underflow, private->underflow,
|
2007-12-18 05:46:15 +00:00
|
|
|
sizeof(info.underflow));
|
2006-04-01 10:25:19 +00:00
|
|
|
info.num_entries = private->number;
|
|
|
|
info.size = private->size;
|
|
|
|
strcpy(info.name, name);
|
|
|
|
|
|
|
|
if (copy_to_user(user, &info, *len) != 0)
|
|
|
|
ret = -EFAULT;
|
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
xt_table_unlock(t);
|
|
|
|
module_put(t->me);
|
|
|
|
} else
|
|
|
|
ret = t ? PTR_ERR(t) : -ENOENT;
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
if (compat)
|
|
|
|
xt_compat_unlock(AF_INET);
|
|
|
|
#endif
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-06-26 05:51:59 +00:00
|
|
|
get_entries(struct net *net, struct ipt_get_entries __user *uptr,
|
|
|
|
const int *len)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ipt_get_entries get;
|
2007-02-07 23:12:33 +00:00
|
|
|
struct xt_table *t;
|
2006-04-01 10:25:19 +00:00
|
|
|
|
|
|
|
if (*len < sizeof(get)) {
|
2007-12-18 05:52:15 +00:00
|
|
|
duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
|
2006-04-01 10:25:19 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (copy_from_user(&get, uptr, sizeof(get)) != 0)
|
|
|
|
return -EFAULT;
|
|
|
|
if (*len != sizeof(struct ipt_get_entries) + get.size) {
|
2007-12-18 05:52:15 +00:00
|
|
|
duprintf("get_entries: %u != %zu\n",
|
|
|
|
*len, sizeof(get) + get.size);
|
2006-04-01 10:25:19 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2008-01-31 12:03:03 +00:00
|
|
|
t = xt_find_table_lock(net, AF_INET, get.name);
|
2006-04-01 10:25:19 +00:00
|
|
|
if (t && !IS_ERR(t)) {
|
2008-04-14 09:15:35 +00:00
|
|
|
const struct xt_table_info *private = t->private;
|
2007-12-18 05:52:00 +00:00
|
|
|
duprintf("t->private->number = %u\n", private->number);
|
2006-04-01 10:25:19 +00:00
|
|
|
if (get.size == private->size)
|
|
|
|
ret = copy_entries_to_user(private->size,
|
|
|
|
t, uptr->entrytable);
|
|
|
|
else {
|
|
|
|
duprintf("get_entries: I've got %u not %u!\n",
|
2007-12-18 05:52:00 +00:00
|
|
|
private->size, get.size);
|
2008-04-14 09:15:45 +00:00
|
|
|
ret = -EAGAIN;
|
2006-04-01 10:25:19 +00:00
|
|
|
}
|
|
|
|
module_put(t->me);
|
|
|
|
xt_table_unlock(t);
|
|
|
|
} else
|
|
|
|
ret = t ? PTR_ERR(t) : -ENOENT;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2008-01-31 12:03:03 +00:00
|
|
|
__do_replace(struct net *net, const char *name, unsigned int valid_hooks,
|
2007-12-18 05:46:15 +00:00
|
|
|
struct xt_table_info *newinfo, unsigned int num_counters,
|
|
|
|
void __user *counters_ptr)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2007-02-07 23:12:33 +00:00
|
|
|
struct xt_table *t;
|
2006-04-01 10:25:19 +00:00
|
|
|
struct xt_table_info *oldinfo;
|
|
|
|
struct xt_counters *counters;
|
|
|
|
void *loc_cpu_old_entry;
|
2010-02-24 17:32:59 +00:00
|
|
|
struct ipt_entry *iter;
|
2006-04-01 10:25:19 +00:00
|
|
|
|
|
|
|
ret = 0;
|
2011-01-10 19:11:38 +00:00
|
|
|
counters = vzalloc(num_counters * sizeof(struct xt_counters));
|
2006-04-01 10:25:19 +00:00
|
|
|
if (!counters) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2008-01-31 12:03:03 +00:00
|
|
|
t = try_then_request_module(xt_find_table_lock(net, AF_INET, name),
|
2006-04-01 10:25:19 +00:00
|
|
|
"iptable_%s", name);
|
|
|
|
if (!t || IS_ERR(t)) {
|
|
|
|
ret = t ? PTR_ERR(t) : -ENOENT;
|
|
|
|
goto free_newinfo_counters_untrans;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* You lied! */
|
|
|
|
if (valid_hooks != t->valid_hooks) {
|
|
|
|
duprintf("Valid hook crap: %08X vs %08X\n",
|
|
|
|
valid_hooks, t->valid_hooks);
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto put_module;
|
|
|
|
}
|
|
|
|
|
|
|
|
oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
|
|
|
|
if (!oldinfo)
|
|
|
|
goto put_module;
|
|
|
|
|
|
|
|
/* Update module usage count based on number of rules */
|
|
|
|
duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
|
|
|
|
oldinfo->number, oldinfo->initial_entries, newinfo->number);
|
|
|
|
if ((oldinfo->number > oldinfo->initial_entries) ||
|
|
|
|
(newinfo->number <= oldinfo->initial_entries))
|
|
|
|
module_put(t->me);
|
|
|
|
if ((oldinfo->number > oldinfo->initial_entries) &&
|
|
|
|
(newinfo->number <= oldinfo->initial_entries))
|
|
|
|
module_put(t->me);
|
|
|
|
|
2009-04-29 05:36:33 +00:00
|
|
|
/* Get the old counters, and synchronize with replace */
|
2006-04-01 10:25:19 +00:00
|
|
|
get_counters(oldinfo, counters);
|
2009-04-29 05:36:33 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
/* Decrease module usage counts and free resource */
|
|
|
|
loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter, loc_cpu_old_entry, oldinfo->size)
|
2010-02-24 17:33:43 +00:00
|
|
|
cleanup_entry(iter, net);
|
2010-02-24 17:32:59 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
xt_free_table_info(oldinfo);
|
|
|
|
if (copy_to_user(counters_ptr, counters,
|
|
|
|
sizeof(struct xt_counters) * num_counters) != 0)
|
|
|
|
ret = -EFAULT;
|
|
|
|
vfree(counters);
|
|
|
|
xt_table_unlock(t);
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
put_module:
|
|
|
|
module_put(t->me);
|
|
|
|
xt_table_unlock(t);
|
|
|
|
free_newinfo_counters_untrans:
|
|
|
|
vfree(counters);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-06-26 05:51:59 +00:00
|
|
|
do_replace(struct net *net, const void __user *user, unsigned int len)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ipt_replace tmp;
|
|
|
|
struct xt_table_info *newinfo;
|
|
|
|
void *loc_cpu_entry;
|
2010-02-24 17:32:59 +00:00
|
|
|
struct ipt_entry *iter;
|
2006-04-01 10:25:19 +00:00
|
|
|
|
|
|
|
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
/* overflow check */
|
|
|
|
if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
|
|
|
|
return -ENOMEM;
|
2011-03-15 12:36:05 +00:00
|
|
|
tmp.name[sizeof(tmp.name)-1] = 0;
|
2006-04-01 10:25:19 +00:00
|
|
|
|
|
|
|
newinfo = xt_alloc_table_info(tmp.size);
|
|
|
|
if (!newinfo)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2007-12-18 05:52:00 +00:00
|
|
|
/* choose the copy that is on our node/cpu */
|
2006-04-01 10:25:19 +00:00
|
|
|
loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
|
|
|
|
if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
|
|
|
|
tmp.size) != 0) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto free_newinfo;
|
|
|
|
}
|
|
|
|
|
2010-02-24 17:36:04 +00:00
|
|
|
ret = translate_table(net, newinfo, loc_cpu_entry, &tmp);
|
2006-04-01 10:25:19 +00:00
|
|
|
if (ret != 0)
|
|
|
|
goto free_newinfo;
|
|
|
|
|
2010-03-19 20:08:16 +00:00
|
|
|
duprintf("Translated table\n");
|
2006-04-01 10:25:19 +00:00
|
|
|
|
2008-01-31 12:03:03 +00:00
|
|
|
ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
|
2007-12-18 05:46:15 +00:00
|
|
|
tmp.num_counters, tmp.counters);
|
2006-04-01 10:25:19 +00:00
|
|
|
if (ret)
|
|
|
|
goto free_newinfo_untrans;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
free_newinfo_untrans:
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
|
2010-02-24 17:33:43 +00:00
|
|
|
cleanup_entry(iter, net);
|
2006-04-01 10:25:19 +00:00
|
|
|
free_newinfo:
|
|
|
|
xt_free_table_info(newinfo);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2009-06-26 05:51:59 +00:00
|
|
|
do_add_counters(struct net *net, const void __user *user,
|
|
|
|
unsigned int len, int compat)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
2009-04-29 05:36:33 +00:00
|
|
|
unsigned int i, curcpu;
|
2006-04-01 10:25:19 +00:00
|
|
|
struct xt_counters_info tmp;
|
|
|
|
struct xt_counters *paddc;
|
|
|
|
unsigned int num_counters;
|
2008-04-14 09:15:35 +00:00
|
|
|
const char *name;
|
2006-04-01 10:25:19 +00:00
|
|
|
int size;
|
|
|
|
void *ptmp;
|
2007-02-07 23:12:33 +00:00
|
|
|
struct xt_table *t;
|
2008-04-14 09:15:35 +00:00
|
|
|
const struct xt_table_info *private;
|
2006-04-01 10:25:19 +00:00
|
|
|
int ret = 0;
|
|
|
|
void *loc_cpu_entry;
|
2010-02-24 17:32:59 +00:00
|
|
|
struct ipt_entry *iter;
|
2006-04-01 10:25:19 +00:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
struct compat_xt_counters_info compat_tmp;
|
|
|
|
|
|
|
|
if (compat) {
|
|
|
|
ptmp = &compat_tmp;
|
|
|
|
size = sizeof(struct compat_xt_counters_info);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
ptmp = &tmp;
|
|
|
|
size = sizeof(struct xt_counters_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (copy_from_user(ptmp, user, size) != 0)
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
if (compat) {
|
|
|
|
num_counters = compat_tmp.num_counters;
|
|
|
|
name = compat_tmp.name;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
num_counters = tmp.num_counters;
|
|
|
|
name = tmp.name;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len != size + num_counters * sizeof(struct xt_counters))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-06-04 11:31:29 +00:00
|
|
|
paddc = vmalloc(len - size);
|
2006-04-01 10:25:19 +00:00
|
|
|
if (!paddc)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (copy_from_user(paddc, user + size, len - size) != 0) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
|
2008-01-31 12:03:03 +00:00
|
|
|
t = xt_find_table_lock(net, AF_INET, name);
|
2006-04-01 10:25:19 +00:00
|
|
|
if (!t || IS_ERR(t)) {
|
|
|
|
ret = t ? PTR_ERR(t) : -ENOENT;
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
|
2009-04-29 05:36:33 +00:00
|
|
|
local_bh_disable();
|
2006-04-01 10:25:19 +00:00
|
|
|
private = t->private;
|
|
|
|
if (private->number != num_counters) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto unlock_up_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
/* Choose the copy that is on our node */
|
2009-04-29 05:36:33 +00:00
|
|
|
curcpu = smp_processor_id();
|
|
|
|
loc_cpu_entry = private->entries[curcpu];
|
|
|
|
xt_info_wrlock(curcpu);
|
2010-02-24 17:33:43 +00:00
|
|
|
xt_entry_foreach(iter, loc_cpu_entry, private->size) {
|
|
|
|
ADD_COUNTER(iter->counters, paddc[i].bcnt, paddc[i].pcnt);
|
|
|
|
++i;
|
|
|
|
}
|
2009-04-29 05:36:33 +00:00
|
|
|
xt_info_wrunlock(curcpu);
|
2006-04-01 10:25:19 +00:00
|
|
|
unlock_up_free:
|
2009-04-29 05:36:33 +00:00
|
|
|
local_bh_enable();
|
2006-04-01 10:25:19 +00:00
|
|
|
xt_table_unlock(t);
|
|
|
|
module_put(t->me);
|
|
|
|
free:
|
|
|
|
vfree(paddc);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
struct compat_ipt_replace {
|
2010-10-13 13:56:56 +00:00
|
|
|
char name[XT_TABLE_MAXNAMELEN];
|
2006-04-01 10:25:19 +00:00
|
|
|
u32 valid_hooks;
|
|
|
|
u32 num_entries;
|
|
|
|
u32 size;
|
2007-11-20 02:53:30 +00:00
|
|
|
u32 hook_entry[NF_INET_NUMHOOKS];
|
|
|
|
u32 underflow[NF_INET_NUMHOOKS];
|
2006-04-01 10:25:19 +00:00
|
|
|
u32 num_counters;
|
2010-10-13 14:11:22 +00:00
|
|
|
compat_uptr_t counters; /* struct xt_counters * */
|
2006-04-01 10:25:19 +00:00
|
|
|
struct compat_ipt_entry entries[0];
|
|
|
|
};
|
|
|
|
|
2007-12-12 18:35:16 +00:00
|
|
|
static int
|
|
|
|
compat_copy_entry_to_user(struct ipt_entry *e, void __user **dstptr,
|
2008-01-31 12:10:18 +00:00
|
|
|
unsigned int *size, struct xt_counters *counters,
|
2010-02-24 17:33:43 +00:00
|
|
|
unsigned int i)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
2010-10-13 14:11:22 +00:00
|
|
|
struct xt_entry_target *t;
|
2006-04-01 10:25:19 +00:00
|
|
|
struct compat_ipt_entry __user *ce;
|
|
|
|
u_int16_t target_offset, next_offset;
|
|
|
|
compat_uint_t origsize;
|
2010-02-24 17:34:48 +00:00
|
|
|
const struct xt_entry_match *ematch;
|
|
|
|
int ret = 0;
|
2006-04-01 10:25:19 +00:00
|
|
|
|
|
|
|
origsize = *size;
|
|
|
|
ce = (struct compat_ipt_entry __user *)*dstptr;
|
2010-02-24 17:33:43 +00:00
|
|
|
if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 ||
|
|
|
|
copy_to_user(&ce->counters, &counters[i],
|
|
|
|
sizeof(counters[i])) != 0)
|
|
|
|
return -EFAULT;
|
2007-12-12 18:35:16 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
*dstptr += sizeof(struct compat_ipt_entry);
|
2007-12-18 05:47:14 +00:00
|
|
|
*size -= sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
|
|
|
|
|
2010-02-24 17:34:48 +00:00
|
|
|
xt_ematch_foreach(ematch, e) {
|
|
|
|
ret = xt_compat_match_to_user(ematch, dstptr, size);
|
|
|
|
if (ret != 0)
|
2010-02-24 17:35:37 +00:00
|
|
|
return ret;
|
2010-02-24 17:34:48 +00:00
|
|
|
}
|
2006-04-01 10:25:19 +00:00
|
|
|
target_offset = e->target_offset - (origsize - *size);
|
|
|
|
t = ipt_get_target(e);
|
2006-09-20 19:05:37 +00:00
|
|
|
ret = xt_compat_target_to_user(t, dstptr, size);
|
2006-04-01 10:25:19 +00:00
|
|
|
if (ret)
|
2010-02-24 17:33:43 +00:00
|
|
|
return ret;
|
2006-04-01 10:25:19 +00:00
|
|
|
next_offset = e->next_offset - (origsize - *size);
|
2010-02-24 17:33:43 +00:00
|
|
|
if (put_user(target_offset, &ce->target_offset) != 0 ||
|
|
|
|
put_user(next_offset, &ce->next_offset) != 0)
|
|
|
|
return -EFAULT;
|
2006-04-01 10:25:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-15 07:44:05 +00:00
|
|
|
static int
|
2010-10-13 14:11:22 +00:00
|
|
|
compat_find_calc_match(struct xt_entry_match *m,
|
2007-12-18 05:46:15 +00:00
|
|
|
const char *name,
|
|
|
|
const struct ipt_ip *ip,
|
|
|
|
unsigned int hookmask,
|
2010-02-24 17:35:37 +00:00
|
|
|
int *size)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
2007-02-07 23:11:19 +00:00
|
|
|
struct xt_match *match;
|
2006-04-01 10:25:19 +00:00
|
|
|
|
2009-07-10 17:27:47 +00:00
|
|
|
match = xt_request_find_match(NFPROTO_IPV4, m->u.user.name,
|
|
|
|
m->u.user.revision);
|
|
|
|
if (IS_ERR(match)) {
|
2006-04-01 10:25:19 +00:00
|
|
|
duprintf("compat_check_calc_match: `%s' not found\n",
|
2007-12-18 05:46:15 +00:00
|
|
|
m->u.user.name);
|
2009-07-10 17:27:47 +00:00
|
|
|
return PTR_ERR(match);
|
2006-04-01 10:25:19 +00:00
|
|
|
}
|
|
|
|
m->u.kernel.match = match;
|
2006-09-20 19:05:37 +00:00
|
|
|
*size += xt_compat_match_offset(match);
|
2007-06-05 19:56:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-24 17:33:43 +00:00
|
|
|
static void compat_release_entry(struct compat_ipt_entry *e)
|
2007-06-05 19:56:09 +00:00
|
|
|
{
|
2010-10-13 14:11:22 +00:00
|
|
|
struct xt_entry_target *t;
|
2010-02-24 17:34:48 +00:00
|
|
|
struct xt_entry_match *ematch;
|
2007-06-05 19:56:09 +00:00
|
|
|
|
|
|
|
/* Cleanup all matches */
|
2010-02-24 17:34:48 +00:00
|
|
|
xt_ematch_foreach(ematch, e)
|
2010-02-24 17:35:37 +00:00
|
|
|
module_put(ematch->u.kernel.match->me);
|
2007-12-18 05:47:32 +00:00
|
|
|
t = compat_ipt_get_target(e);
|
2007-06-05 19:56:09 +00:00
|
|
|
module_put(t->u.kernel.target->me);
|
|
|
|
}
|
|
|
|
|
2008-01-15 07:44:05 +00:00
|
|
|
static int
|
2007-12-18 05:47:32 +00:00
|
|
|
check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
|
2007-12-18 05:46:15 +00:00
|
|
|
struct xt_table_info *newinfo,
|
|
|
|
unsigned int *size,
|
2009-06-26 05:51:59 +00:00
|
|
|
const unsigned char *base,
|
|
|
|
const unsigned char *limit,
|
|
|
|
const unsigned int *hook_entries,
|
|
|
|
const unsigned int *underflows,
|
2007-12-18 05:46:15 +00:00
|
|
|
const char *name)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
2010-02-24 17:34:48 +00:00
|
|
|
struct xt_entry_match *ematch;
|
2010-10-13 14:11:22 +00:00
|
|
|
struct xt_entry_target *t;
|
2007-02-07 23:11:19 +00:00
|
|
|
struct xt_target *target;
|
2007-01-04 20:14:41 +00:00
|
|
|
unsigned int entry_offset;
|
2008-01-31 12:10:18 +00:00
|
|
|
unsigned int j;
|
|
|
|
int ret, off, h;
|
2006-04-01 10:25:19 +00:00
|
|
|
|
|
|
|
duprintf("check_compat_entry_size_and_hooks %p\n", e);
|
2009-11-23 22:17:06 +00:00
|
|
|
if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
|
|
|
|
(unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
|
2006-04-01 10:25:19 +00:00
|
|
|
duprintf("Bad offset %p, limit = %p\n", e, limit);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e->next_offset < sizeof(struct compat_ipt_entry) +
|
2007-12-18 05:46:15 +00:00
|
|
|
sizeof(struct compat_xt_entry_target)) {
|
2006-04-01 10:25:19 +00:00
|
|
|
duprintf("checking: element %p size %u\n",
|
|
|
|
e, e->next_offset);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2007-12-18 05:47:32 +00:00
|
|
|
/* For purposes of check_entry casting the compat entry is fine */
|
|
|
|
ret = check_entry((struct ipt_entry *)e, name);
|
2006-12-12 08:29:26 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
[NETFILTER]: Missed and reordered checks in {arp,ip,ip6}_tables
There is a number of issues in parsing user-provided table in
translate_table(). Malicious user with CAP_NET_ADMIN may crash system by
passing special-crafted table to the *_tables.
The first issue is that mark_source_chains() function is called before entry
content checks. In case of standard target, mark_source_chains() function
uses t->verdict field in order to determine new position. But the check, that
this field leads no further, than the table end, is in check_entry(), which
is called later, than mark_source_chains().
The second issue, that there is no check that target_offset points inside
entry. If so, *_ITERATE_MATCH macro will follow further, than the entry
ends. As a result, we'll have oops or memory disclosure.
And the third issue, that there is no check that the target is completely
inside entry. Results are the same, as in previous issue.
Signed-off-by: Dmitry Mishin <dim@openvz.org>
Acked-by: Kirill Korotaev <dev@openvz.org>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-10-30 23:12:55 +00:00
|
|
|
|
2007-12-18 05:47:14 +00:00
|
|
|
off = sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
|
2006-04-01 10:25:19 +00:00
|
|
|
entry_offset = (void *)e - (void *)base;
|
|
|
|
j = 0;
|
2010-02-24 17:34:48 +00:00
|
|
|
xt_ematch_foreach(ematch, e) {
|
|
|
|
ret = compat_find_calc_match(ematch, name,
|
2010-02-26 16:53:31 +00:00
|
|
|
&e->ip, e->comefrom, &off);
|
2010-02-24 17:34:48 +00:00
|
|
|
if (ret != 0)
|
2010-02-24 17:35:37 +00:00
|
|
|
goto release_matches;
|
|
|
|
++j;
|
2010-02-24 17:34:48 +00:00
|
|
|
}
|
2006-04-01 10:25:19 +00:00
|
|
|
|
2007-12-18 05:47:32 +00:00
|
|
|
t = compat_ipt_get_target(e);
|
2009-07-10 16:55:11 +00:00
|
|
|
target = xt_request_find_target(NFPROTO_IPV4, t->u.user.name,
|
|
|
|
t->u.user.revision);
|
|
|
|
if (IS_ERR(target)) {
|
2006-12-12 08:29:26 +00:00
|
|
|
duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
|
2007-12-18 05:46:15 +00:00
|
|
|
t->u.user.name);
|
2009-07-10 16:55:11 +00:00
|
|
|
ret = PTR_ERR(target);
|
2007-06-05 19:56:09 +00:00
|
|
|
goto release_matches;
|
2006-04-01 10:25:19 +00:00
|
|
|
}
|
|
|
|
t->u.kernel.target = target;
|
|
|
|
|
2006-09-20 19:05:37 +00:00
|
|
|
off += xt_compat_target_offset(target);
|
2006-04-01 10:25:19 +00:00
|
|
|
*size += off;
|
2007-12-18 05:47:48 +00:00
|
|
|
ret = xt_compat_add_offset(AF_INET, entry_offset, off);
|
2006-04-01 10:25:19 +00:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Check hooks & underflows */
|
2007-11-20 02:53:30 +00:00
|
|
|
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
|
2006-04-01 10:25:19 +00:00
|
|
|
if ((unsigned char *)e - base == hook_entries[h])
|
|
|
|
newinfo->hook_entry[h] = hook_entries[h];
|
|
|
|
if ((unsigned char *)e - base == underflows[h])
|
|
|
|
newinfo->underflow[h] = underflows[h];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear counters and comefrom */
|
2007-12-18 05:47:32 +00:00
|
|
|
memset(&e->counters, 0, sizeof(e->counters));
|
2006-04-01 10:25:19 +00:00
|
|
|
e->comefrom = 0;
|
|
|
|
return 0;
|
2006-09-20 19:04:08 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
out:
|
2006-09-20 19:04:08 +00:00
|
|
|
module_put(t->u.kernel.target->me);
|
2007-06-05 19:56:09 +00:00
|
|
|
release_matches:
|
2010-02-24 17:35:37 +00:00
|
|
|
xt_ematch_foreach(ematch, e) {
|
|
|
|
if (j-- == 0)
|
2010-02-24 17:34:48 +00:00
|
|
|
break;
|
2010-02-24 17:35:37 +00:00
|
|
|
module_put(ematch->u.kernel.match->me);
|
|
|
|
}
|
2006-04-01 10:25:19 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-12-18 05:46:15 +00:00
|
|
|
static int
|
2007-12-18 05:47:32 +00:00
|
|
|
compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
|
2007-12-18 05:46:15 +00:00
|
|
|
unsigned int *size, const char *name,
|
|
|
|
struct xt_table_info *newinfo, unsigned char *base)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
2010-10-13 14:11:22 +00:00
|
|
|
struct xt_entry_target *t;
|
2007-02-07 23:11:19 +00:00
|
|
|
struct xt_target *target;
|
2006-04-01 10:25:19 +00:00
|
|
|
struct ipt_entry *de;
|
|
|
|
unsigned int origsize;
|
2006-10-30 23:14:27 +00:00
|
|
|
int ret, h;
|
2010-02-24 17:34:48 +00:00
|
|
|
struct xt_entry_match *ematch;
|
2006-04-01 10:25:19 +00:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
origsize = *size;
|
|
|
|
de = (struct ipt_entry *)*dstptr;
|
|
|
|
memcpy(de, e, sizeof(struct ipt_entry));
|
2007-12-18 05:47:32 +00:00
|
|
|
memcpy(&de->counters, &e->counters, sizeof(e->counters));
|
2006-04-01 10:25:19 +00:00
|
|
|
|
2007-12-18 05:47:32 +00:00
|
|
|
*dstptr += sizeof(struct ipt_entry);
|
2007-12-18 05:47:14 +00:00
|
|
|
*size += sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
|
|
|
|
|
2010-02-24 17:34:48 +00:00
|
|
|
xt_ematch_foreach(ematch, e) {
|
|
|
|
ret = xt_compat_match_from_user(ematch, dstptr, size);
|
|
|
|
if (ret != 0)
|
2010-02-24 17:35:37 +00:00
|
|
|
return ret;
|
2010-02-24 17:34:48 +00:00
|
|
|
}
|
2006-04-01 10:25:19 +00:00
|
|
|
de->target_offset = e->target_offset - (origsize - *size);
|
2007-12-18 05:47:32 +00:00
|
|
|
t = compat_ipt_get_target(e);
|
2006-04-01 10:25:19 +00:00
|
|
|
target = t->u.kernel.target;
|
2006-09-20 19:05:37 +00:00
|
|
|
xt_compat_target_from_user(t, dstptr, size);
|
2006-04-01 10:25:19 +00:00
|
|
|
|
|
|
|
de->next_offset = e->next_offset - (origsize - *size);
|
2007-11-20 02:53:30 +00:00
|
|
|
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
|
2006-04-01 10:25:19 +00:00
|
|
|
if ((unsigned char *)de - base < newinfo->hook_entry[h])
|
|
|
|
newinfo->hook_entry[h] -= origsize - *size;
|
|
|
|
if ((unsigned char *)de - base < newinfo->underflow[h])
|
|
|
|
newinfo->underflow[h] -= origsize - *size;
|
|
|
|
}
|
2006-12-05 21:44:07 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-01-15 07:44:05 +00:00
|
|
|
static int
|
2010-02-24 17:33:43 +00:00
|
|
|
compat_check_entry(struct ipt_entry *e, struct net *net, const char *name)
|
2006-12-05 21:44:07 +00:00
|
|
|
{
|
2010-02-24 17:34:48 +00:00
|
|
|
struct xt_entry_match *ematch;
|
2008-10-08 09:35:18 +00:00
|
|
|
struct xt_mtchk_param mtpar;
|
2008-01-31 12:10:18 +00:00
|
|
|
unsigned int j;
|
2010-02-24 17:34:48 +00:00
|
|
|
int ret = 0;
|
2006-12-05 21:44:07 +00:00
|
|
|
|
2007-06-05 19:56:09 +00:00
|
|
|
j = 0;
|
2010-01-18 07:21:13 +00:00
|
|
|
mtpar.net = net;
|
2008-10-08 09:35:18 +00:00
|
|
|
mtpar.table = name;
|
|
|
|
mtpar.entryinfo = &e->ip;
|
|
|
|
mtpar.hook_mask = e->comefrom;
|
2008-10-08 09:35:20 +00:00
|
|
|
mtpar.family = NFPROTO_IPV4;
|
2010-02-24 17:34:48 +00:00
|
|
|
xt_ematch_foreach(ematch, e) {
|
2010-02-24 17:35:37 +00:00
|
|
|
ret = check_match(ematch, &mtpar);
|
2010-02-24 17:34:48 +00:00
|
|
|
if (ret != 0)
|
2010-02-24 17:35:37 +00:00
|
|
|
goto cleanup_matches;
|
|
|
|
++j;
|
2010-02-24 17:34:48 +00:00
|
|
|
}
|
2007-06-05 19:56:09 +00:00
|
|
|
|
2010-02-03 12:45:12 +00:00
|
|
|
ret = check_target(e, net, name);
|
2007-06-05 19:56:09 +00:00
|
|
|
if (ret)
|
|
|
|
goto cleanup_matches;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cleanup_matches:
|
2010-02-24 17:35:37 +00:00
|
|
|
xt_ematch_foreach(ematch, e) {
|
|
|
|
if (j-- == 0)
|
2010-02-24 17:34:48 +00:00
|
|
|
break;
|
2010-02-24 17:35:37 +00:00
|
|
|
cleanup_match(ematch, net);
|
|
|
|
}
|
2007-06-05 19:56:09 +00:00
|
|
|
return ret;
|
2006-12-05 21:44:07 +00:00
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static int
|
2010-01-18 07:21:13 +00:00
|
|
|
translate_compat_table(struct net *net,
|
|
|
|
const char *name,
|
2007-12-18 05:46:15 +00:00
|
|
|
unsigned int valid_hooks,
|
|
|
|
struct xt_table_info **pinfo,
|
|
|
|
void **pentry0,
|
|
|
|
unsigned int total_size,
|
|
|
|
unsigned int number,
|
|
|
|
unsigned int *hook_entries,
|
|
|
|
unsigned int *underflows)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2006-10-30 23:14:27 +00:00
|
|
|
unsigned int i, j;
|
2006-04-01 10:25:19 +00:00
|
|
|
struct xt_table_info *newinfo, *info;
|
|
|
|
void *pos, *entry0, *entry1;
|
2010-02-24 17:32:59 +00:00
|
|
|
struct compat_ipt_entry *iter0;
|
|
|
|
struct ipt_entry *iter1;
|
2006-04-01 10:25:19 +00:00
|
|
|
unsigned int size;
|
2010-02-24 17:33:43 +00:00
|
|
|
int ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
info = *pinfo;
|
|
|
|
entry0 = *pentry0;
|
|
|
|
size = total_size;
|
|
|
|
info->number = number;
|
|
|
|
|
|
|
|
/* Init all hooks to impossible value. */
|
2007-11-20 02:53:30 +00:00
|
|
|
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
2006-04-01 10:25:19 +00:00
|
|
|
info->hook_entry[i] = 0xFFFFFFFF;
|
|
|
|
info->underflow[i] = 0xFFFFFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
duprintf("translate_compat_table: size %u\n", info->size);
|
2006-10-30 23:14:27 +00:00
|
|
|
j = 0;
|
2006-04-01 10:25:19 +00:00
|
|
|
xt_compat_lock(AF_INET);
|
2010-12-18 17:35:15 +00:00
|
|
|
xt_compat_init_offsets(AF_INET, number);
|
2006-04-01 10:25:19 +00:00
|
|
|
/* Walk through entries, checking offsets. */
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter0, entry0, total_size) {
|
|
|
|
ret = check_compat_entry_size_and_hooks(iter0, info, &size,
|
2010-02-26 16:53:31 +00:00
|
|
|
entry0,
|
|
|
|
entry0 + total_size,
|
|
|
|
hook_entries,
|
|
|
|
underflows,
|
|
|
|
name);
|
2010-02-24 17:32:59 +00:00
|
|
|
if (ret != 0)
|
2010-02-24 17:33:43 +00:00
|
|
|
goto out_unlock;
|
|
|
|
++j;
|
2010-02-24 17:32:59 +00:00
|
|
|
}
|
2006-04-01 10:25:19 +00:00
|
|
|
|
|
|
|
ret = -EINVAL;
|
2006-10-30 23:14:27 +00:00
|
|
|
if (j != number) {
|
2006-04-01 10:25:19 +00:00
|
|
|
duprintf("translate_compat_table: %u not %u entries\n",
|
2006-10-30 23:14:27 +00:00
|
|
|
j, number);
|
2006-04-01 10:25:19 +00:00
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check hooks all assigned */
|
2007-11-20 02:53:30 +00:00
|
|
|
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
2006-04-01 10:25:19 +00:00
|
|
|
/* Only hooks which are valid */
|
|
|
|
if (!(valid_hooks & (1 << i)))
|
|
|
|
continue;
|
|
|
|
if (info->hook_entry[i] == 0xFFFFFFFF) {
|
|
|
|
duprintf("Invalid hook entry %u %u\n",
|
|
|
|
i, hook_entries[i]);
|
|
|
|
goto out_unlock;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2006-04-01 10:25:19 +00:00
|
|
|
if (info->underflow[i] == 0xFFFFFFFF) {
|
|
|
|
duprintf("Invalid underflow %u %u\n",
|
|
|
|
i, underflows[i]);
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
newinfo = xt_alloc_table_info(size);
|
|
|
|
if (!newinfo)
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
newinfo->number = number;
|
2007-11-20 02:53:30 +00:00
|
|
|
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
|
2006-04-01 10:25:19 +00:00
|
|
|
newinfo->hook_entry[i] = info->hook_entry[i];
|
|
|
|
newinfo->underflow[i] = info->underflow[i];
|
|
|
|
}
|
|
|
|
entry1 = newinfo->entries[raw_smp_processor_id()];
|
|
|
|
pos = entry1;
|
2007-12-18 05:46:15 +00:00
|
|
|
size = total_size;
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter0, entry0, total_size) {
|
2010-02-26 16:53:31 +00:00
|
|
|
ret = compat_copy_entry_from_user(iter0, &pos, &size,
|
|
|
|
name, newinfo, entry1);
|
2010-02-24 17:32:59 +00:00
|
|
|
if (ret != 0)
|
|
|
|
break;
|
|
|
|
}
|
2007-12-18 05:47:48 +00:00
|
|
|
xt_compat_flush_offsets(AF_INET);
|
2006-04-01 10:25:19 +00:00
|
|
|
xt_compat_unlock(AF_INET);
|
|
|
|
if (ret)
|
|
|
|
goto free_newinfo;
|
|
|
|
|
|
|
|
ret = -ELOOP;
|
|
|
|
if (!mark_source_chains(newinfo, valid_hooks, entry1))
|
|
|
|
goto free_newinfo;
|
|
|
|
|
2007-06-05 19:56:09 +00:00
|
|
|
i = 0;
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter1, entry1, newinfo->size) {
|
2010-02-24 17:33:43 +00:00
|
|
|
ret = compat_check_entry(iter1, net, name);
|
2010-02-24 17:32:59 +00:00
|
|
|
if (ret != 0)
|
|
|
|
break;
|
2010-02-24 17:33:43 +00:00
|
|
|
++i;
|
2010-08-23 21:41:22 +00:00
|
|
|
if (strcmp(ipt_get_target(iter1)->u.user.name,
|
|
|
|
XT_ERROR_TARGET) == 0)
|
|
|
|
++newinfo->stacksize;
|
2010-02-24 17:32:59 +00:00
|
|
|
}
|
2007-06-05 19:56:09 +00:00
|
|
|
if (ret) {
|
2010-02-24 17:32:59 +00:00
|
|
|
/*
|
|
|
|
* The first i matches need cleanup_entry (calls ->destroy)
|
|
|
|
* because they had called ->check already. The other j-i
|
|
|
|
* entries need only release.
|
|
|
|
*/
|
|
|
|
int skip = i;
|
2007-06-05 19:56:09 +00:00
|
|
|
j -= i;
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter0, entry0, newinfo->size) {
|
|
|
|
if (skip-- > 0)
|
|
|
|
continue;
|
2010-02-24 17:33:43 +00:00
|
|
|
if (j-- == 0)
|
2010-02-24 17:32:59 +00:00
|
|
|
break;
|
2010-02-24 17:33:43 +00:00
|
|
|
compat_release_entry(iter0);
|
2010-02-24 17:32:59 +00:00
|
|
|
}
|
2010-02-24 17:33:43 +00:00
|
|
|
xt_entry_foreach(iter1, entry1, newinfo->size) {
|
|
|
|
if (i-- == 0)
|
2010-02-24 17:32:59 +00:00
|
|
|
break;
|
2010-02-24 17:33:43 +00:00
|
|
|
cleanup_entry(iter1, net);
|
|
|
|
}
|
2007-06-05 19:56:09 +00:00
|
|
|
xt_free_table_info(newinfo);
|
|
|
|
return ret;
|
|
|
|
}
|
2006-12-05 21:44:07 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
/* And one copy for every other CPU */
|
2006-06-25 12:46:43 +00:00
|
|
|
for_each_possible_cpu(i)
|
2006-04-01 10:25:19 +00:00
|
|
|
if (newinfo->entries[i] && newinfo->entries[i] != entry1)
|
|
|
|
memcpy(newinfo->entries[i], entry1, newinfo->size);
|
|
|
|
|
|
|
|
*pinfo = newinfo;
|
|
|
|
*pentry0 = entry1;
|
|
|
|
xt_free_table_info(info);
|
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
free_newinfo:
|
|
|
|
xt_free_table_info(newinfo);
|
|
|
|
out:
|
2010-02-24 17:33:43 +00:00
|
|
|
xt_entry_foreach(iter0, entry0, total_size) {
|
|
|
|
if (j-- == 0)
|
2010-02-24 17:32:59 +00:00
|
|
|
break;
|
2010-02-24 17:33:43 +00:00
|
|
|
compat_release_entry(iter0);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
return ret;
|
2006-04-01 10:25:19 +00:00
|
|
|
out_unlock:
|
2007-12-18 05:47:48 +00:00
|
|
|
xt_compat_flush_offsets(AF_INET);
|
2006-04-01 10:25:19 +00:00
|
|
|
xt_compat_unlock(AF_INET);
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2008-01-31 12:03:03 +00:00
|
|
|
compat_do_replace(struct net *net, void __user *user, unsigned int len)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2006-04-01 10:25:19 +00:00
|
|
|
struct compat_ipt_replace tmp;
|
|
|
|
struct xt_table_info *newinfo;
|
|
|
|
void *loc_cpu_entry;
|
2010-02-24 17:32:59 +00:00
|
|
|
struct ipt_entry *iter;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
|
|
|
|
return -EFAULT;
|
|
|
|
|
2006-02-04 10:16:56 +00:00
|
|
|
/* overflow check */
|
2007-12-05 07:24:56 +00:00
|
|
|
if (tmp.size >= INT_MAX / num_possible_cpus())
|
2006-02-04 10:16:56 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
|
|
|
|
return -ENOMEM;
|
2011-03-15 12:36:05 +00:00
|
|
|
tmp.name[sizeof(tmp.name)-1] = 0;
|
2006-02-04 10:16:56 +00:00
|
|
|
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
newinfo = xt_alloc_table_info(tmp.size);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!newinfo)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2007-12-18 05:52:00 +00:00
|
|
|
/* choose the copy that is on our node/cpu */
|
2005-12-14 07:13:48 +00:00
|
|
|
loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
|
|
|
|
if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
|
2005-04-16 22:20:36 +00:00
|
|
|
tmp.size) != 0) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto free_newinfo;
|
|
|
|
}
|
|
|
|
|
2010-01-18 07:21:13 +00:00
|
|
|
ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
|
2007-12-18 05:46:15 +00:00
|
|
|
&newinfo, &loc_cpu_entry, tmp.size,
|
|
|
|
tmp.num_entries, tmp.hook_entry,
|
|
|
|
tmp.underflow);
|
2006-04-01 10:25:19 +00:00
|
|
|
if (ret != 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
goto free_newinfo;
|
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
duprintf("compat_do_replace: Translated table\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-01-31 12:03:03 +00:00
|
|
|
ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
|
2007-12-18 05:46:15 +00:00
|
|
|
tmp.num_counters, compat_ptr(tmp.counters));
|
2006-04-01 10:25:19 +00:00
|
|
|
if (ret)
|
|
|
|
goto free_newinfo_untrans;
|
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
free_newinfo_untrans:
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
|
2010-02-24 17:33:43 +00:00
|
|
|
cleanup_entry(iter, net);
|
2006-04-01 10:25:19 +00:00
|
|
|
free_newinfo:
|
|
|
|
xt_free_table_info(newinfo);
|
|
|
|
return ret;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
static int
|
|
|
|
compat_do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user,
|
2007-12-18 05:46:15 +00:00
|
|
|
unsigned int len)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
if (!capable(CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case IPT_SO_SET_REPLACE:
|
2008-03-25 17:26:21 +00:00
|
|
|
ret = compat_do_replace(sock_net(sk), user, len);
|
2006-04-01 10:25:19 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
case IPT_SO_SET_ADD_COUNTERS:
|
2008-03-25 17:26:21 +00:00
|
|
|
ret = do_add_counters(sock_net(sk), user, len, 1);
|
2006-04-01 10:25:19 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-12-18 05:46:15 +00:00
|
|
|
struct compat_ipt_get_entries {
|
2010-10-13 13:56:56 +00:00
|
|
|
char name[XT_TABLE_MAXNAMELEN];
|
2006-04-01 10:25:19 +00:00
|
|
|
compat_uint_t size;
|
|
|
|
struct compat_ipt_entry entrytable[0];
|
|
|
|
};
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-12-18 05:46:15 +00:00
|
|
|
static int
|
|
|
|
compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
|
|
|
|
void __user *userptr)
|
2006-04-01 10:25:19 +00:00
|
|
|
{
|
|
|
|
struct xt_counters *counters;
|
2008-04-14 09:15:35 +00:00
|
|
|
const struct xt_table_info *private = table->private;
|
2006-04-01 10:25:19 +00:00
|
|
|
void __user *pos;
|
|
|
|
unsigned int size;
|
|
|
|
int ret = 0;
|
2008-04-14 09:15:35 +00:00
|
|
|
const void *loc_cpu_entry;
|
2007-12-12 18:35:16 +00:00
|
|
|
unsigned int i = 0;
|
2010-02-24 17:32:59 +00:00
|
|
|
struct ipt_entry *iter;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
counters = alloc_counters(table);
|
|
|
|
if (IS_ERR(counters))
|
|
|
|
return PTR_ERR(counters);
|
|
|
|
|
|
|
|
/* choose the copy that is on our node/cpu, ...
|
|
|
|
* This choice is lazy (because current thread is
|
|
|
|
* allowed to migrate to another cpu)
|
|
|
|
*/
|
|
|
|
loc_cpu_entry = private->entries[raw_smp_processor_id()];
|
|
|
|
pos = userptr;
|
|
|
|
size = total_size;
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter, loc_cpu_entry, total_size) {
|
|
|
|
ret = compat_copy_entry_to_user(iter, &pos,
|
2010-02-26 16:53:31 +00:00
|
|
|
&size, counters, i++);
|
2010-02-24 17:32:59 +00:00
|
|
|
if (ret != 0)
|
|
|
|
break;
|
|
|
|
}
|
2006-04-01 10:25:19 +00:00
|
|
|
|
|
|
|
vfree(counters);
|
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2008-01-31 12:03:03 +00:00
|
|
|
compat_get_entries(struct net *net, struct compat_ipt_get_entries __user *uptr,
|
|
|
|
int *len)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2006-04-01 10:25:19 +00:00
|
|
|
int ret;
|
|
|
|
struct compat_ipt_get_entries get;
|
2007-02-07 23:12:33 +00:00
|
|
|
struct xt_table *t;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
if (*len < sizeof(get)) {
|
2007-12-18 05:52:15 +00:00
|
|
|
duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
2006-04-01 10:25:19 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
if (copy_from_user(&get, uptr, sizeof(get)) != 0)
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
if (*len != sizeof(struct compat_ipt_get_entries) + get.size) {
|
2007-12-18 05:52:15 +00:00
|
|
|
duprintf("compat_get_entries: %u != %zu\n",
|
|
|
|
*len, sizeof(get) + get.size);
|
2006-04-01 10:25:19 +00:00
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
xt_compat_lock(AF_INET);
|
2008-01-31 12:03:03 +00:00
|
|
|
t = xt_find_table_lock(net, AF_INET, get.name);
|
2006-04-01 10:25:19 +00:00
|
|
|
if (t && !IS_ERR(t)) {
|
2008-04-14 09:15:35 +00:00
|
|
|
const struct xt_table_info *private = t->private;
|
2006-04-01 10:25:19 +00:00
|
|
|
struct xt_table_info info;
|
2007-12-18 05:52:00 +00:00
|
|
|
duprintf("t->private->number = %u\n", private->number);
|
2006-04-01 10:25:19 +00:00
|
|
|
ret = compat_table_info(private, &info);
|
|
|
|
if (!ret && get.size == info.size) {
|
|
|
|
ret = compat_copy_entries_to_user(private->size,
|
2007-12-18 05:46:15 +00:00
|
|
|
t, uptr->entrytable);
|
2006-04-01 10:25:19 +00:00
|
|
|
} else if (!ret) {
|
|
|
|
duprintf("compat_get_entries: I've got %u not %u!\n",
|
2007-12-18 05:52:00 +00:00
|
|
|
private->size, get.size);
|
2008-04-14 09:15:45 +00:00
|
|
|
ret = -EAGAIN;
|
2006-04-01 10:25:19 +00:00
|
|
|
}
|
2007-12-18 05:47:48 +00:00
|
|
|
xt_compat_flush_offsets(AF_INET);
|
2006-04-01 10:25:19 +00:00
|
|
|
module_put(t->me);
|
|
|
|
xt_table_unlock(t);
|
|
|
|
} else
|
2005-04-16 22:20:36 +00:00
|
|
|
ret = t ? PTR_ERR(t) : -ENOENT;
|
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
xt_compat_unlock(AF_INET);
|
|
|
|
return ret;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-09-20 19:05:08 +00:00
|
|
|
static int do_ipt_get_ctl(struct sock *, int, void __user *, int *);
|
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
static int
|
|
|
|
compat_do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
|
|
|
|
{
|
|
|
|
int ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-10-20 07:21:10 +00:00
|
|
|
if (!capable(CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case IPT_SO_GET_INFO:
|
2008-03-25 17:26:21 +00:00
|
|
|
ret = get_info(sock_net(sk), user, len, 1);
|
2006-04-01 10:25:19 +00:00
|
|
|
break;
|
|
|
|
case IPT_SO_GET_ENTRIES:
|
2008-03-25 17:26:21 +00:00
|
|
|
ret = compat_get_entries(sock_net(sk), user, len);
|
2006-04-01 10:25:19 +00:00
|
|
|
break;
|
|
|
|
default:
|
2006-09-20 19:05:08 +00:00
|
|
|
ret = do_ipt_get_ctl(sk, cmd, user, len);
|
2006-04-01 10:25:19 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2006-04-01 10:25:19 +00:00
|
|
|
#endif
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
static int
|
2007-12-18 05:52:00 +00:00
|
|
|
do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!capable(CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case IPT_SO_SET_REPLACE:
|
2008-03-25 17:26:21 +00:00
|
|
|
ret = do_replace(sock_net(sk), user, len);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IPT_SO_SET_ADD_COUNTERS:
|
2008-03-25 17:26:21 +00:00
|
|
|
ret = do_add_counters(sock_net(sk), user, len, 0);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!capable(CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
switch (cmd) {
|
2006-04-01 10:25:19 +00:00
|
|
|
case IPT_SO_GET_INFO:
|
2008-03-25 17:26:21 +00:00
|
|
|
ret = get_info(sock_net(sk), user, len, 0);
|
2006-04-01 10:25:19 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-04-01 10:25:19 +00:00
|
|
|
case IPT_SO_GET_ENTRIES:
|
2008-03-25 17:26:21 +00:00
|
|
|
ret = get_entries(sock_net(sk), user, len);
|
2005-04-16 22:20:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IPT_SO_GET_REVISION_MATCH:
|
|
|
|
case IPT_SO_GET_REVISION_TARGET: {
|
2010-10-13 13:56:56 +00:00
|
|
|
struct xt_get_revision rev;
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
int target;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (*len != sizeof(rev)) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
2011-03-15 12:36:05 +00:00
|
|
|
rev.name[sizeof(rev.name)-1] = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (cmd == IPT_SO_GET_REVISION_TARGET)
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
target = 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
else
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
target = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
try_then_request_module(xt_find_revision(AF_INET, rev.name,
|
|
|
|
rev.revision,
|
|
|
|
target, &ret),
|
2005-04-16 22:20:36 +00:00
|
|
|
"ipt_%s", rev.name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
duprintf("do_ipt_get_ctl: unknown request %i\n", cmd);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-08-24 12:56:30 +00:00
|
|
|
struct xt_table *ipt_register_table(struct net *net,
|
|
|
|
const struct xt_table *table,
|
2008-01-31 12:02:44 +00:00
|
|
|
const struct ipt_replace *repl)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int ret;
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
struct xt_table_info *newinfo;
|
2010-04-19 14:05:10 +00:00
|
|
|
struct xt_table_info bootstrap = {0};
|
2005-12-14 07:13:48 +00:00
|
|
|
void *loc_cpu_entry;
|
2008-01-31 12:01:49 +00:00
|
|
|
struct xt_table *new_table;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
newinfo = xt_alloc_table_info(repl->size);
|
2008-01-31 12:02:44 +00:00
|
|
|
if (!newinfo) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-12-18 05:52:00 +00:00
|
|
|
/* choose the copy on our node/cpu, but dont care about preemption */
|
2005-12-14 07:13:48 +00:00
|
|
|
loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
|
|
|
|
memcpy(loc_cpu_entry, repl->entries, repl->size);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-02-24 17:36:04 +00:00
|
|
|
ret = translate_table(net, newinfo, loc_cpu_entry, repl);
|
2008-01-31 12:02:44 +00:00
|
|
|
if (ret != 0)
|
|
|
|
goto out_free;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-01-31 12:02:44 +00:00
|
|
|
new_table = xt_register_table(net, table, &bootstrap, newinfo);
|
2008-01-31 12:01:49 +00:00
|
|
|
if (IS_ERR(new_table)) {
|
2008-01-31 12:02:44 +00:00
|
|
|
ret = PTR_ERR(new_table);
|
|
|
|
goto out_free;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2008-01-31 12:02:44 +00:00
|
|
|
return new_table;
|
|
|
|
|
|
|
|
out_free:
|
|
|
|
xt_free_table_info(newinfo);
|
|
|
|
out:
|
|
|
|
return ERR_PTR(ret);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2010-01-18 07:25:47 +00:00
|
|
|
void ipt_unregister_table(struct net *net, struct xt_table *table)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
struct xt_table_info *private;
|
2005-12-14 07:13:48 +00:00
|
|
|
void *loc_cpu_entry;
|
2008-01-31 12:05:34 +00:00
|
|
|
struct module *table_owner = table->me;
|
2010-02-24 17:32:59 +00:00
|
|
|
struct ipt_entry *iter;
|
2005-12-14 07:13:48 +00:00
|
|
|
|
2007-02-09 14:24:47 +00:00
|
|
|
private = xt_unregister_table(table);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Decrease module usage counts and free resources */
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
loc_cpu_entry = private->entries[raw_smp_processor_id()];
|
2010-02-24 17:32:59 +00:00
|
|
|
xt_entry_foreach(iter, loc_cpu_entry, private->size)
|
2010-02-24 17:33:43 +00:00
|
|
|
cleanup_entry(iter, net);
|
2008-01-31 12:05:34 +00:00
|
|
|
if (private->number > private->initial_entries)
|
|
|
|
module_put(table_owner);
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
xt_free_table_info(private);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns 1 if the type and code is matched by the range, 0 otherwise */
|
2007-07-08 05:15:35 +00:00
|
|
|
static inline bool
|
2005-04-16 22:20:36 +00:00
|
|
|
icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
|
|
|
|
u_int8_t type, u_int8_t code,
|
2007-07-08 05:15:35 +00:00
|
|
|
bool invert)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-12-18 05:52:00 +00:00
|
|
|
return ((test_type == 0xFF) ||
|
|
|
|
(type == test_type && code >= min_code && code <= max_code))
|
2005-04-16 22:20:36 +00:00
|
|
|
^ invert;
|
|
|
|
}
|
|
|
|
|
2007-07-08 05:15:35 +00:00
|
|
|
static bool
|
2009-07-07 18:42:08 +00:00
|
|
|
icmp_match(const struct sk_buff *skb, struct xt_action_param *par)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-04-14 09:15:35 +00:00
|
|
|
const struct icmphdr *ic;
|
|
|
|
struct icmphdr _icmph;
|
2008-10-08 09:35:18 +00:00
|
|
|
const struct ipt_icmp *icmpinfo = par->matchinfo;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Must not be a fragment. */
|
2008-10-08 09:35:18 +00:00
|
|
|
if (par->fragoff != 0)
|
2007-07-08 05:15:35 +00:00
|
|
|
return false;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-10-08 09:35:18 +00:00
|
|
|
ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (ic == NULL) {
|
|
|
|
/* We've been asked to examine this packet, and we
|
|
|
|
* can't. Hence, no choice but to drop.
|
|
|
|
*/
|
|
|
|
duprintf("Dropping evil ICMP tinygram.\n");
|
2009-07-07 18:54:30 +00:00
|
|
|
par->hotdrop = true;
|
2007-07-08 05:15:35 +00:00
|
|
|
return false;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return icmp_type_code_match(icmpinfo->type,
|
|
|
|
icmpinfo->code[0],
|
|
|
|
icmpinfo->code[1],
|
|
|
|
ic->type, ic->code,
|
|
|
|
!!(icmpinfo->invflags&IPT_ICMP_INV));
|
|
|
|
}
|
|
|
|
|
2010-03-19 16:16:42 +00:00
|
|
|
static int icmp_checkentry(const struct xt_mtchk_param *par)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-10-08 09:35:18 +00:00
|
|
|
const struct ipt_icmp *icmpinfo = par->matchinfo;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-03-21 02:01:14 +00:00
|
|
|
/* Must specify no unknown invflags */
|
2010-03-23 15:35:56 +00:00
|
|
|
return (icmpinfo->invflags & ~IPT_ICMP_INV) ? -EINVAL : 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2009-07-04 10:50:00 +00:00
|
|
|
static struct xt_target ipt_builtin_tg[] __read_mostly = {
|
|
|
|
{
|
2010-10-13 14:28:00 +00:00
|
|
|
.name = XT_STANDARD_TARGET,
|
2009-07-04 10:50:00 +00:00
|
|
|
.targetsize = sizeof(int),
|
|
|
|
.family = NFPROTO_IPV4,
|
2006-04-01 10:25:19 +00:00
|
|
|
#ifdef CONFIG_COMPAT
|
2009-07-04 10:50:00 +00:00
|
|
|
.compatsize = sizeof(compat_int_t),
|
|
|
|
.compat_from_user = compat_standard_from_user,
|
|
|
|
.compat_to_user = compat_standard_to_user,
|
2006-04-01 10:25:19 +00:00
|
|
|
#endif
|
2009-07-04 10:50:00 +00:00
|
|
|
},
|
|
|
|
{
|
2010-10-13 14:28:00 +00:00
|
|
|
.name = XT_ERROR_TARGET,
|
2009-07-04 10:50:00 +00:00
|
|
|
.target = ipt_error,
|
2010-10-13 13:56:56 +00:00
|
|
|
.targetsize = XT_FUNCTION_MAXNAMELEN,
|
2009-07-04 10:50:00 +00:00
|
|
|
.family = NFPROTO_IPV4,
|
|
|
|
},
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct nf_sockopt_ops ipt_sockopts = {
|
|
|
|
.pf = PF_INET,
|
|
|
|
.set_optmin = IPT_BASE_CTL,
|
|
|
|
.set_optmax = IPT_SO_SET_MAX+1,
|
|
|
|
.set = do_ipt_set_ctl,
|
2006-04-01 10:25:19 +00:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_set = compat_do_ipt_set_ctl,
|
|
|
|
#endif
|
2005-04-16 22:20:36 +00:00
|
|
|
.get_optmin = IPT_BASE_CTL,
|
|
|
|
.get_optmax = IPT_SO_GET_MAX+1,
|
|
|
|
.get = do_ipt_get_ctl,
|
2006-04-01 10:25:19 +00:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_get = compat_do_ipt_get_ctl,
|
|
|
|
#endif
|
[NETFILTER]: Fix/improve deadlock condition on module removal netfilter
So I've had a deadlock reported to me. I've found that the sequence of
events goes like this:
1) process A (modprobe) runs to remove ip_tables.ko
2) process B (iptables-restore) runs and calls setsockopt on a netfilter socket,
increasing the ip_tables socket_ops use count
3) process A acquires a file lock on the file ip_tables.ko, calls remove_module
in the kernel, which in turn executes the ip_tables module cleanup routine,
which calls nf_unregister_sockopt
4) nf_unregister_sockopt, seeing that the use count is non-zero, puts the
calling process into uninterruptible sleep, expecting the process using the
socket option code to wake it up when it exits the kernel
4) the user of the socket option code (process B) in do_ipt_get_ctl, calls
ipt_find_table_lock, which in this case calls request_module to load
ip_tables_nat.ko
5) request_module forks a copy of modprobe (process C) to load the module and
blocks until modprobe exits.
6) Process C. forked by request_module process the dependencies of
ip_tables_nat.ko, of which ip_tables.ko is one.
7) Process C attempts to lock the request module and all its dependencies, it
blocks when it attempts to lock ip_tables.ko (which was previously locked in
step 3)
Theres not really any great permanent solution to this that I can see, but I've
developed a two part solution that corrects the problem
Part 1) Modifies the nf_sockopt registration code so that, instead of using a
use counter internal to the nf_sockopt_ops structure, we instead use a pointer
to the registering modules owner to do module reference counting when nf_sockopt
calls a modules set/get routine. This prevents the deadlock by preventing set 4
from happening.
Part 2) Enhances the modprobe utilty so that by default it preforms non-blocking
remove operations (the same way rmmod does), and add an option to explicity
request blocking operation. So if you select blocking operation in modprobe you
can still cause the above deadlock, but only if you explicity try (and since
root can do any old stupid thing it would like.... :) ).
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-09-11 09:28:26 +00:00
|
|
|
.owner = THIS_MODULE,
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
2009-07-04 10:50:00 +00:00
|
|
|
static struct xt_match ipt_builtin_mt[] __read_mostly = {
|
|
|
|
{
|
|
|
|
.name = "icmp",
|
|
|
|
.match = icmp_match,
|
|
|
|
.matchsize = sizeof(struct ipt_icmp),
|
|
|
|
.checkentry = icmp_checkentry,
|
|
|
|
.proto = IPPROTO_ICMP,
|
|
|
|
.family = NFPROTO_IPV4,
|
|
|
|
},
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
2008-01-31 12:49:35 +00:00
|
|
|
static int __net_init ip_tables_net_init(struct net *net)
|
|
|
|
{
|
2009-04-14 12:24:21 +00:00
|
|
|
return xt_proto_init(net, NFPROTO_IPV4);
|
2008-01-31 12:49:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __net_exit ip_tables_net_exit(struct net *net)
|
|
|
|
{
|
2009-04-14 12:24:21 +00:00
|
|
|
xt_proto_fini(net, NFPROTO_IPV4);
|
2008-01-31 12:49:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct pernet_operations ip_tables_net_ops = {
|
|
|
|
.init = ip_tables_net_init,
|
|
|
|
.exit = ip_tables_net_exit,
|
|
|
|
};
|
|
|
|
|
2006-03-29 00:37:06 +00:00
|
|
|
static int __init ip_tables_init(void)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2008-01-31 12:49:35 +00:00
|
|
|
ret = register_pernet_subsys(&ip_tables_net_ops);
|
2006-08-14 01:57:28 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto err1;
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
|
2011-03-31 01:57:33 +00:00
|
|
|
/* No one else will be downing sem now, so we won't sleep */
|
2009-07-04 10:50:00 +00:00
|
|
|
ret = xt_register_targets(ipt_builtin_tg, ARRAY_SIZE(ipt_builtin_tg));
|
2006-08-14 01:57:28 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto err2;
|
2009-07-04 10:50:00 +00:00
|
|
|
ret = xt_register_matches(ipt_builtin_mt, ARRAY_SIZE(ipt_builtin_mt));
|
2006-08-14 01:57:28 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto err4;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Register setsockopt */
|
|
|
|
ret = nf_register_sockopt(&ipt_sockopts);
|
2006-08-14 01:57:28 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto err5;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-03-19 20:08:16 +00:00
|
|
|
pr_info("(C) 2000-2006 Netfilter Core Team\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
2006-08-14 01:57:28 +00:00
|
|
|
|
|
|
|
err5:
|
2009-07-04 10:50:00 +00:00
|
|
|
xt_unregister_matches(ipt_builtin_mt, ARRAY_SIZE(ipt_builtin_mt));
|
2006-08-14 01:57:28 +00:00
|
|
|
err4:
|
2009-07-04 10:50:00 +00:00
|
|
|
xt_unregister_targets(ipt_builtin_tg, ARRAY_SIZE(ipt_builtin_tg));
|
2006-08-14 01:57:28 +00:00
|
|
|
err2:
|
2008-01-31 12:49:35 +00:00
|
|
|
unregister_pernet_subsys(&ip_tables_net_ops);
|
2006-08-14 01:57:28 +00:00
|
|
|
err1:
|
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-03-29 00:37:06 +00:00
|
|
|
static void __exit ip_tables_fini(void)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
nf_unregister_sockopt(&ipt_sockopts);
|
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-12 21:30:04 +00:00
|
|
|
|
2009-07-04 10:50:00 +00:00
|
|
|
xt_unregister_matches(ipt_builtin_mt, ARRAY_SIZE(ipt_builtin_mt));
|
|
|
|
xt_unregister_targets(ipt_builtin_tg, ARRAY_SIZE(ipt_builtin_tg));
|
2008-01-31 12:49:35 +00:00
|
|
|
unregister_pernet_subsys(&ip_tables_net_ops);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL(ipt_register_table);
|
|
|
|
EXPORT_SYMBOL(ipt_unregister_table);
|
|
|
|
EXPORT_SYMBOL(ipt_do_table);
|
2006-03-29 00:37:06 +00:00
|
|
|
module_init(ip_tables_init);
|
|
|
|
module_exit(ip_tables_fini);
|