blob: 454ce10c8cdc806e0d1393e21622af896c5c730e [file] [log] [blame]
JP Abgrall511b7e32011-06-21 11:14:49 -07001/*
2 * xt_quota2 - enhanced xt_quota that can count upwards and in packets
3 * as a minimal accounting match.
4 * by Jan Engelhardt <jengelh@medozas.de>, 2008
5 *
6 * Originally based on xt_quota.c:
7 * netfilter module to enforce network quotas
8 * Sam Johnston <samj@samj.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License; either
12 * version 2 of the License, as published by the Free Software Foundation.
13 */
14#include <linux/list.h>
15#include <linux/proc_fs.h>
16#include <linux/skbuff.h>
17#include <linux/spinlock.h>
18#include <asm/atomic.h>
19
20#include <linux/netfilter/x_tables.h>
JP Abgrallb4294d62011-07-12 12:02:59 -070021#include <linux/netfilter/xt_quota2.h>
JP Abgrall511b7e32011-06-21 11:14:49 -070022
23/**
24 * @lock: lock to protect quota writers from each other
25 */
26struct xt_quota_counter {
27 u_int64_t quota;
28 spinlock_t lock;
29 struct list_head list;
30 atomic_t ref;
31 char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)];
32 struct proc_dir_entry *procfs_entry;
33};
34
35static LIST_HEAD(counter_list);
36static DEFINE_SPINLOCK(counter_list_lock);
37
38static struct proc_dir_entry *proc_xt_quota;
39static unsigned int quota_list_perms = S_IRUGO | S_IWUSR;
40static unsigned int quota_list_uid = 0;
41static unsigned int quota_list_gid = 0;
42module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR);
43module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR);
44module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR);
45
46static int quota_proc_read(char *page, char **start, off_t offset,
47 int count, int *eof, void *data)
48{
49 struct xt_quota_counter *e = data;
50 int ret;
51
52 spin_lock_bh(&e->lock);
53 ret = snprintf(page, PAGE_SIZE, "%llu\n", e->quota);
54 spin_unlock_bh(&e->lock);
55 return ret;
56}
57
58static int quota_proc_write(struct file *file, const char __user *input,
59 unsigned long size, void *data)
60{
61 struct xt_quota_counter *e = data;
62 char buf[sizeof("18446744073709551616")];
63
64 if (size > sizeof(buf))
65 size = sizeof(buf);
66 if (copy_from_user(buf, input, size) != 0)
67 return -EFAULT;
68 buf[sizeof(buf)-1] = '\0';
69
70 spin_lock_bh(&e->lock);
71 e->quota = simple_strtoull(buf, NULL, 0);
72 spin_unlock_bh(&e->lock);
73 return size;
74}
75
76static struct xt_quota_counter *
77q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon)
78{
79 struct xt_quota_counter *e;
80 unsigned int size;
81
82 /* Do not need all the procfs things for anonymous counters. */
83 size = anon ? offsetof(typeof(*e), list) : sizeof(*e);
84 e = kmalloc(size, GFP_KERNEL);
85 if (e == NULL)
86 return NULL;
87
88 e->quota = q->quota;
89 spin_lock_init(&e->lock);
90 if (!anon) {
91 INIT_LIST_HEAD(&e->list);
92 atomic_set(&e->ref, 1);
JP Abgrallb4294d62011-07-12 12:02:59 -070093 strlcpy(e->name, q->name, sizeof(e->name));
JP Abgrall511b7e32011-06-21 11:14:49 -070094 }
95 return e;
96}
97
98/**
99 * q2_get_counter - get ref to counter or create new
100 * @name: name of counter
101 */
102static struct xt_quota_counter *
103q2_get_counter(const struct xt_quota_mtinfo2 *q)
104{
105 struct proc_dir_entry *p;
JP Abgrallb4294d62011-07-12 12:02:59 -0700106 struct xt_quota_counter *e = NULL;
107 struct xt_quota_counter *new_e;
JP Abgrall511b7e32011-06-21 11:14:49 -0700108
109 if (*q->name == '\0')
110 return q2_new_counter(q, true);
111
JP Abgrallb4294d62011-07-12 12:02:59 -0700112 /* No need to hold a lock while getting a new counter */
113 new_e = q2_new_counter(q, false);
114 if (new_e == NULL)
115 goto out;
116
JP Abgrall511b7e32011-06-21 11:14:49 -0700117 spin_lock_bh(&counter_list_lock);
118 list_for_each_entry(e, &counter_list, list)
119 if (strcmp(e->name, q->name) == 0) {
120 atomic_inc(&e->ref);
121 spin_unlock_bh(&counter_list_lock);
JP Abgrallb4294d62011-07-12 12:02:59 -0700122 kfree(new_e);
123 pr_debug("xt_quota2: old counter name=%s", e->name);
JP Abgrall511b7e32011-06-21 11:14:49 -0700124 return e;
125 }
JP Abgrallb4294d62011-07-12 12:02:59 -0700126 e = new_e;
127 pr_debug("xt_quota2: new_counter name=%s", e->name);
128 list_add_tail(&e->list, &counter_list);
129 /* The entry having a refcount of 1 is not directly destructible.
130 * This func has not yet returned the new entry, thus iptables
131 * has not references for destroying this entry.
132 * For another rule to try to destroy it, it would 1st need for this
133 * func* to be re-invoked, acquire a new ref for the same named quota.
134 * Nobody will access the e->procfs_entry either.
135 * So release the lock. */
136 spin_unlock_bh(&counter_list_lock);
JP Abgrall511b7e32011-06-21 11:14:49 -0700137
JP Abgrallb4294d62011-07-12 12:02:59 -0700138 /* create_proc_entry() is not spin_lock happy */
JP Abgrall511b7e32011-06-21 11:14:49 -0700139 p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms,
140 proc_xt_quota);
JP Abgrall511b7e32011-06-21 11:14:49 -0700141
JP Abgrallb4294d62011-07-12 12:02:59 -0700142 if (IS_ERR_OR_NULL(p)) {
143 spin_lock_bh(&counter_list_lock);
144 list_del(&e->list);
145 spin_unlock_bh(&counter_list_lock);
146 goto out;
147 }
JP Abgrall511b7e32011-06-21 11:14:49 -0700148 p->data = e;
149 p->read_proc = quota_proc_read;
150 p->write_proc = quota_proc_write;
151 p->uid = quota_list_uid;
152 p->gid = quota_list_gid;
JP Abgrall511b7e32011-06-21 11:14:49 -0700153 return e;
154
155 out:
JP Abgrall511b7e32011-06-21 11:14:49 -0700156 kfree(e);
157 return NULL;
158}
159
160static int quota_mt2_check(const struct xt_mtchk_param *par)
161{
162 struct xt_quota_mtinfo2 *q = par->matchinfo;
163
JP Abgrallb4294d62011-07-12 12:02:59 -0700164 pr_debug("xt_quota2: check() flags=0x%04x", q->flags);
165
JP Abgrall511b7e32011-06-21 11:14:49 -0700166 if (q->flags & ~XT_QUOTA_MASK)
167 return -EINVAL;
168
169 q->name[sizeof(q->name)-1] = '\0';
170 if (*q->name == '.' || strchr(q->name, '/') != NULL) {
171 printk(KERN_ERR "xt_quota.3: illegal name\n");
172 return -EINVAL;
173 }
174
175 q->master = q2_get_counter(q);
176 if (q->master == NULL) {
177 printk(KERN_ERR "xt_quota.3: memory alloc failure\n");
178 return -ENOMEM;
179 }
180
181 return 0;
182}
183
184static void quota_mt2_destroy(const struct xt_mtdtor_param *par)
185{
186 struct xt_quota_mtinfo2 *q = par->matchinfo;
187 struct xt_quota_counter *e = q->master;
188
189 if (*q->name == '\0') {
190 kfree(e);
191 return;
192 }
193
194 spin_lock_bh(&counter_list_lock);
195 if (!atomic_dec_and_test(&e->ref)) {
196 spin_unlock_bh(&counter_list_lock);
197 return;
198 }
199
200 list_del(&e->list);
201 remove_proc_entry(e->name, proc_xt_quota);
202 spin_unlock_bh(&counter_list_lock);
203 kfree(e);
204}
205
206static bool
207quota_mt2(const struct sk_buff *skb, struct xt_action_param *par)
208{
209 struct xt_quota_mtinfo2 *q = (void *)par->matchinfo;
210 struct xt_quota_counter *e = q->master;
211 bool ret = q->flags & XT_QUOTA_INVERT;
212
213 spin_lock_bh(&e->lock);
214 if (q->flags & XT_QUOTA_GROW) {
215 /*
216 * While no_change is pointless in "grow" mode, we will
217 * implement it here simply to have a consistent behavior.
218 */
219 if (!(q->flags & XT_QUOTA_NO_CHANGE)) {
220 e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len;
JP Abgrall511b7e32011-06-21 11:14:49 -0700221 }
222 ret = true;
223 } else {
224 if (e->quota >= skb->len) {
225 if (!(q->flags & XT_QUOTA_NO_CHANGE))
226 e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len;
227 ret = !ret;
228 } else {
229 /* we do not allow even small packets from now on */
230 e->quota = 0;
231 }
JP Abgrall511b7e32011-06-21 11:14:49 -0700232 }
233 spin_unlock_bh(&e->lock);
234 return ret;
235}
236
237static struct xt_match quota_mt2_reg[] __read_mostly = {
238 {
239 .name = "quota2",
240 .revision = 3,
241 .family = NFPROTO_IPV4,
242 .checkentry = quota_mt2_check,
243 .match = quota_mt2,
JP Abgrallb4294d62011-07-12 12:02:59 -0700244 .destroy = quota_mt2_destroy,
JP Abgrall511b7e32011-06-21 11:14:49 -0700245 .matchsize = sizeof(struct xt_quota_mtinfo2),
246 .me = THIS_MODULE,
247 },
248 {
249 .name = "quota2",
250 .revision = 3,
251 .family = NFPROTO_IPV6,
252 .checkentry = quota_mt2_check,
253 .match = quota_mt2,
JP Abgrallb4294d62011-07-12 12:02:59 -0700254 .destroy = quota_mt2_destroy,
JP Abgrall511b7e32011-06-21 11:14:49 -0700255 .matchsize = sizeof(struct xt_quota_mtinfo2),
256 .me = THIS_MODULE,
257 },
258};
259
260static int __init quota_mt2_init(void)
261{
262 int ret;
JP Abgrallb4294d62011-07-12 12:02:59 -0700263 pr_debug("xt_quota2: init()");
JP Abgrall511b7e32011-06-21 11:14:49 -0700264
JP Abgrallb4294d62011-07-12 12:02:59 -0700265 proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net);
JP Abgrall511b7e32011-06-21 11:14:49 -0700266 if (proc_xt_quota == NULL)
267 return -EACCES;
268
269 ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg));
270 if (ret < 0)
JP Abgrallb4294d62011-07-12 12:02:59 -0700271 remove_proc_entry("xt_quota", init_net.proc_net);
272 pr_debug("xt_quota2: init() %d", ret);
JP Abgrall511b7e32011-06-21 11:14:49 -0700273 return ret;
274}
275
276static void __exit quota_mt2_exit(void)
277{
278 xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg));
JP Abgrallb4294d62011-07-12 12:02:59 -0700279 remove_proc_entry("xt_quota", init_net.proc_net);
JP Abgrall511b7e32011-06-21 11:14:49 -0700280}
281
282module_init(quota_mt2_init);
283module_exit(quota_mt2_exit);
284MODULE_DESCRIPTION("Xtables: countdown quota match; up counter");
285MODULE_AUTHOR("Sam Johnston <samj@samj.net>");
286MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
287MODULE_LICENSE("GPL");
288MODULE_ALIAS("ipt_quota2");
289MODULE_ALIAS("ip6t_quota2");