#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
"report to author: "format, ## args)
/* #define BUGPRINT(format, args...) */
-#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
- ": out of memory: "format, ## args)
-/* #define MEMPRINT(format, args...) */
-
-
/*
* Each cpu has its own set of counters, so there is no need for write_lock in
return m->u.match->match(skb, par) ? EBT_MATCH : EBT_NOMATCH;
}
-static inline int ebt_dev_check(char *entry, const struct net_device *device)
+static inline int
+ebt_dev_check(const char *entry, const struct net_device *device)
{
int i = 0;
const char *devname;
#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
/* process standard matches */
-static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
- const struct net_device *in, const struct net_device *out)
+static inline int
+ebt_basic_match(const struct ebt_entry *e, const struct ethhdr *h,
+ const struct net_device *in, const struct net_device *out)
{
int verdict, i;
int i, nentries;
struct ebt_entry *point;
struct ebt_counter *counter_base, *cb_base;
- struct ebt_entry_target *t;
+ const struct ebt_entry_target *t;
int verdict, sp = 0;
struct ebt_chainstack *cs;
struct ebt_entries *chaininfo;
- char *base;
- struct ebt_table_info *private;
+ const char *base;
+ const struct ebt_table_info *private;
bool hotdrop = false;
struct xt_match_param mtpar;
struct xt_target_param tgpar;
return 0;
}
-static int ebt_verify_pointers(struct ebt_replace *repl,
+static int ebt_verify_pointers(const struct ebt_replace *repl,
struct ebt_table_info *newinfo)
{
unsigned int limit = repl->entries_size;
break;
if (left < e->next_offset)
break;
+ if (e->next_offset < sizeof(struct ebt_entry))
+ return -EINVAL;
offset += e->next_offset;
}
}
* to parse the userspace data
*/
static inline int
-ebt_check_entry_size_and_hooks(struct ebt_entry *e,
- struct ebt_table_info *newinfo,
+ebt_check_entry_size_and_hooks(const struct ebt_entry *e,
+ const struct ebt_table_info *newinfo,
unsigned int *n, unsigned int *cnt,
unsigned int *totalcnt, unsigned int *udc_cnt)
{
}
static inline int
-ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
+ebt_cleanup_match(struct ebt_entry_match *m, struct net *net, unsigned int *i)
{
struct xt_mtdtor_param par;
if (i && (*i)-- == 0)
return 1;
+ par.net = net;
par.match = m->u.match;
par.matchinfo = m->data;
par.family = NFPROTO_BRIDGE;
}
static inline int
-ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
+ebt_cleanup_watcher(struct ebt_entry_watcher *w, struct net *net, unsigned int *i)
{
struct xt_tgdtor_param par;
if (i && (*i)-- == 0)
return 1;
+ par.net = net;
par.target = w->u.watcher;
par.targinfo = w->data;
par.family = NFPROTO_BRIDGE;
}
static inline int
-ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
+ebt_cleanup_entry(struct ebt_entry *e, struct net *net, unsigned int *cnt)
{
struct xt_tgdtor_param par;
struct ebt_entry_target *t;
/* we're done */
if (cnt && (*cnt)-- == 0)
return 1;
- EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
- EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
+ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, net, NULL);
+ EBT_MATCH_ITERATE(e, ebt_cleanup_match, net, NULL);
t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+ par.net = net;
par.target = t->u.target;
par.targinfo = t->data;
par.family = NFPROTO_BRIDGE;
}
static inline int
-ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
+ebt_check_entry(struct ebt_entry *e, struct net *net,
+ const struct ebt_table_info *newinfo,
const char *name, unsigned int *cnt,
struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
{
}
i = 0;
+ mtpar.net = tgpar.net = net;
mtpar.table = tgpar.table = name;
mtpar.entryinfo = tgpar.entryinfo = e;
mtpar.hook_mask = tgpar.hook_mask = hookmask;
(*cnt)++;
return 0;
cleanup_watchers:
- EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
+ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, net, &j);
cleanup_matches:
- EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
+ EBT_MATCH_ITERATE(e, ebt_cleanup_match, net, &i);
return ret;
}
* the hook mask for udc tells us from which base chains the udc can be
* accessed. This mask is a parameter to the check() functions of the extensions
*/
-static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
+static int check_chainloops(const struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
unsigned int udc_cnt, unsigned int hooknr, char *base)
{
int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
- struct ebt_entry *e = (struct ebt_entry *)chain->data;
- struct ebt_entry_target *t;
+ const struct ebt_entry *e = (struct ebt_entry *)chain->data;
+ const struct ebt_entry_target *t;
while (pos < nentries || chain_nr != -1) {
/* end of udc, go back one 'recursion' step */
}
/* do the parsing of the table/chains/entries/matches/watchers/targets, heh */
-static int translate_table(char *name, struct ebt_table_info *newinfo)
+static int translate_table(struct net *net, const char *name,
+ struct ebt_table_info *newinfo)
{
unsigned int i, j, k, udc_cnt;
int ret;
/* used to know what we need to clean up if something goes wrong */
i = 0;
ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
- ebt_check_entry, newinfo, name, &i, cl_s, udc_cnt);
+ ebt_check_entry, net, newinfo, name, &i, cl_s, udc_cnt);
if (ret != 0) {
EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
- ebt_cleanup_entry, &i);
+ ebt_cleanup_entry, net, &i);
}
vfree(cl_s);
return ret;
}
/* called under write_lock */
-static void get_counters(struct ebt_counter *oldcounters,
+static void get_counters(const struct ebt_counter *oldcounters,
struct ebt_counter *counters, unsigned int nentries)
{
int i, cpu;
}
}
-/* replace the table */
-static int do_replace(struct net *net, void __user *user, unsigned int len)
+static int do_replace_finish(struct net *net, struct ebt_replace *repl,
+ struct ebt_table_info *newinfo)
{
- int ret, i, countersize;
- struct ebt_table_info *newinfo;
- struct ebt_replace tmp;
- struct ebt_table *t;
+ int ret, i;
struct ebt_counter *counterstmp = NULL;
/* used to be able to unlock earlier */
struct ebt_table_info *table;
-
- if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
- return -EFAULT;
-
- if (len != sizeof(tmp) + tmp.entries_size) {
- BUGPRINT("Wrong len argument\n");
- return -EINVAL;
- }
-
- if (tmp.entries_size == 0) {
- BUGPRINT("Entries_size never zero\n");
- return -EINVAL;
- }
- /* overflow check */
- if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) / NR_CPUS -
- SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
- return -ENOMEM;
- if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
- return -ENOMEM;
-
- countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;
- newinfo = vmalloc(sizeof(*newinfo) + countersize);
- if (!newinfo)
- return -ENOMEM;
-
- if (countersize)
- memset(newinfo->counters, 0, countersize);
-
- newinfo->entries = vmalloc(tmp.entries_size);
- if (!newinfo->entries) {
- ret = -ENOMEM;
- goto free_newinfo;
- }
- if (copy_from_user(
- newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
- BUGPRINT("Couldn't copy entries from userspace\n");
- ret = -EFAULT;
- goto free_entries;
- }
+ struct ebt_table *t;
/* the user wants counters back
the check on the size is done later, when we have the lock */
- if (tmp.num_counters) {
- counterstmp = vmalloc(tmp.num_counters * sizeof(*counterstmp));
- if (!counterstmp) {
- ret = -ENOMEM;
- goto free_entries;
- }
+ if (repl->num_counters) {
+ unsigned long size = repl->num_counters * sizeof(*counterstmp);
+ counterstmp = vmalloc(size);
+ if (!counterstmp)
+ return -ENOMEM;
}
- else
- counterstmp = NULL;
- /* this can get initialized by translate_table() */
newinfo->chainstack = NULL;
- ret = ebt_verify_pointers(&tmp, newinfo);
+ ret = ebt_verify_pointers(repl, newinfo);
if (ret != 0)
goto free_counterstmp;
- ret = translate_table(tmp.name, newinfo);
+ ret = translate_table(net, repl->name, newinfo);
if (ret != 0)
goto free_counterstmp;
- t = find_table_lock(net, tmp.name, &ret, &ebt_mutex);
+ t = find_table_lock(net, repl->name, &ret, &ebt_mutex);
if (!t) {
ret = -ENOENT;
goto free_iterate;
}
/* the table doesn't like it */
- if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
+ if (t->check && (ret = t->check(newinfo, repl->valid_hooks)))
goto free_unlock;
- if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
+ if (repl->num_counters && repl->num_counters != t->private->nentries) {
BUGPRINT("Wrong nr. of counters requested\n");
ret = -EINVAL;
goto free_unlock;
module_put(t->me);
/* we need an atomic snapshot of the counters */
write_lock_bh(&t->lock);
- if (tmp.num_counters)
+ if (repl->num_counters)
get_counters(t->private->counters, counterstmp,
t->private->nentries);
allocation. Only reason why this is done is because this way the lock
is held only once, while this doesn't bring the kernel into a
dangerous state. */
- if (tmp.num_counters &&
- copy_to_user(tmp.counters, counterstmp,
- tmp.num_counters * sizeof(struct ebt_counter))) {
- BUGPRINT("Couldn't copy counters to userspace\n");
+ if (repl->num_counters &&
+ copy_to_user(repl->counters, counterstmp,
+ repl->num_counters * sizeof(struct ebt_counter))) {
ret = -EFAULT;
}
else
/* decrease module count and free resources */
EBT_ENTRY_ITERATE(table->entries, table->entries_size,
- ebt_cleanup_entry, NULL);
+ ebt_cleanup_entry, net, NULL);
vfree(table->entries);
if (table->chainstack) {
mutex_unlock(&ebt_mutex);
free_iterate:
EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
- ebt_cleanup_entry, NULL);
+ ebt_cleanup_entry, net, NULL);
free_counterstmp:
vfree(counterstmp);
/* can be initialized in translate_table() */
vfree(newinfo->chainstack[i]);
vfree(newinfo->chainstack);
}
+ return ret;
+}
+
+/* replace the table */
+static int do_replace(struct net *net, const void __user *user,
+ unsigned int len)
+{
+ int ret, countersize;
+ struct ebt_table_info *newinfo;
+ struct ebt_replace tmp;
+
+ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+ return -EFAULT;
+
+ if (len != sizeof(tmp) + tmp.entries_size) {
+ BUGPRINT("Wrong len argument\n");
+ return -EINVAL;
+ }
+
+ if (tmp.entries_size == 0) {
+ BUGPRINT("Entries_size never zero\n");
+ return -EINVAL;
+ }
+ /* overflow check */
+ if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) /
+ NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
+ return -ENOMEM;
+ if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
+ return -ENOMEM;
+
+ countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;
+ newinfo = vmalloc(sizeof(*newinfo) + countersize);
+ if (!newinfo)
+ return -ENOMEM;
+
+ if (countersize)
+ memset(newinfo->counters, 0, countersize);
+
+ newinfo->entries = vmalloc(tmp.entries_size);
+ if (!newinfo->entries) {
+ ret = -ENOMEM;
+ goto free_newinfo;
+ }
+ if (copy_from_user(
+ newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
+ BUGPRINT("Couldn't copy entries from userspace\n");
+ ret = -EFAULT;
+ goto free_entries;
+ }
+
+ ret = do_replace_finish(net, &tmp, newinfo);
+ if (ret == 0)
+ return ret;
free_entries:
vfree(newinfo->entries);
free_newinfo:
newinfo->hook_entry[i] = p +
((char *)repl->hook_entry[i] - repl->entries);
}
- ret = translate_table(repl->name, newinfo);
+ ret = translate_table(net, repl->name, newinfo);
if (ret != 0) {
BUGPRINT("Translate_table failed\n");
goto free_chainstack;
return ERR_PTR(ret);
}
-void ebt_unregister_table(struct ebt_table *table)
+void ebt_unregister_table(struct net *net, struct ebt_table *table)
{
int i;
list_del(&table->list);
mutex_unlock(&ebt_mutex);
EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size,
- ebt_cleanup_entry, NULL);
+ ebt_cleanup_entry, net, NULL);
if (table->private->nentries)
module_put(table->me);
vfree(table->private->entries);
}
/* userspace just supplied us with counters */
-static int update_counters(struct net *net, void __user *user, unsigned int len)
+static int update_counters(struct net *net, const void __user *user,
+ unsigned int len)
{
int i, ret;
struct ebt_counter *tmp;
if (hlp.num_counters == 0)
return -EINVAL;
- if (!(tmp = vmalloc(hlp.num_counters * sizeof(*tmp)))) {
- MEMPRINT("Update_counters && nomemory\n");
+ if (!(tmp = vmalloc(hlp.num_counters * sizeof(*tmp))))
return -ENOMEM;
- }
t = find_table_lock(net, hlp.name, &ret, &ebt_mutex);
if (!t)
return ret;
}
-static inline int ebt_make_matchname(struct ebt_entry_match *m,
- char *base, char __user *ubase)
+static inline int ebt_make_matchname(const struct ebt_entry_match *m,
+ const char *base, char __user *ubase)
{
char __user *hlp = ubase + ((char *)m - base);
if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
return 0;
}
-static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
- char *base, char __user *ubase)
+static inline int ebt_make_watchername(const struct ebt_entry_watcher *w,
+ const char *base, char __user *ubase)
{
char __user *hlp = ubase + ((char *)w - base);
if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
return 0;
}
-static inline int ebt_make_names(struct ebt_entry *e, char *base, char __user *ubase)
+static inline int
+ebt_make_names(struct ebt_entry *e, const char *base, char __user *ubase)
{
int ret;
char __user *hlp;
- struct ebt_entry_target *t;
+ const struct ebt_entry_target *t;
if (e->bitmask == 0)
return 0;
return 0;
}
+static int copy_counters_to_user(struct ebt_table *t,
+ const struct ebt_counter *oldcounters,
+ void __user *user, unsigned int num_counters,
+ unsigned int nentries)
+{
+ struct ebt_counter *counterstmp;
+ int ret = 0;
+
+ /* userspace might not need the counters */
+ if (num_counters == 0)
+ return 0;
+
+ if (num_counters != nentries) {
+ BUGPRINT("Num_counters wrong\n");
+ return -EINVAL;
+ }
+
+ counterstmp = vmalloc(nentries * sizeof(*counterstmp));
+ if (!counterstmp)
+ return -ENOMEM;
+
+ write_lock_bh(&t->lock);
+ get_counters(oldcounters, counterstmp, nentries);
+ write_unlock_bh(&t->lock);
+
+ if (copy_to_user(user, counterstmp,
+ nentries * sizeof(struct ebt_counter)))
+ ret = -EFAULT;
+ vfree(counterstmp);
+ return ret;
+}
+
/* called with ebt_mutex locked */
static int copy_everything_to_user(struct ebt_table *t, void __user *user,
- int *len, int cmd)
+ const int *len, int cmd)
{
struct ebt_replace tmp;
- struct ebt_counter *counterstmp, *oldcounters;
+ const struct ebt_counter *oldcounters;
unsigned int entries_size, nentries;
+ int ret;
char *entries;
if (cmd == EBT_SO_GET_ENTRIES) {
return -EINVAL;
}
- /* userspace might not need the counters */
- if (tmp.num_counters) {
- if (tmp.num_counters != nentries) {
- BUGPRINT("Num_counters wrong\n");
- return -EINVAL;
- }
- counterstmp = vmalloc(nentries * sizeof(*counterstmp));
- if (!counterstmp) {
- MEMPRINT("Couldn't copy counters, out of memory\n");
- return -ENOMEM;
- }
- write_lock_bh(&t->lock);
- get_counters(oldcounters, counterstmp, nentries);
- write_unlock_bh(&t->lock);
-
- if (copy_to_user(tmp.counters, counterstmp,
- nentries * sizeof(struct ebt_counter))) {
- BUGPRINT("Couldn't copy counters to userspace\n");
- vfree(counterstmp);
- return -EFAULT;
- }
- vfree(counterstmp);
- }
+ ret = copy_counters_to_user(t, oldcounters, tmp.counters,
+ tmp.num_counters, nentries);
+ if (ret)
+ return ret;
if (copy_to_user(tmp.entries, entries, entries_size)) {
BUGPRINT("Couldn't copy entries to userspace\n");