RT-AC56 3.0.0.4.374.37 core
[tomato.git] / release / src-rt-6.x.4708 / linux / linux-2.6.36 / net / netfilter / xt_condition.c
blob0518b30b2c7ca44fd5cb41e47eab8d721e93f631
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 \*-------------------------------------------*/
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>
36 #ifndef CONFIG_PROC_FS
37 #error "Proc file system support is required for this module"
38 #endif
40 /* Defaults, these can be overridden on the module command-line. */
41 static unsigned int condition_list_perms = 0644;
42 static unsigned int compat_dir_name = 0;
43 static unsigned int condition_uid_perms = 0;
44 static unsigned int condition_gid_perms = 0;
46 MODULE_AUTHOR("Stephane Ouellette <ouellettes@videotron.ca> and Massimiliano Hofer <max@nucleus.it>");
47 MODULE_DESCRIPTION("Allows rules to match against condition variables");
48 MODULE_LICENSE("GPL");
49 module_param(condition_list_perms, uint, 0600);
50 MODULE_PARM_DESC(condition_list_perms,"permissions on /proc/net/nf_condition/* files");
51 module_param(condition_uid_perms, uint, 0600);
52 MODULE_PARM_DESC(condition_uid_perms,"user owner of /proc/net/nf_condition/* files");
53 module_param(condition_gid_perms, uint, 0600);
54 MODULE_PARM_DESC(condition_gid_perms,"group owner of /proc/net/nf_condition/* files");
55 module_param(compat_dir_name, bool, 0400);
56 MODULE_PARM_DESC(compat_dir_name,"use old style /proc/net/ipt_condition/* files");
57 MODULE_ALIAS("ipt_condition");
58 MODULE_ALIAS("ip6t_condition");
60 struct 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 */
67 /* proc_lock is a user context only semaphore used for write access */
68 /* to the conditions' list. */
69 static DEFINE_MUTEX(proc_lock);
71 static LIST_HEAD(conditions_list);
72 static struct proc_dir_entry *proc_net_condition = NULL;
73 static const char *dir_name;
75 static int
76 xt_condition_read_info(char __user *buffer, char **start, off_t offset,
77 int length, int *eof, void *data)
79 struct condition_variable *var =
80 (struct condition_variable *) data;
82 buffer[0] = (var->enabled) ? '1' : '0';
83 buffer[1] = '\n';
84 if (length>=2)
85 *eof = 1;
87 return 2;
91 static int
92 xt_condition_write_info(struct file *file, const char __user *buffer,
93 unsigned long length, void *data)
95 struct condition_variable *var =
96 (struct condition_variable *) data;
97 char newval;
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;
113 return (int) length;
117 static bool
118 match(const struct sk_buff *skb, struct xt_action_param *par)
120 const struct condition_info *info =
121 (const struct condition_info *) par->matchinfo;
122 struct condition_variable *var;
123 int condition_status = 0;
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;
132 rcu_read_unlock();
134 return condition_status ^ info->invert;
139 static int
140 checkentry(const struct xt_mtchk_param *par)
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;
147 int i;
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;
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;
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;
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;
177 /* At this point, we need to allocate a new condition variable. */
178 newvar = kmalloc(sizeof(struct condition_variable), GFP_KERNEL);
180 if (!newvar) {
181 mutex_unlock(&proc_lock);
182 return -ENOMEM;
185 /* Create the condition variable's proc file entry. */
186 newvar->status_proc = create_proc_entry(info->name, condition_list_perms, proc_net_condition);
188 if (!newvar->status_proc) {
189 kfree(newvar);
190 mutex_unlock(&proc_lock);
191 return -ENOMEM;
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;
201 list_add_rcu(&newvar->list, &conditions_list);
203 newvar->status_proc->uid = condition_uid_perms;
204 newvar->status_proc->gid = condition_gid_perms;
206 mutex_unlock(&proc_lock);
208 return 0;
212 static void
213 destroy(const struct xt_mtdtor_param *par)
215 struct condition_info *info = (struct condition_info *) par->matchinfo;
216 struct list_head *pos;
217 struct condition_variable *var;
220 mutex_lock(&proc_lock);
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;
236 break;
240 mutex_unlock(&proc_lock);
244 static 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
254 static 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
264 static int __init
265 init(void)
267 struct proc_dir_entry * const proc_net=init_net.proc_net;
268 int errorcode;
270 dir_name = compat_dir_name? "ipt_condition": "nf_condition";
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;
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;
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;
293 return 0;
297 static void __exit
298 fini(void)
300 xt_unregister_match(&condition6_match);
301 xt_unregister_match(&condition_match);
302 remove_proc_entry(dir_name, init_net.proc_net);
305 module_init(init);
306 module_exit(fini);