xt_conntrack.c revision efa741656e9ebf5fd6e0432b0d1b3c7f156392d3
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
24MODULE_LICENSE("GPL");
25MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
26MODULE_DESCRIPTION("iptables connection tracking match module");
27MODULE_ALIAS("ipt_conntrack");
28
29#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
30
31static int
32match(const struct sk_buff *skb,
33      const struct net_device *in,
34      const struct net_device *out,
35      const struct xt_match *match,
36      const void *matchinfo,
37      int offset,
38      unsigned int protoff,
39      int *hotdrop)
40{
41	const struct xt_conntrack_info *sinfo = matchinfo;
42	struct ip_conntrack *ct;
43	enum ip_conntrack_info ctinfo;
44	unsigned int statebit;
45
46	ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
47
48#define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg))
49
50	if (ct == &ip_conntrack_untracked)
51		statebit = XT_CONNTRACK_STATE_UNTRACKED;
52	else if (ct)
53 		statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
54 	else
55 		statebit = XT_CONNTRACK_STATE_INVALID;
56
57	if(sinfo->flags & XT_CONNTRACK_STATE) {
58		if (ct) {
59			if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip !=
60			    ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip)
61				statebit |= XT_CONNTRACK_STATE_SNAT;
62
63			if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip !=
64			    ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip)
65				statebit |= XT_CONNTRACK_STATE_DNAT;
66		}
67
68		if (FWINV((statebit & sinfo->statemask) == 0, XT_CONNTRACK_STATE))
69			return 0;
70	}
71
72	if(sinfo->flags & XT_CONNTRACK_PROTO) {
73		if (!ct || FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, XT_CONNTRACK_PROTO))
74                	return 0;
75	}
76
77	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
78		if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip&sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, XT_CONNTRACK_ORIGSRC))
79			return 0;
80	}
81
82	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
83		if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip&sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, XT_CONNTRACK_ORIGDST))
84			return 0;
85	}
86
87	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
88		if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip&sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != sinfo->tuple[IP_CT_DIR_REPLY].src.ip, XT_CONNTRACK_REPLSRC))
89			return 0;
90	}
91
92	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
93		if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip&sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, XT_CONNTRACK_REPLDST))
94			return 0;
95	}
96
97	if(sinfo->flags & XT_CONNTRACK_STATUS) {
98		if (!ct || FWINV((ct->status & sinfo->statusmask) == 0, XT_CONNTRACK_STATUS))
99			return 0;
100	}
101
102	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
103		unsigned long expires;
104
105		if(!ct)
106			return 0;
107
108		expires = timer_pending(&ct->timeout) ? (ct->timeout.expires - jiffies)/HZ : 0;
109
110		if (FWINV(!(expires >= sinfo->expires_min && expires <= sinfo->expires_max), XT_CONNTRACK_EXPIRES))
111			return 0;
112	}
113
114	return 1;
115}
116
117#else /* CONFIG_IP_NF_CONNTRACK */
118static int
119match(const struct sk_buff *skb,
120      const struct net_device *in,
121      const struct net_device *out,
122      const struct xt_match *match,
123      const void *matchinfo,
124      int offset,
125      unsigned int protoff,
126      int *hotdrop)
127{
128	const struct xt_conntrack_info *sinfo = matchinfo;
129	struct nf_conn *ct;
130	enum ip_conntrack_info ctinfo;
131	unsigned int statebit;
132
133	ct = nf_ct_get((struct sk_buff *)skb, &ctinfo);
134
135#define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg))
136
137	if (ct == &nf_conntrack_untracked)
138		statebit = XT_CONNTRACK_STATE_UNTRACKED;
139	else if (ct)
140 		statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
141 	else
142 		statebit = XT_CONNTRACK_STATE_INVALID;
143
144	if(sinfo->flags & XT_CONNTRACK_STATE) {
145		if (ct) {
146			if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip !=
147			    ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip)
148				statebit |= XT_CONNTRACK_STATE_SNAT;
149
150			if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip !=
151			    ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip)
152				statebit |= XT_CONNTRACK_STATE_DNAT;
153		}
154
155		if (FWINV((statebit & sinfo->statemask) == 0, XT_CONNTRACK_STATE))
156			return 0;
157	}
158
159	if(sinfo->flags & XT_CONNTRACK_PROTO) {
160		if (!ct || FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, XT_CONNTRACK_PROTO))
161                	return 0;
162	}
163
164	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
165		if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip&sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, XT_CONNTRACK_ORIGSRC))
166			return 0;
167	}
168
169	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
170		if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip&sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, XT_CONNTRACK_ORIGDST))
171			return 0;
172	}
173
174	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
175		if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip&sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != sinfo->tuple[IP_CT_DIR_REPLY].src.ip, XT_CONNTRACK_REPLSRC))
176			return 0;
177	}
178
179	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
180		if (!ct || FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip&sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, XT_CONNTRACK_REPLDST))
181			return 0;
182	}
183
184	if(sinfo->flags & XT_CONNTRACK_STATUS) {
185		if (!ct || FWINV((ct->status & sinfo->statusmask) == 0, XT_CONNTRACK_STATUS))
186			return 0;
187	}
188
189	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
190		unsigned long expires;
191
192		if(!ct)
193			return 0;
194
195		expires = timer_pending(&ct->timeout) ? (ct->timeout.expires - jiffies)/HZ : 0;
196
197		if (FWINV(!(expires >= sinfo->expires_min && expires <= sinfo->expires_max), XT_CONNTRACK_EXPIRES))
198			return 0;
199	}
200
201	return 1;
202}
203
204#endif /* CONFIG_NF_IP_CONNTRACK */
205
206static int
207checkentry(const char *tablename,
208	   const void *ip,
209	   const struct xt_match *match,
210	   void *matchinfo,
211	   unsigned int hook_mask)
212{
213#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
214	if (nf_ct_l3proto_try_module_get(match->family) < 0) {
215		printk(KERN_WARNING "can't load nf_conntrack support for "
216				    "proto=%d\n", match->family);
217		return 0;
218	}
219#endif
220	return 1;
221}
222
223static void
224destroy(const struct xt_match *match, void *matchinfo)
225{
226#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
227	nf_ct_l3proto_module_put(match->family);
228#endif
229}
230
231static struct xt_match conntrack_match = {
232	.name		= "conntrack",
233	.match		= match,
234	.checkentry	= checkentry,
235	.destroy	= destroy,
236	.matchsize	= sizeof(struct xt_conntrack_info),
237	.family		= AF_INET,
238	.me		= THIS_MODULE,
239};
240
241static int __init xt_conntrack_init(void)
242{
243	need_conntrack();
244	return xt_register_match(&conntrack_match);
245}
246
247static void __exit xt_conntrack_fini(void)
248{
249	xt_unregister_match(&conntrack_match);
250}
251
252module_init(xt_conntrack_init);
253module_exit(xt_conntrack_fini);
254