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