Pull bugzilla-7897 into release branch
[linux-2.6] / net / netfilter / xt_conntrack.c
1 /* Kernel module to match connection tracking information.
2  * Superset of Rusty's minimalistic state match.
3  *
4  * (C) 2001  Marc Boucher (marc@mbsi.ca).
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <linux/module.h>
12 #include <linux/skbuff.h>
13
14 #if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
15 #include <linux/netfilter_ipv4/ip_conntrack.h>
16 #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
17 #else
18 #include <net/netfilter/nf_conntrack.h>
19 #endif
20
21 #include <linux/netfilter/x_tables.h>
22 #include <linux/netfilter/xt_conntrack.h>
23 #include <net/netfilter/nf_conntrack_compat.h>
24
25 MODULE_LICENSE("GPL");
26 MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
27 MODULE_DESCRIPTION("iptables connection tracking match module");
28 MODULE_ALIAS("ipt_conntrack");
29
30 #if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
31
32 static int
33 match(const struct sk_buff *skb,
34       const struct net_device *in,
35       const struct net_device *out,
36       const struct xt_match *match,
37       const void *matchinfo,
38       int offset,
39       unsigned int protoff,
40       int *hotdrop)
41 {
42         const struct xt_conntrack_info *sinfo = matchinfo;
43         struct ip_conntrack *ct;
44         enum ip_conntrack_info ctinfo;
45         unsigned int statebit;
46
47         ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
48
49 #define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & invflg))
50
51         if (ct == &ip_conntrack_untracked)
52                 statebit = XT_CONNTRACK_STATE_UNTRACKED;
53         else if (ct)
54                 statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
55         else
56                 statebit = XT_CONNTRACK_STATE_INVALID;
57
58         if (sinfo->flags & XT_CONNTRACK_STATE) {
59                 if (ct) {
60                         if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
61                                 statebit |= XT_CONNTRACK_STATE_SNAT;
62                         if (test_bit(IPS_DST_NAT_BIT, &ct->status))
63                                 statebit |= XT_CONNTRACK_STATE_DNAT;
64                 }
65                 if (FWINV((statebit & sinfo->statemask) == 0,
66                           XT_CONNTRACK_STATE))
67                         return 0;
68         }
69
70         if (ct == NULL) {
71                 if (sinfo->flags & ~XT_CONNTRACK_STATE)
72                         return 0;
73                 return 1;
74         }
75
76         if (sinfo->flags & XT_CONNTRACK_PROTO &&
77             FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum !=
78                   sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum,
79                   XT_CONNTRACK_PROTO))
80                 return 0;
81
82         if (sinfo->flags & XT_CONNTRACK_ORIGSRC &&
83             FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip &
84                    sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
85                   sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
86                   XT_CONNTRACK_ORIGSRC))
87                 return 0;
88
89         if (sinfo->flags & XT_CONNTRACK_ORIGDST &&
90             FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip &
91                    sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
92                   sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
93                   XT_CONNTRACK_ORIGDST))
94                 return 0;
95
96         if (sinfo->flags & XT_CONNTRACK_REPLSRC &&
97             FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip &
98                    sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) !=
99                   sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
100                   XT_CONNTRACK_REPLSRC))
101                 return 0;
102
103         if (sinfo->flags & XT_CONNTRACK_REPLDST &&
104             FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip &
105                    sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) !=
106                   sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
107                   XT_CONNTRACK_REPLDST))
108                 return 0;
109
110         if (sinfo->flags & XT_CONNTRACK_STATUS &&
111             FWINV((ct->status & sinfo->statusmask) == 0,
112                   XT_CONNTRACK_STATUS))
113                 return 0;
114
115         if (sinfo->flags & XT_CONNTRACK_EXPIRES) {
116                 unsigned long expires = timer_pending(&ct->timeout) ?
117                                         (ct->timeout.expires - jiffies)/HZ : 0;
118
119                 if (FWINV(!(expires >= sinfo->expires_min &&
120                             expires <= sinfo->expires_max),
121                           XT_CONNTRACK_EXPIRES))
122                         return 0;
123         }
124         return 1;
125 }
126
127 #else /* CONFIG_IP_NF_CONNTRACK */
128 static int
129 match(const struct sk_buff *skb,
130       const struct net_device *in,
131       const struct net_device *out,
132       const struct xt_match *match,
133       const void *matchinfo,
134       int offset,
135       unsigned int protoff,
136       int *hotdrop)
137 {
138         const struct xt_conntrack_info *sinfo = matchinfo;
139         struct nf_conn *ct;
140         enum ip_conntrack_info ctinfo;
141         unsigned int statebit;
142
143         ct = nf_ct_get((struct sk_buff *)skb, &ctinfo);
144
145 #define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg))
146
147         if (ct == &nf_conntrack_untracked)
148                 statebit = XT_CONNTRACK_STATE_UNTRACKED;
149         else if (ct)
150                 statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
151         else
152                 statebit = XT_CONNTRACK_STATE_INVALID;
153
154         if (sinfo->flags & XT_CONNTRACK_STATE) {
155                 if (ct) {
156                         if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
157                                 statebit |= XT_CONNTRACK_STATE_SNAT;
158                         if (test_bit(IPS_DST_NAT_BIT, &ct->status))
159                                 statebit |= XT_CONNTRACK_STATE_DNAT;
160                 }
161                 if (FWINV((statebit & sinfo->statemask) == 0,
162                           XT_CONNTRACK_STATE))
163                         return 0;
164         }
165
166         if (ct == NULL) {
167                 if (sinfo->flags & ~XT_CONNTRACK_STATE)
168                         return 0;
169                 return 1;
170         }
171
172         if (sinfo->flags & XT_CONNTRACK_PROTO &&
173             FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum !=
174                   sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum,
175                   XT_CONNTRACK_PROTO))
176                 return 0;
177
178         if (sinfo->flags & XT_CONNTRACK_ORIGSRC &&
179             FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip &
180                    sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
181                   sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
182                   XT_CONNTRACK_ORIGSRC))
183                 return 0;
184
185         if (sinfo->flags & XT_CONNTRACK_ORIGDST &&
186             FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip &
187                    sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
188                   sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
189                   XT_CONNTRACK_ORIGDST))
190                 return 0;
191
192         if (sinfo->flags & XT_CONNTRACK_REPLSRC &&
193             FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip &
194                    sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) !=
195                   sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
196                   XT_CONNTRACK_REPLSRC))
197                 return 0;
198
199         if (sinfo->flags & XT_CONNTRACK_REPLDST &&
200             FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip &
201                    sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) !=
202                   sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
203                   XT_CONNTRACK_REPLDST))
204                 return 0;
205
206         if (sinfo->flags & XT_CONNTRACK_STATUS &&
207             FWINV((ct->status & sinfo->statusmask) == 0,
208                   XT_CONNTRACK_STATUS))
209                 return 0;
210
211         if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
212                 unsigned long expires = timer_pending(&ct->timeout) ?
213                                         (ct->timeout.expires - jiffies)/HZ : 0;
214
215                 if (FWINV(!(expires >= sinfo->expires_min &&
216                             expires <= sinfo->expires_max),
217                           XT_CONNTRACK_EXPIRES))
218                         return 0;
219         }
220         return 1;
221 }
222
223 #endif /* CONFIG_NF_IP_CONNTRACK */
224
225 static int
226 checkentry(const char *tablename,
227            const void *ip,
228            const struct xt_match *match,
229            void *matchinfo,
230            unsigned int hook_mask)
231 {
232         if (nf_ct_l3proto_try_module_get(match->family) < 0) {
233                 printk(KERN_WARNING "can't load conntrack support for "
234                                     "proto=%d\n", match->family);
235                 return 0;
236         }
237         return 1;
238 }
239
240 static void destroy(const struct xt_match *match, void *matchinfo)
241 {
242         nf_ct_l3proto_module_put(match->family);
243 }
244
245 static struct xt_match conntrack_match = {
246         .name           = "conntrack",
247         .match          = match,
248         .checkentry     = checkentry,
249         .destroy        = destroy,
250         .matchsize      = sizeof(struct xt_conntrack_info),
251         .family         = AF_INET,
252         .me             = THIS_MODULE,
253 };
254
255 static int __init xt_conntrack_init(void)
256 {
257         return xt_register_match(&conntrack_match);
258 }
259
260 static void __exit xt_conntrack_fini(void)
261 {
262         xt_unregister_match(&conntrack_match);
263 }
264
265 module_init(xt_conntrack_init);
266 module_exit(xt_conntrack_fini);