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#include <linux/netfilter/x_tables.h>
14#include <linux/netfilter/xt_conntrack.h>
15#include <net/netfilter/nf_conntrack.h>
16
17MODULE_LICENSE("GPL");
18MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
19MODULE_DESCRIPTION("iptables connection tracking match module");
20MODULE_ALIAS("ipt_conntrack");
21
22static int
23match(const struct sk_buff *skb,
24      const struct net_device *in,
25      const struct net_device *out,
26      const struct xt_match *match,
27      const void *matchinfo,
28      int offset,
29      unsigned int protoff,
30      int *hotdrop)
31{
32	const struct xt_conntrack_info *sinfo = matchinfo;
33	struct nf_conn *ct;
34	enum ip_conntrack_info ctinfo;
35	unsigned int statebit;
36
37	ct = nf_ct_get((struct sk_buff *)skb, &ctinfo);
38
39#define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg))
40
41	if (ct == &nf_conntrack_untracked)
42		statebit = XT_CONNTRACK_STATE_UNTRACKED;
43	else if (ct)
44		statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
45	else
46		statebit = XT_CONNTRACK_STATE_INVALID;
47
48	if (sinfo->flags & XT_CONNTRACK_STATE) {
49		if (ct) {
50			if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
51				statebit |= XT_CONNTRACK_STATE_SNAT;
52			if (test_bit(IPS_DST_NAT_BIT, &ct->status))
53				statebit |= XT_CONNTRACK_STATE_DNAT;
54		}
55		if (FWINV((statebit & sinfo->statemask) == 0,
56			  XT_CONNTRACK_STATE))
57			return 0;
58	}
59
60	if (ct == NULL) {
61		if (sinfo->flags & ~XT_CONNTRACK_STATE)
62			return 0;
63		return 1;
64	}
65
66	if (sinfo->flags & XT_CONNTRACK_PROTO &&
67	    FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum !=
68		  sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum,
69		  XT_CONNTRACK_PROTO))
70		return 0;
71
72	if (sinfo->flags & XT_CONNTRACK_ORIGSRC &&
73	    FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip &
74		   sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
75		  sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
76		  XT_CONNTRACK_ORIGSRC))
77		return 0;
78
79	if (sinfo->flags & XT_CONNTRACK_ORIGDST &&
80	    FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip &
81		   sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
82		  sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
83		  XT_CONNTRACK_ORIGDST))
84		return 0;
85
86	if (sinfo->flags & XT_CONNTRACK_REPLSRC &&
87	    FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip &
88		   sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) !=
89		  sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
90		  XT_CONNTRACK_REPLSRC))
91		return 0;
92
93	if (sinfo->flags & XT_CONNTRACK_REPLDST &&
94	    FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip &
95		   sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) !=
96		  sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
97		  XT_CONNTRACK_REPLDST))
98		return 0;
99
100	if (sinfo->flags & XT_CONNTRACK_STATUS &&
101	    FWINV((ct->status & sinfo->statusmask) == 0,
102		  XT_CONNTRACK_STATUS))
103		return 0;
104
105	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
106		unsigned long expires = timer_pending(&ct->timeout) ?
107					(ct->timeout.expires - jiffies)/HZ : 0;
108
109		if (FWINV(!(expires >= sinfo->expires_min &&
110			    expires <= sinfo->expires_max),
111			  XT_CONNTRACK_EXPIRES))
112			return 0;
113	}
114	return 1;
115}
116
117static int
118checkentry(const char *tablename,
119	   const void *ip,
120	   const struct xt_match *match,
121	   void *matchinfo,
122	   unsigned int hook_mask)
123{
124	if (nf_ct_l3proto_try_module_get(match->family) < 0) {
125		printk(KERN_WARNING "can't load conntrack support for "
126				    "proto=%d\n", match->family);
127		return 0;
128	}
129	return 1;
130}
131
132static void destroy(const struct xt_match *match, void *matchinfo)
133{
134	nf_ct_l3proto_module_put(match->family);
135}
136
137#ifdef CONFIG_COMPAT
138struct compat_xt_conntrack_info
139{
140	compat_uint_t			statemask;
141	compat_uint_t			statusmask;
142	struct ip_conntrack_old_tuple	tuple[IP_CT_DIR_MAX];
143	struct in_addr			sipmsk[IP_CT_DIR_MAX];
144	struct in_addr			dipmsk[IP_CT_DIR_MAX];
145	compat_ulong_t			expires_min;
146	compat_ulong_t			expires_max;
147	u_int8_t			flags;
148	u_int8_t			invflags;
149};
150
151static void compat_from_user(void *dst, void *src)
152{
153	struct compat_xt_conntrack_info *cm = src;
154	struct xt_conntrack_info m = {
155		.statemask	= cm->statemask,
156		.statusmask	= cm->statusmask,
157		.expires_min	= cm->expires_min,
158		.expires_max	= cm->expires_max,
159		.flags		= cm->flags,
160		.invflags	= cm->invflags,
161	};
162	memcpy(m.tuple, cm->tuple, sizeof(m.tuple));
163	memcpy(m.sipmsk, cm->sipmsk, sizeof(m.sipmsk));
164	memcpy(m.dipmsk, cm->dipmsk, sizeof(m.dipmsk));
165	memcpy(dst, &m, sizeof(m));
166}
167
168static int compat_to_user(void __user *dst, void *src)
169{
170	struct xt_conntrack_info *m = src;
171	struct compat_xt_conntrack_info cm = {
172		.statemask	= m->statemask,
173		.statusmask	= m->statusmask,
174		.expires_min	= m->expires_min,
175		.expires_max	= m->expires_max,
176		.flags		= m->flags,
177		.invflags	= m->invflags,
178	};
179	memcpy(cm.tuple, m->tuple, sizeof(cm.tuple));
180	memcpy(cm.sipmsk, m->sipmsk, sizeof(cm.sipmsk));
181	memcpy(cm.dipmsk, m->dipmsk, sizeof(cm.dipmsk));
182	return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0;
183}
184#endif
185
186static struct xt_match conntrack_match = {
187	.name		= "conntrack",
188	.match		= match,
189	.checkentry	= checkentry,
190	.destroy	= destroy,
191	.matchsize	= sizeof(struct xt_conntrack_info),
192#ifdef CONFIG_COMPAT
193	.compatsize	= sizeof(struct compat_xt_conntrack_info),
194	.compat_from_user = compat_from_user,
195	.compat_to_user	= compat_to_user,
196#endif
197	.family		= AF_INET,
198	.me		= THIS_MODULE,
199};
200
201static int __init xt_conntrack_init(void)
202{
203	return xt_register_match(&conntrack_match);
204}
205
206static void __exit xt_conntrack_fini(void)
207{
208	xt_unregister_match(&conntrack_match);
209}
210
211module_init(xt_conntrack_init);
212module_exit(xt_conntrack_fini);
213