Blame view
net/netfilter/xt_limit.c
5.88 KB
db955170d
|
1 2 |
/* (C) 1999 Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr> * (C) 1999 Hervé Eychenne <eychenne@info.enserb.u-bordeaux.fr> |
f229f6ce4
|
3 |
* (C) 2006-2012 Patrick McHardy <kaber@trash.net> |
1da177e4c
|
4 5 6 7 8 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ |
8bee4bad0
|
9 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
1da177e4c
|
10 |
|
5a0e3ad6a
|
11 |
#include <linux/slab.h> |
1da177e4c
|
12 13 14 15 |
#include <linux/module.h> #include <linux/skbuff.h> #include <linux/spinlock.h> #include <linux/interrupt.h> |
2e4e6a17a
|
16 17 |
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/xt_limit.h> |
1da177e4c
|
18 |
|
acc738fec
|
19 20 21 22 |
struct xt_limit_priv { unsigned long prev; uint32_t credit; }; |
1da177e4c
|
23 24 |
MODULE_LICENSE("GPL"); MODULE_AUTHOR("Herve Eychenne <rv@wallfire.org>"); |
2ae15b64e
|
25 |
MODULE_DESCRIPTION("Xtables: rate-limit match"); |
2e4e6a17a
|
26 27 |
MODULE_ALIAS("ipt_limit"); MODULE_ALIAS("ip6t_limit"); |
1da177e4c
|
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
/* The algorithm used is the Simple Token Bucket Filter (TBF) * see net/sched/sch_tbf.c in the linux source tree */ static DEFINE_SPINLOCK(limit_lock); /* Rusty: This is my (non-mathematically-inclined) understanding of this algorithm. The `average rate' in jiffies becomes your initial amount of credit `credit' and the most credit you can ever have `credit_cap'. The `peak rate' becomes the cost of passing the test, `cost'. `prev' tracks the last packet hit: you gain one credit per jiffy. If you get credit balance more than this, the extra credit is discarded. Every time the match passes, you lose `cost' credits; if you don't have that many, the test fails. See Alexey's formal explanation in net/sched/sch_tbf.c. To get the maxmum range, we multiply by this factor (ie. you get N credits per jiffy). We want to allow a rate as low as 1 per day (slowest userspace tool allows), which means CREDITS_PER_JIFFY*HZ*60*60*24 < 2^32. ie. */ #define MAX_CPJ (0xFFFFFFFF / (HZ*60*60*24)) /* Repeated shift and or gives us all 1s, final shift and add 1 gives * us the power of 2 below the theoretical max, so GCC simply does a * shift. */ #define _POW2_BELOW2(x) ((x)|((x)>>1)) #define _POW2_BELOW4(x) (_POW2_BELOW2(x)|_POW2_BELOW2((x)>>2)) #define _POW2_BELOW8(x) (_POW2_BELOW4(x)|_POW2_BELOW4((x)>>4)) #define _POW2_BELOW16(x) (_POW2_BELOW8(x)|_POW2_BELOW8((x)>>8)) #define _POW2_BELOW32(x) (_POW2_BELOW16(x)|_POW2_BELOW16((x)>>16)) #define POW2_BELOW32(x) ((_POW2_BELOW32(x)>>1) + 1) #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) |
1d93a9cba
|
65 |
static bool |
62fc80510
|
66 |
limit_mt(const struct sk_buff *skb, struct xt_action_param *par) |
1da177e4c
|
67 |
{ |
acc738fec
|
68 69 |
const struct xt_rateinfo *r = par->matchinfo; struct xt_limit_priv *priv = r->master; |
1da177e4c
|
70 71 72 |
unsigned long now = jiffies; spin_lock_bh(&limit_lock); |
acc738fec
|
73 74 75 |
priv->credit += (now - xchg(&priv->prev, now)) * CREDITS_PER_JIFFY; if (priv->credit > r->credit_cap) priv->credit = r->credit_cap; |
1da177e4c
|
76 |
|
acc738fec
|
77 |
if (priv->credit >= r->cost) { |
1da177e4c
|
78 |
/* We're not limited. */ |
acc738fec
|
79 |
priv->credit -= r->cost; |
1da177e4c
|
80 |
spin_unlock_bh(&limit_lock); |
1d93a9cba
|
81 |
return true; |
1da177e4c
|
82 |
} |
601e68e10
|
83 |
spin_unlock_bh(&limit_lock); |
1d93a9cba
|
84 |
return false; |
1da177e4c
|
85 86 87 |
} /* Precision saver. */ |
7a909ac70
|
88 |
static u32 user2credits(u32 user) |
1da177e4c
|
89 90 91 92 |
{ /* If multiplying would overflow... */ if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY)) /* Divide first. */ |
2e4e6a17a
|
93 |
return (user / XT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY; |
1da177e4c
|
94 |
|
2e4e6a17a
|
95 |
return (user * HZ * CREDITS_PER_JIFFY) / XT_LIMIT_SCALE; |
1da177e4c
|
96 |
} |
b0f38452f
|
97 |
static int limit_mt_check(const struct xt_mtchk_param *par) |
1da177e4c
|
98 |
{ |
9b4fce7a3
|
99 |
struct xt_rateinfo *r = par->matchinfo; |
acc738fec
|
100 |
struct xt_limit_priv *priv; |
1da177e4c
|
101 |
|
1da177e4c
|
102 103 104 |
/* Check for overflow. */ if (r->burst == 0 || user2credits(r->avg * r->burst) < user2credits(r->avg)) { |
8bee4bad0
|
105 106 107 |
pr_info("Overflow, try lower: %u/%u ", r->avg, r->burst); |
4a5a5c73b
|
108 |
return -ERANGE; |
1da177e4c
|
109 |
} |
acc738fec
|
110 111 |
priv = kmalloc(sizeof(*priv), GFP_KERNEL); if (priv == NULL) |
4a5a5c73b
|
112 |
return -ENOMEM; |
acc738fec
|
113 114 115 |
/* For SMP, we only want to use one set of state. */ r->master = priv; |
82e6bfe2f
|
116 117 118 119 |
/* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies * 128. */ priv->prev = jiffies; priv->credit = user2credits(r->avg * r->burst); /* Credits full. */ |
57dab5d0b
|
120 |
if (r->cost == 0) { |
7a909ac70
|
121 |
r->credit_cap = priv->credit; /* Credits full. */ |
57dab5d0b
|
122 123 |
r->cost = user2credits(r->avg); } |
bd414ee60
|
124 |
return 0; |
1da177e4c
|
125 |
} |
acc738fec
|
126 127 128 129 130 131 |
static void limit_mt_destroy(const struct xt_mtdtor_param *par) { const struct xt_rateinfo *info = par->matchinfo; kfree(info->master); } |
02c63cf77
|
132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
#ifdef CONFIG_COMPAT struct compat_xt_rateinfo { u_int32_t avg; u_int32_t burst; compat_ulong_t prev; u_int32_t credit; u_int32_t credit_cap, cost; u_int32_t master; }; /* To keep the full "prev" timestamp, the upper 32 bits are stored in the * master pointer, which does not need to be preserved. */ |
739674fb7
|
146 |
static void limit_mt_compat_from_user(void *dst, const void *src) |
02c63cf77
|
147 |
{ |
a47362a22
|
148 |
const struct compat_xt_rateinfo *cm = src; |
02c63cf77
|
149 150 151 152 153 154 155 156 157 158 |
struct xt_rateinfo m = { .avg = cm->avg, .burst = cm->burst, .prev = cm->prev | (unsigned long)cm->master << 32, .credit = cm->credit, .credit_cap = cm->credit_cap, .cost = cm->cost, }; memcpy(dst, &m, sizeof(m)); } |
739674fb7
|
159 |
static int limit_mt_compat_to_user(void __user *dst, const void *src) |
02c63cf77
|
160 |
{ |
a47362a22
|
161 |
const struct xt_rateinfo *m = src; |
02c63cf77
|
162 163 164 165 166 167 168 169 170 171 172 173 |
struct compat_xt_rateinfo cm = { .avg = m->avg, .burst = m->burst, .prev = m->prev, .credit = m->credit, .credit_cap = m->credit_cap, .cost = m->cost, .master = m->prev >> 32, }; return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; } #endif /* CONFIG_COMPAT */ |
55b69e910
|
174 175 176 177 178 179 |
static struct xt_match limit_mt_reg __read_mostly = { .name = "limit", .revision = 0, .family = NFPROTO_UNSPEC, .match = limit_mt, .checkentry = limit_mt_check, |
acc738fec
|
180 |
.destroy = limit_mt_destroy, |
55b69e910
|
181 |
.matchsize = sizeof(struct xt_rateinfo), |
02c63cf77
|
182 |
#ifdef CONFIG_COMPAT |
55b69e910
|
183 184 185 |
.compatsize = sizeof(struct compat_xt_rateinfo), .compat_from_user = limit_mt_compat_from_user, .compat_to_user = limit_mt_compat_to_user, |
02c63cf77
|
186 |
#endif |
55b69e910
|
187 |
.me = THIS_MODULE, |
1da177e4c
|
188 |
}; |
d3c5ee6d5
|
189 |
static int __init limit_mt_init(void) |
1da177e4c
|
190 |
{ |
55b69e910
|
191 |
return xt_register_match(&limit_mt_reg); |
1da177e4c
|
192 |
} |
d3c5ee6d5
|
193 |
static void __exit limit_mt_exit(void) |
1da177e4c
|
194 |
{ |
55b69e910
|
195 |
xt_unregister_match(&limit_mt_reg); |
1da177e4c
|
196 |
} |
d3c5ee6d5
|
197 198 |
module_init(limit_mt_init); module_exit(limit_mt_exit); |