diff options
author | Harald Welte <laforge@netfilter.org> | 2006-01-12 13:30:04 -0800 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-01-12 14:06:43 -0800 |
commit | 2e4e6a17af35be359cc8f1c924f8f198fbd478cc (patch) | |
tree | cb4b5438dcf9ff9d57518a26124308bcbfffd214 /net/netfilter | |
parent | 880b005f294454d989783d0984dc554dfe3c8214 (diff) |
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netfilter')
27 files changed, 4316 insertions, 5 deletions
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 7d55f9cbd85..99c0a0fa4a9 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -103,3 +103,261 @@ config NF_CT_NETLINK This option enables support for a netlink-based userspace interface endmenu + +config NETFILTER_XTABLES + tristate "Netfilter Xtables support (required for ip_tables)" + help + This is required if you intend to use any of ip_tables, + ip6_tables or arp_tables. + +# alphabetically ordered list of targets + +config NETFILTER_XT_TARGET_CLASSIFY + tristate '"CLASSIFY" target support' + depends on NETFILTER_XTABLES + help + This option adds a `CLASSIFY' target, which enables the user to set + the priority of a packet. Some qdiscs can use this value for + classification, among these are: + + atm, cbq, dsmark, pfifo_fast, htb, prio + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_TARGET_CONNMARK + tristate '"CONNMARK" target support' + depends on NETFILTER_XTABLES + depends on IP_NF_MANGLE || IP6_NF_MANGLE + depends on (IP_NF_CONNTRACK && IP_NF_CONNTRACK_MARK) || (NF_CONNTRACK_MARK && NF_CONNTRACK_IPV4) + help + This option adds a `CONNMARK' target, which allows one to manipulate + the connection mark value. Similar to the MARK target, but + affects the connection mark value rather than the packet mark value. + + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. The module will be called + ipt_CONNMARK.o. If unsure, say `N'. + +config NETFILTER_XT_TARGET_MARK + tristate '"MARK" target support' + depends on NETFILTER_XTABLES + help + This option adds a `MARK' target, which allows you to create rules + in the `mangle' table which alter the netfilter mark (nfmark) field + associated with the packet prior to routing. This can change + the routing method (see `Use netfilter MARK value as routing + key') and can also be used by other subsystems to change their + behavior. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_TARGET_NFQUEUE + tristate '"NFQUEUE" target Support' + depends on NETFILTER_XTABLES + help + This Target replaced the old obsolete QUEUE target. + + As opposed to QUEUE, it supports 65535 different queues, + not just one. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_TARGET_NOTRACK + tristate '"NOTRACK" target support' + depends on NETFILTER_XTABLES + depends on IP_NF_RAW || IP6_NF_RAW + depends on IP_NF_CONNTRACK || NF_CONNTRACK + help + The NOTRACK target allows a select rule to specify + which packets *not* to enter the conntrack/NAT + subsystem with all the consequences (no ICMP error tracking, + no protocol helpers for the selected packets). + + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. If unsure, say `N'. + +config NETFILTER_XT_MATCH_COMMENT + tristate '"comment" match support' + depends on NETFILTER_XTABLES + help + This option adds a `comment' dummy-match, which allows you to put + comments in your iptables ruleset. + + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. If unsure, say `N'. + +config NETFILTER_XT_MATCH_CONNBYTES + tristate '"connbytes" per-connection counter match support' + depends on NETFILTER_XTABLES + depends on (IP_NF_CONNTRACK && IP_NF_CT_ACCT) || NF_CT_ACCT + help + This option adds a `connbytes' match, which allows you to match the + number of bytes and/or packets for each direction within a connection. + + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. If unsure, say `N'. + +config NETFILTER_XT_MATCH_CONNMARK + tristate '"connmark" connection mark match support' + depends on NETFILTER_XTABLES + depends on (IP_NF_CONNTRACK && IP_NF_CONNTRACK_MARK) || NF_CONNTRACK_MARK + help + This option adds a `connmark' match, which allows you to match the + connection mark value previously set for the session by `CONNMARK'. + + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. The module will be called + ipt_connmark.o. If unsure, say `N'. + +config NETFILTER_XT_MATCH_CONNTRACK + tristate '"conntrack" connection tracking match support' + depends on NETFILTER_XTABLES + depends on IP_NF_CONNTRACK || NF_CONNTRACK + help + This is a general conntrack match module, a superset of the state match. + + It allows matching on additional conntrack information, which is + useful in complex configurations, such as NAT gateways with multiple + internet links or tunnels. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_MATCH_DCCP + tristate '"DCCP" protocol match support' + depends on NETFILTER_XTABLES + help + With this option enabled, you will be able to use the iptables + `dccp' match in order to match on DCCP source/destination ports + and DCCP flags. + + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. If unsure, say `N'. + +config NETFILTER_XT_MATCH_HELPER + tristate '"helper" match support' + depends on NETFILTER_XTABLES + depends on IP_NF_CONNTRACK || NF_CONNTRACK + help + Helper matching allows you to match packets in dynamic connections + tracked by a conntrack-helper, ie. ip_conntrack_ftp + + To compile it as a module, choose M here. If unsure, say Y. + +config NETFILTER_XT_MATCH_LENGTH + tristate '"length" match support' + depends on NETFILTER_XTABLES + help + This option allows you to match the length of a packet against a + specific value or range of values. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_MATCH_LIMIT + tristate '"limit" match support' + depends on NETFILTER_XTABLES + help + limit matching allows you to control the rate at which a rule can be + matched: mainly useful in combination with the LOG target ("LOG + target support", below) and to avoid some Denial of Service attacks. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_MATCH_MAC + tristate '"mac" address match support' + depends on NETFILTER_XTABLES + help + MAC matching allows you to match packets based on the source + Ethernet address of the packet. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_MATCH_MARK + tristate '"mark" match support' + depends on NETFILTER_XTABLES + help + Netfilter mark matching allows you to match packets based on the + `nfmark' value in the packet. This can be set by the MARK target + (see below). + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_MATCH_PHYSDEV + tristate '"physdev" match support' + depends on NETFILTER_XTABLES && BRIDGE_NETFILTER + help + Physdev packet matching matches against the physical bridge ports + the IP packet arrived on or will leave by. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_MATCH_PKTTYPE + tristate '"pkttype" packet type match support' + depends on NETFILTER_XTABLES + help + Packet type matching allows you to match a packet by + its "class", eg. BROADCAST, MULTICAST, ... + + Typical usage: + iptables -A INPUT -m pkttype --pkt-type broadcast -j LOG + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_MATCH_REALM + tristate '"realm" match support' + depends on NETFILTER_XTABLES + select NET_CLS_ROUTE + help + This option adds a `realm' match, which allows you to use the realm + key from the routing subsystem inside iptables. + + This match pretty much resembles the CONFIG_NET_CLS_ROUTE4 option + in tc world. + + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. If unsure, say `N'. + +config NETFILTER_XT_MATCH_SCTP + tristate '"sctp" protocol match support' + depends on NETFILTER_XTABLES + help + With this option enabled, you will be able to use the + `sctp' match in order to match on SCTP source/destination ports + and SCTP chunk types. + + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. If unsure, say `N'. + +config NETFILTER_XT_MATCH_STATE + tristate '"state" match support' + depends on NETFILTER_XTABLES + depends on IP_NF_CONNTRACK || NF_CONNTRACK + help + Connection state matching allows you to match packets based on their + relationship to a tracked connection (ie. previous packets). This + is a powerful tool for packet classification. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_MATCH_STRING + tristate '"string" match support' + depends on NETFILTER_XTABLES + select TEXTSEARCH + select TEXTSEARCH_KMP + select TEXTSEARCH_BM + select TEXTSEARCH_FSM + help + This option adds a `string' match, which allows you to look for + pattern matchings in packets. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_MATCH_TCPMSS + tristate '"tcpmss" match support' + depends on NETFILTER_XTABLES + help + This option adds a `tcpmss' match, which allows you to examine the + MSS value of TCP SYN packets, which control the maximum packet size + for that connection. + + To compile it as a module, choose M here. If unsure, say N. + diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index cb2183145c3..746172ebc91 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -1,4 +1,5 @@ netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o +nf_conntrack-objs := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o obj-$(CONFIG_NETFILTER) = netfilter.o @@ -6,13 +7,43 @@ obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o -nf_conntrack-objs := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o - +# connection tracking obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o -obj-$(CONFIG_NF_CONNTRACK_FTP) += nf_conntrack_ftp.o # SCTP protocol connection tracking obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o # netlink interface for nf_conntrack obj-$(CONFIG_NF_CT_NETLINK) += nf_conntrack_netlink.o + +# connection tracking helpers +obj-$(CONFIG_NF_CONNTRACK_FTP) += nf_conntrack_ftp.o + +# generic X tables +obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o + +# targets +obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIFY) += xt_CLASSIFY.o +obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMARK) += xt_CONNMARK.o +obj-$(CONFIG_NETFILTER_XT_TARGET_MARK) += xt_MARK.o +obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o +obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o + +# matches +obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o +obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o +obj-$(CONFIG_NETFILTER_XT_MATCH_CONNMARK) += xt_connmark.o +obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o +obj-$(CONFIG_NETFILTER_XT_MATCH_DCCP) += xt_dccp.o +obj-$(CONFIG_NETFILTER_XT_MATCH_HELPER) += xt_helper.o +obj-$(CONFIG_NETFILTER_XT_MATCH_LENGTH) += xt_length.o +obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o +obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o +obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o +obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o +obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o +obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o +obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o +obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o +obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o +obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 3531d142f69..617599aeeea 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -821,7 +821,7 @@ module_exit(fini); /* Some modules need us, but don't depend directly on any symbol. They should call this. */ -void need_nf_conntrack(void) +void need_conntrack(void) { } @@ -841,7 +841,7 @@ EXPORT_SYMBOL(nf_conntrack_protocol_unregister); EXPORT_SYMBOL(nf_ct_invert_tuplepr); EXPORT_SYMBOL(nf_conntrack_alter_reply); EXPORT_SYMBOL(nf_conntrack_destroyed); -EXPORT_SYMBOL(need_nf_conntrack); +EXPORT_SYMBOL(need_conntrack); EXPORT_SYMBOL(nf_conntrack_helper_register); EXPORT_SYMBOL(nf_conntrack_helper_unregister); EXPORT_SYMBOL(nf_ct_iterate_cleanup); diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c new file mode 100644 index 00000000000..d7817afc6b9 --- /dev/null +++ b/net/netfilter/x_tables.c @@ -0,0 +1,624 @@ +/* + * x_tables core - Backend for {ip,ip6,arp}_tables + * + * Copyright (C) 2006-2006 Harald Welte <laforge@netfilter.org> + * + * Based on existing ip_tables code which is + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/socket.h> +#include <linux/net.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/string.h> +#include <linux/vmalloc.h> + +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter_arp.h> + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); +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 list_head match; + struct list_head target; + struct list_head tables; +}; + +static struct xt_af *xt; + +#ifdef DEBUG_IP_FIREWALL_USER +#define duprintf(format, args...) printk(format , ## args) +#else +#define duprintf(format, args...) +#endif + +enum { + TABLE, + TARGET, + MATCH, +}; + +/* Registration hooks for targets. */ +int +xt_register_target(int af, struct xt_target *target) +{ + int ret; + + ret = down_interruptible(&xt[af].mutex); + if (ret != 0) + return ret; + list_add(&target->list, &xt[af].target); + up(&xt[af].mutex); + return ret; +} +EXPORT_SYMBOL(xt_register_target); + +void +xt_unregister_target(int af, struct xt_target *target) +{ + down(&xt[af].mutex); + LIST_DELETE(&xt[af].target, target); + up(&xt[af].mutex); +} +EXPORT_SYMBOL(xt_unregister_target); + +int +xt_register_match(int af, struct xt_match *match) +{ + int ret; + + ret = down_interruptible(&xt[af].mutex); + if (ret != 0) + return ret; + + list_add(&match->list, &xt[af].match); + up(&xt[af].mutex); + + return ret; +} +EXPORT_SYMBOL(xt_register_match); + +void +xt_unregister_match(int af, struct xt_match *match) +{ + down(&xt[af].mutex); + LIST_DELETE(&xt[af].match, match); + up(&xt[af].mutex); +} +EXPORT_SYMBOL(xt_unregister_match); + + +/* + * These are weird, but module loading must not be done with mutex + * held (since they will register), and we have to have a single + * function to use try_then_request_module(). + */ + +/* Find match, grabs ref. Returns ERR_PTR() on error. */ +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) + 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); + return m; + } + } else + err = -EPROTOTYPE; /* Found something. */ + } + } + up(&xt[af].mutex); + return ERR_PTR(err); +} +EXPORT_SYMBOL(xt_find_match); + +/* Find target, grabs ref. Returns ERR_PTR() on error. */ +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) + 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); + return t; + } + } else + err = -EPROTOTYPE; /* Found something. */ + } + } + up(&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); + if (IS_ERR(target) || !target) + return NULL; + return target; +} +EXPORT_SYMBOL_GPL(xt_request_find_target); + +static int match_revfn(int af, const char *name, u8 revision, int *bestp) +{ + struct xt_match *m; + int have_rev = 0; + + list_for_each_entry(m, &xt[af].match, list) { + if (strcmp(m->name, name) == 0) { + if (m->revision > *bestp) + *bestp = m->revision; + if (m->revision == revision) + have_rev = 1; + } + } + return have_rev; +} + +static int target_revfn(int af, const char *name, u8 revision, int *bestp) +{ + struct xt_target *t; + int have_rev = 0; + + list_for_each_entry(t, &xt[af].target, list) { + if (strcmp(t->name, name) == 0) { + if (t->revision > *bestp) + *bestp = t->revision; + if (t->revision == revision) + have_rev = 1; + } + } + return have_rev; +} + +/* Returns true or false (if no such extension at all) */ +int xt_find_revision(int af, const char *name, u8 revision, int target, + int *err) +{ + int have_rev, best = -1; + + if (down_interruptible(&xt[af].mutex) != 0) { + *err = -EINTR; + return 1; + } + if (target == 1) + have_rev = target_revfn(af, name, revision, &best); + else + have_rev = match_revfn(af, name, revision, &best); + up(&xt[af].mutex); + + /* Nothing at all? Return 0 to try loading module. */ + if (best == -1) { + *err = -ENOENT; + return 0; + } + + *err = best; + if (!have_rev) + *err = -EPROTONOSUPPORT; + return 1; +} +EXPORT_SYMBOL_GPL(xt_find_revision); + +struct xt_table_info *xt_alloc_table_info(unsigned int size) +{ + struct xt_table_info *newinfo; + int cpu; + + /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */ + if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > num_physpages) + return NULL; + + newinfo = kzalloc(sizeof(struct xt_table_info), GFP_KERNEL); + if (!newinfo) + return NULL; + + newinfo->size = size; + + for_each_cpu(cpu) { + if (size <= PAGE_SIZE) + newinfo->entries[cpu] = kmalloc_node(size, + GFP_KERNEL, + cpu_to_node(cpu)); + else + newinfo->entries[cpu] = vmalloc_node(size, + cpu_to_node(cpu)); + + if (newinfo->entries[cpu] == NULL) { + xt_free_table_info(newinfo); + return NULL; + } + } + + return newinfo; +} +EXPORT_SYMBOL(xt_alloc_table_info); + +void xt_free_table_info(struct xt_table_info *info) +{ + int cpu; + + for_each_cpu(cpu) { + if (info->size <= PAGE_SIZE) + kfree(info->entries[cpu]); + else + vfree(info->entries[cpu]); + } + kfree(info); +} +EXPORT_SYMBOL(xt_free_table_info); + +/* Find table by name, grabs mutex & ref. Returns ERR_PTR() on error. */ +struct xt_table *xt_find_table_lock(int af, const char *name) +{ + struct xt_table *t; + + if (down_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); + return NULL; +} +EXPORT_SYMBOL_GPL(xt_find_table_lock); + +void xt_table_unlock(struct xt_table *table) +{ + up(&xt[table->af].mutex); +} +EXPORT_SYMBOL_GPL(xt_table_unlock); + + +struct xt_table_info * +xt_replace_table(struct xt_table *table, + unsigned int num_counters, + struct xt_table_info *newinfo, + int *error) +{ + struct xt_table_info *oldinfo, *private; + + /* Do the substitution. */ + write_lock_bh(&table->lock); + private = table->private; + /* Check inside lock: is the old number correct? */ + if (num_counters != private->number) { + duprintf("num_counters != table->private->number (%u/%u)\n", + num_counters, private->number); + write_unlock_bh(&table->lock); + *error = -EAGAIN; + return NULL; + } + oldinfo = private; + table->private = newinfo; + newinfo->initial_entries = oldinfo->initial_entries; + write_unlock_bh(&table->lock); + + return oldinfo; +} +EXPORT_SYMBOL_GPL(xt_replace_table); + +int xt_register_table(struct xt_table *table, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo) +{ + int ret; + struct xt_table_info *private; + + ret = down_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; + } + + /* Simplifies replace_table code. */ + table->private = bootstrap; + if (!xt_replace_table(table, 0, newinfo, &ret)) + goto unlock; + + private = table->private; + duprintf("table->private->number = %u\n", private->number); + + /* save number of initial entries */ + private->initial_entries = private->number; + + rwlock_init(&table->lock); + list_prepend(&xt[table->af].tables, table); + + ret = 0; + unlock: + up(&xt[table->af].mutex); + return ret; +} +EXPORT_SYMBOL_GPL(xt_register_table); + +void *xt_unregister_table(struct xt_table *table) +{ + struct xt_table_info *private; + + down(&xt[table->af].mutex); + private = table->private; + LIST_DELETE(&xt[table->af].tables, table); + up(&xt[table->af].mutex); + + return private; +} +EXPORT_SYMBOL_GPL(xt_unregister_table); + +#ifdef CONFIG_PROC_FS +static char *xt_proto_prefix[NPROTO] = { + [AF_INET] = "ip", + [AF_INET6] = "ip6", + [NF_ARP] = "arp", +}; + +static struct list_head *xt_get_idx(struct list_head *list, struct seq_file *seq, loff_t pos) +{ + struct list_head *head = list->next; + + if (!head || list_empty(list)) + return NULL; + + while (pos && (head = head->next)) { + if (head == list) + return NULL; + pos--; + } + return pos ? NULL : head; +} + +static struct list_head *type2list(u_int16_t af, u_int16_t type) +{ + struct list_head *list; + + switch (type) { + case TARGET: + list = &xt[af].target; + break; + case MATCH: + list = &xt[af].match; + break; + case TABLE: + list = &xt[af].tables; + break; + default: + list = NULL; + break; + } + + return list; +} + +static void *xt_tgt_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct proc_dir_entry *pde = (struct proc_dir_entry *) seq->private; + u_int16_t af = (unsigned long)pde->data & 0xffff; + u_int16_t type = (unsigned long)pde->data >> 16; + struct list_head *list; + + if (af >= NPROTO) + return NULL; + + list = type2list(af, type); + if (!list) + return NULL; + + if (down_interruptible(&xt[af].mutex) != 0) + return NULL; + + return xt_get_idx(list, seq, *pos); +} + +static void *xt_tgt_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct proc_dir_entry *pde = seq->private; + u_int16_t af = (unsigned long)pde->data & 0xffff; + u_int16_t type = (unsigned long)pde->data >> 16; + struct list_head *list; + + if (af >= NPROTO) + return NULL; + + list = type2list(af, type); + if (!list) + return NULL; + + (*pos)++; + return xt_get_idx(list, seq, *pos); +} + +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); +} + +static int xt_name_seq_show(struct seq_file *seq, void *v) +{ + char *name = (char *)v + sizeof(struct list_head); + + if (strlen(name)) + return seq_printf(seq, "%s\n", name); + else + return 0; +} + +static struct seq_operations xt_tgt_seq_ops = { + .start = xt_tgt_seq_start, + .next = xt_tgt_seq_next, + .stop = xt_tgt_seq_stop, + .show = xt_name_seq_show, +}; + +static int xt_tgt_open(struct inode *inode, struct file *file) +{ + int ret; + + ret = seq_open(file, &xt_tgt_seq_ops); + if (!ret) { + struct seq_file *seq = file->private_data; + struct proc_dir_entry *pde = PDE(inode); + + seq->private = pde; + } + + return ret; +} + +static struct file_operations xt_file_ops = { + .owner = THIS_MODULE, + .open = xt_tgt_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +#define FORMAT_TABLES "_tables_names" +#define FORMAT_MATCHES "_tables_matches" +#define FORMAT_TARGETS "_tables_targets" + +#endif /* CONFIG_PROC_FS */ + +int xt_proto_init(int af) +{ +#ifdef CONFIG_PROC_FS + char buf[XT_FUNCTION_MAXNAMELEN]; + struct proc_dir_entry *proc; +#endif + + if (af >= NPROTO) + return -EINVAL; + + +#ifdef CONFIG_PROC_FS + strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); + strlcat(buf, FORMAT_TABLES, sizeof(buf)); + proc = proc_net_fops_create(buf, 0440, &xt_file_ops); + if (!proc) + goto out; + proc->data = (void *) ((unsigned long) af | (TABLE << 16)); + + + strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); + strlcat(buf, FORMAT_MATCHES, sizeof(buf)); + proc = proc_net_fops_create(buf, 0440, &xt_file_ops); + if (!proc) + goto out_remove_tables; + proc->data = (void *) ((unsigned long) af | (MATCH << 16)); + + strlcpy(buf, xt_proto_prefix[af], sizeof(buf)); + strlcat(buf, FORMAT_TARGETS, sizeof(buf)); + proc = proc_net_fops_create(buf, 0440, &xt_file_ops); + if (!proc) + goto ou |