]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * ebt_limit | |
3 | * | |
4 | * Authors: | |
5 | * Tom Marshall <tommy@home.tig-grr.com> | |
6 | * | |
7 | * Mostly copied from netfilter's ipt_limit.c, see that file for | |
8 | * more explanation | |
9 | * | |
10 | * September, 2003 | |
11 | * | |
12 | */ | |
1da177e4 | 13 | #include <linux/module.h> |
1da177e4 LT |
14 | #include <linux/netdevice.h> |
15 | #include <linux/spinlock.h> | |
18219d3f JE |
16 | #include <linux/netfilter/x_tables.h> |
17 | #include <linux/netfilter_bridge/ebtables.h> | |
18 | #include <linux/netfilter_bridge/ebt_limit.h> | |
1da177e4 LT |
19 | |
20 | static DEFINE_SPINLOCK(limit_lock); | |
21 | ||
22 | #define MAX_CPJ (0xFFFFFFFF / (HZ*60*60*24)) | |
23 | ||
24 | #define _POW2_BELOW2(x) ((x)|((x)>>1)) | |
25 | #define _POW2_BELOW4(x) (_POW2_BELOW2(x)|_POW2_BELOW2((x)>>2)) | |
26 | #define _POW2_BELOW8(x) (_POW2_BELOW4(x)|_POW2_BELOW4((x)>>4)) | |
27 | #define _POW2_BELOW16(x) (_POW2_BELOW8(x)|_POW2_BELOW8((x)>>8)) | |
28 | #define _POW2_BELOW32(x) (_POW2_BELOW16(x)|_POW2_BELOW16((x)>>16)) | |
29 | #define POW2_BELOW32(x) ((_POW2_BELOW32(x)>>1) + 1) | |
30 | ||
31 | #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) | |
32 | ||
33 | static int ebt_limit_match(const struct sk_buff *skb, | |
34 | const struct net_device *in, const struct net_device *out, | |
35 | const void *data, unsigned int datalen) | |
36 | { | |
37 | struct ebt_limit_info *info = (struct ebt_limit_info *)data; | |
38 | unsigned long now = jiffies; | |
39 | ||
40 | spin_lock_bh(&limit_lock); | |
41 | info->credit += (now - xchg(&info->prev, now)) * CREDITS_PER_JIFFY; | |
42 | if (info->credit > info->credit_cap) | |
43 | info->credit = info->credit_cap; | |
44 | ||
45 | if (info->credit >= info->cost) { | |
46 | /* We're not limited. */ | |
47 | info->credit -= info->cost; | |
48 | spin_unlock_bh(&limit_lock); | |
49 | return EBT_MATCH; | |
50 | } | |
51 | ||
52 | spin_unlock_bh(&limit_lock); | |
53 | return EBT_NOMATCH; | |
54 | } | |
55 | ||
56 | /* Precision saver. */ | |
57 | static u_int32_t | |
58 | user2credits(u_int32_t user) | |
59 | { | |
60 | /* If multiplying would overflow... */ | |
61 | if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY)) | |
62 | /* Divide first. */ | |
63 | return (user / EBT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY; | |
64 | ||
65 | return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE; | |
66 | } | |
67 | ||
68 | static int ebt_limit_check(const char *tablename, unsigned int hookmask, | |
69 | const struct ebt_entry *e, void *data, unsigned int datalen) | |
70 | { | |
abfdf1c4 | 71 | struct ebt_limit_info *info = data; |
1da177e4 | 72 | |
1da177e4 LT |
73 | /* Check for overflow. */ |
74 | if (info->burst == 0 || | |
75 | user2credits(info->avg * info->burst) < user2credits(info->avg)) { | |
76 | printk("Overflow in ebt_limit, try lower: %u/%u\n", | |
77 | info->avg, info->burst); | |
78 | return -EINVAL; | |
79 | } | |
80 | ||
81 | /* User avg in seconds * EBT_LIMIT_SCALE: convert to jiffies * 128. */ | |
82 | info->prev = jiffies; | |
83 | info->credit = user2credits(info->avg * info->burst); | |
84 | info->credit_cap = user2credits(info->avg * info->burst); | |
85 | info->cost = user2credits(info->avg); | |
86 | return 0; | |
87 | } | |
88 | ||
30083c95 | 89 | static struct ebt_match ebt_limit_reg __read_mostly = { |
1da177e4 LT |
90 | .name = EBT_LIMIT_MATCH, |
91 | .match = ebt_limit_match, | |
92 | .check = ebt_limit_check, | |
18219d3f | 93 | .matchsize = XT_ALIGN(sizeof(struct ebt_limit_info)), |
1da177e4 LT |
94 | .me = THIS_MODULE, |
95 | }; | |
96 | ||
65b4b4e8 | 97 | static int __init ebt_limit_init(void) |
1da177e4 LT |
98 | { |
99 | return ebt_register_match(&ebt_limit_reg); | |
100 | } | |
101 | ||
65b4b4e8 | 102 | static void __exit ebt_limit_fini(void) |
1da177e4 LT |
103 | { |
104 | ebt_unregister_match(&ebt_limit_reg); | |
105 | } | |
106 | ||
65b4b4e8 AM |
107 | module_init(ebt_limit_init); |
108 | module_exit(ebt_limit_fini); | |
f776c4cd | 109 | MODULE_DESCRIPTION("Ebtables: Rate-limit match"); |
1da177e4 | 110 | MODULE_LICENSE("GPL"); |