Merge master.kernel.org:/home/rmk/linux-2.6-serial
[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 void *matchinfo,
48       int offset,
49       unsigned int protoff,
50       int *hotdrop)
51 {
52         const struct xt_connbytes_info *sinfo = matchinfo;
53         u_int64_t what = 0;     /* initialize to make gcc happy */
54         const struct ip_conntrack_counter *counters;
55
56         if (!(counters = nf_ct_get_counters(skb)))
57                 return 0; /* no match */
58
59         switch (sinfo->what) {
60         case XT_CONNBYTES_PKTS:
61                 switch (sinfo->direction) {
62                 case XT_CONNBYTES_DIR_ORIGINAL:
63                         what = counters[IP_CT_DIR_ORIGINAL].packets;
64                         break;
65                 case XT_CONNBYTES_DIR_REPLY:
66                         what = counters[IP_CT_DIR_REPLY].packets;
67                         break;
68                 case XT_CONNBYTES_DIR_BOTH:
69                         what = counters[IP_CT_DIR_ORIGINAL].packets;
70                         what += counters[IP_CT_DIR_REPLY].packets;
71                         break;
72                 }
73                 break;
74         case XT_CONNBYTES_BYTES:
75                 switch (sinfo->direction) {
76                 case XT_CONNBYTES_DIR_ORIGINAL:
77                         what = counters[IP_CT_DIR_ORIGINAL].bytes;
78                         break;
79                 case XT_CONNBYTES_DIR_REPLY:
80                         what = counters[IP_CT_DIR_REPLY].bytes;
81                         break;
82                 case XT_CONNBYTES_DIR_BOTH:
83                         what = counters[IP_CT_DIR_ORIGINAL].bytes;
84                         what += counters[IP_CT_DIR_REPLY].bytes;
85                         break;
86                 }
87                 break;
88         case XT_CONNBYTES_AVGPKT:
89                 switch (sinfo->direction) {
90                 case XT_CONNBYTES_DIR_ORIGINAL:
91                         what = div64_64(counters[IP_CT_DIR_ORIGINAL].bytes,
92                                         counters[IP_CT_DIR_ORIGINAL].packets);
93                         break;
94                 case XT_CONNBYTES_DIR_REPLY:
95                         what = div64_64(counters[IP_CT_DIR_REPLY].bytes,
96                                         counters[IP_CT_DIR_REPLY].packets);
97                         break;
98                 case XT_CONNBYTES_DIR_BOTH:
99                         {
100                                 u_int64_t bytes;
101                                 u_int64_t pkts;
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;
106
107                                 /* FIXME_THEORETICAL: what to do if sum
108                                  * overflows ? */
109
110                                 what = div64_64(bytes, pkts);
111                         }
112                         break;
113                 }
114                 break;
115         }
116
117         if (sinfo->count.to)
118                 return (what <= sinfo->count.to && what >= sinfo->count.from);
119         else
120                 return (what >= sinfo->count.from);
121 }
122
123 static int check(const char *tablename,
124                  const void *ip,
125                  void *matchinfo,
126                  unsigned int matchsize,
127                  unsigned int hook_mask)
128 {
129         const struct xt_connbytes_info *sinfo = matchinfo;
130
131         if (matchsize != XT_ALIGN(sizeof(struct xt_connbytes_info)))
132                 return 0;
133
134         if (sinfo->what != XT_CONNBYTES_PKTS &&
135             sinfo->what != XT_CONNBYTES_BYTES &&
136             sinfo->what != XT_CONNBYTES_AVGPKT)
137                 return 0;
138
139         if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL &&
140             sinfo->direction != XT_CONNBYTES_DIR_REPLY &&
141             sinfo->direction != XT_CONNBYTES_DIR_BOTH)
142                 return 0;
143
144         return 1;
145 }
146
147 static struct xt_match connbytes_match = {
148         .name           = "connbytes",
149         .match          = &match,
150         .checkentry     = &check,
151         .me             = THIS_MODULE
152 };
153 static struct xt_match connbytes6_match = {
154         .name           = "connbytes",
155         .match          = &match,
156         .checkentry     = &check,
157         .me             = THIS_MODULE
158 };
159
160 static int __init init(void)
161 {
162         int ret;
163         ret = xt_register_match(AF_INET, &connbytes_match);
164         if (ret)
165                 return ret;
166
167         ret = xt_register_match(AF_INET6, &connbytes6_match);
168         if (ret)
169                 xt_unregister_match(AF_INET, &connbytes_match);
170         return ret;
171 }
172
173 static void __exit fini(void)
174 {
175         xt_unregister_match(AF_INET, &connbytes_match);
176         xt_unregister_match(AF_INET6, &connbytes6_match);
177 }
178
179 module_init(init);
180 module_exit(fini);