Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq
[sfrench/cifs-2.6.git] / 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 hook_mask)
129 {
130         const struct xt_connbytes_info *sinfo = matchinfo;
131
132         if (sinfo->what != XT_CONNBYTES_PKTS &&
133             sinfo->what != XT_CONNBYTES_BYTES &&
134             sinfo->what != XT_CONNBYTES_AVGPKT)
135                 return 0;
136
137         if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL &&
138             sinfo->direction != XT_CONNBYTES_DIR_REPLY &&
139             sinfo->direction != XT_CONNBYTES_DIR_BOTH)
140                 return 0;
141
142         if (nf_ct_l3proto_try_module_get(match->family) < 0) {
143                 printk(KERN_WARNING "can't load conntrack support for "
144                                     "proto=%d\n", match->family);
145                 return 0;
146         }
147
148         return 1;
149 }
150
151 static void
152 destroy(const struct xt_match *match, void *matchinfo)
153 {
154         nf_ct_l3proto_module_put(match->family);
155 }
156
157 static struct xt_match xt_connbytes_match[] = {
158         {
159                 .name           = "connbytes",
160                 .family         = AF_INET,
161                 .checkentry     = check,
162                 .match          = match,
163                 .destroy        = destroy,
164                 .matchsize      = sizeof(struct xt_connbytes_info),
165                 .me             = THIS_MODULE
166         },
167         {
168                 .name           = "connbytes",
169                 .family         = AF_INET6,
170                 .checkentry     = check,
171                 .match          = match,
172                 .destroy        = destroy,
173                 .matchsize      = sizeof(struct xt_connbytes_info),
174                 .me             = THIS_MODULE
175         },
176 };
177
178 static int __init xt_connbytes_init(void)
179 {
180         return xt_register_matches(xt_connbytes_match,
181                                    ARRAY_SIZE(xt_connbytes_match));
182 }
183
184 static void __exit xt_connbytes_fini(void)
185 {
186         xt_unregister_matches(xt_connbytes_match,
187                               ARRAY_SIZE(xt_connbytes_match));
188 }
189
190 module_init(xt_connbytes_init);
191 module_exit(xt_connbytes_fini);