diff options
author | Harald Welte <laforge@netfilter.org> | 2005-08-13 16:56:26 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2005-08-29 18:58:04 -0400 |
commit | 9d810fd2d28a9d672eca3136476af1a54a380bb2 (patch) | |
tree | 1eaa57851bbc1eeee094b92c539de8e7509aa297 | |
parent | 0ba2c6e8c0fb5cde5a23a213c2e7cb851b85c310 (diff) |
[NETFILTER]: Add new iptables "connbytes" match
This patch ads a new "connbytes" match that utilizes the CONFIG_NF_CT_ACCT
per-connection byte and packet counters. Using it you can do things like
packet classification on average packet size within a connection.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/netfilter_ipv4/ipt_connbytes.h | 25 | ||||
-rw-r--r-- | net/ipv4/netfilter/Kconfig | 11 | ||||
-rw-r--r-- | net/ipv4/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/ipv4/netfilter/ipt_connbytes.c | 166 |
4 files changed, 202 insertions, 1 deletions
diff --git a/include/linux/netfilter_ipv4/ipt_connbytes.h b/include/linux/netfilter_ipv4/ipt_connbytes.h new file mode 100644 index 000000000000..abaa65afd4e9 --- /dev/null +++ b/include/linux/netfilter_ipv4/ipt_connbytes.h | |||
@@ -0,0 +1,25 @@ | |||
1 | #ifndef _IPT_CONNBYTES_H | ||
2 | #define _IPT_CONNBYTES_H | ||
3 | |||
4 | enum ipt_connbytes_what { | ||
5 | IPT_CONNBYTES_WHAT_PKTS, | ||
6 | IPT_CONNBYTES_WHAT_BYTES, | ||
7 | IPT_CONNBYTES_WHAT_AVGPKT, | ||
8 | }; | ||
9 | |||
10 | enum ipt_connbytes_direction { | ||
11 | IPT_CONNBYTES_DIR_ORIGINAL, | ||
12 | IPT_CONNBYTES_DIR_REPLY, | ||
13 | IPT_CONNBYTES_DIR_BOTH, | ||
14 | }; | ||
15 | |||
16 | struct ipt_connbytes_info | ||
17 | { | ||
18 | struct { | ||
19 | aligned_u64 from; /* count to be matched */ | ||
20 | aligned_u64 to; /* count to be matched */ | ||
21 | } count; | ||
22 | u_int8_t what; /* ipt_connbytes_what */ | ||
23 | u_int8_t direction; /* ipt_connbytes_direction */ | ||
24 | }; | ||
25 | #endif | ||
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 9f5e1d769b5f..3f7e6e49cbdd 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig | |||
@@ -386,6 +386,16 @@ config IP_NF_MATCH_CONNMARK | |||
386 | <file:Documentation/modules.txt>. The module will be called | 386 | <file:Documentation/modules.txt>. The module will be called |
387 | ipt_connmark.o. If unsure, say `N'. | 387 | ipt_connmark.o. If unsure, say `N'. |
388 | 388 | ||
389 | config IP_NF_MATCH_CONNBYTES | ||
390 | tristate 'Connection byte/packet counter match support' | ||
391 | depends on IP_NF_CT_ACCT && IP_NF_IPTABLES | ||
392 | help | ||
393 | This option adds a `connbytes' match, which allows you to match the | ||
394 | number of bytes and/or packets for each direction within a connection. | ||
395 | |||
396 | If you want to compile it as a module, say M here and read | ||
397 | <file:Documentation/modules.txt>. If unsure, say `N'. | ||
398 | |||
389 | config IP_NF_MATCH_HASHLIMIT | 399 | config IP_NF_MATCH_HASHLIMIT |
390 | tristate 'hashlimit match support' | 400 | tristate 'hashlimit match support' |
391 | depends on IP_NF_IPTABLES | 401 | depends on IP_NF_IPTABLES |
@@ -723,6 +733,5 @@ config IP_NF_CONNTRACK_NETLINK | |||
723 | help | 733 | help |
724 | This option enables support for a netlink-based userspace interface | 734 | This option enables support for a netlink-based userspace interface |
725 | 735 | ||
726 | |||
727 | endmenu | 736 | endmenu |
728 | 737 | ||
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 58aa7c616e1f..7c8ae858aa43 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile | |||
@@ -59,6 +59,7 @@ obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o | |||
59 | obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o | 59 | obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o |
60 | obj-$(CONFIG_IP_NF_MATCH_CONNMARK) += ipt_connmark.o | 60 | obj-$(CONFIG_IP_NF_MATCH_CONNMARK) += ipt_connmark.o |
61 | obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o | 61 | obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o |
62 | obj-$(CONFIG_IP_NF_MATCH_CONNBYTES) += ipt_connbytes.o | ||
62 | obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o | 63 | obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o |
63 | obj-$(CONFIG_IP_NF_MATCH_REALM) += ipt_realm.o | 64 | obj-$(CONFIG_IP_NF_MATCH_REALM) += ipt_realm.o |
64 | obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o | 65 | obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o |
diff --git a/net/ipv4/netfilter/ipt_connbytes.c b/net/ipv4/netfilter/ipt_connbytes.c new file mode 100644 index 000000000000..0dfb52c0e808 --- /dev/null +++ b/net/ipv4/netfilter/ipt_connbytes.c | |||
@@ -0,0 +1,166 @@ | |||
1 | /* Kernel module to match connection tracking byte counter. | ||
2 | * GPL (C) 2002 Martin Devera (devik@cdi.cz). | ||
3 | * | ||
4 | * 2004-07-20 Harald Welte <laforge@netfilter.org> | ||
5 | * - reimplemented to use per-connection accounting counters | ||
6 | * - add functionality to match number of packets | ||
7 | * - add functionality to match average packet size | ||
8 | * - add support to match directions seperately | ||
9 | * | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/skbuff.h> | ||
13 | #include <linux/netfilter_ipv4/ip_conntrack.h> | ||
14 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
15 | #include <linux/netfilter_ipv4/ipt_connbytes.h> | ||
16 | |||
17 | #include <asm/div64.h> | ||
18 | #include <asm/bitops.h> | ||
19 | |||
20 | MODULE_LICENSE("GPL"); | ||
21 | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); | ||
22 | MODULE_DESCRIPTION("iptables match for matching number of pkts/bytes per connection"); | ||
23 | |||
24 | /* 64bit divisor, dividend and result. dynamic precision */ | ||
25 | static u_int64_t div64_64(u_int64_t divisor, u_int64_t dividend) | ||
26 | { | ||
27 | u_int64_t result = divisor; | ||
28 | |||
29 | if (dividend > 0xffffffff) { | ||
30 | int first_bit = find_first_bit((unsigned long *) ÷nd, sizeof(dividend)); | ||
31 | /* calculate number of bits to shift. shift exactly enough | ||
32 | * bits to make dividend fit in 32bits. */ | ||
33 | int num_shift = (64 - 32 - first_bit); | ||
34 | /* first bit has to be < 32, since dividend was > 0xffffffff */ | ||
35 | result = result >> num_shift; | ||
36 | dividend = dividend >> num_shift; | ||
37 | } | ||
38 | |||
39 | do_div(divisor, dividend); | ||
40 | |||
41 | return divisor; | ||
42 | } | ||
43 | |||
44 | static int | ||
45 | match(const struct sk_buff *skb, | ||
46 | const struct net_device *in, | ||
47 | const struct net_device *out, | ||
48 | const void *matchinfo, | ||
49 | int offset, | ||
50 | int *hotdrop) | ||
51 | { | ||
52 | const struct ipt_connbytes_info *sinfo = matchinfo; | ||
53 | enum ip_conntrack_info ctinfo; | ||
54 | struct ip_conntrack *ct; | ||
55 | u_int64_t what = 0; /* initialize to make gcc happy */ | ||
56 | |||
57 | if (!(ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo))) | ||
58 | return 0; /* no match */ | ||
59 | |||
60 | switch (sinfo->what) { | ||
61 | case IPT_CONNBYTES_WHAT_PKTS: | ||
62 | switch (sinfo->direction) { | ||
63 | case IPT_CONNBYTES_DIR_ORIGINAL: | ||
64 | what = ct->counters[IP_CT_DIR_ORIGINAL].packets; | ||
65 | break; | ||
66 | case IPT_CONNBYTES_DIR_REPLY: | ||
67 | what = ct->counters[IP_CT_DIR_REPLY].packets; | ||
68 | break; | ||
69 | case IPT_CONNBYTES_DIR_BOTH: | ||
70 | what = ct->counters[IP_CT_DIR_ORIGINAL].packets; | ||
71 | what += ct->counters[IP_CT_DIR_REPLY].packets; | ||
72 | break; | ||
73 | } | ||
74 | break; | ||
75 | case IPT_CONNBYTES_WHAT_BYTES: | ||
76 | switch (sinfo->direction) { | ||
77 | case IPT_CONNBYTES_DIR_ORIGINAL: | ||
78 | what = ct->counters[IP_CT_DIR_ORIGINAL].bytes; | ||
79 | break; | ||
80 | case IPT_CONNBYTES_DIR_REPLY: | ||
81 | what = ct->counters[IP_CT_DIR_REPLY].bytes; | ||
82 | break; | ||
83 | case IPT_CONNBYTES_DIR_BOTH: | ||
84 | what = ct->counters[IP_CT_DIR_ORIGINAL].bytes; | ||
85 | what += ct->counters[IP_CT_DIR_REPLY].bytes; | ||
86 | break; | ||
87 | } | ||
88 | break; | ||
89 | case IPT_CONNBYTES_WHAT_AVGPKT: | ||
90 | switch (sinfo->direction) { | ||
91 | case IPT_CONNBYTES_DIR_ORIGINAL: | ||
92 | what = div64_64(ct->counters[IP_CT_DIR_ORIGINAL].bytes, | ||
93 | ct->counters[IP_CT_DIR_ORIGINAL].packets); | ||
94 | break; | ||
95 | case IPT_CONNBYTES_DIR_REPLY: | ||
96 | what = div64_64(ct->counters[IP_CT_DIR_REPLY].bytes, | ||
97 | ct->counters[IP_CT_DIR_REPLY].packets); | ||
98 | break; | ||
99 | case IPT_CONNBYTES_DIR_BOTH: | ||
100 | { | ||
101 | u_int64_t bytes; | ||
102 | u_int64_t pkts; | ||
103 | bytes = ct->counters[IP_CT_DIR_ORIGINAL].bytes + | ||
104 | ct->counters[IP_CT_DIR_REPLY].bytes; | ||
105 | pkts = ct->counters[IP_CT_DIR_ORIGINAL].packets+ | ||
106 | ct->counters[IP_CT_DIR_REPLY].packets; | ||
107 | |||
108 | /* FIXME_THEORETICAL: what to do if sum | ||
109 | * overflows ? */ | ||
110 | |||
111 | what = div64_64(bytes, pkts); | ||
112 | } | ||
113 | break; | ||
114 | } | ||
115 | break; | ||
116 | } | ||
117 | |||
118 | if (sinfo->count.to) | ||
119 | return (what <= sinfo->count.to && what >= sinfo->count.from); | ||
120 | else | ||
121 | return (what >= sinfo->count.from); | ||
122 | } | ||
123 | |||
124 | static int check(const char *tablename, | ||
125 | const struct ipt_ip *ip, | ||
126 | void *matchinfo, | ||
127 | unsigned int matchsize, | ||
128 | unsigned int hook_mask) | ||
129 | { | ||
130 | const struct ipt_connbytes_info *sinfo = matchinfo; | ||
131 | |||
132 | if (matchsize != IPT_ALIGN(sizeof(struct ipt_connbytes_info))) | ||
133 | return 0; | ||
134 | |||
135 | if (sinfo->what != IPT_CONNBYTES_WHAT_PKTS && | ||
136 | sinfo->what != IPT_CONNBYTES_WHAT_BYTES && | ||
137 | sinfo->what != IPT_CONNBYTES_WHAT_AVGPKT) | ||
138 | return 0; | ||
139 | |||
140 | if (sinfo->direction != IPT_CONNBYTES_DIR_ORIGINAL && | ||
141 | sinfo->direction != IPT_CONNBYTES_DIR_REPLY && | ||
142 | sinfo->direction != IPT_CONNBYTES_DIR_BOTH) | ||
143 | return 0; | ||
144 | |||
145 | return 1; | ||
146 | } | ||
147 | |||
148 | static struct ipt_match state_match = { | ||
149 | .name = "connbytes", | ||
150 | .match = &match, | ||
151 | .checkentry = &check, | ||
152 | .me = THIS_MODULE | ||
153 | }; | ||
154 | |||
155 | static int __init init(void) | ||
156 | { | ||
157 | return ipt_register_match(&state_match); | ||
158 | } | ||
159 | |||
160 | static void __exit fini(void) | ||
161 | { | ||
162 | ipt_unregister_match(&state_match); | ||
163 | } | ||
164 | |||
165 | module_init(init); | ||
166 | module_exit(fini); | ||