diff options
Diffstat (limited to 'net/netfilter/x_tables.c')
-rw-r--r-- | net/netfilter/x_tables.c | 128 |
1 files changed, 112 insertions, 16 deletions
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 665f5beef6ad..445de702b8b7 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -12,7 +12,7 @@ * published by the Free Software Foundation. * */ - +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> #include <linux/socket.h> #include <linux/net.h> @@ -55,12 +55,6 @@ struct xt_af { static struct xt_af *xt; -#ifdef DEBUG_IP_FIREWALL_USER -#define duprintf(format, args...) printk(format , ## args) -#else -#define duprintf(format, args...) -#endif - static const char *const xt_prefix[NFPROTO_NUMPROTO] = { [NFPROTO_UNSPEC] = "x", [NFPROTO_IPV4] = "ip", @@ -69,6 +63,9 @@ static const char *const xt_prefix[NFPROTO_NUMPROTO] = { [NFPROTO_IPV6] = "ip6", }; +/* Allow this many total (re)entries. */ +static const unsigned int xt_jumpstack_multiplier = 2; + /* Registration hooks for targets. */ int xt_register_target(struct xt_target *target) @@ -221,6 +218,17 @@ struct xt_match *xt_find_match(u8 af, const char *name, u8 revision) } EXPORT_SYMBOL(xt_find_match); +struct xt_match * +xt_request_find_match(uint8_t nfproto, const char *name, uint8_t revision) +{ + struct xt_match *match; + + match = try_then_request_module(xt_find_match(nfproto, name, revision), + "%st_%s", xt_prefix[nfproto], name); + return (match != NULL) ? match : ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL_GPL(xt_request_find_match); + /* Find target, grabs ref. Returns ERR_PTR() on error. */ struct xt_target *xt_find_target(u8 af, const char *name, u8 revision) { @@ -257,9 +265,7 @@ struct xt_target *xt_request_find_target(u8 af, const char *name, u8 revision) target = try_then_request_module(xt_find_target(af, name, revision), "%st_%s", xt_prefix[af], name); - if (IS_ERR(target) || !target) - return NULL; - return target; + return (target != NULL) ? target : ERR_PTR(-ENOENT); } EXPORT_SYMBOL_GPL(xt_request_find_target); @@ -361,6 +367,8 @@ static char *textify_hooks(char *buf, size_t size, unsigned int mask) int xt_check_match(struct xt_mtchk_param *par, unsigned int size, u_int8_t proto, bool inv_proto) { + int ret; + if (XT_ALIGN(par->match->matchsize) != size && par->match->matchsize != -1) { /* @@ -397,8 +405,14 @@ int xt_check_match(struct xt_mtchk_param *par, par->match->proto); return -EINVAL; } - if (par->match->checkentry != NULL && !par->match->checkentry(par)) - return -EINVAL; + if (par->match->checkentry != NULL) { + ret = par->match->checkentry(par); + if (ret < 0) + return ret; + else if (ret > 0) + /* Flag up potential errors. */ + return -EIO; + } return 0; } EXPORT_SYMBOL_GPL(xt_check_match); @@ -518,6 +532,8 @@ EXPORT_SYMBOL_GPL(xt_compat_match_to_user); int xt_check_target(struct xt_tgchk_param *par, unsigned int size, u_int8_t proto, bool inv_proto) { + int ret; + if (XT_ALIGN(par->target->targetsize) != size) { pr_err("%s_tables: %s.%u target: invalid size " "%u (kernel) != (user) %u\n", @@ -549,8 +565,14 @@ int xt_check_target(struct xt_tgchk_param *par, par->target->proto); return -EINVAL; } - if (par->target->checkentry != NULL && !par->target->checkentry(par)) - return -EINVAL; + if (par->target->checkentry != NULL) { + ret = par->target->checkentry(par); + if (ret < 0) + return ret; + else if (ret > 0) + /* Flag up potential errors. */ + return -EIO; + } return 0; } EXPORT_SYMBOL_GPL(xt_check_target); @@ -662,6 +684,26 @@ void xt_free_table_info(struct xt_table_info *info) else vfree(info->entries[cpu]); } + + if (info->jumpstack != NULL) { + if (sizeof(void *) * info->stacksize > PAGE_SIZE) { + for_each_possible_cpu(cpu) + vfree(info->jumpstack[cpu]); + } else { + for_each_possible_cpu(cpu) + kfree(info->jumpstack[cpu]); + } + } + + if (sizeof(void **) * nr_cpu_ids > PAGE_SIZE) + vfree(info->jumpstack); + else + kfree(info->jumpstack); + if (sizeof(unsigned int) * nr_cpu_ids > PAGE_SIZE) + vfree(info->stackptr); + else + kfree(info->stackptr); + kfree(info); } EXPORT_SYMBOL(xt_free_table_info); @@ -706,6 +748,49 @@ EXPORT_SYMBOL_GPL(xt_compat_unlock); DEFINE_PER_CPU(struct xt_info_lock, xt_info_locks); EXPORT_PER_CPU_SYMBOL_GPL(xt_info_locks); +static int xt_jumpstack_alloc(struct xt_table_info *i) +{ + unsigned int size; + int cpu; + + size = sizeof(unsigned int) * nr_cpu_ids; + if (size > PAGE_SIZE) + i->stackptr = vmalloc(size); + else + i->stackptr = kmalloc(size, GFP_KERNEL); + if (i->stackptr == NULL) + return -ENOMEM; + memset(i->stackptr, 0, size); + + size = sizeof(void **) * nr_cpu_ids; + if (size > PAGE_SIZE) + i->jumpstack = vmalloc(size); + else + i->jumpstack = kmalloc(size, GFP_KERNEL); + if (i->jumpstack == NULL) + return -ENOMEM; + memset(i->jumpstack, 0, size); + + i->stacksize *= xt_jumpstack_multiplier; + size = sizeof(void *) * i->stacksize; + for_each_possible_cpu(cpu) { + if (size > PAGE_SIZE) + i->jumpstack[cpu] = vmalloc_node(size, + cpu_to_node(cpu)); + else + i->jumpstack[cpu] = kmalloc_node(size, + GFP_KERNEL, cpu_to_node(cpu)); + if (i->jumpstack[cpu] == NULL) + /* + * Freeing will be done later on by the callers. The + * chain is: xt_replace_table -> __do_replace -> + * do_replace -> xt_free_table_info. + */ + return -ENOMEM; + } + + return 0; +} struct xt_table_info * xt_replace_table(struct xt_table *table, @@ -714,6 +799,13 @@ xt_replace_table(struct xt_table *table, int *error) { struct xt_table_info *private; + int ret; + + ret = xt_jumpstack_alloc(newinfo); + if (ret < 0) { + *error = ret; + return NULL; + } /* Do the substitution. */ local_bh_disable(); @@ -721,7 +813,7 @@ xt_replace_table(struct xt_table *table, /* Check inside lock: is the old number correct? */ if (num_counters != private->number) { - duprintf("num_counters != table->private->number (%u/%u)\n", + pr_debug("num_counters != table->private->number (%u/%u)\n", num_counters, private->number); local_bh_enable(); *error = -EAGAIN; @@ -752,6 +844,10 @@ struct xt_table *xt_register_table(struct net *net, struct xt_table_info *private; struct xt_table *t, *table; + ret = xt_jumpstack_alloc(newinfo); + if (ret < 0) + return ERR_PTR(ret); + /* Don't add one object to multiple lists. */ table = kmemdup(input_table, sizeof(struct xt_table), GFP_KERNEL); if (!table) { @@ -778,7 +874,7 @@ struct xt_table *xt_register_table(struct net *net, goto unlock; private = table->private; - duprintf("table->private->number = %u\n", private->number); + pr_debug("table->private->number = %u\n", private->number); /* save number of initial entries */ private->initial_entries = private->number; |