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 */ |
fb74a841 PM |
55 | u_int64_t bytes = 0; |
56 | u_int64_t pkts = 0; | |
9fb9cbb1 | 57 | const struct ip_conntrack_counter *counters; |
9d810fd2 | 58 | |
9fb9cbb1 | 59 | if (!(counters = nf_ct_get_counters(skb))) |
9d810fd2 HW |
60 | return 0; /* no match */ |
61 | ||
62 | switch (sinfo->what) { | |
2e4e6a17 | 63 | case XT_CONNBYTES_PKTS: |
9d810fd2 | 64 | switch (sinfo->direction) { |
2e4e6a17 | 65 | case XT_CONNBYTES_DIR_ORIGINAL: |
9fb9cbb1 | 66 | what = counters[IP_CT_DIR_ORIGINAL].packets; |
9d810fd2 | 67 | break; |
2e4e6a17 | 68 | case XT_CONNBYTES_DIR_REPLY: |
9fb9cbb1 | 69 | what = counters[IP_CT_DIR_REPLY].packets; |
9d810fd2 | 70 | break; |
2e4e6a17 | 71 | case XT_CONNBYTES_DIR_BOTH: |
9fb9cbb1 YK |
72 | what = counters[IP_CT_DIR_ORIGINAL].packets; |
73 | what += counters[IP_CT_DIR_REPLY].packets; | |
9d810fd2 HW |
74 | break; |
75 | } | |
76 | break; | |
2e4e6a17 | 77 | case XT_CONNBYTES_BYTES: |
9d810fd2 | 78 | switch (sinfo->direction) { |
2e4e6a17 | 79 | case XT_CONNBYTES_DIR_ORIGINAL: |
9fb9cbb1 | 80 | what = counters[IP_CT_DIR_ORIGINAL].bytes; |
9d810fd2 | 81 | break; |
2e4e6a17 | 82 | case XT_CONNBYTES_DIR_REPLY: |
9fb9cbb1 | 83 | what = counters[IP_CT_DIR_REPLY].bytes; |
9d810fd2 | 84 | break; |
2e4e6a17 | 85 | case XT_CONNBYTES_DIR_BOTH: |
9fb9cbb1 YK |
86 | what = counters[IP_CT_DIR_ORIGINAL].bytes; |
87 | what += counters[IP_CT_DIR_REPLY].bytes; | |
9d810fd2 HW |
88 | break; |
89 | } | |
90 | break; | |
2e4e6a17 | 91 | case XT_CONNBYTES_AVGPKT: |
9d810fd2 | 92 | switch (sinfo->direction) { |
2e4e6a17 | 93 | case XT_CONNBYTES_DIR_ORIGINAL: |
fb74a841 PM |
94 | bytes = counters[IP_CT_DIR_ORIGINAL].bytes; |
95 | pkts = counters[IP_CT_DIR_ORIGINAL].packets; | |
9d810fd2 | 96 | break; |
2e4e6a17 | 97 | case XT_CONNBYTES_DIR_REPLY: |
fb74a841 PM |
98 | bytes = counters[IP_CT_DIR_REPLY].bytes; |
99 | pkts = counters[IP_CT_DIR_REPLY].packets; | |
9d810fd2 | 100 | break; |
2e4e6a17 | 101 | case XT_CONNBYTES_DIR_BOTH: |
fb74a841 PM |
102 | bytes = counters[IP_CT_DIR_ORIGINAL].bytes + |
103 | counters[IP_CT_DIR_REPLY].bytes; | |
104 | pkts = counters[IP_CT_DIR_ORIGINAL].packets + | |
105 | counters[IP_CT_DIR_REPLY].packets; | |
9d810fd2 HW |
106 | break; |
107 | } | |
fb74a841 PM |
108 | if (pkts != 0) |
109 | what = div64_64(bytes, pkts); | |
9d810fd2 HW |
110 | break; |
111 | } | |
112 | ||
113 | if (sinfo->count.to) | |
114 | return (what <= sinfo->count.to && what >= sinfo->count.from); | |
115 | else | |
116 | return (what >= sinfo->count.from); | |
117 | } | |
118 | ||
119 | static int check(const char *tablename, | |
2e4e6a17 | 120 | const void *ip, |
c4986734 | 121 | const struct xt_match *match, |
9d810fd2 | 122 | void *matchinfo, |
9d810fd2 HW |
123 | unsigned int hook_mask) |
124 | { | |
2e4e6a17 | 125 | const struct xt_connbytes_info *sinfo = matchinfo; |
9d810fd2 | 126 | |
2e4e6a17 HW |
127 | if (sinfo->what != XT_CONNBYTES_PKTS && |
128 | sinfo->what != XT_CONNBYTES_BYTES && | |
129 | sinfo->what != XT_CONNBYTES_AVGPKT) | |
9d810fd2 HW |
130 | return 0; |
131 | ||
2e4e6a17 HW |
132 | if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL && |
133 | sinfo->direction != XT_CONNBYTES_DIR_REPLY && | |
134 | sinfo->direction != XT_CONNBYTES_DIR_BOTH) | |
9d810fd2 HW |
135 | return 0; |
136 | ||
11078c37 YK |
137 | if (nf_ct_l3proto_try_module_get(match->family) < 0) { |
138 | printk(KERN_WARNING "can't load conntrack support for " | |
139 | "proto=%d\n", match->family); | |
140 | return 0; | |
141 | } | |
142 | ||
9d810fd2 HW |
143 | return 1; |
144 | } | |
145 | ||
11078c37 YK |
146 | static void |
147 | destroy(const struct xt_match *match, void *matchinfo) | |
148 | { | |
149 | nf_ct_l3proto_module_put(match->family); | |
150 | } | |
151 | ||
fe1cb108 | 152 | static struct xt_match xt_connbytes_match[] = { |
4470bbc7 PM |
153 | { |
154 | .name = "connbytes", | |
155 | .family = AF_INET, | |
156 | .checkentry = check, | |
157 | .match = match, | |
11078c37 | 158 | .destroy = destroy, |
4470bbc7 PM |
159 | .matchsize = sizeof(struct xt_connbytes_info), |
160 | .me = THIS_MODULE | |
161 | }, | |
162 | { | |
163 | .name = "connbytes", | |
164 | .family = AF_INET6, | |
165 | .checkentry = check, | |
166 | .match = match, | |
11078c37 | 167 | .destroy = destroy, |
4470bbc7 PM |
168 | .matchsize = sizeof(struct xt_connbytes_info), |
169 | .me = THIS_MODULE | |
170 | }, | |
9d810fd2 HW |
171 | }; |
172 | ||
65b4b4e8 | 173 | static int __init xt_connbytes_init(void) |
9d810fd2 | 174 | { |
4470bbc7 PM |
175 | return xt_register_matches(xt_connbytes_match, |
176 | ARRAY_SIZE(xt_connbytes_match)); | |
9d810fd2 HW |
177 | } |
178 | ||
65b4b4e8 | 179 | static void __exit xt_connbytes_fini(void) |
9d810fd2 | 180 | { |
4470bbc7 PM |
181 | xt_unregister_matches(xt_connbytes_match, |
182 | ARRAY_SIZE(xt_connbytes_match)); | |
9d810fd2 HW |
183 | } |
184 | ||
65b4b4e8 AM |
185 | module_init(xt_connbytes_init); |
186 | module_exit(xt_connbytes_fini); |