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