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