Signed-off-by: Jan Engelhardt <jengelh@xxxxxxxxxx> --- net/ipv4/netfilter/ip_tables.c | 792 ---------------------------------------- 1 files changed, 0 insertions(+), 792 deletions(-) diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 6a45da8..3c98c7d 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -1000,197 +1000,6 @@ translate_table(struct xt_table_info *newinfo, void *entry0, return ret; } -static void -get_counters(const struct xt_table_info *t, - struct xt_counters counters[]) -{ - struct ipt_entry *iter; - unsigned int cpu; - unsigned int i; - unsigned int curcpu; - - /* Instead of clearing (by a previous call to memset()) - * the counters and using adds, we set the counters - * with data used by 'current' CPU. - * - * Bottom half has to be disabled to prevent deadlock - * if new softirq were to run and call ipt_do_table - */ - local_bh_disable(); - curcpu = smp_processor_id(); - - i = 0; - xt_entry_foreach(iter, t->entries[curcpu], t->size) { - SET_COUNTER(counters[i], iter->counters.bcnt, - iter->counters.pcnt); - ++i; - } - - for_each_possible_cpu(cpu) { - if (cpu == curcpu) - continue; - i = 0; - xt_info_wrlock(cpu); - xt_entry_foreach(iter, t->entries[cpu], t->size) { - ADD_COUNTER(counters[i], iter->counters.bcnt, - iter->counters.pcnt); - ++i; /* macro does multi eval of i */ - } - xt_info_wrunlock(cpu); - } - local_bh_enable(); -} - -static struct xt_counters *alloc_counters(const struct xt_table *table) -{ - unsigned int countersize; - struct xt_counters *counters; - const struct xt_table_info *private = table->private; - - /* We need atomic snapshot of counters: rest doesn't change - (other than comefrom, which userspace doesn't care - about). */ - countersize = sizeof(struct xt_counters) * private->number; - counters = vmalloc_node(countersize, numa_node_id()); - - if (counters == NULL) - return ERR_PTR(-ENOMEM); - - get_counters(private, counters); - - return counters; -} - -static int -copy_entries_to_user(unsigned int total_size, - const struct xt_table *table, - void __user *userptr) -{ - unsigned int off, num; - const struct ipt_entry *e; - struct xt_counters *counters; - const struct xt_table_info *private = table->private; - int ret = 0; - const void *loc_cpu_entry; - - 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()]; - if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) { - 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; - const struct ipt_entry_match *m; - const struct ipt_entry_target *t; - - e = (struct ipt_entry *)(loc_cpu_entry + off); - 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 - + offsetof(struct ipt_entry_match, - u.user.name), - m->u.kernel.match->name, - strlen(m->u.kernel.match->name)+1) - != 0) { - ret = -EFAULT; - goto free_counters; - } - } - - t = ipt_get_target_c(e); - if (copy_to_user(userptr + off + e->target_offset - + offsetof(struct ipt_entry_target, - 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; -} - -#ifdef CONFIG_COMPAT -static int compat_calc_entry(const struct ipt_entry *e, - const struct xt_table_info *info, - const void *base, struct xt_table_info *newinfo) -{ - const struct xt_entry_match *ematch; - const struct ipt_entry_target *t; - unsigned int entry_offset; - int off, i, ret; - - off = sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry); - entry_offset = (void *)e - base; - xt_ematch_foreach(ematch, e) - off += xt_compat_match_offset(ematch->u.kernel.match); - - t = ipt_get_target_c(e); - off += xt_compat_target_offset(t->u.kernel.target); - newinfo->size -= off; - ret = xt_compat_add_offset(AF_INET, entry_offset, off); - if (ret) - return ret; - - for (i = 0; i < NF_INET_NUMHOOKS; i++) { - if (info->hook_entry[i] && - (e < (struct ipt_entry *)(base + info->hook_entry[i]))) - newinfo->hook_entry[i] -= off; - if (info->underflow[i] && - (e < (struct ipt_entry *)(base + info->underflow[i]))) - newinfo->underflow[i] -= off; - } - return 0; -} - -static int compat_table_info(const struct xt_table_info *info, - struct xt_table_info *newinfo) -{ - struct ipt_entry *iter; - void *loc_cpu_entry; - int ret; - - if (!newinfo || !info) - return -EINVAL; - - /* we dont care about newinfo->entries[] */ - memcpy(newinfo, info, offsetof(struct xt_table_info, entries)); - newinfo->initial_entries = 0; - loc_cpu_entry = info->entries[raw_smp_processor_id()]; - xt_entry_foreach(iter, loc_cpu_entry, info->size) { - ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo); - if (ret != 0) - return ret; - } - return 0; -} -#endif - static const struct xt1_xlat_info ipt_compat_xlat_info = { #ifdef CONFIG_COMPAT .marker_size = XT_ALIGN(sizeof(struct ipt_error_target)), @@ -1289,127 +1098,6 @@ get_entries(struct net *net, struct ipt_get_entries __user *uptr, } static int -__do_replace(struct net *net, const char *name, unsigned int valid_hooks, - struct xt_table_info *newinfo, unsigned int num_counters, - void __user *counters_ptr) -{ - int ret; - struct xt_table *t; - struct xt_table_info *oldinfo; - struct xt_counters *counters; - void *loc_cpu_old_entry; - struct ipt_entry *iter; - - ret = 0; - counters = vmalloc(num_counters * sizeof(struct xt_counters)); - if (!counters) { - ret = -ENOMEM; - goto out; - } - - t = try_then_request_module(xt_find_table_lock(net, AF_INET, name), - "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); - - /* Get the old counters, and synchronize with replace */ - get_counters(oldinfo, counters); - - /* Decrease module usage counts and free resource */ - loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()]; - xt_entry_foreach(iter, loc_cpu_old_entry, oldinfo->size) - cleanup_entry(iter); - - 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 -do_replace(struct net *net, const void __user *user, unsigned int len) -{ - int ret; - struct ipt_replace tmp; - struct xt_table_info *newinfo; - void *loc_cpu_entry; - struct ipt_entry *iter; - - 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; - - newinfo = xt_alloc_table_info(tmp.size); - if (!newinfo) - return -ENOMEM; - - /* choose the copy that is on our node/cpu */ - 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; - } - - ret = translate_table(newinfo, loc_cpu_entry, &tmp); - if (ret != 0) - goto free_newinfo; - - duprintf("ip_tables: Translated table\n"); - - ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo, - tmp.num_counters, tmp.counters); - if (ret) - goto free_newinfo_untrans; - return 0; - - free_newinfo_untrans: - xt_entry_foreach(iter, loc_cpu_entry, newinfo->size) - cleanup_entry(iter); - free_newinfo: - xt_free_table_info(newinfo); - return ret; -} - -static int do_add_counters(struct net *net, const void __user *user, unsigned int len, int compat) { @@ -1460,451 +1148,6 @@ do_add_counters(struct net *net, const void __user *user, #ifdef CONFIG_COMPAT static int -compat_copy_entry_to_user(struct ipt_entry *e, void __user **dstptr, - unsigned int *size, struct xt_counters *counters, - unsigned int i) -{ - struct ipt_entry_target *t; - struct compat_ipt_entry __user *ce; - u_int16_t target_offset, next_offset; - compat_uint_t origsize; - const struct xt_entry_match *ematch; - int ret = 0; - - origsize = *size; - ce = (struct compat_ipt_entry __user *)*dstptr; - if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 || - copy_to_user(&ce->counters, &counters[i], - sizeof(counters[i])) != 0) - return -EFAULT; - - *dstptr += sizeof(struct compat_ipt_entry); - *size -= sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry); - - xt_ematch_foreach(ematch, e) { - ret = xt_compat_match_to_user(ematch, dstptr, size); - if (ret != 0) - return ret; - } - target_offset = e->target_offset - (origsize - *size); - t = ipt_get_target(e); - ret = xt_compat_target_to_user(t, dstptr, size); - if (ret) - return ret; - next_offset = e->next_offset - (origsize - *size); - if (put_user(target_offset, &ce->target_offset) != 0 || - put_user(next_offset, &ce->next_offset) != 0) - return -EFAULT; - return 0; -} - -static int -compat_find_calc_match(struct ipt_entry_match *m, - const char *name, - const struct ipt_ip *ip, - unsigned int hookmask, - int *size) -{ - struct xt_match *match; - - match = xt_request_find_match(NFPROTO_IPV4, m->u.user.name, - m->u.user.revision); - if (IS_ERR(match)) { - duprintf("compat_check_calc_match: `%s' not found\n", - m->u.user.name); - return PTR_ERR(match); - } - m->u.kernel.match = match; - *size += xt_compat_match_offset(match); - return 0; -} - -static void compat_release_entry(struct compat_ipt_entry *e) -{ - struct ipt_entry_target *t; - struct xt_entry_match *ematch; - - /* Cleanup all matches */ - xt_ematch_foreach(ematch, e) - module_put(ematch->u.kernel.match->me); - t = compat_ipt_get_target(e); - module_put(t->u.kernel.target->me); -} - -static int -check_compat_entry_size_and_hooks(struct compat_ipt_entry *e, - struct xt_table_info *newinfo, - unsigned int *size, - const unsigned char *base, - const unsigned char *limit, - const unsigned int *hook_entries, - const unsigned int *underflows, - const char *name) -{ - struct xt_entry_match *ematch; - struct ipt_entry_target *t; - struct xt_target *target; - unsigned int entry_offset; - unsigned int j; - int ret, off, h; - struct xt_mtchk_param mtpar; - - duprintf("check_compat_entry_size_and_hooks %p\n", e); - if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 - || (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) { - duprintf("Bad offset %p, limit = %p\n", e, limit); - return -EINVAL; - } - - if (e->next_offset < sizeof(struct compat_ipt_entry) + - sizeof(struct compat_xt_entry_target)) { - duprintf("checking: element %p size %u\n", - e, e->next_offset); - return -EINVAL; - } - - /* For purposes of check_entry casting the compat entry is fine */ - mtpar.table = name; - mtpar.entryinfo = &e->ip; - mtpar.hook_mask = e->comefrom; - mtpar.family = NFPROTO_IPV4; - mtpar.match = &ipt_builtin_mt[0]; /* ipv4 */ - mtpar.matchinfo = &e->ip; - ret = check_entry((struct ipt_entry *)e, &mtpar); - if (ret) - return ret; - - off = sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry); - entry_offset = (void *)e - (void *)base; - j = 0; - xt_ematch_foreach(ematch, e) { - ret = compat_find_calc_match(ematch, name, - &e->ip, e->comefrom, &off); - if (ret != 0) - goto release_matches; - ++j; - } - - t = compat_ipt_get_target(e); - target = xt_request_find_target(NFPROTO_IPV4, t->u.user.name, - t->u.user.revision); - if (IS_ERR(target)) { - duprintf("check_compat_entry_size_and_hooks: `%s' not found\n", - t->u.user.name); - ret = PTR_ERR(target); - goto release_matches; - } - t->u.kernel.target = target; - - off += xt_compat_target_offset(target); - *size += off; - ret = xt_compat_add_offset(AF_INET, entry_offset, off); - if (ret) - goto out; - - /* Check hooks & underflows */ - for (h = 0; h < NF_INET_NUMHOOKS; h++) { - 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 */ - memset(&e->counters, 0, sizeof(e->counters)); - e->comefrom = 0; - return 0; - -out: - module_put(t->u.kernel.target->me); -release_matches: - xt_ematch_foreach(ematch, e) { - if (j-- == 0) - break; - module_put(ematch->u.kernel.match->me); - } - return ret; -} - -static int -compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr, - unsigned int *size, const char *name, - struct xt_table_info *newinfo, unsigned char *base) -{ - struct ipt_entry_target *t; - struct xt_target *target; - struct ipt_entry *de; - unsigned int origsize; - int ret, h; - struct xt_entry_match *ematch; - - ret = 0; - origsize = *size; - de = (struct ipt_entry *)*dstptr; - memcpy(de, e, sizeof(struct ipt_entry)); - memcpy(&de->counters, &e->counters, sizeof(e->counters)); - - *dstptr += sizeof(struct ipt_entry); - *size += sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry); - - xt_ematch_foreach(ematch, e) { - ret = xt_compat_match_from_user(ematch, dstptr, size); - if (ret != 0) - return ret; - } - de->target_offset = e->target_offset - (origsize - *size); - t = compat_ipt_get_target(e); - target = t->u.kernel.target; - xt_compat_target_from_user(t, dstptr, size); - - de->next_offset = e->next_offset - (origsize - *size); - for (h = 0; h < NF_INET_NUMHOOKS; h++) { - 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; - } - return ret; -} - -static int -compat_check_entry(struct ipt_entry *e, const char *name) -{ - struct xt_entry_match *ematch; - struct xt_mtchk_param mtpar; - unsigned int j; - int ret = 0; - - j = 0; - mtpar.table = name; - mtpar.entryinfo = &e->ip; - mtpar.hook_mask = e->comefrom; - mtpar.family = NFPROTO_IPV4; - xt_ematch_foreach(ematch, e) { - ret = check_match(ematch, &mtpar); - if (ret != 0) - goto cleanup_matches; - ++j; - } - - ret = check_target(e, name); - if (ret) - goto cleanup_matches; - return 0; - - cleanup_matches: - xt_ematch_foreach(ematch, e) { - if (j-- == 0) - break; - cleanup_match(ematch); - } - return ret; -} - -static int -translate_compat_table(const char *name, - 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) -{ - unsigned int i, j; - struct xt_table_info *newinfo, *info; - void *pos, *entry0, *entry1; - struct compat_ipt_entry *iter0; - struct ipt_entry *iter1; - unsigned int size; - int ret; - - info = *pinfo; - entry0 = *pentry0; - size = total_size; - info->number = number; - - /* Init all hooks to impossible value. */ - for (i = 0; i < NF_INET_NUMHOOKS; i++) { - info->hook_entry[i] = 0xFFFFFFFF; - info->underflow[i] = 0xFFFFFFFF; - } - - duprintf("translate_compat_table: size %u\n", info->size); - j = 0; - xt_compat_lock(AF_INET); - /* Walk through entries, checking offsets. */ - xt_entry_foreach(iter0, entry0, total_size) { - ret = check_compat_entry_size_and_hooks(iter0, info, &size, - entry0, entry0 + total_size, hook_entries, underflows, - name); - if (ret != 0) - goto out_unlock; - ++j; - } - - ret = -EINVAL; - if (j != number) { - duprintf("translate_compat_table: %u not %u entries\n", - j, number); - goto out_unlock; - } - - /* Check hooks all assigned */ - for (i = 0; i < NF_INET_NUMHOOKS; i++) { - /* 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; - } - 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; - for (i = 0; i < NF_INET_NUMHOOKS; i++) { - newinfo->hook_entry[i] = info->hook_entry[i]; - newinfo->underflow[i] = info->underflow[i]; - } - entry1 = newinfo->entries[raw_smp_processor_id()]; - pos = entry1; - size = total_size; - xt_entry_foreach(iter0, entry0, total_size) { - ret = compat_copy_entry_from_user(iter0, &pos, - &size, name, newinfo, entry1); - if (ret != 0) - break; - } - xt_compat_flush_offsets(AF_INET); - xt_compat_unlock(AF_INET); - if (ret) - goto free_newinfo; - - ret = -ELOOP; - if (!mark_source_chains(newinfo, valid_hooks, entry1)) - goto free_newinfo; - - i = 0; - xt_entry_foreach(iter1, entry1, newinfo->size) { - ret = compat_check_entry(iter1, name); - if (ret != 0) - break; - ++i; - } - if (ret) { - /* - * 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; - j -= i; - xt_entry_foreach(iter0, entry0, newinfo->size) { - if (skip-- > 0) - continue; - if (j-- == 0) - break; - compat_release_entry(iter0); - } - xt_entry_foreach(iter1, entry1, newinfo->size) { - if (i-- == 0) - break; - cleanup_entry(iter1); - } - xt_free_table_info(newinfo); - return ret; - } - - /* And one copy for every other CPU */ - for_each_possible_cpu(i) - 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; - -free_newinfo: - xt_free_table_info(newinfo); -out: - xt_entry_foreach(iter0, entry0, total_size) { - if (j-- == 0) - break; - compat_release_entry(iter0); - } - return ret; -out_unlock: - xt_compat_flush_offsets(AF_INET); - xt_compat_unlock(AF_INET); - goto out; -} - -static int -compat_do_replace(struct net *net, void __user *user, unsigned int len) -{ - int ret; - struct compat_ipt_replace tmp; - struct xt_table_info *newinfo; - void *loc_cpu_entry; - struct ipt_entry *iter; - - if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) - return -EFAULT; - - /* overflow check */ - if (tmp.size >= INT_MAX / num_possible_cpus()) - return -ENOMEM; - if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) - return -ENOMEM; - - newinfo = xt_alloc_table_info(tmp.size); - if (!newinfo) - return -ENOMEM; - - /* choose the copy that is on our node/cpu */ - 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; - } - - ret = translate_compat_table(tmp.name, tmp.valid_hooks, - &newinfo, &loc_cpu_entry, tmp.size, - tmp.num_entries, tmp.hook_entry, - tmp.underflow); - if (ret != 0) - goto free_newinfo; - - duprintf("compat_do_replace: Translated table\n"); - - ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo, - tmp.num_counters, compat_ptr(tmp.counters)); - if (ret) - goto free_newinfo_untrans; - return 0; - - free_newinfo_untrans: - xt_entry_foreach(iter, loc_cpu_entry, newinfo->size) - cleanup_entry(iter); - free_newinfo: - xt_free_table_info(newinfo); - return ret; -} - -static int compat_do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) { @@ -1937,41 +1180,6 @@ struct compat_ipt_get_entries { }; static int -compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table, - void __user *userptr) -{ - struct xt_counters *counters; - const struct xt_table_info *private = table->private; - void __user *pos; - unsigned int size; - int ret = 0; - const void *loc_cpu_entry; - unsigned int i = 0; - struct ipt_entry *iter; - - 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; - xt_entry_foreach(iter, loc_cpu_entry, total_size) { - ret = compat_copy_entry_to_user(iter, &pos, - &size, counters, i++); - if (ret != 0) - break; - } - - vfree(counters); - return ret; -} - -static int compat_get_entries(struct net *net, struct compat_ipt_get_entries __user *uptr, int *len) { -- 1.6.3.3 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html