diff options
author | JP Abgrall | 2011-07-12 14:02:59 -0500 |
---|---|---|
committer | Arve Hjønnevåg | 2013-02-19 19:53:34 -0600 |
commit | 955caa36b865ded4754a7107ee2d627e38472fb2 (patch) | |
tree | 9f637458ebe3f384e5d908e6c357a9c5d181d356 /net | |
parent | f6cd614292d5e6f2c4b7b3747c25b189955de778 (diff) | |
download | kernel-common-955caa36b865ded4754a7107ee2d627e38472fb2.tar.gz kernel-common-955caa36b865ded4754a7107ee2d627e38472fb2.tar.xz kernel-common-955caa36b865ded4754a7107ee2d627e38472fb2.zip |
netfilter: fixup the quota2, and enable.
The xt_quota2 came from
http://sourceforge.net/projects/xtables-addons/develop
It needed tweaking for it to compile within the kernel tree.
Fixed kmalloc() and create_proc_entry() invocations within
a non-interruptible context.
Removed useless copying of current quota back to the iptable's
struct matchinfo:
- those are per CPU: they will change randomly based on which
cpu gets to update the value.
- they prevent matching a rule: e.g.
-A chain -m quota2 --name q1 --quota 123
can't be followed by
-D chain -m quota2 --name q1 --quota 123
as the 123 will be compared to the struct matchinfo's quota member.
Use the NETLINK NETLINK_NFLOG family to log a single message
when the quota limit is reached.
It uses the same packet type as ipt_ULOG, but
- never copies skb data,
- uses 112 as the event number (ULOG's +1)
It doesn't log if the module param "event_num" is 0.
Change-Id: I021d3b743db3b22158cc49acb5c94d905b501492
Signed-off-by: JP Abgrall <jpa@google.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/Kconfig | 24 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/xt_quota2.c | 154 |
3 files changed, 156 insertions, 23 deletions
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index a3da13036d1..bf5c7c9efb8 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -1128,6 +1128,30 @@ config NETFILTER_XT_MATCH_QUOTA | |||
1128 | If you want to compile it as a module, say M here and read | 1128 | If you want to compile it as a module, say M here and read |
1129 | <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. | 1129 | <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. |
1130 | 1130 | ||
1131 | config NETFILTER_XT_MATCH_QUOTA2 | ||
1132 | tristate '"quota2" match support' | ||
1133 | depends on NETFILTER_ADVANCED | ||
1134 | help | ||
1135 | This option adds a `quota2' match, which allows to match on a | ||
1136 | byte counter correctly and not per CPU. | ||
1137 | It allows naming the quotas. | ||
1138 | This is based on http://xtables-addons.git.sourceforge.net | ||
1139 | |||
1140 | If you want to compile it as a module, say M here and read | ||
1141 | <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. | ||
1142 | |||
1143 | config NETFILTER_XT_MATCH_QUOTA2_LOG | ||
1144 | bool '"quota2" Netfilter LOG support' | ||
1145 | depends on NETFILTER_XT_MATCH_QUOTA2 | ||
1146 | depends on IP_NF_TARGET_ULOG=n # not yes, not module, just no | ||
1147 | default n | ||
1148 | help | ||
1149 | This option allows `quota2' to log ONCE when a quota limit | ||
1150 | is passed. It logs via NETLINK using the NETLINK_NFLOG family. | ||
1151 | It logs similarly to how ipt_ULOG would without data. | ||
1152 | |||
1153 | If unsure, say `N'. | ||
1154 | |||
1131 | config NETFILTER_XT_MATCH_RATEEST | 1155 | config NETFILTER_XT_MATCH_RATEEST |
1132 | tristate '"rateest" match support' | 1156 | tristate '"rateest" match support' |
1133 | depends on NETFILTER_ADVANCED | 1157 | depends on NETFILTER_ADVANCED |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 71ed5f82093..115b4fe9044 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -126,6 +126,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o | |||
126 | obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o | 126 | obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o |
127 | obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid_print.o xt_qtaguid.o | 127 | obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid_print.o xt_qtaguid.o |
128 | obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o | 128 | obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o |
129 | obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA2) += xt_quota2.o | ||
129 | obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o | 130 | obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o |
130 | obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o | 131 | obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o |
131 | obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o | 132 | obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o |
diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c index 4857008f1eb..aace7292853 100644 --- a/net/netfilter/xt_quota2.c +++ b/net/netfilter/xt_quota2.c | |||
@@ -12,14 +12,18 @@ | |||
12 | * version 2 of the License, as published by the Free Software Foundation. | 12 | * version 2 of the License, as published by the Free Software Foundation. |
13 | */ | 13 | */ |
14 | #include <linux/list.h> | 14 | #include <linux/list.h> |
15 | #include <linux/module.h> | ||
15 | #include <linux/proc_fs.h> | 16 | #include <linux/proc_fs.h> |
16 | #include <linux/skbuff.h> | 17 | #include <linux/skbuff.h> |
17 | #include <linux/spinlock.h> | 18 | #include <linux/spinlock.h> |
18 | #include <asm/atomic.h> | 19 | #include <asm/atomic.h> |
20 | #include <net/netlink.h> | ||
19 | 21 | ||
20 | #include <linux/netfilter/x_tables.h> | 22 | #include <linux/netfilter/x_tables.h> |
21 | #include "xt_quota2.h" | 23 | #include <linux/netfilter/xt_quota2.h> |
22 | #include "compat_xtables.h" | 24 | #ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG |
25 | #include <linux/netfilter_ipv4/ipt_ULOG.h> | ||
26 | #endif | ||
23 | 27 | ||
24 | /** | 28 | /** |
25 | * @lock: lock to protect quota writers from each other | 29 | * @lock: lock to protect quota writers from each other |
@@ -33,6 +37,16 @@ struct xt_quota_counter { | |||
33 | struct proc_dir_entry *procfs_entry; | 37 | struct proc_dir_entry *procfs_entry; |
34 | }; | 38 | }; |
35 | 39 | ||
40 | #ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG | ||
41 | /* Harald's favorite number +1 :D From ipt_ULOG.C */ | ||
42 | static int qlog_nl_event = 112; | ||
43 | module_param_named(event_num, qlog_nl_event, uint, S_IRUGO | S_IWUSR); | ||
44 | MODULE_PARM_DESC(event_num, | ||
45 | "Event number for NETLINK_NFLOG message. 0 disables log." | ||
46 | "111 is what ipt_ULOG uses."); | ||
47 | static struct sock *nflognl; | ||
48 | #endif | ||
49 | |||
36 | static LIST_HEAD(counter_list); | 50 | static LIST_HEAD(counter_list); |
37 | static DEFINE_SPINLOCK(counter_list_lock); | 51 | static DEFINE_SPINLOCK(counter_list_lock); |
38 | 52 | ||
@@ -44,6 +58,70 @@ module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); | |||
44 | module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); | 58 | module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); |
45 | module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); | 59 | module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); |
46 | 60 | ||
61 | |||
62 | #ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG | ||
63 | static void quota2_log(unsigned int hooknum, | ||
64 | const struct sk_buff *skb, | ||
65 | const struct net_device *in, | ||
66 | const struct net_device *out, | ||
67 | const char *prefix) | ||
68 | { | ||
69 | ulog_packet_msg_t *pm; | ||
70 | struct sk_buff *log_skb; | ||
71 | size_t size; | ||
72 | struct nlmsghdr *nlh; | ||
73 | |||
74 | if (!qlog_nl_event) | ||
75 | return; | ||
76 | |||
77 | size = NLMSG_SPACE(sizeof(*pm)); | ||
78 | size = max(size, (size_t)NLMSG_GOODSIZE); | ||
79 | log_skb = alloc_skb(size, GFP_ATOMIC); | ||
80 | if (!log_skb) { | ||
81 | pr_err("xt_quota2: cannot alloc skb for logging\n"); | ||
82 | return; | ||
83 | } | ||
84 | |||
85 | nlh = nlmsg_put(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event, | ||
86 | sizeof(*pm), 0); | ||
87 | if (!nlh) { | ||
88 | pr_err("xt_quota2: nlmsg_put failed\n"); | ||
89 | kfree_skb(log_skb); | ||
90 | return; | ||
91 | } | ||
92 | pm = nlmsg_data(nlh); | ||
93 | if (skb->tstamp.tv64 == 0) | ||
94 | __net_timestamp((struct sk_buff *)skb); | ||
95 | pm->data_len = 0; | ||
96 | pm->hook = hooknum; | ||
97 | if (prefix != NULL) | ||
98 | strlcpy(pm->prefix, prefix, sizeof(pm->prefix)); | ||
99 | else | ||
100 | *(pm->prefix) = '\0'; | ||
101 | if (in) | ||
102 | strlcpy(pm->indev_name, in->name, sizeof(pm->indev_name)); | ||
103 | else | ||
104 | pm->indev_name[0] = '\0'; | ||
105 | |||
106 | if (out) | ||
107 | strlcpy(pm->outdev_name, out->name, sizeof(pm->outdev_name)); | ||
108 | else | ||
109 | pm->outdev_name[0] = '\0'; | ||
110 | |||
111 | NETLINK_CB(log_skb).dst_group = 1; | ||
112 | pr_debug("throwing 1 packets to netlink group 1\n"); | ||
113 | netlink_broadcast(nflognl, log_skb, 0, 1, GFP_ATOMIC); | ||
114 | } | ||
115 | #else | ||
116 | static void quota2_log(unsigned int hooknum, | ||
117 | const struct sk_buff *skb, | ||
118 | const struct net_device *in, | ||
119 | const struct net_device *out, | ||
120 | const char *prefix) | ||
121 | { | ||
122 | } | ||
123 | #endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */ | ||
124 | |||
47 | static int quota_proc_read(char *page, char **start, off_t offset, | 125 | static int quota_proc_read(char *page, char **start, off_t offset, |
48 | int count, int *eof, void *data) | 126 | int count, int *eof, void *data) |
49 | { | 127 | { |
@@ -91,7 +169,7 @@ q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) | |||
91 | if (!anon) { | 169 | if (!anon) { |
92 | INIT_LIST_HEAD(&e->list); | 170 | INIT_LIST_HEAD(&e->list); |
93 | atomic_set(&e->ref, 1); | 171 | atomic_set(&e->ref, 1); |
94 | strncpy(e->name, q->name, sizeof(e->name)); | 172 | strlcpy(e->name, q->name, sizeof(e->name)); |
95 | } | 173 | } |
96 | return e; | 174 | return e; |
97 | } | 175 | } |
@@ -104,42 +182,56 @@ static struct xt_quota_counter * | |||
104 | q2_get_counter(const struct xt_quota_mtinfo2 *q) | 182 | q2_get_counter(const struct xt_quota_mtinfo2 *q) |
105 | { | 183 | { |
106 | struct proc_dir_entry *p; | 184 | struct proc_dir_entry *p; |
107 | struct xt_quota_counter *e; | 185 | struct xt_quota_counter *e = NULL; |
186 | struct xt_quota_counter *new_e; | ||
108 | 187 | ||
109 | if (*q->name == '\0') | 188 | if (*q->name == '\0') |
110 | return q2_new_counter(q, true); | 189 | return q2_new_counter(q, true); |
111 | 190 | ||
191 | /* No need to hold a lock while getting a new counter */ | ||
192 | new_e = q2_new_counter(q, false); | ||
193 | if (new_e == NULL) | ||
194 | goto out; | ||
195 | |||
112 | spin_lock_bh(&counter_list_lock); | 196 | spin_lock_bh(&counter_list_lock); |
113 | list_for_each_entry(e, &counter_list, list) | 197 | list_for_each_entry(e, &counter_list, list) |
114 | if (strcmp(e->name, q->name) == 0) { | 198 | if (strcmp(e->name, q->name) == 0) { |
115 | atomic_inc(&e->ref); | 199 | atomic_inc(&e->ref); |
116 | spin_unlock_bh(&counter_list_lock); | 200 | spin_unlock_bh(&counter_list_lock); |
201 | kfree(new_e); | ||
202 | pr_debug("xt_quota2: old counter name=%s", e->name); | ||
117 | return e; | 203 | return e; |
118 | } | 204 | } |
205 | e = new_e; | ||
206 | pr_debug("xt_quota2: new_counter name=%s", e->name); | ||
207 | list_add_tail(&e->list, &counter_list); | ||
208 | /* The entry having a refcount of 1 is not directly destructible. | ||
209 | * This func has not yet returned the new entry, thus iptables | ||
210 | * has not references for destroying this entry. | ||
211 | * For another rule to try to destroy it, it would 1st need for this | ||
212 | * func* to be re-invoked, acquire a new ref for the same named quota. | ||
213 | * Nobody will access the e->procfs_entry either. | ||
214 | * So release the lock. */ | ||
215 | spin_unlock_bh(&counter_list_lock); | ||
119 | 216 | ||
120 | e = q2_new_counter(q, false); | 217 | /* create_proc_entry() is not spin_lock happy */ |
121 | if (e == NULL) | ||
122 | goto out; | ||
123 | |||
124 | p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, | 218 | p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, |
125 | proc_xt_quota); | 219 | proc_xt_quota); |
126 | if (p == NULL || IS_ERR(p)) | ||
127 | goto out; | ||
128 | 220 | ||
129 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) | 221 | if (IS_ERR_OR_NULL(p)) { |
130 | p->owner = THIS_MODULE; | 222 | spin_lock_bh(&counter_list_lock); |
131 | #endif | 223 | list_del(&e->list); |
224 | spin_unlock_bh(&counter_list_lock); | ||
225 | goto out; | ||
226 | } | ||
132 | p->data = e; | 227 | p->data = e; |
133 | p->read_proc = quota_proc_read; | 228 | p->read_proc = quota_proc_read; |
134 | p->write_proc = quota_proc_write; | 229 | p->write_proc = quota_proc_write; |
135 | p->uid = quota_list_uid; | 230 | p->uid = quota_list_uid; |
136 | p->gid = quota_list_gid; | 231 | p->gid = quota_list_gid; |
137 | list_add_tail(&e->list, &counter_list); | ||
138 | spin_unlock_bh(&counter_list_lock); | ||
139 | return e; | 232 | return e; |
140 | 233 | ||
141 | out: | 234 | out: |
142 | spin_unlock_bh(&counter_list_lock); | ||
143 | kfree(e); | 235 | kfree(e); |
144 | return NULL; | 236 | return NULL; |
145 | } | 237 | } |
@@ -148,6 +240,8 @@ static int quota_mt2_check(const struct xt_mtchk_param *par) | |||
148 | { | 240 | { |
149 | struct xt_quota_mtinfo2 *q = par->matchinfo; | 241 | struct xt_quota_mtinfo2 *q = par->matchinfo; |
150 | 242 | ||
243 | pr_debug("xt_quota2: check() flags=0x%04x", q->flags); | ||
244 | |||
151 | if (q->flags & ~XT_QUOTA_MASK) | 245 | if (q->flags & ~XT_QUOTA_MASK) |
152 | return -EINVAL; | 246 | return -EINVAL; |
153 | 247 | ||
@@ -203,7 +297,6 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) | |||
203 | */ | 297 | */ |
204 | if (!(q->flags & XT_QUOTA_NO_CHANGE)) { | 298 | if (!(q->flags & XT_QUOTA_NO_CHANGE)) { |
205 | e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; | 299 | e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; |
206 | q->quota = e->quota; | ||
207 | } | 300 | } |
208 | ret = true; | 301 | ret = true; |
209 | } else { | 302 | } else { |
@@ -212,10 +305,17 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) | |||
212 | e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; | 305 | e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; |
213 | ret = !ret; | 306 | ret = !ret; |
214 | } else { | 307 | } else { |
308 | /* We are transitioning, log that fact. */ | ||
309 | if (e->quota) { | ||
310 | quota2_log(par->hooknum, | ||
311 | skb, | ||
312 | par->in, | ||
313 | par->out, | ||
314 | q->name); | ||
315 | } | ||
215 | /* we do not allow even small packets from now on */ | 316 | /* we do not allow even small packets from now on */ |
216 | e->quota = 0; | 317 | e->quota = 0; |
217 | } | 318 | } |
218 | q->quota = e->quota; | ||
219 | } | 319 | } |
220 | spin_unlock_bh(&e->lock); | 320 | spin_unlock_bh(&e->lock); |
221 | return ret; | 321 | return ret; |
@@ -228,7 +328,7 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { | |||
228 | .family = NFPROTO_IPV4, | 328 | .family = NFPROTO_IPV4, |
229 | .checkentry = quota_mt2_check, | 329 | .checkentry = quota_mt2_check, |
230 | .match = quota_mt2, | 330 | .match = quota_mt2, |
231 | .destroy = quota_mt2_destroy, | 331 | .destroy = quota_mt2_destroy, |
232 | .matchsize = sizeof(struct xt_quota_mtinfo2), | 332 | .matchsize = sizeof(struct xt_quota_mtinfo2), |
233 | .me = THIS_MODULE, | 333 | .me = THIS_MODULE, |
234 | }, | 334 | }, |
@@ -238,7 +338,7 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { | |||
238 | .family = NFPROTO_IPV6, | 338 | .family = NFPROTO_IPV6, |
239 | .checkentry = quota_mt2_check, | 339 | .checkentry = quota_mt2_check, |
240 | .match = quota_mt2, | 340 | .match = quota_mt2, |
241 | .destroy = quota_mt2_destroy, | 341 | .destroy = quota_mt2_destroy, |
242 | .matchsize = sizeof(struct xt_quota_mtinfo2), | 342 | .matchsize = sizeof(struct xt_quota_mtinfo2), |
243 | .me = THIS_MODULE, | 343 | .me = THIS_MODULE, |
244 | }, | 344 | }, |
@@ -247,21 +347,29 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { | |||
247 | static int __init quota_mt2_init(void) | 347 | static int __init quota_mt2_init(void) |
248 | { | 348 | { |
249 | int ret; | 349 | int ret; |
350 | pr_debug("xt_quota2: init()"); | ||
351 | |||
352 | #ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG | ||
353 | nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, NULL); | ||
354 | if (!nflognl) | ||
355 | return -ENOMEM; | ||
356 | #endif | ||
250 | 357 | ||
251 | proc_xt_quota = proc_mkdir("xt_quota", init_net__proc_net); | 358 | proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net); |
252 | if (proc_xt_quota == NULL) | 359 | if (proc_xt_quota == NULL) |
253 | return -EACCES; | 360 | return -EACCES; |
254 | 361 | ||
255 | ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); | 362 | ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); |
256 | if (ret < 0) | 363 | if (ret < 0) |
257 | remove_proc_entry("xt_quota", init_net__proc_net); | 364 | remove_proc_entry("xt_quota", init_net.proc_net); |
365 | pr_debug("xt_quota2: init() %d", ret); | ||
258 | return ret; | 366 | return ret; |
259 | } | 367 | } |
260 | 368 | ||
261 | static void __exit quota_mt2_exit(void) | 369 | static void __exit quota_mt2_exit(void) |
262 | { | 370 | { |
263 | xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); | 371 | xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); |
264 | remove_proc_entry("xt_quota", init_net__proc_net); | 372 | remove_proc_entry("xt_quota", init_net.proc_net); |
265 | } | 373 | } |
266 | 374 | ||
267 | module_init(quota_mt2_init); | 375 | module_init(quota_mt2_init); |