blob: 3d2ae4e49a248298c962dded9030c831d9c1e934 [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';
138
139 spin_lock_bh(&e->lock);
140 e->quota = simple_strtoull(buf, NULL, 0);
141 spin_unlock_bh(&e->lock);
142 return size;
143}
144
Arve Hjønnevågf1075f72013-05-13 20:42:46 -0700145static const struct file_operations q2_counter_fops = {
146 .read = quota_proc_read,
147 .write = quota_proc_write,
148 .llseek = default_llseek,
149};
150
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700151static struct xt_quota_counter *
152q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon)
153{
154 struct xt_quota_counter *e;
155 unsigned int size;
156
157 /* Do not need all the procfs things for anonymous counters. */
158 size = anon ? offsetof(typeof(*e), list) : sizeof(*e);
159 e = kmalloc(size, GFP_KERNEL);
160 if (e == NULL)
161 return NULL;
162
163 e->quota = q->quota;
164 spin_lock_init(&e->lock);
165 if (!anon) {
166 INIT_LIST_HEAD(&e->list);
167 atomic_set(&e->ref, 1);
JP Abgrall3fd82382011-07-12 12:02:59 -0700168 strlcpy(e->name, q->name, sizeof(e->name));
Tyler Weare8a34e52016-05-27 15:48:38 -0700169 strlcpy(e->last_prefix, "UNSET", sizeof(e->last_prefix));
170 strlcpy(e->last_iface, "UNSET", sizeof(e->last_iface));
171 INIT_WORK(&e->work, quota2_work);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700172 }
173 return e;
174}
175
176/**
177 * q2_get_counter - get ref to counter or create new
178 * @name: name of counter
179 */
180static struct xt_quota_counter *
181q2_get_counter(const struct xt_quota_mtinfo2 *q)
182{
183 struct proc_dir_entry *p;
JP Abgrall3fd82382011-07-12 12:02:59 -0700184 struct xt_quota_counter *e = NULL;
185 struct xt_quota_counter *new_e;
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700186
187 if (*q->name == '\0')
188 return q2_new_counter(q, true);
189
JP Abgrall3fd82382011-07-12 12:02:59 -0700190 /* No need to hold a lock while getting a new counter */
191 new_e = q2_new_counter(q, false);
192 if (new_e == NULL)
193 goto out;
194
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700195 spin_lock_bh(&counter_list_lock);
196 list_for_each_entry(e, &counter_list, list)
197 if (strcmp(e->name, q->name) == 0) {
198 atomic_inc(&e->ref);
199 spin_unlock_bh(&counter_list_lock);
JP Abgrall3fd82382011-07-12 12:02:59 -0700200 kfree(new_e);
201 pr_debug("xt_quota2: old counter name=%s", e->name);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700202 return e;
203 }
JP Abgrall3fd82382011-07-12 12:02:59 -0700204 e = new_e;
205 pr_debug("xt_quota2: new_counter name=%s", e->name);
206 list_add_tail(&e->list, &counter_list);
207 /* The entry having a refcount of 1 is not directly destructible.
208 * This func has not yet returned the new entry, thus iptables
209 * has not references for destroying this entry.
210 * For another rule to try to destroy it, it would 1st need for this
211 * func* to be re-invoked, acquire a new ref for the same named quota.
212 * Nobody will access the e->procfs_entry either.
213 * So release the lock. */
214 spin_unlock_bh(&counter_list_lock);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700215
JP Abgrall3fd82382011-07-12 12:02:59 -0700216 /* create_proc_entry() is not spin_lock happy */
Arve Hjønnevågf1075f72013-05-13 20:42:46 -0700217 p = e->procfs_entry = proc_create_data(e->name, quota_list_perms,
218 proc_xt_quota, &q2_counter_fops, e);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700219
JP Abgrall3fd82382011-07-12 12:02:59 -0700220 if (IS_ERR_OR_NULL(p)) {
221 spin_lock_bh(&counter_list_lock);
222 list_del(&e->list);
223 spin_unlock_bh(&counter_list_lock);
224 goto out;
225 }
Arve Hjønnevågf1075f72013-05-13 20:42:46 -0700226 proc_set_user(p, quota_list_uid, quota_list_gid);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700227 return e;
228
229 out:
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700230 kfree(e);
231 return NULL;
232}
233
234static int quota_mt2_check(const struct xt_mtchk_param *par)
235{
236 struct xt_quota_mtinfo2 *q = par->matchinfo;
237
JP Abgrall3fd82382011-07-12 12:02:59 -0700238 pr_debug("xt_quota2: check() flags=0x%04x", q->flags);
239
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700240 if (q->flags & ~XT_QUOTA_MASK)
241 return -EINVAL;
242
243 q->name[sizeof(q->name)-1] = '\0';
244 if (*q->name == '.' || strchr(q->name, '/') != NULL) {
245 printk(KERN_ERR "xt_quota.3: illegal name\n");
246 return -EINVAL;
247 }
248
249 q->master = q2_get_counter(q);
250 if (q->master == NULL) {
251 printk(KERN_ERR "xt_quota.3: memory alloc failure\n");
252 return -ENOMEM;
253 }
254
255 return 0;
256}
257
258static void quota_mt2_destroy(const struct xt_mtdtor_param *par)
259{
260 struct xt_quota_mtinfo2 *q = par->matchinfo;
261 struct xt_quota_counter *e = q->master;
262
263 if (*q->name == '\0') {
264 kfree(e);
265 return;
266 }
267
268 spin_lock_bh(&counter_list_lock);
269 if (!atomic_dec_and_test(&e->ref)) {
270 spin_unlock_bh(&counter_list_lock);
271 return;
272 }
273
274 list_del(&e->list);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700275 spin_unlock_bh(&counter_list_lock);
DongJoo Kimf6b56092019-10-21 11:48:00 +0900276 remove_proc_entry(e->name, proc_xt_quota);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700277 kfree(e);
278}
279
280static bool
281quota_mt2(const struct sk_buff *skb, struct xt_action_param *par)
282{
283 struct xt_quota_mtinfo2 *q = (void *)par->matchinfo;
284 struct xt_quota_counter *e = q->master;
285 bool ret = q->flags & XT_QUOTA_INVERT;
286
287 spin_lock_bh(&e->lock);
288 if (q->flags & XT_QUOTA_GROW) {
289 /*
290 * While no_change is pointless in "grow" mode, we will
291 * implement it here simply to have a consistent behavior.
292 */
293 if (!(q->flags & XT_QUOTA_NO_CHANGE)) {
294 e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len;
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700295 }
296 ret = true;
297 } else {
298 if (e->quota >= skb->len) {
299 if (!(q->flags & XT_QUOTA_NO_CHANGE))
300 e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len;
301 ret = !ret;
302 } else {
JP Abgrall3fd82382011-07-12 12:02:59 -0700303 /* We are transitioning, log that fact. */
304 if (e->quota) {
Tyler Weare8a34e52016-05-27 15:48:38 -0700305 quota2_log(par->in, par->out, e, q->name);
JP Abgrall3fd82382011-07-12 12:02:59 -0700306 }
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700307 /* we do not allow even small packets from now on */
308 e->quota = 0;
309 }
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700310 }
311 spin_unlock_bh(&e->lock);
312 return ret;
313}
314
315static struct xt_match quota_mt2_reg[] __read_mostly = {
316 {
317 .name = "quota2",
318 .revision = 3,
319 .family = NFPROTO_IPV4,
320 .checkentry = quota_mt2_check,
321 .match = quota_mt2,
JP Abgrall3fd82382011-07-12 12:02:59 -0700322 .destroy = quota_mt2_destroy,
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700323 .matchsize = sizeof(struct xt_quota_mtinfo2),
Todd Kjosd6761bc2021-08-17 14:23:28 -0700324 .usersize = offsetof(struct xt_quota_mtinfo2, master),
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700325 .me = THIS_MODULE,
326 },
327 {
328 .name = "quota2",
329 .revision = 3,
330 .family = NFPROTO_IPV6,
331 .checkentry = quota_mt2_check,
332 .match = quota_mt2,
JP Abgrall3fd82382011-07-12 12:02:59 -0700333 .destroy = quota_mt2_destroy,
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700334 .matchsize = sizeof(struct xt_quota_mtinfo2),
Todd Kjosd6761bc2021-08-17 14:23:28 -0700335 .usersize = offsetof(struct xt_quota_mtinfo2, master),
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700336 .me = THIS_MODULE,
337 },
338};
339
340static int __init quota_mt2_init(void)
341{
342 int ret;
JP Abgrall3fd82382011-07-12 12:02:59 -0700343 pr_debug("xt_quota2: init()");
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700344
Tyler Weare8a34e52016-05-27 15:48:38 -0700345 quota_class = class_create(THIS_MODULE, "xt_quota2");
346 ret = PTR_ERR(quota_class);
347 if (IS_ERR(quota_class)) {
348 pr_err("xt_quota2: couldn't create class");
349 class_destroy(quota_class);
350 return ret;
351 }
352
353 quota_device = device_create(quota_class, NULL, MKDEV(0, 0), NULL,
354 "counters");
355 ret = PTR_ERR(quota_device);
356 if (IS_ERR(quota_device)) {
357 pr_err("xt_quota2: couldn't create device");
358 device_destroy(quota_class, MKDEV(0, 0));
359 class_destroy(quota_class);
360 return ret;
361 }
362
363 quota_kobj = &quota_device->kobj;
JP Abgrall3fd82382011-07-12 12:02:59 -0700364
365 proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700366 if (proc_xt_quota == NULL)
367 return -EACCES;
368
369 ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg));
370 if (ret < 0)
JP Abgrall3fd82382011-07-12 12:02:59 -0700371 remove_proc_entry("xt_quota", init_net.proc_net);
372 pr_debug("xt_quota2: init() %d", ret);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700373 return ret;
374}
375
376static void __exit quota_mt2_exit(void)
377{
378 xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg));
JP Abgrall3fd82382011-07-12 12:02:59 -0700379 remove_proc_entry("xt_quota", init_net.proc_net);
Tyler Weare8a34e52016-05-27 15:48:38 -0700380 device_destroy(quota_class, MKDEV(0, 0));
381 class_destroy(quota_class);
JP Abgralldc5ce6e2011-06-21 11:14:49 -0700382}
383
384module_init(quota_mt2_init);
385module_exit(quota_mt2_exit);
386MODULE_DESCRIPTION("Xtables: countdown quota match; up counter");
387MODULE_AUTHOR("Sam Johnston <samj@samj.net>");
388MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
389MODULE_LICENSE("GPL");
390MODULE_ALIAS("ipt_quota2");
391MODULE_ALIAS("ip6t_quota2");