-#ifdef CONFIG_COMPAT
-struct compat_ipt_replace {
- char name[IPT_TABLE_MAXNAMELEN];
- u32 valid_hooks;
- u32 num_entries;
- u32 size;
- u32 hook_entry[NF_IP_NUMHOOKS];
- u32 underflow[NF_IP_NUMHOOKS];
- u32 num_counters;
- compat_uptr_t counters; /* struct ipt_counters * */
- struct compat_ipt_entry entries[0];
-};
-
-static inline int compat_copy_match_to_user(struct ipt_entry_match *m,
- void __user **dstptr, compat_uint_t *size)
-{
- if (m->u.kernel.match->compat)
- return m->u.kernel.match->compat(m, dstptr, size,
- COMPAT_TO_USER);
- else
- return xt_compat_match(m, dstptr, size, COMPAT_TO_USER);
-}
-
-static int compat_copy_entry_to_user(struct ipt_entry *e,
- void __user **dstptr, compat_uint_t *size)
-{
- struct ipt_entry_target __user *t;
- struct compat_ipt_entry __user *ce;
- u_int16_t target_offset, next_offset;
- compat_uint_t origsize;
- int ret;
-
- ret = -EFAULT;
- origsize = *size;
- ce = (struct compat_ipt_entry __user *)*dstptr;
- if (copy_to_user(ce, e, sizeof(struct ipt_entry)))
- goto out;
-
- *dstptr += sizeof(struct compat_ipt_entry);
- ret = IPT_MATCH_ITERATE(e, compat_copy_match_to_user, dstptr, size);
- target_offset = e->target_offset - (origsize - *size);
- if (ret)
- goto out;
- t = ipt_get_target(e);
- if (t->u.kernel.target->compat)
- ret = t->u.kernel.target->compat(t, dstptr, size,
- COMPAT_TO_USER);
- else
- ret = xt_compat_target(t, dstptr, size, COMPAT_TO_USER);
- if (ret)
- goto out;
- ret = -EFAULT;
- next_offset = e->next_offset - (origsize - *size);
- if (put_user(target_offset, &ce->target_offset))
- goto out;
- if (put_user(next_offset, &ce->next_offset))
- goto out;
- return 0;
-out:
- return ret;
-}
-
-static inline int
-compat_check_calc_match(struct ipt_entry_match *m,
- const char *name,
- const struct ipt_ip *ip,
- unsigned int hookmask,
- int *size, int *i)
-{
- struct ipt_match *match;
-
- match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
- m->u.user.revision),
- "ipt_%s", m->u.user.name);
- if (IS_ERR(match) || !match) {
- duprintf("compat_check_calc_match: `%s' not found\n",
- m->u.user.name);
- return match ? PTR_ERR(match) : -ENOENT;
- }
- m->u.kernel.match = match;
-
- if (m->u.kernel.match->compat)
- m->u.kernel.match->compat(m, NULL, size, COMPAT_CALC_SIZE);
- else
- xt_compat_match(m, NULL, size, COMPAT_CALC_SIZE);
-
- (*i)++;
- return 0;
-}
-
-static inline int
-check_compat_entry_size_and_hooks(struct ipt_entry *e,
- struct xt_table_info *newinfo,
- unsigned int *size,
- unsigned char *base,
- unsigned char *limit,
- unsigned int *hook_entries,
- unsigned int *underflows,
- unsigned int *i,
- const char *name)
-{
- struct ipt_entry_target *t;
- struct ipt_target *target;
- u_int16_t entry_offset;
- int ret, off, h, j;
-
- 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;
- }
-
- if (!ip_checkentry(&e->ip)) {
- duprintf("ip_tables: ip check failed %p %s.\n", e, name);
- return -EINVAL;
- }
-
- if (e->target_offset + sizeof(struct compat_xt_entry_target) >
- e->next_offset)
- return -EINVAL;
-
- off = 0;
- entry_offset = (void *)e - (void *)base;
- j = 0;
- ret = IPT_MATCH_ITERATE(e, compat_check_calc_match, name, &e->ip,
- e->comefrom, &off, &j);
- if (ret != 0)
- goto cleanup_matches;
-
- t = ipt_get_target(e);
- ret = -EINVAL;
- if (e->target_offset + t->u.target_size > e->next_offset)
- goto cleanup_matches;
- target = try_then_request_module(xt_find_target(AF_INET,
- t->u.user.name,
- t->u.user.revision),
- "ipt_%s", t->u.user.name);
- if (IS_ERR(target) || !target) {
- duprintf("check_entry: `%s' not found\n", t->u.user.name);
- ret = target ? PTR_ERR(target) : -ENOENT;
- goto cleanup_matches;
- }
- t->u.kernel.target = target;
-
- if (t->u.kernel.target->compat)
- t->u.kernel.target->compat(t, NULL, &off, COMPAT_CALC_SIZE);
- else
- xt_compat_target(t, NULL, &off, COMPAT_CALC_SIZE);
- *size += off;
- ret = compat_add_offset(entry_offset, off);
- if (ret)
- goto out;
-
- /* Check hooks & underflows */
- for (h = 0; h < NF_IP_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 */
- e->counters = ((struct ipt_counters) { 0, 0 });
- e->comefrom = 0;
-
- (*i)++;
- return 0;
-
-out:
- module_put(t->u.kernel.target->me);
-cleanup_matches:
- IPT_MATCH_ITERATE(e, cleanup_match, &j);
- return ret;
-}
-
-static inline int compat_copy_match_from_user(struct ipt_entry_match *m,
- void **dstptr, compat_uint_t *size, const char *name,
- const struct ipt_ip *ip, unsigned int hookmask)
-{
- struct ipt_entry_match *dm;
- struct ipt_match *match;
- int ret;
-
- dm = (struct ipt_entry_match *)*dstptr;
- match = m->u.kernel.match;
- if (match->compat)
- match->compat(m, dstptr, size, COMPAT_FROM_USER);
- else
- xt_compat_match(m, dstptr, size, COMPAT_FROM_USER);
-
- ret = xt_check_match(match, AF_INET, dm->u.match_size - sizeof(*dm),
- name, hookmask, ip->proto,
- ip->invflags & IPT_INV_PROTO);
-
- if (!ret && m->u.kernel.match->checkentry
- && !m->u.kernel.match->checkentry(name, ip, match, dm->data,
- dm->u.match_size - sizeof(*dm),
- hookmask)) {
- duprintf("ip_tables: check failed for `%s'.\n",
- m->u.kernel.match->name);
- ret = -EINVAL;
- }
- return ret;
-}
-
-static int compat_copy_entry_from_user(struct 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 ipt_target *target;
- struct ipt_entry *de;
- unsigned int origsize;
- int ret, h;
-
- ret = 0;
- origsize = *size;
- de = (struct ipt_entry *)*dstptr;
- memcpy(de, e, sizeof(struct ipt_entry));
-
- *dstptr += sizeof(struct compat_ipt_entry);
- ret = IPT_MATCH_ITERATE(e, compat_copy_match_from_user, dstptr, size,
- name, &de->ip, de->comefrom);
- if (ret)
- goto err;
- de->target_offset = e->target_offset - (origsize - *size);
- t = ipt_get_target(e);
- target = t->u.kernel.target;
- if (target->compat)
- target->compat(t, dstptr, size, COMPAT_FROM_USER);
- else
- xt_compat_target(t, dstptr, size, COMPAT_FROM_USER);
-
- de->next_offset = e->next_offset - (origsize - *size);
- for (h = 0; h < NF_IP_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;
- }
-
- t = ipt_get_target(de);
- target = t->u.kernel.target;
- ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t),
- name, e->comefrom, e->ip.proto,
- e->ip.invflags & IPT_INV_PROTO);
- if (ret)
- goto err;
-
- if (t->u.kernel.target->checkentry
- && !t->u.kernel.target->checkentry(name, de, target,
- t->data, t->u.target_size - sizeof(*t),
- de->comefrom)) {
- duprintf("ip_tables: compat: check failed for `%s'.\n",
- t->u.kernel.target->name);
- ret = -EINVAL;
- goto err;
- }
- err:
- 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;
- 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_IP_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. */
- ret = IPT_ENTRY_ITERATE(entry0, total_size,
- check_compat_entry_size_and_hooks,
- info, &size, entry0,
- entry0 + total_size,
- hook_entries, underflows, &j, name);
- if (ret != 0)
- goto out_unlock;
-
- 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_IP_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_IP_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;
- ret = IPT_ENTRY_ITERATE(entry0, total_size,
- compat_copy_entry_from_user, &pos, &size,
- name, newinfo, entry1);
- compat_flush_offsets();
- xt_compat_unlock(AF_INET);
- if (ret)
- goto free_newinfo;
-
- ret = -ELOOP;
- if (!mark_source_chains(newinfo, valid_hooks, entry1))
- goto free_newinfo;
-
- /* 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:
- IPT_ENTRY_ITERATE(entry0, total_size, cleanup_entry, &j);
- return ret;
-out_unlock:
- compat_flush_offsets();
- xt_compat_unlock(AF_INET);
- goto out;
-}
-
-static int
-compat_do_replace(void __user *user, unsigned int len)
-{
- int ret;
- struct compat_ipt_replace tmp;
- struct xt_table_info *newinfo;
- void *loc_cpu_entry;
-
- if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
- return -EFAULT;
-
- /* Hack: Causes ipchains to give correct error msg --RR */
- if (len != sizeof(tmp) + tmp.size)
- return -ENOPROTOOPT;
-
- /* overflow check */
- if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
- SMP_CACHE_BYTES)
- 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 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(tmp.name, tmp.valid_hooks,
- newinfo, tmp.num_counters,
- compat_ptr(tmp.counters));
- if (ret)
- goto free_newinfo_untrans;
- return 0;
-
- free_newinfo_untrans:
- IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
- 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)