diff options
author | Jan Engelhardt <jengelh@medozas.de> | 2009-03-16 10:35:29 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2009-03-16 10:35:29 -0400 |
commit | acc738fec03bdaa5b77340c32a82fbfedaaabef0 (patch) | |
tree | 646495518d8e4946862ab9d361eb2248d4212be5 /net | |
parent | 95ba434f898c3cb5c7457dce265bf0ab72ba8ce9 (diff) |
netfilter: xtables: avoid pointer to self
Commit 784544739a25c30637397ace5489eeb6e15d7d49 (netfilter: iptables:
lock free counters) broke a number of modules whose rule data referenced
itself. A reallocation would not reestablish the correct references, so
it is best to use a separate struct that does not fall under RCU.
Signed-off-by: Jan Engelhardt <jengelh@medozas.de>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/xt_limit.c | 40 | ||||
-rw-r--r-- | net/netfilter/xt_quota.c | 31 | ||||
-rw-r--r-- | net/netfilter/xt_statistic.c | 28 |
3 files changed, 76 insertions, 23 deletions
diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c index c908d69a5595..2e8089ecd0af 100644 --- a/net/netfilter/xt_limit.c +++ b/net/netfilter/xt_limit.c | |||
@@ -14,6 +14,11 @@ | |||
14 | #include <linux/netfilter/x_tables.h> | 14 | #include <linux/netfilter/x_tables.h> |
15 | #include <linux/netfilter/xt_limit.h> | 15 | #include <linux/netfilter/xt_limit.h> |
16 | 16 | ||
17 | struct xt_limit_priv { | ||
18 | unsigned long prev; | ||
19 | uint32_t credit; | ||
20 | }; | ||
21 | |||
17 | MODULE_LICENSE("GPL"); | 22 | MODULE_LICENSE("GPL"); |
18 | MODULE_AUTHOR("Herve Eychenne <rv@wallfire.org>"); | 23 | MODULE_AUTHOR("Herve Eychenne <rv@wallfire.org>"); |
19 | MODULE_DESCRIPTION("Xtables: rate-limit match"); | 24 | MODULE_DESCRIPTION("Xtables: rate-limit match"); |
@@ -60,18 +65,18 @@ static DEFINE_SPINLOCK(limit_lock); | |||
60 | static bool | 65 | static bool |
61 | limit_mt(const struct sk_buff *skb, const struct xt_match_param *par) | 66 | limit_mt(const struct sk_buff *skb, const struct xt_match_param *par) |
62 | { | 67 | { |
63 | struct xt_rateinfo *r = | 68 | const struct xt_rateinfo *r = par->matchinfo; |
64 | ((const struct xt_rateinfo *)par->matchinfo)->master; | 69 | struct xt_limit_priv *priv = r->master; |
65 | unsigned long now = jiffies; | 70 | unsigned long now = jiffies; |
66 | 71 | ||
67 | spin_lock_bh(&limit_lock); | 72 | spin_lock_bh(&limit_lock); |
68 | r->credit += (now - xchg(&r->prev, now)) * CREDITS_PER_JIFFY; | 73 | priv->credit += (now - xchg(&priv->prev, now)) * CREDITS_PER_JIFFY; |
69 | if (r->credit > r->credit_cap) | 74 | if (priv->credit > r->credit_cap) |
70 | r->credit = r->credit_cap; | 75 | priv->credit = r->credit_cap; |
71 | 76 | ||
72 | if (r->credit >= r->cost) { | 77 | if (priv->credit >= r->cost) { |
73 | /* We're not limited. */ | 78 | /* We're not limited. */ |
74 | r->credit -= r->cost; | 79 | priv->credit -= r->cost; |
75 | spin_unlock_bh(&limit_lock); | 80 | spin_unlock_bh(&limit_lock); |
76 | return true; | 81 | return true; |
77 | } | 82 | } |
@@ -95,6 +100,7 @@ user2credits(u_int32_t user) | |||
95 | static bool limit_mt_check(const struct xt_mtchk_param *par) | 100 | static bool limit_mt_check(const struct xt_mtchk_param *par) |
96 | { | 101 | { |
97 | struct xt_rateinfo *r = par->matchinfo; | 102 | struct xt_rateinfo *r = par->matchinfo; |
103 | struct xt_limit_priv *priv; | ||
98 | 104 | ||
99 | /* Check for overflow. */ | 105 | /* Check for overflow. */ |
100 | if (r->burst == 0 | 106 | if (r->burst == 0 |
@@ -104,19 +110,30 @@ static bool limit_mt_check(const struct xt_mtchk_param *par) | |||
104 | return false; | 110 | return false; |
105 | } | 111 | } |
106 | 112 | ||
107 | /* For SMP, we only want to use one set of counters. */ | 113 | priv = kmalloc(sizeof(*priv), GFP_KERNEL); |
108 | r->master = r; | 114 | if (priv == NULL) |
115 | return -ENOMEM; | ||
116 | |||
117 | /* For SMP, we only want to use one set of state. */ | ||
118 | r->master = priv; | ||
109 | if (r->cost == 0) { | 119 | if (r->cost == 0) { |
110 | /* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies * | 120 | /* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies * |
111 | 128. */ | 121 | 128. */ |
112 | r->prev = jiffies; | 122 | priv->prev = jiffies; |
113 | r->credit = user2credits(r->avg * r->burst); /* Credits full. */ | 123 | priv->credit = user2credits(r->avg * r->burst); /* Credits full. */ |
114 | r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */ | 124 | r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */ |
115 | r->cost = user2credits(r->avg); | 125 | r->cost = user2credits(r->avg); |
116 | } | 126 | } |
117 | return true; | 127 | return true; |
118 | } | 128 | } |
119 | 129 | ||
130 | static void limit_mt_destroy(const struct xt_mtdtor_param *par) | ||
131 | { | ||
132 | const struct xt_rateinfo *info = par->matchinfo; | ||
133 | |||
134 | kfree(info->master); | ||
135 | } | ||
136 | |||
120 | #ifdef CONFIG_COMPAT | 137 | #ifdef CONFIG_COMPAT |
121 | struct compat_xt_rateinfo { | 138 | struct compat_xt_rateinfo { |
122 | u_int32_t avg; | 139 | u_int32_t avg; |
@@ -167,6 +184,7 @@ static struct xt_match limit_mt_reg __read_mostly = { | |||
167 | .family = NFPROTO_UNSPEC, | 184 | .family = NFPROTO_UNSPEC, |
168 | .match = limit_mt, | 185 | .match = limit_mt, |
169 | .checkentry = limit_mt_check, | 186 | .checkentry = limit_mt_check, |
187 | .destroy = limit_mt_destroy, | ||
170 | .matchsize = sizeof(struct xt_rateinfo), | 188 | .matchsize = sizeof(struct xt_rateinfo), |
171 | #ifdef CONFIG_COMPAT | 189 | #ifdef CONFIG_COMPAT |
172 | .compatsize = sizeof(struct compat_xt_rateinfo), | 190 | .compatsize = sizeof(struct compat_xt_rateinfo), |
diff --git a/net/netfilter/xt_quota.c b/net/netfilter/xt_quota.c index c84fce5e0f3e..01dd07b764ec 100644 --- a/net/netfilter/xt_quota.c +++ b/net/netfilter/xt_quota.c | |||
@@ -9,6 +9,10 @@ | |||
9 | #include <linux/netfilter/x_tables.h> | 9 | #include <linux/netfilter/x_tables.h> |
10 | #include <linux/netfilter/xt_quota.h> | 10 | #include <linux/netfilter/xt_quota.h> |
11 | 11 | ||
12 | struct xt_quota_priv { | ||
13 | uint64_t quota; | ||
14 | }; | ||
15 | |||
12 | MODULE_LICENSE("GPL"); | 16 | MODULE_LICENSE("GPL"); |
13 | MODULE_AUTHOR("Sam Johnston <samj@samj.net>"); | 17 | MODULE_AUTHOR("Sam Johnston <samj@samj.net>"); |
14 | MODULE_DESCRIPTION("Xtables: countdown quota match"); | 18 | MODULE_DESCRIPTION("Xtables: countdown quota match"); |
@@ -20,18 +24,20 @@ static DEFINE_SPINLOCK(quota_lock); | |||
20 | static bool | 24 | static bool |
21 | quota_mt(const struct sk_buff *skb, const struct xt_match_param *par) | 25 | quota_mt(const struct sk_buff *skb, const struct xt_match_param *par) |
22 | { | 26 | { |
23 | struct xt_quota_info *q = | 27 | struct xt_quota_info *q = (void *)par->matchinfo; |
24 | ((const struct xt_quota_info *)par->matchinfo)->master; | 28 | struct xt_quota_priv *priv = q->master; |
25 | bool ret = q->flags & XT_QUOTA_INVERT; | 29 | bool ret = q->flags & XT_QUOTA_INVERT; |
26 | 30 | ||
27 | spin_lock_bh("a_lock); | 31 | spin_lock_bh("a_lock); |
28 | if (q->quota >= skb->len) { | 32 | if (priv->quota >= skb->len) { |
29 | q->quota -= skb->len; | 33 | priv->quota -= skb->len; |
30 | ret = !ret; | 34 | ret = !ret; |
31 | } else { | 35 | } else { |
32 | /* we do not allow even small packets from now on */ | 36 | /* we do not allow even small packets from now on */ |
33 | q->quota = 0; | 37 | priv->quota = 0; |
34 | } | 38 | } |
39 | /* Copy quota back to matchinfo so that iptables can display it */ | ||
40 | q->quota = priv->quota; | ||
35 | spin_unlock_bh("a_lock); | 41 | spin_unlock_bh("a_lock); |
36 | 42 | ||
37 | return ret; | 43 | return ret; |
@@ -43,17 +49,28 @@ static bool quota_mt_check(const struct xt_mtchk_param *par) | |||
43 | 49 | ||
44 | if (q->flags & ~XT_QUOTA_MASK) | 50 | if (q->flags & ~XT_QUOTA_MASK) |
45 | return false; | 51 | return false; |
46 | /* For SMP, we only want to use one set of counters. */ | 52 | |
47 | q->master = q; | 53 | q->master = kmalloc(sizeof(*q->master), GFP_KERNEL); |
54 | if (q->master == NULL) | ||
55 | return -ENOMEM; | ||
56 | |||
48 | return true; | 57 | return true; |
49 | } | 58 | } |
50 | 59 | ||
60 | static void quota_mt_destroy(const struct xt_mtdtor_param *par) | ||
61 | { | ||
62 | const struct xt_quota_info *q = par->matchinfo; | ||
63 | |||
64 | kfree(q->master); | ||
65 | } | ||
66 | |||
51 | static struct xt_match quota_mt_reg __read_mostly = { | 67 | static struct xt_match quota_mt_reg __read_mostly = { |
52 | .name = "quota", | 68 | .name = "quota", |
53 | .revision = 0, | 69 | .revision = 0, |
54 | .family = NFPROTO_UNSPEC, | 70 | .family = NFPROTO_UNSPEC, |
55 | .match = quota_mt, | 71 | .match = quota_mt, |
56 | .checkentry = quota_mt_check, | 72 | .checkentry = quota_mt_check, |
73 | .destroy = quota_mt_destroy, | ||
57 | .matchsize = sizeof(struct xt_quota_info), | 74 | .matchsize = sizeof(struct xt_quota_info), |
58 | .me = THIS_MODULE, | 75 | .me = THIS_MODULE, |
59 | }; | 76 | }; |
diff --git a/net/netfilter/xt_statistic.c b/net/netfilter/xt_statistic.c index 0d75141139d5..d8c0f8f1a78e 100644 --- a/net/netfilter/xt_statistic.c +++ b/net/netfilter/xt_statistic.c | |||
@@ -16,6 +16,10 @@ | |||
16 | #include <linux/netfilter/xt_statistic.h> | 16 | #include <linux/netfilter/xt_statistic.h> |
17 | #include <linux/netfilter/x_tables.h> | 17 | #include <linux/netfilter/x_tables.h> |
18 | 18 | ||
19 | struct xt_statistic_priv { | ||
20 | uint32_t count; | ||
21 | }; | ||
22 | |||
19 | MODULE_LICENSE("GPL"); | 23 | MODULE_LICENSE("GPL"); |
20 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | 24 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); |
21 | MODULE_DESCRIPTION("Xtables: statistics-based matching (\"Nth\", random)"); | 25 | MODULE_DESCRIPTION("Xtables: statistics-based matching (\"Nth\", random)"); |
@@ -27,7 +31,7 @@ static DEFINE_SPINLOCK(nth_lock); | |||
27 | static bool | 31 | static bool |
28 | statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par) | 32 | statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par) |
29 | { | 33 | { |
30 | struct xt_statistic_info *info = (void *)par->matchinfo; | 34 | const struct xt_statistic_info *info = par->matchinfo; |
31 | bool ret = info->flags & XT_STATISTIC_INVERT; | 35 | bool ret = info->flags & XT_STATISTIC_INVERT; |
32 | 36 | ||
33 | switch (info->mode) { | 37 | switch (info->mode) { |
@@ -36,10 +40,9 @@ statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par) | |||
36 | ret = !ret; | 40 | ret = !ret; |
37 | break; | 41 | break; |
38 | case XT_STATISTIC_MODE_NTH: | 42 | case XT_STATISTIC_MODE_NTH: |
39 | info = info->master; | ||
40 | spin_lock_bh(&nth_lock); | 43 | spin_lock_bh(&nth_lock); |
41 | if (info->u.nth.count++ == info->u.nth.every) { | 44 | if (info->master->count++ == info->u.nth.every) { |
42 | info->u.nth.count = 0; | 45 | info->master->count = 0; |
43 | ret = !ret; | 46 | ret = !ret; |
44 | } | 47 | } |
45 | spin_unlock_bh(&nth_lock); | 48 | spin_unlock_bh(&nth_lock); |
@@ -56,16 +59,31 @@ static bool statistic_mt_check(const struct xt_mtchk_param *par) | |||
56 | if (info->mode > XT_STATISTIC_MODE_MAX || | 59 | if (info->mode > XT_STATISTIC_MODE_MAX || |
57 | info->flags & ~XT_STATISTIC_MASK) | 60 | info->flags & ~XT_STATISTIC_MASK) |
58 | return false; | 61 | return false; |
59 | info->master = info; | 62 | |
63 | info->master = kzalloc(sizeof(*info->master), GFP_KERNEL); | ||
64 | if (info->master == NULL) { | ||
65 | printk(KERN_ERR KBUILD_MODNAME ": Out of memory\n"); | ||
66 | return false; | ||
67 | } | ||
68 | info->master->count = info->u.nth.count; | ||
69 | |||
60 | return true; | 70 | return true; |
61 | } | 71 | } |
62 | 72 | ||
73 | static void statistic_mt_destroy(const struct xt_mtdtor_param *par) | ||
74 | { | ||
75 | const struct xt_statistic_info *info = par->matchinfo; | ||
76 | |||
77 | kfree(info->master); | ||
78 | } | ||
79 | |||
63 | static struct xt_match xt_statistic_mt_reg __read_mostly = { | 80 | static struct xt_match xt_statistic_mt_reg __read_mostly = { |
64 | .name = "statistic", | 81 | .name = "statistic", |
65 | .revision = 0, | 82 | .revision = 0, |
66 | .family = NFPROTO_UNSPEC, | 83 | .family = NFPROTO_UNSPEC, |
67 | .match = statistic_mt, | 84 | .match = statistic_mt, |
68 | .checkentry = statistic_mt_check, | 85 | .checkentry = statistic_mt_check, |
86 | .destroy = statistic_mt_destroy, | ||
69 | .matchsize = sizeof(struct xt_statistic_info), | 87 | .matchsize = sizeof(struct xt_statistic_info), |
70 | .me = THIS_MODULE, | 88 | .me = THIS_MODULE, |
71 | }; | 89 | }; |