Merge branch 'smsc47b397-new-id' into release
[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 #include <linux/module.h>
5 #include <linux/bitops.h>
6 #include <linux/skbuff.h>
7 #include <linux/netfilter/x_tables.h>
8 #include <linux/netfilter/xt_connbytes.h>
9 #include <net/netfilter/nf_conntrack.h>
10
11 #include <asm/div64.h>
12
13 MODULE_LICENSE("GPL");
14 MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
15 MODULE_DESCRIPTION("Xtables: Number of packets/bytes per connection matching");
16 MODULE_ALIAS("ipt_connbytes");
17 MODULE_ALIAS("ip6t_connbytes");
18
19 static bool
20 connbytes_mt(const struct sk_buff *skb, const struct net_device *in,
21              const struct net_device *out, const struct xt_match *match,
22              const void *matchinfo, int offset, unsigned int protoff,
23              bool *hotdrop)
24 {
25         const struct xt_connbytes_info *sinfo = matchinfo;
26         const struct nf_conn *ct;
27         enum ip_conntrack_info ctinfo;
28         u_int64_t what = 0;     /* initialize to make gcc happy */
29         u_int64_t bytes = 0;
30         u_int64_t pkts = 0;
31         const struct ip_conntrack_counter *counters;
32
33         ct = nf_ct_get(skb, &ctinfo);
34         if (!ct)
35                 return false;
36         counters = ct->counters;
37
38         switch (sinfo->what) {
39         case XT_CONNBYTES_PKTS:
40                 switch (sinfo->direction) {
41                 case XT_CONNBYTES_DIR_ORIGINAL:
42                         what = counters[IP_CT_DIR_ORIGINAL].packets;
43                         break;
44                 case XT_CONNBYTES_DIR_REPLY:
45                         what = counters[IP_CT_DIR_REPLY].packets;
46                         break;
47                 case XT_CONNBYTES_DIR_BOTH:
48                         what = counters[IP_CT_DIR_ORIGINAL].packets;
49                         what += counters[IP_CT_DIR_REPLY].packets;
50                         break;
51                 }
52                 break;
53         case XT_CONNBYTES_BYTES:
54                 switch (sinfo->direction) {
55                 case XT_CONNBYTES_DIR_ORIGINAL:
56                         what = counters[IP_CT_DIR_ORIGINAL].bytes;
57                         break;
58                 case XT_CONNBYTES_DIR_REPLY:
59                         what = counters[IP_CT_DIR_REPLY].bytes;
60                         break;
61                 case XT_CONNBYTES_DIR_BOTH:
62                         what = counters[IP_CT_DIR_ORIGINAL].bytes;
63                         what += counters[IP_CT_DIR_REPLY].bytes;
64                         break;
65                 }
66                 break;
67         case XT_CONNBYTES_AVGPKT:
68                 switch (sinfo->direction) {
69                 case XT_CONNBYTES_DIR_ORIGINAL:
70                         bytes = counters[IP_CT_DIR_ORIGINAL].bytes;
71                         pkts  = counters[IP_CT_DIR_ORIGINAL].packets;
72                         break;
73                 case XT_CONNBYTES_DIR_REPLY:
74                         bytes = counters[IP_CT_DIR_REPLY].bytes;
75                         pkts  = counters[IP_CT_DIR_REPLY].packets;
76                         break;
77                 case XT_CONNBYTES_DIR_BOTH:
78                         bytes = counters[IP_CT_DIR_ORIGINAL].bytes +
79                                 counters[IP_CT_DIR_REPLY].bytes;
80                         pkts  = counters[IP_CT_DIR_ORIGINAL].packets +
81                                 counters[IP_CT_DIR_REPLY].packets;
82                         break;
83                 }
84                 if (pkts != 0)
85                         what = div64_64(bytes, pkts);
86                 break;
87         }
88
89         if (sinfo->count.to)
90                 return what <= sinfo->count.to && what >= sinfo->count.from;
91         else
92                 return what >= sinfo->count.from;
93 }
94
95 static bool
96 connbytes_mt_check(const char *tablename, const void *ip,
97                    const struct xt_match *match, void *matchinfo,
98                    unsigned int hook_mask)
99 {
100         const struct xt_connbytes_info *sinfo = matchinfo;
101
102         if (sinfo->what != XT_CONNBYTES_PKTS &&
103             sinfo->what != XT_CONNBYTES_BYTES &&
104             sinfo->what != XT_CONNBYTES_AVGPKT)
105                 return false;
106
107         if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL &&
108             sinfo->direction != XT_CONNBYTES_DIR_REPLY &&
109             sinfo->direction != XT_CONNBYTES_DIR_BOTH)
110                 return false;
111
112         if (nf_ct_l3proto_try_module_get(match->family) < 0) {
113                 printk(KERN_WARNING "can't load conntrack support for "
114                                     "proto=%u\n", match->family);
115                 return false;
116         }
117
118         return true;
119 }
120
121 static void
122 connbytes_mt_destroy(const struct xt_match *match, void *matchinfo)
123 {
124         nf_ct_l3proto_module_put(match->family);
125 }
126
127 static struct xt_match connbytes_mt_reg[] __read_mostly = {
128         {
129                 .name           = "connbytes",
130                 .family         = AF_INET,
131                 .checkentry     = connbytes_mt_check,
132                 .match          = connbytes_mt,
133                 .destroy        = connbytes_mt_destroy,
134                 .matchsize      = sizeof(struct xt_connbytes_info),
135                 .me             = THIS_MODULE
136         },
137         {
138                 .name           = "connbytes",
139                 .family         = AF_INET6,
140                 .checkentry     = connbytes_mt_check,
141                 .match          = connbytes_mt,
142                 .destroy        = connbytes_mt_destroy,
143                 .matchsize      = sizeof(struct xt_connbytes_info),
144                 .me             = THIS_MODULE
145         },
146 };
147
148 static int __init connbytes_mt_init(void)
149 {
150         return xt_register_matches(connbytes_mt_reg,
151                ARRAY_SIZE(connbytes_mt_reg));
152 }
153
154 static void __exit connbytes_mt_exit(void)
155 {
156         xt_unregister_matches(connbytes_mt_reg, ARRAY_SIZE(connbytes_mt_reg));
157 }
158
159 module_init(connbytes_mt_init);
160 module_exit(connbytes_mt_exit);