[INET_DIAG]: Introduce sk_diag_fill
[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 <net/netfilter/nf_conntrack_compat.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         u_int64_t what = 0;     /* initialize to make gcc happy */
50         const struct ip_conntrack_counter *counters;
51
52         if (!(counters = nf_ct_get_counters(skb)))
53                 return 0; /* no match */
54
55         switch (sinfo->what) {
56         case IPT_CONNBYTES_PKTS:
57                 switch (sinfo->direction) {
58                 case IPT_CONNBYTES_DIR_ORIGINAL:
59                         what = counters[IP_CT_DIR_ORIGINAL].packets;
60                         break;
61                 case IPT_CONNBYTES_DIR_REPLY:
62                         what = counters[IP_CT_DIR_REPLY].packets;
63                         break;
64                 case IPT_CONNBYTES_DIR_BOTH:
65                         what = counters[IP_CT_DIR_ORIGINAL].packets;
66                         what += counters[IP_CT_DIR_REPLY].packets;
67                         break;
68                 }
69                 break;
70         case IPT_CONNBYTES_BYTES:
71                 switch (sinfo->direction) {
72                 case IPT_CONNBYTES_DIR_ORIGINAL:
73                         what = counters[IP_CT_DIR_ORIGINAL].bytes;
74                         break;
75                 case IPT_CONNBYTES_DIR_REPLY:
76                         what = counters[IP_CT_DIR_REPLY].bytes;
77                         break;
78                 case IPT_CONNBYTES_DIR_BOTH:
79                         what = counters[IP_CT_DIR_ORIGINAL].bytes;
80                         what += counters[IP_CT_DIR_REPLY].bytes;
81                         break;
82                 }
83                 break;
84         case IPT_CONNBYTES_AVGPKT:
85                 switch (sinfo->direction) {
86                 case IPT_CONNBYTES_DIR_ORIGINAL:
87                         what = div64_64(counters[IP_CT_DIR_ORIGINAL].bytes,
88                                         counters[IP_CT_DIR_ORIGINAL].packets);
89                         break;
90                 case IPT_CONNBYTES_DIR_REPLY:
91                         what = div64_64(counters[IP_CT_DIR_REPLY].bytes,
92                                         counters[IP_CT_DIR_REPLY].packets);
93                         break;
94                 case IPT_CONNBYTES_DIR_BOTH:
95                         {
96                                 u_int64_t bytes;
97                                 u_int64_t pkts;
98                                 bytes = counters[IP_CT_DIR_ORIGINAL].bytes +
99                                         counters[IP_CT_DIR_REPLY].bytes;
100                                 pkts = counters[IP_CT_DIR_ORIGINAL].packets+
101                                         counters[IP_CT_DIR_REPLY].packets;
102
103                                 /* FIXME_THEORETICAL: what to do if sum
104                                  * overflows ? */
105
106                                 what = div64_64(bytes, pkts);
107                         }
108                         break;
109                 }
110                 break;
111         }
112
113         if (sinfo->count.to)
114                 return (what <= sinfo->count.to && what >= sinfo->count.from);
115         else
116                 return (what >= sinfo->count.from);
117 }
118
119 static int check(const char *tablename,
120                  const struct ipt_ip *ip,
121                  void *matchinfo,
122                  unsigned int matchsize,
123                  unsigned int hook_mask)
124 {
125         const struct ipt_connbytes_info *sinfo = matchinfo;
126
127         if (matchsize != IPT_ALIGN(sizeof(struct ipt_connbytes_info)))
128                 return 0;
129
130         if (sinfo->what != IPT_CONNBYTES_PKTS &&
131             sinfo->what != IPT_CONNBYTES_BYTES &&
132             sinfo->what != IPT_CONNBYTES_AVGPKT)
133                 return 0;
134
135         if (sinfo->direction != IPT_CONNBYTES_DIR_ORIGINAL &&
136             sinfo->direction != IPT_CONNBYTES_DIR_REPLY &&
137             sinfo->direction != IPT_CONNBYTES_DIR_BOTH)
138                 return 0;
139
140         return 1;
141 }
142
143 static struct ipt_match state_match = {
144         .name           = "connbytes",
145         .match          = &match,
146         .checkentry     = &check,
147         .me             = THIS_MODULE
148 };
149
150 static int __init init(void)
151 {
152         return ipt_register_match(&state_match);
153 }
154
155 static void __exit fini(void)
156 {
157         ipt_unregister_match(&state_match);
158 }
159
160 module_init(init);
161 module_exit(fini);