X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fnetfilter%2Fx_tables.c;h=8996584b84998708ccc0eabf94b4faa19b96ad11;hb=refs%2Fheads%2Fvserver;hp=d7817afc6b9654cd12499135d5e1b9c2617b5440;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index d7817afc6..8996584b8 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -13,7 +13,6 @@ * */ -#include #include #include #include @@ -21,10 +20,13 @@ #include #include #include +#include +#include #include #include + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte "); MODULE_DESCRIPTION("[ip,ip6,arp]_tables backend module"); @@ -32,10 +34,11 @@ MODULE_DESCRIPTION("[ip,ip6,arp]_tables backend module"); #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1)) struct xt_af { - struct semaphore mutex; + struct mutex mutex; struct list_head match; struct list_head target; struct list_head tables; + struct mutex compat_mutex; }; static struct xt_af *xt; @@ -52,55 +55,125 @@ enum { MATCH, }; +static const char *xt_prefix[NPROTO] = { + [AF_INET] = "ip", + [AF_INET6] = "ip6", + [NF_ARP] = "arp", +}; + /* Registration hooks for targets. */ int -xt_register_target(int af, struct xt_target *target) +xt_register_target(struct xt_target *target) { - int ret; + int ret, af = target->family; - ret = down_interruptible(&xt[af].mutex); + ret = mutex_lock_interruptible(&xt[af].mutex); if (ret != 0) return ret; list_add(&target->list, &xt[af].target); - up(&xt[af].mutex); + mutex_unlock(&xt[af].mutex); return ret; } EXPORT_SYMBOL(xt_register_target); void -xt_unregister_target(int af, struct xt_target *target) +xt_unregister_target(struct xt_target *target) { - down(&xt[af].mutex); - LIST_DELETE(&xt[af].target, target); - up(&xt[af].mutex); + int af = target->family; + + mutex_lock(&xt[af].mutex); + list_del(&target->list); + mutex_unlock(&xt[af].mutex); } EXPORT_SYMBOL(xt_unregister_target); int -xt_register_match(int af, struct xt_match *match) +xt_register_targets(struct xt_target *target, unsigned int n) { - int ret; + unsigned int i; + int err = 0; + + for (i = 0; i < n; i++) { + err = xt_register_target(&target[i]); + if (err) + goto err; + } + return err; - ret = down_interruptible(&xt[af].mutex); +err: + if (i > 0) + xt_unregister_targets(target, i); + return err; +} +EXPORT_SYMBOL(xt_register_targets); + +void +xt_unregister_targets(struct xt_target *target, unsigned int n) +{ + unsigned int i; + + for (i = 0; i < n; i++) + xt_unregister_target(&target[i]); +} +EXPORT_SYMBOL(xt_unregister_targets); + +int +xt_register_match(struct xt_match *match) +{ + int ret, af = match->family; + + ret = mutex_lock_interruptible(&xt[af].mutex); if (ret != 0) return ret; list_add(&match->list, &xt[af].match); - up(&xt[af].mutex); + mutex_unlock(&xt[af].mutex); return ret; } EXPORT_SYMBOL(xt_register_match); void -xt_unregister_match(int af, struct xt_match *match) +xt_unregister_match(struct xt_match *match) { - down(&xt[af].mutex); - LIST_DELETE(&xt[af].match, match); - up(&xt[af].mutex); + int af = match->family; + + mutex_lock(&xt[af].mutex); + list_del(&match->list); + mutex_unlock(&xt[af].mutex); } EXPORT_SYMBOL(xt_unregister_match); +int +xt_register_matches(struct xt_match *match, unsigned int n) +{ + unsigned int i; + int err = 0; + + for (i = 0; i < n; i++) { + err = xt_register_match(&match[i]); + if (err) + goto err; + } + return err; + +err: + if (i > 0) + xt_unregister_matches(match, i); + return err; +} +EXPORT_SYMBOL(xt_register_matches); + +void +xt_unregister_matches(struct xt_match *match, unsigned int n) +{ + unsigned int i; + + for (i = 0; i < n; i++) + xt_unregister_match(&match[i]); +} +EXPORT_SYMBOL(xt_unregister_matches); + /* * These are weird, but module loading must not be done with mutex @@ -114,21 +187,21 @@ struct xt_match *xt_find_match(int af, const char *name, u8 revision) struct xt_match *m; int err = 0; - if (down_interruptible(&xt[af].mutex) != 0) + if (mutex_lock_interruptible(&xt[af].mutex) != 0) return ERR_PTR(-EINTR); list_for_each_entry(m, &xt[af].match, list) { if (strcmp(m->name, name) == 0) { if (m->revision == revision) { if (try_module_get(m->me)) { - up(&xt[af].mutex); + mutex_unlock(&xt[af].mutex); return m; } } else err = -EPROTOTYPE; /* Found something. */ } } - up(&xt[af].mutex); + mutex_unlock(&xt[af].mutex); return ERR_PTR(err); } EXPORT_SYMBOL(xt_find_match); @@ -139,37 +212,31 @@ struct xt_target *xt_find_target(int af, const char *name, u8 revision) struct xt_target *t; int err = 0; - if (down_interruptible(&xt[af].mutex) != 0) + if (mutex_lock_interruptible(&xt[af].mutex) != 0) return ERR_PTR(-EINTR); list_for_each_entry(t, &xt[af].target, list) { if (strcmp(t->name, name) == 0) { if (t->revision == revision) { if (try_module_get(t->me)) { - up(&xt[af].mutex); + mutex_unlock(&xt[af].mutex); return t; } } else err = -EPROTOTYPE; /* Found something. */ } } - up(&xt[af].mutex); + mutex_unlock(&xt[af].mutex); return ERR_PTR(err); } EXPORT_SYMBOL(xt_find_target); -static const char *xt_prefix[NPROTO] = { - [AF_INET] = "ipt_%s", - [AF_INET6] = "ip6t_%s", - [NF_ARP] = "arpt_%s", -}; - struct xt_target *xt_request_find_target(int af, const char *name, u8 revision) { struct xt_target *target; target = try_then_request_module(xt_find_target(af, name, revision), - xt_prefix[af], name); + "%st_%s", xt_prefix[af], name); if (IS_ERR(target) || !target) return NULL; return target; @@ -214,7 +281,7 @@ int xt_find_revision(int af, const char *name, u8 revision, int target, { int have_rev, best = -1; - if (down_interruptible(&xt[af].mutex) != 0) { + if (mutex_lock_interruptible(&xt[af].mutex) != 0) { *err = -EINTR; return 1; } @@ -222,7 +289,7 @@ int xt_find_revision(int af, const char *name, u8 revision, int target, have_rev = target_revfn(af, name, revision, &best); else have_rev = match_revfn(af, name, revision, &best); - up(&xt[af].mutex); + mutex_unlock(&xt[af].mutex); /* Nothing at all? Return 0 to try loading module. */ if (best == -1) { @@ -237,6 +304,186 @@ int xt_find_revision(int af, const char *name, u8 revision, int target, } EXPORT_SYMBOL_GPL(xt_find_revision); +int xt_check_match(const struct xt_match *match, unsigned short family, + unsigned int size, const char *table, unsigned int hook_mask, + unsigned short proto, int inv_proto) +{ + if (XT_ALIGN(match->matchsize) != size) { + printk("%s_tables: %s match: invalid size %Zu != %u\n", + xt_prefix[family], match->name, + XT_ALIGN(match->matchsize), size); + return -EINVAL; + } + if (match->table && strcmp(match->table, table)) { + printk("%s_tables: %s match: only valid in %s table, not %s\n", + xt_prefix[family], match->name, match->table, table); + return -EINVAL; + } + if (match->hooks && (hook_mask & ~match->hooks) != 0) { + printk("%s_tables: %s match: bad hook_mask %u\n", + xt_prefix[family], match->name, hook_mask); + return -EINVAL; + } + if (match->proto && (match->proto != proto || inv_proto)) { + printk("%s_tables: %s match: only valid for protocol %u\n", + xt_prefix[family], match->name, match->proto); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(xt_check_match); + +#ifdef CONFIG_COMPAT +int xt_compat_match_offset(struct xt_match *match) +{ + u_int16_t csize = match->compatsize ? : match->matchsize; + return XT_ALIGN(match->matchsize) - COMPAT_XT_ALIGN(csize); +} +EXPORT_SYMBOL_GPL(xt_compat_match_offset); + +void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr, + int *size) +{ + struct xt_match *match = m->u.kernel.match; + struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m; + int pad, off = xt_compat_match_offset(match); + u_int16_t msize = cm->u.user.match_size; + + m = *dstptr; + memcpy(m, cm, sizeof(*cm)); + if (match->compat_from_user) + match->compat_from_user(m->data, cm->data); + else + memcpy(m->data, cm->data, msize - sizeof(*cm)); + pad = XT_ALIGN(match->matchsize) - match->matchsize; + if (pad > 0) + memset(m->data + match->matchsize, 0, pad); + + msize += off; + m->u.user.match_size = msize; + + *size += off; + *dstptr += msize; +} +EXPORT_SYMBOL_GPL(xt_compat_match_from_user); + +int xt_compat_match_to_user(struct xt_entry_match *m, void __user **dstptr, + int *size) +{ + struct xt_match *match = m->u.kernel.match; + struct compat_xt_entry_match __user *cm = *dstptr; + int off = xt_compat_match_offset(match); + u_int16_t msize = m->u.user.match_size - off; + + if (copy_to_user(cm, m, sizeof(*cm)) || + put_user(msize, &cm->u.user.match_size)) + return -EFAULT; + + if (match->compat_to_user) { + if (match->compat_to_user((void __user *)cm->data, m->data)) + return -EFAULT; + } else { + if (copy_to_user(cm->data, m->data, msize - sizeof(*cm))) + return -EFAULT; + } + + *size -= off; + *dstptr += msize; + return 0; +} +EXPORT_SYMBOL_GPL(xt_compat_match_to_user); +#endif /* CONFIG_COMPAT */ + +int xt_check_target(const struct xt_target *target, unsigned short family, + unsigned int size, const char *table, unsigned int hook_mask, + unsigned short proto, int inv_proto) +{ + if (XT_ALIGN(target->targetsize) != size) { + printk("%s_tables: %s target: invalid size %Zu != %u\n", + xt_prefix[family], target->name, + XT_ALIGN(target->targetsize), size); + return -EINVAL; + } + if (target->table && strcmp(target->table, table)) { + printk("%s_tables: %s target: only valid in %s table, not %s\n", + xt_prefix[family], target->name, target->table, table); + return -EINVAL; + } + if (target->hooks && (hook_mask & ~target->hooks) != 0) { + printk("%s_tables: %s target: bad hook_mask %u\n", + xt_prefix[family], target->name, hook_mask); + return -EINVAL; + } + if (target->proto && (target->proto != proto || inv_proto)) { + printk("%s_tables: %s target: only valid for protocol %u\n", + xt_prefix[family], target->name, target->proto); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(xt_check_target); + +#ifdef CONFIG_COMPAT +int xt_compat_target_offset(struct xt_target *target) +{ + u_int16_t csize = target->compatsize ? : target->targetsize; + return XT_ALIGN(target->targetsize) - COMPAT_XT_ALIGN(csize); +} +EXPORT_SYMBOL_GPL(xt_compat_target_offset); + +void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr, + int *size) +{ + struct xt_target *target = t->u.kernel.target; + struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t; + int pad, off = xt_compat_target_offset(target); + u_int16_t tsize = ct->u.user.target_size; + + t = *dstptr; + memcpy(t, ct, sizeof(*ct)); + if (target->compat_from_user) + target->compat_from_user(t->data, ct->data); + else + memcpy(t->data, ct->data, tsize - sizeof(*ct)); + pad = XT_ALIGN(target->targetsize) - target->targetsize; + if (pad > 0) + memset(t->data + target->targetsize, 0, pad); + + tsize += off; + t->u.user.target_size = tsize; + + *size += off; + *dstptr += tsize; +} +EXPORT_SYMBOL_GPL(xt_compat_target_from_user); + +int xt_compat_target_to_user(struct xt_entry_target *t, void __user **dstptr, + int *size) +{ + struct xt_target *target = t->u.kernel.target; + struct compat_xt_entry_target __user *ct = *dstptr; + int off = xt_compat_target_offset(target); + u_int16_t tsize = t->u.user.target_size - off; + + if (copy_to_user(ct, t, sizeof(*ct)) || + put_user(tsize, &ct->u.user.target_size)) + return -EFAULT; + + if (target->compat_to_user) { + if (target->compat_to_user((void __user *)ct->data, t->data)) + return -EFAULT; + } else { + if (copy_to_user(ct->data, t->data, tsize - sizeof(*ct))) + return -EFAULT; + } + + *size -= off; + *dstptr += tsize; + return 0; +} +EXPORT_SYMBOL_GPL(xt_compat_target_to_user); +#endif + struct xt_table_info *xt_alloc_table_info(unsigned int size) { struct xt_table_info *newinfo; @@ -252,7 +499,7 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size) newinfo->size = size; - for_each_cpu(cpu) { + for_each_possible_cpu(cpu) { if (size <= PAGE_SIZE) newinfo->entries[cpu] = kmalloc_node(size, GFP_KERNEL, @@ -275,7 +522,7 @@ void xt_free_table_info(struct xt_table_info *info) { int cpu; - for_each_cpu(cpu) { + for_each_possible_cpu(cpu) { if (info->size <= PAGE_SIZE) kfree(info->entries[cpu]); else @@ -290,23 +537,36 @@ struct xt_table *xt_find_table_lock(int af, const char *name) { struct xt_table *t; - if (down_interruptible(&xt[af].mutex) != 0) + if (mutex_lock_interruptible(&xt[af].mutex) != 0) return ERR_PTR(-EINTR); list_for_each_entry(t, &xt[af].tables, list) if (strcmp(t->name, name) == 0 && try_module_get(t->me)) return t; - up(&xt[af].mutex); + mutex_unlock(&xt[af].mutex); return NULL; } EXPORT_SYMBOL_GPL(xt_find_table_lock); void xt_table_unlock(struct xt_table *table) { - up(&xt[table->af].mutex); + mutex_unlock(&xt[table->af].mutex); } EXPORT_SYMBOL_GPL(xt_table_unlock); +#ifdef CONFIG_COMPAT +void xt_compat_lock(int af) +{ + mutex_lock(&xt[af].compat_mutex); +} +EXPORT_SYMBOL_GPL(xt_compat_lock); + +void xt_compat_unlock(int af) +{ + mutex_unlock(&xt[af].compat_mutex); +} +EXPORT_SYMBOL_GPL(xt_compat_unlock); +#endif struct xt_table_info * xt_replace_table(struct xt_table *table, @@ -342,19 +602,23 @@ int xt_register_table(struct xt_table *table, { int ret; struct xt_table_info *private; + struct xt_table *t; - ret = down_interruptible(&xt[table->af].mutex); + ret = mutex_lock_interruptible(&xt[table->af].mutex); if (ret != 0) return ret; /* Don't autoload: we'd eat our tail... */ - if (list_named_find(&xt[table->af].tables, table->name)) { - ret = -EEXIST; - goto unlock; + list_for_each_entry(t, &xt[table->af].tables, list) { + if (strcmp(t->name, table->name) == 0) { + ret = -EEXIST; + goto unlock; + } } /* Simplifies replace_table code. */ table->private = bootstrap; + rwlock_init(&table->lock); if (!xt_replace_table(table, 0, newinfo, &ret)) goto unlock; @@ -364,12 +628,11 @@ int xt_register_table(struct xt_table *table, /* save number of initial entries */ private->initial_entries = private->number; - rwlock_init(&table->lock); - list_prepend(&xt[table->af].tables, table); + list_add(&table->list, &xt[table->af].tables); ret = 0; unlock: - up(&xt[table->af].mutex); + mutex_unlock(&xt[table->af].mutex); return ret; } EXPORT_SYMBOL_GPL(xt_register_table); @@ -378,10 +641,10 @@ void *xt_unregister_table(struct xt_table *table) { struct xt_table_info *private; - down(&xt[table->af].mutex); + mutex_lock(&xt[table->af].mutex); private = table->private; - LIST_DELETE(&xt[table->af].tables, table); - up(&xt[table->af].mutex); + list_del(&table->list); + mutex_unlock(&xt[table->af].mutex); return private; } @@ -445,7 +708,7 @@ static void *xt_tgt_seq_start(struct seq_file *seq, loff_t *pos) if (!list) return NULL; - if (down_interruptible(&xt[af].mutex) != 0) + if (mutex_lock_interruptible(&xt[af].mutex) != 0) return NULL; return xt_get_idx(list, seq, *pos); @@ -474,7 +737,7 @@ static void xt_tgt_seq_stop(struct seq_file *seq, void *v) struct proc_dir_entry *pde = seq->private; u_int16_t af = (unsigned long)pde->data & 0xffff; - up(&xt[af].mutex); + mutex_unlock(&xt[af].mutex); } static int xt_name_seq_show(struct seq_file *seq, void *v) @@ -606,7 +869,10 @@ static int __init xt_init(void) return -ENOMEM; for (i = 0; i < NPROTO; i++) { - init_MUTEX(&xt[i].mutex); + mutex_init(&xt[i].mutex); +#ifdef CONFIG_COMPAT + mutex_init(&xt[i].compat_mutex); +#endif INIT_LIST_HEAD(&xt[i].target); INIT_LIST_HEAD(&xt[i].match); INIT_LIST_HEAD(&xt[i].tables);