include/linux/netfilter/xt_condition.h | 11 + net/netfilter/Kconfig | 13 + net/netfilter/Makefile | 1 net/netfilter/xt_condition.c | 315 +++++++++++++++++++++++++++++++++ 4 files changed, 340 insertions(+) diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter/xt_condition.h linux/include/linux/netfilter/xt_condition.h --- linux.org/include/linux/netfilter/xt_condition.h 1970-01-01 00:00:00.000000000 +0000 +++ linux/include/linux/netfilter/xt_condition.h 2006-08-29 12:30:00.000000000 +0000 @@ -0,0 +1,11 @@ +#ifndef _XT_CONDITION_H +#define _XT_CONDITION_H + +#define CONDITION_NAME_LEN 32 + +struct condition_info { + char name[CONDITION_NAME_LEN]; + int invert; +}; + +#endif /* _XT_CONDITION_H */ diff -Nur --exclude '*.orig' linux.org/net/netfilter/Kconfig linux/net/netfilter/Kconfig --- linux.org/net/netfilter/Kconfig 2006-06-18 01:49:35.000000000 +0000 +++ linux/net/netfilter/Kconfig 2006-08-29 12:30:00.000000000 +0000 @@ -388,5 +388,18 @@ To compile it as a module, choose M here. If unsure, say N. +config NETFILTER_XT_MATCH_CONDITION + tristate '"condition" match support' + depends on NETFILTER_XTABLES + help + This option allows you to match firewall rules against condition + variables stored in the /proc/net/nf_condition directory. + + N.B.: older versions used /proc/net/ipt_condition. You can + reenable it with "compat_dir_name". + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + endmenu diff -Nur --exclude '*.orig' linux.org/net/netfilter/Makefile linux/net/netfilter/Makefile --- linux.org/net/netfilter/Makefile 2006-06-18 01:49:35.000000000 +0000 +++ linux/net/netfilter/Makefile 2006-08-29 12:30:00.000000000 +0000 @@ -0,0 +0,1 @@ +obj-$(CONFIG_NETFILTER_XT_MATCH_LENGTH) += xt_condition.o diff -Nur --exclude '*.orig' linux.org/net/netfilter/xt_condition.c linux/net/netfilter/xt_condition.c --- linux.org/net/netfilter/xt_condition.c 1970-01-01 00:00:00.000000000 +0000 +++ linux/net/netfilter/xt_condition.c 2006-08-29 12:30:00.000000000 +0000 @@ -0,0 +1,315 @@ +/*-------------------------------------------*\ +| Netfilter Condition Module | +| | +| Description: This module allows firewall | +| rules to match using condition variables | +| stored in /proc files. | +| | +| Author: Stephane Ouellette 2002-10-22 | +| | +| Massimiliano Hofer 2006-05-15 | +| | +| | +| History: | +| 2003-02-10 Second version with improved | +| locking and simplified code. | +| 2006-05-15 2.6.16 adaptations. | +| Locking overhaul. | +| Various bug fixes. | +| | +| This software is distributed under the | +| terms of the GNU GPL. | +\*-------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_PROC_FS +#error "Proc file system support is required for this module" +#endif + +/* Defaults, these can be overridden on the module command-line. */ +static unsigned int condition_list_perms = 0644; +static unsigned int compat_dir_name = 0; +static unsigned int condition_uid_perms = 0; +static unsigned int condition_gid_perms = 0; + +MODULE_AUTHOR("Stephane Ouellette and Massimiliano Hofer "); +MODULE_DESCRIPTION("Allows rules to match against condition variables"); +MODULE_LICENSE("GPL"); +module_param(condition_list_perms, uint, 0600); +MODULE_PARM_DESC(condition_list_perms,"permissions on /proc/net/nf_condition/* files"); +module_param(condition_uid_perms, uint, 0600); +MODULE_PARM_DESC(condition_uid_perms,"user owner of /proc/net/nf_condition/* files"); +module_param(condition_gid_perms, uint, 0600); +MODULE_PARM_DESC(condition_gid_perms,"group owner of /proc/net/nf_condition/* files"); +module_param(compat_dir_name, bool, 0400); +MODULE_PARM_DESC(compat_dir_name,"use old style /proc/net/ipt_condition/* files"); +MODULE_ALIAS("ipt_condition"); +MODULE_ALIAS("ip6t_condition"); + +struct condition_variable { + struct list_head list; + struct proc_dir_entry *status_proc; + unsigned int refcount; + int enabled; /* TRUE == 1, FALSE == 0 */ +}; + +/* proc_lock is a user context only semaphore used for write access */ +/* to the conditions' list. */ +static DECLARE_MUTEX(proc_lock); + +static LIST_HEAD(conditions_list); +static struct proc_dir_entry *proc_net_condition = NULL; +static const char *dir_name; + +static int +xt_condition_read_info(char __user *buffer, char **start, off_t offset, + int length, int *eof, void *data) +{ + struct condition_variable *var = + (struct condition_variable *) data; + + buffer[0] = (var->enabled) ? '1' : '0'; + buffer[1] = '\n'; + if (length>=2) + *eof = 1; + + return 2; +} + + +static int +xt_condition_write_info(struct file *file, const char __user *buffer, + unsigned long length, void *data) +{ + struct condition_variable *var = + (struct condition_variable *) data; + char newval; + + if (length>0) { + if (get_user(newval, buffer)) + return -EFAULT; + /* Match only on the first character */ + switch (newval) { + case '0': + var->enabled = 0; + break; + case '1': + var->enabled = 1; + break; + } + } + + return (int) length; +} + + +static int +match(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *matchinfo, int offset, + unsigned int protoff, int *hotdrop) +{ + const struct condition_info *info = + (const struct condition_info *) matchinfo; + struct condition_variable *var; + int condition_status = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(var, &conditions_list, list) { + if (strcmp(info->name, var->status_proc->name) == 0) { + condition_status = var->enabled; + break; + } + } + rcu_read_unlock(); + + return condition_status ^ info->invert; +} + + + +static int +checkentry(const char *tablename, const void *ip, + const struct xt_match *match, + void *matchinfo, unsigned int matchsize, + unsigned int hook_mask) +{ + static const char * const forbidden_names[]={ "", ".", ".." }; + struct condition_info *info = (struct condition_info *) matchinfo; + struct list_head *pos; + struct condition_variable *var, *newvar; + + int i; + + /* We don't want a '/' in a proc file name. */ + for (i=0; i < CONDITION_NAME_LEN && info->name[i] != '\0'; i++) + if (info->name[i] == '/') + return 0; + /* We can't handle file names longer than CONDITION_NAME_LEN and */ + /* we want a NULL terminated string. */ + if (i == CONDITION_NAME_LEN) + return 0; + + /* We don't want certain reserved names. */ + for (i=0; i < sizeof(forbidden_names)/sizeof(char *); i++) + if(strcmp(info->name, forbidden_names[i])==0) + return 0; + + /* Let's acquire the lock, check for the condition and add it */ + /* or increase the reference counter. */ + if (down_interruptible(&proc_lock)) + return -EINTR; + + list_for_each(pos, &conditions_list) { + var = list_entry(pos, struct condition_variable, list); + if (strcmp(info->name, var->status_proc->name) == 0) { + var->refcount++; + up(&proc_lock); + return 1; + } + } + + /* At this point, we need to allocate a new condition variable. */ + newvar = kmalloc(sizeof(struct condition_variable), GFP_KERNEL); + + if (!newvar) { + up(&proc_lock); + return -ENOMEM; + } + + /* Create the condition variable's proc file entry. */ + newvar->status_proc = create_proc_entry(info->name, condition_list_perms, proc_net_condition); + + if (!newvar->status_proc) { + kfree(newvar); + up(&proc_lock); + return -ENOMEM; + } + + newvar->refcount = 1; + newvar->enabled = 0; + newvar->status_proc->owner = THIS_MODULE; + newvar->status_proc->data = newvar; + wmb(); + newvar->status_proc->read_proc = xt_condition_read_info; + newvar->status_proc->write_proc = xt_condition_write_info; + + list_add_rcu(&newvar->list, &conditions_list); + + newvar->status_proc->uid = condition_uid_perms; + newvar->status_proc->gid = condition_gid_perms; + + up(&proc_lock); + + return 1; +} + + +static void +destroy(const struct xt_match *match, void *matchinfo, + unsigned int matchsize) +{ + struct condition_info *info = (struct condition_info *) matchinfo; + struct list_head *pos; + struct condition_variable *var; + + if (matchsize != XT_ALIGN(sizeof(struct condition_info))) + return; + + down(&proc_lock); + + list_for_each(pos, &conditions_list) { + var = list_entry(pos, struct condition_variable, list); + if (strcmp(info->name, var->status_proc->name) == 0) { + if (--var->refcount == 0) { + list_del_rcu(pos); + remove_proc_entry(var->status_proc->name, proc_net_condition); + up(&proc_lock); + /* synchronize_rcu() would be goog enough, but synchronize_net() */ + /* guarantees that no packet will go out with the old rule after */ + /* succesful removal. */ + synchronize_net(); + kfree(var); + return; + } + break; + } + } + + up(&proc_lock); +} + + +static struct xt_match condition_match = { + .name = "condition", + .family = AF_INET, + .matchsize = sizeof(struct condition_info), + .match = &match, + .checkentry = &checkentry, + .destroy = &destroy, + .me = THIS_MODULE +}; + +static struct xt_match condition6_match = { + .name = "condition", + .family = AF_INET6, + .matchsize = sizeof(struct condition_info), + .match = &match, + .checkentry = &checkentry, + .destroy = &destroy, + .me = THIS_MODULE +}; + +static int __init +init(void) +{ + int errorcode; + + dir_name = compat_dir_name? "ipt_condition": "nf_condition"; + + proc_net_condition = proc_mkdir(dir_name, proc_net); + if (!proc_net_condition) { + remove_proc_entry(dir_name, proc_net); + return -EACCES; + } + + errorcode = xt_register_match(&condition_match); + if (errorcode) { + xt_unregister_match(&condition_match); + remove_proc_entry(dir_name, proc_net); + return errorcode; + } + + errorcode = xt_register_match(&condition6_match); + if (errorcode) { + xt_unregister_match(&condition6_match); + xt_unregister_match(&condition_match); + remove_proc_entry(dir_name, proc_net); + return errorcode; + } + + return 0; +} + + +static void __exit +fini(void) +{ + xt_unregister_match(&condition6_match); + xt_unregister_match(&condition_match); + remove_proc_entry(dir_name, proc_net); +} + +module_init(init); +module_exit(fini);