Blame view

net/netfilter/xt_limit.c 5.94 KB
db955170d   Marcin Garski   more UTF-8 conver...
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   Patrick McHardy   netfilter: add my...
3
   * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Jan Engelhardt   netfilter: xt ext...
9
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10

5a0e3ad6a   Tejun Heo   include cleanup: ...
11
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
  #include <linux/module.h>
  #include <linux/skbuff.h>
  #include <linux/spinlock.h>
  #include <linux/interrupt.h>
2e4e6a17a   Harald Welte   [NETFILTER] x_tab...
16
17
  #include <linux/netfilter/x_tables.h>
  #include <linux/netfilter/xt_limit.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18

acc738fec   Jan Engelhardt   netfilter: xtable...
19
  struct xt_limit_priv {
2cb4bbd75   Liping Zhang   netfilter: limit:...
20
  	spinlock_t lock;
acc738fec   Jan Engelhardt   netfilter: xtable...
21
22
23
  	unsigned long prev;
  	uint32_t credit;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Herve Eychenne <rv@wallfire.org>");
2ae15b64e   Jan Engelhardt   [NETFILTER]: Upda...
26
  MODULE_DESCRIPTION("Xtables: rate-limit match");
2e4e6a17a   Harald Welte   [NETFILTER] x_tab...
27
28
  MODULE_ALIAS("ipt_limit");
  MODULE_ALIAS("ip6t_limit");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
  
  /* The algorithm used is the Simple Token Bucket Filter (TBF)
   * see net/sched/sch_tbf.c in the linux source tree
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  /* 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   Jan Engelhardt   [NETFILTER]: x_ta...
63
  static bool
62fc80510   Jan Engelhardt   netfilter: xtable...
64
  limit_mt(const struct sk_buff *skb, struct xt_action_param *par)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
  {
acc738fec   Jan Engelhardt   netfilter: xtable...
66
67
  	const struct xt_rateinfo *r = par->matchinfo;
  	struct xt_limit_priv *priv = r->master;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
  	unsigned long now = jiffies;
2cb4bbd75   Liping Zhang   netfilter: limit:...
69
  	spin_lock_bh(&priv->lock);
acc738fec   Jan Engelhardt   netfilter: xtable...
70
71
72
  	priv->credit += (now - xchg(&priv->prev, now)) * CREDITS_PER_JIFFY;
  	if (priv->credit > r->credit_cap)
  		priv->credit = r->credit_cap;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73

acc738fec   Jan Engelhardt   netfilter: xtable...
74
  	if (priv->credit >= r->cost) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  		/* We're not limited. */
acc738fec   Jan Engelhardt   netfilter: xtable...
76
  		priv->credit -= r->cost;
2cb4bbd75   Liping Zhang   netfilter: limit:...
77
  		spin_unlock_bh(&priv->lock);
1d93a9cba   Jan Engelhardt   [NETFILTER]: x_ta...
78
  		return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
  	}
2cb4bbd75   Liping Zhang   netfilter: limit:...
80
  	spin_unlock_bh(&priv->lock);
1d93a9cba   Jan Engelhardt   [NETFILTER]: x_ta...
81
  	return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
  }
  
  /* Precision saver. */
7a909ac70   Florian Westphal   netfilter: limit,...
85
  static u32 user2credits(u32 user)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
89
  {
  	/* If multiplying would overflow... */
  	if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
  		/* Divide first. */
2e4e6a17a   Harald Welte   [NETFILTER] x_tab...
90
  		return (user / XT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91

2e4e6a17a   Harald Welte   [NETFILTER] x_tab...
92
  	return (user * HZ * CREDITS_PER_JIFFY) / XT_LIMIT_SCALE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
  }
b0f38452f   Jan Engelhardt   netfilter: xtable...
94
  static int limit_mt_check(const struct xt_mtchk_param *par)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
  {
9b4fce7a3   Jan Engelhardt   netfilter: xtable...
96
  	struct xt_rateinfo *r = par->matchinfo;
acc738fec   Jan Engelhardt   netfilter: xtable...
97
  	struct xt_limit_priv *priv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
101
  	/* Check for overflow. */
  	if (r->burst == 0
  	    || user2credits(r->avg * r->burst) < user2credits(r->avg)) {
8bee4bad0   Jan Engelhardt   netfilter: xt ext...
102
103
104
  		pr_info("Overflow, try lower: %u/%u
  ",
  			r->avg, r->burst);
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
105
  		return -ERANGE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  	}
acc738fec   Jan Engelhardt   netfilter: xtable...
107
108
  	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
  	if (priv == NULL)
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
109
  		return -ENOMEM;
acc738fec   Jan Engelhardt   netfilter: xtable...
110
111
112
  
  	/* For SMP, we only want to use one set of state. */
  	r->master = priv;
82e6bfe2f   Jan Engelhardt   netfilter: xt_lim...
113
114
115
116
  	/* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies *
  	   128. */
  	priv->prev = jiffies;
  	priv->credit = user2credits(r->avg * r->burst); /* Credits full. */
57dab5d0b   Patrick McHardy   [NETFILTER]: xt_l...
117
  	if (r->cost == 0) {
7a909ac70   Florian Westphal   netfilter: limit,...
118
  		r->credit_cap = priv->credit; /* Credits full. */
57dab5d0b   Patrick McHardy   [NETFILTER]: xt_l...
119
120
  		r->cost = user2credits(r->avg);
  	}
2cb4bbd75   Liping Zhang   netfilter: limit:...
121
  	spin_lock_init(&priv->lock);
bd414ee60   Jan Engelhardt   netfilter: xtable...
122
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  }
acc738fec   Jan Engelhardt   netfilter: xtable...
124
125
126
127
128
129
  static void limit_mt_destroy(const struct xt_mtdtor_param *par)
  {
  	const struct xt_rateinfo *info = par->matchinfo;
  
  	kfree(info->master);
  }
02c63cf77   Patrick McHardy   [NETFILTER]: xt_l...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  #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   Jan Engelhardt   netfilter: xtable...
144
  static void limit_mt_compat_from_user(void *dst, const void *src)
02c63cf77   Patrick McHardy   [NETFILTER]: xt_l...
145
  {
a47362a22   Jan Engelhardt   [NETFILTER]: add ...
146
  	const struct compat_xt_rateinfo *cm = src;
02c63cf77   Patrick McHardy   [NETFILTER]: xt_l...
147
148
149
150
151
152
153
154
155
156
  	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   Jan Engelhardt   netfilter: xtable...
157
  static int limit_mt_compat_to_user(void __user *dst, const void *src)
02c63cf77   Patrick McHardy   [NETFILTER]: xt_l...
158
  {
a47362a22   Jan Engelhardt   [NETFILTER]: add ...
159
  	const struct xt_rateinfo *m = src;
02c63cf77   Patrick McHardy   [NETFILTER]: xt_l...
160
161
162
163
164
165
166
167
168
169
170
171
  	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   Jan Engelhardt   netfilter: implem...
172
173
174
175
176
177
  static struct xt_match limit_mt_reg __read_mostly = {
  	.name             = "limit",
  	.revision         = 0,
  	.family           = NFPROTO_UNSPEC,
  	.match            = limit_mt,
  	.checkentry       = limit_mt_check,
acc738fec   Jan Engelhardt   netfilter: xtable...
178
  	.destroy          = limit_mt_destroy,
55b69e910   Jan Engelhardt   netfilter: implem...
179
  	.matchsize        = sizeof(struct xt_rateinfo),
02c63cf77   Patrick McHardy   [NETFILTER]: xt_l...
180
  #ifdef CONFIG_COMPAT
55b69e910   Jan Engelhardt   netfilter: implem...
181
182
183
  	.compatsize       = sizeof(struct compat_xt_rateinfo),
  	.compat_from_user = limit_mt_compat_from_user,
  	.compat_to_user   = limit_mt_compat_to_user,
02c63cf77   Patrick McHardy   [NETFILTER]: xt_l...
184
  #endif
ad10785a7   Dmitry Vyukov   netfilter: x_tabl...
185
  	.usersize         = offsetof(struct xt_rateinfo, prev),
55b69e910   Jan Engelhardt   netfilter: implem...
186
  	.me               = THIS_MODULE,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
  };
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
188
  static int __init limit_mt_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  {
55b69e910   Jan Engelhardt   netfilter: implem...
190
  	return xt_register_match(&limit_mt_reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
192
  static void __exit limit_mt_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
  {
55b69e910   Jan Engelhardt   netfilter: implem...
194
  	xt_unregister_match(&limit_mt_reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
196
197
  module_init(limit_mt_init);
  module_exit(limit_mt_exit);