xt_conntrack.c revision 5d04bff096180f032de8b9b12153a8a1b4009b8d
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 void *matchinfo,
36      int offset,
37      unsigned int protoff,
38      int *hotdrop)
39{
40	const struct xt_conntrack_info *sinfo = matchinfo;
41	struct ip_conntrack *ct;
42	enum ip_conntrack_info ctinfo;
43	unsigned int statebit;
44
45	ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
46
47#define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg))
48
49	if (ct == &ip_conntrack_untracked)
50		statebit = XT_CONNTRACK_STATE_UNTRACKED;
51	else if (ct)
52 		statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
53 	else
54 		statebit = XT_CONNTRACK_STATE_INVALID;
55
56	if(sinfo->flags & XT_CONNTRACK_STATE) {
57		if (ct) {
58			if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip !=
59			    ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip)
60				statebit |= XT_CONNTRACK_STATE_SNAT;
61
62			if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip !=
63			    ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip)
64				statebit |= XT_CONNTRACK_STATE_DNAT;
65		}
66
67		if (FWINV((statebit & sinfo->statemask) == 0, XT_CONNTRACK_STATE))
68			return 0;
69	}
70
71	if(sinfo->flags & XT_CONNTRACK_PROTO) {
72		if (!ct || FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, XT_CONNTRACK_PROTO))
73                	return 0;
74	}
75
76	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
77		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))
78			return 0;
79	}
80
81	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
82		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))
83			return 0;
84	}
85
86	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
87		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))
88			return 0;
89	}
90
91	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
92		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))
93			return 0;
94	}
95
96	if(sinfo->flags & XT_CONNTRACK_STATUS) {
97		if (!ct || FWINV((ct->status & sinfo->statusmask) == 0, XT_CONNTRACK_STATUS))
98			return 0;
99	}
100
101	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
102		unsigned long expires;
103
104		if(!ct)
105			return 0;
106
107		expires = timer_pending(&ct->timeout) ? (ct->timeout.expires - jiffies)/HZ : 0;
108
109		if (FWINV(!(expires >= sinfo->expires_min && expires <= sinfo->expires_max), XT_CONNTRACK_EXPIRES))
110			return 0;
111	}
112
113	return 1;
114}
115
116#else /* CONFIG_IP_NF_CONNTRACK */
117static int
118match(const struct sk_buff *skb,
119      const struct net_device *in,
120      const struct net_device *out,
121      const void *matchinfo,
122      int offset,
123      unsigned int protoff,
124      int *hotdrop)
125{
126	const struct xt_conntrack_info *sinfo = matchinfo;
127	struct nf_conn *ct;
128	enum ip_conntrack_info ctinfo;
129	unsigned int statebit;
130
131	ct = nf_ct_get((struct sk_buff *)skb, &ctinfo);
132
133#define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg))
134
135	if (ct == &nf_conntrack_untracked)
136		statebit = XT_CONNTRACK_STATE_UNTRACKED;
137	else if (ct)
138 		statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
139 	else
140 		statebit = XT_CONNTRACK_STATE_INVALID;
141
142	if(sinfo->flags & XT_CONNTRACK_STATE) {
143		if (ct) {
144			if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip !=
145			    ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip)
146				statebit |= XT_CONNTRACK_STATE_SNAT;
147
148			if(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip !=
149			    ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip)
150				statebit |= XT_CONNTRACK_STATE_DNAT;
151		}
152
153		if (FWINV((statebit & sinfo->statemask) == 0, XT_CONNTRACK_STATE))
154			return 0;
155	}
156
157	if(sinfo->flags & XT_CONNTRACK_PROTO) {
158		if (!ct || FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, XT_CONNTRACK_PROTO))
159                	return 0;
160	}
161
162	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
163		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))
164			return 0;
165	}
166
167	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
168		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))
169			return 0;
170	}
171
172	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
173		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))
174			return 0;
175	}
176
177	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
178		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))
179			return 0;
180	}
181
182	if(sinfo->flags & XT_CONNTRACK_STATUS) {
183		if (!ct || FWINV((ct->status & sinfo->statusmask) == 0, XT_CONNTRACK_STATUS))
184			return 0;
185	}
186
187	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
188		unsigned long expires;
189
190		if(!ct)
191			return 0;
192
193		expires = timer_pending(&ct->timeout) ? (ct->timeout.expires - jiffies)/HZ : 0;
194
195		if (FWINV(!(expires >= sinfo->expires_min && expires <= sinfo->expires_max), XT_CONNTRACK_EXPIRES))
196			return 0;
197	}
198
199	return 1;
200}
201
202#endif /* CONFIG_NF_IP_CONNTRACK */
203
204static struct xt_match conntrack_match = {
205	.name		= "conntrack",
206	.match		= match,
207	.matchsize	= sizeof(struct xt_conntrack_info),
208	.me		= THIS_MODULE,
209};
210
211static int __init init(void)
212{
213	int ret;
214	need_conntrack();
215	ret = xt_register_match(AF_INET, &conntrack_match);
216
217	return ret;
218}
219
220static void __exit fini(void)
221{
222	xt_unregister_match(AF_INET, &conntrack_match);
223}
224
225module_init(init);
226module_exit(fini);
227