Merge branch 'nfs-for-2.6.35' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
[safe/jmp/linux-2.6] / net / netfilter / x_tables.c
index 150e5cf..665f5be 100644 (file)
 #include <linux/vmalloc.h>
 #include <linux/mutex.h>
 #include <linux/mm.h>
+#include <linux/slab.h>
 #include <net/net_namespace.h>
 
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter_arp.h>
-
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_arp/arp_tables.h>
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
@@ -37,7 +40,7 @@ MODULE_DESCRIPTION("{ip,ip6,arp,eb}_tables backend module");
 struct compat_delta {
        struct compat_delta *next;
        unsigned int offset;
-       short delta;
+       int delta;
 };
 
 struct xt_af {
@@ -329,6 +332,32 @@ int xt_find_revision(u8 af, const char *name, u8 revision, int target,
 }
 EXPORT_SYMBOL_GPL(xt_find_revision);
 
+static char *textify_hooks(char *buf, size_t size, unsigned int mask)
+{
+       static const char *const names[] = {
+               "PREROUTING", "INPUT", "FORWARD",
+               "OUTPUT", "POSTROUTING", "BROUTING",
+       };
+       unsigned int i;
+       char *p = buf;
+       bool np = false;
+       int res;
+
+       *p = '\0';
+       for (i = 0; i < ARRAY_SIZE(names); ++i) {
+               if (!(mask & (1 << i)))
+                       continue;
+               res = snprintf(p, size, "%s%s", np ? "/" : "", names[i]);
+               if (res > 0) {
+                       size -= res;
+                       p += res;
+               }
+               np = true;
+       }
+
+       return buf;
+}
+
 int xt_check_match(struct xt_mtchk_param *par,
                   unsigned int size, u_int8_t proto, bool inv_proto)
 {
@@ -338,26 +367,32 @@ int xt_check_match(struct xt_mtchk_param *par,
                 * ebt_among is exempt from centralized matchsize checking
                 * because it uses a dynamic-size data set.
                 */
-               printk("%s_tables: %s match: invalid size %Zu != %u\n",
+               pr_err("%s_tables: %s.%u match: invalid size "
+                      "%u (kernel) != (user) %u\n",
                       xt_prefix[par->family], par->match->name,
+                      par->match->revision,
                       XT_ALIGN(par->match->matchsize), size);
                return -EINVAL;
        }
        if (par->match->table != NULL &&
            strcmp(par->match->table, par->table) != 0) {
-               printk("%s_tables: %s match: only valid in %s table, not %s\n",
+               pr_err("%s_tables: %s match: only valid in %s table, not %s\n",
                       xt_prefix[par->family], par->match->name,
                       par->match->table, par->table);
                return -EINVAL;
        }
        if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) {
-               printk("%s_tables: %s match: bad hook_mask %#x/%#x\n",
+               char used[64], allow[64];
+
+               pr_err("%s_tables: %s match: used from hooks %s, but only "
+                      "valid from %s\n",
                       xt_prefix[par->family], par->match->name,
-                      par->hook_mask, par->match->hooks);
+                      textify_hooks(used, sizeof(used), par->hook_mask),
+                      textify_hooks(allow, sizeof(allow), par->match->hooks));
                return -EINVAL;
        }
        if (par->match->proto && (par->match->proto != proto || inv_proto)) {
-               printk("%s_tables: %s match: only valid for protocol %u\n",
+               pr_err("%s_tables: %s match: only valid for protocol %u\n",
                       xt_prefix[par->family], par->match->name,
                       par->match->proto);
                return -EINVAL;
@@ -405,10 +440,10 @@ void xt_compat_flush_offsets(u_int8_t af)
 }
 EXPORT_SYMBOL_GPL(xt_compat_flush_offsets);
 
-short xt_compat_calc_jump(u_int8_t af, unsigned int offset)
+int xt_compat_calc_jump(u_int8_t af, unsigned int offset)
 {
        struct compat_delta *tmp;
-       short delta;
+       int delta;
 
        for (tmp = xt[af].compat_offsets, delta = 0; tmp; tmp = tmp->next)
                if (tmp->offset < offset)
@@ -451,8 +486,8 @@ int xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
 }
 EXPORT_SYMBOL_GPL(xt_compat_match_from_user);
 
-int xt_compat_match_to_user(struct xt_entry_match *m, void __user **dstptr,
-                           unsigned int *size)
+int xt_compat_match_to_user(const struct xt_entry_match *m,
+                           void __user **dstptr, unsigned int *size)
 {
        const struct xt_match *match = m->u.kernel.match;
        struct compat_xt_entry_match __user *cm = *dstptr;
@@ -484,26 +519,32 @@ int xt_check_target(struct xt_tgchk_param *par,
                    unsigned int size, u_int8_t proto, bool inv_proto)
 {
        if (XT_ALIGN(par->target->targetsize) != size) {
-               printk("%s_tables: %s target: invalid size %Zu != %u\n",
+               pr_err("%s_tables: %s.%u target: invalid size "
+                      "%u (kernel) != (user) %u\n",
                       xt_prefix[par->family], par->target->name,
+                      par->target->revision,
                       XT_ALIGN(par->target->targetsize), size);
                return -EINVAL;
        }
        if (par->target->table != NULL &&
            strcmp(par->target->table, par->table) != 0) {
-               printk("%s_tables: %s target: only valid in %s table, not %s\n",
+               pr_err("%s_tables: %s target: only valid in %s table, not %s\n",
                       xt_prefix[par->family], par->target->name,
                       par->target->table, par->table);
                return -EINVAL;
        }
        if (par->target->hooks && (par->hook_mask & ~par->target->hooks) != 0) {
-               printk("%s_tables: %s target: bad hook_mask %#x/%#x\n",
+               char used[64], allow[64];
+
+               pr_err("%s_tables: %s target: used from hooks %s, but only "
+                      "usable from %s\n",
                       xt_prefix[par->family], par->target->name,
-                      par->hook_mask, par->target->hooks);
+                      textify_hooks(used, sizeof(used), par->hook_mask),
+                      textify_hooks(allow, sizeof(allow), par->target->hooks));
                return -EINVAL;
        }
        if (par->target->proto && (par->target->proto != proto || inv_proto)) {
-               printk("%s_tables: %s target: only valid for protocol %u\n",
+               pr_err("%s_tables: %s target: only valid for protocol %u\n",
                       xt_prefix[par->family], par->target->name,
                       par->target->proto);
                return -EINVAL;
@@ -548,8 +589,8 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
 }
 EXPORT_SYMBOL_GPL(xt_compat_target_from_user);
 
-int xt_compat_target_to_user(struct xt_entry_target *t, void __user **dstptr,
-                            unsigned int *size)
+int xt_compat_target_to_user(const struct xt_entry_target *t,
+                            void __user **dstptr, unsigned int *size)
 {
        const struct xt_target *target = t->u.kernel.target;
        struct compat_xt_entry_target __user *ct = *dstptr;
@@ -583,7 +624,7 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size)
        int cpu;
 
        /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
-       if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > num_physpages)
+       if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > totalram_pages)
                return NULL;
 
        newinfo = kzalloc(XT_TABLE_INFO_SZ, GFP_KERNEL);
@@ -702,16 +743,17 @@ xt_replace_table(struct xt_table *table,
 }
 EXPORT_SYMBOL_GPL(xt_replace_table);
 
-struct xt_table *xt_register_table(struct net *net, struct xt_table *table,
+struct xt_table *xt_register_table(struct net *net,
+                                  const struct xt_table *input_table,
                                   struct xt_table_info *bootstrap,
                                   struct xt_table_info *newinfo)
 {
        int ret;
        struct xt_table_info *private;
-       struct xt_table *t;
+       struct xt_table *t, *table;
 
        /* Don't add one object to multiple lists. */
-       table = kmemdup(table, sizeof(struct xt_table), GFP_KERNEL);
+       table = kmemdup(input_table, sizeof(struct xt_table), GFP_KERNEL);
        if (!table) {
                ret = -ENOMEM;
                goto out;
@@ -1056,6 +1098,60 @@ static const struct file_operations xt_target_ops = {
 
 #endif /* CONFIG_PROC_FS */
 
+/**
+ * xt_hook_link - set up hooks for a new table
+ * @table:     table with metadata needed to set up hooks
+ * @fn:                Hook function
+ *
+ * This function will take care of creating and registering the necessary
+ * Netfilter hooks for XT tables.
+ */
+struct nf_hook_ops *xt_hook_link(const struct xt_table *table, nf_hookfn *fn)
+{
+       unsigned int hook_mask = table->valid_hooks;
+       uint8_t i, num_hooks = hweight32(hook_mask);
+       uint8_t hooknum;
+       struct nf_hook_ops *ops;
+       int ret;
+
+       ops = kmalloc(sizeof(*ops) * num_hooks, GFP_KERNEL);
+       if (ops == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       for (i = 0, hooknum = 0; i < num_hooks && hook_mask != 0;
+            hook_mask >>= 1, ++hooknum) {
+               if (!(hook_mask & 1))
+                       continue;
+               ops[i].hook     = fn;
+               ops[i].owner    = table->me;
+               ops[i].pf       = table->af;
+               ops[i].hooknum  = hooknum;
+               ops[i].priority = table->priority;
+               ++i;
+       }
+
+       ret = nf_register_hooks(ops, num_hooks);
+       if (ret < 0) {
+               kfree(ops);
+               return ERR_PTR(ret);
+       }
+
+       return ops;
+}
+EXPORT_SYMBOL_GPL(xt_hook_link);
+
+/**
+ * xt_hook_unlink - remove hooks for a table
+ * @ops:       nf_hook_ops array as returned by nf_hook_link
+ * @hook_mask: the very same mask that was passed to nf_hook_link
+ */
+void xt_hook_unlink(const struct xt_table *table, struct nf_hook_ops *ops)
+{
+       nf_unregister_hooks(ops, hweight32(table->valid_hooks));
+       kfree(ops);
+}
+EXPORT_SYMBOL_GPL(xt_hook_unlink);
+
 int xt_proto_init(struct net *net, u_int8_t af)
 {
 #ifdef CONFIG_PROC_FS