--- /dev/null
+/* Shared library add-on to iptables for condition match */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <xtables.h>
+#include "xt_condition.h"
+
+static void condition_help(void)
+{
+ printf("condition match options:\n"
+ "--condition [!] filename "
+ "Match on boolean value stored in /proc file\n");
+}
+
+static const struct option condition_opts[] = {
+ { .name = "condition", .has_arg = 1, .flag = 0, .val = 'X' },
+ { .name = 0 }
+};
+
+static int condition_parse(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_match **match)
+{
+ struct condition_info *info =
+ (struct condition_info *) (*match)->data;
+
+ if (c == 'X') {
+ if (*flags)
+ exit_error(PARAMETER_PROBLEM,
+ "Can't specify multiple conditions");
+
+ check_inverse(optarg, &invert, &optind, 0);
+
+ if (strlen(argv[optind - 1]) < CONDITION_NAME_LEN)
+ strcpy(info->name, argv[optind - 1]);
+ else
+ exit_error(PARAMETER_PROBLEM,
+ "File name too long");
+
+ info->invert = invert;
+ *flags = 1;
+ return 1;
+ }
+
+ return 0;
+}
+
+static void condition_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM,
+ "Condition match: must specify --condition");
+}
+
+static void condition_print(const void *ip, const struct xt_entry_match *match,
+ int numeric)
+{
+ const struct condition_info *info =
+ (const struct condition_info *) match->data;
+
+ printf("condition %s%s ", (info->invert) ? "!" : "", info->name);
+}
+
+
+static void condition_save(const void *ip, const struct xt_entry_match *match)
+{
+ const struct condition_info *info =
+ (const struct condition_info *) match->data;
+
+ printf("--condition %s\"%s\" ", (info->invert) ? "! " : "", info->name);
+}
+
+static struct xtables_match condition_match = {
+ .name = "condition",
+ .version = XTABLES_VERSION,
+ .size = XT_ALIGN(sizeof(struct condition_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct condition_info)),
+ .help = condition_help,
+ .parse = condition_parse,
+ .final_check = condition_check,
+ .print = condition_print,
+ .save = condition_save,
+ .extra_opts = condition_opts,
+};
+
+void _init(void);
+void _init(void)
+{
+ xtables_register_match(&condition_match);
+}
--- /dev/null
+/*-------------------------------------------*\
+| Netfilter Condition Module |
+| |
+| Description: This module allows firewall |
+| rules to match using condition variables |
+| stored in /proc files. |
+| |
+| Author: Stephane Ouellette 2002-10-22 |
+| <ouellettes@videotron.ca> |
+| Massimiliano Hofer 2006-05-15 |
+| <max@nucleus.it> |
+| |
+| 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 <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+#include <linux/netfilter/x_tables.h>
+#include "xt_condition.h"
+#include "compat_xtables.h"
+
+#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 <ouellettes@videotron.ca> and Massimiliano Hofer <max@nucleus.it>");
+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) != 0)
+ 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;
+}
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+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)
+#else
+bool
+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, bool *hotdrop)
+#endif
+{
+ 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;
+}
+
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static int
+#else
+bool
+#endif
+checkentry(const char *tablename, const void *ip,
+ const struct xt_match *match,
+ void *matchinfo,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+ unsigned int matchsize,
+#endif
+ 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 == NULL) {
+ 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 == NULL) {
+ 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
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+destroy(const struct xt_match *match, void *matchinfo,
+ unsigned int matchsize)
+#else
+destroy(const struct xt_match *match, void *matchinfo)
+#endif
+{
+ struct condition_info *info = (struct condition_info *) matchinfo;
+ struct list_head *pos;
+ struct condition_variable *var;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+ if (matchsize != XT_ALIGN(sizeof(struct condition_info)))
+ return;
+#endif
+
+ 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 = PF_INET,
+ .matchsize = sizeof(struct condition_info),
+ .match = &match,
+ .checkentry = &checkentry,
+ .destroy = &destroy,
+ .me = THIS_MODULE
+};
+
+static struct xt_match condition6_match = {
+ .name = "condition",
+ .family = PF_INET6,
+ .matchsize = sizeof(struct condition_info),
+ .match = &match,
+ .checkentry = &checkentry,
+ .destroy = &destroy,
+ .me = THIS_MODULE
+};
+
+static int __init
+init(void)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ struct proc_dir_entry * const proc_net=init_net.proc_net;
+#endif
+ int errorcode;
+
+ dir_name = compat_dir_name? "ipt_condition": "nf_condition";
+
+ proc_net_condition = proc_mkdir(dir_name, proc_net);
+ if (proc_net_condition == NULL) {
+ 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);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ remove_proc_entry(dir_name, init_net.proc_net);
+#else
+ remove_proc_entry(dir_name, proc_net);
+#endif
+}
+
+module_init(init);
+module_exit(fini);