Commit | Line | Data |
---|---|---|
9d810fd2 HW |
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 | |
2e4e6a17 HW |
9 | * 2005-10-16 Harald Welte <laforge@netfilter.org> |
10 | * - Port to x_tables | |
9d810fd2 HW |
11 | * |
12 | */ | |
13 | #include <linux/module.h> | |
14 | #include <linux/skbuff.h> | |
9fb9cbb1 | 15 | #include <net/netfilter/nf_conntrack_compat.h> |
2e4e6a17 HW |
16 | #include <linux/netfilter/x_tables.h> |
17 | #include <linux/netfilter/xt_connbytes.h> | |
9d810fd2 HW |
18 | |
19 | #include <asm/div64.h> | |
20 | #include <asm/bitops.h> | |
21 | ||
22 | MODULE_LICENSE("GPL"); | |
23 | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); | |
24 | MODULE_DESCRIPTION("iptables match for matching number of pkts/bytes per connection"); | |
2e4e6a17 | 25 | MODULE_ALIAS("ipt_connbytes"); |
9d810fd2 HW |
26 | |
27 | /* 64bit divisor, dividend and result. dynamic precision */ | |
8ffde671 | 28 | static u_int64_t div64_64(u_int64_t dividend, u_int64_t divisor) |
9d810fd2 | 29 | { |
8ffde671 | 30 | u_int32_t d = divisor; |
9d810fd2 | 31 | |
8ffde671 PM |
32 | if (divisor > 0xffffffffULL) { |
33 | unsigned int shift = fls(divisor >> 32); | |
9d810fd2 | 34 | |
8ffde671 PM |
35 | d = divisor >> shift; |
36 | dividend >>= shift; | |
37 | } | |
9d810fd2 | 38 | |
8ffde671 PM |
39 | do_div(dividend, d); |
40 | return dividend; | |
9d810fd2 HW |
41 | } |
42 | ||
43 | static int | |
44 | match(const struct sk_buff *skb, | |
45 | const struct net_device *in, | |
46 | const struct net_device *out, | |
c4986734 | 47 | const struct xt_match *match, |
9d810fd2 HW |
48 | const void *matchinfo, |
49 | int offset, | |
2e4e6a17 | 50 | unsigned int protoff, |
9d810fd2 HW |
51 | int *hotdrop) |
52 | { | |
2e4e6a17 | 53 | const struct xt_connbytes_info *sinfo = matchinfo; |
9d810fd2 | 54 | u_int64_t what = 0; /* initialize to make gcc happy */ |
9fb9cbb1 | 55 | const struct ip_conntrack_counter *counters; |
9d810fd2 | 56 | |
9fb9cbb1 | 57 | if (!(counters = nf_ct_get_counters(skb))) |
9d810fd2 HW |
58 | return 0; /* no match */ |
59 | ||
60 | switch (sinfo->what) { | |
2e4e6a17 | 61 | case XT_CONNBYTES_PKTS: |
9d810fd2 | 62 | switch (sinfo->direction) { |
2e4e6a17 | 63 | case XT_CONNBYTES_DIR_ORIGINAL: |
9fb9cbb1 | 64 | what = counters[IP_CT_DIR_ORIGINAL].packets; |
9d810fd2 | 65 | break; |
2e4e6a17 | 66 | case XT_CONNBYTES_DIR_REPLY: |
9fb9cbb1 | 67 | what = counters[IP_CT_DIR_REPLY].packets; |
9d810fd2 | 68 | break; |
2e4e6a17 | 69 | case XT_CONNBYTES_DIR_BOTH: |
9fb9cbb1 YK |
70 | what = counters[IP_CT_DIR_ORIGINAL].packets; |
71 | what += counters[IP_CT_DIR_REPLY].packets; | |
9d810fd2 HW |
72 | break; |
73 | } | |
74 | break; | |
2e4e6a17 | 75 | case XT_CONNBYTES_BYTES: |
9d810fd2 | 76 | switch (sinfo->direction) { |
2e4e6a17 | 77 | case XT_CONNBYTES_DIR_ORIGINAL: |
9fb9cbb1 | 78 | what = counters[IP_CT_DIR_ORIGINAL].bytes; |
9d810fd2 | 79 | break; |
2e4e6a17 | 80 | case XT_CONNBYTES_DIR_REPLY: |
9fb9cbb1 | 81 | what = counters[IP_CT_DIR_REPLY].bytes; |
9d810fd2 | 82 | break; |
2e4e6a17 | 83 | case XT_CONNBYTES_DIR_BOTH: |
9fb9cbb1 YK |
84 | what = counters[IP_CT_DIR_ORIGINAL].bytes; |
85 | what += counters[IP_CT_DIR_REPLY].bytes; | |
9d810fd2 HW |
86 | break; |
87 | } | |
88 | break; | |
2e4e6a17 | 89 | case XT_CONNBYTES_AVGPKT: |
9d810fd2 | 90 | switch (sinfo->direction) { |
2e4e6a17 | 91 | case XT_CONNBYTES_DIR_ORIGINAL: |
9fb9cbb1 YK |
92 | what = div64_64(counters[IP_CT_DIR_ORIGINAL].bytes, |
93 | counters[IP_CT_DIR_ORIGINAL].packets); | |
9d810fd2 | 94 | break; |
2e4e6a17 | 95 | case XT_CONNBYTES_DIR_REPLY: |
9fb9cbb1 YK |
96 | what = div64_64(counters[IP_CT_DIR_REPLY].bytes, |
97 | counters[IP_CT_DIR_REPLY].packets); | |
9d810fd2 | 98 | break; |
2e4e6a17 | 99 | case XT_CONNBYTES_DIR_BOTH: |
9d810fd2 HW |
100 | { |
101 | u_int64_t bytes; | |
102 | u_int64_t pkts; | |
9fb9cbb1 YK |
103 | bytes = counters[IP_CT_DIR_ORIGINAL].bytes + |
104 | counters[IP_CT_DIR_REPLY].bytes; | |
105 | pkts = counters[IP_CT_DIR_ORIGINAL].packets+ | |
106 | counters[IP_CT_DIR_REPLY].packets; | |
9d810fd2 HW |
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, | |
2e4e6a17 | 125 | const void *ip, |
c4986734 | 126 | const struct xt_match *match, |
9d810fd2 HW |
127 | void *matchinfo, |
128 | unsigned int matchsize, | |
129 | unsigned int hook_mask) | |
130 | { | |
2e4e6a17 | 131 | const struct xt_connbytes_info *sinfo = matchinfo; |
9d810fd2 | 132 | |
2e4e6a17 HW |
133 | if (sinfo->what != XT_CONNBYTES_PKTS && |
134 | sinfo->what != XT_CONNBYTES_BYTES && | |
135 | sinfo->what != XT_CONNBYTES_AVGPKT) | |
9d810fd2 HW |
136 | return 0; |
137 | ||
2e4e6a17 HW |
138 | if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL && |
139 | sinfo->direction != XT_CONNBYTES_DIR_REPLY && | |
140 | sinfo->direction != XT_CONNBYTES_DIR_BOTH) | |
9d810fd2 HW |
141 | return 0; |
142 | ||
143 | return 1; | |
144 | } | |
145 | ||
2e4e6a17 HW |
146 | static struct xt_match connbytes_match = { |
147 | .name = "connbytes", | |
5d04bff0 PM |
148 | .match = match, |
149 | .checkentry = check, | |
150 | .matchsize = sizeof(struct xt_connbytes_info), | |
a45049c5 | 151 | .family = AF_INET, |
2e4e6a17 HW |
152 | .me = THIS_MODULE |
153 | }; | |
154 | static struct xt_match connbytes6_match = { | |
9d810fd2 | 155 | .name = "connbytes", |
5d04bff0 PM |
156 | .match = match, |
157 | .checkentry = check, | |
158 | .matchsize = sizeof(struct xt_connbytes_info), | |
a45049c5 | 159 | .family = AF_INET6, |
9d810fd2 HW |
160 | .me = THIS_MODULE |
161 | }; | |
162 | ||
65b4b4e8 | 163 | static int __init xt_connbytes_init(void) |
9d810fd2 | 164 | { |
2e4e6a17 | 165 | int ret; |
a45049c5 | 166 | ret = xt_register_match(&connbytes_match); |
2e4e6a17 HW |
167 | if (ret) |
168 | return ret; | |
169 | ||
a45049c5 | 170 | ret = xt_register_match(&connbytes6_match); |
2e4e6a17 | 171 | if (ret) |
a45049c5 | 172 | xt_unregister_match(&connbytes_match); |
2e4e6a17 | 173 | return ret; |
9d810fd2 HW |
174 | } |
175 | ||
65b4b4e8 | 176 | static void __exit xt_connbytes_fini(void) |
9d810fd2 | 177 | { |
a45049c5 PNA |
178 | xt_unregister_match(&connbytes_match); |
179 | xt_unregister_match(&connbytes6_match); | |
9d810fd2 HW |
180 | } |
181 | ||
65b4b4e8 AM |
182 | module_init(xt_connbytes_init); |
183 | module_exit(xt_connbytes_fini); |