diff options
author | Patrick McHardy <kaber@trash.net> | 2006-11-28 20:35:36 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-12-03 00:31:31 -0500 |
commit | 39b46fc6f0d1161a5585cd8af7b3a05e8118ab7e (patch) | |
tree | 708126eec291a8f7105751c656fb50a6091ec97e | |
parent | d7a5c32442ed3d528b9ddfd3d5b837bad0ffa9da (diff) |
[NETFILTER]: x_tables: add port of hashlimit match for IPv4 and IPv6
Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r-- | include/linux/netfilter/Kbuild | 1 | ||||
-rw-r--r-- | include/linux/netfilter/xt_hashlimit.h | 40 | ||||
-rw-r--r-- | include/linux/netfilter_ipv4/ipt_hashlimit.h | 42 | ||||
-rw-r--r-- | net/ipv4/netfilter/Kconfig | 14 | ||||
-rw-r--r-- | net/ipv4/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/Kconfig | 14 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/xt_hashlimit.c (renamed from net/ipv4/netfilter/ipt_hashlimit.c) | 511 |
8 files changed, 339 insertions, 285 deletions
diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index 312bd2ffee33..e379a2d89ea0 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild | |||
@@ -14,6 +14,7 @@ header-y += xt_dscp.h | |||
14 | header-y += xt_DSCP.h | 14 | header-y += xt_DSCP.h |
15 | header-y += xt_esp.h | 15 | header-y += xt_esp.h |
16 | header-y += xt_helper.h | 16 | header-y += xt_helper.h |
17 | header-y += xt_hashlimit.h | ||
17 | header-y += xt_length.h | 18 | header-y += xt_length.h |
18 | header-y += xt_limit.h | 19 | header-y += xt_limit.h |
19 | header-y += xt_mac.h | 20 | header-y += xt_mac.h |
diff --git a/include/linux/netfilter/xt_hashlimit.h b/include/linux/netfilter/xt_hashlimit.h new file mode 100644 index 000000000000..b4556b8edbfd --- /dev/null +++ b/include/linux/netfilter/xt_hashlimit.h | |||
@@ -0,0 +1,40 @@ | |||
1 | #ifndef _XT_HASHLIMIT_H | ||
2 | #define _XT_HASHLIMIT_H | ||
3 | |||
4 | /* timings are in milliseconds. */ | ||
5 | #define XT_HASHLIMIT_SCALE 10000 | ||
6 | /* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490 | ||
7 | seconds, or one every 59 hours. */ | ||
8 | |||
9 | /* details of this structure hidden by the implementation */ | ||
10 | struct xt_hashlimit_htable; | ||
11 | |||
12 | #define XT_HASHLIMIT_HASH_DIP 0x0001 | ||
13 | #define XT_HASHLIMIT_HASH_DPT 0x0002 | ||
14 | #define XT_HASHLIMIT_HASH_SIP 0x0004 | ||
15 | #define XT_HASHLIMIT_HASH_SPT 0x0008 | ||
16 | |||
17 | struct hashlimit_cfg { | ||
18 | u_int32_t mode; /* bitmask of IPT_HASHLIMIT_HASH_* */ | ||
19 | u_int32_t avg; /* Average secs between packets * scale */ | ||
20 | u_int32_t burst; /* Period multiplier for upper limit. */ | ||
21 | |||
22 | /* user specified */ | ||
23 | u_int32_t size; /* how many buckets */ | ||
24 | u_int32_t max; /* max number of entries */ | ||
25 | u_int32_t gc_interval; /* gc interval */ | ||
26 | u_int32_t expire; /* when do entries expire? */ | ||
27 | }; | ||
28 | |||
29 | struct xt_hashlimit_info { | ||
30 | char name [IFNAMSIZ]; /* name */ | ||
31 | struct hashlimit_cfg cfg; | ||
32 | struct xt_hashlimit_htable *hinfo; | ||
33 | |||
34 | /* Used internally by the kernel */ | ||
35 | union { | ||
36 | void *ptr; | ||
37 | struct xt_hashlimit_info *master; | ||
38 | } u; | ||
39 | }; | ||
40 | #endif /*_XT_HASHLIMIT_H*/ | ||
diff --git a/include/linux/netfilter_ipv4/ipt_hashlimit.h b/include/linux/netfilter_ipv4/ipt_hashlimit.h index ac2cb64ecd76..5662120a3d7b 100644 --- a/include/linux/netfilter_ipv4/ipt_hashlimit.h +++ b/include/linux/netfilter_ipv4/ipt_hashlimit.h | |||
@@ -1,40 +1,14 @@ | |||
1 | #ifndef _IPT_HASHLIMIT_H | 1 | #ifndef _IPT_HASHLIMIT_H |
2 | #define _IPT_HASHLIMIT_H | 2 | #define _IPT_HASHLIMIT_H |
3 | 3 | ||
4 | /* timings are in milliseconds. */ | 4 | #include <linux/netfilter/xt_hashlimit.h> |
5 | #define IPT_HASHLIMIT_SCALE 10000 | ||
6 | /* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490 | ||
7 | seconds, or one every 59 hours. */ | ||
8 | 5 | ||
9 | /* details of this structure hidden by the implementation */ | 6 | #define IPT_HASHLIMIT_SCALE XT_HASHLIMIT_SCALE |
10 | struct ipt_hashlimit_htable; | 7 | #define IPT_HASHLIMIT_HASH_DIP XT_HASHLIMIT_HASH_DIP |
8 | #define IPT_HASHLIMIT_HASH_DPT XT_HASHLIMIT_HASH_DPT | ||
9 | #define IPT_HASHLIMIT_HASH_SIP XT_HASHLIMIT_HASH_SIP | ||
10 | #define IPT_HASHLIMIT_HASH_SPT XT_HASHLIMIT_HASH_SPT | ||
11 | 11 | ||
12 | #define IPT_HASHLIMIT_HASH_DIP 0x0001 | 12 | #define ipt_hashlimit_info xt_hashlimit_info |
13 | #define IPT_HASHLIMIT_HASH_DPT 0x0002 | ||
14 | #define IPT_HASHLIMIT_HASH_SIP 0x0004 | ||
15 | #define IPT_HASHLIMIT_HASH_SPT 0x0008 | ||
16 | 13 | ||
17 | struct hashlimit_cfg { | 14 | #endif /* _IPT_HASHLIMIT_H */ |
18 | u_int32_t mode; /* bitmask of IPT_HASHLIMIT_HASH_* */ | ||
19 | u_int32_t avg; /* Average secs between packets * scale */ | ||
20 | u_int32_t burst; /* Period multiplier for upper limit. */ | ||
21 | |||
22 | /* user specified */ | ||
23 | u_int32_t size; /* how many buckets */ | ||
24 | u_int32_t max; /* max number of entries */ | ||
25 | u_int32_t gc_interval; /* gc interval */ | ||
26 | u_int32_t expire; /* when do entries expire? */ | ||
27 | }; | ||
28 | |||
29 | struct ipt_hashlimit_info { | ||
30 | char name [IFNAMSIZ]; /* name */ | ||
31 | struct hashlimit_cfg cfg; | ||
32 | struct ipt_hashlimit_htable *hinfo; | ||
33 | |||
34 | /* Used internally by the kernel */ | ||
35 | union { | ||
36 | void *ptr; | ||
37 | struct ipt_hashlimit_info *master; | ||
38 | } u; | ||
39 | }; | ||
40 | #endif /*_IPT_HASHLIMIT_H*/ | ||
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 4ac5b5c4678d..bc298a3f236f 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig | |||
@@ -326,20 +326,6 @@ config IP_NF_MATCH_ADDRTYPE | |||
326 | If you want to compile it as a module, say M here and read | 326 | If you want to compile it as a module, say M here and read |
327 | <file:Documentation/modules.txt>. If unsure, say `N'. | 327 | <file:Documentation/modules.txt>. If unsure, say `N'. |
328 | 328 | ||
329 | config IP_NF_MATCH_HASHLIMIT | ||
330 | tristate 'hashlimit match support' | ||
331 | depends on IP_NF_IPTABLES | ||
332 | help | ||
333 | This option adds a new iptables `hashlimit' match. | ||
334 | |||
335 | As opposed to `limit', this match dynamically creates a hash table | ||
336 | of limit buckets, based on your selection of source/destination | ||
337 | ip addresses and/or ports. | ||
338 | |||
339 | It enables you to express policies like `10kpps for any given | ||
340 | destination IP' or `500pps from any given source IP' with a single | ||
341 | IPtables rule. | ||
342 | |||
343 | # `filter', generic and specific targets | 329 | # `filter', generic and specific targets |
344 | config IP_NF_FILTER | 330 | config IP_NF_FILTER |
345 | tristate "Packet filtering" | 331 | tristate "Packet filtering" |
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 4ce20ebc4d6c..21359d83f0c7 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile | |||
@@ -53,7 +53,6 @@ obj-$(CONFIG_IP_NF_NAT) += iptable_nat.o | |||
53 | obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o | 53 | obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o |
54 | 54 | ||
55 | # matches | 55 | # matches |
56 | obj-$(CONFIG_IP_NF_MATCH_HASHLIMIT) += ipt_hashlimit.o | ||
57 | obj-$(CONFIG_IP_NF_MATCH_IPRANGE) += ipt_iprange.o | 56 | obj-$(CONFIG_IP_NF_MATCH_IPRANGE) += ipt_iprange.o |
58 | obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o | 57 | obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o |
59 | obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o | 58 | obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o |
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index f619c6527266..7e6125467c12 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -464,5 +464,19 @@ config NETFILTER_XT_MATCH_TCPMSS | |||
464 | 464 | ||
465 | To compile it as a module, choose M here. If unsure, say N. | 465 | To compile it as a module, choose M here. If unsure, say N. |
466 | 466 | ||
467 | config NETFILTER_XT_MATCH_HASHLIMIT | ||
468 | tristate '"hashlimit" match support' | ||
469 | depends on NETFILTER_XTABLES | ||
470 | help | ||
471 | This option adds a `hashlimit' match. | ||
472 | |||
473 | As opposed to `limit', this match dynamically creates a hash table | ||
474 | of limit buckets, based on your selection of source/destination | ||
475 | addresses and/or ports. | ||
476 | |||
477 | It enables you to express policies like `10kpps for any given | ||
478 | destination address' or `500pps from any given source address' | ||
479 | with a single rule. | ||
480 | |||
467 | endmenu | 481 | endmenu |
468 | 482 | ||
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 84d529ded952..f85811bfcfe5 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -59,3 +59,4 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o | |||
59 | obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o | 59 | obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o |
60 | obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o | 60 | obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o |
61 | obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o | 61 | obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o |
62 | obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o | ||
diff --git a/net/ipv4/netfilter/ipt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 33ccdbf8e794..46de566d3721 100644 --- a/net/ipv4/netfilter/ipt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c | |||
@@ -6,23 +6,8 @@ | |||
6 | * $Id: ipt_hashlimit.c 3244 2004-10-20 16:24:29Z laforge@netfilter.org $ | 6 | * $Id: ipt_hashlimit.c 3244 2004-10-20 16:24:29Z laforge@netfilter.org $ |
7 | * | 7 | * |
8 | * Development of this code was funded by Astaro AG, http://www.astaro.com/ | 8 | * Development of this code was funded by Astaro AG, http://www.astaro.com/ |
9 | * | ||
10 | * based on ipt_limit.c by: | ||
11 | * Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr> | ||
12 | * Hervé Eychenne <eychenne@info.enserb.u-bordeaux.fr> | ||
13 | * Rusty Russell <rusty@rustcorp.com.au> | ||
14 | * | ||
15 | * The general idea is to create a hash table for every dstip and have a | ||
16 | * seperate limit counter per tuple. This way you can do something like 'limit | ||
17 | * the number of syn packets for each of my internal addresses. | ||
18 | * | ||
19 | * Ideally this would just be implemented as a general 'hash' match, which would | ||
20 | * allow us to attach any iptables target to it's hash buckets. But this is | ||
21 | * not possible in the current iptables architecture. As always, pkttables for | ||
22 | * 2.7.x will help ;) | ||
23 | */ | 9 | */ |
24 | #include <linux/module.h> | 10 | #include <linux/module.h> |
25 | #include <linux/skbuff.h> | ||
26 | #include <linux/spinlock.h> | 11 | #include <linux/spinlock.h> |
27 | #include <linux/random.h> | 12 | #include <linux/random.h> |
28 | #include <linux/jhash.h> | 13 | #include <linux/jhash.h> |
@@ -31,28 +16,40 @@ | |||
31 | #include <linux/proc_fs.h> | 16 | #include <linux/proc_fs.h> |
32 | #include <linux/seq_file.h> | 17 | #include <linux/seq_file.h> |
33 | #include <linux/list.h> | 18 | #include <linux/list.h> |
19 | #include <linux/skbuff.h> | ||
20 | #include <linux/in.h> | ||
21 | #include <linux/ip.h> | ||
22 | #include <linux/ipv6.h> | ||
34 | 23 | ||
24 | #include <linux/netfilter/x_tables.h> | ||
35 | #include <linux/netfilter_ipv4/ip_tables.h> | 25 | #include <linux/netfilter_ipv4/ip_tables.h> |
36 | #include <linux/netfilter_ipv4/ipt_hashlimit.h> | 26 | #include <linux/netfilter_ipv6/ip6_tables.h> |
37 | 27 | #include <linux/netfilter/xt_hashlimit.h> | |
38 | /* FIXME: this is just for IP_NF_ASSERRT */ | ||
39 | #include <linux/netfilter_ipv4/ip_conntrack.h> | ||
40 | #include <linux/mutex.h> | 28 | #include <linux/mutex.h> |
41 | 29 | ||
42 | MODULE_LICENSE("GPL"); | 30 | MODULE_LICENSE("GPL"); |
43 | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); | 31 | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); |
44 | MODULE_DESCRIPTION("iptables match for limiting per hash-bucket"); | 32 | MODULE_DESCRIPTION("iptables match for limiting per hash-bucket"); |
33 | MODULE_ALIAS("ipt_hashlimit"); | ||
34 | MODULE_ALIAS("ip6t_hashlimit"); | ||
45 | 35 | ||
46 | /* need to declare this at the top */ | 36 | /* need to declare this at the top */ |
47 | static struct proc_dir_entry *hashlimit_procdir; | 37 | static struct proc_dir_entry *hashlimit_procdir4; |
38 | static struct proc_dir_entry *hashlimit_procdir6; | ||
48 | static struct file_operations dl_file_ops; | 39 | static struct file_operations dl_file_ops; |
49 | 40 | ||
50 | /* hash table crap */ | 41 | /* hash table crap */ |
51 | |||
52 | struct dsthash_dst { | 42 | struct dsthash_dst { |
53 | __be32 src_ip; | 43 | union { |
54 | __be32 dst_ip; | 44 | struct { |
55 | /* ports have to be consecutive !!! */ | 45 | __be32 src; |
46 | __be32 dst; | ||
47 | } ip; | ||
48 | struct { | ||
49 | __be32 src[4]; | ||
50 | __be32 dst[4]; | ||
51 | } ip6; | ||
52 | } addr; | ||
56 | __be16 src_port; | 53 | __be16 src_port; |
57 | __be16 dst_port; | 54 | __be16 dst_port; |
58 | }; | 55 | }; |
@@ -71,9 +68,10 @@ struct dsthash_ent { | |||
71 | } rateinfo; | 68 | } rateinfo; |
72 | }; | 69 | }; |
73 | 70 | ||
74 | struct ipt_hashlimit_htable { | 71 | struct xt_hashlimit_htable { |
75 | struct hlist_node node; /* global list of all htables */ | 72 | struct hlist_node node; /* global list of all htables */ |
76 | atomic_t use; | 73 | atomic_t use; |
74 | int family; | ||
77 | 75 | ||
78 | struct hashlimit_cfg cfg; /* config */ | 76 | struct hashlimit_cfg cfg; /* config */ |
79 | 77 | ||
@@ -81,8 +79,8 @@ struct ipt_hashlimit_htable { | |||
81 | spinlock_t lock; /* lock for list_head */ | 79 | spinlock_t lock; /* lock for list_head */ |
82 | u_int32_t rnd; /* random seed for hash */ | 80 | u_int32_t rnd; /* random seed for hash */ |
83 | int rnd_initialized; | 81 | int rnd_initialized; |
82 | unsigned int count; /* number entries in table */ | ||
84 | struct timer_list timer; /* timer for gc */ | 83 | struct timer_list timer; /* timer for gc */ |
85 | atomic_t count; /* number entries in table */ | ||
86 | 84 | ||
87 | /* seq_file stuff */ | 85 | /* seq_file stuff */ |
88 | struct proc_dir_entry *pde; | 86 | struct proc_dir_entry *pde; |
@@ -97,41 +95,33 @@ static kmem_cache_t *hashlimit_cachep __read_mostly; | |||
97 | 95 | ||
98 | static inline int dst_cmp(const struct dsthash_ent *ent, struct dsthash_dst *b) | 96 | static inline int dst_cmp(const struct dsthash_ent *ent, struct dsthash_dst *b) |
99 | { | 97 | { |
100 | return (ent->dst.dst_ip == b->dst_ip | 98 | return !memcmp(&ent->dst, b, sizeof(ent->dst)); |
101 | && ent->dst.dst_port == b->dst_port | ||
102 | && ent->dst.src_port == b->src_port | ||
103 | && ent->dst.src_ip == b->src_ip); | ||
104 | } | 99 | } |
105 | 100 | ||
106 | static inline u_int32_t | 101 | static u_int32_t |
107 | hash_dst(const struct ipt_hashlimit_htable *ht, const struct dsthash_dst *dst) | 102 | hash_dst(const struct xt_hashlimit_htable *ht, const struct dsthash_dst *dst) |
108 | { | 103 | { |
109 | return (jhash_3words((__force u32)dst->dst_ip, | 104 | return jhash(dst, sizeof(*dst), ht->rnd) % ht->cfg.size; |
110 | ((__force u32)dst->dst_port<<16 | | ||
111 | (__force u32)dst->src_port), | ||
112 | (__force u32)dst->src_ip, ht->rnd) % ht->cfg.size); | ||
113 | } | 105 | } |
114 | 106 | ||
115 | static inline struct dsthash_ent * | 107 | static struct dsthash_ent * |
116 | __dsthash_find(const struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst) | 108 | dsthash_find(const struct xt_hashlimit_htable *ht, struct dsthash_dst *dst) |
117 | { | 109 | { |
118 | struct dsthash_ent *ent; | 110 | struct dsthash_ent *ent; |
119 | struct hlist_node *pos; | 111 | struct hlist_node *pos; |
120 | u_int32_t hash = hash_dst(ht, dst); | 112 | u_int32_t hash = hash_dst(ht, dst); |
121 | 113 | ||
122 | if (!hlist_empty(&ht->hash[hash])) | 114 | if (!hlist_empty(&ht->hash[hash])) { |
123 | hlist_for_each_entry(ent, pos, &ht->hash[hash], node) { | 115 | hlist_for_each_entry(ent, pos, &ht->hash[hash], node) |
124 | if (dst_cmp(ent, dst)) { | 116 | if (dst_cmp(ent, dst)) |
125 | return ent; | 117 | return ent; |
126 | } | 118 | } |
127 | } | ||
128 | |||
129 | return NULL; | 119 | return NULL; |
130 | } | 120 | } |
131 | 121 | ||
132 | /* allocate dsthash_ent, initialize dst, put in htable and lock it */ | 122 | /* allocate dsthash_ent, initialize dst, put in htable and lock it */ |
133 | static struct dsthash_ent * | 123 | static struct dsthash_ent * |
134 | __dsthash_alloc_init(struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst) | 124 | dsthash_alloc_init(struct xt_hashlimit_htable *ht, struct dsthash_dst *dst) |
135 | { | 125 | { |
136 | struct dsthash_ent *ent; | 126 | struct dsthash_ent *ent; |
137 | 127 | ||
@@ -142,12 +132,11 @@ __dsthash_alloc_init(struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst) | |||
142 | ht->rnd_initialized = 1; | 132 | ht->rnd_initialized = 1; |
143 | } | 133 | } |
144 | 134 | ||
145 | if (ht->cfg.max && | 135 | if (ht->cfg.max && ht->count >= ht->cfg.max) { |
146 | atomic_read(&ht->count) >= ht->cfg.max) { | ||
147 | /* FIXME: do something. question is what.. */ | 136 | /* FIXME: do something. question is what.. */ |
148 | if (net_ratelimit()) | 137 | if (net_ratelimit()) |
149 | printk(KERN_WARNING | 138 | printk(KERN_WARNING |
150 | "ipt_hashlimit: max count of %u reached\n", | 139 | "xt_hashlimit: max count of %u reached\n", |
151 | ht->cfg.max); | 140 | ht->cfg.max); |
152 | return NULL; | 141 | return NULL; |
153 | } | 142 | } |
@@ -155,53 +144,47 @@ __dsthash_alloc_init(struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst) | |||
155 | ent = kmem_cache_alloc(hashlimit_cachep, GFP_ATOMIC); | 144 | ent = kmem_cache_alloc(hashlimit_cachep, GFP_ATOMIC); |
156 | if (!ent) { | 145 | if (!ent) { |
157 | if (net_ratelimit()) | 146 | if (net_ratelimit()) |
158 | printk(KERN_ERR | 147 | printk(KERN_ERR |
159 | "ipt_hashlimit: can't allocate dsthash_ent\n"); | 148 | "xt_hashlimit: can't allocate dsthash_ent\n"); |
160 | return NULL; | 149 | return NULL; |
161 | } | 150 | } |
162 | 151 | memcpy(&ent->dst, dst, sizeof(ent->dst)); | |
163 | atomic_inc(&ht->count); | ||
164 | |||
165 | ent->dst.dst_ip = dst->dst_ip; | ||
166 | ent->dst.dst_port = dst->dst_port; | ||
167 | ent->dst.src_ip = dst->src_ip; | ||
168 | ent->dst.src_port = dst->src_port; | ||
169 | 152 | ||
170 | hlist_add_head(&ent->node, &ht->hash[hash_dst(ht, dst)]); | 153 | hlist_add_head(&ent->node, &ht->hash[hash_dst(ht, dst)]); |
171 | 154 | ht->count++; | |
172 | return ent; | 155 | return ent; |
173 | } | 156 | } |
174 | 157 | ||
175 | static inline void | 158 | static inline void |
176 | __dsthash_free(struct ipt_hashlimit_htable *ht, struct dsthash_ent *ent) | 159 | dsthash_free(struct xt_hashlimit_htable *ht, struct dsthash_ent *ent) |
177 | { | 160 | { |
178 | hlist_del(&ent->node); | 161 | hlist_del(&ent->node); |
179 | kmem_cache_free(hashlimit_cachep, ent); | 162 | kmem_cache_free(hashlimit_cachep, ent); |
180 | atomic_dec(&ht->count); | 163 | ht->count--; |
181 | } | 164 | } |
182 | static void htable_gc(unsigned long htlong); | 165 | static void htable_gc(unsigned long htlong); |
183 | 166 | ||
184 | static int htable_create(struct ipt_hashlimit_info *minfo) | 167 | static int htable_create(struct xt_hashlimit_info *minfo, int family) |
185 | { | 168 | { |
186 | int i; | 169 | struct xt_hashlimit_htable *hinfo; |
187 | unsigned int size; | 170 | unsigned int size; |
188 | struct ipt_hashlimit_htable *hinfo; | 171 | unsigned int i; |
189 | 172 | ||
190 | if (minfo->cfg.size) | 173 | if (minfo->cfg.size) |
191 | size = minfo->cfg.size; | 174 | size = minfo->cfg.size; |
192 | else { | 175 | else { |
193 | size = (((num_physpages << PAGE_SHIFT) / 16384) | 176 | size = ((num_physpages << PAGE_SHIFT) / 16384) / |
194 | / sizeof(struct list_head)); | 177 | sizeof(struct list_head); |
195 | if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE)) | 178 | if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE)) |
196 | size = 8192; | 179 | size = 8192; |
197 | if (size < 16) | 180 | if (size < 16) |
198 | size = 16; | 181 | size = 16; |
199 | } | 182 | } |
200 | /* FIXME: don't use vmalloc() here or anywhere else -HW */ | 183 | /* FIXME: don't use vmalloc() here or anywhere else -HW */ |
201 | hinfo = vmalloc(sizeof(struct ipt_hashlimit_htable) | 184 | hinfo = vmalloc(sizeof(struct xt_hashlimit_htable) + |
202 | + (sizeof(struct list_head) * size)); | 185 | sizeof(struct list_head) * size); |
203 | if (!hinfo) { | 186 | if (!hinfo) { |
204 | printk(KERN_ERR "ipt_hashlimit: Unable to create hashtable\n"); | 187 | printk(KERN_ERR "xt_hashlimit: unable to create hashtable\n"); |
205 | return -1; | 188 | return -1; |
206 | } | 189 | } |
207 | minfo->hinfo = hinfo; | 190 | minfo->hinfo = hinfo; |
@@ -217,11 +200,14 @@ static int htable_create(struct ipt_hashlimit_info *minfo) | |||
217 | for (i = 0; i < hinfo->cfg.size; i++) | 200 | for (i = 0; i < hinfo->cfg.size; i++) |
218 | INIT_HLIST_HEAD(&hinfo->hash[i]); | 201 | INIT_HLIST_HEAD(&hinfo->hash[i]); |
219 | 202 | ||
220 | atomic_set(&hinfo->count, 0); | ||
221 | atomic_set(&hinfo->use, 1); | 203 | atomic_set(&hinfo->use, 1); |
204 | hinfo->count = 0; | ||
205 | hinfo->family = family; | ||
222 | hinfo->rnd_initialized = 0; | 206 | hinfo->rnd_initialized = 0; |
223 | spin_lock_init(&hinfo->lock); | 207 | spin_lock_init(&hinfo->lock); |
224 | hinfo->pde = create_proc_entry(minfo->name, 0, hashlimit_procdir); | 208 | hinfo->pde = create_proc_entry(minfo->name, 0, |
209 | family == AF_INET ? hashlimit_procdir4 : | ||
210 | hashlimit_procdir6); | ||
225 | if (!hinfo->pde) { | 211 | if (!hinfo->pde) { |
226 | vfree(hinfo); | 212 | vfree(hinfo); |
227 | return -1; | 213 | return -1; |
@@ -242,23 +228,21 @@ static int htable_create(struct ipt_hashlimit_info *minfo) | |||
242 | return 0; | 228 | return 0; |
243 | } | 229 | } |
244 | 230 | ||
245 | static int select_all(struct ipt_hashlimit_htable *ht, struct dsthash_ent *he) | 231 | static int select_all(struct xt_hashlimit_htable *ht, struct dsthash_ent *he) |
246 | { | 232 | { |
247 | return 1; | 233 | return 1; |
248 | } | 234 | } |
249 | 235 | ||
250 | static int select_gc(struct ipt_hashlimit_htable *ht, struct dsthash_ent *he) | 236 | static int select_gc(struct xt_hashlimit_htable *ht, struct dsthash_ent *he) |
251 | { | 237 | { |
252 | return (jiffies >= he->expires); | 238 | return (jiffies >= he->expires); |
253 | } | 239 | } |
254 | 240 | ||
255 | static void htable_selective_cleanup(struct ipt_hashlimit_htable *ht, | 241 | static void htable_selective_cleanup(struct xt_hashlimit_htable *ht, |
256 | int (*select)(struct ipt_hashlimit_htable *ht, | 242 | int (*select)(struct xt_hashlimit_htable *ht, |
257 | struct dsthash_ent *he)) | 243 | struct dsthash_ent *he)) |
258 | { | 244 | { |
259 | int i; | 245 | unsigned int i; |
260 | |||
261 | IP_NF_ASSERT(ht->cfg.size && ht->cfg.max); | ||
262 | 246 | ||
263 | /* lock hash table and iterate over it */ | 247 | /* lock hash table and iterate over it */ |
264 | spin_lock_bh(&ht->lock); | 248 | spin_lock_bh(&ht->lock); |
@@ -267,7 +251,7 @@ static void htable_selective_cleanup(struct ipt_hashlimit_htable *ht, | |||
267 | struct hlist_node *pos, *n; | 251 | struct hlist_node *pos, *n; |
268 | hlist_for_each_entry_safe(dh, pos, n, &ht->hash[i], node) { | 252 | hlist_for_each_entry_safe(dh, pos, n, &ht->hash[i], node) { |
269 | if ((*select)(ht, dh)) | 253 | if ((*select)(ht, dh)) |
270 | __dsthash_free(ht, dh); | 254 | dsthash_free(ht, dh); |
271 | } | 255 | } |
272 | } | 256 | } |
273 | spin_unlock_bh(&ht->lock); | 257 | spin_unlock_bh(&ht->lock); |
@@ -276,7 +260,7 @@ static void htable_selective_cleanup(struct ipt_hashlimit_htable *ht, | |||
276 | /* hash table garbage collector, run by timer */ | 260 | /* hash table garbage collector, run by timer */ |
277 | static void htable_gc(unsigned long htlong) | 261 | static void htable_gc(unsigned long htlong) |
278 | { | 262 | { |
279 | struct ipt_hashlimit_htable *ht = (struct ipt_hashlimit_htable *)htlong; | 263 | struct xt_hashlimit_htable *ht = (struct xt_hashlimit_htable *)htlong; |
280 | 264 | ||
281 | htable_selective_cleanup(ht, select_gc); | 265 | htable_selective_cleanup(ht, select_gc); |
282 | 266 | ||
@@ -285,38 +269,39 @@ static void htable_gc(unsigned long htlong) | |||
285 | add_timer(&ht->timer); | 269 | add_timer(&ht->timer); |
286 | } | 270 | } |
287 | 271 | ||
288 | static void htable_destroy(struct ipt_hashlimit_htable *hinfo) | 272 | static void htable_destroy(struct xt_hashlimit_htable *hinfo) |
289 | { | 273 | { |
290 | /* remove timer, if it is pending */ | 274 | /* remove timer, if it is pending */ |
291 | if (timer_pending(&hinfo->timer)) | 275 | if (timer_pending(&hinfo->timer)) |
292 | del_timer(&hinfo->timer); | 276 | del_timer(&hinfo->timer); |
293 | 277 | ||
294 | /* remove proc entry */ | 278 | /* remove proc entry */ |
295 | remove_proc_entry(hinfo->pde->name, hashlimit_procdir); | 279 | remove_proc_entry(hinfo->pde->name, |
296 | 280 | hinfo->family == AF_INET ? hashlimit_procdir4 : | |
281 | hashlimit_procdir6); | ||
297 | htable_selective_cleanup(hinfo, select_all); | 282 | htable_selective_cleanup(hinfo, select_all); |
298 | vfree(hinfo); | 283 | vfree(hinfo); |
299 | } | 284 | } |
300 | 285 | ||
301 | static struct ipt_hashlimit_htable *htable_find_get(char *name) | 286 | static struct xt_hashlimit_htable *htable_find_get(char *name, int family) |
302 | { | 287 | { |
303 | struct ipt_hashlimit_htable *hinfo; | 288 | struct xt_hashlimit_htable *hinfo; |
304 | struct hlist_node *pos; | 289 | struct hlist_node *pos; |
305 | 290 | ||
306 | spin_lock_bh(&hashlimit_lock); | 291 | spin_lock_bh(&hashlimit_lock); |
307 | hlist_for_each_entry(hinfo, pos, &hashlimit_htables, node) { | 292 | hlist_for_each_entry(hinfo, pos, &hashlimit_htables, node) { |
308 | if (!strcmp(name, hinfo->pde->name)) { | 293 | if (!strcmp(name, hinfo->pde->name) && |
294 | hinfo->family == family) { | ||
309 | atomic_inc(&hinfo->use); | 295 | atomic_inc(&hinfo->use); |
310 | spin_unlock_bh(&hashlimit_lock); | 296 | spin_unlock_bh(&hashlimit_lock); |
311 | return hinfo; | 297 | return hinfo; |
312 | } | 298 | } |
313 | } | 299 | } |
314 | spin_unlock_bh(&hashlimit_lock); | 300 | spin_unlock_bh(&hashlimit_lock); |
315 | |||
316 | return NULL; | 301 | return NULL; |
317 | } | 302 | } |
318 | 303 | ||
319 | static void htable_put(struct ipt_hashlimit_htable *hinfo) | 304 | static void htable_put(struct xt_hashlimit_htable *hinfo) |
320 | { | 305 | { |
321 | if (atomic_dec_and_test(&hinfo->use)) { | 306 | if (atomic_dec_and_test(&hinfo->use)) { |
322 | spin_lock_bh(&hashlimit_lock); | 307 | spin_lock_bh(&hashlimit_lock); |
@@ -326,7 +311,6 @@ static void htable_put(struct ipt_hashlimit_htable *hinfo) | |||
326 | } | 311 | } |
327 | } | 312 | } |
328 | 313 | ||
329 | |||
330 | /* The algorithm used is the Simple Token Bucket Filter (TBF) | 314 | /* The algorithm used is the Simple Token Bucket Filter (TBF) |
331 | * see net/sched/sch_tbf.c in the linux source tree | 315 | * see net/sched/sch_tbf.c in the linux source tree |
332 | */ | 316 | */ |
@@ -370,17 +354,82 @@ user2credits(u_int32_t user) | |||
370 | /* If multiplying would overflow... */ | 354 | /* If multiplying would overflow... */ |
371 | if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY)) | 355 | if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY)) |
372 | /* Divide first. */ | 356 | /* Divide first. */ |
373 | return (user / IPT_HASHLIMIT_SCALE) * HZ * CREDITS_PER_JIFFY; | 357 | return (user / XT_HASHLIMIT_SCALE) * HZ * CREDITS_PER_JIFFY; |
374 | 358 | ||
375 | return (user * HZ * CREDITS_PER_JIFFY) / IPT_HASHLIMIT_SCALE; | 359 | return (user * HZ * CREDITS_PER_JIFFY) / XT_HASHLIMIT_SCALE; |
376 | } | 360 | } |
377 | 361 | ||
378 | static inline void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now) | 362 | static inline void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now) |
379 | { | 363 | { |
380 | dh->rateinfo.credit += (now - xchg(&dh->rateinfo.prev, now)) | 364 | dh->rateinfo.credit += (now - dh->rateinfo.prev) * CREDITS_PER_JIFFY; |
381 | * CREDITS_PER_JIFFY; | ||
382 | if (dh->rateinfo.credit > dh->rateinfo.credit_cap) | 365 | if (dh->rateinfo.credit > dh->rateinfo.credit_cap) |
383 | dh->rateinfo.credit = dh->rateinfo.credit_cap; | 366 | dh->rateinfo.credit = dh->rateinfo.credit_cap; |
367 | dh->rateinfo.prev = now; | ||
368 | } | ||
369 | |||
370 | static int | ||
371 | hashlimit_init_dst(struct xt_hashlimit_htable *hinfo, struct dsthash_dst *dst, | ||
372 | const struct sk_buff *skb, unsigned int protoff) | ||
373 | { | ||
374 | __be16 _ports[2], *ports; | ||
375 | int nexthdr; | ||
376 | |||
377 | memset(dst, 0, sizeof(*dst)); | ||
378 | |||
379 | switch (hinfo->family) { | ||
380 | case AF_INET: | ||
381 | if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DIP) | ||
382 | dst->addr.ip.dst = skb->nh.iph->daddr; | ||
383 | if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_SIP) | ||
384 | dst->addr.ip.src = skb->nh.iph->saddr; | ||
385 | |||
386 | if (!(hinfo->cfg.mode & | ||
387 | (XT_HASHLIMIT_HASH_DPT | XT_HASHLIMIT_HASH_SPT))) | ||
388 | return 0; | ||
389 | nexthdr = skb->nh.iph->protocol; | ||
390 | break; | ||
391 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
392 | case AF_INET6: | ||
393 | if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DIP) | ||
394 | memcpy(&dst->addr.ip6.dst, &skb->nh.ipv6h->daddr, | ||
395 | sizeof(dst->addr.ip6.dst)); | ||
396 | if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_SIP) | ||
397 | memcpy(&dst->addr.ip6.src, &skb->nh.ipv6h->saddr, | ||
398 | sizeof(dst->addr.ip6.src)); | ||
399 | |||
400 | if (!(hinfo->cfg.mode & | ||
401 | (XT_HASHLIMIT_HASH_DPT | XT_HASHLIMIT_HASH_SPT))) | ||
402 | return 0; | ||
403 | nexthdr = ipv6_find_hdr(skb, &protoff, -1, NULL); | ||
404 | if (nexthdr < 0) | ||
405 | return -1; | ||
406 | break; | ||
407 | #endif | ||
408 | default: | ||
409 | BUG(); | ||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | switch (nexthdr) { | ||
414 | case IPPROTO_TCP: | ||
415 | case IPPROTO_UDP: | ||
416 | case IPPROTO_SCTP: | ||
417 | case IPPROTO_DCCP: | ||
418 | ports = skb_header_pointer(skb, protoff, sizeof(_ports), | ||
419 | &_ports); | ||
420 | break; | ||
421 | default: | ||
422 | _ports[0] = _ports[1] = 0; | ||
423 | ports = _ports; | ||
424 | break; | ||
425 | } | ||
426 | if (!ports) | ||
427 | return -1; | ||
428 | if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_SPT) | ||
429 | dst->src_port = ports[0]; | ||
430 | if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DPT) | ||
431 | dst->dst_port = ports[1]; | ||
432 | return 0; | ||
384 | } | 433 | } |
385 | 434 | ||
386 | static int | 435 | static int |
@@ -393,68 +442,31 @@ hashlimit_match(const struct sk_buff *skb, | |||
393 | unsigned int protoff, | 442 | unsigned int protoff, |
394 | int *hotdrop) | 443 | int *hotdrop) |
395 | { | 444 | { |
396 | struct ipt_hashlimit_info *r = | 445 | struct xt_hashlimit_info *r = |
397 | ((struct ipt_hashlimit_info *)matchinfo)->u.master; | 446 | ((struct xt_hashlimit_info *)matchinfo)->u.master; |
398 | struct ipt_hashlimit_htable *hinfo = r->hinfo; | 447 | struct xt_hashlimit_htable *hinfo = r->hinfo; |
399 | unsigned long now = jiffies; | 448 | unsigned long now = jiffies; |
400 | struct dsthash_ent *dh; | 449 | struct dsthash_ent *dh; |
401 | struct dsthash_dst dst; | 450 | struct dsthash_dst dst; |
402 | 451 | ||
403 | /* build 'dst' according to hinfo->cfg and current packet */ | 452 | if (hashlimit_init_dst(hinfo, &dst, skb, protoff) < 0) |
404 | memset(&dst, 0, sizeof(dst)); | 453 | goto hotdrop; |
405 | if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_DIP) | ||
406 | dst.dst_ip = skb->nh.iph->daddr; | ||
407 | if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_SIP) | ||
408 | dst.src_ip = skb->nh.iph->saddr; | ||
409 | if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_DPT | ||
410 | ||hinfo->cfg.mode & IPT_HASHLIMIT_HASH_SPT) { | ||
411 | __be16 _ports[2], *ports; | ||
412 | |||
413 | switch (skb->nh.iph->protocol) { | ||
414 | case IPPROTO_TCP: | ||
415 | case IPPROTO_UDP: | ||
416 | case IPPROTO_SCTP: | ||
417 | case IPPROTO_DCCP: | ||
418 | ports = skb_header_pointer(skb, skb->nh.iph->ihl*4, | ||
419 | sizeof(_ports), &_ports); | ||
420 | break; | ||
421 | default: | ||
422 | _ports[0] = _ports[1] = 0; | ||
423 | ports = _ports; | ||
424 | break; | ||
425 | } | ||
426 | if (!ports) { | ||
427 | /* We've been asked to examine this packet, and we | ||
428 | can't. Hence, no choice but to drop. */ | ||
429 | *hotdrop = 1; | ||
430 | return 0; | ||
431 | } | ||
432 | if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_SPT) | ||
433 | dst.src_port = ports[0]; | ||
434 | if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_DPT) | ||
435 | dst.dst_port = ports[1]; | ||
436 | } | ||
437 | 454 | ||
438 | spin_lock_bh(&hinfo->lock); | 455 | spin_lock_bh(&hinfo->lock); |
439 | dh = __dsthash_find(hinfo, &dst); | 456 | dh = dsthash_find(hinfo, &dst); |
440 | if (!dh) { | 457 | if (!dh) { |
441 | dh = __dsthash_alloc_init(hinfo, &dst); | 458 | dh = dsthash_alloc_init(hinfo, &dst); |
442 | |||
443 | if (!dh) { | 459 | if (!dh) { |
444 | /* enomem... don't match == DROP */ | ||
445 | if (net_ratelimit()) | ||
446 | printk(KERN_ERR "%s: ENOMEM\n", __FUNCTION__); | ||
447 | spin_unlock_bh(&hinfo->lock); | 460 | spin_unlock_bh(&hinfo->lock); |
448 | return 0; | 461 | goto hotdrop; |
449 | } | 462 | } |
450 | 463 | ||
451 | dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire); | 464 | dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire); |
452 | |||
453 | dh->rateinfo.prev = jiffies; | 465 | dh->rateinfo.prev = jiffies; |
454 | dh->rateinfo.credit = user2credits(hinfo->cfg.avg * | 466 | dh->rateinfo.credit = user2credits(hinfo->cfg.avg * |
455 | hinfo->cfg.burst); | 467 | hinfo->cfg.burst); |
456 | dh->rateinfo.credit_cap = user2credits(hinfo->cfg.avg * | 468 | dh->rateinfo.credit_cap = user2credits(hinfo->cfg.avg * |
457 | hinfo->cfg.burst); | 469 | hinfo->cfg.burst); |
458 | dh->rateinfo.cost = user2credits(hinfo->cfg.avg); | 470 | dh->rateinfo.cost = user2credits(hinfo->cfg.avg); |
459 | } else { | 471 | } else { |
460 | /* update expiration timeout */ | 472 | /* update expiration timeout */ |
@@ -473,6 +485,10 @@ hashlimit_match(const struct sk_buff *skb, | |||
473 | 485 | ||
474 | /* default case: we're overlimit, thus don't match */ | 486 | /* default case: we're overlimit, thus don't match */ |
475 | return 0; | 487 | return 0; |
488 | |||
489 | hotdrop: | ||
490 | *hotdrop = 1; | ||
491 | return 0; | ||
476 | } | 492 | } |
477 | 493 | ||
478 | static int | 494 | static int |
@@ -482,42 +498,37 @@ hashlimit_checkentry(const char *tablename, | |||
482 | void *matchinfo, | 498 | void *matchinfo, |
483 | unsigned int hook_mask) | 499 | unsigned int hook_mask) |
484 | { | 500 | { |
485 | struct ipt_hashlimit_info *r = matchinfo; | 501 | struct xt_hashlimit_info *r = matchinfo; |
486 | 502 | ||
487 | /* Check for overflow. */ | 503 | /* Check for overflow. */ |
488 | if (r->cfg.burst == 0 | 504 | if (r->cfg.burst == 0 || |
489 | || user2credits(r->cfg.avg * r->cfg.burst) < | 505 | user2credits(r->cfg.avg * r->cfg.burst) < user2credits(r->cfg.avg)) { |
490 | user2credits(r->cfg.avg)) { | 506 | printk(KERN_ERR "xt_hashlimit: overflow, try lower: %u/%u\n", |
491 | printk(KERN_ERR "ipt_hashlimit: Overflow, try lower: %u/%u\n", | ||
492 | r->cfg.avg, r->cfg.burst); | 507 | r->cfg.avg, r->cfg.burst); |
493 | return 0; | 508 | return 0; |
494 | } | 509 | } |
495 | 510 | if (r->cfg.mode == 0 || | |
496 | if (r->cfg.mode == 0 | 511 | r->cfg.mode > (XT_HASHLIMIT_HASH_DPT | |
497 | || r->cfg.mode > (IPT_HASHLIMIT_HASH_DPT | 512 | XT_HASHLIMIT_HASH_DIP | |
498 | |IPT_HASHLIMIT_HASH_DIP | 513 | XT_HASHLIMIT_HASH_SIP | |
499 | |IPT_HASHLIMIT_HASH_SIP | 514 | XT_HASHLIMIT_HASH_SPT)) |
500 | |IPT_HASHLIMIT_HASH_SPT)) | ||
501 | return 0; | 515 | return 0; |
502 | |||
503 | if (!r->cfg.gc_interval) | 516 | if (!r->cfg.gc_interval) |
504 | return 0; | 517 | return 0; |
505 | |||
506 | if (!r->cfg.expire) | 518 | if (!r->cfg.expire) |
507 | return 0; | 519 | return 0; |
508 | |||
509 | if (r->name[sizeof(r->name) - 1] != '\0') | 520 | if (r->name[sizeof(r->name) - 1] != '\0') |
510 | return 0; | 521 | return 0; |
511 | 522 | ||
512 | /* This is the best we've got: We cannot release and re-grab lock, | 523 | /* This is the best we've got: We cannot release and re-grab lock, |
513 | * since checkentry() is called before ip_tables.c grabs ipt_mutex. | 524 | * since checkentry() is called before x_tables.c grabs xt_mutex. |
514 | * We also cannot grab the hashtable spinlock, since htable_create will | 525 | * We also cannot grab the hashtable spinlock, since htable_create will |
515 | * call vmalloc, and that can sleep. And we cannot just re-search | 526 | * call vmalloc, and that can sleep. And we cannot just re-search |
516 | * the list of htable's in htable_create(), since then we would | 527 | * the list of htable's in htable_create(), since then we would |
517 | * create duplicate proc files. -HW */ | 528 | * create duplicate proc files. -HW */ |
518 | mutex_lock(&hlimit_mutex); | 529 | mutex_lock(&hlimit_mutex); |
519 | r->hinfo = htable_find_get(r->name); | 530 | r->hinfo = htable_find_get(r->name, match->family); |
520 | if (!r->hinfo && (htable_create(r) != 0)) { | 531 | if (!r->hinfo && htable_create(r, match->family) != 0) { |
521 | mutex_unlock(&hlimit_mutex); | 532 | mutex_unlock(&hlimit_mutex); |
522 | return 0; | 533 | return 0; |
523 | } | 534 | } |
@@ -525,20 +536,19 @@ hashlimit_checkentry(const char *tablename, | |||
525 | 536 | ||
526 | /* Ugly hack: For SMP, we only want to use one set */ | 537 | /* Ugly hack: For SMP, we only want to use one set */ |
527 | r->u.master = r; | 538 | r->u.master = r; |
528 | |||
529 | return 1; | 539 | return 1; |
530 | } | 540 | } |
531 | 541 | ||
532 | static void | 542 | static void |
533 | hashlimit_destroy(const struct xt_match *match, void *matchinfo) | 543 | hashlimit_destroy(const struct xt_match *match, void *matchinfo) |
534 | { | 544 | { |
535 | struct ipt_hashlimit_info *r = matchinfo; | 545 | struct xt_hashlimit_info *r = matchinfo; |
536 | 546 | ||
537 | htable_put(r->hinfo); | 547 | htable_put(r->hinfo); |
538 | } | 548 | } |
539 | 549 | ||
540 | #ifdef CONFIG_COMPAT | 550 | #ifdef CONFIG_COMPAT |
541 | struct compat_ipt_hashlimit_info { | 551 | struct compat_xt_hashlimit_info { |
542 | char name[IFNAMSIZ]; | 552 | char name[IFNAMSIZ]; |
543 | struct hashlimit_cfg cfg; | 553 | struct hashlimit_cfg cfg; |
544 | compat_uptr_t hinfo; | 554 | compat_uptr_t hinfo; |
@@ -547,40 +557,56 @@ struct compat_ipt_hashlimit_info { | |||
547 | 557 | ||
548 | static void compat_from_user(void *dst, void *src) | 558 | static void compat_from_user(void *dst, void *src) |
549 | { | 559 | { |
550 | int off = offsetof(struct compat_ipt_hashlimit_info, hinfo); | 560 | int off = offsetof(struct compat_xt_hashlimit_info, hinfo); |
551 | 561 | ||
552 | memcpy(dst, src, off); | 562 | memcpy(dst, src, off); |
553 | memset(dst + off, 0, sizeof(struct compat_ipt_hashlimit_info) - off); | 563 | memset(dst + off, 0, sizeof(struct compat_xt_hashlimit_info) - off); |
554 | } | 564 | } |
555 | 565 | ||
556 | static int compat_to_user(void __user *dst, void *src) | 566 | static int compat_to_user(void __user *dst, void *src) |
557 | { | 567 | { |
558 | int off = offsetof(struct compat_ipt_hashlimit_info, hinfo); | 568 | int off = offsetof(struct compat_xt_hashlimit_info, hinfo); |
559 | 569 | ||
560 | return copy_to_user(dst, src, off) ? -EFAULT : 0; | 570 | return copy_to_user(dst, src, off) ? -EFAULT : 0; |
561 | } | 571 | } |
562 | #endif | 572 | #endif |
563 | 573 | ||
564 | static struct ipt_match ipt_hashlimit = { | 574 | static struct xt_match xt_hashlimit[] = { |
565 | .name = "hashlimit", | 575 | { |
566 | .match = hashlimit_match, | 576 | .name = "hashlimit", |
567 | .matchsize = sizeof(struct ipt_hashlimit_info), | 577 | .family = AF_INET, |
578 | .match = hashlimit_match, | ||
579 | .matchsize = sizeof(struct xt_hashlimit_info), | ||
580 | #ifdef CONFIG_COMPAT | ||
581 | .compatsize = sizeof(struct compat_xt_hashlimit_info), | ||
582 | .compat_from_user = compat_from_user, | ||
583 | .compat_to_user = compat_to_user, | ||
584 | #endif | ||
585 | .checkentry = hashlimit_checkentry, | ||
586 | .destroy = hashlimit_destroy, | ||
587 | .me = THIS_MODULE | ||
588 | }, | ||
589 | { | ||
590 | .name = "hashlimit", | ||
591 | .family = AF_INET6, | ||
592 | .match = hashlimit_match, | ||
593 | .matchsize = sizeof(struct xt_hashlimit_info), | ||
568 | #ifdef CONFIG_COMPAT | 594 | #ifdef CONFIG_COMPAT |
569 | .compatsize = sizeof(struct compat_ipt_hashlimit_info), | 595 | .compatsize = sizeof(struct compat_xt_hashlimit_info), |
570 | .compat_from_user = compat_from_user, | 596 | .compat_from_user = compat_from_user, |
571 | .compat_to_user = compat_to_user, | 597 | .compat_to_user = compat_to_user, |
572 | #endif | 598 | #endif |
573 | .checkentry = hashlimit_checkentry, | 599 | .checkentry = hashlimit_checkentry, |
574 | .destroy = hashlimit_destroy, | 600 | .destroy = hashlimit_destroy, |
575 | .me = THIS_MODULE | 601 | .me = THIS_MODULE |
602 | }, | ||
576 | }; | 603 | }; |
577 | 604 | ||
578 | /* PROC stuff */ | 605 | /* PROC stuff */ |
579 | |||
580 | static void *dl_seq_start(struct seq_file *s, loff_t *pos) | 606 | static void *dl_seq_start(struct seq_file *s, loff_t *pos) |
581 | { | 607 | { |
582 | struct proc_dir_entry *pde = s->private; | 608 | struct proc_dir_entry *pde = s->private; |
583 | struct ipt_hashlimit_htable *htable = pde->data; | 609 | struct xt_hashlimit_htable *htable = pde->data; |
584 | unsigned int *bucket; | 610 | unsigned int *bucket; |
585 | 611 | ||
586 | spin_lock_bh(&htable->lock); | 612 | spin_lock_bh(&htable->lock); |
@@ -598,7 +624,7 @@ static void *dl_seq_start(struct seq_file *s, loff_t *pos) | |||
598 | static void *dl_seq_next(struct seq_file *s, void *v, loff_t *pos) | 624 | static void *dl_seq_next(struct seq_file *s, void *v, loff_t *pos) |
599 | { | 625 | { |
600 | struct proc_dir_entry *pde = s->private; | 626 | struct proc_dir_entry *pde = s->private; |
601 | struct ipt_hashlimit_htable *htable = pde->data; | 627 | struct xt_hashlimit_htable *htable = pde->data; |
602 | unsigned int *bucket = (unsigned int *)v; | 628 | unsigned int *bucket = (unsigned int *)v; |
603 | 629 | ||
604 | *pos = ++(*bucket); | 630 | *pos = ++(*bucket); |
@@ -612,43 +638,59 @@ static void *dl_seq_next(struct seq_file *s, void *v, loff_t *pos) | |||
612 | static void dl_seq_stop(struct seq_file *s, void *v) | 638 | static void dl_seq_stop(struct seq_file *s, void *v) |
613 | { | 639 | { |
614 | struct proc_dir_entry *pde = s->private; | 640 | struct proc_dir_entry *pde = s->private; |
615 | struct ipt_hashlimit_htable *htable = pde->data; | 641 | struct xt_hashlimit_htable *htable = pde->data; |
616 | unsigned int *bucket = (unsigned int *)v; | 642 | unsigned int *bucket = (unsigned int *)v; |
617 | 643 | ||
618 | kfree(bucket); | 644 | kfree(bucket); |
619 | |||
620 | spin_unlock_bh(&htable->lock); | 645 | spin_unlock_bh(&htable->lock); |
621 | } | 646 | } |
622 | 647 | ||
623 | static inline int dl_seq_real_show(struct dsthash_ent *ent, struct seq_file *s) | 648 | static int dl_seq_real_show(struct dsthash_ent *ent, int family, |
649 | struct seq_file *s) | ||
624 | { | 650 | { |
625 | /* recalculate to show accurate numbers */ | 651 | /* recalculate to show accurate numbers */ |
626 | rateinfo_recalc(ent, jiffies); | 652 | rateinfo_recalc(ent, jiffies); |
627 | 653 | ||
628 | return seq_printf(s, "%ld %u.%u.%u.%u:%u->%u.%u.%u.%u:%u %u %u %u\n", | 654 | switch (family) { |
629 | (long)(ent->expires - jiffies)/HZ, | 655 | case AF_INET: |
630 | NIPQUAD(ent->dst.src_ip), ntohs(ent->dst.src_port), | 656 | return seq_printf(s, "%ld %u.%u.%u.%u:%u->" |
631 | NIPQUAD(ent->dst.dst_ip), ntohs(ent->dst.dst_port), | 657 | "%u.%u.%u.%u:%u %u %u %u\n", |
632 | ent->rateinfo.credit, ent->rateinfo.credit_cap, | 658 | (long)(ent->expires - jiffies)/HZ, |
633 | ent->rateinfo.cost); | 659 | NIPQUAD(ent->dst.addr.ip.src), |
660 | ntohs(ent->dst.src_port), | ||
661 | NIPQUAD(ent->dst.addr.ip.dst), | ||
662 | ntohs(ent->dst.dst_port), | ||
663 | ent->rateinfo.credit, ent->rateinfo.credit_cap, | ||
664 | ent->rateinfo.cost); | ||
665 | case AF_INET6: | ||
666 | return seq_printf(s, "%ld " NIP6_FMT ":%u->" | ||
667 | NIP6_FMT ":%u %u %u %u\n", | ||
668 | (long)(ent->expires - jiffies)/HZ, | ||
669 | NIP6(*(struct in6_addr *)&ent->dst.addr.ip6.src), | ||
670 | ntohs(ent->dst.src_port), | ||
671 | NIP6(*(struct in6_addr *)&ent->dst.addr.ip6.dst), | ||
672 | ntohs(ent->dst.dst_port), | ||
673 | ent->rateinfo.credit, ent->rateinfo.credit_cap, | ||
674 | ent->rateinfo.cost); | ||
675 | default: | ||
676 | BUG(); | ||
677 | return 0; | ||
678 | } | ||
634 | } | 679 | } |
635 | 680 | ||
636 | static int dl_seq_show(struct seq_file *s, void *v) | 681 | static int dl_seq_show(struct seq_file *s, void *v) |
637 | { | 682 | { |
638 | struct proc_dir_entry *pde = s->private; | 683 | struct proc_dir_entry *pde = s->private; |
639 | struct ipt_hashlimit_htable *htable = pde->data; | 684 | struct xt_hashlimit_htable *htable = pde->data; |
640 | unsigned int *bucket = (unsigned int *)v; | 685 | unsigned int *bucket = (unsigned int *)v; |
641 | struct dsthash_ent *ent; | 686 | struct dsthash_ent *ent; |
642 | struct hlist_node *pos; | 687 | struct hlist_node *pos; |
643 | 688 | ||
644 | if (!hlist_empty(&htable->hash[*bucket])) | 689 | if (!hlist_empty(&htable->hash[*bucket])) { |
645 | hlist_for_each_entry(ent, pos, &htable->hash[*bucket], node) { | 690 | hlist_for_each_entry(ent, pos, &htable->hash[*bucket], node) |
646 | if (dl_seq_real_show(ent, s)) { | 691 | if (dl_seq_real_show(ent, htable->family, s)) |
647 | /* buffer was filled and unable to print that tuple */ | ||
648 | return 1; | 692 | return 1; |
649 | } | 693 | } |
650 | } | ||
651 | |||
652 | return 0; | 694 | return 0; |
653 | } | 695 | } |
654 | 696 | ||
@@ -678,56 +720,53 @@ static struct file_operations dl_file_ops = { | |||
678 | .release = seq_release | 720 | .release = seq_release |
679 | }; | 721 | }; |
680 | 722 | ||
681 | static int init_or_fini(int fini) | 723 | static int __init xt_hashlimit_init(void) |
682 | { | 724 | { |
683 | int ret = 0; | 725 | int err; |
684 | |||
685 | if (fini) | ||
686 | goto cleanup; | ||
687 | 726 | ||
688 | if (ipt_register_match(&ipt_hashlimit)) { | 727 | err = xt_register_matches(xt_hashlimit, ARRAY_SIZE(xt_hashlimit)); |
689 | ret = -EINVAL; | 728 | if (err < 0) |
690 | goto cleanup_nothing; | 729 | goto err1; |
691 | } | ||
692 | 730 | ||
693 | hashlimit_cachep = kmem_cache_create("ipt_hashlimit", | 731 | err = -ENOMEM; |
694 | sizeof(struct dsthash_ent), 0, | 732 | hashlimit_cachep = kmem_cache_create("xt_hashlimit", |
695 | 0, NULL, NULL); | 733 | sizeof(struct dsthash_ent), 0, 0, |
734 | NULL, NULL); | ||
696 | if (!hashlimit_cachep) { | 735 | if (!hashlimit_cachep) { |
697 | printk(KERN_ERR "Unable to create ipt_hashlimit slab cache\n"); | 736 | printk(KERN_ERR "xt_hashlimit: unable to create slab cache\n"); |
698 | ret = -ENOMEM; | 737 | goto err2; |
699 | goto cleanup_unreg_match; | ||
700 | } | 738 | } |
701 | 739 | hashlimit_procdir4 = proc_mkdir("ipt_hashlimit", proc_net); | |
702 | hashlimit_procdir = proc_mkdir("ipt_hashlimit", proc_net); | 740 | if (!hashlimit_procdir4) { |
703 | if (!hashlimit_procdir) { | 741 | printk(KERN_ERR "xt_hashlimit: unable to create proc dir " |
704 | printk(KERN_ERR "Unable to create proc dir entry\n"); | 742 | "entry\n"); |
705 | ret = -ENOMEM; | 743 | goto err3; |
706 | goto cleanup_free_slab; | ||
707 | } | 744 | } |
708 | 745 | hashlimit_procdir6 = proc_mkdir("ip6t_hashlimit", proc_net); | |
709 | return ret; | 746 | if (!hashlimit_procdir6) { |
710 | 747 | printk(KERN_ERR "xt_hashlimit: tnable to create proc dir " | |
711 | cleanup: | 748 | "entry\n"); |
749 | goto err4; | ||
750 | } | ||
751 | return 0; | ||
752 | err4: | ||
712 | remove_proc_entry("ipt_hashlimit", proc_net); | 753 | remove_proc_entry("ipt_hashlimit", proc_net); |
713 | cleanup_free_slab: | 754 | err3: |
714 | kmem_cache_destroy(hashlimit_cachep); | 755 | kmem_cache_destroy(hashlimit_cachep); |
715 | cleanup_unreg_match: | 756 | err2: |
716 | ipt_unregister_match(&ipt_hashlimit); | 757 | xt_unregister_matches(xt_hashlimit, ARRAY_SIZE(xt_hashlimit)); |
717 | cleanup_nothing: | 758 | err1: |
718 | return ret; | 759 | return err; |
719 | |||
720 | } | ||
721 | 760 | ||
722 | static int __init ipt_hashlimit_init(void) | ||
723 | { | ||
724 | return init_or_fini(0); | ||
725 | } | 761 | } |
726 | 762 | ||
727 | static void __exit ipt_hashlimit_fini(void) | 763 | static void __exit xt_hashlimit_fini(void) |
728 | { | 764 | { |
729 | init_or_fini(1); | 765 | remove_proc_entry("ipt_hashlimit", proc_net); |
766 | remove_proc_entry("ip6t_hashlimit", proc_net); | ||
767 | kmem_cache_destroy(hashlimit_cachep); | ||
768 | xt_unregister_matches(xt_hashlimit, ARRAY_SIZE(xt_hashlimit)); | ||
730 | } | 769 | } |
731 | 770 | ||
732 | module_init(ipt_hashlimit_init); | 771 | module_init(xt_hashlimit_init); |
733 | module_exit(ipt_hashlimit_fini); | 772 | module_exit(xt_hashlimit_fini); |