blob: 7f9d9981a9e2834ddd07608eb7994a5c1a78d0e2 [file] [log] [blame]
JP Abgralldc5ce6e2011-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>
JP Abgrall3fd82382011-07-12 12:02:59 -070015#include <linux/module.h>
JP Abgralldc5ce6e2011-06-21 11:14:49 -070016#include <linux/proc_fs.h>
17#include <linux/skbuff.h>
18#include <linux/spinlock.h>
Tyler Weare8a34e52016-05-27 15:48:38 -070019#include <linux/workqueue.h>
JP Abgralldc5ce6e2011-06-21 11:14:49 -070020#include <asm/atomic.h>
JP Abgrall3fd82382011-07-12 12:02:59 -070021#include <net/netlink.h>
JP Abgralldc5ce6e2011-06-21 11:14:49 -070022
23#include <linux/netfilter/x_tables.h>
JP Abgrall3fd82382011-07-12 12:02:59 -070024#include <linux/netfilter/xt_quota2.h>
Liping Zhangdd111312016-06-22 16:49:48 +080025
JP Abgrall3fd82382011-07-12 12:02:59 -070026#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
Liping Zhangdd111312016-06-22 16:49:48 +080027/* For compatibility, these definitions are copied from the
28 * deprecated header file <linux/netfilter_ipv4/ipt_ULOG.h> */
29#define ULOG_MAC_LEN 80
30#define ULOG_PREFIX_LEN 32
31
32/* Format of the ULOG packets passed through netlink */
33typedef struct ulog_packet_msg {
34 unsigned long mark;
35 long timestamp_sec;
36 long timestamp_usec;
37 unsigned int hook;
38 char indev_name[IFNAMSIZ];
39 char outdev_name[IFNAMSIZ];
40 size_t data_len;
41 char prefix[ULOG_PREFIX_LEN];
42 unsigned char mac_len;
43 unsigned char mac[ULOG_MAC_LEN];
44 unsigned char payload[0];
45} ulog_packet_msg_t;
JP Abgrall3fd82382011-07-12 12:02:59 -070046#endif
Tyler Weare8a34e52016-05-27 15:48:38 -070047#define QUOTA2_SYSFS_WORK_MAX_SIZE 64
48#define QUOTA2_SYSFS_NUM_ENVP 3
JP Abgralldc5ce6e2011-06-21 11:14:49 -070049
50/**
51 * @lock: lock to protect quota writers from each other
52 */
53struct xt_quota_counter {
54 u_int64_t quota;
55 spinlock_t lock;
56 struct list_head list;
57 atomic_t ref;
58 char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)];
59 struct proc_dir_entry *procfs_entry;
Tyler Weare8a34e52016-05-27 15:48:38 -070060 char last_iface[QUOTA2_SYSFS_WORK_MAX_SIZE];
61 char last_prefix[QUOTA2_SYSFS_WORK_MAX_SIZE];
62 struct work_struct work;
JP Abgralldc5ce6e2011-06-21 11:14:49 -070063};
64
Tyler Weare8a34e52016-05-27 15:48:38 -070065#define to_quota_counter(x) container_of(x, struct xt_quota_counter, work)
66
67static struct class *quota_class;
68static struct device *quota_device;
69static struct kobject *quota_kobj;
JP Abgrall3fd82382011-07-12 12:02:59 -070070
JP Abgralldc5ce6e2011-06-21 11:14:49 -070071static LIST_HEAD(counter_list);
72static DEFINE_SPINLOCK(counter_list_lock);
73
74static struct proc_dir_entry *proc_xt_quota;
75static unsigned int quota_list_perms = S_IRUGO | S_IWUSR;
John Stultzbc1bb862014-03-28 12:19:27 -070076static kuid_t quota_list_uid = KUIDT_INIT(0);
77static kgid_t quota_list_gid = KGIDT_INIT(0);
JP Abgralldc5ce6e2011-06-21 11:14:49 -070078module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR);
JP Abgrall3fd82382011-07-12 12:02:59 -070079
Tyler Weare8a34e52016-05-27 15:48:38 -070080static void quota2_work(struct work_struct *work)
81{
82 char alert_msg[QUOTA2_SYSFS_WORK_MAX_SIZE];
83 char iface_name[QUOTA2_SYSFS_WORK_MAX_SIZE];
84 char *envp[QUOTA2_SYSFS_NUM_ENVP] = {alert_msg, iface_name, NULL};
85 struct xt_quota_counter *counter = to_quota_counter(work);
86
87 snprintf(alert_msg, sizeof(alert_msg), "ALERT_NAME=%s", counter->name);
88 snprintf(iface_name, sizeof(iface_name), "INTERFACE=%s",
89 counter->last_iface);
90
91 kobject_uevent_env(quota_kobj, KOBJ_CHANGE, envp);
92}
93
94static void quota2_log(const struct net_device *in,
JP Abgrall3fd82382011-07-12 12:02:59 -070095 const struct net_device *out,
Tyler Weare8a34e52016-05-27 15:48:38 -070096 struct xt_quota_counter *q,
JP Abgrall3fd82382011-07-12 12:02:59 -070097 const char *prefix)
98{
Tyler Weare8a34e52016-05-27 15:48:38 -070099 if (!prefix)
JP Abgrall3fd82382011-07-12 12:02:59 -0700100 return;
101
Tyler Weare8a34e52016-05-27 15:48:38 -0700102 strlcpy(q->last_prefix, prefix, QUOTA2_SYSFS_WORK_MAX_SIZE);
JP Abgrall3fd82382011-07-12 12:02:59 -0700103
JP Abgrall3fd82382011-07-12 12:02:59 -0700104 if (in)
Tyler Weare8a34e52016-05-27 15:48:38 -0700105 strlcpy(q->last_iface, in->name, QUOTA2_SYSFS_WORK_MAX_SIZE);
106 else if (out)
107 strlcpy(q->last_iface, out->name, QUOTA2_SYSFS_WORK_MAX_SIZE);
JP Abgrall3fd82382011-07-12 12:02:59 -0700108 else
Tyler Weare8a34e52016-05-27 15:48:38 -0700109 strlcpy(q->last_iface, "UNKNOWN", QUOTA2_SYSFS_WORK_MAX_SIZE);
JP Abgrall3fd82382011-07-12 12:02:59 -0700110
Tyler Weare8a34e52016-05-27 15:48:38 -0700111 schedule_work(&q->work);
JP Abgrall3fd82382011-07-12 12:02:59 -0700112}
JP Abgrall3fd82382011-07-12 12:02:59 -0700113
Greg Hackmann81667c52014-02-24 09:39:46 -0800114static ssize_t quota_proc_read(struct file *file, char __user *buf,
Arve Hjønnevågf1075f72013-05-13 20:42:46 -0700115 size_t size, loff_t *ppos)
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700116{
Arve Hjønnevågf1075f72013-05-13 20:42:46 -0700117 struct xt_quota_counter *e = PDE_DATA(file_inode(file));
118 char tmp[24];
119 size_t tmp_size;
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700120
121 spin_lock_bh(&e->lock);
Arve Hjønnevågf1075f72013-05-13 20:42:46 -0700122 tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", e->quota);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700123 spin_unlock_bh(&e->lock);
Arve Hjønnevågf1075f72013-05-13 20:42:46 -0700124 return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700125}
126
Greg Hackmann81667c52014-02-24 09:39:46 -0800127static ssize_t quota_proc_write(struct file *file, const char __user *input,
Arve Hjønnevågf1075f72013-05-13 20:42:46 -0700128 size_t size, loff_t *ppos)
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700129{
Arve Hjønnevågf1075f72013-05-13 20:42:46 -0700130 struct xt_quota_counter *e = PDE_DATA(file_inode(file));
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700131 char buf[sizeof("18446744073709551616")];
132
133 if (size > sizeof(buf))
134 size = sizeof(buf);
135 if (copy_from_user(buf, input, size) != 0)
136 return -EFAULT;
137 buf[sizeof(buf)-1] = '\0';
Sam Liddicott4fb03602014-01-07 09:21:53 -0800138 if (size < sizeof(buf))
139 buf[size] = '\0';
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700140
141 spin_lock_bh(&e->lock);
142 e->quota = simple_strtoull(buf, NULL, 0);
143 spin_unlock_bh(&e->lock);
144 return size;
145}
146
Arve Hjønnevågf1075f72013-05-13 20:42:46 -0700147static const struct file_operations q2_counter_fops = {
148 .read = quota_proc_read,
149 .write = quota_proc_write,
150 .llseek = default_llseek,
151};
152
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700153static struct xt_quota_counter *
154q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon)
155{
156 struct xt_quota_counter *e;
157 unsigned int size;
158
159 /* Do not need all the procfs things for anonymous counters. */
160 size = anon ? offsetof(typeof(*e), list) : sizeof(*e);
161 e = kmalloc(size, GFP_KERNEL);
162 if (e == NULL)
163 return NULL;
164
165 e->quota = q->quota;
166 spin_lock_init(&e->lock);
167 if (!anon) {
168 INIT_LIST_HEAD(&e->list);
169 atomic_set(&e->ref, 1);
JP Abgrall3fd82382011-07-12 12:02:59 -0700170 strlcpy(e->name, q->name, sizeof(e->name));
Tyler Weare8a34e52016-05-27 15:48:38 -0700171 strlcpy(e->last_prefix, "UNSET", sizeof(e->last_prefix));
172 strlcpy(e->last_iface, "UNSET", sizeof(e->last_iface));
173 INIT_WORK(&e->work, quota2_work);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700174 }
175 return e;
176}
177
178/**
179 * q2_get_counter - get ref to counter or create new
180 * @name: name of counter
181 */
182static struct xt_quota_counter *
183q2_get_counter(const struct xt_quota_mtinfo2 *q)
184{
185 struct proc_dir_entry *p;
JP Abgrall3fd82382011-07-12 12:02:59 -0700186 struct xt_quota_counter *e = NULL;
187 struct xt_quota_counter *new_e;
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700188
189 if (*q->name == '\0')
190 return q2_new_counter(q, true);
191
JP Abgrall3fd82382011-07-12 12:02:59 -0700192 /* No need to hold a lock while getting a new counter */
193 new_e = q2_new_counter(q, false);
194 if (new_e == NULL)
195 goto out;
196
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700197 spin_lock_bh(&counter_list_lock);
198 list_for_each_entry(e, &counter_list, list)
199 if (strcmp(e->name, q->name) == 0) {
200 atomic_inc(&e->ref);
201 spin_unlock_bh(&counter_list_lock);
JP Abgrall3fd82382011-07-12 12:02:59 -0700202 kfree(new_e);
203 pr_debug("xt_quota2: old counter name=%s", e->name);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700204 return e;
205 }
JP Abgrall3fd82382011-07-12 12:02:59 -0700206 e = new_e;
207 pr_debug("xt_quota2: new_counter name=%s", e->name);
208 list_add_tail(&e->list, &counter_list);
209 /* The entry having a refcount of 1 is not directly destructible.
210 * This func has not yet returned the new entry, thus iptables
211 * has not references for destroying this entry.
212 * For another rule to try to destroy it, it would 1st need for this
213 * func* to be re-invoked, acquire a new ref for the same named quota.
214 * Nobody will access the e->procfs_entry either.
215 * So release the lock. */
216 spin_unlock_bh(&counter_list_lock);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700217
JP Abgrall3fd82382011-07-12 12:02:59 -0700218 /* create_proc_entry() is not spin_lock happy */
Arve Hjønnevågf1075f72013-05-13 20:42:46 -0700219 p = e->procfs_entry = proc_create_data(e->name, quota_list_perms,
220 proc_xt_quota, &q2_counter_fops, e);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700221
JP Abgrall3fd82382011-07-12 12:02:59 -0700222 if (IS_ERR_OR_NULL(p)) {
223 spin_lock_bh(&counter_list_lock);
224 list_del(&e->list);
225 spin_unlock_bh(&counter_list_lock);
226 goto out;
227 }
Arve Hjønnevågf1075f72013-05-13 20:42:46 -0700228 proc_set_user(p, quota_list_uid, quota_list_gid);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700229 return e;
230
231 out:
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700232 kfree(e);
233 return NULL;
234}
235
236static int quota_mt2_check(const struct xt_mtchk_param *par)
237{
238 struct xt_quota_mtinfo2 *q = par->matchinfo;
239
JP Abgrall3fd82382011-07-12 12:02:59 -0700240 pr_debug("xt_quota2: check() flags=0x%04x", q->flags);
241
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700242 if (q->flags & ~XT_QUOTA_MASK)
243 return -EINVAL;
244
245 q->name[sizeof(q->name)-1] = '\0';
246 if (*q->name == '.' || strchr(q->name, '/') != NULL) {
247 printk(KERN_ERR "xt_quota.3: illegal name\n");
248 return -EINVAL;
249 }
250
251 q->master = q2_get_counter(q);
252 if (q->master == NULL) {
253 printk(KERN_ERR "xt_quota.3: memory alloc failure\n");
254 return -ENOMEM;
255 }
256
257 return 0;
258}
259
260static void quota_mt2_destroy(const struct xt_mtdtor_param *par)
261{
262 struct xt_quota_mtinfo2 *q = par->matchinfo;
263 struct xt_quota_counter *e = q->master;
264
265 if (*q->name == '\0') {
266 kfree(e);
267 return;
268 }
269
270 spin_lock_bh(&counter_list_lock);
271 if (!atomic_dec_and_test(&e->ref)) {
272 spin_unlock_bh(&counter_list_lock);
273 return;
274 }
275
276 list_del(&e->list);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700277 spin_unlock_bh(&counter_list_lock);
DongJoo Kimf6b56092019-10-21 11:48:00 +0900278 remove_proc_entry(e->name, proc_xt_quota);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700279 kfree(e);
280}
281
282static bool
283quota_mt2(const struct sk_buff *skb, struct xt_action_param *par)
284{
285 struct xt_quota_mtinfo2 *q = (void *)par->matchinfo;
286 struct xt_quota_counter *e = q->master;
287 bool ret = q->flags & XT_QUOTA_INVERT;
288
289 spin_lock_bh(&e->lock);
290 if (q->flags & XT_QUOTA_GROW) {
291 /*
292 * While no_change is pointless in "grow" mode, we will
293 * implement it here simply to have a consistent behavior.
294 */
295 if (!(q->flags & XT_QUOTA_NO_CHANGE)) {
296 e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len;
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700297 }
298 ret = true;
299 } else {
300 if (e->quota >= skb->len) {
301 if (!(q->flags & XT_QUOTA_NO_CHANGE))
302 e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len;
303 ret = !ret;
304 } else {
JP Abgrall3fd82382011-07-12 12:02:59 -0700305 /* We are transitioning, log that fact. */
306 if (e->quota) {
Tyler Weare8a34e52016-05-27 15:48:38 -0700307 quota2_log(par->in, par->out, e, q->name);
JP Abgrall3fd82382011-07-12 12:02:59 -0700308 }
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700309 /* we do not allow even small packets from now on */
310 e->quota = 0;
311 }
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700312 }
313 spin_unlock_bh(&e->lock);
314 return ret;
315}
316
317static struct xt_match quota_mt2_reg[] __read_mostly = {
318 {
319 .name = "quota2",
320 .revision = 3,
321 .family = NFPROTO_IPV4,
322 .checkentry = quota_mt2_check,
323 .match = quota_mt2,
JP Abgrall3fd82382011-07-12 12:02:59 -0700324 .destroy = quota_mt2_destroy,
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700325 .matchsize = sizeof(struct xt_quota_mtinfo2),
Todd Kjosd6761bc2021-08-17 14:23:28 -0700326 .usersize = offsetof(struct xt_quota_mtinfo2, master),
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700327 .me = THIS_MODULE,
328 },
329 {
330 .name = "quota2",
331 .revision = 3,
332 .family = NFPROTO_IPV6,
333 .checkentry = quota_mt2_check,
334 .match = quota_mt2,
JP Abgrall3fd82382011-07-12 12:02:59 -0700335 .destroy = quota_mt2_destroy,
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700336 .matchsize = sizeof(struct xt_quota_mtinfo2),
Todd Kjosd6761bc2021-08-17 14:23:28 -0700337 .usersize = offsetof(struct xt_quota_mtinfo2, master),
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700338 .me = THIS_MODULE,
339 },
340};
341
342static int __init quota_mt2_init(void)
343{
344 int ret;
JP Abgrall3fd82382011-07-12 12:02:59 -0700345 pr_debug("xt_quota2: init()");
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700346
Tyler Weare8a34e52016-05-27 15:48:38 -0700347 quota_class = class_create(THIS_MODULE, "xt_quota2");
348 ret = PTR_ERR(quota_class);
349 if (IS_ERR(quota_class)) {
350 pr_err("xt_quota2: couldn't create class");
351 class_destroy(quota_class);
352 return ret;
353 }
354
355 quota_device = device_create(quota_class, NULL, MKDEV(0, 0), NULL,
356 "counters");
357 ret = PTR_ERR(quota_device);
358 if (IS_ERR(quota_device)) {
359 pr_err("xt_quota2: couldn't create device");
360 device_destroy(quota_class, MKDEV(0, 0));
361 class_destroy(quota_class);
362 return ret;
363 }
364
365 quota_kobj = &quota_device->kobj;
JP Abgrall3fd82382011-07-12 12:02:59 -0700366
367 proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700368 if (proc_xt_quota == NULL)
369 return -EACCES;
370
371 ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg));
372 if (ret < 0)
JP Abgrall3fd82382011-07-12 12:02:59 -0700373 remove_proc_entry("xt_quota", init_net.proc_net);
374 pr_debug("xt_quota2: init() %d", ret);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700375 return ret;
376}
377
378static void __exit quota_mt2_exit(void)
379{
380 xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg));
JP Abgrall3fd82382011-07-12 12:02:59 -0700381 remove_proc_entry("xt_quota", init_net.proc_net);
Tyler Weare8a34e52016-05-27 15:48:38 -0700382 device_destroy(quota_class, MKDEV(0, 0));
383 class_destroy(quota_class);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700384}
385
386module_init(quota_mt2_init);
387module_exit(quota_mt2_exit);
388MODULE_DESCRIPTION("Xtables: countdown quota match; up counter");
389MODULE_AUTHOR("Sam Johnston <samj@samj.net>");
390MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
391MODULE_LICENSE("GPL");
392MODULE_ALIAS("ipt_quota2");
393MODULE_ALIAS("ip6t_quota2");