Merge git://oak/home/sfr/kernels/iseries/work
[linux-2.6] / net / ipv4 / netfilter / ipt_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  *
10  */
11 #include <linux/module.h>
12 #include <linux/skbuff.h>
13 #include <linux/netfilter_ipv4/ip_conntrack.h>
14 #include <linux/netfilter_ipv4/ip_tables.h>
15 #include <linux/netfilter_ipv4/ipt_connbytes.h>
16
17 #include <asm/div64.h>
18 #include <asm/bitops.h>
19
20 MODULE_LICENSE("GPL");
21 MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
22 MODULE_DESCRIPTION("iptables match for matching number of pkts/bytes per connection");
23
24 /* 64bit divisor, dividend and result. dynamic precision */
25 static u_int64_t div64_64(u_int64_t dividend, u_int64_t divisor)
26 {
27         u_int32_t d = divisor;
28
29         if (divisor > 0xffffffffULL) {
30                 unsigned int shift = fls(divisor >> 32);
31
32                 d = divisor >> shift;
33                 dividend >>= shift;
34         }
35
36         do_div(dividend, d);
37         return dividend;
38 }
39
40 static int
41 match(const struct sk_buff *skb,
42       const struct net_device *in,
43       const struct net_device *out,
44       const void *matchinfo,
45       int offset,
46       int *hotdrop)
47 {
48         const struct ipt_connbytes_info *sinfo = matchinfo;
49         enum ip_conntrack_info ctinfo;
50         struct ip_conntrack *ct;
51         u_int64_t what = 0;     /* initialize to make gcc happy */
52
53         if (!(ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo)))
54                 return 0; /* no match */
55
56         switch (sinfo->what) {
57         case IPT_CONNBYTES_PKTS:
58                 switch (sinfo->direction) {
59                 case IPT_CONNBYTES_DIR_ORIGINAL:
60                         what = ct->counters[IP_CT_DIR_ORIGINAL].packets;
61                         break;
62                 case IPT_CONNBYTES_DIR_REPLY:
63                         what = ct->counters[IP_CT_DIR_REPLY].packets;
64                         break;
65                 case IPT_CONNBYTES_DIR_BOTH:
66                         what = ct->counters[IP_CT_DIR_ORIGINAL].packets;
67                         what += ct->counters[IP_CT_DIR_REPLY].packets;
68                         break;
69                 }
70                 break;
71         case IPT_CONNBYTES_BYTES:
72                 switch (sinfo->direction) {
73                 case IPT_CONNBYTES_DIR_ORIGINAL:
74                         what = ct->counters[IP_CT_DIR_ORIGINAL].bytes;
75                         break;
76                 case IPT_CONNBYTES_DIR_REPLY:
77                         what = ct->counters[IP_CT_DIR_REPLY].bytes;
78                         break;
79                 case IPT_CONNBYTES_DIR_BOTH:
80                         what = ct->counters[IP_CT_DIR_ORIGINAL].bytes;
81                         what += ct->counters[IP_CT_DIR_REPLY].bytes;
82                         break;
83                 }
84                 break;
85         case IPT_CONNBYTES_AVGPKT:
86                 switch (sinfo->direction) {
87                 case IPT_CONNBYTES_DIR_ORIGINAL:
88                         what = div64_64(ct->counters[IP_CT_DIR_ORIGINAL].bytes,
89                                         ct->counters[IP_CT_DIR_ORIGINAL].packets);
90                         break;
91                 case IPT_CONNBYTES_DIR_REPLY:
92                         what = div64_64(ct->counters[IP_CT_DIR_REPLY].bytes,
93                                         ct->counters[IP_CT_DIR_REPLY].packets);
94                         break;
95                 case IPT_CONNBYTES_DIR_BOTH:
96                         {
97                                 u_int64_t bytes;
98                                 u_int64_t pkts;
99                                 bytes = ct->counters[IP_CT_DIR_ORIGINAL].bytes +
100                                         ct->counters[IP_CT_DIR_REPLY].bytes;
101                                 pkts = ct->counters[IP_CT_DIR_ORIGINAL].packets+
102                                         ct->counters[IP_CT_DIR_REPLY].packets;
103
104                                 /* FIXME_THEORETICAL: what to do if sum
105                                  * overflows ? */
106
107                                 what = div64_64(bytes, pkts);
108                         }
109                         break;
110                 }
111                 break;
112         }
113
114         if (sinfo->count.to)
115                 return (what <= sinfo->count.to && what >= sinfo->count.from);
116         else
117                 return (what >= sinfo->count.from);
118 }
119
120 static int check(const char *tablename,
121                  const struct ipt_ip *ip,
122                  void *matchinfo,
123                  unsigned int matchsize,
124                  unsigned int hook_mask)
125 {
126         const struct ipt_connbytes_info *sinfo = matchinfo;
127
128         if (matchsize != IPT_ALIGN(sizeof(struct ipt_connbytes_info)))
129                 return 0;
130
131         if (sinfo->what != IPT_CONNBYTES_PKTS &&
132             sinfo->what != IPT_CONNBYTES_BYTES &&
133             sinfo->what != IPT_CONNBYTES_AVGPKT)
134                 return 0;
135
136         if (sinfo->direction != IPT_CONNBYTES_DIR_ORIGINAL &&
137             sinfo->direction != IPT_CONNBYTES_DIR_REPLY &&
138             sinfo->direction != IPT_CONNBYTES_DIR_BOTH)
139                 return 0;
140
141         return 1;
142 }
143
144 static struct ipt_match state_match = {
145         .name           = "connbytes",
146         .match          = &match,
147         .checkentry     = &check,
148         .me             = THIS_MODULE
149 };
150
151 static int __init init(void)
152 {
153         return ipt_register_match(&state_match);
154 }
155
156 static void __exit fini(void)
157 {
158         ipt_unregister_match(&state_match);
159 }
160
161 module_init(init);
162 module_exit(fini);