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