diff options
author | Patrick McHardy <kaber@trash.net> | 2007-12-04 07:02:19 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 17:56:03 -0500 |
commit | 50c164a81f1c0dfad056f99e5685537fdd0f07dd (patch) | |
tree | 8f1383e3bdff0de1969dc280faf11b3e4bf01ef8 | |
parent | 5859034d7eb8793d3d78d3af515c4175e7b9d03a (diff) |
[NETFILTER]: x_tables: add rateest match
Add rate estimator match. The rate estimator match can match on
estimated rates by the RATEEST target. It supports matching on
absolute bps/pps values, comparing two rate estimators and matching
on the difference between two rate estimators.
This is what I use to route outgoing data connections from a FTP
server over two lines based on the available bandwidth:
# estimate outgoing rates
iptables -t mangle -A POSTROUTING -o eth0 -j RATEEST --rateest-name eth0 \
--rateest-interval 250ms \
--rateest-ewma 0.5s
iptables -t mangle -A POSTROUTING -o ppp0 -j RATEEST --rateest-name ppp0 \
--rateest-interval 250ms \
--rateest-ewma 0.5s
# mark based on available bandwidth
iptables -t mangle -A BALANCE -m state --state NEW \
-m helper --helper ftp \
-m rateest --rateest-delta \
--rateest1 eth0 \
--rateest-bps1 2.5mbit \
--rateest-gt \
--rateest2 ppp0 \
--rateest-bps2 2mbit \
-j CONNMARK --set-mark 0x1
iptables -t mangle -A BALANCE -m state --state NEW \
-m helper --helper ftp \
-m rateest --rateest-delta \
--rateest1 ppp0 \
--rateest-bps1 2mbit \
--rateest-gt \
--rateest2 eth0 \
--rateest-bps2 2.5mbit \
-j CONNMARK --set-mark 0x2
iptables -t mangle -A BALANCE -j CONNMARK --restore-mark
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/netfilter/Kbuild | 1 | ||||
-rw-r--r-- | include/linux/netfilter/xt_rateest.h | 33 | ||||
-rw-r--r-- | net/netfilter/Kconfig | 10 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/xt_rateest.c | 178 |
5 files changed, 223 insertions, 0 deletions
diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index 707a15854430..ac9e6429f747 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild | |||
@@ -30,6 +30,7 @@ header-y += xt_multiport.h | |||
30 | header-y += xt_owner.h | 30 | header-y += xt_owner.h |
31 | header-y += xt_pkttype.h | 31 | header-y += xt_pkttype.h |
32 | header-y += xt_policy.h | 32 | header-y += xt_policy.h |
33 | header-y += xt_rateest.h | ||
33 | header-y += xt_realm.h | 34 | header-y += xt_realm.h |
34 | header-y += xt_sctp.h | 35 | header-y += xt_sctp.h |
35 | header-y += xt_state.h | 36 | header-y += xt_state.h |
diff --git a/include/linux/netfilter/xt_rateest.h b/include/linux/netfilter/xt_rateest.h new file mode 100644 index 000000000000..51948e15aea2 --- /dev/null +++ b/include/linux/netfilter/xt_rateest.h | |||
@@ -0,0 +1,33 @@ | |||
1 | #ifndef _XT_RATEEST_MATCH_H | ||
2 | #define _XT_RATEEST_MATCH_H | ||
3 | |||
4 | enum xt_rateest_match_flags { | ||
5 | XT_RATEEST_MATCH_INVERT = 1<<0, | ||
6 | XT_RATEEST_MATCH_ABS = 1<<1, | ||
7 | XT_RATEEST_MATCH_REL = 1<<2, | ||
8 | XT_RATEEST_MATCH_DELTA = 1<<3, | ||
9 | XT_RATEEST_MATCH_BPS = 1<<4, | ||
10 | XT_RATEEST_MATCH_PPS = 1<<5, | ||
11 | }; | ||
12 | |||
13 | enum xt_rateest_match_mode { | ||
14 | XT_RATEEST_MATCH_NONE, | ||
15 | XT_RATEEST_MATCH_EQ, | ||
16 | XT_RATEEST_MATCH_LT, | ||
17 | XT_RATEEST_MATCH_GT, | ||
18 | }; | ||
19 | |||
20 | struct xt_rateest_match_info { | ||
21 | char name1[IFNAMSIZ]; | ||
22 | char name2[IFNAMSIZ]; | ||
23 | u_int16_t flags; | ||
24 | u_int16_t mode; | ||
25 | u_int32_t bps1; | ||
26 | u_int32_t pps1; | ||
27 | u_int32_t bps2; | ||
28 | u_int32_t pps2; | ||
29 | struct xt_rateest *est1 __attribute__((aligned(8))); | ||
30 | struct xt_rateest *est2 __attribute__((aligned(8))); | ||
31 | }; | ||
32 | |||
33 | #endif /* _XT_RATEEST_MATCH_H */ | ||
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 22d1f10e88b0..4182393186ae 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -631,6 +631,16 @@ config NETFILTER_XT_MATCH_QUOTA | |||
631 | If you want to compile it as a module, say M here and read | 631 | If you want to compile it as a module, say M here and read |
632 | <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. | 632 | <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. |
633 | 633 | ||
634 | config NETFILTER_XT_MATCH_RATEEST | ||
635 | tristate '"rateest" match support' | ||
636 | depends on NETFILTER_XTABLES | ||
637 | select NETFILTER_XT_TARGET_RATEEST | ||
638 | help | ||
639 | This option adds a `rateest' match, which allows to match on the | ||
640 | rate estimated by the RATEEST target. | ||
641 | |||
642 | To compile it as a module, choose M here. If unsure, say N. | ||
643 | |||
634 | config NETFILTER_XT_MATCH_REALM | 644 | config NETFILTER_XT_MATCH_REALM |
635 | tristate '"realm" match support' | 645 | tristate '"realm" match support' |
636 | depends on NETFILTER_XTABLES | 646 | depends on NETFILTER_XTABLES |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 413afaad361f..3b9ea8fb3a07 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -73,6 +73,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o | |||
73 | obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o | 73 | obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o |
74 | obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o | 74 | obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o |
75 | obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o | 75 | obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o |
76 | obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o | ||
76 | obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o | 77 | obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o |
77 | obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o | 78 | obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o |
78 | obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o | 79 | obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o |
diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c new file mode 100644 index 000000000000..fdb86a515146 --- /dev/null +++ b/net/netfilter/xt_rateest.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /* | ||
2 | * (C) 2007 Patrick McHardy <kaber@trash.net> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | #include <linux/module.h> | ||
9 | #include <linux/skbuff.h> | ||
10 | #include <linux/gen_stats.h> | ||
11 | |||
12 | #include <linux/netfilter/x_tables.h> | ||
13 | #include <linux/netfilter/xt_rateest.h> | ||
14 | #include <net/netfilter/xt_rateest.h> | ||
15 | |||
16 | |||
17 | static bool xt_rateest_mt(const struct sk_buff *skb, | ||
18 | const struct net_device *in, | ||
19 | const struct net_device *out, | ||
20 | const struct xt_match *match, | ||
21 | const void *matchinfo, | ||
22 | int offset, | ||
23 | unsigned int protoff, | ||
24 | bool *hotdrop) | ||
25 | { | ||
26 | const struct xt_rateest_match_info *info = matchinfo; | ||
27 | struct gnet_stats_rate_est *r; | ||
28 | u_int32_t bps1, bps2, pps1, pps2; | ||
29 | bool ret = true; | ||
30 | |||
31 | spin_lock_bh(&info->est1->lock); | ||
32 | r = &info->est1->rstats; | ||
33 | if (info->flags & XT_RATEEST_MATCH_DELTA) { | ||
34 | bps1 = info->bps1 >= r->bps ? info->bps1 - r->bps : 0; | ||
35 | pps1 = info->pps1 >= r->pps ? info->pps1 - r->pps : 0; | ||
36 | } else { | ||
37 | bps1 = r->bps; | ||
38 | pps1 = r->pps; | ||
39 | } | ||
40 | spin_unlock_bh(&info->est1->lock); | ||
41 | |||
42 | if (info->flags & XT_RATEEST_MATCH_ABS) { | ||
43 | bps2 = info->bps2; | ||
44 | pps2 = info->pps2; | ||
45 | } else { | ||
46 | spin_lock_bh(&info->est2->lock); | ||
47 | r = &info->est2->rstats; | ||
48 | if (info->flags & XT_RATEEST_MATCH_DELTA) { | ||
49 | bps2 = info->bps2 >= r->bps ? info->bps2 - r->bps : 0; | ||
50 | pps2 = info->pps2 >= r->pps ? info->pps2 - r->pps : 0; | ||
51 | } else { | ||
52 | bps2 = r->bps; | ||
53 | pps2 = r->pps; | ||
54 | } | ||
55 | spin_unlock_bh(&info->est2->lock); | ||
56 | } | ||
57 | |||
58 | switch (info->mode) { | ||
59 | case XT_RATEEST_MATCH_LT: | ||
60 | if (info->flags & XT_RATEEST_MATCH_BPS) | ||
61 | ret &= bps1 < bps2; | ||
62 | if (info->flags & XT_RATEEST_MATCH_PPS) | ||
63 | ret &= pps1 < pps2; | ||
64 | break; | ||
65 | case XT_RATEEST_MATCH_GT: | ||
66 | if (info->flags & XT_RATEEST_MATCH_BPS) | ||
67 | ret &= bps1 > bps2; | ||
68 | if (info->flags & XT_RATEEST_MATCH_PPS) | ||
69 | ret &= pps1 > pps2; | ||
70 | break; | ||
71 | case XT_RATEEST_MATCH_EQ: | ||
72 | if (info->flags & XT_RATEEST_MATCH_BPS) | ||
73 | ret &= bps1 == bps2; | ||
74 | if (info->flags & XT_RATEEST_MATCH_PPS) | ||
75 | ret &= pps2 == pps2; | ||
76 | break; | ||
77 | } | ||
78 | |||
79 | ret ^= info->flags & XT_RATEEST_MATCH_INVERT ? true : false; | ||
80 | return ret; | ||
81 | } | ||
82 | |||
83 | static bool xt_rateest_mt_checkentry(const char *tablename, | ||
84 | const void *ip, | ||
85 | const struct xt_match *match, | ||
86 | void *matchinfo, | ||
87 | unsigned int hook_mask) | ||
88 | { | ||
89 | struct xt_rateest_match_info *info = (void *)matchinfo; | ||
90 | struct xt_rateest *est1, *est2; | ||
91 | |||
92 | if (hweight32(info->flags & (XT_RATEEST_MATCH_ABS | | ||
93 | XT_RATEEST_MATCH_REL)) != 1) | ||
94 | goto err1; | ||
95 | |||
96 | if (!(info->flags & (XT_RATEEST_MATCH_BPS | XT_RATEEST_MATCH_PPS))) | ||
97 | goto err1; | ||
98 | |||
99 | switch (info->mode) { | ||
100 | case XT_RATEEST_MATCH_EQ: | ||
101 | case XT_RATEEST_MATCH_LT: | ||
102 | case XT_RATEEST_MATCH_GT: | ||
103 | break; | ||
104 | default: | ||
105 | goto err1; | ||
106 | } | ||
107 | |||
108 | est1 = xt_rateest_lookup(info->name1); | ||
109 | if (!est1) | ||
110 | goto err1; | ||
111 | |||
112 | if (info->flags & XT_RATEEST_MATCH_REL) { | ||
113 | est2 = xt_rateest_lookup(info->name2); | ||
114 | if (!est2) | ||
115 | goto err2; | ||
116 | } else | ||
117 | est2 = NULL; | ||
118 | |||
119 | |||
120 | info->est1 = est1; | ||
121 | info->est2 = est2; | ||
122 | return true; | ||
123 | |||
124 | err2: | ||
125 | xt_rateest_put(est1); | ||
126 | err1: | ||
127 | return false; | ||
128 | } | ||
129 | |||
130 | static void xt_rateest_mt_destroy(const struct xt_match *match, | ||
131 | void *matchinfo) | ||
132 | { | ||
133 | struct xt_rateest_match_info *info = (void *)matchinfo; | ||
134 | |||
135 | xt_rateest_put(info->est1); | ||
136 | if (info->est2) | ||
137 | xt_rateest_put(info->est2); | ||
138 | } | ||
139 | |||
140 | static struct xt_match xt_rateest_match[] __read_mostly = { | ||
141 | { | ||
142 | .family = AF_INET, | ||
143 | .name = "rateest", | ||
144 | .match = xt_rateest_mt, | ||
145 | .checkentry = xt_rateest_mt_checkentry, | ||
146 | .destroy = xt_rateest_mt_destroy, | ||
147 | .matchsize = sizeof(struct xt_rateest_match_info), | ||
148 | .me = THIS_MODULE, | ||
149 | }, | ||
150 | { | ||
151 | .family = AF_INET6, | ||
152 | .name = "rateest", | ||
153 | .match = xt_rateest_mt, | ||
154 | .checkentry = xt_rateest_mt_checkentry, | ||
155 | .destroy = xt_rateest_mt_destroy, | ||
156 | .matchsize = sizeof(struct xt_rateest_match_info), | ||
157 | .me = THIS_MODULE, | ||
158 | }, | ||
159 | }; | ||
160 | |||
161 | static int __init xt_rateest_mt_init(void) | ||
162 | { | ||
163 | return xt_register_matches(xt_rateest_match, | ||
164 | ARRAY_SIZE(xt_rateest_match)); | ||
165 | } | ||
166 | |||
167 | static void __exit xt_rateest_mt_fini(void) | ||
168 | { | ||
169 | xt_unregister_matches(xt_rateest_match, ARRAY_SIZE(xt_rateest_match)); | ||
170 | } | ||
171 | |||
172 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | ||
173 | MODULE_LICENSE("GPL"); | ||
174 | MODULE_DESCRIPTION("xtables rate estimator match"); | ||
175 | MODULE_ALIAS("ipt_rateest"); | ||
176 | MODULE_ALIAS("ip6t_rateest"); | ||
177 | module_init(xt_rateest_mt_init); | ||
178 | module_exit(xt_rateest_mt_fini); | ||