• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/net/netfilter/
1/*-------------------------------------------*\
2|          Netfilter Condition Module         |
3|                                             |
4|  Description: This module allows firewall   |
5|    rules to match using condition variables |
6|    stored in /proc files.                   |
7|                                             |
8|  Author: Stephane Ouellette     2002-10-22  |
9|          <ouellettes@videotron.ca>          |
10|          Massimiliano Hofer     2006-05-15  |
11|          <max@nucleus.it>                   |
12|                                             |
13|  History:                                   |
14|    2003-02-10  Second version with improved |
15|                locking and simplified code. |
16|    2006-05-15  2.6.16 adaptations.          |
17|                Locking overhaul.            |
18|                Various bug fixes.           |
19|                                             |
20|  This software is distributed under the     |
21|  terms of the GNU GPL.                      |
22\*-------------------------------------------*/
23
24#include <linux/version.h>
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/proc_fs.h>
28#include <linux/spinlock.h>
29#include <linux/string.h>
30#include <linux/list.h>
31#include <asm/atomic.h>
32#include <asm/uaccess.h>
33#include <linux/netfilter/x_tables.h>
34#include <linux/netfilter/xt_condition.h>
35
36#ifndef CONFIG_PROC_FS
37#error  "Proc file system support is required for this module"
38#endif
39
40/* Defaults, these can be overridden on the module command-line. */
41static unsigned int condition_list_perms = 0644;
42static unsigned int compat_dir_name = 0;
43static unsigned int condition_uid_perms = 0;
44static unsigned int condition_gid_perms = 0;
45
46MODULE_AUTHOR("Stephane Ouellette <ouellettes@videotron.ca> and Massimiliano Hofer <max@nucleus.it>");
47MODULE_DESCRIPTION("Allows rules to match against condition variables");
48MODULE_LICENSE("GPL");
49module_param(condition_list_perms, uint, 0600);
50MODULE_PARM_DESC(condition_list_perms,"permissions on /proc/net/nf_condition/* files");
51module_param(condition_uid_perms, uint, 0600);
52MODULE_PARM_DESC(condition_uid_perms,"user owner of /proc/net/nf_condition/* files");
53module_param(condition_gid_perms, uint, 0600);
54MODULE_PARM_DESC(condition_gid_perms,"group owner of /proc/net/nf_condition/* files");
55module_param(compat_dir_name, bool, 0400);
56MODULE_PARM_DESC(compat_dir_name,"use old style /proc/net/ipt_condition/* files");
57MODULE_ALIAS("ipt_condition");
58MODULE_ALIAS("ip6t_condition");
59
60struct condition_variable {
61	struct list_head list;
62	struct proc_dir_entry *status_proc;
63	unsigned int refcount;
64        int enabled;   /* TRUE == 1, FALSE == 0 */
65};
66
67/* proc_lock is a user context only semaphore used for write access */
68/*           to the conditions' list.                               */
69static DEFINE_MUTEX(proc_lock);
70
71static LIST_HEAD(conditions_list);
72static struct proc_dir_entry *proc_net_condition = NULL;
73static const char *dir_name;
74
75static int
76xt_condition_read_info(char __user *buffer, char **start, off_t offset,
77			int length, int *eof, void *data)
78{
79	struct condition_variable *var =
80	    (struct condition_variable *) data;
81
82	buffer[0] = (var->enabled) ? '1' : '0';
83	buffer[1] = '\n';
84	if (length>=2)
85		*eof = 1;
86
87	return 2;
88}
89
90
91static int
92xt_condition_write_info(struct file *file, const char __user *buffer,
93			 unsigned long length, void *data)
94{
95	struct condition_variable *var =
96	    (struct condition_variable *) data;
97	char newval;
98
99	if (length>0) {
100		if (get_user(newval, buffer))
101			return -EFAULT;
102	        /* Match only on the first character */
103		switch (newval) {
104		case '0':
105			var->enabled = 0;
106			break;
107		case '1':
108			var->enabled = 1;
109			break;
110		}
111	}
112
113	return (int) length;
114}
115
116
117static bool
118match(const struct sk_buff *skb, struct xt_action_param *par)
119{
120	const struct condition_info *info =
121	    (const struct condition_info *) par->matchinfo;
122	struct condition_variable *var;
123	int condition_status = 0;
124
125	rcu_read_lock();
126	list_for_each_entry_rcu(var, &conditions_list, list) {
127		if (strcmp(info->name, var->status_proc->name) == 0) {
128			condition_status = var->enabled;
129			break;
130		}
131	}
132	rcu_read_unlock();
133
134	return condition_status ^ info->invert;
135}
136
137
138
139static int
140checkentry(const struct xt_mtchk_param *par)
141{
142	static const char * const forbidden_names[]={ "", ".", ".." };
143	struct condition_info *info = (struct condition_info *) par->matchinfo;
144	struct list_head *pos;
145	struct condition_variable *var, *newvar;
146
147	int i;
148
149	/* We don't want a '/' in a proc file name. */
150	for (i=0; i < CONDITION_NAME_LEN && info->name[i] != '\0'; i++)
151		if (info->name[i] == '/')
152			return -EINVAL;
153	/* We can't handle file names longer than CONDITION_NAME_LEN and */
154	/* we want a NULL terminated string. */
155	if (i == CONDITION_NAME_LEN)
156		return -EINVAL;
157
158	/* We don't want certain reserved names. */
159	for (i=0; i < sizeof(forbidden_names)/sizeof(char *); i++)
160		if(strcmp(info->name, forbidden_names[i])==0)
161			return -EINVAL;
162
163	/* Let's acquire the lock, check for the condition and add it */
164	/* or increase the reference counter.                         */
165	if (mutex_lock_interruptible(&proc_lock))
166	   return -EINTR;
167
168	list_for_each(pos, &conditions_list) {
169		var = list_entry(pos, struct condition_variable, list);
170		if (strcmp(info->name, var->status_proc->name) == 0) {
171			var->refcount++;
172			mutex_unlock(&proc_lock);
173			return 0;
174		}
175	}
176
177	/* At this point, we need to allocate a new condition variable. */
178	newvar = kmalloc(sizeof(struct condition_variable), GFP_KERNEL);
179
180	if (!newvar) {
181		mutex_unlock(&proc_lock);
182		return -ENOMEM;
183	}
184
185	/* Create the condition variable's proc file entry. */
186	newvar->status_proc = create_proc_entry(info->name, condition_list_perms, proc_net_condition);
187
188	if (!newvar->status_proc) {
189		kfree(newvar);
190		mutex_unlock(&proc_lock);
191		return -ENOMEM;
192	}
193
194	newvar->refcount = 1;
195	newvar->enabled = 0;
196	newvar->status_proc->data = newvar;
197	wmb();
198	newvar->status_proc->read_proc = xt_condition_read_info;
199	newvar->status_proc->write_proc = xt_condition_write_info;
200
201	list_add_rcu(&newvar->list, &conditions_list);
202
203	newvar->status_proc->uid = condition_uid_perms;
204	newvar->status_proc->gid = condition_gid_perms;
205
206	mutex_unlock(&proc_lock);
207
208	return 0;
209}
210
211
212static void
213destroy(const struct xt_mtdtor_param *par)
214{
215	struct condition_info *info = (struct condition_info *) par->matchinfo;
216	struct list_head *pos;
217	struct condition_variable *var;
218
219
220	mutex_lock(&proc_lock);
221
222	list_for_each(pos, &conditions_list) {
223		var = list_entry(pos, struct condition_variable, list);
224		if (strcmp(info->name, var->status_proc->name) == 0) {
225			if (--var->refcount == 0) {
226				list_del_rcu(pos);
227				remove_proc_entry(var->status_proc->name, proc_net_condition);
228				mutex_unlock(&proc_lock);
229				/* synchronize_rcu() would be goog enough, but synchronize_net() */
230				/* guarantees that no packet will go out with the old rule after */
231				/* succesful removal.                                            */
232				synchronize_net();
233				kfree(var);
234				return;
235			}
236			break;
237		}
238	}
239
240	mutex_unlock(&proc_lock);
241}
242
243
244static struct xt_match condition_match = {
245	.name = "condition",
246	.family = NFPROTO_IPV4,
247	.matchsize = sizeof(struct condition_info),
248	.match = &match,
249	.checkentry = &checkentry,
250	.destroy = &destroy,
251	.me = THIS_MODULE
252};
253
254static struct xt_match condition6_match = {
255	.name = "condition",
256	.family = NFPROTO_IPV6,
257	.matchsize = sizeof(struct condition_info),
258	.match = &match,
259	.checkentry = &checkentry,
260	.destroy = &destroy,
261	.me = THIS_MODULE
262};
263
264static int __init
265init(void)
266{
267	struct proc_dir_entry * const proc_net=init_net.proc_net;
268	int errorcode;
269
270	dir_name = compat_dir_name? "ipt_condition": "nf_condition";
271
272	proc_net_condition = proc_mkdir(dir_name, proc_net);
273	if (!proc_net_condition) {
274		remove_proc_entry(dir_name, proc_net);
275		return -EACCES;
276	}
277
278        errorcode = xt_register_match(&condition_match);
279	if (errorcode) {
280		xt_unregister_match(&condition_match);
281		remove_proc_entry(dir_name, proc_net);
282		return errorcode;
283	}
284
285	errorcode = xt_register_match(&condition6_match);
286	if (errorcode) {
287		xt_unregister_match(&condition6_match);
288		xt_unregister_match(&condition_match);
289		remove_proc_entry(dir_name, proc_net);
290		return errorcode;
291	}
292
293	return 0;
294}
295
296
297static void __exit
298fini(void)
299{
300	xt_unregister_match(&condition6_match);
301	xt_unregister_match(&condition_match);
302	remove_proc_entry(dir_name, init_net.proc_net);
303}
304
305module_init(init);
306module_exit(fini);
307