diff options
Diffstat (limited to 'release/src/linux/linux/net/ipv6/netfilter')
6 files changed, 647 insertions, 6 deletions
diff --git a/release/src/linux/linux/net/ipv6/netfilter/Config.in b/release/src/linux/linux/net/ipv6/netfilter/Config.in index 062ed247..5d2dac2f 100644 --- a/release/src/linux/linux/net/ipv6/netfilter/Config.in +++ b/release/src/linux/linux/net/ipv6/netfilter/Config.in @@ -17,6 +17,7 @@ tristate 'IP6 tables support (required for filtering/masq/NAT)' CONFIG_IP6_NF_IP if [ "$CONFIG_IP6_NF_IPTABLES" != "n" ]; then # The simple matches. dep_tristate ' limit match support' CONFIG_IP6_NF_MATCH_LIMIT $CONFIG_IP6_NF_IPTABLES + dep_tristate ' condition match support' CONFIG_IP6_NF_MATCH_CONDITION $CONFIG_IP6_NF_IPTABLES dep_tristate ' MAC address match support' CONFIG_IP6_NF_MATCH_MAC $CONFIG_IP6_NF_IPTABLES dep_tristate ' Multiple port match support' CONFIG_IP6_NF_MATCH_MULTIPORT $CONFIG_IP6_NF_IPTABLES if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then @@ -55,6 +56,9 @@ if [ "$CONFIG_IP6_NF_IPTABLES" != "n" ]; then if [ "$CONFIG_IP6_NF_MANGLE" != "n" ]; then # dep_tristate ' TOS target support' CONFIG_IP6_NF_TARGET_TOS $CONFIG_IP_NF_MANGLE dep_tristate ' MARK target support' CONFIG_IP6_NF_TARGET_MARK $CONFIG_IP6_NF_MANGLE + dep_tristate ' ROUTE target support' CONFIG_IP6_NF_TARGET_ROUTE $CONFIG_IP6_NF_MANGLE + + dep_tristate ' IMQ target support' CONFIG_IP6_NF_TARGET_IMQ $CONFIG_IP6_NF_MANGLE fi #dep_tristate ' LOG target support' CONFIG_IP6_NF_TARGET_LOG $CONFIG_IP6_NF_IPTABLES fi diff --git a/release/src/linux/linux/net/ipv6/netfilter/Makefile b/release/src/linux/linux/net/ipv6/netfilter/Makefile index dfd36a89..2bd664f4 100644 --- a/release/src/linux/linux/net/ipv6/netfilter/Makefile +++ b/release/src/linux/linux/net/ipv6/netfilter/Makefile @@ -14,6 +14,7 @@ export-objs := ip6_tables.o # Link order matters here. obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o obj-$(CONFIG_IP6_NF_MATCH_LIMIT) += ip6t_limit.o +obj-$(CONFIG_IP6_NF_MATCH_CONDITION) += ip6t_condition.o obj-$(CONFIG_IP6_NF_MATCH_MARK) += ip6t_mark.o obj-$(CONFIG_IP6_NF_MATCH_LENGTH) += ip6t_length.o obj-$(CONFIG_IP6_NF_MATCH_MAC) += ip6t_mac.o @@ -23,6 +24,8 @@ obj-$(CONFIG_IP6_NF_MATCH_OWNER) += ip6t_owner.o obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o obj-$(CONFIG_IP6_NF_TARGET_MARK) += ip6t_MARK.o +obj-$(CONFIG_IP6_NF_TARGET_ROUTE) += ip6t_ROUTE.o +obj-$(CONFIG_IP6_NF_TARGET_IMQ) += ip6t_IMQ.o obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o diff --git a/release/src/linux/linux/net/ipv6/netfilter/ip6_tables.c b/release/src/linux/linux/net/ipv6/netfilter/ip6_tables.c index b521af78..8eac7586 100644 --- a/release/src/linux/linux/net/ipv6/netfilter/ip6_tables.c +++ b/release/src/linux/linux/net/ipv6/netfilter/ip6_tables.c @@ -1241,13 +1241,7 @@ do_add_counters(void *user, unsigned int len) goto free; write_lock_bh(&t->lock); - /************************************* - * modify by tanghui @ 2006-10-11 - * for a RACE CONDITION in the "do_add_counters()" function - *************************************/ if (t->private->number != paddc->num_counters) { - if (t->private->number != tmp.num_counters) { - /*************************************/ ret = -EINVAL; goto unlock_up_free; } diff --git a/release/src/linux/linux/net/ipv6/netfilter/ip6t_IMQ.c b/release/src/linux/linux/net/ipv6/netfilter/ip6t_IMQ.c new file mode 100644 index 00000000..760d7447 --- /dev/null +++ b/release/src/linux/linux/net/ipv6/netfilter/ip6t_IMQ.c @@ -0,0 +1,78 @@ +/* This target marks packets to be enqueued to an imq device */ +#include <linux/module.h> +#include <linux/skbuff.h> +#include <linux/netfilter_ipv6/ip6_tables.h> +#include <linux/netfilter_ipv6/ip6t_IMQ.h> +#include <linux/imq.h> + +static unsigned int imq_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + struct ip6t_imq_info *mr = (struct ip6t_imq_info*)targinfo; + + (*pskb)->imq_flags = mr->todev | IMQ_F_ENQUEUE; + (*pskb)->nfcache |= NFC_ALTERED; + + return IP6T_CONTINUE; +} + +static int imq_checkentry(const char *tablename, + const struct ip6t_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + struct ip6t_imq_info *mr; + + if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_imq_info))) { + printk(KERN_WARNING "IMQ: invalid targinfosize\n"); + return 0; + } + mr = (struct ip6t_imq_info*)targinfo; + + if (strcmp(tablename, "mangle") != 0) { + printk(KERN_WARNING + "IMQ: IMQ can only be called from \"mangle\" table, not \"%s\"\n", + tablename); + return 0; + } + + if (mr->todev > IMQ_MAX_DEVS) { + printk(KERN_WARNING + "IMQ: invalid device specified, highest is %u\n", + IMQ_MAX_DEVS); + return 0; + } + + return 1; +} + +static struct ip6t_target ip6t_imq_reg = { + { NULL, NULL}, + "IMQ", + imq_target, + imq_checkentry, + NULL, + THIS_MODULE +}; + +static int __init init(void) +{ + if (ip6t_register_target(&ip6t_imq_reg)) + return -EINVAL; + + return 0; +} + +static void __exit fini(void) +{ + ip6t_unregister_target(&ip6t_imq_reg); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff --git a/release/src/linux/linux/net/ipv6/netfilter/ip6t_ROUTE.c b/release/src/linux/linux/net/ipv6/netfilter/ip6t_ROUTE.c new file mode 100644 index 00000000..bb6d11f8 --- /dev/null +++ b/release/src/linux/linux/net/ipv6/netfilter/ip6t_ROUTE.c @@ -0,0 +1,308 @@ +/* + * This implements the ROUTE v6 target, which enables you to setup unusual + * routes not supported by the standard kernel routing table. + * + * Copyright (C) 2003 Cedric de Launois <delaunois@info.ucl.ac.be> + * + * v 1.1 2004/11/23 + * + * This software is distributed under GNU GPL v2, 1991 + */ + +#include <linux/module.h> +#include <linux/skbuff.h> +#include <linux/ipv6.h> +#include <linux/netfilter_ipv6/ip6_tables.h> +#include <linux/netfilter_ipv6/ip6t_ROUTE.h> +#include <linux/netdevice.h> +#include <net/ipv6.h> +#include <net/ndisc.h> +#include <net/ip6_route.h> +#include <linux/icmpv6.h> + +#if 1 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +#define NIP6(addr) \ + ntohs((addr).s6_addr16[0]), \ + ntohs((addr).s6_addr16[1]), \ + ntohs((addr).s6_addr16[2]), \ + ntohs((addr).s6_addr16[3]), \ + ntohs((addr).s6_addr16[4]), \ + ntohs((addr).s6_addr16[5]), \ + ntohs((addr).s6_addr16[6]), \ + ntohs((addr).s6_addr16[7]) + +/* Route the packet according to the routing keys specified in + * route_info. Keys are : + * - ifindex : + * 0 if no oif preferred, + * otherwise set to the index of the desired oif + * - route_info->gw : + * 0 if no gateway specified, + * otherwise set to the next host to which the pkt must be routed + * If success, skb->dev is the output device to which the packet must + * be sent and skb->dst is not NULL + * + * RETURN: 1 if the packet was succesfully routed to the + * destination desired + * 0 if the kernel routing table could not route the packet + * according to the keys specified + */ +static int +route6(struct sk_buff *skb, + unsigned int ifindex, + const struct ip6t_route_target_info *route_info) +{ + struct rt6_info *rt = NULL; + struct ipv6hdr *ipv6h = skb->nh.ipv6h; + struct in6_addr *gw = (struct in6_addr*)&route_info->gw; + + DEBUGP("ip6t_ROUTE: called with: "); + DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->daddr)); + DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(*gw)); + DEBUGP("OUT=%s\n", route_info->oif); + + if (ipv6_addr_any(gw)) + rt = rt6_lookup(&ipv6h->daddr, &ipv6h->saddr, ifindex, 1); + else + rt = rt6_lookup(gw, &ipv6h->saddr, ifindex, 1); + + if (!rt) + goto no_route; + + DEBUGP("ip6t_ROUTE: routing gives: "); + DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_dst.addr)); + DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_gateway)); + DEBUGP("OUT=%s\n", rt->rt6i_dev->name); + + if (ifindex && rt->rt6i_dev->ifindex!=ifindex) + goto wrong_route; + + if (!rt->rt6i_nexthop) { + DEBUGP("ip6t_ROUTE: discovering neighbour\n"); + rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_dst.addr); + } + + /* Drop old route. */ + dst_release(skb->dst); + skb->dst = &rt->u.dst; + skb->dev = rt->rt6i_dev; + return 1; + + wrong_route: + dst_release(&rt->u.dst); + no_route: + if (!net_ratelimit()) + return 0; + + printk("ip6t_ROUTE: no explicit route found "); + if (ifindex) + printk("via interface %s ", route_info->oif); + if (!ipv6_addr_any(gw)) + printk("via gateway %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", NIP6(*gw)); + printk("\n"); + return 0; +} + + +/* Stolen from ip6_output_finish + * PRE : skb->dev is set to the device we are leaving by + * skb->dst is not NULL + * POST: the packet is sent with the link layer header pushed + * the packet is destroyed + */ +static void ip_direct_send(struct sk_buff *skb) +{ + struct dst_entry *dst = skb->dst; + struct hh_cache *hh = dst->hh; + + if (hh) { + read_lock_bh(&hh->hh_lock); + memcpy(skb->data - 16, hh->hh_data, 16); + read_unlock_bh(&hh->hh_lock); + skb_push(skb, hh->hh_len); + hh->hh_output(skb); + } else if (dst->neighbour) + dst->neighbour->output(skb); + else { + if (net_ratelimit()) + DEBUGP(KERN_DEBUG "ip6t_ROUTE: no hdr & no neighbour cache!\n"); + kfree_skb(skb); + } +} + + +static unsigned int +route6_oif(const struct ip6t_route_target_info *route_info, + struct sk_buff *skb) +{ + unsigned int ifindex = 0; + struct net_device *dev_out = NULL; + + /* The user set the interface name to use. + * Getting the current interface index. + */ + if ((dev_out = dev_get_by_name(route_info->oif))) { + ifindex = dev_out->ifindex; + } else { + /* Unknown interface name : packet dropped */ + if (net_ratelimit()) + DEBUGP("ip6t_ROUTE: oif interface %s not found\n", route_info->oif); + + if (route_info->flags & IP6T_ROUTE_CONTINUE) + return IP6T_CONTINUE; + else + return NF_DROP; + } + + /* Trying the standard way of routing packets */ + if (route6(skb, ifindex, route_info)) { + dev_put(dev_out); + if (route_info->flags & IP6T_ROUTE_CONTINUE) + return IP6T_CONTINUE; + + ip_direct_send(skb); + return NF_STOLEN; + } else + return NF_DROP; +} + + +static unsigned int +route6_gw(const struct ip6t_route_target_info *route_info, + struct sk_buff *skb) +{ + if (route6(skb, 0, route_info)) { + if (route_info->flags & IP6T_ROUTE_CONTINUE) + return IP6T_CONTINUE; + + ip_direct_send(skb); + return NF_STOLEN; + } else + return NF_DROP; +} + + +static unsigned int +ip6t_route_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ip6t_route_target_info *route_info = targinfo; + struct sk_buff *skb = *pskb; + struct in6_addr *gw = (struct in6_addr*)&route_info->gw; + unsigned int res; + + if (route_info->flags & IP6T_ROUTE_CONTINUE) + goto do_it; + + /* If we are at PREROUTING or INPUT hook + * the TTL isn't decreased by the IP stack + */ + if (hooknum == NF_IP6_PRE_ROUTING || + hooknum == NF_IP6_LOCAL_IN) { + + struct ipv6hdr *ipv6h = skb->nh.ipv6h; + + if (ipv6h->hop_limit <= 1) { + /* Force OUTPUT device used as source address */ + skb->dev = skb->dst->dev; + + icmpv6_send(skb, ICMPV6_TIME_EXCEED, + ICMPV6_EXC_HOPLIMIT, 0, skb->dev); + + return NF_DROP; + } + + ipv6h->hop_limit--; + } + + if ((route_info->flags & IP6T_ROUTE_TEE)) { + /* + * Copy the *pskb, and route the copy. Will later return + * IP6T_CONTINUE for the original skb, which should continue + * on its way as if nothing happened. The copy should be + * independantly delivered to the ROUTE --gw. + */ + skb = skb_copy(*pskb, GFP_ATOMIC); + if (!skb) { + if (net_ratelimit()) + DEBUGP(KERN_DEBUG "ip6t_ROUTE: copy failed!\n"); + return IP6T_CONTINUE; + } + } + +do_it: + if (route_info->oif[0]) { + res = route6_oif(route_info, skb); + } else if (!ipv6_addr_any(gw)) { + res = route6_gw(route_info, skb); + } else { + if (net_ratelimit()) + DEBUGP(KERN_DEBUG "ip6t_ROUTE: no parameter !\n"); + res = IP6T_CONTINUE; + } + + if ((route_info->flags & IP6T_ROUTE_TEE)) + res = IP6T_CONTINUE; + + return res; +} + + +static int +ip6t_route_checkentry(const char *tablename, + const struct ip6t_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + if (strcmp(tablename, "mangle") != 0) { + printk("ip6t_ROUTE: can only be called from \"mangle\" table.\n"); + return 0; + } + + if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_route_target_info))) { + printk(KERN_WARNING "ip6t_ROUTE: targinfosize %u != %Zu\n", + targinfosize, + IP6T_ALIGN(sizeof(struct ip6t_route_target_info))); + return 0; + } + + return 1; +} + + +static struct ip6t_target ip6t_route_reg = { + .name = "ROUTE", + .target = ip6t_route_target, + .checkentry = ip6t_route_checkentry, + .me = THIS_MODULE +}; + + +static int __init init(void) +{ + printk(KERN_DEBUG "registering ipv6 ROUTE target\n"); + if (ip6t_register_target(&ip6t_route_reg)) + return -EINVAL; + + return 0; +} + + +static void __exit fini(void) +{ + ip6t_unregister_target(&ip6t_route_reg); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff --git a/release/src/linux/linux/net/ipv6/netfilter/ip6t_condition.c b/release/src/linux/linux/net/ipv6/netfilter/ip6t_condition.c new file mode 100644 index 00000000..15d805e6 --- /dev/null +++ b/release/src/linux/linux/net/ipv6/netfilter/ip6t_condition.c @@ -0,0 +1,254 @@ +/*-------------------------------------------*\ +| Netfilter Condition Module for IPv6 | +| | +| Description: This module allows firewall | +| rules to match using condition variables | +| stored in /proc files. | +| | +| Author: Stephane Ouellette 2003-02-10 | +| <ouellettes@videotron.ca> | +| | +| This software is distributed under the | +| terms of the GNU GPL. | +\*-------------------------------------------*/ + +#include<linux/module.h> +#include<linux/proc_fs.h> +#include<linux/spinlock.h> +#include<linux/string.h> +#include<asm/atomic.h> +#include<linux/netfilter_ipv6/ip6_tables.h> +#include<linux/netfilter_ipv6/ip6t_condition.h> + + +#ifndef CONFIG_PROC_FS +#error "Proc file system support is required for this module" +#endif + + +MODULE_AUTHOR("Stephane Ouellette <ouellettes@videotron.ca>"); +MODULE_DESCRIPTION("Allows rules to match against condition variables"); +MODULE_LICENSE("GPL"); + + +struct condition_variable { + struct condition_variable *next; + struct proc_dir_entry *status_proc; + atomic_t refcount; + int enabled; /* TRUE == 1, FALSE == 0 */ +}; + + +static rwlock_t list_lock; +static struct condition_variable *head = NULL; +static struct proc_dir_entry *proc_net_condition = NULL; + + +static int +ipt_condition_read_info(char *buffer, char **start, off_t offset, + int length, int *eof, void *data) +{ + struct condition_variable *var = + (struct condition_variable *) data; + + if (offset == 0) { + *start = buffer; + buffer[0] = (var->enabled) ? '1' : '0'; + buffer[1] = '\n'; + return 2; + } + + *eof = 1; + return 0; +} + + +static int +ipt_condition_write_info(struct file *file, const char *buffer, + unsigned long length, void *data) +{ + struct condition_variable *var = + (struct condition_variable *) data; + + if (length) { + /* Match only on the first character */ + switch (buffer[0]) { + case '0': + var->enabled = 0; + break; + case '1': + var->enabled = 1; + } + } + + return (int) length; +} + + +static int +match(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const void *matchinfo, int offset, + const void *hdr, u_int16_t datalen, int *hotdrop) +{ + const struct condition6_info *info = + (const struct condition6_info *) matchinfo; + struct condition_variable *var; + int condition_status = 0; + + read_lock(&list_lock); + + for (var = head; var; var = var->next) { + if (strcmp(info->name, var->status_proc->name) == 0) { + condition_status = var->enabled; + break; + } + } + + read_unlock(&list_lock); + + return condition_status ^ info->invert; +} + + + +static int +checkentry(const char *tablename, const struct ip6t_ip6 *ip, + void *matchinfo, unsigned int matchsize, unsigned int hook_mask) +{ + struct condition6_info *info = + (struct condition6_info *) matchinfo; + struct condition_variable *var, *newvar; + + if (matchsize != IP6T_ALIGN(sizeof(struct condition6_info))) + return 0; + + /* The first step is to check if the condition variable already exists. */ + /* Here, a read lock is sufficient because we won't change the list */ + read_lock(&list_lock); + + for (var = head; var; var = var->next) { + if (strcmp(info->name, var->status_proc->name) == 0) { + atomic_inc(&var->refcount); + read_unlock(&list_lock); + return 1; + } + } + + read_unlock(&list_lock); + + /* At this point, we need to allocate a new condition variable */ + newvar = kmalloc(sizeof(struct condition_variable), GFP_KERNEL); + + if (!newvar) + return -ENOMEM; + + /* Create the condition variable's proc file entry */ + newvar->status_proc = create_proc_entry(info->name, 0644, proc_net_condition); + + if (!newvar->status_proc) { + /* + * There are two possibilities: + * 1- Another condition variable with the same name has been created, which is valid. + * 2- There was a memory allocation error. + */ + kfree(newvar); + read_lock(&list_lock); + + for (var = head; var; var = var->next) { + if (strcmp(info->name, var->status_proc->name) == 0) { + atomic_inc(&var->refcount); + read_unlock(&list_lock); + return 1; + } + } + + read_unlock(&list_lock); + return -ENOMEM; + } + + atomic_set(&newvar->refcount, 1); + newvar->enabled = 0; + newvar->status_proc->owner = THIS_MODULE; + newvar->status_proc->data = newvar; + wmb(); + newvar->status_proc->read_proc = ipt_condition_read_info; + newvar->status_proc->write_proc = ipt_condition_write_info; + + write_lock(&list_lock); + + newvar->next = head; + head = newvar; + + write_unlock(&list_lock); + + return 1; +} + + +static void +destroy(void *matchinfo, unsigned int matchsize) +{ + struct condition6_info *info = + (struct condition6_info *) matchinfo; + struct condition_variable *var, *prev = NULL; + + if (matchsize != IP6T_ALIGN(sizeof(struct condition6_info))) + return; + + write_lock(&list_lock); + + for (var = head; var && strcmp(info->name, var->status_proc->name); + prev = var, var = var->next); + + if (var && atomic_dec_and_test(&var->refcount)) { + if (prev) + prev->next = var->next; + else + head = var->next; + + write_unlock(&list_lock); + remove_proc_entry(var->status_proc->name, proc_net_condition); + kfree(var); + } else + write_unlock(&list_lock); +} + + +static struct ip6t_match condition_match = { + .name = "condition", + .match = &match, + .checkentry = &checkentry, + .destroy = &destroy, + .me = THIS_MODULE +}; + + +static int __init +init(void) +{ + int errorcode; + + rwlock_init(&list_lock); + proc_net_condition = proc_mkdir("ip6t_condition", proc_net); + + if (proc_net_condition) { + errorcode = ipt_register_match(&condition_match); + + if (errorcode) + remove_proc_entry("ip6t_condition", proc_net); + } else + errorcode = -EACCES; + + return errorcode; +} + + +static void __exit +fini(void) +{ + ipt_unregister_match(&condition_match); + remove_proc_entry("ip6t_condition", proc_net); +} + +module_init(init); +module_exit(fini); |