Blame view

net/netfilter/xt_limit.c 5.86 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>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
7
   *
   * 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...
8
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9

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

acc738fec   Jan Engelhardt   netfilter: xtable...
18
19
20
21
  struct xt_limit_priv {
  	unsigned long prev;
  	uint32_t credit;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Herve Eychenne <rv@wallfire.org>");
2ae15b64e   Jan Engelhardt   [NETFILTER]: Upda...
24
  MODULE_DESCRIPTION("Xtables: rate-limit match");
2e4e6a17a   Harald Welte   [NETFILTER] x_tab...
25
26
  MODULE_ALIAS("ipt_limit");
  MODULE_ALIAS("ip6t_limit");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
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
  
  /* 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   Jan Engelhardt   [NETFILTER]: x_ta...
64
  static bool
62fc80510   Jan Engelhardt   netfilter: xtable...
65
  limit_mt(const struct sk_buff *skb, struct xt_action_param *par)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
  {
acc738fec   Jan Engelhardt   netfilter: xtable...
67
68
  	const struct xt_rateinfo *r = par->matchinfo;
  	struct xt_limit_priv *priv = r->master;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
71
  	unsigned long now = jiffies;
  
  	spin_lock_bh(&limit_lock);
acc738fec   Jan Engelhardt   netfilter: xtable...
72
73
74
  	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
75

acc738fec   Jan Engelhardt   netfilter: xtable...
76
  	if (priv->credit >= r->cost) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
  		/* We're not limited. */
acc738fec   Jan Engelhardt   netfilter: xtable...
78
  		priv->credit -= r->cost;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
  		spin_unlock_bh(&limit_lock);
1d93a9cba   Jan Engelhardt   [NETFILTER]: x_ta...
80
  		return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
  	}
601e68e10   YOSHIFUJI Hideaki   [NETFILTER]: Fix ...
82
  	spin_unlock_bh(&limit_lock);
1d93a9cba   Jan Engelhardt   [NETFILTER]: x_ta...
83
  	return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
86
87
88
89
90
91
92
  }
  
  /* Precision saver. */
  static u_int32_t
  user2credits(u_int32_t user)
  {
  	/* If multiplying would overflow... */
  	if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
  		/* Divide first. */
2e4e6a17a   Harald Welte   [NETFILTER] x_tab...
93
  		return (user / XT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
  	/* Check for overflow. */
  	if (r->burst == 0
  	    || user2credits(r->avg * r->burst) < user2credits(r->avg)) {
8bee4bad0   Jan Engelhardt   netfilter: xt ext...
105
106
107
  		pr_info("Overflow, try lower: %u/%u
  ",
  			r->avg, r->burst);
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
108
  		return -ERANGE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
  	}
acc738fec   Jan Engelhardt   netfilter: xtable...
110
111
  	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
  	if (priv == NULL)
4a5a5c73b   Jan Engelhardt   netfilter: xtable...
112
  		return -ENOMEM;
acc738fec   Jan Engelhardt   netfilter: xtable...
113
114
115
  
  	/* For SMP, we only want to use one set of state. */
  	r->master = priv;
57dab5d0b   Patrick McHardy   [NETFILTER]: xt_l...
116
117
118
  	if (r->cost == 0) {
  		/* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies *
  		   128. */
acc738fec   Jan Engelhardt   netfilter: xtable...
119
120
  		priv->prev = jiffies;
  		priv->credit = user2credits(r->avg * r->burst); /* Credits full. */
57dab5d0b   Patrick McHardy   [NETFILTER]: xt_l...
121
122
123
  		r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */
  		r->cost = user2credits(r->avg);
  	}
bd414ee60   Jan Engelhardt   netfilter: xtable...
124
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
  }
acc738fec   Jan Engelhardt   netfilter: xtable...
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   Patrick McHardy   [NETFILTER]: xt_l...
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   Jan Engelhardt   netfilter: xtable...
146
  static void limit_mt_compat_from_user(void *dst, const void *src)
02c63cf77   Patrick McHardy   [NETFILTER]: xt_l...
147
  {
a47362a22   Jan Engelhardt   [NETFILTER]: add ...
148
  	const struct compat_xt_rateinfo *cm = src;
02c63cf77   Patrick McHardy   [NETFILTER]: xt_l...
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   Jan Engelhardt   netfilter: xtable...
159
  static int limit_mt_compat_to_user(void __user *dst, const void *src)
02c63cf77   Patrick McHardy   [NETFILTER]: xt_l...
160
  {
a47362a22   Jan Engelhardt   [NETFILTER]: add ...
161
  	const struct xt_rateinfo *m = src;
02c63cf77   Patrick McHardy   [NETFILTER]: xt_l...
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   Jan Engelhardt   netfilter: implem...
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   Jan Engelhardt   netfilter: xtable...
180
  	.destroy          = limit_mt_destroy,
55b69e910   Jan Engelhardt   netfilter: implem...
181
  	.matchsize        = sizeof(struct xt_rateinfo),
02c63cf77   Patrick McHardy   [NETFILTER]: xt_l...
182
  #ifdef CONFIG_COMPAT
55b69e910   Jan Engelhardt   netfilter: implem...
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   Patrick McHardy   [NETFILTER]: xt_l...
186
  #endif
55b69e910   Jan Engelhardt   netfilter: implem...
187
  	.me               = THIS_MODULE,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
  };
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
189
  static int __init limit_mt_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
  {
55b69e910   Jan Engelhardt   netfilter: implem...
191
  	return xt_register_match(&limit_mt_reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
193
  static void __exit limit_mt_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  {
55b69e910   Jan Engelhardt   netfilter: implem...
195
  	xt_unregister_match(&limit_mt_reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
  }
d3c5ee6d5   Jan Engelhardt   [NETFILTER]: x_ta...
197
198
  module_init(limit_mt_init);
  module_exit(limit_mt_exit);