diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ip_pool.h linux-2.6.3/include/linux/netfilter_ipv4/ip_pool.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ip_pool.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ip_pool.h 2004-02-19 08:47:30.712978249 +0100 @@ -0,0 +1,64 @@ +#ifndef _IP_POOL_H +#define _IP_POOL_H + +/***************************************************************************/ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/ +/***************************************************************************/ + +/* A sockopt of such quality has hardly ever been seen before on the open + * market! This little beauty, hardly ever used: above 64, so it's + * traditionally used for firewalling, not touched (even once!) by the + * 2.0, 2.2 and 2.4 kernels! + * + * Comes with its own certificate of authenticity, valid anywhere in the + * Free world! + * + * Rusty, 19.4.2000 + */ +#define SO_IP_POOL 81 + +typedef int ip_pool_t; /* pool index */ +#define IP_POOL_NONE ((ip_pool_t)-1) + +struct ip_pool_request { + int op; + ip_pool_t index; + u_int32_t addr; + u_int32_t addr2; +}; + +/* NOTE: I deliberately break the first cut ippool utility. Nobody uses it. */ + +#define IP_POOL_BAD001 0x00000010 + +#define IP_POOL_FLUSH 0x00000011 /* req.index, no arguments */ +#define IP_POOL_INIT 0x00000012 /* from addr to addr2 incl. */ +#define IP_POOL_DESTROY 0x00000013 /* req.index, no arguments */ +#define IP_POOL_ADD_ADDR 0x00000014 /* add addr to pool */ +#define IP_POOL_DEL_ADDR 0x00000015 /* del addr from pool */ +#define IP_POOL_HIGH_NR 0x00000016 /* result in req.index */ +#define IP_POOL_LOOKUP 0x00000017 /* result in addr and addr2 */ +#define IP_POOL_USAGE 0x00000018 /* result in addr */ +#define IP_POOL_TEST_ADDR 0x00000019 /* result (0/1) returned */ + +#ifdef __KERNEL__ + +/* NOTE: ip_pool_match() and ip_pool_mod() expect ADDR to be host byte order */ +extern int ip_pool_match(ip_pool_t pool, u_int32_t addr); +extern int ip_pool_mod(ip_pool_t pool, u_int32_t addr, int isdel); + +#endif + +#endif /*_IP_POOL_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ip_set.h linux-2.6.3/include/linux/netfilter_ipv4/ip_set.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ip_set.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ip_set.h 2004-02-09 15:02:00.000000000 +0100 @@ -0,0 +1,440 @@ +#ifndef _IP_SET_H +#define _IP_SET_H + +/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu) + * Patrick Schaaf (bof@bof.de) + * Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * A sockopt of such quality has hardly ever been seen before on the open + * market! This little beauty, hardly ever used: above 64, so it's + * traditionally used for firewalling, not touched (even once!) by the + * 2.0, 2.2 and 2.4 kernels! + * + * Comes with its own certificate of authenticity, valid anywhere in the + * Free world! + * + * Rusty, 19.4.2000 + */ +#define SO_IP_SET 83 + +/* Directions: */ +#define IPSET_SRC 0x01 +#define IPSET_DST 0x02 +/* Inverse flag for matching: */ +#define IPSET_MATCH_INV 0x04 +/* Overwrite at adding a new entry: */ +#define IPSET_ADD_OVERWRITE 0x08 +/* Set typecodes: */ +#define IPSET_TYPE_IP 0x10 +#define IPSET_TYPE_PORT 0x20 + +/*FIXME: remove this */ +/* #define CONFIG_IP_NF_SET_DEBUG */ + +/* + * Heavily modify by Joakim Axelsson 08.03.2002 + * - Made it more modulebased + * + * Additional heavy modifications by Jozsef Kadlecsik 22.02.2004 + * - multilevel pools (sets) + * - in order to "deal with" backward compatibility, renamed to ipset + */ + +/* Used so that the kernel module and ipset-binary can match thier versions + */ +#define IP_SET_PROTOCOL_VERSION 1 + +#define IP_SET_MAXNAMELEN 32 /* set names and set typenames */ + +/* The max level of the sets. + * Do not increase lightheartedly before eliminating + * the recursive functions from ip_set.c. + */ +/* So many IPs can identify a set: */ +#define IP_SET_SETIP_LEVELS 4 +/* Max level of a set: */ +#define IP_SET_LEVELS (IP_SET_SETIP_LEVELS+1) + +/* Lets work with our own typedef for representing an IP address. + * We hope to make the code more portable, possibly to IPv6... + * + * The representation works in HOST byte order, because most set types + * will perform arithmetic operations and compare operations. + * + * For now the type is an uint32_t. + * + * We do not enforce, but assume that a set may not store more than + * 65536 entries. + * + * Make sure to ONLY use the functions when translating and parsing + * in order to keep the host byte order and make it more portable: + * parse_ip() + * parse_mask() + * parse_ipandmask() + * ip_tostring() + * (Joakim: where are they???) + */ + +typedef uint32_t ip_set_ip_t; + +/* SO_IP_SET operation constants, and their request struct types. + */ + +/* IP_SET_REQ_BASE defines the first components of ANY request structure. + * It is used for all SO_IP_SET calls, set or get. + */ +#define IP_SET_REQ_BASE \ + unsigned op; \ + int id \ + +struct ip_set_req_base { + IP_SET_REQ_BASE; +}; + +struct ip_set_req_std { + IP_SET_REQ_BASE; + ip_set_ip_t ip[IP_SET_SETIP_LEVELS]; + u_int8_t level; +}; + +#define IP_SET_OP_CREATE 0x00000001 /* Create a new (empty) set */ +struct ip_set_req_create { + IP_SET_REQ_BASE; + char name[IP_SET_MAXNAMELEN]; + char typename[IP_SET_LEVELS][IP_SET_MAXNAMELEN]; + u_int8_t levels; +}; + +#define IP_SET_OP_DESTROY 0x00000002 /* Remove a (empty) set */ +/* Uses ip_set_req_std */ + +#define IP_SET_OP_CREATE_CHILD 0x00000003 /* Create a new child set */ +struct ip_set_req_sub { + IP_SET_REQ_BASE; + ip_set_ip_t ip[IP_SET_SETIP_LEVELS]; + u_int8_t level; + u_int8_t childsets; +}; + +#define IP_SET_OP_FLUSH 0x00000004 /* Remove all IPs in a set */ +/* Uses ip_set_req_sub */ + +#define IP_SET_OP_RENAME 0x00000005 /* Rename a set */ +struct ip_set_req_rename { + IP_SET_REQ_BASE; + char newname[IP_SET_MAXNAMELEN]; +}; + +#define IP_SET_OP_SWAP 0x00000006 /* Swap two sets */ +struct ip_set_req_swap { + IP_SET_REQ_BASE; + int to; +}; + +#define IP_SET_OP_ADD_IP 0x00000007 /* Add an IP to a set */ +/* Uses ip_set_req_std, with type specific addage */ + +#define IP_SET_OP_DEL_IP 0x00000008 /* Remove an IP from a set */ +/* Uses ip_set_req_std, with type specific addage */ + +/* Test if an IP is in the set + */ +#define IP_SET_OP_TEST_IP 0x00000009 /* Test an IP in a set */ +struct ip_set_req_test { + IP_SET_REQ_BASE; + ip_set_ip_t ip[IP_SET_SETIP_LEVELS]; + u_int8_t level; + int reply; /* Test result */ +}; + +#define IP_SET_OP_VERSION 0x00000010 +struct ip_set_req_version { + IP_SET_REQ_BASE; + unsigned version; +}; + +/* List operations: + * Size requests are sent by ip_set_req_list + * except for LISTING. + */ +#define IP_SET_OP_LIST_HEADER_SIZE 0x00000101 +#define IP_SET_OP_LIST_HEADER 0x00000102 +#define IP_SET_OP_LIST_MEMBERS_SIZE 0x00000103 +#define IP_SET_OP_LIST_MEMBERS 0x00000104 +#define IP_SET_OP_LIST_CHILDSETS_SIZE 0x00000105 +#define IP_SET_OP_LIST_CHILDSETS 0x00000106 +struct ip_set_req_list { + IP_SET_REQ_BASE; + ip_set_ip_t ip[IP_SET_SETIP_LEVELS]; + u_int8_t level; + size_t size; +}; + +#define IP_SET_OP_LISTING_SIZE 0x00000107 +#define IP_SET_OP_LISTING 0x00000108 + +struct ip_set_req_listing_size { + IP_SET_REQ_BASE; + size_t size; +}; + +struct ip_set_req_listing { + char name[IP_SET_MAXNAMELEN]; + char typename[IP_SET_LEVELS][IP_SET_MAXNAMELEN]; + u_int8_t levels; + unsigned ref; + int id; +}; + +/* Between the iptables(8) set extension modules and the kernel we + * identify a set by its id. + * + * The GETSET_BYNAME call passes the name of a set to the kernel, and + * the a valid set id is returned if the set is still exist. + * The GETSET_BYID call passes the id a set to the kernel, and + * the set name is returned if the set is still exist. + */ +#define IP_SET_OP_GETSET_BYNAME 0x00000011 +struct ip_set_req_get { + IP_SET_REQ_BASE; + unsigned ref; + char name[IP_SET_MAXNAMELEN]; +}; + +#define IP_SET_OP_GETSET_BYID 0x00000012 +/* Uses ip_set_req_get */ + +static inline int bitmap_bytes(ip_set_ip_t a, ip_set_ip_t b) +{ + return 4 * ((((b - a + 8) / 8) + 3) / 4); +} + +#ifdef __KERNEL__ + +#define ip_set_printk(format, args...) \ + do { \ + printk("%s: %s: ", __FILE__, __FUNCTION__); \ + printk(format "\n" , ## args); \ + } while (0) + +#if defined(CONFIG_IP_NF_SET_DEBUG) || defined(CONFIG_IP_NF_SET_DEBUG_MODULE) +#define CONFIG_IP_NF_SET_DEBUG +#define CONFIG_IP_NF_SET_DEBUG_MODULE + +#define DP(format, args...) \ + do { \ + printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\ + printk(format "\n" , ## args); \ + } while (0) +#else +#define DP(format, args...) +#endif + +/* Generic set type: */ +struct ip_set_private { + struct ip_set_private **childsets; /* child sets */ + + /* type speficic members */ +}; + +/* + * The ip_set_type_t definition - one per set type, e.g. "ipmap". + * + * Each individual set has a pointer, set->type, going to one + * of these structures. Function pointers inside the structure implement + * the real behaviour of the sets. + * + * If not mentioned differently, the implementation behind the function + * pointers of a set_type, is expected to return 0 if ok, and a negative + * errno (e.g. -EINVAL) on error. + */ +struct ip_set_type { + struct list_head list; /* next in list of set types */ + + /* match IP in set - internally required + * return 0 if not in set, 1 if in set or + * negative errno if input was invalid + */ + int (*matchip) (struct ip_set_private *private, + ip_set_ip_t ip, + ip_set_ip_t *id); + + /* test for IP in set (kernel: iptables -m set --entry x) + * return 0 if not in set, 1 if in set. + */ + int (*testip_kernel) (struct ip_set_private *private, + const struct sk_buff * skb, + u_int32_t flags, + ip_set_ip_t *id); + + /* test for IP in set (userspace: ipset -T set --entry x) + * return 0 if not in set, 1 if in set. + */ + int (*testip) (struct ip_set_private *private, + const void *data, size_t size, + ip_set_ip_t *id); + + /* + * Size of the data structure passed by when + * adding/deletin/testing an entry. + */ + size_t reqsize; + + /* Add IP into set (userspace: ipset -A set --entry x) + * Return -EEXIST if the address is already in the set, + * and -ERANGE if the address lies outside the set bounds. + * If the address was not already in the set, 0 is returned. + */ + int (*addip) (struct ip_set_private *private, + const void *data, size_t size, + ip_set_ip_t *id); + + /* Add IP into set (kernel: iptables ... -j SET --entry x) + * Return -EEXIST if the address is already in the set, + * and -ERANGE if the address lies outside the set bounds. + * If the address was not already in the set, 0 is returned. + */ + int (*addip_kernel) (struct ip_set_private *private, + const struct sk_buff * skb, + u_int32_t flags, + ip_set_ip_t *id); + + /* remove IP from set (userspace: ipset -D set --entry x) + * Return -EEXIST if the address is NOT in the set, + * and -ERANGE if the address lies outside the set bounds. + * If the address really was in the set, 0 is returned. + */ + int (*delip) (struct ip_set_private *private, + const void *data, size_t size, + ip_set_ip_t *id); + + /* remove IP from set (kernel: iptables ... -j SET --entry x) + * Return -EEXIST if the address is NOT in the set, + * and -ERANGE if the address lies outside the set bounds. + * If the address really was in the set, 0 is returned. + */ + int (*delip_kernel) (struct ip_set_private *private, + const struct sk_buff * skb, + u_int32_t flags, + ip_set_ip_t *id); + + /* new set creation - allocated type specific items + */ + int (*create) (struct ip_set_private **private, + const void *data, size_t size); + + /* set destruction - free type specific items + * There is no return value. + * Can be called only when child sets are destroyed. + */ + void (*destroy) (struct ip_set_private **private); + + /* set flushing - reset all bits in the set, or something similar. + * There is no return value. + */ + void (*flush) (struct ip_set_private *private); + + /* Listing: Get size needed for header + */ + int (*list_header_size) (const struct ip_set_private *private); + + /* Listing: Get the header + * + * Fill in the information in "data". + * This function is always run after list_header_size() under a + * writelock on the set. Therefor is the length of "data" always + * correct. + */ + void (*list_header) (const struct ip_set_private *private, + void *data); + + /* Listing: Get the size for the set members + */ + int (*list_members_size) (const struct ip_set_private *private); + + /* Listing: Get the set members + * + * Fill in the information in "data". + * This function is always run after list_member_size() under a + * writelock on the set. Therefor is the length of "data" always + * correct. + */ + void (*list_members) (const struct ip_set_private *private, + void *data); + + /* Listing: set size in ids (first id is 0. Cannot change for a set). + */ + ip_set_ip_t (*sizeid) (const struct ip_set_private *private); + + /* Listing: Get the bitmap for the valid childsets + */ + void (*list_childsets) (const struct ip_set_private *private, + void *data); + + char typename[IP_SET_MAXNAMELEN]; + char typecode; + int protocol_version; + + /* Set this to THIS_MODULE if you are a module, otherwise NULL */ + struct module *me; +}; + +extern int ip_set_register_set_type(struct ip_set_type *set_type); +extern void ip_set_unregister_set_type(struct ip_set_type *set_type); + +/* A generic ipset */ +struct ip_set { + struct list_head list; /* next in list of all sets */ + rwlock_t lock; /* a lock for concurrency control */ + unsigned ref; /* reference counter */ + unsigned subref; /* reference counter at creating/destroying childsets */ + u_int8_t levels; /* max levels of subsets */ + struct ip_set_type *type[IP_SET_LEVELS]; /* the set types */ + struct ip_set_private *private; /* type specific data */ + char name[IP_SET_MAXNAMELEN]; /* the proper name of the set */ +}; + +extern struct ip_set **ip_set_list; + +/* register and unregister set pointer references */ +extern struct ip_set *ip_set_get_byname(const char name[IP_SET_MAXNAMELEN], + int *id); +extern struct ip_set *ip_set_get_byid(int id); +extern void ip_set_put(struct ip_set *set); + +/* API for iptables set match, and SET target */ +extern void ip_set_addip_kernel(struct ip_set *set, + const struct sk_buff *skb, + const u_int32_t *flags, + u_int8_t set_level, + u_int8_t ip_level); +extern void ip_set_delip_kernel(struct ip_set *set, + const struct sk_buff *skb, + const u_int32_t *flags, + u_int8_t set_level, + u_int8_t ip_level); +extern int ip_set_testip_kernel(struct ip_set *set, + const struct sk_buff *skb, + const u_int32_t *flags, + u_int8_t set_level, + u_int8_t ip_level); + +#endif /* __KERNEL__ */ + +#endif /*_IP_SET_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ip_set_iphash.h linux-2.6.3/include/linux/netfilter_ipv4/ip_set_iphash.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ip_set_iphash.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ip_set_iphash.h 2004-02-09 15:02:00.000000000 +0100 @@ -0,0 +1,30 @@ +#ifndef __IP_SET_IPHASH_H +#define __IP_SET_IPHASH_H + +#include + +#define SETTYPE_NAME "iphash" +#define MAX_RANGE 0x0000FFFF + +struct ip_set_iphash { + struct ip_set_private **childsets; /* child sets */ + + /* Type speficic members: */ + uint32_t initval; /* initval for jhash_1word */ + ip_set_ip_t hashsize; /* hash size */ + ip_set_ip_t netmask; /* netmask */ + ip_set_ip_t *members; /* the iphash proper */ +}; + +struct ip_set_req_iphash_create { + uint32_t initval; + ip_set_ip_t hashsize; + ip_set_ip_t netmask; +}; + +struct ip_set_req_iphash { + ip_set_ip_t ip; + u_int32_t flags; +}; + +#endif /* __IP_SET_IPHASH_H */ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ip_set_ipmap.h linux-2.6.3/include/linux/netfilter_ipv4/ip_set_ipmap.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ip_set_ipmap.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ip_set_ipmap.h 2004-02-09 15:02:00.000000000 +0100 @@ -0,0 +1,59 @@ +#ifndef __IP_SET_IPMAP_H +#define __IP_SET_IPMAP_H + +#include + +#define SETTYPE_NAME "ipmap" +#define MAX_RANGE 0x0000FFFF + +struct ip_set_ipmap { + struct ip_set_private **childsets; /* child sets */ + + /* Type speficic members: */ + ip_set_ip_t first_ip; /* host byte order, included in range */ + ip_set_ip_t last_ip; /* host byte order, included in range */ + ip_set_ip_t netmask; /* subnet netmask */ + ip_set_ip_t sizeid; /* size of set in ids */ + u_int16_t hosts; /* hosts per subnet */ + void *members; /* the ipmap proper */ +}; + +struct ip_set_req_ipmap_create { + ip_set_ip_t from; + ip_set_ip_t to; + ip_set_ip_t netmask; +}; + +struct ip_set_req_ipmap { + ip_set_ip_t ip; +}; + +unsigned int +mask_to_bits(ip_set_ip_t mask) +{ + unsigned int bits = 32; + ip_set_ip_t maskaddr; + + if (mask == 0xFFFFFFFF) + return bits; + + maskaddr = 0xFFFFFFFE; + while (--bits >= 0 && maskaddr != mask) + maskaddr <<= 1; + + return bits; +} + +ip_set_ip_t +range_to_mask(ip_set_ip_t from, ip_set_ip_t to, unsigned int *bits) +{ + ip_set_ip_t mask = 0xFFFFFFFE; + + *bits = 32; + while (--(*bits) >= 0 && mask && (to & mask) != from) + mask <<= 1; + + return mask; +} + +#endif /* __IP_SET_IPMAP_H */ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ip_set_jhash.h linux-2.6.3/include/linux/netfilter_ipv4/ip_set_jhash.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ip_set_jhash.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ip_set_jhash.h 2004-02-09 15:02:00.000000000 +0100 @@ -0,0 +1,148 @@ +#ifndef _LINUX_IPSET_JHASH_H +#define _LINUX_IPSET_JHASH_H + +/* This is a copy of linux/jhash.h but the types u32/u8 are changed + * to __u32/__u8 so that the header file can be included into + * userspace code as well. Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + */ + +/* jhash.h: Jenkins hash support. + * + * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) + * + * http://burtleburtle.net/bob/hash/ + * + * These are the credits from Bob's sources: + * + * lookup2.c, by Bob Jenkins, December 1996, Public Domain. + * hash(), hash2(), hash3, and mix() are externally useful functions. + * Routines to test the hash are included if SELF_TEST is defined. + * You can use this free for any purpose. It has no warranty. + * + * Copyright (C) 2003 David S. Miller (davem@redhat.com) + * + * I've modified Bob's hash to be useful in the Linux kernel, and + * any bugs present are surely my fault. -DaveM + */ + +/* NOTE: Arguments are modified. */ +#define __jhash_mix(a, b, c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* The golden ration: an arbitrary value */ +#define JHASH_GOLDEN_RATIO 0x9e3779b9 + +/* The most generic version, hashes an arbitrary sequence + * of bytes. No alignment or length assumptions are made about + * the input key. + */ +static inline __u32 jhash(void *key, __u32 length, __u32 initval) +{ + __u32 a, b, c, len; + __u8 *k = key; + + len = length; + a = b = JHASH_GOLDEN_RATIO; + c = initval; + + while (len >= 12) { + a += (k[0] +((__u32)k[1]<<8) +((__u32)k[2]<<16) +((__u32)k[3]<<24)); + b += (k[4] +((__u32)k[5]<<8) +((__u32)k[6]<<16) +((__u32)k[7]<<24)); + c += (k[8] +((__u32)k[9]<<8) +((__u32)k[10]<<16)+((__u32)k[11]<<24)); + + __jhash_mix(a,b,c); + + k += 12; + len -= 12; + } + + c += length; + switch (len) { + case 11: c += ((__u32)k[10]<<24); + case 10: c += ((__u32)k[9]<<16); + case 9 : c += ((__u32)k[8]<<8); + case 8 : b += ((__u32)k[7]<<24); + case 7 : b += ((__u32)k[6]<<16); + case 6 : b += ((__u32)k[5]<<8); + case 5 : b += k[4]; + case 4 : a += ((__u32)k[3]<<24); + case 3 : a += ((__u32)k[2]<<16); + case 2 : a += ((__u32)k[1]<<8); + case 1 : a += k[0]; + }; + + __jhash_mix(a,b,c); + + return c; +} + +/* A special optimized version that handles 1 or more of __u32s. + * The length parameter here is the number of __u32s in the key. + */ +static inline __u32 jhash2(__u32 *k, __u32 length, __u32 initval) +{ + __u32 a, b, c, len; + + a = b = JHASH_GOLDEN_RATIO; + c = initval; + len = length; + + while (len >= 3) { + a += k[0]; + b += k[1]; + c += k[2]; + __jhash_mix(a, b, c); + k += 3; len -= 3; + } + + c += length * 4; + + switch (len) { + case 2 : b += k[1]; + case 1 : a += k[0]; + }; + + __jhash_mix(a,b,c); + + return c; +} + + +/* A special ultra-optimized versions that knows they are hashing exactly + * 3, 2 or 1 word(s). + * + * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally + * done at the end is not done here. + */ +static inline __u32 jhash_3words(__u32 a, __u32 b, __u32 c, __u32 initval) +{ + a += JHASH_GOLDEN_RATIO; + b += JHASH_GOLDEN_RATIO; + c += initval; + + __jhash_mix(a, b, c); + + return c; +} + +static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) +{ + return jhash_3words(a, b, 0, initval); +} + +static inline __u32 jhash_1word(__u32 a, __u32 initval) +{ + return jhash_3words(a, 0, 0, initval); +} + +#endif /* _LINUX_IPSET_JHASH_H */ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ip_set_macipmap.h linux-2.6.3/include/linux/netfilter_ipv4/ip_set_macipmap.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ip_set_macipmap.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ip_set_macipmap.h 2004-02-09 15:02:00.000000000 +0100 @@ -0,0 +1,41 @@ +#ifndef __IP_SET_MACIPMAP_H +#define __IP_SET_MACIPMAP_H + +#include + +#define SETTYPE_NAME "macipmap" +#define MAX_RANGE 0x0000FFFF + +/* general flags */ +#define IPSET_MACIP_MATCHUNSET 1 + +/* per ip flags */ +#define IPSET_MACIP_ISSET 1 + +struct ip_set_macipmap { + struct ip_set_private **childsets; /* child sets */ + + /* Type speficic members: */ + ip_set_ip_t first_ip; /* host byte order, included in range */ + ip_set_ip_t last_ip; /* host byte order, included in range */ + void *members; /* the macipmap proper */ + u_int32_t flags; +}; + +struct ip_set_req_macipmap_create { + ip_set_ip_t from; + ip_set_ip_t to; + u_int32_t flags; +}; + +struct ip_set_req_macipmap { + ip_set_ip_t ip; + unsigned char ethernet[ETH_ALEN]; +}; + +struct ip_set_macip { + unsigned short flags; + unsigned char ethernet[ETH_ALEN]; +}; + +#endif /* __IP_SET_MACIPMAP_H */ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ip_set_portmap.h linux-2.6.3/include/linux/netfilter_ipv4/ip_set_portmap.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ip_set_portmap.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ip_set_portmap.h 2004-02-09 15:02:00.000000000 +0100 @@ -0,0 +1,28 @@ +#ifndef __IP_SET_PORTMAP_H +#define __IP_SET_PORTMAP_H + +#include + +#define SETTYPE_NAME "portmap" +#define MAX_RANGE 0x0000FFFF +#define INVALID_PORT (MAX_RANGE + 1) + +struct ip_set_portmap { + struct ip_set_private **childsets; /* child sets */ + + /* Type speficic members: */ + ip_set_ip_t first_port; /* host byte order, included in range */ + ip_set_ip_t last_port; /* host byte order, included in range */ + void *members; /* the portmap proper */ +}; + +struct ip_set_req_portmap_create { + ip_set_ip_t from; + ip_set_ip_t to; +}; + +struct ip_set_req_portmap { + ip_set_ip_t port; +}; + +#endif /* __IP_SET_PORTMAP_H */ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_NETLINK.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_NETLINK.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_NETLINK.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_NETLINK.h 2004-02-19 08:47:24.269530314 +0100 @@ -0,0 +1,27 @@ +#ifndef _IPT_FWMON_H +#define _IPT_FWMON_H + +/* Bitmask macros */ +#define MASK(x,y) (x & y) +#define MASK_SET(x,y) x |= y +#define MASK_UNSET(x,y) x &= ~y + +#define USE_MARK 0x00000001 +#define USE_DROP 0x00000002 +#define USE_SIZE 0x00000004 + +struct ipt_nldata +{ + unsigned int flags; + unsigned int mark; + unsigned int size; +}; + +/* Old header */ +struct netlink_t { + unsigned int len; + unsigned int mark; + char iface[IFNAMSIZ]; +}; + +#endif /*_IPT_FWMON_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_TTL.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_TTL.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_TTL.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_TTL.h 2004-02-19 08:47:25.605208582 +0100 @@ -0,0 +1,21 @@ +/* TTL modification module for IP tables + * (C) 2000 by Harald Welte */ + +#ifndef _IPT_TTL_H +#define _IPT_TTL_H + +enum { + IPT_TTL_SET = 0, + IPT_TTL_INC, + IPT_TTL_DEC +}; + +#define IPT_TTL_MAXMODE IPT_TTL_DEC + +struct ipt_TTL_info { + u_int8_t mode; + u_int8_t ttl; +}; + + +#endif diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_connlimit.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_connlimit.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_connlimit.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_connlimit.h 2004-02-19 08:47:26.593970414 +0100 @@ -0,0 +1,12 @@ +#ifndef _IPT_CONNLIMIT_H +#define _IPT_CONNLIMIT_H + +struct ipt_connlimit_data; + +struct ipt_connlimit_info { + int limit; + int inverse; + u_int32_t mask; + struct ipt_connlimit_data *data; +}; +#endif /* _IPT_CONNLIMIT_H */ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_dstlimit.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_dstlimit.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_dstlimit.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_dstlimit.h 2004-02-19 08:47:27.235815809 +0100 @@ -0,0 +1,39 @@ +#ifndef _IPT_DSTLIMIT_H +#define _IPT_DSTLIMIT_H + +/* timings are in milliseconds. */ +#define IPT_DSTLIMIT_SCALE 10000 +/* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490 + seconds, or one every 59 hours. */ + +/* details of this structure hidden by the implementation */ +struct ipt_dstlimit_htable; + +#define IPT_DSTLIMIT_HASH_DIP 0x0001 +#define IPT_DSTLIMIT_HASH_DPT 0x0002 +#define IPT_DSTLIMIT_HASH_SIP 0x0004 + +struct dstlimit_cfg { + u_int32_t mode; /* bitmask of IPT_DSTLIMIT_HASH_* */ + u_int32_t avg; /* Average secs between packets * scale */ + u_int32_t burst; /* Period multiplier for upper limit. */ + + /* user specified */ + u_int32_t size; /* how many buckets */ + u_int32_t max; /* max number of entries */ + u_int32_t gc_interval; /* gc interval */ + u_int32_t expire; /* when do entries expire? */ +}; + +struct ipt_dstlimit_info { + char name [IFNAMSIZ]; /* name */ + struct dstlimit_cfg cfg; + struct ipt_dstlimit_htable *hinfo; + + /* Used internally by the kernel */ + union { + void *ptr; + struct ipt_dstlimit_info *master; + } u; +}; +#endif /*_IPT_DSTLIMIT_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_fuzzy.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_fuzzy.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_fuzzy.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_fuzzy.h 2004-02-19 08:47:27.855666503 +0100 @@ -0,0 +1,21 @@ +#ifndef _IPT_FUZZY_H +#define _IPT_FUZZY_H + +#include +#include + +#define MAXFUZZYRATE 10000000 +#define MINFUZZYRATE 3 + +struct ipt_fuzzy_info { + u_int32_t minimum_rate; + u_int32_t maximum_rate; + u_int32_t packets_total; + u_int32_t bytes_total; + u_int32_t previous_time; + u_int32_t present_time; + u_int32_t mean_rate; + u_int8_t acceptance_rate; +}; + +#endif /*_IPT_FUZZY_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_ipv4options.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_ipv4options.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_ipv4options.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_ipv4options.h 2004-02-19 08:47:28.441525384 +0100 @@ -0,0 +1,21 @@ +#ifndef __ipt_ipv4options_h_included__ +#define __ipt_ipv4options_h_included__ + +#define IPT_IPV4OPTION_MATCH_SSRR 0x01 /* For strict source routing */ +#define IPT_IPV4OPTION_MATCH_LSRR 0x02 /* For loose source routing */ +#define IPT_IPV4OPTION_DONT_MATCH_SRR 0x04 /* any source routing */ +#define IPT_IPV4OPTION_MATCH_RR 0x08 /* For Record route */ +#define IPT_IPV4OPTION_DONT_MATCH_RR 0x10 +#define IPT_IPV4OPTION_MATCH_TIMESTAMP 0x20 /* For timestamp request */ +#define IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP 0x40 +#define IPT_IPV4OPTION_MATCH_ROUTER_ALERT 0x80 /* For router-alert */ +#define IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT 0x100 +#define IPT_IPV4OPTION_MATCH_ANY_OPT 0x200 /* match packet with any option */ +#define IPT_IPV4OPTION_DONT_MATCH_ANY_OPT 0x400 /* match packet with no option */ + +struct ipt_ipv4options_info { + u_int16_t options; +}; + + +#endif /* __ipt_ipv4options_h_included__ */ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_mport.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_mport.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_mport.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_mport.h 2004-02-19 08:47:29.017386674 +0100 @@ -0,0 +1,24 @@ +#ifndef _IPT_MPORT_H +#define _IPT_MPORT_H +#include + +#define IPT_MPORT_SOURCE (1<<0) +#define IPT_MPORT_DESTINATION (1<<1) +#define IPT_MPORT_EITHER (IPT_MPORT_SOURCE|IPT_MPORT_DESTINATION) + +#define IPT_MULTI_PORTS 15 + +/* Must fit inside union ipt_matchinfo: 32 bytes */ +/* every entry in ports[] except for the last one has one bit in pflags + * associated with it. If this bit is set, the port is the first port of + * a portrange, with the next entry being the last. + * End of list is marked with pflags bit set and port=65535. + * If 14 ports are used (last one does not have a pflag), the last port + * is repeated to fill the last entry in ports[] */ +struct ipt_mport +{ + u_int8_t flags:2; /* Type of comparison */ + u_int16_t pflags:14; /* Port flags */ + u_int16_t ports[IPT_MULTI_PORTS]; /* Ports */ +}; +#endif /*_IPT_MPORT_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_nth.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_nth.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_nth.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_nth.h 2004-02-19 08:47:29.579251335 +0100 @@ -0,0 +1,19 @@ +#ifndef _IPT_NTH_H +#define _IPT_NTH_H + +#include +#include + +#ifndef IPT_NTH_NUM_COUNTERS +#define IPT_NTH_NUM_COUNTERS 16 +#endif + +struct ipt_nth_info { + u_int8_t every; + u_int8_t not; + u_int8_t startat; + u_int8_t counter; + u_int8_t packet; +}; + +#endif /*_IPT_NTH_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_osf.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_osf.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_osf.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_osf.h 2004-02-19 08:47:30.115122257 +0100 @@ -0,0 +1,130 @@ +/* + * ipt_osf.h + * + * Copyright (c) 2003 Evgeniy Polyakov + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _IPT_OSF_H +#define _IPT_OSF_H + +#define MAXGENRELEN 32 +#define MAXDETLEN 64 + +#define IPT_OSF_GENRE 1 +#define IPT_OSF_SMART 2 +#define IPT_OSF_LOG 4 + +#define IPT_OSF_LOGLEVEL_ALL 0 +#define IPT_OSF_LOGLEVEL_FIRST 1 + +#include + +struct ipt_osf_info +{ + char genre[MAXGENRELEN]; + int len; + unsigned long flags; + int loglevel; + int invert; /* UNSUPPORTED */ +}; + +struct osf_wc +{ + char wc; + unsigned long val; +}; + +/* This struct represents IANA options + * http://www.iana.org/assignments/tcp-parameters + */ +struct osf_opt +{ + unsigned char kind; + unsigned char length; + struct osf_wc wc; +}; + +#ifdef __KERNEL__ + +struct osf_finger +{ + struct list_head flist; + struct osf_wc wss; + unsigned char ttl; + unsigned char df; + unsigned long ss; + char genre[MAXGENRELEN]; + char version[MAXGENRELEN], subtype[MAXGENRELEN]; + + /* Not needed, but for consistency with original table from Michal Zalewski */ + char details[MAXDETLEN]; + + int opt_num; + struct osf_opt opt[MAX_IPOPTLEN]; /* In case it is all NOP or EOL */ + +}; + +/* Defines for IANA option kinds */ + +#define OSFOPT_EOL 0 /* End of options */ +#define OSFOPT_NOP 1 /* NOP */ +#define OSFOPT_MSS 2 /* Maximum segment size */ +#define OSFOPT_WSO 3 /* Window scale option */ +#define OSFOPT_SACKP 4 /* SACK permitted */ +#define OSFOPT_SACK 5 /* SACK */ +#define OSFOPT_ECHO 6 +#define OSFOPT_ECHOREPLY 7 +#define OSFOPT_TS 8 /* Timestamp option */ +#define OSFOPT_POCP 9 /* Partial Order Connection Permitted */ +#define OSFOPT_POSP 10 /* Partial Order Service Profile */ +/* Others are not used in current OSF */ + +static struct osf_opt IANA_opts[] = +{ + {0, 1,}, + {1, 1,}, + {2, 4,}, + {3, 3,}, + {4, 2,}, + {5, 1 ,}, /* SACK length is not defined */ + {6, 6,}, + {7, 6,}, + {8, 10,}, + {9, 2,}, + {10, 3,}, + {11, 1,}, /* CC: Suppose 1 */ + {12, 1,}, /* the same */ + {13, 1,}, /* and here too */ + {14, 3,}, + {15, 1,}, /* TCP Alternate Checksum Data. Length is not defined */ + {16, 1,}, + {17, 1,}, + {18, 3,}, + {19, 18,}, + {20, 1,}, + {21, 1,}, + {22, 1,}, + {23, 1,}, + {24, 1,}, + {25, 1,}, + {26, 1,}, +}; + +#endif /* __KERNEL__ */ + +#endif /* _IPT_OSF_H */ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_pool.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_pool.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_pool.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_pool.h 2004-02-19 08:47:30.712978249 +0100 @@ -0,0 +1,25 @@ +#ifndef _IPT_POOL_H +#define _IPT_POOL_H + +#include + +#define IPT_POOL_INV_SRC 0x00000001 +#define IPT_POOL_INV_DST 0x00000002 +#define IPT_POOL_DEL_SRC 0x00000004 +#define IPT_POOL_DEL_DST 0x00000008 +#define IPT_POOL_INV_MOD_SRC 0x00000010 +#define IPT_POOL_INV_MOD_DST 0x00000020 +#define IPT_POOL_MOD_SRC_ACCEPT 0x00000040 +#define IPT_POOL_MOD_DST_ACCEPT 0x00000080 +#define IPT_POOL_MOD_SRC_DROP 0x00000100 +#define IPT_POOL_MOD_DST_DROP 0x00000200 + +/* match info */ +struct ipt_pool_info +{ + ip_pool_t src; + ip_pool_t dst; + unsigned flags; +}; + +#endif /*_IPT_POOL_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_psd.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_psd.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_psd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_psd.h 2004-02-19 08:47:31.249848931 +0100 @@ -0,0 +1,40 @@ +#ifndef _IPT_PSD_H +#define _IPT_PSD_H + +#include +#include + +/* + * High port numbers have a lower weight to reduce the frequency of false + * positives, such as from passive mode FTP transfers. + */ +#define PORT_WEIGHT_PRIV 3 +#define PORT_WEIGHT_HIGH 1 + +/* + * Port scan detection thresholds: at least COUNT ports need to be scanned + * from the same source, with no longer than DELAY ticks between ports. + */ +#define SCAN_MIN_COUNT 7 +#define SCAN_MAX_COUNT (SCAN_MIN_COUNT * PORT_WEIGHT_PRIV) +#define SCAN_WEIGHT_THRESHOLD SCAN_MAX_COUNT +#define SCAN_DELAY_THRESHOLD (HZ * 3) + +/* + * Keep track of up to LIST_SIZE source addresses, using a hash table of + * HASH_SIZE entries for faster lookups, but limiting hash collisions to + * HASH_MAX source addresses per the same hash value. + */ +#define LIST_SIZE 0x100 +#define HASH_LOG 9 +#define HASH_SIZE (1 << HASH_LOG) +#define HASH_MAX 0x10 + +struct ipt_psd_info { + unsigned int weight_threshold; + unsigned int delay_threshold; + unsigned short lo_ports_weight; + unsigned short hi_ports_weight; +}; + +#endif /*_IPT_PSD_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_quota.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_quota.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_quota.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_quota.h 2004-02-19 08:47:31.780721057 +0100 @@ -0,0 +1,11 @@ +#ifndef _IPT_QUOTA_H +#define _IPT_QUOTA_H + +/* print debug info in both kernel/netfilter module & iptable library */ +//#define DEBUG_IPT_QUOTA + +struct ipt_quota_info { + u_int64_t quota; +}; + +#endif /*_IPT_QUOTA_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_random.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_random.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_random.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_random.h 2004-02-19 08:47:32.335587404 +0100 @@ -0,0 +1,11 @@ +#ifndef _IPT_RAND_H +#define _IPT_RAND_H + +#include +#include + +struct ipt_rand_info { + u_int8_t average; +}; + +#endif /*_IPT_RAND_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_realm.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_realm.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_realm.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_realm.h 2004-02-19 08:47:33.450318894 +0100 @@ -0,0 +1,9 @@ +#ifndef _IPT_REALM_H +#define _IPT_REALM_H + +struct ipt_realm_info { + u_int32_t id; + u_int32_t mask; + u_int8_t invert; +}; +#endif /*_IPT_REALM_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_set.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_set.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_set.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_set.h 2004-02-09 15:02:00.000000000 +0100 @@ -0,0 +1,22 @@ +#ifndef _IPT_SET_H +#define _IPT_SET_H + +#include + +struct ipt_set_info { + int16_t id; + u_int8_t set_level, ip_level; + u_int32_t flags[IP_SET_LEVELS]; +}; + +/* match info */ +struct ipt_set_info_match { + struct ipt_set_info match; +}; + +struct ipt_set_info_target { + struct ipt_set_info add_set; + struct ipt_set_info del_set; +}; + +#endif /*_IPT_SET_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_time.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_time.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_time.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_time.h 2004-02-19 08:47:33.998186927 +0100 @@ -0,0 +1,13 @@ +#ifndef __ipt_time_h_included__ +#define __ipt_time_h_included__ + + +struct ipt_time_info { + u_int8_t days_match; /* 1 bit per day. -SMTWTFS */ + u_int16_t time_start; /* 0 < time_start < 23*60+59 = 1439 */ + u_int16_t time_stop; /* 0:0 < time_stat < 23:59 */ + u_int8_t kerneltime; /* ignore skb time (and use kerneltime) or not. */ +}; + + +#endif /* __ipt_time_h_included__ */ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_u32.h linux-2.6.3/include/linux/netfilter_ipv4/ipt_u32.h --- linux-2.6.3.org/include/linux/netfilter_ipv4/ipt_u32.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv4/ipt_u32.h 2004-02-19 08:47:34.549054238 +0100 @@ -0,0 +1,40 @@ +#ifndef _IPT_U32_H +#define _IPT_U32_H +#include + +enum ipt_u32_ops +{ + IPT_U32_AND, + IPT_U32_LEFTSH, + IPT_U32_RIGHTSH, + IPT_U32_AT +}; + +struct ipt_u32_location_element +{ + u_int32_t number; + u_int8_t nextop; +}; +struct ipt_u32_value_element +{ + u_int32_t min; + u_int32_t max; +}; +/* *** any way to allow for an arbitrary number of elements? + for now I settle for a limit of 10 of each */ +#define U32MAXSIZE 10 +struct ipt_u32_test +{ + u_int8_t nnums; + struct ipt_u32_location_element location[U32MAXSIZE+1]; + u_int8_t nvalues; + struct ipt_u32_value_element value[U32MAXSIZE+1]; +}; + +struct ipt_u32 +{ + u_int8_t ntests; + struct ipt_u32_test tests[U32MAXSIZE+1]; +}; + +#endif /*_IPT_U32_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv6/ip6t_HL.h linux-2.6.3/include/linux/netfilter_ipv6/ip6t_HL.h --- linux-2.6.3.org/include/linux/netfilter_ipv6/ip6t_HL.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv6/ip6t_HL.h 2004-02-19 09:09:04.595401585 +0100 @@ -0,0 +1,22 @@ +/* Hop Limit modification module for ip6tables + * Maciej Soltysiak + * Based on HW's TTL module */ + +#ifndef _IP6T_HL_H +#define _IP6T_HL_H + +enum { + IP6T_HL_SET = 0, + IP6T_HL_INC, + IP6T_HL_DEC +}; + +#define IP6T_HL_MAXMODE IP6T_HL_DEC + +struct ip6t_HL_info { + u_int8_t mode; + u_int8_t hop_limit; +}; + + +#endif diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv6/ip6t_REJECT.h linux-2.6.3/include/linux/netfilter_ipv6/ip6t_REJECT.h --- linux-2.6.3.org/include/linux/netfilter_ipv6/ip6t_REJECT.h 2004-02-18 04:57:12.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv6/ip6t_REJECT.h 2004-02-19 08:47:25.031346811 +0100 @@ -2,15 +2,17 @@ #define _IP6T_REJECT_H enum ip6t_reject_with { - IP6T_ICMP_NET_UNREACHABLE, - IP6T_ICMP_HOST_UNREACHABLE, - IP6T_ICMP_PROT_UNREACHABLE, - IP6T_ICMP_PORT_UNREACHABLE, - IP6T_ICMP_ECHOREPLY + IP6T_ICMP6_NO_ROUTE, + IP6T_ICMP6_ADM_PROHIBITED, + IP6T_ICMP6_NOT_NEIGHBOUR, + IP6T_ICMP6_ADDR_UNREACH, + IP6T_ICMP6_PORT_UNREACH, + IP6T_ICMP6_ECHOREPLY, + IP6T_TCP_RESET }; struct ip6t_reject_info { enum ip6t_reject_with with; /* reject type */ }; -#endif /*_IPT_REJECT_H*/ +#endif /*_IP6T_REJECT_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv6/ip6t_fuzzy.h linux-2.6.3/include/linux/netfilter_ipv6/ip6t_fuzzy.h --- linux-2.6.3.org/include/linux/netfilter_ipv6/ip6t_fuzzy.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv6/ip6t_fuzzy.h 2004-02-19 08:47:27.855666503 +0100 @@ -0,0 +1,21 @@ +#ifndef _IP6T_FUZZY_H +#define _IP6T_FUZZY_H + +#include +#include + +#define MAXFUZZYRATE 10000000 +#define MINFUZZYRATE 3 + +struct ip6t_fuzzy_info { + u_int32_t minimum_rate; + u_int32_t maximum_rate; + u_int32_t packets_total; + u_int32_t bytes_total; + u_int32_t previous_time; + u_int32_t present_time; + u_int32_t mean_rate; + u_int8_t acceptance_rate; +}; + +#endif /*_IP6T_FUZZY_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv6/ip6t_nth.h linux-2.6.3/include/linux/netfilter_ipv6/ip6t_nth.h --- linux-2.6.3.org/include/linux/netfilter_ipv6/ip6t_nth.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv6/ip6t_nth.h 2004-02-19 08:47:29.579251335 +0100 @@ -0,0 +1,19 @@ +#ifndef _IP6T_NTH_H +#define _IP6T_NTH_H + +#include +#include + +#ifndef IP6T_NTH_NUM_COUNTERS +#define IP6T_NTH_NUM_COUNTERS 16 +#endif + +struct ip6t_nth_info { + u_int8_t every; + u_int8_t not; + u_int8_t startat; + u_int8_t counter; + u_int8_t packet; +}; + +#endif /*_IP6T_NTH_H*/ diff -Nur linux-2.6.3.org/include/linux/netfilter_ipv6/ip6t_random.h linux-2.6.3/include/linux/netfilter_ipv6/ip6t_random.h --- linux-2.6.3.org/include/linux/netfilter_ipv6/ip6t_random.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/include/linux/netfilter_ipv6/ip6t_random.h 2004-02-19 08:47:32.336587163 +0100 @@ -0,0 +1,11 @@ +#ifndef _IP6T_RAND_H +#define _IP6T_RAND_H + +#include +#include + +struct ip6t_rand_info { + u_int8_t average; +}; + +#endif /*_IP6T_RAND_H*/ diff -Nur linux-2.6.3.org/net/ipv4/netfilter/Kconfig linux-2.6.3/net/ipv4/netfilter/Kconfig --- linux-2.6.3.org/net/ipv4/netfilter/Kconfig 2004-02-18 04:59:13.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/Kconfig 2004-02-19 09:06:18.111476624 +0100 @@ -579,5 +579,414 @@ To compile it as a module, choose M here. If unsure, say N. +config IP_NF_TARGET_IPV4OPTSSTRIP + tristate 'IPV4OPTSSTRIP target support' + depends on IP_NF_MANGLE + help + This option adds CONFIG_IP_NF_TARGET_IPV4OPTSSTRIP, which supplies a target + module that will allow you to strip all the IP options from a packet. + + The target doesn't take any option, and therefore is extremly easy to use : + + # iptables -t mangle -A PREROUTING -j IPV4OPTSSTRIP + # iptables -t mangle --list -n + Chain PREROUTING (policy ACCEPT) + target prot opt source destination + IPV4OPTSSTRIP all -- 0.0.0.0/0 0.0.0.0/0 + + Chain OUTPUT (policy ACCEPT) + target prot opt source destination + +config IP_NF_TARGET_NETLINK + tristate 'NETLINK target support' + depends on IP_NF_FILTER + help + CONFIG_IP_NF_TARGET_NETLINK adds a NETLINK target that sends dropped packets to + userspace via a netlink socket. Apps such as fwmon + (http://firestorm.geek-ware.co.uk) can then recieve and dislpay these packets. + This option is basically a re-implementation of the ipchains -o option. + +config IP_NF_TARGET_TTL + tristate 'TTL target support' + depends on IP_NF_MANGLE + help + This adds an iptables TTL manipulation target, which enables the user + to set the TTL value of an IP packet or to increment / decrement it + by a given value. + +config IP_NF_MATCH_CONNLIMIT + tristate 'Connections/IP limit match support' + depends on IP_NF_IPTABLES + help + This adds an iptables match which allows you to restrict the + number of parallel TCP connections to a server per client IP address + (or address block). + + Examples: + + # allow 2 telnet connections per client host + iptables -p tcp --syn --dport 23 -m connlimit --connlimit-above 2 -j REJECT + + # you can also match the other way around: + iptables -p tcp --syn --dport 23 -m connlimit ! --connlimit-above 2 -j ACCEPT + + # limit the nr of parallel http requests to 16 per class C sized + # network (24 bit netmask) + iptables -p tcp --syn --dport 80 -m connlimit --connlimit-above 16 \ + --connlimit-mask 24 -j REJECT + +config IP_NF_MATCH_DSTLIMIT + tristate 'dstlimit match support' + depends on IP_NF_IPTABLES + help + + This patch adds a new match called 'dstlimit'. + The idea is to have something like 'limit', but either per + destination-ip or per (destip,destport) tuple. + + It gives you the ability to say + '1000 packets per second for every host in 192.168.0.0/16' + '100 packets per second for every service of 192.168.1.1' + + Parameters are: + --dstlimit + A rate just like the limit match + --dstlimit-burst + Burst value, just like limit match + --dstlimit-mode destip | destip-destport + Limit per IP or per port + --dstlimit-name foo + The name for the /proc/net/ipt_dstlimit/foo entry + --dstlimit-htable-size + The number of buckets of the hash table + --dstlimit-htable-max + Maximum entries in the hash + --dstlimit-htable-expire + After how many miliseconds do hash entries expire + --dstlimit-htable-gcinterval + How many miliseconds between garbage collection intervals + + +config IP_NF_MATCH_FUZZY + tristate 'fuzzy match support' + depends on IP_NF_IPTABLES + help + This option adds a `fuzzy' match which allows you to match packets according to + a dynamic profile implemented by means of a simple Fuzzy Logic Controller (FLC) + +config IP_NF_MATCH_IPV4OPTIONS + tristate 'IPV4OPTIONS match support' + depends on IP_NF_IPTABLES + help + This option adds an iptables 'ipv4options' match, which allows you to + match on IPv4 header options like source routing, record route, timestamp and + router-alert. + + Suppported options are: + --ssrr + To match packets with the flag strict source routing. + --lsrr + To match packets with the flag loose source routing. + --no-srr + To match packets with no flag for source routing. + [!] --rr + To match packets with the RR flag. + [!] --ts + To match packets with the TS flag. + [!] --ra + To match packets with the router-alert option. + [!] --any-opt + To match a packet with at least one IP option, or no IP option + at all if ! is chosen. + + Example: + $ iptables -A input -m ipv4options --rr -j DROP + will drop packets with the record-route flag. + + $ iptables -A input -m ipv4options --ts -j DROP + will drop packets with the timestamp flag. + +config IP_NF_MATCH_MPORT + tristate 'Multiple port with ranges match support' + depends on IP_NF_IPTABLES + help + This module is an enhanced multiport match. It has support for byte + ranges as well as for single ports. + Up to 15 ports are allowed. Note that a portrange uses up 2 port values. + + Examples: + # iptables -A FORWARD -p tcp -m mport --ports 23:42,65 + +config IP_NF_MATCH_NTH + tristate 'Nth match support' + depends on IP_NF_IPTABLES + help + This option adds an iptables `Nth' match, which allows you to match every Nth + packet encountered. By default there are 16 different counters that can be + used. + + This match functions in one of two ways + 1) Match ever Nth packet, and only the Nth packet. + example: + iptables -t mangle -A PREROUTING -m nth --every 10 -j DROP + This rule will drop every 10th packet. + 2) Unique rule for every packet. This is an easy and quick + method to produce load-balancing for both inbound and outbound. + example: + iptables -t nat -A POSTROUTING -o eth0 -m nth --counter 7 \ + --every 3 --packet 0 -j SNAT --to-source 10.0.0.5 + iptables -t nat -A POSTROUTING -o eth0 -m nth --counter 7 \ + --every 3 --packet 1 -j SNAT --to-source 10.0.0.6 + iptables -t nat -A POSTROUTING -o eth0 -m nth --counter 7 \ + --every 3 --packet 2 -j SNAT --to-source 10.0.0.7 + This example evenly splits connections between the three SNAT + addresses. + + By using the mangle table and iproute2, you can setup complex + load-balanced routing. There's lot of other uses. Be creative! + + Suppported options are: + --every Nth Match every Nth packet + [--counter] num Use counter 0-15 (default:0) + [--start] num Initialize the counter at the number 'num' + instead of 0. Must be between 0 and Nth-1 + [--packet] num Match on 'num' packet. Must be between 0 + and Nth-1. + If --packet is used for a counter than + there must be Nth number of --packet + rules, covering all values between 0 and + Nth-1 inclusively. + +config IP_NF_MATCH_OSF + tristate 'OSF match support' + depends on IP_NF_IPTABLES + help + The idea of passive OS fingerprint matching exists for quite a long time, + but was created as extension fo OpenBSD pf only some weeks ago. + Original idea was lurked in some OpenBSD mailing list (thanks + grange@open...) and than adopted for Linux netfilter in form of this code. + + Original table was created by Michal Zalewski for + his excellent p0f and than changed a bit for more convenience. + + This module compares some data(WS, MSS, options and it's order, ttl, + df and others) from first SYN packet (actually from packets with SYN + bit set) with hardcoded in fingers[] table ones. + + Example: (Of course this only an example, do not get inspired by this) + + # iptables -N LINUX + # iptables -A LINUX -j LOG --log-prefix "Linux" + + # iptables -A INPUT -p tcp -m osf --genre Linux -j LINUX + # iptables -A INPUT -p tcp -m osf --genre FreeBSD -j REJECT + + NOTE: -p tcp is obviously required as it is a TCP match. + + OSF also has: + --log 1/0. + If present, OSF will log determined genres even if they don't match + desired one. + 0 - log all determined entries, + 1 - only first one. + + Example: + #iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 1 --smart + + In syslog you find something like this: + ipt_osf: Windows [Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139 + ipt_osf: Unknown: 16384:106:1:48:020405B401010402 44.33.22.11:1239 -> 11.22.33.44:80 + + --smart + if present, OSF will use some smartness to determine remote OS. + Now only not use TTL( with it far remote machines can be determined). + + Fingerprints can be loaded through /proc/sys/net/ipv4/osf file. + Only one fingerprint per open/close. + + Fingerprints can be downloaded from http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os + +config IP_POOL_STATISTICS + bool 'enable statistics on pool usage' + depends on IP_NF_POOL!=n + +config IP_NF_POOL + tristate 'IP address pool support' + depends on IP_NF_IPTABLES + help + CONFIG_IP_NF_POOL provides a match which lets you use bitmaps with one bit per + address from some range of IP addresses; the match depends on whether a checked + source or destination address has its bit set in the pool. It also provides a + POOL target, which can be used to add or remove the addresses of a packet + to/from a pool. + + There is also a POOL netfilter target, which can be used to set or remove the + addresses of a packet from a pool. + + To define and use pools, you need userlevel utilities: a patched iptables, and + the program ippool(8), which defines the pools and their bounds. The current + release of pool matching is ippool-0.0.2, and can be found in the archives of + the netfilter mailing list at http://lists.netfilter.org/. + +config IP_NF_MATCH_PSD + tristate 'psd match support' + depends on IP_NF_IPTABLES + help + This option adds a `psd' match, which supplies portscan + detection match (psd). This match will attempt to detect TCP and UDP + port scans. This match was derived from Solar Designer's scanlogd. + + Suppported options are: + + --psd-weight-threshold + + Total weight of the latest TCP/UDP packets with different + destination ports coming from the same host to be treated as port + scan sequence. + + --psd-delay-threshold + + Delay (in hundredths of second) for the packets with different + destination ports coming from the same host to be treated as + possible port scan subsequence. + + --psd-lo-ports-weight + + Weight of the packet with privileged (<=1024) destination port. + + --psd-hi-ports-weight + + Weight of the packet with non-priviliged destination port. + +config IP_NF_MATCH_QUOTA + tristate 'quota match support' + depends on IP_NF_IPTABLES + help + This option adds CONFIG_IP_NF_MATCH_QUOTA, which implements network + quotas by decrementing a byte counter with each packet. + + Supported options are: + --quota + The quota in bytes. + + KNOWN BUGS: this does not work on SMP systems. + + +config IP_NF_MATCH_RANDOM + tristate 'random match support' + depends on IP_NF_IPTABLES + help + This option adds a `random' match, + which allow you to match packets randomly + following a given probability. + + Suppported options are: + + [--average] percent will match randomly packets with a probability of + 'percent'. default is 50% + +config IP_NF_MATCH_REALM + tristate 'realm match support' + depends on IP_NF_IPTABLES + help + realm match: uses realm key from routing as match criteria similiar to + one in packet classifier + + /** snip from packet classifier documentation */ + Routing tables based classifier + CONFIG_NET_CLS_ROUTE4 + If you say Y here, you will be able to classify outgoing packets + according to the route table entry they matched. If unsure, say Y. + /** end snip **/ + + + # Example + # add route + /sbin/ip route add 194.29.194.0/24 via 194.29.192.1 realm 10 + + # source realm is in realm with mask 0xFFFF0000, + # destination is in realm with mask 0x0000FFFF + + # match destination realm + /usr/local/sbin/iptables -A OUTPUT -m realm --realm 10 -j LOG + + # match realm of source, this is also determinated by routing, + /usr/local/sbin/iptables -A INPUT -m realm --realm 655360 -j LOG + + THIS PATCH REQUIRES CONFIG_NET_CLS_ROUTE TO BE SET + +config IP_NF_MATCH_TIME + tristate 'TIME match support' + depends on IP_NF_IPTABLES + help + + This option adds CONFIG_IP_NF_MATCH_TIME, which supplies a time match module. + This match allows you to filter based on the packet arrival time + (arrival time at the machine which the netfilter is running on) or + departure time (for locally generated packets). + + Supported options are: + --timestart HH:MM + The starting point of the time match frame. + + --timestop HH:MM + The stopping point of the time match frame + + --days Tue,Mon... + Days of the week to match separated by a coma, no space + (one of Sun,Mon,Tue,Wed,Thu,Fri,Sat) + + Example: + -A INPUT -m time --timestart 8:00 --timestop 18:00 --days Mon,Tue,Wed,Thu,Fri + will match packets that have an arrival timestamp in the range 8:00->18:00 from Monday + to Friday. + + -A OUTPUT -m time --timestart 8:00 --timestop 18:00 --Days Mon + will match the packets (locally generated) that have a departure timestamp + in the range 8:00->18:00 on Monday only. + +config IP_NF_MATCH_U32 + tristate 'U32 match support' + depends on IP_NF_IPTABLES + help + + U32 allows you to extract quantities of up to 4 bytes from a packet, + AND them with specified masks, shift them by specified amounts and + test whether the results are in any of a set of specified ranges. + The specification of what to extract is general enough to skip over + headers with lengths stored in the packet, as in IP or TCP header + lengths. + Details and examples are in the kernel module source. + +config IP_NF_SET + tristate 'IP address/port sets support' + depends on IP_NF_IPTABLES + help + +config IP_NF_SET_MAX + int ' Maximum number of sets' + depends on IP_NF_SET + default 256 + +config IP_NF_SET_IPMAP + tristate ' ipmap set type support' + depends on IP_NF_SET + help + +config IP_NF_SET_PORTMAP + tristate ' portmap set type support' + depends on IP_NF_SET + help + +config IP_NF_SET_MACIPMAP + tristate ' macipmap set type support' + depends on IP_NF_SET + help + +config IP_NF_SET_IPHASH + tristate ' iphash set type support' + depends on IP_NF_SET + help + endmenu diff -Nur linux-2.6.3.org/net/ipv4/netfilter/Makefile linux-2.6.3/net/ipv4/netfilter/Makefile --- linux-2.6.3.org/net/ipv4/netfilter/Makefile 2004-02-18 04:57:20.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/Makefile 2004-02-19 08:55:20.544815689 +0100 @@ -42,15 +42,38 @@ # matches obj-$(CONFIG_IP_NF_MATCH_HELPER) += ipt_helper.o obj-$(CONFIG_IP_NF_MATCH_LIMIT) += ipt_limit.o +obj-$(CONFIG_IP_NF_MATCH_QUOTA) += ipt_quota.o +obj-$(CONFIG_IP_NF_MATCH_DSTLIMIT) += ipt_dstlimit.o obj-$(CONFIG_IP_NF_MATCH_MARK) += ipt_mark.o +obj-$(CONFIG_IP_NF_POOL) += ipt_pool.o ipt_POOL.o ip_pool.o obj-$(CONFIG_IP_NF_MATCH_MAC) += ipt_mac.o obj-$(CONFIG_IP_NF_MATCH_IPRANGE) += ipt_iprange.o obj-$(CONFIG_IP_NF_MATCH_PKTTYPE) += ipt_pkttype.o obj-$(CONFIG_IP_NF_MATCH_MULTIPORT) += ipt_multiport.o + +obj-$(CONFIG_IP_NF_MATCH_MPORT) += ipt_mport.o + obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o +obj-$(CONFIG_IP_NF_MATCH_TIME) += ipt_time.o + + +obj-$(CONFIG_IP_NF_MATCH_RANDOM) += ipt_random.o + +obj-$(CONFIG_IP_NF_MATCH_PSD) += ipt_psd.o + +obj-$(CONFIG_IP_NF_MATCH_OSF) += ipt_osf.o + + +obj-$(CONFIG_IP_NF_MATCH_NTH) += ipt_nth.o + +obj-$(CONFIG_IP_NF_MATCH_IPV4OPTIONS) += ipt_ipv4options.o + + +obj-$(CONFIG_IP_NF_MATCH_FUZZY) += ipt_fuzzy.o + obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o @@ -59,10 +82,15 @@ obj-$(CONFIG_IP_NF_MATCH_LENGTH) += ipt_length.o +obj-$(CONFIG_IP_NF_MATCH_U32) += ipt_u32.o + + obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o +obj-$(CONFIG_IP_NF_MATCH_CONNLIMIT) += ipt_connlimit.o obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o +obj-$(CONFIG_IP_NF_MATCH_REALM) += ipt_realm.o obj-$(CONFIG_IP_NF_MATCH_PHYSDEV) += ipt_physdev.o @@ -79,6 +107,9 @@ obj-$(CONFIG_IP_NF_TARGET_CLASSIFY) += ipt_CLASSIFY.o obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o +obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o +obj-$(CONFIG_IP_NF_TARGET_NETLINK) += ipt_NETLINK.o +obj-$(CONFIG_IP_NF_TARGET_IPV4OPTSSTRIP) += ipt_IPV4OPTSSTRIP.o obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt_TCPMSS.o @@ -94,3 +125,10 @@ obj-$(CONFIG_IP_NF_COMPAT_IPFWADM) += ipfwadm.o obj-$(CONFIG_IP_NF_QUEUE) += ip_queue.o + +obj-$(CONFIG_IP_NF_SET) += ipt_set.o ipt_SET.o ip_set.o +obj-$(CONFIG_IP_NF_SET_IPMAP) += ip_set_ipmap.o +obj-$(CONFIG_IP_NF_SET_PORTMAP) += ip_set_portmap.o +obj-$(CONFIG_IP_NF_SET_MACIPMAP) += ip_set_macipmap.o +obj-$(CONFIG_IP_NF_SET_IPHASH) += ip_set_iphash.o + diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ip_pool.c linux-2.6.3/net/ipv4/netfilter/ip_pool.c --- linux-2.6.3.org/net/ipv4/netfilter/ip_pool.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ip_pool.c 2004-02-19 09:08:42.247779733 +0100 @@ -0,0 +1,334 @@ +/* Kernel module for IP pool management */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DP printk +#else +#define DP(format, args...) +#endif + +MODULE_LICENSE("GPL"); + +#define NR_POOL 16 +static int nr_pool = NR_POOL;/* overwrite this when loading module */ + +struct ip_pool { + u_int32_t first_ip; /* host byte order, included in range */ + u_int32_t last_ip; /* host byte order, included in range */ + void *members; /* the bitmap proper */ + int nr_use; /* total nr. of tests through this */ + int nr_match; /* total nr. of matches through this */ + rwlock_t lock; +}; + +static struct ip_pool *POOL; + +static inline struct ip_pool *lookup(ip_pool_t index) +{ + if (index < 0 || index >= nr_pool) { + DP("ip_pool:lookup: bad index %d\n", index); + return 0; + } + return POOL+index; +} + +int ip_pool_match(ip_pool_t index, u_int32_t addr) +{ + struct ip_pool *pool = lookup(index); + int res = 0; + + if (!pool || !pool->members) + return 0; + read_lock_bh(&pool->lock); + if (pool->members) { + if (addr >= pool->first_ip && addr <= pool->last_ip) { + addr -= pool->first_ip; + if (test_bit(addr, pool->members)) { + res = 1; +#ifdef CONFIG_IP_POOL_STATISTICS + pool->nr_match++; +#endif + } + } +#ifdef CONFIG_IP_POOL_STATISTICS + pool->nr_use++; +#endif + } + read_unlock_bh(&pool->lock); + return res; +} +EXPORT_SYMBOL(ip_pool_match); + +static int pool_change(ip_pool_t index, u_int32_t addr, int isdel) +{ + struct ip_pool *pool; + int res = -1; + + pool = lookup(index); + if ( !pool || !pool->members + || addr < pool->first_ip || addr > pool->last_ip) + return -1; + read_lock_bh(&pool->lock); + if (pool->members && addr >= pool->first_ip && addr <= pool->last_ip) { + addr -= pool->first_ip; + res = isdel + ? (0 != test_and_clear_bit(addr, pool->members)) + : (0 != test_and_set_bit(addr, pool->members)); + } + read_unlock_bh(&pool->lock); + return res; +} + +int ip_pool_mod(ip_pool_t index, u_int32_t addr, int isdel) +{ + int res = pool_change(index,addr,isdel); + + if (!isdel) res = !res; + return res; +} +EXPORT_SYMBOL(ip_pool_mod); + +static inline int bitmap_bytes(u_int32_t a, u_int32_t b) +{ + return 4*((((b-a+8)/8)+3)/4); +} + +static inline int poolbytes(ip_pool_t index) +{ + struct ip_pool *pool = lookup(index); + + return pool ? bitmap_bytes(pool->first_ip, pool->last_ip) : 0; +} + +static int setpool( + struct sock *sk, + int optval, + void *user, + unsigned int len +) { + struct ip_pool_request req; + + DP("ip_pool:setpool: optval=%d, user=%p, len=%d\n", optval, user, len); + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (optval != SO_IP_POOL) + return -EBADF; + if (len != sizeof(req)) + return -EINVAL; + if (copy_from_user(&req, user, sizeof(req)) != 0) + return -EFAULT; + printk("obsolete op - upgrade your ippool(8) utility.\n"); + return -EINVAL; +} + +static int getpool( + struct sock *sk, + int optval, + void *user, + int *len +) { + struct ip_pool_request req; + struct ip_pool *pool; + ip_pool_t i; + int newbytes; + void *newmembers; + int res; + + DP("ip_pool:getpool: optval=%d, user=%p\n", optval, user); + if (!capable(CAP_NET_ADMIN)) + return -EINVAL; + if (optval != SO_IP_POOL) + return -EINVAL; + if (*len != sizeof(req)) { + return -EFAULT; + } + if (copy_from_user(&req, user, sizeof(req)) != 0) + return -EFAULT; + DP("ip_pool:getpool op=%d, index=%d\n", req.op, req.index); + if (req.op < IP_POOL_BAD001) { + printk("obsolete op - upgrade your ippool(8) utility.\n"); + return -EFAULT; + } + switch(req.op) { + case IP_POOL_HIGH_NR: + DP("ip_pool HIGH_NR\n"); + req.index = IP_POOL_NONE; + for (i=0; imembers) + return -EBADF; + req.addr = htonl(pool->first_ip); + req.addr2 = htonl(pool->last_ip); + return copy_to_user(user, &req, sizeof(req)); + case IP_POOL_USAGE: + DP("ip_pool USE\n"); + pool = lookup(req.index); + if (!pool) + return -EINVAL; + if (!pool->members) + return -EBADF; + req.addr = pool->nr_use; + req.addr2 = pool->nr_match; + return copy_to_user(user, &req, sizeof(req)); + case IP_POOL_TEST_ADDR: + DP("ip_pool TEST 0x%08x\n", req.addr); + pool = lookup(req.index); + if (!pool) + return -EINVAL; + res = 0; + read_lock_bh(&pool->lock); + if (!pool->members) { + DP("ip_pool TEST_ADDR no members in pool\n"); + res = -EBADF; + goto unlock_and_return_res; + } + req.addr = ntohl(req.addr); + if (req.addr < pool->first_ip) { + DP("ip_pool TEST_ADDR address < pool bounds\n"); + res = -ERANGE; + goto unlock_and_return_res; + } + if (req.addr > pool->last_ip) { + DP("ip_pool TEST_ADDR address > pool bounds\n"); + res = -ERANGE; + goto unlock_and_return_res; + } + req.addr = (0 != test_bit((req.addr - pool->first_ip), + pool->members)); + read_unlock_bh(&pool->lock); + return copy_to_user(user, &req, sizeof(req)); + case IP_POOL_FLUSH: + DP("ip_pool FLUSH not yet implemented.\n"); + return -EBUSY; + case IP_POOL_DESTROY: + DP("ip_pool DESTROY not yet implemented.\n"); + return -EBUSY; + case IP_POOL_INIT: + DP("ip_pool INIT 0x%08x-0x%08x\n", req.addr, req.addr2); + pool = lookup(req.index); + if (!pool) + return -EINVAL; + req.addr = ntohl(req.addr); + req.addr2 = ntohl(req.addr2); + if (req.addr > req.addr2) { + DP("ip_pool INIT bad ip range\n"); + return -EINVAL; + } + newbytes = bitmap_bytes(req.addr, req.addr2); + newmembers = kmalloc(newbytes, GFP_KERNEL); + if (!newmembers) { + DP("ip_pool INIT out of mem for %d bytes\n", newbytes); + return -ENOMEM; + } + memset(newmembers, 0, newbytes); + write_lock_bh(&pool->lock); + if (pool->members) { + DP("ip_pool INIT pool %d exists\n", req.index); + kfree(newmembers); + res = -EBUSY; + goto unlock_and_return_res; + } + pool->first_ip = req.addr; + pool->last_ip = req.addr2; + pool->nr_use = 0; + pool->nr_match = 0; + pool->members = newmembers; + write_unlock_bh(&pool->lock); + return 0; + case IP_POOL_ADD_ADDR: + DP("ip_pool ADD_ADDR 0x%08x\n", req.addr); + req.addr = pool_change(req.index, ntohl(req.addr), 0); + return copy_to_user(user, &req, sizeof(req)); + case IP_POOL_DEL_ADDR: + DP("ip_pool DEL_ADDR 0x%08x\n", req.addr); + req.addr = pool_change(req.index, ntohl(req.addr), 1); + return copy_to_user(user, &req, sizeof(req)); + default: + DP("ip_pool:getpool bad op %d\n", req.op); + return -EINVAL; + } + return -EINVAL; + +unlock_and_return_res: + if (pool) + read_unlock_bh(&pool->lock); + return res; +} + +static struct nf_sockopt_ops so_pool += { { NULL, NULL }, PF_INET, + SO_IP_POOL, SO_IP_POOL+1, &setpool, + SO_IP_POOL, SO_IP_POOL+1, &getpool, + 0, NULL }; + +MODULE_PARM(nr_pool, "i"); + +static int __init init(void) +{ + ip_pool_t i; + int res; + + if (nr_pool < 1) { + printk("ip_pool module init: bad nr_pool %d\n", nr_pool); + return -EINVAL; + } + POOL = kmalloc(nr_pool * sizeof(*POOL), GFP_KERNEL); + if (!POOL) { + printk("ip_pool module init: out of memory for nr_pool %d\n", + nr_pool); + return -ENOMEM; + } + for (i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASSERT_READ_LOCK(x) /* dont use that */ +#define ASSERT_WRITE_LOCK(x) +#include +#include + +static struct list_head set_type_list; /* all registred set types */ +struct ip_set **ip_set_list; /* all individual sets */ +static rwlock_t list_lock = RW_LOCK_UNLOCKED; /* protects both set_type_list and ip_set_list */ +static unsigned int max_sets = 0; /* max number of sets, */ + +/* Arrgh */ +#ifdef MODULE +#define __MOD_INC(foo) __MOD_INC_USE_COUNT(foo) +#define __MOD_DEC(foo) __MOD_DEC_USE_COUNT(foo) +#else +#define __MOD_INC(foo) do { } while (0) +#define __MOD_DEC(foo) do { } while (0) +#endif + +#define NOT_IN_CHILD_SET(fn,args...) \ + !*private \ + || !(*private)->childsets \ + || (set->type[i]->fn(*private,##args) < 0) + +static struct ip_set_private ** +ip_set_find_private(struct ip_set *set, + struct ip_set_private **private, + ip_set_ip_t *ip, + u_int8_t level) +{ + int i; + ip_set_ip_t id; + + for (i = 0; i < level; i++) { + if (NOT_IN_CHILD_SET(matchip, ip[i], &id)) + return NULL; + private = &(*private)->childsets[id]; + } + DP("id: %i private: %p %p", id, private, *private); + return private; +} + +/* Destroy function for the private part of the (child)sets. + * Must be called without holding any locks. + */ +static void +ip_set_destroy_private(struct ip_set *set, + struct ip_set_private **private, + u_int8_t level) +{ + int i; + + DP("set %p private %p %p %p", set, private, *private, (*private)->childsets); + if ((*private)->childsets) { + for (i = 0; i < set->type[level]->sizeid(*private); i++) + if ((*private)->childsets[i]) { + DP("%i -> %p", i, (*private)->childsets[i]); + ip_set_destroy_private(set, + &(*private)->childsets[i], + level + 1); + } + vfree((*private)->childsets); + } + + set->type[level]->destroy(private); + DP("%p %p", private, *private); +} + +static void ip_set_flush_private(struct ip_set *set, + struct ip_set_private *private, + u_int8_t level, + u_int8_t childsets) +{ + int i; + + if (childsets && private->childsets) + for (i = 0; i < set->type[level]->sizeid(private); i++) + if (private->childsets[i]) + ip_set_flush_private(set, + private->childsets[i], + level + 1, + childsets); + + set->type[level]->flush(private); + +} + +/* ip_set_flush() - flush data in a set + */ +static int ip_set_flush(struct ip_set *set, + ip_set_ip_t *ip, + u_int8_t level, + u_int8_t childsets) +{ + int res = 0; + struct ip_set_private **private; + + write_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + + private = ip_set_find_private(set, &set->private, ip, level); + + if (private) + ip_set_flush_private(set, *private, level, childsets); + + unlock: + write_unlock_bh(&set->lock); + return res; +} + +int +ip_set_testip_kernel(struct ip_set *set, + const struct sk_buff *skb, + const u_int32_t *flags, + u_int8_t set_level, + u_int8_t ip_level) +{ + struct ip_set_private **private = &set->private; + ip_set_ip_t id; + int i, res = 0; + + read_lock_bh(&set->lock); + if (set->levels < ip_level || set->subref) + goto unlock; + + for (i = 0; i < set_level; i++) { + if (NOT_IN_CHILD_SET(testip_kernel, skb, + flags[i] | set->type[i]->typecode, &id)) + goto unlock; + DP("id: %i private: %p", id, *private); + private = &(*private)->childsets[id]; + } + for (i = set_level; private && *private && i < ip_level; i++) { + if (set->type[i]->testip_kernel(*private, skb, + flags[i] | set->type[i]->typecode, &id) <= 0) + goto unlock; + private = (*private)->childsets + ? &(*private)->childsets[id] : NULL; + } + res = 1; + unlock: + read_unlock_bh(&set->lock); + return res; +} + +void +ip_set_addip_kernel(struct ip_set *set, + const struct sk_buff *skb, + const u_int32_t *flags, + u_int8_t set_level, + u_int8_t ip_level) +{ + struct ip_set_private **private = &set->private; + ip_set_ip_t id; + int i, res; + + write_lock_bh(&set->lock); + if (set->levels < ip_level || set->subref) { + write_unlock_bh(&set->lock); + return; + } + for (i = 0; i < set_level; i++) { + if (NOT_IN_CHILD_SET(testip_kernel, skb, + flags[i] | set->type[i]->typecode, &id)) { + write_unlock_bh(&set->lock); + return; + } + private = &(*private)->childsets[id]; + } + for (i = set_level; private && *private && i < ip_level; i++) { + res = set->type[i]->addip_kernel(*private, skb, + flags[i] | set->type[i]->typecode, &id); + if (!(res == 0 || res == -EEXIST)) { + write_unlock_bh(&set->lock); + return; + } + private = (*private)->childsets + ? &(*private)->childsets[id] : NULL; + } + write_unlock_bh(&set->lock); +} + +void +ip_set_delip_kernel(struct ip_set *set, + const struct sk_buff *skb, + const u_int32_t *flags, + u_int8_t set_level, + u_int8_t ip_level) +{ + struct ip_set_private **private = &set->private; + ip_set_ip_t id; + int i, res; + + write_lock_bh(&set->lock); + if (set->levels < ip_level || set->subref) { + write_unlock_bh(&set->lock); + return; + } + for (i = 0; i < set_level; i++) { + if (NOT_IN_CHILD_SET(testip_kernel, skb, + flags[i] | set->type[i]->typecode, &id)) { + write_unlock_bh(&set->lock); + return; + } + private = &(*private)->childsets[id]; + } + for (i = set_level; private && *private && i < ip_level; i++) { + res = set->type[i]->delip_kernel(*private, skb, + flags[i] | set->type[i]->typecode, &id); + if (!(res == 0 || res == -EEXIST)) { + write_unlock_bh(&set->lock); + return; + } + private = (*private)->childsets + ? &(*private)->childsets[id] : NULL; + } + write_unlock_bh(&set->lock); +} + +static int +ip_set_addip(struct ip_set *set, + ip_set_ip_t *ip, + u_int8_t level, + const void *data, + size_t size) +{ + struct ip_set_private **private; + ip_set_ip_t id; + int res = 0; + + DP("%s %i %d", set->name, level, size); + write_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + private = ip_set_find_private(set, &set->private, ip, level); + DP("%s %i %d", set->name, level, size); + while (level <= set->levels && size) { + DP("%s %i %d", set->name, level, size); + if (!(private && *private)) { + res = -ENOENT; + goto unlock; + } + if (size < set->type[level]->reqsize) { + res = -EINVAL; + goto unlock; + } + res = set->type[level]->addip(*private, data, + set->type[level]->reqsize, &id); + if (!(res == 0 || res == -EEXIST)) + goto unlock; + private = (*private)->childsets ? &(*private)->childsets[id] : NULL; + data += set->type[level]->reqsize; + size -= set->type[level++]->reqsize; + } + if (size) + res = -EINVAL; + unlock: + write_unlock_bh(&set->lock); + return res; +} + +static int +ip_set_delip(struct ip_set *set, + ip_set_ip_t *ip, + u_int8_t level, + const void *data, + size_t size) +{ + struct ip_set_private **private; + ip_set_ip_t id; + int res = 0; + + write_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + private = ip_set_find_private(set, &set->private, ip, level); + while (level <= set->levels && size) { + if (!(private && *private)) { + res = -ENOENT; + goto unlock; + } + if (size < set->type[level]->reqsize) { + res = -EINVAL; + goto unlock; + } + res = set->type[level]->delip(*private, data, + set->type[level]->reqsize, &id); + if (!(res == 0 || res == -EEXIST)) + goto unlock; + private = (*private)->childsets ? &(*private)->childsets[id] : NULL; + data += set->type[level]->reqsize; + size -= set->type[level++]->reqsize; + } + if (size) + res = -EINVAL; + unlock: + write_unlock_bh(&set->lock); + return res; +} + +static int +ip_set_testip(struct ip_set *set, + ip_set_ip_t *ip, + u_int8_t level, + const void *data, + size_t size) +{ + struct ip_set_private **private; + ip_set_ip_t id; + int res = 0; + + write_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + private = ip_set_find_private(set, &set->private, ip, level); + while (level <= set->levels && size) { + if (!(private && *private)) { + res = -ENOENT; + goto unlock; + } + if (size < set->type[level]->reqsize) { + res = -EINVAL; + goto unlock; + } + res = set->type[level]->testip(*private, data, + set->type[level]->reqsize, &id); + DP("level: %i res: %i", level, res); + if (res <= 0) + goto unlock; + private = (*private)->childsets ? &(*private)->childsets[id] : NULL; + data += set->type[level]->reqsize; + size -= set->type[level++]->reqsize; + } + if (size) + res = -EINVAL; + unlock: + write_unlock_bh(&set->lock); + return (res > 0); +} + +static inline int +set_type_equal(const struct ip_set_type *set_type, const char *str2) +{ + DP("'%s' vs. '%s'", set_type->typename, str2); + return !strncmp(set_type->typename, str2, IP_SET_MAXNAMELEN - 1); +} + +/* + * Always use find_setfoo() under the &list_lock. + */ +static inline struct ip_set_type *find_set_type(const char name[IP_SET_MAXNAMELEN]) +{ + return LIST_FIND(&set_type_list, + set_type_equal, + struct ip_set_type *, + name); +} + +int ip_set_register_set_type(struct ip_set_type *set_type) +{ + if (set_type->protocol_version != IP_SET_PROTOCOL_VERSION) { + ip_set_printk("'%s' uses wrong protocol version %u (want %u)", + set_type->typename, + set_type->protocol_version, + IP_SET_PROTOCOL_VERSION); + return -EINVAL; + } + + write_lock_bh(&list_lock); + if (find_set_type(set_type->typename)) { + /* Duplicate! */ + write_unlock_bh(&list_lock); + ip_set_printk("'%s' already registered!", + set_type->typename); + return -EINVAL; + } + MOD_INC_USE_COUNT; + list_append(&set_type_list, set_type); + write_unlock_bh(&list_lock); + DP("'%s' registered.", set_type->typename); + return 0; +} + +void ip_set_unregister_set_type(struct ip_set_type *set_type) +{ + write_lock_bh(&list_lock); + if (!find_set_type(set_type->typename)) { + ip_set_printk("'%s' not registered?", + set_type->typename); + write_unlock_bh(&list_lock); + return; + } + LIST_DELETE(&set_type_list, set_type); + write_unlock_bh(&list_lock); + MOD_DEC_USE_COUNT; + + DP("'%s' unregistered.", set_type->typename); +} + +/* Create the private part of a (child)set. + * Must be called without holding any locks. + */ +static int +ip_set_create_private(struct ip_set_type *set_type, + struct ip_set_private **private, + const void *data, + size_t size, + u_int8_t childsets) +{ + int res = 0; + int newbytes; + + DP("%s %p %p %i", set_type->typename, private, *private, childsets); + + if (*private) + printk("%p: %p as private already occupied", private, *private); + + /* Call the set_type initializer. */ + res = set_type->create(private, data, size); + if (res != 0) + return res; + + if (!childsets) { + (*private)->childsets = NULL; + return res; + } + + /* Create room for subsets */ + newbytes = set_type->sizeid(*private) * sizeof(struct ip_set_private *); + DP("%s (%p) %i", set_type->typename, *private, newbytes); + (*private)->childsets = vmalloc(newbytes); \ + if (!(*private)->childsets) { + set_type->destroy(private); + return -ENOMEM; + } + DP("%s %p %p %p", set_type->typename, private, *private, (*private)->childsets); + memset((*private)->childsets, 0, newbytes); + return res; +} + +static int +ip_set_create_childset(struct ip_set *set, + ip_set_ip_t *ip, + u_int8_t level, + u_int8_t childsets, + const void *data, + size_t size) +{ + struct ip_set_private **private = &set->private; + ip_set_ip_t id; + int res; + + DP("%s (%i %i)", set->name, level, childsets); + write_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + if (level > 1) + private = ip_set_find_private(set, private, ip, level - 1); + DP("%s (%i %i) %p %p", set->name, level, childsets, private, *private); + if (!(private && *private && (*private)->childsets)) { + res = -ENOENT; + goto unlock; + } + DP("%s (%i %i) %p %p", set->name, level, childsets, private, *private); + set->type[level - 1]->matchip(*private, ip[level - 1], &id); + DP("%s (%i %i) %p %p %i", set->name, level, childsets, private, *private, id); + if (id < 0) { + res = -ENOENT; + goto unlock; + } + if ((*private)->childsets[id]) { + res = -EEXIST; + goto unlock; + } + set->subref++; + write_unlock_bh(&set->lock); + + /* Without holding any locks, create private part. */ + res = ip_set_create_private(set->type[level], + &(*private)->childsets[id], + data, size, childsets); + + write_lock_bh(&set->lock); + set->subref--; + unlock: + DP("%s (%p %p) res=%i", set->name, private, *private, res); + write_unlock_bh(&set->lock); + return res; +} + +static int +ip_set_create(const char name[IP_SET_MAXNAMELEN], + char typename[IP_SET_LEVELS][IP_SET_MAXNAMELEN], + u_int8_t level, + const void *data, + size_t size) +{ + int i, id, res = 0; + struct ip_set *set; + + DP("%s (%i): %s", typename[0], level, name); + /* + * First, and without any locks, allocate and initialize + * a normal base set structure. + */ + set = kmalloc(sizeof(struct ip_set), GFP_KERNEL); + if (!set) + return -ENOMEM; + set->lock = RW_LOCK_UNLOCKED; + strncpy(set->name, name, IP_SET_MAXNAMELEN); + set->name[IP_SET_MAXNAMELEN - 1] = '\0'; + set->ref = 0; + set->subref = 0; + set->levels = level; + set->private = NULL; + + /* + * Next, take the &list_lock, check that we know the type, + * and take a reference on the type, to make sure it + * stays available while constructing our new set. + * + * After referencing the type, we drop the &list_lock, + * and let the new set construction run without locks. + */ + write_lock_bh(&list_lock); + for (i = 0; i < level; i++) { + set->type[i] = find_set_type(typename[i]); + if (set->type[i] == NULL) { + /* FIXME: try loading the module */ + write_unlock_bh(&list_lock); + ip_set_printk("no set type '%s', set '%s' not created", + typename[i], name); + kfree(set); + return -EINVAL; + } + } + for (i = 0; i < level; i++) + __MOD_INC(set->type[i]->me); + write_unlock_bh(&list_lock); + + /* + * Without holding any locks, create private part. + */ + res = ip_set_create_private(set->type[0], + &set->private, + data, size, level - 1); + if (res != 0) { + for (i = 0; i <= level; i++) + __MOD_DEC(set->type[i]->me); + kfree(set); + return res; + } + + /* BTW, res==0 here. */ + + /* + * Here, we have a valid, constructed set. &list_lock again, + * and check that it is not already in ip_set_list. + */ + write_lock_bh(&list_lock); + id = -1; + for (i = 0; i < max_sets; i++) { + if (ip_set_list[i] != NULL + && strncmp(ip_set_list[i]->name, set->name, + IP_SET_MAXNAMELEN - 1) == 0) { + res = -EEXIST; + goto cleanup; + } else if (id < 0 && ip_set_list[i] == NULL) + id = i; + } + if (id < 0) { + /* No free slot remained */ + res = -ERANGE; + goto cleanup; + } + /* + * Finally! Append our shiny new set into the list, and be done. + */ + DP("create: '%s' created with id %i!", set->name, id); + ip_set_list[id] = set; + write_unlock_bh(&list_lock); + return res; + + cleanup: + write_unlock_bh(&list_lock); + ip_set_destroy_private(set, &set->private, 0); + for (i = 0; i < level; i++) + __MOD_DEC(set->type[i]->me); + kfree(set); + return res; +} + +static int ip_set_destroy(struct ip_set *set, + ip_set_ip_t *ip, + u_int8_t level) +{ + struct ip_set_private **private; + int i, res = 0; + + write_lock_bh(&list_lock); + /* there is no race, here. ->ref modification always happens + * under &list_lock. Fine. + */ + if (level == 0) { + /* one ref from caller */ + if (set->ref > 1 || set->subref) { + res = -EBUSY; + goto unlock; + } + + for (i = 0; i < max_sets; i++) + if (ip_set_list[i] == set) { + ip_set_list[i] = NULL; + break; + } + write_unlock_bh(&list_lock); + + ip_set_destroy_private(set, &set->private, 0); + for (i = 0; i < set->levels; i++) + __MOD_DEC(set->type[i]->me); + kfree(set); + return res; + } + + private = ip_set_find_private(set, &set->private, + ip, level); + + if (private && *private) { + if (set->subref) { + res = -EBUSY; + goto unlock; + } + set->subref++; + write_unlock_bh(&list_lock); + + DP("%p %p", private, *private); + ip_set_destroy_private(set, private, level); + DP("%p %p", private, *private); + + write_lock_bh(&list_lock); + set->subref--; + } else + res = -ENOENT; + + unlock: + write_unlock_bh(&list_lock); + return res; +} + +/* + * Find set by name, reference it once. The reference makes sure the + * thing pointed to, does not go away under our feet. Drop the reference + * later, using ip_set_put(). + */ +struct ip_set *ip_set_get_byname(const char name[IP_SET_MAXNAMELEN], + int *id) +{ + struct ip_set *set = NULL; + int i; + + read_lock_bh(&list_lock); + for (i = 0; i < max_sets; i++) { + set = ip_set_list[i]; + if (set != NULL + && strncmp(set->name, name, IP_SET_MAXNAMELEN - 1) == 0) { + set->ref++; + *id = i; + break; + } + } + read_unlock_bh(&list_lock); + return set; +} + +/* + * Find set by id, reference it once. The reference makes sure the + * thing pointed to, does not go away under our feet. Drop the reference + * later, using ip_set_put(). + */ +struct ip_set *ip_set_get_byid(int id) +{ + struct ip_set *set; + + if (id < 0 || id >= max_sets) + return NULL; + + write_lock_bh(&list_lock); + set = ip_set_list[id];; + if (set) + set->ref++; + write_unlock_bh(&list_lock); + return set; +} + +/* + * If the given set pointer points to a valid set, decrement + * reference count by 1. The caller shall not assume the pointer + * to be valid, after calling this function. + */ +void ip_set_put(struct ip_set *set) +{ + write_lock_bh(&list_lock); + if (set) + set->ref--; + write_unlock_bh(&list_lock); +} + +static int ip_set_rename(struct ip_set *set, const char *name) +{ + int i, res = 0; + + write_lock_bh(&list_lock); + for (i = 0; i < max_sets; i++) { + if (ip_set_list[i] != NULL + && strncmp(ip_set_list[i]->name, + name, + IP_SET_MAXNAMELEN - 1) == 0) { + res = -EEXIST; + goto unlock; + } + } + strncpy(set->name, name, IP_SET_MAXNAMELEN); + set->name[IP_SET_MAXNAMELEN - 1] = '\0'; + unlock: + write_unlock_bh(&list_lock); + return res; +} + +static int ip_set_swap(struct ip_set *from, struct ip_set *to) +{ + char from_name[IP_SET_MAXNAMELEN]; + unsigned from_ref; + int i, res = 0; + int from_id = -1, to_id = -1; + + write_lock_bh(&list_lock); + for (i = 0; i < max_sets && (from_id < 0 || to_id < 0); i++) { + if (ip_set_list[i] == from) + from_id = i; + if (ip_set_list[i] == to) + to_id = i; + } + /* We must have got both sets: we hold refcounts against them! */ + if (from_id < 0 || to_id < 0) { + res = -EINVAL; + goto unlock; + } + + strncpy(from_name, from->name, IP_SET_MAXNAMELEN); + from_ref = from->ref; + + ip_set_list[from_id] = to; + ip_set_list[to_id] = from; + + strncpy(from->name, to->name, IP_SET_MAXNAMELEN); + from->ref = to->ref; + strncpy(to->name, from_name, IP_SET_MAXNAMELEN); + to->ref = from_ref; + unlock: + write_unlock_bh(&list_lock); + return res; +} + +size_t ip_set_listing_size(void) +{ + size_t size = 0; + int id; + + read_lock_bh(&list_lock); + for (id = 0; id < max_sets; id++) { + if (ip_set_list[id] != NULL) + size += sizeof(struct ip_set_req_listing); + } + read_unlock_bh(&list_lock); + + return size; +} + +int ip_set_listing(void *data, int *len) +{ + int used = 0; + int res = 0; /* All OK */ + int i, id; + struct ip_set *set; + struct ip_set_req_listing *header = data; + + read_lock_bh(&list_lock); + for (id = 0; id < max_sets; id++) { + if (ip_set_list[id] == NULL) + continue; + + /* Pointer to our header */ + header = (struct ip_set_req_listing *) (data + used); + + DP("used before= %d %p %p %p", used, header, data, + data + used); + + /* Get and ensure header size */ + if (used + sizeof(struct ip_set_req_listing) > *len) + goto not_enough_mem; + + set = ip_set_list[id]; + + /* Fill with data */ + strncpy(header->name, set->name, IP_SET_MAXNAMELEN - 1); + for (i = 0; i < set->levels; i++) + strncpy(header->typename[i], set->type[i]->typename, + IP_SET_MAXNAMELEN - 1); + header->levels = set->levels; + header->ref = set->ref; + header->id = id; + + used += sizeof(struct ip_set_req_listing); + DP("used after= %d", used); + } + *len = used; /* How much did we use? */ + goto unlock_and_return; + + not_enough_mem: + DP("not enough mem, try again"); + res = -ENOMEM; + + unlock_and_return: + read_unlock_bh(&list_lock); + return res; +} + +int ip_set_list_size(struct ip_set * set, + ip_set_ip_t *ip, + unsigned level, + size_t *size, + unsigned op) +{ + int res = 0; /* OK */ + struct ip_set_private **private; + + DP("%d %s %d", op, set->name, level); + read_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + private = ip_set_find_private(set, &set->private, ip, level); + if (!(private && *private)) { + res = -ENOENT; + goto unlock; + } + switch (op) { + case IP_SET_OP_LIST_HEADER_SIZE: + *size = set->type[level]->list_header_size(*private); + break; + case IP_SET_OP_LIST_MEMBERS_SIZE: + *size = set->type[level]->list_members_size(*private); + break; + case IP_SET_OP_LIST_CHILDSETS_SIZE: + *size = (*private)->childsets == NULL ? 0 + : bitmap_bytes(0, set->type[level]->sizeid(*private) - 1); + break; + default: + res = -EINVAL; + } + unlock: + read_unlock_bh(&set->lock); + DP("%d %s %d: %u", op, set->name, level, *size); + + return res; +} + +static void list_childsets(const struct ip_set_private *private, + void *data, + ip_set_ip_t sizeid) +{ + ip_set_ip_t id; + + memset(data, 0, bitmap_bytes(0, sizeid - 1)); + + if (private->childsets == NULL) + return; + + for (id = 0; id < sizeid; id++) + if (private->childsets[id] != NULL) + set_bit(id, data); +} + +int ip_set_list_data(struct ip_set *set, + ip_set_ip_t *ip, + unsigned level, + void *data, + int *len, + unsigned op) +{ + int res = 0; /* All OK */ + size_t need; + struct ip_set_private **private; + void (*datafn)(const struct ip_set_private *, void *); + + read_lock_bh(&set->lock); + if (set->subref) { + res = -EBUSY; + goto unlock; + } + private = ip_set_find_private(set, &set->private, ip, level); + if (!(private && *private)) { + res = -ENOENT; + goto unlock; + } + switch (op) { + case IP_SET_OP_LIST_HEADER: + need = set->type[level]->list_header_size(*private); + datafn = set->type[level]->list_header; + break; + case IP_SET_OP_LIST_MEMBERS: + need = set->type[level]->list_members_size(*private); + datafn = set->type[level]->list_members; + break; + case IP_SET_OP_LIST_CHILDSETS: + if ((*private)->childsets == NULL) { + res = -ENOENT; + goto unlock; + } + need = bitmap_bytes(0, set->type[level]->sizeid(*private) - 1); + datafn = NULL; + break; + default: + res = -EINVAL; + goto unlock; + } + if (need > *len) { + res = -ENOMEM; + goto unlock; + } + *len = need; + if (datafn) + datafn(*private, data); + else + list_childsets(*private, data, set->type[level]->sizeid(*private)); + + unlock: + read_unlock_bh(&set->lock); + return res; +} + +static int +ip_set_sockfn_set(struct sock *sk, int optval, void *user, unsigned int len) +{ + void *data; + int res = 0; /* Assume OK */ + struct ip_set_req_base *req_base; + struct ip_set_req_std *req_std; + struct ip_set *set = NULL; + + DP("optval=%d, user=%p, len=%d", optval, user, len); + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (optval != SO_IP_SET) + return -EBADF; + if (len < sizeof(struct ip_set_req_base)) { + ip_set_printk("short userdata (want >=%d, got %d)", + sizeof(struct ip_set_req_base), len); + return -EINVAL; + } + data = vmalloc(len); + if (!data) { + DP("out of mem for %d bytes", len); + return -ENOMEM; + } + if (copy_from_user(data, user, len) != 0) { + res = -EFAULT; + goto done; + } + + req_base = (struct ip_set_req_base *) data; + + DP("op=%x id='%x'", req_base->op, req_base->id); + + /* Handle set creation first - no incoming set specified */ + + if (req_base->op == IP_SET_OP_CREATE) { + struct ip_set_req_create *req_create + = (struct ip_set_req_create *) data; + int i; + + if (len < sizeof(struct ip_set_req_create)) { + ip_set_printk("short CREATE data (want >%d, got %d)", + sizeof(struct ip_set_req_create), len); + res = -EINVAL; + goto done; + } + if (req_create->levels > IP_SET_LEVELS) { + ip_set_printk("set level %d too deep (max %d)", + req_create->levels, IP_SET_LEVELS); + res = -EINVAL; + goto done; + } + req_create->name[IP_SET_MAXNAMELEN - 1] = '\0'; + for (i = 0; i < req_create->levels; i++) + req_create->typename[i][IP_SET_MAXNAMELEN - 1] = '\0'; + res = ip_set_create(req_create->name, + req_create->typename, + req_create->levels, + data + sizeof(struct ip_set_req_create), + len - sizeof(struct ip_set_req_create)); + goto done; + } + + /* All remaining requests want a set by id. + * We take a proper reference here, and drop it after processing. + * From hereon, code goes to '*put_set', not to 'done'. + */ + + set = ip_set_get_byid(req_base->id); + if (set == NULL) { + res = -ESRCH; + goto done; + } + + DP("set %s (%d) (%u)", set->name, req_base->id, set->ref); + /* Simple requests: no subsets */ + switch (req_base->op) { + case IP_SET_OP_RENAME:{ + struct ip_set_req_rename *req_rename + = (struct ip_set_req_rename *) data; + + if (len != sizeof(struct ip_set_req_rename)) { + ip_set_printk("short RENAME data (want >%d, got %d)", + sizeof(struct ip_set_req_rename), len); + res = -EINVAL; + goto put_set; + } + + res = ip_set_rename(set, req_rename->newname); + goto put_set; + } + + case IP_SET_OP_SWAP:{ + struct ip_set_req_swap *req_swap + = (struct ip_set_req_swap *) data; + struct ip_set *to; + + if (len != sizeof(struct ip_set_req_swap)) { + + ip_set_printk("short SWAP data (want >%d, got %d)", + sizeof(struct ip_set_req_swap), len); + res = -EINVAL; + goto put_set; + } + + to = ip_set_get_byid(req_swap->to); + if (to == NULL) { + res = -ESRCH; + goto put_set; + } + res = ip_set_swap(set, to); + ip_set_put(to); + goto put_set; + } + default: + ; /* Requests with possible subsets: fall trough. */ + } + + req_std = (struct ip_set_req_std *) data; + if (len < sizeof(struct ip_set_req_std)) { + ip_set_printk("short data in std request (want >%d, got %d)", + sizeof(struct ip_set_req_std), len); + res = -EINVAL; + goto put_set; + } else if (req_std->level >= set->levels) { + res = -EINVAL; + goto put_set; + } + + switch (req_base->op) { + case IP_SET_OP_ADD_IP:{ + res = ip_set_addip(set, + req_std->ip, req_std->level, + data + sizeof(struct ip_set_req_std), + len - sizeof(struct ip_set_req_std)); + goto put_set; + } + case IP_SET_OP_DEL_IP:{ + res = ip_set_delip(set, + req_std->ip, req_std->level, + data + sizeof(struct ip_set_req_std), + len - sizeof(struct ip_set_req_std)); + goto put_set; + } + case IP_SET_OP_DESTROY:{ + res = ip_set_destroy(set, req_std->ip, req_std->level); + if (req_std->level == 0 && res == 0) + goto done; /* destroyed: no ip_set_put */ + goto put_set; + } + case IP_SET_OP_FLUSH:{ + struct ip_set_req_sub *req_sub = + (struct ip_set_req_sub *) data; + + if (len < sizeof(struct ip_set_req_sub)) { + ip_set_printk("short data in flush request (want >%d, got %d)", + sizeof(struct ip_set_req_sub), len); + res = -EINVAL; + goto put_set; + } + res = ip_set_flush(set, req_sub->ip, req_sub->level, req_sub->childsets); + goto put_set; + } + case IP_SET_OP_CREATE_CHILD:{ + struct ip_set_req_sub *req_sub + = (struct ip_set_req_sub *) data; + + if (len < sizeof(struct ip_set_req_sub)) { + ip_set_printk("short CREATE_CHILD data (want >%d, got %d)", + sizeof(struct ip_set_req_sub), len); + res = -EINVAL; + goto put_set; + } + if (req_sub->level < 1) { + /* No entry supplied? */ + res = -EINVAL; + goto put_set; + } + if (((req_sub->level >= set->levels - 1) && req_sub->childsets)) { + /* No room for subsets to be created. */ + res = -ERANGE; + goto put_set; + } + res = ip_set_create_childset(set, + req_sub->ip, + req_sub->level, + req_sub->childsets, + data + sizeof(struct ip_set_req_sub), + len - sizeof(struct ip_set_req_sub)); + goto put_set; + } + default:{ + DP("unknown op %d", req_base->op); + ip_set_printk("obsolete - upgrade your ipset(8) utility."); + res = -EINVAL; + } + } /* end of switch(op) */ + + put_set: + if (set) + ip_set_put(set); + done: + vfree(data); + if (res > 0) + res = 0; + return res; +} + +static int +ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len) +{ + int res = 0; + struct ip_set_req_base *req_base; + struct ip_set_req_std *req_std; + struct ip_set *set = NULL; + void *data; + int copylen = *len; + + DP("optval=%d, user=%p, len=%d", optval, user, *len); + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (optval != SO_IP_SET) + return -EBADF; + if (*len < sizeof(struct ip_set_req_base)) { + ip_set_printk("short userdata (want >=%d, got %d)", + sizeof(struct ip_set_req_base), *len); + return -EINVAL; + } + data = vmalloc(*len); + if (!data) { + DP("out of mem for %d bytes", *len); + return -ENOMEM; + } + if (copy_from_user(data, user, *len) != 0) { + res = -EFAULT; + goto done; + } + + req_base = (struct ip_set_req_base *) data; + + DP("op=%x id='%x'", req_base->op, req_base->id); + + /* Requests without a named set. */ + switch (req_base->op) { + case IP_SET_OP_VERSION:{ + struct ip_set_req_version *req_version = + (struct ip_set_req_version *) data; + + if (*len != sizeof(struct ip_set_req_version)) { + ip_set_printk("short VERSION (want >=%d, got %d)", + sizeof(struct ip_set_req_version), + *len); + res = -EINVAL; + goto done; + } + + req_version->version = IP_SET_PROTOCOL_VERSION; + res = copy_to_user(user, req_version, + sizeof(struct ip_set_req_version)); + goto done; + } + case IP_SET_OP_LISTING_SIZE:{ + struct ip_set_req_listing_size *req_list = + (struct ip_set_req_listing_size *) data; + + DP("IP_SET_OP_LISTING_SIZE"); + + if (*len != sizeof(struct ip_set_req_listing_size)) { + ip_set_printk("short LISTING_SIZE (want >=%d, got %d)", + sizeof(struct ip_set_req_listing_size), + *len); + res = -EINVAL; + goto done; + } + + req_list->size = ip_set_listing_size(); + DP("req_list->size = %d", req_list->size); + res = copy_to_user(user, req_list, + sizeof(struct ip_set_req_listing_size)); + goto done; + } + case IP_SET_OP_LISTING:{ + DP("LISTING before len=%d", *len); + res = ip_set_listing(data, len); + DP("LISTING done len=%d", *len); + if (res < 0) + goto done; /* Error */ + + res = copy_to_user(user, data, *len); /* Only copy the mem used */ + goto done; + } + default: + ; /* Requests with named set: fall trought */ + } + + /* Special cases: GETSET_BYNAME/BYID */ + switch (req_base->op) { + case IP_SET_OP_GETSET_BYNAME: { + struct ip_set_req_get *req_get + = (struct ip_set_req_get *) data; + + if (*len != sizeof(struct ip_set_req_get)) { + ip_set_printk("short _BYNAME (want >=%d, got %d)", + sizeof(struct ip_set_req_get), *len); + res = -EINVAL; + goto done; + } + req_get->name[IP_SET_MAXNAMELEN - 1] = '\0'; + req_get->id = -1; + set = ip_set_get_byname(req_get->name, &req_get->id); + if (set) { + req_get->ref = set->ref - 1; + ip_set_put(set); + } + res = copy_to_user(user, data, copylen); + goto done; + } + case IP_SET_OP_GETSET_BYID: { + struct ip_set_req_get *req_get + = (struct ip_set_req_get *) data; + + if (*len != sizeof(struct ip_set_req_get)) { + ip_set_printk("short _BYID (want >=%d, got %d)", + sizeof(struct ip_set_req_get), *len); + res = -EINVAL; + goto done; + } + set = ip_set_get_byid(req_get->id); + if (set) { + req_get->ref = set->ref - 1; + strncpy(req_get->name, set->name, IP_SET_MAXNAMELEN); + ip_set_put(set); + } else + req_get->id = -1; + res = copy_to_user(user, data, copylen); + goto done; + } + default: + ; /* Requests with set id: fall trought */ + } + + /* Requests with set id: */ + if (req_base->id < 0 || req_base->id >= max_sets) { + res = -EINVAL; + goto done; + } + set = ip_set_get_byid(req_base->id); /* Reference lock */ + if (!set) { + res = -ENOENT; + goto done; + } + + DP("set %s (%d) (%u)", set->name, req_base->id, set->ref); + req_std = (struct ip_set_req_std *) data; + if (*len < sizeof(struct ip_set_req_std)) { + ip_set_printk("short data in std request (want >%d, got %d)", + sizeof(struct ip_set_req_std), *len); + goto put_inval; + } else if (req_std->level >= set->levels) { + res = -ERANGE; + goto put_set; + } + + switch (req_base->op) { + case IP_SET_OP_TEST_IP:{ + struct ip_set_req_test *req_test = + (struct ip_set_req_test *) data; + + if (*len < sizeof(struct ip_set_req_test)) { + ip_set_printk("short data in testip request (want >%d, got %d)", + sizeof(struct ip_set_req_test), *len); + res = -EINVAL; + goto put_set; + } + req_test->reply = ip_set_testip(set, + req_test->ip, + req_test->level, + data + sizeof(struct ip_set_req_test), + *len - sizeof(struct ip_set_req_test)); + + DP("test result: %i", req_test->reply); + *len = copylen = sizeof(struct ip_set_req_test); + goto put_copy; + } + case IP_SET_OP_LIST_HEADER_SIZE: + case IP_SET_OP_LIST_MEMBERS_SIZE: + case IP_SET_OP_LIST_CHILDSETS_SIZE: { + struct ip_set_req_list *req_list = + (struct ip_set_req_list *) data; + + if (*len != sizeof(struct ip_set_req_list)) { + ip_set_printk("short LIST (want >=%d, got %d)", + sizeof(struct ip_set_req_list), + *len); + goto put_inval; + } + res = ip_set_list_size(set, + req_list->ip, + req_list->level, + &req_list->size, + req_base->op); + DP("SIZEfoo size=%d", req_list->size); + if (res < 0) + goto put_set; /* Error */ + goto put_copy; + } + case IP_SET_OP_LIST_HEADER: + case IP_SET_OP_LIST_MEMBERS: + case IP_SET_OP_LIST_CHILDSETS:{ + DP("LISTfoo before len=%d", *len); + res = ip_set_list_data(set, + req_std->ip, + req_std->level, + data, + len, + req_base->op); + DP("LISTfoo done len=%d", *len); + + if (res < 0) + goto put_set; /* Error */ + + copylen = *len; /* Only copy the mem used */ + goto put_copy; + } + default:{ + DP("unknown op %d", req_base->op); + ip_set_printk("obsolete - upgrade your ipset(8) utility."); + goto put_inval; + } + } /* end of switch(op) */ + + put_copy: + ip_set_put(set); + DP("set %s (%u)", set->name, set->ref); + res = copy_to_user(user, data, copylen); + goto done; + put_inval: + res = -EINVAL; + put_set: + ip_set_put(set); + DP("set %s (%u)", set->name, set->ref); + done: + vfree(data); + if (res > 0) + res = 0; + DP("final result %d", res); + return res; +} + +static struct nf_sockopt_ops so_set = { + .pf = PF_INET, + .set_optmin = SO_IP_SET, + .set_optmax = SO_IP_SET + 1, + .set = &ip_set_sockfn_set, + .get_optmin = SO_IP_SET, + .get_optmax = SO_IP_SET + 1, + .get = &ip_set_sockfn_get, + .use = 0 +}; + +MODULE_PARM(max_sets, "i"); +MODULE_PARM_DESC(max_sets, "maximal number of sets"); + +static int __init init(void) +{ + int res; + + if (max_sets <= 0) + max_sets = CONFIG_IP_NF_SET_MAX; + ip_set_list = vmalloc(sizeof(struct ip_set *) * max_sets); + if (!ip_set_list) { + printk(KERN_ERR "Unable to create ip_set_list\n"); + return -ENOMEM; + } + memset(ip_set_list, 0, sizeof(struct ip_set *) * max_sets); + INIT_LIST_HEAD(&set_type_list); + + res = nf_register_sockopt(&so_set); + if (res != 0) { + ip_set_printk("SO_SET registry failed: %d", res); + vfree(ip_set_list); + return res; + } + return 0; +} + +static void __exit fini(void) +{ + nf_unregister_sockopt(&so_set); + vfree(ip_set_list); + DP("these are the famous last words"); +} + +EXPORT_SYMBOL(ip_set_register_set_type); +EXPORT_SYMBOL(ip_set_unregister_set_type); + +EXPORT_SYMBOL(ip_set_list); +EXPORT_SYMBOL(ip_set_get_byname); +EXPORT_SYMBOL(ip_set_get_byid); +EXPORT_SYMBOL(ip_set_put); + +EXPORT_SYMBOL(ip_set_addip_kernel); +EXPORT_SYMBOL(ip_set_delip_kernel); +EXPORT_SYMBOL(ip_set_testip_kernel); + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ip_set_iphash.c linux-2.6.3/net/ipv4/netfilter/ip_set_iphash.c --- linux-2.6.3.org/net/ipv4/netfilter/ip_set_iphash.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ip_set_iphash.c 2004-02-09 15:02:00.000000000 +0100 @@ -0,0 +1,319 @@ +/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Kernel module implementing an ip hash set */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +static inline ip_set_ip_t +hash_ip(const struct ip_set_iphash *map, ip_set_ip_t ip) +{ + return (jhash_1word(ip & map->netmask, map->initval) % map->hashsize); +} + +static inline int +__testip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + + *id = hash_ip(map, ip); + return (map->members[*id] == ip); +} + +static int +matchip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + return __testip(private, ip, id); +} + +static int +testip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_iphash *req = + (struct ip_set_req_iphash *) data; + + if (size != sizeof(struct ip_set_req_iphash)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_iphash), + size); + return -EINVAL; + } + return __testip(private, req->ip, id); +} + +static int +testip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + return __testip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id); +} + +static inline int +__addip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id, + u_int32_t flags) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + + *id = hash_ip(map, ip); + + if (map->members[*id] == ip) + return -EEXIST; + + if (map->members[*id] != 0 && !(flags & IPSET_ADD_OVERWRITE)) + return -EADDRINUSE; + + map->members[*id] = ip; + return 0; +} + +static int +addip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_iphash *req = + (struct ip_set_req_iphash *) data; + + if (size != sizeof(struct ip_set_req_iphash)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_iphash), + size); + return -EINVAL; + } + return __addip(private, req->ip, id, req->flags); +} + +static int +addip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + return __addip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id, + flags); +} + +static inline int +__delip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + + *id = hash_ip(map, ip); + + if (map->members[*id] == 0) + return -EEXIST; + + if (map->members[*id] != ip) + return -EADDRINUSE; + + map->members[*id] = 0; + return 0; +} + +static int +delip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_iphash *req = + (struct ip_set_req_iphash *) data; + + if (size != sizeof(struct ip_set_req_iphash)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_iphash), + size); + return -EINVAL; + } + return __delip(private, req->ip, id); +} + +static int +delip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + return __delip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id); +} + +static int create(struct ip_set_private **private, const void *data, size_t size) +{ + int newbytes; + struct ip_set_req_iphash_create *req = + (struct ip_set_req_iphash_create *) data; + struct ip_set_iphash *map; + + if (size != sizeof(struct ip_set_req_iphash_create)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_iphash_create), + size); + return -EINVAL; + } + + if (req->hashsize > MAX_RANGE) { + ip_set_printk("hashsize too big (max %d)", + MAX_RANGE); + return -ERANGE; + } + + if (req->hashsize < 1) { + ip_set_printk("hashsize too small"); + return -ERANGE; + } + + map = kmalloc(sizeof(struct ip_set_iphash), GFP_KERNEL); + if (!map) { + DP("out of memory for %d bytes", + sizeof(struct ip_set_iphash)); + return -ENOMEM; + } + map->initval = req->initval; + map->hashsize = req->hashsize; + map->netmask = req->netmask; + newbytes = map->hashsize * sizeof(ip_set_ip_t); + map->members = vmalloc(newbytes); + if (!map->members) { + DP("out of memory for %d bytes", newbytes); + kfree(map); + return -ENOMEM; + } + memset(map->members, 0, newbytes); + + *private = (struct ip_set_private *) map; + return 0; +} + +static void destroy(struct ip_set_private **private) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) *private; + + vfree(map->members); + kfree(map); + + *private = NULL; +} + +static void flush(struct ip_set_private *private) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + memset(map->members, 0, map->hashsize * sizeof(ip_set_ip_t)); +} + +static int list_header_size(const struct ip_set_private *private) +{ + return sizeof(struct ip_set_req_iphash_create); +} + +static void list_header(const struct ip_set_private *private, void *data) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + struct ip_set_req_iphash_create *header = + (struct ip_set_req_iphash_create *) data; + + header->initval = map->initval; + header->hashsize = map->hashsize; + header->netmask = map->netmask; +} + +static int list_members_size(const struct ip_set_private *private) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + + return (map->hashsize * sizeof(ip_set_ip_t)); +} + +static void list_members(const struct ip_set_private *private, void *data) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + + int bytes = map->hashsize * sizeof(ip_set_ip_t); + + memcpy(data, map->members, bytes); +} + +static ip_set_ip_t sizeid(const struct ip_set_private *private) +{ + struct ip_set_iphash *map = (struct ip_set_iphash *) private; + + return (map->hashsize); +} + +static struct ip_set_type ip_set_iphash = { + .typename = SETTYPE_NAME, + .typecode = IPSET_TYPE_IP, + .protocol_version = IP_SET_PROTOCOL_VERSION, + .create = &create, + .destroy = &destroy, + .flush = &flush, + .reqsize = sizeof(struct ip_set_req_iphash), + .addip = &addip, + .addip_kernel = &addip_kernel, + .delip = &delip, + .delip_kernel = &delip_kernel, + .matchip = &matchip, + .testip = &testip, + .testip_kernel = &testip_kernel, + .list_header_size = &list_header_size, + .list_header = &list_header, + .list_members_size = &list_members_size, + .list_members = &list_members, + .sizeid = &sizeid, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + return ip_set_register_set_type(&ip_set_iphash); +} + +static void __exit fini(void) +{ + /* FIXME: possible race with ip_set_create() */ + ip_set_unregister_set_type(&ip_set_iphash); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ip_set_ipmap.c linux-2.6.3/net/ipv4/netfilter/ip_set_ipmap.c --- linux-2.6.3.org/net/ipv4/netfilter/ip_set_ipmap.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ip_set_ipmap.c 2004-02-09 15:02:00.000000000 +0100 @@ -0,0 +1,348 @@ +/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu) + * Patrick Schaaf (bof@bof.de) + * Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Kernel module implementing an IP set type: the single bitmap type */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static inline ip_set_ip_t +ip_to_id(const struct ip_set_ipmap *map, ip_set_ip_t ip) +{ + return ((ip & map->netmask) - map->first_ip)/map->hosts; +} + +static inline int +__testip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + + *id = ip_to_id(map, ip); + return !!test_bit(*id, map->members); +} + +static int +matchip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + return __testip(private, ip, id); +} + +static int +testip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_ipmap *req = + (struct ip_set_req_ipmap *) data; + + if (size != sizeof(struct ip_set_req_ipmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_ipmap), + size); + return -EINVAL; + } + return __testip(private, req->ip, id); +} + +static int +testip_kernel(struct ip_set_private *private, + const struct sk_buff *skb, + u_int32_t flags, + ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + DP("flags: %u (%s) ip %u.%u.%u.%u", flags, + flags & IPSET_SRC ? "SRC" : "DST", + NIPQUAD(skb->nh.iph->saddr)); + DP("flags: %u (%s) ip %u.%u.%u.%u", flags, + flags & IPSET_SRC ? "SRC" : "DST", + NIPQUAD(skb->nh.iph->daddr)); + + return __testip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id); +} + +static inline int +__addip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + + *id = ip_to_id(map, ip); + if (test_and_set_bit(*id, map->members)) + return -EEXIST; + + return 0; +} + +static int +addip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_ipmap *req = + (struct ip_set_req_ipmap *) data; + + if (size != sizeof(struct ip_set_req_ipmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_ipmap), + size); + return -EINVAL; + } + DP("%u.%u.%u.%u", NIPQUAD(req->ip)); + return __addip(private, req->ip, id); +} + +static int +addip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + return __addip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id); +} + +static inline int +__delip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + + *id = ip_to_id(map, ip); + if (!test_and_clear_bit(*id, map->members)) + return -EEXIST; + + return 0; +} + +static int +delip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_ipmap *req = + (struct ip_set_req_ipmap *) data; + + if (size != sizeof(struct ip_set_req_ipmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_ipmap), + size); + return -EINVAL; + } + return __delip(private, req->ip, id); +} + +static int +delip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + return __delip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id); +} + +static int create(struct ip_set_private **private, const void *data, size_t size) +{ + int newbytes; + struct ip_set_req_ipmap_create *req = + (struct ip_set_req_ipmap_create *) data; + struct ip_set_ipmap *map; + + if (size != sizeof(struct ip_set_req_ipmap_create)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_ipmap_create), + size); + return -EINVAL; + } + + DP("from 0x%08x to 0x%08x", req->from, req->to); + + if (req->from > req->to) { + DP("bad ip range"); + return -EINVAL; + } + + if (req->to - req->from > MAX_RANGE) { + ip_set_printk("range too big (max %d addresses)", + MAX_RANGE); + return -ERANGE; + } + + map = kmalloc(sizeof(struct ip_set_ipmap), GFP_KERNEL); + if (!map) { + DP("out of memory for %d bytes", + sizeof(struct ip_set_ipmap)); + return -ENOMEM; + } + map->first_ip = req->from; + map->last_ip = req->to; + map->netmask = req->netmask; + + if (req->netmask == 0xFFFFFFFF) { + map->hosts = 1; + map->sizeid = map->last_ip - map->first_ip + 1; + } else { + unsigned int mask_bits, netmask_bits; + ip_set_ip_t mask; + + map->first_ip &= map->netmask; /* Should we better bark? */ + + mask = range_to_mask(map->first_ip, map->last_ip, &mask_bits); + netmask_bits = mask_to_bits(map->netmask); + + if (!mask || netmask_bits <= mask_bits) + return -EINVAL; + + map->hosts = 2 << (32 - netmask_bits - 1); + map->sizeid = 2 << (netmask_bits - mask_bits - 1); + } + newbytes = bitmap_bytes(0, map->sizeid - 1); + DP("%x %x %i", map->first_ip, map->last_ip, newbytes); + map->members = kmalloc(newbytes, GFP_KERNEL); + if (!map->members) { + DP("out of memory for %d bytes", newbytes); + kfree(map); + return -ENOMEM; + } + memset(map->members, 0, newbytes); + + *private = (struct ip_set_private *) map; + return 0; +} + +static void destroy(struct ip_set_private **private) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) *private; + + kfree(map->members); + kfree(map); + + *private = NULL; +} + +static void flush(struct ip_set_private *private) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + memset(map->members, 0, bitmap_bytes(0, map->sizeid - 1)); +} + +static int list_header_size(const struct ip_set_private *private) +{ + return sizeof(struct ip_set_req_ipmap_create); +} + +static void list_header(const struct ip_set_private *private, void *data) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + struct ip_set_req_ipmap_create *header = + (struct ip_set_req_ipmap_create *) data; + + DP("list_header %x %x", map->first_ip, map->last_ip); + + header->from = map->first_ip; + header->to = map->last_ip; + header->netmask = map->netmask; +} + +static int list_members_size(const struct ip_set_private *private) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + + return bitmap_bytes(0, map->sizeid - 1); +} + +static void list_members(const struct ip_set_private *private, void *data) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + + int bytes = bitmap_bytes(0, map->sizeid - 1); + + memcpy(data, map->members, bytes); +} + +static ip_set_ip_t sizeid(const struct ip_set_private *private) +{ + struct ip_set_ipmap *map = (struct ip_set_ipmap *) private; + + return (map->sizeid); +} + +static struct ip_set_type ip_set_ipmap = { + .typename = SETTYPE_NAME, + .typecode = IPSET_TYPE_IP, + .protocol_version = IP_SET_PROTOCOL_VERSION, + .create = &create, + .destroy = &destroy, + .flush = &flush, + .reqsize = sizeof(struct ip_set_req_ipmap), + .addip = &addip, + .addip_kernel = &addip_kernel, + .delip = &delip, + .delip_kernel = &delip_kernel, + .matchip = &matchip, + .testip = &testip, + .testip_kernel = &testip_kernel, + .list_header_size = &list_header_size, + .list_header = &list_header, + .list_members_size = &list_members_size, + .list_members = &list_members, + .sizeid = &sizeid, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + return ip_set_register_set_type(&ip_set_ipmap); +} + +static void __exit fini(void) +{ + /* FIXME: possible race with ip_set_create() */ + ip_set_unregister_set_type(&ip_set_ipmap); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ip_set_macipmap.c linux-2.6.3/net/ipv4/netfilter/ip_set_macipmap.c --- linux-2.6.3.org/net/ipv4/netfilter/ip_set_macipmap.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ip_set_macipmap.c 2004-02-09 15:02:00.000000000 +0100 @@ -0,0 +1,371 @@ +/* Copyright 2000-2004 Joakim Axelsson (gozem@linux.nu) + * Patrick Schaaf (bof@bof.de) + * Martin Josefsson (gandalf@wlug.westbo.se) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Kernel module implementing an IP set type: the macipmap type */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int +matchip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_macipmap *map = (struct ip_set_macipmap *) private; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + + *id = ip - map->first_ip; + return !!test_bit(IPSET_MACIP_ISSET, &table[*id].flags); +} + +static int +testip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_macipmap *map = (struct ip_set_macipmap *) private; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + + struct ip_set_req_macipmap *req = + (struct ip_set_req_macipmap *) data; + + if (size != sizeof(struct ip_set_req_macipmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_macipmap), + size); + return -EINVAL; + } + + if (req->ip < map->first_ip || req->ip > map->last_ip) + return -ERANGE; + + *id = req->ip - map->first_ip; + if (test_bit(IPSET_MACIP_ISSET, &table[*id].flags)) { + /* Is mac pointer valid? + * If so, compare... */ + return (memcmp(req->ethernet, + &table[*id].ethernet, + ETH_ALEN) == 0); + } else { + return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); + } +} + +static int +testip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + ip_set_ip_t ip; + + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr); + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + + *id = ip - map->first_ip; + if (test_bit(IPSET_MACIP_ISSET, &table[*id].flags)) { + /* Is mac pointer valid? + * If so, compare... */ + return (skb->mac.raw >= skb->head + && (skb->mac.raw + ETH_HLEN) <= skb->data + && (memcmp(skb->mac.ethernet->h_source, + &table[*id].ethernet, + ETH_ALEN) == 0)); + } else { + return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); + } +} + +/* returns 0 on success */ +static inline int +__addip(struct ip_set_private *private, + ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *id) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + if (test_and_set_bit(IPSET_MACIP_ISSET, + &table[ip - map->first_ip].flags)) + return -EEXIST; + + *id = ip - map->first_ip; + memcpy(&table[*id].ethernet, ethernet, ETH_ALEN); + return 0; +} + +static int +addip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_macipmap *req = + (struct ip_set_req_macipmap *) data; + + if (size != sizeof(struct ip_set_req_macipmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_macipmap), + size); + return -EINVAL; + } + return __addip(private, req->ip, req->ethernet, id); +} + +static int +addip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + ip_set_ip_t ip; + + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr); + + if (!(skb->mac.raw >= skb->head + && (skb->mac.raw + ETH_HLEN) <= skb->data)) + return -EINVAL; + + return __addip(private, ip, skb->mac.ethernet->h_source, id); +} + +static inline int +__delip(struct ip_set_private *private, ip_set_ip_t ip, ip_set_ip_t *id) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + if (!test_and_clear_bit(IPSET_MACIP_ISSET, + &table[ip - map->first_ip].flags)) + return -EEXIST; + + *id = ip - map->first_ip; + return 0; +} + +static int +delip(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_macipmap *req = + (struct ip_set_req_macipmap *) data; + + if (size != sizeof(struct ip_set_req_macipmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_macipmap), + size); + return -EINVAL; + } + return __delip(private, req->ip, id); +} + +static int +delip_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + if (!(flags & IPSET_TYPE_IP)) + return -EINVAL; + + return __delip(private, + ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + id); +} + +static int create(struct ip_set_private **private, const void *data, size_t size) +{ + int newbytes; + struct ip_set_req_macipmap_create *req = + (struct ip_set_req_macipmap_create *) data; + struct ip_set_macipmap *map; + + if (size != sizeof(struct ip_set_req_macipmap_create)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_macipmap_create), + size); + return -EINVAL; + } + + DP("from 0x%08x to 0x%08x", req->from, req->to); + + if (req->from > req->to) { + DP("bad ip range"); + return -EINVAL; + } + + if (req->to - req->from > MAX_RANGE) { + ip_set_printk("range too big (max %d addresses)", + MAX_RANGE); + return -ERANGE; + } + + map = kmalloc(sizeof(struct ip_set_macipmap), GFP_KERNEL); + if (!map) { + DP("out of memory for %d bytes", + sizeof(struct ip_set_macipmap)); + return -ENOMEM; + } + map->flags = req->flags; + map->first_ip = req->from; + map->last_ip = req->to; + newbytes = (req->to - req->from + 1) * sizeof(struct ip_set_macip); + map->members = vmalloc(newbytes); + if (!map->members) { + DP("out of memory for %d bytes", newbytes); + kfree(map); + return -ENOMEM; + } + memset(map->members, 0, newbytes); + + *private = (struct ip_set_private *) map; + return 0; +} + +static void destroy(struct ip_set_private **private) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) *private; + + vfree(map->members); + kfree(map); + + *private = NULL; +} + +static void flush(struct ip_set_private *private) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + memset(map->members, 0, (map->last_ip - map->first_ip) + * sizeof(struct ip_set_macip)); +} + +static int list_header_size(const struct ip_set_private *private) +{ + return sizeof(struct ip_set_req_macipmap_create); +} + +static void list_header(const struct ip_set_private *private, void *data) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + struct ip_set_req_macipmap_create *header = + (struct ip_set_req_macipmap_create *) data; + + DP("list_header %x %x %u", map->first_ip, map->last_ip, + map->flags); + + header->from = map->first_ip; + header->to = map->last_ip; + header->flags = map->flags; +} + +static int list_members_size(const struct ip_set_private *private) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + + return (map->last_ip + - map->first_ip + 1) * sizeof(struct ip_set_macip); +} + +static void list_members(const struct ip_set_private *private, void *data) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) private; + + int bytes = (map->last_ip - + - map->first_ip + 1) * sizeof(struct ip_set_macip); + + memcpy(data, map->members, bytes); +} + +static ip_set_ip_t sizeid(const struct ip_set_private *private) +{ + struct ip_set_macipmap *map = (struct ip_set_macipmap *) private; + + return (map->last_ip - map->first_ip + 1); +} + +static struct ip_set_type ip_set_macipmap = { + .typename = SETTYPE_NAME, + .typecode = IPSET_TYPE_IP, + .protocol_version = IP_SET_PROTOCOL_VERSION, + .create = &create, + .destroy = &destroy, + .flush = &flush, + .reqsize = sizeof(struct ip_set_req_macipmap), + .addip = &addip, + .addip_kernel = &addip_kernel, + .delip = &delip, + .delip_kernel = &delip_kernel, + .matchip = &matchip, + .testip = &testip, + .testip_kernel = &testip_kernel, + .list_header_size = &list_header_size, + .list_header = &list_header, + .list_members_size = &list_members_size, + .list_members = &list_members, + .sizeid = &sizeid, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + return ip_set_register_set_type(&ip_set_macipmap); +} + +static void __exit fini(void) +{ + /* FIXME: possible race with ip_set_create() */ + ip_set_unregister_set_type(&ip_set_macipmap); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ip_set_portmap.c linux-2.6.3/net/ipv4/netfilter/ip_set_portmap.c --- linux-2.6.3.org/net/ipv4/netfilter/ip_set_portmap.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ip_set_portmap.c 2004-02-09 15:02:00.000000000 +0100 @@ -0,0 +1,363 @@ +/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * Based on ip_set_ipmap.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Kernel module implementing a port set type as a bitmap */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static inline ip_set_ip_t +get_port(const struct sk_buff *skb, u_int32_t flags) +{ + struct iphdr *iph = skb->nh.iph; + u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; + + switch (iph->protocol) { + case IPPROTO_TCP: { + struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); + + /* See comments at tcp_match in ip_tables.c */ + if (offset != 0 + || (offset == 0 + && (skb->len - iph->ihl * 4) < sizeof(struct tcphdr))) + return INVALID_PORT; + + return ntohs(flags & IPSET_SRC ? + tcph->source : tcph->dest); + } + case IPPROTO_UDP: { + struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl); + + if (offset != 0 + || (offset == 0 + && (skb->len - iph->ihl * 4) < sizeof(struct udphdr))) + return INVALID_PORT; + + return ntohs(flags & IPSET_SRC ? + udph->source : udph->dest); + } + default: + return INVALID_PORT; + } +} + +static inline int +__testport(struct ip_set_private *private, ip_set_ip_t port, ip_set_ip_t *id) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + + if (port < map->first_port || port > map->last_port) + return -ERANGE; + + *id = port - map->first_port; + return !!test_bit(*id, map->members); +} + +static int +matchport(struct ip_set_private *private, ip_set_ip_t port, ip_set_ip_t *id) +{ + return __testport(private, port, id); +} + +static int +testport(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_portmap *req = + (struct ip_set_req_portmap *) data; + + if (size != sizeof(struct ip_set_req_portmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_portmap), + size); + return -EINVAL; + } + return __testport(private, req->port, id); +} + +static int +testport_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + ip_set_ip_t port; + + if (!(flags & IPSET_TYPE_PORT)) + return -EINVAL; + + port = get_port(skb, flags); + DP("flags %u %s port %u", + flags, + flags & IPSET_SRC ? "SRC" : "DST", + port); + + if (port == INVALID_PORT) + return -EINVAL; + + return __testport(private, port, id); +} + +static inline int +__addport(struct ip_set_private *private, ip_set_ip_t port, ip_set_ip_t *id) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + + if (port < map->first_port || port > map->last_port) + return -ERANGE; + if (test_and_set_bit(port - map->first_port, map->members)) + return -EEXIST; + + *id = port - map->first_port; + return 0; +} + +static int +addport(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_portmap *req = + (struct ip_set_req_portmap *) data; + + if (size != sizeof(struct ip_set_req_portmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_portmap), + size); + return -EINVAL; + } + return __addport(private, req->port, id); +} + +static int +addport_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + ip_set_ip_t port; + + if (!(flags & IPSET_TYPE_PORT)) + return -EINVAL; + + port = get_port(skb, flags); + + if (port == INVALID_PORT) + return -EINVAL; + + return __addport(private, port, id); +} + +static inline int +__delport(struct ip_set_private *private, ip_set_ip_t port, ip_set_ip_t *id) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + + if (port < map->first_port || port > map->last_port) + return -ERANGE; + if (!test_and_clear_bit(port - map->first_port, map->members)) + return -EEXIST; + + *id = port - map->first_port; + return 0; +} + +static int +delport(struct ip_set_private *private, const void *data, size_t size, + ip_set_ip_t *id) +{ + struct ip_set_req_portmap *req = + (struct ip_set_req_portmap *) data; + + if (size != sizeof(struct ip_set_req_portmap)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_portmap), + size); + return -EINVAL; + } + return __delport(private, req->port, id); +} + +static int +delport_kernel(struct ip_set_private *private, const struct sk_buff *skb, + u_int32_t flags, ip_set_ip_t *id) +{ + ip_set_ip_t port; + + if (!(flags & IPSET_TYPE_PORT)) + return -EINVAL; + + port = get_port(skb, flags); + + if (port == INVALID_PORT) + return -EINVAL; + + return __delport(private, port, id); +} + +static int create(struct ip_set_private **private, const void *data, size_t size) +{ + int newbytes; + struct ip_set_req_portmap_create *req = + (struct ip_set_req_portmap_create *) data; + struct ip_set_portmap *map; + + if (size != sizeof(struct ip_set_req_portmap_create)) { + ip_set_printk("data length wrong (want %d, have %d)", + sizeof(struct ip_set_req_portmap_create), + size); + return -EINVAL; + } + + DP("from 0x%08x to 0x%08x", req->from, req->to); + + if (req->from > req->to) { + DP("bad port range"); + return -EINVAL; + } + + if (req->to - req->from > MAX_RANGE) { + ip_set_printk("range too big (max %d ports)", + MAX_RANGE); + return -ERANGE; + } + + map = kmalloc(sizeof(struct ip_set_portmap), GFP_KERNEL); + if (!map) { + DP("out of memory for %d bytes", + sizeof(struct ip_set_portmap)); + return -ENOMEM; + } + map->first_port = req->from; + map->last_port = req->to; + newbytes = bitmap_bytes(req->from, req->to); + map->members = kmalloc(newbytes, GFP_KERNEL); + if (!map->members) { + DP("out of memory for %d bytes", newbytes); + kfree(map); + return -ENOMEM; + } + memset(map->members, 0, newbytes); + + *private = (struct ip_set_private *) map; + return 0; +} + +static void destroy(struct ip_set_private **private) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) *private; + + kfree(map->members); + kfree(map); + + *private = NULL; +} + +static void flush(struct ip_set_private *private) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + memset(map->members, 0, bitmap_bytes(map->first_port, map->last_port)); +} + +static int list_header_size(const struct ip_set_private *private) +{ + return sizeof(struct ip_set_req_portmap_create); +} + +static void list_header(const struct ip_set_private *private, void *data) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + struct ip_set_req_portmap_create *header = + (struct ip_set_req_portmap_create *) data; + + DP("list_header %x %x", map->first_port, map->last_port); + + header->from = map->first_port; + header->to = map->last_port; +} + +static int list_members_size(const struct ip_set_private *private) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + + return bitmap_bytes(map->first_port, map->last_port); +} + +static void list_members(const struct ip_set_private *private, void *data) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + + int bytes = bitmap_bytes(map->first_port, map->last_port); + + memcpy(data, map->members, bytes); +} + +static ip_set_ip_t sizeid(const struct ip_set_private *private) +{ + struct ip_set_portmap *map = (struct ip_set_portmap *) private; + + return (map->last_port - map->first_port + 1); +} + +static struct ip_set_type ip_set_portmap = { + .typename = SETTYPE_NAME, + .typecode = IPSET_TYPE_PORT, + .protocol_version = IP_SET_PROTOCOL_VERSION, + .create = &create, + .destroy = &destroy, + .flush = &flush, + .reqsize = sizeof(struct ip_set_req_portmap), + .addip = &addport, + .addip_kernel = &addport_kernel, + .delip = &delport, + .delip_kernel = &delport_kernel, + .matchip = &matchport, + .testip = &testport, + .testip_kernel = &testport_kernel, + .list_header_size = &list_header_size, + .list_header = &list_header, + .list_members_size = &list_members_size, + .list_members = &list_members, + .sizeid = &sizeid, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + return ip_set_register_set_type(&ip_set_portmap); +} + +static void __exit fini(void) +{ + /* FIXME: possible race with ip_set_create() */ + ip_set_unregister_set_type(&ip_set_portmap); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c linux-2.6.3/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c 2004-02-19 08:47:22.288007614 +0100 @@ -0,0 +1,84 @@ +/** + * Strip all IP options in the IP packet header. + * + * (C) 2001 by Fabrice MARIE + * This software is distributed under GNU GPL v2, 1991 + */ + +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Fabrice MARIE "); +MODULE_DESCRIPTION("Strip all options in IPv4 packets"); +MODULE_LICENSE("GPL"); + +static unsigned int +target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct sk_buff *skb = (*pskb); + struct ip_options * opt; + unsigned char * optiph = skb->nh.raw; + int l = ((struct ip_options *)(&(IPCB(skb)->opt)))->optlen; + + + /* if no options in packet then nothing to clear. */ + if (iph->ihl * 4 == sizeof(struct iphdr)) + return IPT_CONTINUE; + + /* else clear all options */ + memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); + memset(optiph+sizeof(struct iphdr), IPOPT_NOOP, l); + opt = &(IPCB(skb)->opt); + opt->is_data = 0; + opt->optlen = l; + + skb->nfcache |= NFC_ALTERED; + + return IPT_CONTINUE; +} + +static int +checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + if (strcmp(tablename, "mangle")) { + printk(KERN_WARNING "IPV4OPTSSTRIP: can only be called from \"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + /* nothing else to check because no parameters */ + return 1; +} + +static struct ipt_target ipt_ipv4optsstrip_reg += { { NULL, NULL }, "IPV4OPTSSTRIP", target, checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_target(&ipt_ipv4optsstrip_reg)) + return -EINVAL; + printk("ipt_IPV4OPTSSTRIP loaded\n"); + + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_ipv4optsstrip_reg); + printk("ipt_IPV4OPTSSTRIP unloaded\n"); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_NETLINK.c linux-2.6.3/net/ipv4/netfilter/ipt_NETLINK.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_NETLINK.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_NETLINK.c 2004-02-19 08:47:24.269530314 +0100 @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Gianni Tedesco "); +MODULE_DESCRIPTION("Provides iptables NETLINK target similar to ipchains -o"); +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static struct sock *ipfwsk; + +static unsigned int ipt_netlink_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, void *userinfo) +{ + struct ipt_nldata *nld = (struct ipt_nldata *)targinfo; + struct iphdr *ip = (*pskb)->nh.iph; + struct sk_buff *outskb; + struct netlink_t nlhdr; + size_t len=0; + + /* Allocate a socket buffer */ + if ( MASK(nld->flags, USE_SIZE) ) + len = nld->size+sizeof(nlhdr); + else + len = ntohs(ip->tot_len)+sizeof(nlhdr); + + outskb=alloc_skb(len, GFP_ATOMIC); + + if (outskb) { + nlhdr.len=len; + + if ( MASK(nld->flags, USE_MARK) ) + nlhdr.mark=(*pskb)->nfmark=nld->mark; + else + nlhdr.mark=(*pskb)->nfmark; + + if ( in && in->name ) { + strncpy((char *)&nlhdr.iface, in->name, IFNAMSIZ); + }else if ( out && out->name ){ + strncpy((char *)&nlhdr.iface, out->name, IFNAMSIZ); + } + + skb_put(outskb, len); + memcpy(outskb->data, &nlhdr, sizeof(nlhdr)); + memcpy((outskb->data)+sizeof(nlhdr), ip, len-sizeof(nlhdr)); + netlink_broadcast(ipfwsk, outskb, 0, ~0, GFP_ATOMIC); + }else{ + if (net_ratelimit()) + printk(KERN_WARNING "ipt_NETLINK: packet drop due to netlink failure\n"); + } + + if ( MASK(nld->flags, USE_DROP) ) + return NF_DROP; + + return IPT_CONTINUE; +} + +static int ipt_netlink_checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hookmask) +{ + //struct ipt_nldata *nld = (struct ipt_nldata *)targinfo; + + return 1; +} + +static struct ipt_target ipt_netlink_reg = { + {NULL, NULL}, + "NETLINK", + ipt_netlink_target, + ipt_netlink_checkentry, + NULL, + THIS_MODULE +}; + +static int __init init(void) +{ + DEBUGP("ipt_NETLINK: init module\n"); + + if (ipt_register_target(&ipt_netlink_reg) != 0) { + return -EINVAL; + } + + if ( !(ipfwsk=netlink_kernel_create(NETLINK_FIREWALL, NULL)) ){ + return -EINVAL; + } + + return 0; +} + +static void __exit fini(void) +{ + DEBUGP("ipt_NETLINK: cleanup_module\n"); + ipt_unregister_target(&ipt_netlink_reg); + if(ipfwsk->socket) sock_release(ipfwsk->socket); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_POOL.c linux-2.6.3/net/ipv4/netfilter/ipt_POOL.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_POOL.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_POOL.c 2004-02-19 08:47:30.712978249 +0100 @@ -0,0 +1,116 @@ +/* ipt_POOL.c - netfilter target to manipulate IP pools + * + * This target can be used almost everywhere. It acts on some specified + * IP pool, adding or deleting some IP address in the pool. The address + * can be either the source (--addsrc, --delsrc), or destination (--add/deldst) + * of the packet under inspection. + * + * The target normally returns IPT_CONTINUE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/*** NOTE NOTE NOTE NOTE *** +** +** By sheer luck, I get away with using the "struct ipt_pool_info", as defined +** in , both as the match and target info. +** Here, in the target implementation, ipt_pool_info.src, if not IP_POOL_NONE, +** is modified for the source IP address of the packet under inspection. +** The same way, the ipt_pool_info.dst pool is modified for the destination. +** +** The address is added to the pool normally. However, if IPT_POOL_DEL_dir +** flag is set in ipt_pool_info.flags, the address is deleted from the pool. +** +** If a modification was done to the pool, we possibly return ACCEPT or DROP, +** if the right IPT_POOL_MOD_dir_ACCEPT or _MOD_dir_DROP flags are set. +** The IPT_POOL_INV_MOD_dir flag inverts the sense of the check (i.e. the +** ACCEPT and DROP flags are evaluated when the pool was not modified.) +*/ + +static int +do_check(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + const struct ipt_pool_info *ipi = targinfo; + + if (targinfosize != IPT_ALIGN(sizeof(*ipi))) { + DEBUGP("POOL_check: size %u.\n", targinfosize); + return 0; + } + DEBUGP("ipt_POOL:do_check(%d,%d,%d)\n",ipi->src,ipi->dst,ipi->flags); + return 1; +} + +static unsigned int +do_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 ipt_pool_info *ipi = targinfo; + int modified; + unsigned int verdict = IPT_CONTINUE; + + if (ipi->src != IP_POOL_NONE) { + modified = ip_pool_mod(ipi->src, ntohl((*pskb)->nh.iph->saddr), + ipi->flags & IPT_POOL_DEL_SRC); + if (!!modified ^ !!(ipi->flags & IPT_POOL_INV_MOD_SRC)) { + if (ipi->flags & IPT_POOL_MOD_SRC_ACCEPT) + verdict = NF_ACCEPT; + else if (ipi->flags & IPT_POOL_MOD_SRC_DROP) + verdict = NF_DROP; + } + } + if (verdict == IPT_CONTINUE && ipi->dst != IP_POOL_NONE) { + modified = ip_pool_mod(ipi->dst, ntohl((*pskb)->nh.iph->daddr), + ipi->flags & IPT_POOL_DEL_DST); + if (!!modified ^ !!(ipi->flags & IPT_POOL_INV_MOD_DST)) { + if (ipi->flags & IPT_POOL_MOD_DST_ACCEPT) + verdict = NF_ACCEPT; + else if (ipi->flags & IPT_POOL_MOD_DST_DROP) + verdict = NF_DROP; + } + } + return verdict; +} + +static struct ipt_target pool_reg += { { NULL, NULL }, "POOL", do_target, do_check, NULL, THIS_MODULE }; + +static int __init init(void) +{ + DEBUGP("init ipt_POOL\n"); + return ipt_register_target(&pool_reg); +} + +static void __exit fini(void) +{ + DEBUGP("fini ipt_POOL\n"); + ipt_unregister_target(&pool_reg); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_SET.c linux-2.6.3/net/ipv4/netfilter/ipt_SET.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_SET.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_SET.c 2004-02-09 15:02:00.000000000 +0100 @@ -0,0 +1,115 @@ +/* ipt_SET.c - netfilter target to manipulate IP sets + * + * This target can be used almost everywhere. It acts on some specified + * IP set, adding or deleting some IP addresses/ports in the set. + * The addresses/ports can be either the source, or destination + * of the packet under inspection. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int +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 ipt_set_info_target *info = targinfo; + + if (info->add_set.id >= 0) + ip_set_addip_kernel(ip_set_list[info->add_set.id], + *pskb, + info->add_set.flags, + info->add_set.set_level, + info->add_set.ip_level); + if (info->del_set.id >= 0) + ip_set_delip_kernel(ip_set_list[info->del_set.id], + *pskb, + info->del_set.flags, + info->del_set.set_level, + info->del_set.ip_level); + + return IPT_CONTINUE; +} + +static int +checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, unsigned int hook_mask) +{ + struct ipt_set_info_target *info = targinfo; + + if (targinfosize != IPT_ALIGN(sizeof(*info))) { + DP("bad target info size %u", targinfosize); + return 0; + } + + if (info->add_set.id >= 0 + && !ip_set_get_byid(info->add_set.id)) { + ip_set_printk("cannot verify add_set id %i as target", + info->add_set.id); + return 0; /* error */ + } + if (info->del_set.id >= 0 + && !ip_set_get_byid(info->del_set.id)) { + ip_set_printk("cannot verify del_set id %i as target", + info->del_set.id); + return 0; /* error */ + } + DP("checkentry OK"); + + return 1; +} + +static void destroy(void *targetinfo, unsigned int targetsize) +{ + struct ipt_set_info_target *info = targetinfo; + + if (targetsize != IPT_ALIGN(sizeof(struct ipt_set_info_target))) { + ip_set_printk("invalid targetsize %d", targetsize); + return; + } + + if (info->add_set.id >= 0) + ip_set_put(ip_set_list[info->add_set.id]); + if (info->del_set.id >= 0) + ip_set_put(ip_set_list[info->del_set.id]); +} + +static struct ipt_target SET_target = { + .name = "SET", + .target = target, + .checkentry = checkentry, + .destroy = destroy, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_target(&SET_target); +} + +static void __exit fini(void) +{ + ipt_unregister_target(&SET_target); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_TTL.c linux-2.6.3/net/ipv4/netfilter/ipt_TTL.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_TTL.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_TTL.c 2004-02-19 08:47:25.605208582 +0100 @@ -0,0 +1,110 @@ +/* TTL modification target for IP tables + * (C) 2000 by Harald Welte + * + * Version: 1.8 + * + * This software is distributed under the terms of GNU GPL + */ + +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("IP tables TTL modification module"); +MODULE_LICENSE("GPL"); + +static unsigned int ipt_ttl_target(struct sk_buff **pskb, unsigned int hooknum, + const struct net_device *in, const struct net_device *out, + const void *targinfo, void *userinfo) +{ + struct iphdr *iph = (*pskb)->nh.iph; + const struct ipt_TTL_info *info = targinfo; + u_int16_t diffs[2]; + int new_ttl; + + switch (info->mode) { + case IPT_TTL_SET: + new_ttl = info->ttl; + break; + case IPT_TTL_INC: + new_ttl = iph->ttl + info->ttl; + if (new_ttl > 255) + new_ttl = 255; + break; + case IPT_TTL_DEC: + new_ttl = iph->ttl + info->ttl; + if (new_ttl < 0) + new_ttl = 0; + break; + default: + new_ttl = iph->ttl; + break; + } + + if (new_ttl != iph->ttl) { + diffs[0] = htons(((unsigned)iph->ttl) << 8) ^ 0xFFFF; + iph->ttl = new_ttl; + diffs[1] = htons(((unsigned)iph->ttl) << 8); + iph->check = csum_fold(csum_partial((char *)diffs, + sizeof(diffs), + iph->check^0xFFFF)); + (*pskb)->nfcache |= NFC_ALTERED; + } + + return IPT_CONTINUE; +} + +static int ipt_ttl_checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + struct ipt_TTL_info *info = targinfo; + + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_TTL_info))) { + printk(KERN_WARNING "TTL: targinfosize %u != %Zu\n", + targinfosize, + IPT_ALIGN(sizeof(struct ipt_TTL_info))); + return 0; + } + + if (strcmp(tablename, "mangle")) { + printk(KERN_WARNING "TTL: can only be called from \"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + + if (info->mode > IPT_TTL_MAXMODE) { + printk(KERN_WARNING "TTL: invalid or unknown Mode %u\n", + info->mode); + return 0; + } + + if ((info->mode != IPT_TTL_SET) && (info->ttl == 0)) { + printk(KERN_WARNING "TTL: increment/decrement doesn't make sense with value 0\n"); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_TTL = { { NULL, NULL }, "TTL", + ipt_ttl_target, ipt_ttl_checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_target(&ipt_TTL); +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_TTL); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_connlimit.c linux-2.6.3/net/ipv4/netfilter/ipt_connlimit.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_connlimit.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_connlimit.c 2004-02-19 08:47:26.593970414 +0100 @@ -0,0 +1,232 @@ +/* + * netfilter module to limit the number of parallel tcp + * connections per IP address. + * (c) 2000 Gerd Knorr + * Nov 2002: Martin Bene : + * only ignore TIME_WAIT or gone connections + * + * based on ... + * + * Kernel module to match connection tracking information. + * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au). + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG 0 + +MODULE_LICENSE("GPL"); + +/* we'll save the tuples of all connections we care about */ +struct ipt_connlimit_conn +{ + struct list_head list; + struct ip_conntrack_tuple tuple; +}; + +struct ipt_connlimit_data { + spinlock_t lock; + struct list_head iphash[256]; +}; + +static int ipt_iphash(u_int32_t addr) +{ + int hash; + + hash = addr & 0xff; + hash ^= (addr >> 8) & 0xff; + hash ^= (addr >> 16) & 0xff; + hash ^= (addr >> 24) & 0xff; + return hash; +} + +static int count_them(struct ipt_connlimit_data *data, + u_int32_t addr, u_int32_t mask, + struct ip_conntrack *ct) +{ +#if DEBUG + const static char *tcp[] = { "none", "established", "syn_sent", "syn_recv", + "fin_wait", "time_wait", "close", "close_wait", + "last_ack", "listen" }; +#endif + int addit = 1, matches = 0; + struct ip_conntrack_tuple tuple; + struct ip_conntrack_tuple_hash *found; + struct ipt_connlimit_conn *conn; + struct list_head *hash,*lh; + + spin_lock(&data->lock); + tuple = ct->tuplehash[0].tuple; + hash = &data->iphash[ipt_iphash(addr & mask)]; + + /* check the saved connections */ + for (lh = hash->next; lh != hash; lh = lh->next) { + conn = list_entry(lh,struct ipt_connlimit_conn,list); + found = ip_conntrack_find_get(&conn->tuple,ct); + if (0 == memcmp(&conn->tuple,&tuple,sizeof(tuple)) && + found != NULL && + found->ctrack->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT) { + /* Just to be sure we have it only once in the list. + We should'nt see tuples twice unless someone hooks this + into a table without "-p tcp --syn" */ + addit = 0; + } +#if DEBUG + printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d %s\n", + ipt_iphash(addr & mask), + NIPQUAD(conn->tuple.src.ip), ntohs(conn->tuple.src.u.tcp.port), + NIPQUAD(conn->tuple.dst.ip), ntohs(conn->tuple.dst.u.tcp.port), + (NULL != found) ? tcp[found->ctrack->proto.tcp.state] : "gone"); +#endif + if (NULL == found) { + /* this one is gone */ + lh = lh->prev; + list_del(lh->next); + kfree(conn); + continue; + } + if (found->ctrack->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT) { + /* we don't care about connections which are + closed already -> ditch it */ + lh = lh->prev; + list_del(lh->next); + kfree(conn); + nf_conntrack_put(&found->ctrack->infos[0]); + continue; + } + if ((addr & mask) == (conn->tuple.src.ip & mask)) { + /* same source IP address -> be counted! */ + matches++; + } + nf_conntrack_put(&found->ctrack->infos[0]); + } + if (addit) { + /* save the new connection in our list */ +#if DEBUG + printk("ipt_connlimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d new\n", + ipt_iphash(addr & mask), + NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port), + NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port)); +#endif + conn = kmalloc(sizeof(*conn),GFP_ATOMIC); + if (NULL == conn) + return -1; + memset(conn,0,sizeof(*conn)); + INIT_LIST_HEAD(&conn->list); + conn->tuple = tuple; + list_add(&conn->list,hash); + matches++; + } + spin_unlock(&data->lock); + return matches; +} + +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 ipt_connlimit_info *info = matchinfo; + int connections, match; + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + + ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + if (NULL == ct) { + printk("ipt_connlimit: Oops: invalid ct state ?\n"); + *hotdrop = 1; + return 0; + } + connections = count_them(info->data,skb->nh.iph->saddr,info->mask,ct); + if (-1 == connections) { + printk("ipt_connlimit: Hmm, kmalloc failed :-(\n"); + *hotdrop = 1; /* let's free some memory :-) */ + return 0; + } + match = (info->inverse) ? (connections <= info->limit) : (connections > info->limit); +#if DEBUG + printk("ipt_connlimit: src=%u.%u.%u.%u mask=%u.%u.%u.%u " + "connections=%d limit=%d match=%s\n", + NIPQUAD(skb->nh.iph->saddr), NIPQUAD(info->mask), + connections, info->limit, match ? "yes" : "no"); +#endif + + return match; +} + +static int check(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_connlimit_info *info = matchinfo; + int i; + + /* verify size */ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_connlimit_info))) + return 0; + + /* refuse anything but tcp */ + if (ip->proto != IPPROTO_TCP) + return 0; + + /* init private data */ + info->data = kmalloc(sizeof(struct ipt_connlimit_data),GFP_KERNEL); + spin_lock_init(&(info->data->lock)); + for (i = 0; i < 256; i++) + INIT_LIST_HEAD(&(info->data->iphash[i])); + + return 1; +} + +static void destroy(void *matchinfo, unsigned int matchinfosize) +{ + struct ipt_connlimit_info *info = matchinfo; + struct ipt_connlimit_conn *conn; + struct list_head *hash; + int i; + + /* cleanup */ + for (i = 0; i < 256; i++) { + hash = &(info->data->iphash[i]); + while (hash != hash->next) { + conn = list_entry(hash->next,struct ipt_connlimit_conn,list); + list_del(hash->next); + kfree(conn); + } + } + kfree(info->data); +} + +static struct ipt_match connlimit_match += { { NULL, NULL }, "connlimit", &match, &check, &destroy, THIS_MODULE }; + +static int __init init(void) +{ + /* NULL if ip_conntrack not a module */ + if (ip_conntrack_module) + __MOD_INC_USE_COUNT(ip_conntrack_module); + return ipt_register_match(&connlimit_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&connlimit_match); + if (ip_conntrack_module) + __MOD_DEC_USE_COUNT(ip_conntrack_module); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_dstlimit.c linux-2.6.3/net/ipv4/netfilter/ipt_dstlimit.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_dstlimit.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_dstlimit.c 2004-02-19 08:47:27.235815809 +0100 @@ -0,0 +1,677 @@ +/* iptables match extension to limit the number of packets per second + * seperately for each destination. + * + * (C) 2003 by Harald Welte + * + * $Id$ + * + * Development of this code was funded by Astaro AG, http://www.astaro.com/ + * + * based on ipt_limit.c by: + * Jérôme de Vivie + * Hervé Eychenne + * Rusty Russell + * + * The general idea is to create a hash table for every dstip and have a + * seperate limit counter per tuple. This way you can do something like 'limit + * the number of syn packets for each of my internal addresses. + * + * Ideally this would just be implemented as a general 'hash' match, which would + * allow us to attach any iptables target to it's hash buckets. But this is + * not possible in the current iptables architecture. As always, pkttables for + * 2.7.x will help ;) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASSERT_READ_LOCK(x) +#define ASSERT_WRITE_LOCK(x) +#include +#include + +#include +#include + +/* FIXME: this is just for IP_NF_ASSERRT */ +#include + +#define MS2JIFFIES(x) ((x*HZ)/1000) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("iptables match for limiting per destination"); + +/* need to declare this at the top */ +static struct proc_dir_entry *dstlimit_procdir; +static struct file_operations dl_file_ops; + +/* hash table crap */ + +struct dsthash_dst { + u_int32_t src_ip; + u_int32_t dst_ip; + u_int16_t port; +}; + +struct dsthash_ent { + /* static / read-only parts in the beginning */ + struct list_head list; + struct dsthash_dst dst; + + /* modified structure members in the end */ + unsigned long expires; /* precalculated expiry time */ + struct { + unsigned long prev; /* last modification */ + u_int32_t credit; + u_int32_t credit_cap, cost; + } rateinfo; +}; + +struct ipt_dstlimit_htable { + struct list_head list; /* global list of all htables */ + atomic_t use; + + struct dstlimit_cfg cfg; /* config */ + + /* used internally */ + spinlock_t lock; /* lock for list_head */ + u_int32_t rnd; /* random seed for hash */ + struct timer_list timer; /* timer for gc */ + atomic_t count; /* number entries in table */ + + /* seq_file stuff */ + struct proc_dir_entry *pde; + + struct list_head hash[0]; /* hashtable itself */ +}; + +DECLARE_RWLOCK(dstlimit_lock); /* protects htables list */ +static LIST_HEAD(dstlimit_htables); +static kmem_cache_t *dstlimit_cachep; + +static inline int dst_cmp(const struct dsthash_ent *ent, struct dsthash_dst *b) +{ + return (ent->dst.dst_ip == b->dst_ip + && ent->dst.port == b->port + && ent->dst.src_ip == b->src_ip); +} + +static inline u_int32_t +hash_dst(const struct ipt_dstlimit_htable *ht, const struct dsthash_dst *dst) +{ + return (jhash_3words(dst->dst_ip, dst->port, + dst->src_ip, ht->rnd) % ht->cfg.size); +} + +static inline struct dsthash_ent * +__dsthash_find(const struct ipt_dstlimit_htable *ht, struct dsthash_dst *dst) +{ + struct dsthash_ent *ent; + u_int32_t hash = hash_dst(ht, dst); + MUST_BE_LOCKED(&ht->lock); + ent = LIST_FIND(&ht->hash[hash], dst_cmp, struct dsthash_ent *, dst); + return ent; +} + +/* allocate dsthash_ent, initialize dst, put in htable and lock it */ +static struct dsthash_ent * +__dsthash_alloc_init(struct ipt_dstlimit_htable *ht, struct dsthash_dst *dst) +{ + struct dsthash_ent *ent; + + /* initialize hash with random val at the time we allocate + * the first hashtable entry */ + if (!ht->rnd) + get_random_bytes(&ht->rnd, 4); + + if (ht->cfg.max && + atomic_read(&ht->count) >= ht->cfg.max) { + /* FIXME: do something. question is what.. */ + if (net_ratelimit()) + printk(KERN_WARNING + "ipt_dstlimit: max count of %u reached\n", + ht->cfg.max); + return NULL; + } + + ent = kmem_cache_alloc(dstlimit_cachep, GFP_ATOMIC); + if (!ent) { + if (net_ratelimit()) + printk(KERN_ERR + "ipt_dstlimit: can't allocate dsthash_ent\n"); + return NULL; + } + + atomic_inc(&ht->count); + + ent->dst.dst_ip = dst->dst_ip; + ent->dst.port = dst->port; + ent->dst.src_ip = dst->src_ip; + + list_add(&ent->list, &ht->hash[hash_dst(ht, dst)]); + + return ent; +} + +static inline void +__dsthash_free(struct ipt_dstlimit_htable *ht, struct dsthash_ent *ent) +{ + MUST_BE_LOCKED(&ht->lock); + + list_del(&ent->list); + kmem_cache_free(dstlimit_cachep, ent); + atomic_dec(&ht->count); +} +static void htable_gc(unsigned long htlong); + +static int htable_create(struct ipt_dstlimit_info *minfo) +{ + int i; + unsigned int size; + struct ipt_dstlimit_htable *hinfo; + + if (minfo->cfg.size) + size = minfo->cfg.size; + else { + size = (((num_physpages << PAGE_SHIFT) / 16384) + / sizeof(struct list_head)); + if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE)) + size = 8192; + if (size < 16) + size = 16; + } + /* FIXME: don't use vmalloc() here or anywhere else -HW */ + hinfo = vmalloc(sizeof(struct ipt_dstlimit_htable) + + (sizeof(struct list_head) * size)); + if (!hinfo) { + printk(KERN_ERR "ipt_dstlimit: Unable to create hashtable\n"); + return -1; + } + minfo->hinfo = hinfo; + + /* copy match config into hashtable config */ + memcpy(&hinfo->cfg, &minfo->cfg, sizeof(hinfo->cfg)); + hinfo->cfg.size = size; + if (!hinfo->cfg.max) + hinfo->cfg.max = 8 * hinfo->cfg.size; + else if (hinfo->cfg.max < hinfo->cfg.size) + hinfo->cfg.max = hinfo->cfg.size; + + for (i = 0; i < hinfo->cfg.size; i++) + INIT_LIST_HEAD(&hinfo->hash[i]); + + atomic_set(&hinfo->count, 0); + atomic_set(&hinfo->use, 1); + hinfo->rnd = 0; + hinfo->lock = SPIN_LOCK_UNLOCKED; + hinfo->pde = create_proc_entry(minfo->name, 0, dstlimit_procdir); + if (!hinfo->pde) { + vfree(hinfo); + return -1; + } + hinfo->pde->proc_fops = &dl_file_ops; + hinfo->pde->data = hinfo; + + init_timer(&hinfo->timer); + hinfo->timer.expires = jiffies + MS2JIFFIES(hinfo->cfg.gc_interval); + hinfo->timer.data = (unsigned long )hinfo; + hinfo->timer.function = htable_gc; + add_timer(&hinfo->timer); + + WRITE_LOCK(&dstlimit_lock); + list_add(&hinfo->list, &dstlimit_htables); + WRITE_UNLOCK(&dstlimit_lock); + + return 0; +} + +static int select_all(struct ipt_dstlimit_htable *ht, struct dsthash_ent *he) +{ + return 1; +} + +static int select_gc(struct ipt_dstlimit_htable *ht, struct dsthash_ent *he) +{ + return (jiffies >= he->expires); +} + +static void htable_selective_cleanup(struct ipt_dstlimit_htable *ht, + int (*select)(struct ipt_dstlimit_htable *ht, + struct dsthash_ent *he)) +{ + int i; + + IP_NF_ASSERT(ht->cfg.size && ht->cfg.max); + + /* lock hash table and iterate over it */ + LOCK_BH(&ht->lock); + for (i = 0; i < ht->cfg.size; i++) { + struct dsthash_ent *dh, *n; + list_for_each_entry_safe(dh, n, &ht->hash[i], list) { + if ((*select)(ht, dh)) + __dsthash_free(ht, dh); + } + } + UNLOCK_BH(&ht->lock); +} + +/* hash table garbage collector, run by timer */ +static void htable_gc(unsigned long htlong) +{ + struct ipt_dstlimit_htable *ht = (struct ipt_dstlimit_htable *)htlong; + + htable_selective_cleanup(ht, select_gc); + + /* re-add the timer accordingly */ + ht->timer.expires = jiffies + MS2JIFFIES(ht->cfg.gc_interval); + add_timer(&ht->timer); +} + +static void htable_destroy(struct ipt_dstlimit_htable *hinfo) +{ + /* remove timer, if it is pending */ + if (timer_pending(&hinfo->timer)) + del_timer(&hinfo->timer); + + /* remove proc entry */ + remove_proc_entry(hinfo->pde->name, dstlimit_procdir); + + htable_selective_cleanup(hinfo, select_all); + vfree(hinfo); +} + +static struct ipt_dstlimit_htable *htable_find_get(char *name) +{ + struct ipt_dstlimit_htable *hinfo; + + READ_LOCK(&dstlimit_lock); + list_for_each_entry(hinfo, &dstlimit_htables, list) { + if (!strcmp(name, hinfo->pde->name)) { + atomic_inc(&hinfo->use); + READ_UNLOCK(&dstlimit_lock); + return hinfo; + } + } + READ_UNLOCK(&dstlimit_lock); + + return NULL; +} + +static void htable_put(struct ipt_dstlimit_htable *hinfo) +{ + if (atomic_dec_and_test(&hinfo->use)) { + WRITE_LOCK(&dstlimit_lock); + list_del(&hinfo->list); + WRITE_UNLOCK(&dstlimit_lock); + htable_destroy(hinfo); + } +} + + +/* The algorithm used is the Simple Token Bucket Filter (TBF) + * see net/sched/sch_tbf.c in the linux source tree + */ + +/* Rusty: This is my (non-mathematically-inclined) understanding of + this algorithm. The `average rate' in jiffies becomes your initial + amount of credit `credit' and the most credit you can ever have + `credit_cap'. The `peak rate' becomes the cost of passing the + test, `cost'. + + `prev' tracks the last packet hit: you gain one credit per jiffy. + If you get credit balance more than this, the extra credit is + discarded. Every time the match passes, you lose `cost' credits; + if you don't have that many, the test fails. + + See Alexey's formal explanation in net/sched/sch_tbf.c. + + To get the maximum range, we multiply by this factor (ie. you get N + credits per jiffy). We want to allow a rate as low as 1 per day + (slowest userspace tool allows), which means + CREDITS_PER_JIFFY*HZ*60*60*24 < 2^32 ie. +*/ +#define MAX_CPJ (0xFFFFFFFF / (HZ*60*60*24)) + +/* Repeated shift and or gives us all 1s, final shift and add 1 gives + * us the power of 2 below the theoretical max, so GCC simply does a + * shift. */ +#define _POW2_BELOW2(x) ((x)|((x)>>1)) +#define _POW2_BELOW4(x) (_POW2_BELOW2(x)|_POW2_BELOW2((x)>>2)) +#define _POW2_BELOW8(x) (_POW2_BELOW4(x)|_POW2_BELOW4((x)>>4)) +#define _POW2_BELOW16(x) (_POW2_BELOW8(x)|_POW2_BELOW8((x)>>8)) +#define _POW2_BELOW32(x) (_POW2_BELOW16(x)|_POW2_BELOW16((x)>>16)) +#define POW2_BELOW32(x) ((_POW2_BELOW32(x)>>1) + 1) + +#define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) + +/* Precision saver. */ +static inline u_int32_t +user2credits(u_int32_t user) +{ + /* If multiplying would overflow... */ + if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY)) + /* Divide first. */ + return (user / IPT_DSTLIMIT_SCALE) * HZ * CREDITS_PER_JIFFY; + + return (user * HZ * CREDITS_PER_JIFFY) / IPT_DSTLIMIT_SCALE; +} + +static inline void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now) +{ + dh->rateinfo.credit += (now - xchg(&dh->rateinfo.prev, now)) + * CREDITS_PER_JIFFY; + if (dh->rateinfo.credit > dh->rateinfo.credit_cap) + dh->rateinfo.credit = dh->rateinfo.credit_cap; +} + +static int +dstlimit_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) +{ + struct ipt_dstlimit_info *r = + ((struct ipt_dstlimit_info *)matchinfo)->u.master; + struct ipt_dstlimit_htable *hinfo = r->hinfo; + unsigned long now = jiffies; + struct dsthash_ent *dh; + struct dsthash_dst dst; + + memset(&dst, 0, sizeof(dst)); + + /* dest ip is always in hash */ + dst.dst_ip = skb->nh.iph->daddr; + + /* source ip only if respective hashmode, otherwise set to + * zero */ + if (hinfo->cfg.mode & IPT_DSTLIMIT_HASH_SIP) + dst.src_ip = skb->nh.iph->saddr; + + /* dest port only if respective mode */ + if (hinfo->cfg.mode & IPT_DSTLIMIT_HASH_DPT) { + switch (skb->nh.iph->protocol) { + struct tcphdr *th; + struct udphdr *uh; + case IPPROTO_TCP: + th = (void *)skb->nh.iph+skb->nh.iph->ihl*4; + dst.port = th->dest; + break; + case IPPROTO_UDP: + uh = (void *)skb->nh.iph+skb->nh.iph->ihl*4; + dst.port = uh->dest; + break; + default: + break; + } + } + + LOCK_BH(&hinfo->lock); + dh = __dsthash_find(hinfo, &dst); + if (!dh) { + dh = __dsthash_alloc_init(hinfo, &dst); + + if (!dh) { + /* enomem... don't match == DROP */ + if (net_ratelimit()) + printk(KERN_ERR "%s: ENOMEM\n", __FUNCTION__); + UNLOCK_BH(&hinfo->lock); + return 0; + } + + dh->expires = jiffies + MS2JIFFIES(hinfo->cfg.expire); + + dh->rateinfo.prev = jiffies; + dh->rateinfo.credit = user2credits(hinfo->cfg.avg * + hinfo->cfg.burst); + dh->rateinfo.credit_cap = user2credits(hinfo->cfg.avg * + hinfo->cfg.burst); + dh->rateinfo.cost = user2credits(hinfo->cfg.avg); + + UNLOCK_BH(&hinfo->lock); + return 0; + } + + /* update expiration timeout */ + dh->expires = now + MS2JIFFIES(hinfo->cfg.expire); + + rateinfo_recalc(dh, now); + if (dh->rateinfo.credit >= dh->rateinfo.cost) { + /* We're underlimit. */ + dh->rateinfo.credit -= dh->rateinfo.cost; + UNLOCK_BH(&hinfo->lock); + return 1; + } + + UNLOCK_BH(&hinfo->lock); + + /* default case: we're overlimit, thus don't match */ + return 0; +} + +static int +dstlimit_checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_dstlimit_info *r = matchinfo; + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_dstlimit_info))) + return 0; + + /* Check for overflow. */ + if (r->cfg.burst == 0 + || user2credits(r->cfg.avg * r->cfg.burst) < + user2credits(r->cfg.avg)) { + printk(KERN_ERR "ipt_dstlimit: Overflow, try lower: %u/%u\n", + r->cfg.avg, r->cfg.burst); + return 0; + } + + if (r->cfg.mode == 0 + || r->cfg.mode > (IPT_DSTLIMIT_HASH_DPT + |IPT_DSTLIMIT_HASH_DIP + |IPT_DSTLIMIT_HASH_SIP)) + return 0; + + if (!r->cfg.gc_interval) + return 0; + + if (!r->cfg.expire) + return 0; + + r->hinfo = htable_find_get(r->name); + if (!r->hinfo && (htable_create(r) != 0)) { + return 0; + } + + /* Ugly hack: For SMP, we only want to use one set */ + r->u.master = r; + + return 1; +} + +static void +dstlimit_destroy(void *matchinfo, unsigned int matchsize) +{ + struct ipt_dstlimit_info *r = (struct ipt_dstlimit_info *) matchinfo; + + htable_put(r->hinfo); +} + +static struct ipt_match ipt_dstlimit = { + .list = { .prev = NULL, .next = NULL }, + .name = "dstlimit", + .match = dstlimit_match, + .checkentry = dstlimit_checkentry, + .destroy = dstlimit_destroy, + .me = THIS_MODULE +}; + +/* PROC stuff */ + +static void *dl_seq_start(struct seq_file *s, loff_t *pos) +{ + struct proc_dir_entry *pde = s->private; + struct ipt_dstlimit_htable *htable = pde->data; + unsigned int *bucket; + + LOCK_BH(&htable->lock); + if (*pos >= htable->cfg.size) + return NULL; + + bucket = kmalloc(sizeof(unsigned int), GFP_KERNEL); + if (!bucket) + return ERR_PTR(-ENOMEM); + + *bucket = *pos; + return bucket; +} + +static void *dl_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct proc_dir_entry *pde = s->private; + struct ipt_dstlimit_htable *htable = pde->data; + unsigned int *bucket = (unsigned int *)v; + + *pos = ++(*bucket); + if (*pos >= htable->cfg.size) { + kfree(v); + return NULL; + } + return bucket; +} + +static void dl_seq_stop(struct seq_file *s, void *v) +{ + struct proc_dir_entry *pde = s->private; + struct ipt_dstlimit_htable *htable = pde->data; + unsigned int *bucket = (unsigned int *)v; + + kfree(bucket); + + UNLOCK_BH(&htable->lock); +} + +static inline int dl_seq_real_show(struct dsthash_ent *ent, struct seq_file *s) +{ + /* recalculate to show accurate numbers */ + rateinfo_recalc(ent, jiffies); + + return seq_printf(s, "%ld %u.%u.%u.%u->%u.%u.%u.%u:%u %u %u %u\n", + (ent->expires - jiffies)/HZ, + NIPQUAD(ent->dst.src_ip), + NIPQUAD(ent->dst.dst_ip), ntohs(ent->dst.port), + ent->rateinfo.credit, ent->rateinfo.credit_cap, + ent->rateinfo.cost); +} + +static int dl_seq_show(struct seq_file *s, void *v) +{ + struct proc_dir_entry *pde = s->private; + struct ipt_dstlimit_htable *htable = pde->data; + unsigned int *bucket = (unsigned int *)v; + + if (LIST_FIND_W(&htable->hash[*bucket], dl_seq_real_show, + struct dsthash_ent *, s)) { + /* buffer was filled and unable to print that tuple */ + return 1; + } + return 0; +} + +static struct seq_operations dl_seq_ops = { + .start = dl_seq_start, + .next = dl_seq_next, + .stop = dl_seq_stop, + .show = dl_seq_show +}; + +static int dl_proc_open(struct inode *inode, struct file *file) +{ + int ret = seq_open(file, &dl_seq_ops); + + if (!ret) { + struct seq_file *sf = file->private_data; + sf->private = PDE(inode); + } + return ret; +} + +static struct file_operations dl_file_ops = { + .owner = THIS_MODULE, + .open = dl_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release +}; + +static int init_or_fini(int fini) +{ + int ret = 0; + + if (fini) + goto cleanup; + + if (ipt_register_match(&ipt_dstlimit)) { + ret = -EINVAL; + goto cleanup_nothing; + } + + /* FIXME: do we really want HWCACHE_ALIGN since our objects are + * quite small ? */ + dstlimit_cachep = kmem_cache_create("ipt_dstlimit", + sizeof(struct dsthash_ent), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!dstlimit_cachep) { + printk(KERN_ERR "Unable to create ipt_dstlimit slab cache\n"); + ret = -ENOMEM; + goto cleanup_unreg_match; + } + + dstlimit_procdir = proc_mkdir("ipt_dstlimit", proc_net); + if (!dstlimit_procdir) { + printk(KERN_ERR "Unable to create proc dir entry\n"); + ret = -ENOMEM; + goto cleanup_free_slab; + } + + return ret; + +cleanup: + remove_proc_entry("ipt_dstlimit", proc_net); +cleanup_free_slab: + kmem_cache_destroy(dstlimit_cachep); +cleanup_unreg_match: + ipt_unregister_match(&ipt_dstlimit); +cleanup_nothing: + return ret; + +} + +static int __init init(void) +{ + return init_or_fini(0); +} + +static void __exit fini(void) +{ + init_or_fini(1); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_fuzzy.c linux-2.6.3/net/ipv4/netfilter/ipt_fuzzy.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_fuzzy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_fuzzy.c 2004-02-19 08:47:27.855666503 +0100 @@ -0,0 +1,190 @@ +/* + * This module implements a simple TSK FLC + * (Takagi-Sugeno-Kang Fuzzy Logic Controller) that aims + * to limit , in an adaptive and flexible way , the packet rate crossing + * a given stream . It serves as an initial and very simple (but effective) + * example of how Fuzzy Logic techniques can be applied to defeat DoS attacks. + * As a matter of fact , Fuzzy Logic can help us to insert any "behavior" + * into our code in a precise , adaptive and efficient manner. + * The goal is very similar to that of "limit" match , but using techniques of + * Fuzzy Control , that allow us to shape the transfer functions precisely , + * avoiding over and undershoots - and stuff like that . + * + * + * 2002-08-10 Hime Aguiar e Oliveira Jr. : Initial version. + * 2002-08-17 : Changed to eliminate floating point operations . + * 2002-08-23 : Coding style changes . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + Packet Acceptance Rate - LOW and Packet Acceptance Rate - HIGH + Expressed in percentage +*/ + +#define PAR_LOW 1/100 +#define PAR_HIGH 1 + +static spinlock_t fuzzy_lock = SPIN_LOCK_UNLOCKED ; + +MODULE_AUTHOR("Hime Aguiar e Oliveira Junior "); +MODULE_DESCRIPTION("IP tables Fuzzy Logic Controller match module"); +MODULE_LICENSE("GPL"); + +static u_int8_t mf_high(u_int32_t tx,u_int32_t mini,u_int32_t maxi) +{ + if (tx >= maxi) return 100; + + if (tx <= mini) return 0; + + return ( (100*(tx-mini)) / (maxi-mini) ) ; +} + +static u_int8_t mf_low(u_int32_t tx,u_int32_t mini,u_int32_t maxi) +{ + if (tx <= mini) return 100; + + if (tx >= maxi) return 0; + + return ( (100*( maxi - tx )) / ( maxi - mini ) ) ; + +} + +static int +ipt_fuzzy_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + /* From userspace */ + + struct ipt_fuzzy_info *info = (struct ipt_fuzzy_info *) matchinfo; + + u_int8_t random_number; + unsigned long amount ; + u_int8_t howhigh , howlow ; + + + spin_lock_bh(&fuzzy_lock) ; /* Rise the lock */ + + info->bytes_total += pskb->len ; + info->packets_total++ ; + + info->present_time = jiffies ; + + if ( info->present_time >= info->previous_time ) + amount = info->present_time - info->previous_time ; + else { + /* There was a transition : I choose to re-sample + and keep the old acceptance rate... + */ + + amount = 0 ; + info->previous_time = info->present_time ; + info->bytes_total = info->packets_total = 0; + }; + + if ( amount > HZ/10 ) /* More than 100 ms elapsed ... */ + { + + info->mean_rate = (u_int32_t) ( ( HZ * info->packets_total ) \ + / amount ) ; + + info->previous_time = info->present_time ; + info->bytes_total = info->packets_total = 0 ; + + howhigh = mf_high(info->mean_rate,info->minimum_rate,info->maximum_rate); + howlow = mf_low(info->mean_rate,info->minimum_rate,info->maximum_rate); + + info->acceptance_rate = (u_int8_t) \ + ( howhigh*PAR_LOW + PAR_HIGH*howlow ) ; + + /* In fact , the above defuzzification would require a denominator + proportional to (howhigh+howlow) but , in this particular case , + that expression is constant . + An imediate consequence is that it isn't necessary to call + both mf_high and mf_low - but to keep things understandable , + I did so . + */ + + } + + spin_unlock_bh(&fuzzy_lock) ; /* Release the lock */ + + + if ( info->acceptance_rate < 100 ) + { + get_random_bytes((void *)(&random_number), 1); + + /* If within the acceptance , it can pass => don't match */ + if ( random_number <= (255 * info->acceptance_rate) / 100 ) + return 0 ; + else + return 1; /* It can't pass ( It matches ) */ + } ; + + return 0; /* acceptance_rate == 100 % => Everything passes ... */ + +} + +static int +ipt_fuzzy_checkentry(const char *tablename, + const struct ipt_ip *e, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + + const struct ipt_fuzzy_info *info = matchinfo; + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_fuzzy_info))) { + printk("ipt_fuzzy: matchsize %u != %u\n", matchsize, + IPT_ALIGN(sizeof(struct ipt_fuzzy_info))); + return 0; + } + +if ((info->minimum_rate < MINFUZZYRATE ) || (info->maximum_rate > MAXFUZZYRATE) + || (info->minimum_rate >= info->maximum_rate )) + { + printk("ipt_fuzzy: BAD limits , please verify !!!\n"); + return 0; + } + + return 1; +} + +static struct ipt_match ipt_fuzzy_reg = { + {NULL, NULL}, + "fuzzy", + ipt_fuzzy_match, + ipt_fuzzy_checkentry, + NULL, + THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_match(&ipt_fuzzy_reg)) + return -EINVAL; + + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipt_fuzzy_reg); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_ipv4options.c linux-2.6.3/net/ipv4/netfilter/ipt_ipv4options.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_ipv4options.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_ipv4options.c 2004-02-19 08:47:28.441525384 +0100 @@ -0,0 +1,170 @@ +/* + This is a module which is used to match ipv4 options. + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: + ftp://prep.ai.mit.edu/pub/gnu/GPL + + 11-mars-2001 Fabrice MARIE : initial development. + 12-july-2001 Fabrice MARIE : added router-alert otions matching. Fixed a bug with no-srr + 12-august-2001 Imran Patel : optimization of the match. + 18-november-2001 Fabrice MARIE : added [!] 'any' option match. +*/ + +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +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 ipt_ipv4options_info *info = matchinfo; /* match info for rule */ + const struct iphdr *iph = skb->nh.iph; + const struct ip_options *opt; + + if (iph->ihl * 4 == sizeof(struct iphdr)) { + /* No options, so we match only the "DONTs" and the "IGNOREs" */ + + if (((info->options & IPT_IPV4OPTION_MATCH_ANY_OPT) == IPT_IPV4OPTION_MATCH_ANY_OPT) || + ((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) || + ((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR) || + ((info->options & IPT_IPV4OPTION_MATCH_RR) == IPT_IPV4OPTION_MATCH_RR) || + ((info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP) == IPT_IPV4OPTION_MATCH_TIMESTAMP) || + ((info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_MATCH_ROUTER_ALERT)) + return 0; + return 1; + } + else { + if ((info->options & IPT_IPV4OPTION_MATCH_ANY_OPT) == IPT_IPV4OPTION_MATCH_ANY_OPT) + /* there are options, and we don't need to care which one */ + return 1; + else { + if ((info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) == IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) + /* there are options but we don't want any ! */ + return 0; + } + } + + opt = &(IPCB(skb)->opt); + + /* source routing */ + if ((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) { + if (!((opt->srr) & (opt->is_strictroute))) + return 0; + } + else if ((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR) { + if (!((opt->srr) & (!opt->is_strictroute))) + return 0; + } + else if ((info->options & IPT_IPV4OPTION_DONT_MATCH_SRR) == IPT_IPV4OPTION_DONT_MATCH_SRR) { + if (opt->srr) + return 0; + } + /* record route */ + if ((info->options & IPT_IPV4OPTION_MATCH_RR) == IPT_IPV4OPTION_MATCH_RR) { + if (!opt->rr) + return 0; + } + else if ((info->options & IPT_IPV4OPTION_DONT_MATCH_RR) == IPT_IPV4OPTION_DONT_MATCH_RR) { + if (opt->rr) + return 0; + } + /* timestamp */ + if ((info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP) == IPT_IPV4OPTION_MATCH_TIMESTAMP) { + if (!opt->ts) + return 0; + } + else if ((info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) == IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) { + if (opt->ts) + return 0; + } + /* router-alert option */ + if ((info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_MATCH_ROUTER_ALERT) { + if (!opt->router_alert) + return 0; + } + else if ((info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) { + if (opt->router_alert) + return 0; + } + + /* we match ! */ + return 1; +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + const struct ipt_ipv4options_info *info = matchinfo; /* match info for rule */ + /* Check the size */ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_ipv4options_info))) + return 0; + /* Now check the coherence of the data ... */ + if (((info->options & IPT_IPV4OPTION_MATCH_ANY_OPT) == IPT_IPV4OPTION_MATCH_ANY_OPT) && + (((info->options & IPT_IPV4OPTION_DONT_MATCH_SRR) == IPT_IPV4OPTION_DONT_MATCH_SRR) || + ((info->options & IPT_IPV4OPTION_DONT_MATCH_RR) == IPT_IPV4OPTION_DONT_MATCH_RR) || + ((info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) == IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) || + ((info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) || + ((info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) == IPT_IPV4OPTION_DONT_MATCH_ANY_OPT))) + return 0; /* opposites */ + if (((info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) == IPT_IPV4OPTION_DONT_MATCH_ANY_OPT) && + (((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR) || + ((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) || + ((info->options & IPT_IPV4OPTION_MATCH_RR) == IPT_IPV4OPTION_MATCH_RR) || + ((info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP) == IPT_IPV4OPTION_MATCH_TIMESTAMP) || + ((info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_MATCH_ROUTER_ALERT) || + ((info->options & IPT_IPV4OPTION_MATCH_ANY_OPT) == IPT_IPV4OPTION_MATCH_ANY_OPT))) + return 0; /* opposites */ + if (((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) && + ((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR)) + return 0; /* cannot match in the same time loose and strict source routing */ + if ((((info->options & IPT_IPV4OPTION_MATCH_SSRR) == IPT_IPV4OPTION_MATCH_SSRR) || + ((info->options & IPT_IPV4OPTION_MATCH_LSRR) == IPT_IPV4OPTION_MATCH_LSRR)) && + ((info->options & IPT_IPV4OPTION_DONT_MATCH_SRR) == IPT_IPV4OPTION_DONT_MATCH_SRR)) + return 0; /* opposites */ + if (((info->options & IPT_IPV4OPTION_MATCH_RR) == IPT_IPV4OPTION_MATCH_RR) && + ((info->options & IPT_IPV4OPTION_DONT_MATCH_RR) == IPT_IPV4OPTION_DONT_MATCH_RR)) + return 0; /* opposites */ + if (((info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP) == IPT_IPV4OPTION_MATCH_TIMESTAMP) && + ((info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) == IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP)) + return 0; /* opposites */ + if (((info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_MATCH_ROUTER_ALERT) && + ((info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT) == IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT)) + return 0; /* opposites */ + + /* everything looks ok. */ + return 1; +} + +static struct ipt_match ipv4options_match += { { NULL, NULL }, "ipv4options", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + printk("ipt_ipv4options loading\n"); + return ipt_register_match(&ipv4options_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipv4options_match); + printk("ipt_ipv4options unloaded\n"); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_mport.c linux-2.6.3/net/ipv4/netfilter/ipt_mport.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_mport.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_mport.c 2004-02-19 08:47:29.018386433 +0100 @@ -0,0 +1,112 @@ +/* Kernel module to match one of a list of TCP/UDP ports: ports are in + the same place so we can treat them as equal. */ +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +#if 0 +#define duprintf(format, args...) printk(format , ## args) +#else +#define duprintf(format, args...) +#endif + +/* Returns 1 if the port is matched by the test, 0 otherwise. */ +static inline int +ports_match(const struct ipt_mport *minfo, u_int16_t src, u_int16_t dst) +{ + unsigned int i; + unsigned int m; + u_int16_t pflags = minfo->pflags; + for (i=0, m=1; iports[i] == 65535) + return 0; + + s = minfo->ports[i]; + + if (pflags & m) { + e = minfo->ports[++i]; + m <<= 1; + } else + e = s; + + if (minfo->flags & IPT_MPORT_SOURCE + && src >= s && src <= e) + return 1; + + if (minfo->flags & IPT_MPORT_DESTINATION + && dst >= s && dst <= e) + return 1; + } + + return 0; +} + +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 udphdr *udp = hdr; + const struct ipt_mport *minfo = matchinfo; + + /* Must be big enough to read ports. */ + if (offset == 0 && datalen < sizeof(struct udphdr)) { + /* We've been asked to examine this packet, and we + can't. Hence, no choice but to drop. */ + duprintf("ipt_mport:" + " Dropping evil offset=0 tinygram.\n"); + *hotdrop = 1; + return 0; + } + + /* Must not be a fragment. */ + return !offset + && ports_match(minfo, ntohs(udp->source), ntohs(udp->dest)); +} + +/* Called when user tries to insert an entry of this type. */ +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_mport))) + return 0; + + /* Must specify proto == TCP/UDP, no unknown flags or bad count */ + return (ip->proto == IPPROTO_TCP || ip->proto == IPPROTO_UDP) + && !(ip->invflags & IPT_INV_PROTO) + && matchsize == IPT_ALIGN(sizeof(struct ipt_mport)); +} + +static struct ipt_match mport_match += { { NULL, NULL }, "mport", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&mport_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&mport_match); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_nth.c linux-2.6.3/net/ipv4/netfilter/ipt_nth.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_nth.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_nth.c 2004-02-19 08:47:29.579251335 +0100 @@ -0,0 +1,172 @@ +/* + This is a module which is used for match support for every Nth packet + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: + ftp://prep.ai.mit.edu/pub/gnu/GPL + + 2001-07-18 Fabrice MARIE : initial implementation. + 2001-09-20 Richard Wagner (rwagner@cloudnet.com) + * added support for multiple counters + * added support for matching on individual packets + in the counter cycle + +*/ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +/* + * State information. + */ +struct state { + spinlock_t lock; + u_int16_t number; +}; + +static struct state states[IPT_NTH_NUM_COUNTERS]; + +static int +ipt_nth_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + /* Parameters from userspace */ + const struct ipt_nth_info *info = matchinfo; + unsigned counter = info->counter; + if((counter < 0) || (counter >= IPT_NTH_NUM_COUNTERS)) + { + printk(KERN_WARNING "nth: invalid counter %u. counter between 0 and %u\n", counter, IPT_NTH_NUM_COUNTERS-1); + return 0; + }; + + spin_lock(&states[counter].lock); + + /* Are we matching every nth packet?*/ + if (info->packet == 0xFF) + { + /* We're matching every nth packet and only every nth packet*/ + /* Do we match or invert match? */ + if (info->not == 0) + { + if (states[counter].number == 0) + { + ++states[counter].number; + goto match; + } + if (states[counter].number >= info->every) + states[counter].number = 0; /* reset the counter */ + else + ++states[counter].number; + goto dontmatch; + } + else + { + if (states[counter].number == 0) + { + ++states[counter].number; + goto dontmatch; + } + if (states[counter].number >= info->every) + states[counter].number = 0; + else + ++states[counter].number; + goto match; + } + } + else + { + /* We're using the --packet, so there must be a rule for every value */ + if (states[counter].number == info->packet) + { + /* only increment the counter when a match happens */ + if (states[counter].number >= info->every) + states[counter].number = 0; /* reset the counter */ + else + ++states[counter].number; + goto match; + } + else + goto dontmatch; + } + + dontmatch: + /* don't match */ + spin_unlock(&states[counter].lock); + return 0; + + match: + spin_unlock(&states[counter].lock); + return 1; +} + +static int +ipt_nth_checkentry(const char *tablename, + const struct ipt_ip *e, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + /* Parameters from userspace */ + const struct ipt_nth_info *info = matchinfo; + unsigned counter = info->counter; + if((counter < 0) || (counter >= IPT_NTH_NUM_COUNTERS)) + { + printk(KERN_WARNING "nth: invalid counter %u. counter between 0 and %u\n", counter, IPT_NTH_NUM_COUNTERS-1); + return 0; + }; + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_nth_info))) { + printk("nth: matchsize %u != %u\n", matchsize, + IPT_ALIGN(sizeof(struct ipt_nth_info))); + return 0; + } + + states[counter].number = info->startat; + + return 1; +} + +static struct ipt_match ipt_nth_reg = { + {NULL, NULL}, + "nth", + ipt_nth_match, + ipt_nth_checkentry, + NULL, + THIS_MODULE }; + +static int __init init(void) +{ + unsigned counter; + memset(&states, 0, sizeof(states)); + if (ipt_register_match(&ipt_nth_reg)) + return -EINVAL; + + for(counter = 0; counter < IPT_NTH_NUM_COUNTERS; counter++) + { + spin_lock_init(&(states[counter].lock)); + }; + + printk("ipt_nth match loaded\n"); + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipt_nth_reg); + printk("ipt_nth match unloaded\n"); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_osf.c linux-2.6.3/net/ipv4/netfilter/ipt_osf.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_osf.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_osf.c 2004-02-19 08:47:30.115122257 +0100 @@ -0,0 +1,733 @@ +/* + * ipt_osf.c + * + * Copyright (c) 2003 Evgeniy Polyakov + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * OS fingerprint matching module. + * It simply compares various parameters from SYN packet with + * some hardcoded ones. + * + * Original table was created by Michal Zalewski + * for his p0f. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#define OSF_DEBUG + +#ifdef OSF_DEBUG +#define log(x...) printk(KERN_INFO "ipt_osf: " x) +#define loga(x...) printk(x) +#else +#define log(x...) do {} while(0) +#define loga(x...) do {} while(0) +#endif + +#define FMATCH_WRONG 0 +#define FMATCH_OK 1 +#define FMATCH_OPT_WRONG 2 + +#define OPTDEL ',' +#define OSFPDEL ':' +#define MAXOPTSTRLEN 128 +#define OSFFLUSH "FLUSH" + +static rwlock_t osf_lock = RW_LOCK_UNLOCKED; +static struct list_head finger_list; + +static int match(const struct sk_buff *, const struct net_device *, const struct net_device *, + const void *, int, const void *, u_int16_t, int *); +static int checkentry(const char *, const struct ipt_ip *, void *, + unsigned int, unsigned int); + +static struct ipt_match osf_match = +{ + { NULL, NULL }, + "osf", + &match, + &checkentry, + NULL, + THIS_MODULE +}; + +static inline int smart_dec(unsigned long flags, unsigned char ip_ttl, unsigned char f_ttl) +{ + if (flags & IPT_OSF_SMART) + return 1; + else + return (ip_ttl == f_ttl); +} + +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) +{ + struct ipt_osf_info *info = (struct ipt_osf_info *)matchinfo; + struct iphdr *ip = skb->nh.iph; + struct tcphdr *tcp; + int fmatch = FMATCH_WRONG, fcount = 0; + unsigned long totlen, optsize = 0, window; + unsigned char df, *optp = NULL, *_optp = NULL; + char check_WSS = 0; + struct list_head *ent; + struct osf_finger *f; + + if (!ip || !info) + return 0; + + tcp = (struct tcphdr *)((u_int32_t *)ip + ip->ihl); + + if (!tcp->syn) + return 0; + else if (tcp->ack) + return 0; + + totlen = ntohs(ip->tot_len); + df = ((ntohs(ip->frag_off) & IP_DF)?1:0); + window = ntohs(tcp->window); + + if (tcp->doff*4 > sizeof(struct tcphdr)) + { + _optp = optp = (char *)(tcp+1); + optsize = tcp->doff*4 - sizeof(struct tcphdr); + } + + + /* Actually we can create hash/table of all genres and search + * only in appropriate part, but here is initial variant, + * so will use slow path. + */ + read_lock(&osf_lock); + list_for_each(ent, &finger_list) + { + f = list_entry(ent, struct osf_finger, flist); + + if (!(info->flags & IPT_OSF_LOG) && strcmp(info->genre, f->genre)) + continue; + + optp = _optp; + fmatch = FMATCH_WRONG; + + if (totlen == f->ss && df == f->df && + smart_dec(info->flags, ip->ttl, f->ttl)) + { + unsigned long foptsize; + int optnum; + unsigned short mss = 0; + + check_WSS = 0; + + switch (f->wss.wc) + { + case 0: check_WSS = 0; break; + case 'S': check_WSS = 1; break; + case 'T': check_WSS = 2; break; + case '%': check_WSS = 3; break; + default: log("Wrong fingerprint wss.wc=%d, %s - %s\n", + f->wss.wc, f->genre, f->details); + check_WSS = 4; + break; + } + if (check_WSS == 4) + continue; + + /* Check options */ + + foptsize = 0; + for (optnum=0; optnumopt_num; ++optnum) + foptsize += f->opt[optnum].length; + + + if (foptsize > MAX_IPOPTLEN || optsize > MAX_IPOPTLEN || optsize != foptsize) + continue; + + if (!optp) + { + fmatch = FMATCH_OK; + loga("\tYEP : matching without options.\n"); + if ((info->flags & IPT_OSF_LOG) && + info->loglevel == IPT_OSF_LOGLEVEL_FIRST) + break; + else + continue; + } + + + for (optnum=0; optnumopt_num; ++optnum) + { + if (f->opt[optnum].kind == (*optp)) + { + unsigned char len = f->opt[optnum].length; + unsigned char *optend = optp + len; + + fmatch = FMATCH_OK; + + if (*optp == OSFOPT_MSS) /* MSS */ + mss = ntohs(*(unsigned short *)(optp+2)); + + if (len != 1) + { + /* Skip kind and length fields*/ + optp += 2; + + if (f->opt[optnum].wc.wc != 0) + { + unsigned long tmp = 0; + + /* Hmmm... It looks a bit ugly. :) */ + memcpy(&tmp, &f->opt[optnum].wc.val, + (len > sizeof(unsigned long)? + sizeof(unsigned long):len)); + + tmp = ntohl(tmp); + if (tmp != f->opt[optnum].wc.val) + fmatch = FMATCH_OPT_WRONG; + } + } + + optp = optend; + } + else + fmatch = FMATCH_OPT_WRONG; + + if (fmatch != FMATCH_OK) + break; + } + + if (fmatch != FMATCH_OPT_WRONG) + { + fmatch = FMATCH_WRONG; + + switch (check_WSS) + { + case 0: + if (window == f->wss.val) + fmatch = FMATCH_OK; + break; + case 1: /* MSS */ + if (window == f->wss.val*mss) + fmatch = FMATCH_OK; + break; + case 2: /* MTU */ + if (window == f->wss.val*(mss+40)) + fmatch = FMATCH_OK; + break; + case 3: /* MOD */ + if (window % f->wss.val == 0) + fmatch = FMATCH_OK; + break; + } + } + + + if (fmatch == FMATCH_OK) + { + fcount++; + log("%s [%s]: %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n", + f->genre, f->details, + NIPQUAD(ip->saddr), ntohs(tcp->source), + NIPQUAD(ip->daddr), ntohs(tcp->dest)); + if ((info->flags & IPT_OSF_LOG) && + info->loglevel == IPT_OSF_LOGLEVEL_FIRST) + break; + } + } + } + if (!fcount && (info->flags & IPT_OSF_LOG)) + { + log("Unknown: %lu:%d:%d:%lu:", window, ip->ttl, df, totlen); + if (optp) + { + unsigned char opt[4 * 15 - sizeof(struct tcphdr)]; + unsigned int i, optsize; + + optsize = tcp->doff * 4 - sizeof(struct tcphdr); + if (skb_copy_bits(skb, ip->ihl*4 + sizeof(struct tcphdr), + opt, optsize) < 0) + loga("TRUNCATED"); + else + for (i = 0; i < optsize; i++) + loga("%02X", opt[i]); + + } + loga(" %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n", + NIPQUAD(ip->saddr), ntohs(tcp->source), + NIPQUAD(ip->daddr), ntohs(tcp->dest)); + } + read_unlock(&osf_lock); + + return (fmatch == FMATCH_OK)?1:0; +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_osf_info))) + return 0; + if (ip->proto != IPPROTO_TCP) + return 0; + + return 1; +} + +static char * osf_strchr(char *ptr, char c) +{ + char *tmp; + + tmp = strchr(ptr, c); + + while (tmp && tmp+1 && isspace(*(tmp+1))) + tmp++; + + return tmp; +} + +static struct osf_finger * finger_alloc(void) +{ + struct osf_finger *f; + + f = kmalloc(sizeof(struct osf_finger), GFP_KERNEL); + if (f) + memset(f, 0, sizeof(struct osf_finger)); + + return f; +} + +static void finger_free(struct osf_finger *f) +{ + memset(f, 0, sizeof(struct osf_finger)); + kfree(f); +} + + +static void osf_parse_opt(struct osf_opt *opt, int *optnum, char *obuf, int olen) +{ + int i, op; + char *ptr, wc; + unsigned long val; + + ptr = &obuf[0]; + i = 0; + while (ptr != NULL && i < olen) + { + val = 0; + op = 0; + wc = 0; + switch (obuf[i]) + { + case 'N': + op = OSFOPT_NOP; + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + *ptr = '\0'; + ptr++; + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + case 'S': + op = OSFOPT_SACKP; + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + *ptr = '\0'; + ptr++; + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + case 'T': + op = OSFOPT_TS; + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + *ptr = '\0'; + ptr++; + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + case 'W': + op = OSFOPT_WSO; + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + switch (obuf[i+1]) + { + case '%': wc = '%'; break; + case 'S': wc = 'S'; break; + case 'T': wc = 'T'; break; + default: wc = 0; break; + } + + *ptr = '\0'; + ptr++; + if (wc) + val = simple_strtoul(&obuf[i+2], NULL, 10); + else + val = simple_strtoul(&obuf[i+1], NULL, 10); + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + case 'M': + op = OSFOPT_MSS; + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + if (obuf[i+1] == '%') + wc = '%'; + *ptr = '\0'; + ptr++; + if (wc) + val = simple_strtoul(&obuf[i+2], NULL, 10); + else + val = simple_strtoul(&obuf[i+1], NULL, 10); + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + case 'E': + op = OSFOPT_EOL; + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + *ptr = '\0'; + ptr++; + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + default: + ptr = osf_strchr(&obuf[i], OPTDEL); + if (ptr) + { + ptr++; + i += (int)(ptr-&obuf[i]); + + } + else + i++; + break; + } + + opt[*optnum].kind = IANA_opts[op].kind; + opt[*optnum].length = IANA_opts[op].length; + opt[*optnum].wc.wc = wc; + opt[*optnum].wc.val = val; + + (*optnum)++; + } +} + +static int osf_proc_read(char *buf, char **start, off_t off, int count, int *eof, void *data) +{ + struct list_head *ent; + struct osf_finger *f = NULL; + int i; + + *eof = 1; + count = 0; + + read_lock_bh(&osf_lock); + list_for_each(ent, &finger_list) + { + f = list_entry(ent, struct osf_finger, flist); + + log("%s [%s]", f->genre, f->details); + + count += sprintf(buf+count, "%s - %s[%s] : %s", + f->genre, f->version, + f->subtype, f->details); + + if (f->opt_num) + { + loga(" OPT: "); + //count += sprintf(buf+count, " OPT: "); + for (i=0; iopt_num; ++i) + { + //count += sprintf(buf+count, "%d.%c%lu; ", + // f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', f->opt[i].wc.val); + loga("%d.%c%lu; ", + f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', f->opt[i].wc.val); + } + } + loga("\n"); + count += sprintf(buf+count, "\n"); + } + read_unlock_bh(&osf_lock); + + return count; +} + +static int osf_proc_write(struct file *file, const char *buffer, unsigned long count, void *data) +{ + int cnt; + unsigned long i; + char obuf[MAXOPTSTRLEN]; + struct osf_finger *finger; + struct list_head *ent, *n; + + char *pbeg, *pend; + + if (count == strlen(OSFFLUSH) && !strncmp(buffer, OSFFLUSH, strlen(OSFFLUSH))) + { + int i = 0; + write_lock_bh(&osf_lock); + list_for_each_safe(ent, n, &finger_list) + { + i++; + finger = list_entry(ent, struct osf_finger, flist); + list_del(&finger->flist); + finger_free(finger); + } + write_unlock_bh(&osf_lock); + + log("Flushed %d entries.\n", i); + + return count; + } + + + cnt = 0; + for (i=0; iwss.wc = 'S'; + if (pbeg[1] == '%') + finger->wss.val = simple_strtoul(pbeg+2, NULL, 10); + else if (pbeg[1] == '*') + finger->wss.val = 0; + else + finger->wss.val = simple_strtoul(pbeg+1, NULL, 10); + } + else if (pbeg[0] == 'T') + { + finger->wss.wc = 'T'; + if (pbeg[1] == '%') + finger->wss.val = simple_strtoul(pbeg+2, NULL, 10); + else if (pbeg[1] == '*') + finger->wss.val = 0; + else + finger->wss.val = simple_strtoul(pbeg+1, NULL, 10); + } + if (isdigit(pbeg[0])) + { + finger->wss.wc = 0; + finger->wss.val = simple_strtoul(pbeg, NULL, 10); + } + + pbeg = pend+1; + } + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + finger->ttl = simple_strtoul(pbeg, NULL, 10); + pbeg = pend+1; + } + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + finger->df = simple_strtoul(pbeg, NULL, 10); + pbeg = pend+1; + } + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + finger->ss = simple_strtoul(pbeg, NULL, 10); + pbeg = pend+1; + } + + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + cnt = snprintf(obuf, sizeof(obuf), "%s", pbeg); + pbeg = pend+1; + } + + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + if (pbeg[0] == '@' || pbeg[0] == '*') + cnt = snprintf(finger->genre, sizeof(finger->genre), "%s", pbeg+1); + else + cnt = snprintf(finger->genre, sizeof(finger->genre), "%s", pbeg); + pbeg = pend+1; + } + + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + cnt = snprintf(finger->version, sizeof(finger->version), "%s", pbeg); + pbeg = pend+1; + } + + pend = osf_strchr(pbeg, OSFPDEL); + if (pend) + { + *pend = '\0'; + cnt = snprintf(finger->subtype, sizeof(finger->subtype), "%s", pbeg); + pbeg = pend+1; + } + + cnt = snprintf(finger->details, + ((count - (pbeg - buffer)+1) > MAXDETLEN)?MAXDETLEN:(count - (pbeg - buffer)+1), + "%s", pbeg); + + log("%s - %s[%s] : %s\n", + finger->genre, finger->version, + finger->subtype, finger->details); + + osf_parse_opt(finger->opt, &finger->opt_num, obuf, sizeof(obuf)); + + + write_lock_bh(&osf_lock); + list_add_tail(&finger->flist, &finger_list); + write_unlock_bh(&osf_lock); + + return count; +} + +static int __init osf_init(void) +{ + int err; + struct proc_dir_entry *p; + + log("Startng OS fingerprint matching module.\n"); + + INIT_LIST_HEAD(&finger_list); + + err = ipt_register_match(&osf_match); + if (err) + { + log("Failed to register OS fingerprint matching module.\n"); + return -ENXIO; + } + + p = create_proc_entry("sys/net/ipv4/osf", S_IFREG | 0644, NULL); + if (!p) + { + ipt_unregister_match(&osf_match); + return -ENXIO; + } + + p->write_proc = osf_proc_write; + p->read_proc = osf_proc_read; + + return 0; +} + +static void __exit osf_fini(void) +{ + struct list_head *ent, *n; + struct osf_finger *f; + + remove_proc_entry("sys/net/ipv4/osf", NULL); + ipt_unregister_match(&osf_match); + + list_for_each_safe(ent, n, &finger_list) + { + f = list_entry(ent, struct osf_finger, flist); + list_del(&f->flist); + finger_free(f); + } + + log("OS fingerprint matching module finished.\n"); +} + +module_init(osf_init); +module_exit(osf_fini); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Evgeniy Polyakov "); +MODULE_DESCRIPTION("Passive OS fingerprint matching."); + diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_pool.c linux-2.6.3/net/ipv4/netfilter/ipt_pool.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_pool.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_pool.c 2004-02-19 08:47:30.713978008 +0100 @@ -0,0 +1,71 @@ +/* Kernel module to match an IP address pool. */ + +#include +#include +#include + +#include +#include +#include + +static inline int match_pool( + ip_pool_t index, + __u32 addr, + int inv +) { + if (ip_pool_match(index, ntohl(addr))) + inv = !inv; + return inv; +} + +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 ipt_pool_info *info = matchinfo; + const struct iphdr *iph = skb->nh.iph; + + if (info->src != IP_POOL_NONE && !match_pool(info->src, iph->saddr, + info->flags&IPT_POOL_INV_SRC)) + return 0; + + if (info->dst != IP_POOL_NONE && !match_pool(info->dst, iph->daddr, + info->flags&IPT_POOL_INV_DST)) + return 0; + + return 1; +} + +static int checkentry( + const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask +) { + if (matchsize != IPT_ALIGN(sizeof(struct ipt_pool_info))) + return 0; + return 1; +} + +static struct ipt_match pool_match += { { NULL, NULL }, "pool", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&pool_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&pool_match); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_psd.c linux-2.6.3/net/ipv4/netfilter/ipt_psd.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_psd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_psd.c 2004-02-19 08:47:31.249848931 +0100 @@ -0,0 +1,361 @@ +/* + This is a module which is used for PSD (portscan detection) + Derived from scanlogd v2.1 written by Solar Designer + and LOG target module. + + Copyright (C) 2000,2001 astaro AG + + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: + ftp://prep.ai.mit.edu/pub/gnu/GPL + + 2000-05-04 Markus Hennig : initial + 2000-08-18 Dennis Koslowski : first release + 2000-12-01 Dennis Koslowski : UDP scans detection added + 2001-01-02 Dennis Koslowski : output modified + 2001-02-04 Jan Rekorajski : converted from target to match +*/ + +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Dennis Koslowski "); + +#define HF_DADDR_CHANGING 0x01 +#define HF_SPORT_CHANGING 0x02 +#define HF_TOS_CHANGING 0x04 +#define HF_TTL_CHANGING 0x08 + +/* + * Information we keep per each target port + */ +struct port { + u_int16_t number; /* port number */ + u_int8_t proto; /* protocol number */ + u_int8_t and_flags; /* tcp ANDed flags */ + u_int8_t or_flags; /* tcp ORed flags */ +}; + +/* + * Information we keep per each source address. + */ +struct host { + struct host *next; /* Next entry with the same hash */ + clock_t timestamp; /* Last update time */ + struct in_addr src_addr; /* Source address */ + struct in_addr dest_addr; /* Destination address */ + unsigned short src_port; /* Source port */ + int count; /* Number of ports in the list */ + int weight; /* Total weight of ports in the list */ + struct port ports[SCAN_MAX_COUNT - 1]; /* List of ports */ + unsigned char tos; /* TOS */ + unsigned char ttl; /* TTL */ + unsigned char flags; /* HF_ flags bitmask */ +}; + +/* + * State information. + */ +static struct { + spinlock_t lock; + struct host list[LIST_SIZE]; /* List of source addresses */ + struct host *hash[HASH_SIZE]; /* Hash: pointers into the list */ + int index; /* Oldest entry to be replaced */ +} state; + +/* + * Convert an IP address into a hash table index. + */ +static inline int hashfunc(struct in_addr addr) +{ + unsigned int value; + int hash; + + value = addr.s_addr; + hash = 0; + do { + hash ^= value; + } while ((value >>= HASH_LOG)); + + return hash & (HASH_SIZE - 1); +} + +static int +ipt_psd_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + struct iphdr *ip_hdr; + struct tcphdr *tcp_hdr; + struct in_addr addr; + u_int16_t src_port,dest_port; + u_int8_t tcp_flags, proto; + clock_t now; + struct host *curr, *last, **head; + int hash, index, count; + + /* Parameters from userspace */ + const struct ipt_psd_info *psdinfo = matchinfo; + + /* IP header */ + ip_hdr = pskb->nh.iph; + + /* Sanity check */ + if (ntohs(ip_hdr->frag_off) & IP_OFFSET) { + DEBUGP("PSD: sanity check failed\n"); + return 0; + } + + /* TCP or UDP ? */ + proto = ip_hdr->protocol; + + if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) { + DEBUGP("PSD: protocol not supported\n"); + return 0; + } + + /* Get the source address, source & destination ports, and TCP flags */ + + addr.s_addr = ip_hdr->saddr; + + tcp_hdr = (struct tcphdr*)((u_int32_t *)ip_hdr + ip_hdr->ihl); + + /* Yep, itīs dirty */ + src_port = tcp_hdr->source; + dest_port = tcp_hdr->dest; + + if (proto == IPPROTO_TCP) { + tcp_flags = *((u_int8_t*)tcp_hdr + 13); + } + else { + tcp_flags = 0x00; + } + + /* We're using IP address 0.0.0.0 for a special purpose here, so don't let + * them spoof us. [DHCP needs this feature - HW] */ + if (!addr.s_addr) { + DEBUGP("PSD: spoofed source address (0.0.0.0)\n"); + return 0; + } + + /* Use jiffies here not to depend on someone setting the time while we're + * running; we need to be careful with possible return value overflows. */ + now = jiffies; + + spin_lock(&state.lock); + + /* Do we know this source address already? */ + count = 0; + last = NULL; + if ((curr = *(head = &state.hash[hash = hashfunc(addr)]))) + do { + if (curr->src_addr.s_addr == addr.s_addr) break; + count++; + if (curr->next) last = curr; + } while ((curr = curr->next)); + + if (curr) { + + /* We know this address, and the entry isn't too old. Update it. */ + if (now - curr->timestamp <= (psdinfo->delay_threshold*HZ)/100 && + time_after_eq(now, curr->timestamp)) { + + /* Just update the appropriate list entry if we've seen this port already */ + for (index = 0; index < curr->count; index++) { + if (curr->ports[index].number == dest_port) { + curr->ports[index].proto = proto; + curr->ports[index].and_flags &= tcp_flags; + curr->ports[index].or_flags |= tcp_flags; + goto out_no_match; + } + } + + /* TCP/ACK and/or TCP/RST to a new port? This could be an outgoing connection. */ + if (proto == IPPROTO_TCP && (tcp_hdr->ack || tcp_hdr->rst)) + goto out_no_match; + + /* Packet to a new port, and not TCP/ACK: update the timestamp */ + curr->timestamp = now; + + /* Logged this scan already? Then drop the packet. */ + if (curr->weight >= psdinfo->weight_threshold) + goto out_match; + + /* Specify if destination address, source port, TOS or TTL are not fixed */ + if (curr->dest_addr.s_addr != ip_hdr->daddr) + curr->flags |= HF_DADDR_CHANGING; + if (curr->src_port != src_port) + curr->flags |= HF_SPORT_CHANGING; + if (curr->tos != ip_hdr->tos) + curr->flags |= HF_TOS_CHANGING; + if (curr->ttl != ip_hdr->ttl) + curr->flags |= HF_TTL_CHANGING; + + /* Update the total weight */ + curr->weight += (ntohs(dest_port) < 1024) ? + psdinfo->lo_ports_weight : psdinfo->hi_ports_weight; + + /* Got enough destination ports to decide that this is a scan? */ + /* Then log it and drop the packet. */ + if (curr->weight >= psdinfo->weight_threshold) + goto out_match; + + /* Remember the new port */ + if (curr->count < SCAN_MAX_COUNT) { + curr->ports[curr->count].number = dest_port; + curr->ports[curr->count].proto = proto; + curr->ports[curr->count].and_flags = tcp_flags; + curr->ports[curr->count].or_flags = tcp_flags; + curr->count++; + } + + goto out_no_match; + } + + /* We know this address, but the entry is outdated. Mark it unused, and + * remove from the hash table. We'll allocate a new entry instead since + * this one might get re-used too soon. */ + curr->src_addr.s_addr = 0; + if (last) + last->next = last->next->next; + else if (*head) + *head = (*head)->next; + last = NULL; + } + + /* We don't need an ACK from a new source address */ + if (proto == IPPROTO_TCP && tcp_hdr->ack) + goto out_no_match; + + /* Got too many source addresses with the same hash value? Then remove the + * oldest one from the hash table, so that they can't take too much of our + * CPU time even with carefully chosen spoofed IP addresses. */ + if (count >= HASH_MAX && last) last->next = NULL; + + /* We're going to re-use the oldest list entry, so remove it from the hash + * table first (if it is really already in use, and isn't removed from the + * hash table already because of the HASH_MAX check above). */ + + /* First, find it */ + if (state.list[state.index].src_addr.s_addr) + head = &state.hash[hashfunc(state.list[state.index].src_addr)]; + else + head = &last; + last = NULL; + if ((curr = *head)) + do { + if (curr == &state.list[state.index]) break; + last = curr; + } while ((curr = curr->next)); + + /* Then, remove it */ + if (curr) { + if (last) + last->next = last->next->next; + else if (*head) + *head = (*head)->next; + } + + /* Get our list entry */ + curr = &state.list[state.index++]; + if (state.index >= LIST_SIZE) state.index = 0; + + /* Link it into the hash table */ + head = &state.hash[hash]; + curr->next = *head; + *head = curr; + + /* And fill in the fields */ + curr->timestamp = now; + curr->src_addr = addr; + curr->dest_addr.s_addr = ip_hdr->daddr; + curr->src_port = src_port; + curr->count = 1; + curr->weight = (ntohs(dest_port) < 1024) ? + psdinfo->lo_ports_weight : psdinfo->hi_ports_weight; + curr->ports[0].number = dest_port; + curr->ports[0].proto = proto; + curr->ports[0].and_flags = tcp_flags; + curr->ports[0].or_flags = tcp_flags; + curr->tos = ip_hdr->tos; + curr->ttl = ip_hdr->ttl; + +out_no_match: + spin_unlock(&state.lock); + return 0; + +out_match: + spin_unlock(&state.lock); + return 1; +} + +static int ipt_psd_checkentry(const char *tablename, + const struct ipt_ip *e, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ +/* const struct ipt_psd_info *psdinfo = targinfo;*/ + + /* we accept TCP only */ +/* if (e->ip.proto != IPPROTO_TCP) { */ +/* DEBUGP("PSD: specified protocol may be TCP only\n"); */ +/* return 0; */ +/* } */ + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_psd_info))) { + DEBUGP("PSD: matchsize %u != %u\n", + matchsize, + IPT_ALIGN(sizeof(struct ipt_psd_info))); + return 0; + } + + return 1; +} + +static struct ipt_match ipt_psd_reg = { + {NULL, NULL}, + "psd", + ipt_psd_match, + ipt_psd_checkentry, + NULL, + THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_match(&ipt_psd_reg)) + return -EINVAL; + + memset(&state, 0, sizeof(state)); + + spin_lock_init(&(state.lock)); + + printk("netfilter PSD loaded - (c) astaro AG\n"); + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipt_psd_reg); + printk("netfilter PSD unloaded - (c) astaro AG\n"); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_quota.c linux-2.6.3/net/ipv4/netfilter/ipt_quota.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_quota.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_quota.c 2004-02-19 08:47:31.780721057 +0100 @@ -0,0 +1,81 @@ +/* + * netfilter module to enforce network quotas + * + * Sam Johnston + */ +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +static spinlock_t quota_lock = SPIN_LOCK_UNLOCKED; + +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) +{ + + struct ipt_quota_info *q = (struct ipt_quota_info *) matchinfo; + + spin_lock_bh("a_lock); + + if (q->quota >= datalen) { + /* we can afford this one */ + q->quota -= datalen; + spin_unlock_bh("a_lock); + +#ifdef DEBUG_IPT_QUOTA + printk("IPT Quota OK: %llu datlen %d \n", q->quota, datalen); +#endif + return 1; + } + + /* so we do not allow even small packets from now on */ + q->quota = 0; + +#ifdef DEBUG_IPT_QUOTA + printk("IPT Quota Failed: %llu datlen %d \n", q->quota, datalen); +#endif + + spin_unlock_bh("a_lock); + return 0; +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, unsigned int matchsize, unsigned int hook_mask) +{ + /* TODO: spinlocks? sanity checks? */ + if (matchsize != IPT_ALIGN(sizeof (struct ipt_quota_info))) + return 0; + + return 1; +} + +static struct ipt_match quota_match + = { {NULL, NULL}, "quota", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init +init(void) +{ + return ipt_register_match("a_match); +} + +static void __exit +fini(void) +{ + ipt_unregister_match("a_match); +} + +module_init(init); +module_exit(fini); + diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_random.c linux-2.6.3/net/ipv4/netfilter/ipt_random.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_random.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_random.c 2004-02-19 08:47:32.336587163 +0100 @@ -0,0 +1,96 @@ +/* + This is a module which is used for a "random" match support. + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: + ftp://prep.ai.mit.edu/pub/gnu/GPL + + 2001-10-14 Fabrice MARIE : initial implementation. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +static int +ipt_rand_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + /* Parameters from userspace */ + const struct ipt_rand_info *info = matchinfo; + u_int8_t random_number; + + /* get 1 random number from the kernel random number generation routine */ + get_random_bytes((void *)(&random_number), 1); + + /* Do we match ? */ + if (random_number <= info->average) + return 1; + else + return 0; +} + +static int +ipt_rand_checkentry(const char *tablename, + const struct ipt_ip *e, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + /* Parameters from userspace */ + const struct ipt_rand_info *info = matchinfo; + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_rand_info))) { + printk("ipt_random: matchsize %u != %u\n", matchsize, + IPT_ALIGN(sizeof(struct ipt_rand_info))); + return 0; + } + + /* must be 1 <= average % <= 99 */ + /* 1 x 2.55 = 2 */ + /* 99 x 2.55 = 252 */ + if ((info->average < 2) || (info->average > 252)) { + printk("ipt_random: invalid average %u\n", info->average); + return 0; + } + + return 1; +} + +static struct ipt_match ipt_rand_reg = { + {NULL, NULL}, + "random", + ipt_rand_match, + ipt_rand_checkentry, + NULL, + THIS_MODULE }; + +static int __init init(void) +{ + if (ipt_register_match(&ipt_rand_reg)) + return -EINVAL; + + printk("ipt_random match loaded\n"); + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipt_rand_reg); + printk("ipt_random match unloaded\n"); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_realm.c linux-2.6.3/net/ipv4/netfilter/ipt_realm.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_realm.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_realm.c 2004-02-19 08:47:33.450318894 +0100 @@ -0,0 +1,68 @@ +/* Kernel module to match realm from routing. */ +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Sampsa Ranta "); +MODULE_LICENSE("GPL"); + +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 ipt_realm_info *info = matchinfo; + struct dst_entry *dst = skb->dst; + u32 id; + + if(dst == NULL) + return 0; + id = dst->tclassid; + + return (info->id == (id & info->mask)) ^ info->invert; +} + +static int check(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (hook_mask + & ~((1 << NF_IP_POST_ROUTING) | (1 << NF_IP_FORWARD) | + (1 << NF_IP_LOCAL_OUT)| (1 << NF_IP_LOCAL_IN))) { + printk("ipt_realm: only valid for POST_ROUTING, LOCAL_OUT, " + "LOCAL_IN or FORWARD.\n"); + return 0; + } + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_realm_info))) + return 0; + + return 1; +} + +static struct ipt_match realm_match += { { NULL, NULL }, "realm", &match, &check, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&realm_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&realm_match); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_time.c linux-2.6.3/net/ipv4/netfilter/ipt_time.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_time.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_time.c 2004-02-19 08:47:33.998186927 +0100 @@ -0,0 +1,185 @@ +/* + This is a module which is used for time matching + It is using some modified code from dietlibc (localtime() function) + that you can find at http://www.fefe.de/dietlibc/ + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: ftp://prep.ai.mit.edu/pub/gnu/GPL + 2001-05-04 Fabrice MARIE : initial development. + 2001-21-05 Fabrice MARIE : bug fix in the match code, + thanks to "Zeng Yu" for bug report. + 2001-26-09 Fabrice MARIE : force the match to be in LOCAL_IN or PRE_ROUTING only. + 2001-30-11 Fabrice : added the possibility to use the match in FORWARD/OUTPUT with a little hack, + added Nguyen Dang Phuoc Dong patch to support timezones. +*/ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Fabrice MARIE "); +MODULE_DESCRIPTION("Match arrival timestamp"); +MODULE_LICENSE("GPL"); + +struct tm +{ + int tm_sec; /* Seconds. [0-60] (1 leap second) */ + int tm_min; /* Minutes. [0-59] */ + int tm_hour; /* Hours. [0-23] */ + int tm_mday; /* Day. [1-31] */ + int tm_mon; /* Month. [0-11] */ + int tm_year; /* Year - 1900. */ + int tm_wday; /* Day of week. [0-6] */ + int tm_yday; /* Days in year.[0-365] */ + int tm_isdst; /* DST. [-1/0/1]*/ + + long int tm_gmtoff; /* we don't care, we count from GMT */ + const char *tm_zone; /* we don't care, we count from GMT */ +}; + +void +localtime(const time_t *timepr, struct tm *r); + +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 ipt_time_info *info = matchinfo; /* match info for rule */ + struct tm currenttime; /* time human readable */ + u_int8_t days_of_week[7] = {64, 32, 16, 8, 4, 2, 1}; + u_int16_t packet_time; + struct timeval kerneltimeval; + time_t packet_local_time; + + /* if kerneltime=1, we don't read the skb->timestamp but kernel time instead */ + if (info->kerneltime) + { + do_gettimeofday(&kerneltimeval); + packet_local_time = kerneltimeval.tv_sec; + } + else + packet_local_time = skb->stamp.tv_sec; + + /* Transform the timestamp of the packet, in a human readable form */ + localtime(&packet_local_time, ¤ttime); + + /* check if we match this timestamp, we start by the days... */ + if ((days_of_week[currenttime.tm_wday] & info->days_match) != days_of_week[currenttime.tm_wday]) + return 0; /* the day doesn't match */ + + /* ... check the time now */ + packet_time = (currenttime.tm_hour * 60) + currenttime.tm_min; + if ((packet_time < info->time_start) || (packet_time > info->time_stop)) + return 0; + + /* here we match ! */ + return 1; +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_time_info *info = matchinfo; /* match info for rule */ + + /* First, check that we are in the correct hook */ + /* PRE_ROUTING, LOCAL_IN or FROWARD */ + if (hook_mask + & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))) + { + printk("ipt_time: error, only valid for PRE_ROUTING, LOCAL_IN, FORWARD and OUTPUT)\n"); + return 0; + } + /* we use the kerneltime if we are in forward or output */ + info->kerneltime = 1; + if (hook_mask & ~((1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))) + /* if not, we use the skb time */ + info->kerneltime = 0; + + /* Check the size */ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_time_info))) + return 0; + /* Now check the coherence of the data ... */ + if ((info->time_start > 1439) || /* 23*60+59 = 1439*/ + (info->time_stop > 1439)) + { + printk(KERN_WARNING "ipt_time: invalid argument\n"); + return 0; + } + + return 1; +} + +static struct ipt_match time_match += { { NULL, NULL }, "time", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + printk("ipt_time loading\n"); + return ipt_register_match(&time_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&time_match); + printk("ipt_time unloaded\n"); +} + +module_init(init); +module_exit(fini); + + +/* The part below is borowed and modified from dietlibc */ + +/* seconds per day */ +#define SPD 24*60*60 + +void +localtime(const time_t *timepr, struct tm *r) { + time_t i; + time_t timep; + extern struct timezone sys_tz; + const unsigned int __spm[12] = + { 0, + (31), + (31+28), + (31+28+31), + (31+28+31+30), + (31+28+31+30+31), + (31+28+31+30+31+30), + (31+28+31+30+31+30+31), + (31+28+31+30+31+30+31+31), + (31+28+31+30+31+30+31+31+30), + (31+28+31+30+31+30+31+31+30+31), + (31+28+31+30+31+30+31+31+30+31+30), + }; + register time_t work; + + timep = (*timepr) - (sys_tz.tz_minuteswest * 60); + work=timep%(SPD); + r->tm_sec=work%60; work/=60; + r->tm_min=work%60; r->tm_hour=work/60; + work=timep/(SPD); + r->tm_wday=(4+work)%7; + for (i=1970; ; ++i) { + register time_t k= (!(i%4) && ((i%100) || !(i%400)))?366:365; + if (work>k) + work-=k; + else + break; + } + r->tm_year=i-1900; + for (i=11; i && __spm[i]>work; --i) ; + r->tm_mon=i; + r->tm_mday=work-__spm[i]+1; +} diff -Nur linux-2.6.3.org/net/ipv4/netfilter/ipt_u32.c linux-2.6.3/net/ipv4/netfilter/ipt_u32.c --- linux-2.6.3.org/net/ipv4/netfilter/ipt_u32.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv4/netfilter/ipt_u32.c 2004-02-19 08:47:34.549054238 +0100 @@ -0,0 +1,211 @@ +/* Kernel module to match u32 packet content. */ + +/* +U32 tests whether quantities of up to 4 bytes extracted from a packet +have specified values. The specification of what to extract is general +enough to find data at given offsets from tcp headers or payloads. + + --u32 tests + The argument amounts to a program in a small language described below. + tests := location = value | tests && location = value + value := range | value , range + range := number | number : number + a single number, n, is interpreted the same as n:n + n:m is interpreted as the range of numbers >=n and <=m + location := number | location operator number + operator := & | << | >> | @ + + The operators &, <<, >>, && mean the same as in c. The = is really a set + membership operator and the value syntax describes a set. The @ operator + is what allows moving to the next header and is described further below. + + *** Until I can find out how to avoid it, there are some artificial limits + on the size of the tests: + - no more than 10 ='s (and 9 &&'s) in the u32 argument + - no more than 10 ranges (and 9 commas) per value + - no more than 10 numbers (and 9 operators) per location + + To describe the meaning of location, imagine the following machine that + interprets it. There are three registers: + A is of type char*, initially the address of the IP header + B and C are unsigned 32 bit integers, initially zero + + The instructions are: + number B = number; + C = (*(A+B)<<24)+(*(A+B+1)<<16)+(*(A+B+2)<<8)+*(A+B+3) + &number C = C&number + <>number C = C>>number + @number A = A+C; then do the instruction number + Any access of memory outside [skb->head,skb->end] causes the match to fail. + Otherwise the result of the computation is the final value of C. + + Whitespace is allowed but not required in the tests. + However the characters that do occur there are likely to require + shell quoting, so it's a good idea to enclose the arguments in quotes. + +Example: + match IP packets with total length >= 256 + The IP header contains a total length field in bytes 2-3. + --u32 "0&0xFFFF=0x100:0xFFFF" + read bytes 0-3 + AND that with FFFF (giving bytes 2-3), + and test whether that's in the range [0x100:0xFFFF] + +Example: (more realistic, hence more complicated) + match icmp packets with icmp type 0 + First test that it's an icmp packet, true iff byte 9 (protocol) = 1 + --u32 "6&0xFF=1 && ... + read bytes 6-9, use & to throw away bytes 6-8 and compare the result to 1 + Next test that it's not a fragment. + (If so it might be part of such a packet but we can't always tell.) + n.b. This test is generally needed if you want to match anything + beyond the IP header. + The last 6 bits of byte 6 and all of byte 7 are 0 iff this is a complete + packet (not a fragment). Alternatively, you can allow first fragments + by only testing the last 5 bits of byte 6. + ... 4&0x3FFF=0 && ... + Last test: the first byte past the IP header (the type) is 0 + This is where we have to use the @syntax. The length of the IP header + (IHL) in 32 bit words is stored in the right half of byte 0 of the + IP header itself. + ... 0>>22&0x3C@0>>24=0" + The first 0 means read bytes 0-3, + >>22 means shift that 22 bits to the right. Shifting 24 bits would give + the first byte, so only 22 bits is four times that plus a few more bits. + &3C then eliminates the two extra bits on the right and the first four + bits of the first byte. + For instance, if IHL=5 then the IP header is 20 (4 x 5) bytes long. + In this case bytes 0-1 are (in binary) xxxx0101 yyzzzzzz, + >>22 gives the 10 bit value xxxx0101yy and &3C gives 010100. + @ means to use this number as a new offset into the packet, and read + four bytes starting from there. This is the first 4 bytes of the icmp + payload, of which byte 0 is the icmp type. Therefore we simply shift + the value 24 to the right to throw out all but the first byte and compare + the result with 0. + +Example: + tcp payload bytes 8-12 is any of 1, 2, 5 or 8 + First we test that the packet is a tcp packet (similar to icmp). + --u32 "6&0xFF=6 && ... + Next, test that it's not a fragment (same as above). + ... 0>>22&0x3C@12>>26&0x3C@8=1,2,5,8" + 0>>22&3C as above computes the number of bytes in the IP header. + @ makes this the new offset into the packet, which is the start of the + tcp header. The length of the tcp header (again in 32 bit words) is + the left half of byte 12 of the tcp header. The 12>>26&3C + computes this length in bytes (similar to the IP header before). + @ makes this the new offset, which is the start of the tcp payload. + Finally 8 reads bytes 8-12 of the payload and = checks whether the + result is any of 1, 2, 5 or 8 +*/ + +#include +#include + +#include +#include + +/* #include for timing */ + +MODULE_AUTHOR("Don Cohen "); +MODULE_DESCRIPTION("IP tables u32 matching module"); +MODULE_LICENSE("GPL"); + +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 ipt_u32 *data = matchinfo; + int testind, i; + unsigned char* origbase = (char*)skb->nh.iph; + unsigned char* base = origbase; + unsigned char* head = skb->head; + unsigned char* end = skb->end; + int nnums, nvals; + u_int32_t pos, val; + /* unsigned long long cycles1, cycles2, cycles3, cycles4; + cycles1 = get_cycles(); */ + + for (testind=0; testind < data->ntests; testind++) { + base = origbase; /* reset for each test */ + pos = data->tests[testind].location[0].number; + if (base+pos+3 > end || base+pos < head) + return 0; + val = (base[pos]<<24) + (base[pos+1]<<16) + + (base[pos+2]<<8) + base[pos+3]; + nnums = data->tests[testind].nnums; + for (i=1; i < nnums; i++) { + u_int32_t number = data->tests[testind].location[i].number; + switch (data->tests[testind].location[i].nextop) { + case IPT_U32_AND: + val = val & number; + break; + case IPT_U32_LEFTSH: + val = val << number; + break; + case IPT_U32_RIGHTSH: + val = val >> number; + break; + case IPT_U32_AT: + base = base + val; + pos = number; + if (base+pos+3 > end || base+pos < head) + return 0; + val = (base[pos]<<24) + (base[pos+1]<<16) + + (base[pos+2]<<8) + base[pos+3]; + break; + } + } + nvals = data->tests[testind].nvalues; + for (i=0; i < nvals; i++) { + if ((data->tests[testind].value[i].min <= val) && + (val <= data->tests[testind].value[i].max)) { + break; + } + } + if (i >= data->tests[testind].nvalues) { + /* cycles2 = get_cycles(); + printk("failed %d in %d cycles\n", testind, + cycles2-cycles1); */ + return 0; + } + } + /* cycles2 = get_cycles(); + printk("succeeded in %d cycles\n", cycles2-cycles1); */ + return 1; +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_u32))) + return 0; + return 1; +} + +static struct ipt_match u32_match += { { NULL, NULL }, "u32", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&u32_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&u32_match); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv6/netfilter/Kconfig linux-2.6.3/net/ipv6/netfilter/Kconfig --- linux-2.6.3.org/net/ipv6/netfilter/Kconfig 2004-02-18 04:59:20.000000000 +0100 +++ linux-2.6.3/net/ipv6/netfilter/Kconfig 2004-02-19 08:47:32.341585959 +0100 @@ -218,5 +218,85 @@ To compile it as a module, choose M here. If unsure, say N. #dep_tristate ' LOG target support' CONFIG_IP6_NF_TARGET_LOG $CONFIG_IP6_NF_IPTABLES +config IP6_NF_TARGET_HL + tristate 'HL target support' + depends on IP6_NF_MANGLE + help + This allows the user to set the IPv6 Hop Limit value on a packet or + to increment or decrement it by a given value. + + Example: + # ip6tables -t mangle -A OUTPUT -j HL --hl-inc 1 + # ip6tables -t mangle -A INPUT -j HL --hl-eq 64 + # ip6tables -t mangle -A OUTPUT -j HL --hl-dec 2 + +config IP6_NF_TARGET_REJECT + tristate 'REJECT target support' + depends on IP6_NF_FILTER + help + This CONFIG_IP6_NF_TARGET_REJECT option adds a REJECT target to ip6tables. + Please keep in mind that the icmp-types are different from the icmpv6 types + (see ip6tables -j REJECT -h for more info) + +config IP6_NF_MATCH_FUZZY + tristate 'Fuzzy match support' + depends on IP6_NF_FILTER + help + This option adds a `fuzzy' match which allows you to match packets according to + a dynamic profile implemented by means of a simple Fuzzy Logic Controller (FLC) + +config IP6_NF_MATCH_NTH + tristate 'Nth match support' + depends on IP6_NF_IPTABLES + help + This option adds an iptables `Nth' match, which allows you to match every Nth + packet encountered. By default there are 16 different counters that can be + used. + + This match functions in one of two ways + 1) Match ever Nth packet, and only the Nth packet. + example: + iptables -t mangle -A PREROUTING -m nth --every 10 -j DROP + This rule will drop every 10th packet. + 2) Unique rule for every packet. This is an easy and quick + method to produce load-balancing for both inbound and outbound. + example: + iptables -t nat -A POSTROUTING -o eth0 -m nth --counter 7 \ + --every 3 --packet 0 -j SNAT --to-source 10.0.0.5 + iptables -t nat -A POSTROUTING -o eth0 -m nth --counter 7 \ + --every 3 --packet 1 -j SNAT --to-source 10.0.0.6 + iptables -t nat -A POSTROUTING -o eth0 -m nth --counter 7 \ + --every 3 --packet 2 -j SNAT --to-source 10.0.0.7 + This example evenly splits connections between the three SNAT + addresses. + + By using the mangle table and iproute2, you can setup complex + load-balanced routing. There's lot of other uses. Be creative! + + Suppported options are: + --every Nth Match every Nth packet + [--counter] num Use counter 0-15 (default:0) + [--start] num Initialize the counter at the number 'num' + instead of 0. Must be between 0 and Nth-1 + [--packet] num Match on 'num' packet. Must be between 0 + and Nth-1. + If --packet is used for a counter than + there must be Nth number of --packet + rules, covering all values between 0 and + Nth-1 inclusively. + +config IP6_NF_MATCH_RANDOM + tristate 'Random match support' + depends on IP6_NF_IPTABLES + help + This option adds a `random' match, + which allow you to match packets randomly + following a given probability. + + Suppported options are: + + [--average] percent will match randomly packets with a probability of + 'percent'. default is 50% + endmenu diff -Nur linux-2.6.3.org/net/ipv6/netfilter/Makefile linux-2.6.3/net/ipv6/netfilter/Makefile --- linux-2.6.3.org/net/ipv6/netfilter/Makefile 2004-02-18 04:58:26.000000000 +0100 +++ linux-2.6.3/net/ipv6/netfilter/Makefile 2004-02-19 08:47:32.341585959 +0100 @@ -8,6 +8,7 @@ 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 +obj-$(CONFIG_IP6_NF_MATCH_FUZZY) += ip6t_fuzzy.o obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o obj-$(CONFIG_IP6_NF_MATCH_OPTS) += ip6t_hbh.o ip6t_dst.o obj-$(CONFIG_IP6_NF_MATCH_IPV6HEADER) += ip6t_ipv6header.o @@ -19,6 +20,12 @@ 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_REJECT) += ip6t_REJECT.o obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o + +obj-$(CONFIG_IP6_NF_MATCH_RANDOM) += ip6t_random.o + +obj-$(CONFIG_IP6_NF_MATCH_NTH) += ip6t_nth.o +obj-$(CONFIG_IP6_NF_TARGET_HL) += ip6t_HL.o obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o diff -Nur linux-2.6.3.org/net/ipv6/netfilter/ip6t_HL.c linux-2.6.3/net/ipv6/netfilter/ip6t_HL.c --- linux-2.6.3.org/net/ipv6/netfilter/ip6t_HL.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv6/netfilter/ip6t_HL.c 2004-02-19 09:07:45.267493759 +0100 @@ -0,0 +1,105 @@ +/* + * Hop Limit modification target for ip6tables + * Maciej Soltysiak + * Based on HW's TTL module + * + * This software is distributed under the terms of GNU GPL + */ + +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Maciej Soltysiak "); +MODULE_DESCRIPTION("IP tables Hop Limit modification module"); +MODULE_LICENSE("GPL"); + +static unsigned int ip6t_hl_target(struct sk_buff **pskb, unsigned int hooknum, + const struct net_device *in, const struct net_device *out, + const void *targinfo, void *userinfo) +{ + struct ipv6hdr *ip6h = (*pskb)->nh.ipv6h; + const struct ip6t_HL_info *info = targinfo; + u_int16_t diffs[2]; + int new_hl; + + switch (info->mode) { + case IP6T_HL_SET: + new_hl = info->hop_limit; + break; + case IP6T_HL_INC: + new_hl = ip6h->hop_limit + info->hop_limit; + if (new_hl > 255) + new_hl = 255; + break; + case IP6T_HL_DEC: + new_hl = ip6h->hop_limit + info->hop_limit; + if (new_hl < 0) + new_hl = 0; + break; + default: + new_hl = ip6h->hop_limit; + break; + } + + if (new_hl != ip6h->hop_limit) { + diffs[0] = htons(((unsigned)ip6h->hop_limit) << 8) ^ 0xFFFF; + ip6h->hop_limit = new_hl; + diffs[1] = htons(((unsigned)ip6h->hop_limit) << 8); + } + + return IP6T_CONTINUE; +} + +static int ip6t_hl_checkentry(const char *tablename, + const struct ip6t_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + struct ip6t_HL_info *info = targinfo; + + if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_HL_info))) { + printk(KERN_WARNING "HL: targinfosize %u != %Zu\n", + targinfosize, + IP6T_ALIGN(sizeof(struct ip6t_HL_info))); + return 0; + } + + if (strcmp(tablename, "mangle")) { + printk(KERN_WARNING "HL: can only be called from \"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + + if (info->mode > IP6T_HL_MAXMODE) { + printk(KERN_WARNING "HL: invalid or unknown Mode %u\n", + info->mode); + return 0; + } + + if ((info->mode != IP6T_HL_SET) && (info->hop_limit == 0)) { + printk(KERN_WARNING "HL: increment/decrement doesn't make sense with value 0\n"); + return 0; + } + + return 1; +} + +static struct ip6t_target ip6t_HL = { { NULL, NULL }, "HL", + ip6t_hl_target, ip6t_hl_checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ip6t_register_target(&ip6t_HL); +} + +static void __exit fini(void) +{ + ip6t_unregister_target(&ip6t_HL); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv6/netfilter/ip6t_REJECT.c linux-2.6.3/net/ipv6/netfilter/ip6t_REJECT.c --- linux-2.6.3.org/net/ipv6/netfilter/ip6t_REJECT.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv6/netfilter/ip6t_REJECT.c 2004-02-19 08:47:25.025348256 +0100 @@ -0,0 +1,274 @@ +/* + * This is a module which is used for rejecting packets. + * Added support for customized reject packets (Jozsef Kadlecsik). + * Sun 12 Nov 2000 + * Port to IPv6 / ip6tables (Harald Welte ) + */ +#include +#include +#include +#include +#include +#include +#include + +#if 1 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +#if 0 +/* Send RST reply */ +static void send_reset(struct sk_buff *oldskb) +{ + struct sk_buff *nskb; + struct tcphdr *otcph, *tcph; + struct rtable *rt; + unsigned int otcplen; + int needs_ack; + + /* IP header checks: fragment, too short. */ + if (oldskb->nh.iph->frag_off & htons(IP_OFFSET) + || oldskb->len < (oldskb->nh.iph->ihl<<2) + sizeof(struct tcphdr)) + return; + + otcph = (struct tcphdr *)((u_int32_t*)oldskb->nh.iph + oldskb->nh.iph->ihl); + otcplen = oldskb->len - oldskb->nh.iph->ihl*4; + + /* No RST for RST. */ + if (otcph->rst) + return; + + /* Check checksum. */ + if (tcp_v4_check(otcph, otcplen, oldskb->nh.iph->saddr, + oldskb->nh.iph->daddr, + csum_partial((char *)otcph, otcplen, 0)) != 0) + return; + + /* Copy skb (even if skb is about to be dropped, we can't just + clone it because there may be other things, such as tcpdump, + interested in it) */ + nskb = skb_copy(oldskb, GFP_ATOMIC); + if (!nskb) + return; + + /* This packet will not be the same as the other: clear nf fields */ + nf_conntrack_put(nskb->nfct); + nskb->nfct = NULL; + nskb->nfcache = 0; +#ifdef CONFIG_NETFILTER_DEBUG + nskb->nf_debug = 0; +#endif + + tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl); + + nskb->nh.iph->daddr = xchg(&nskb->nh.iph->saddr, nskb->nh.iph->daddr); + tcph->source = xchg(&tcph->dest, tcph->source); + + /* Truncate to length (no data) */ + tcph->doff = sizeof(struct tcphdr)/4; + skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr)); + nskb->nh.iph->tot_len = htons(nskb->len); + + if (tcph->ack) { + needs_ack = 0; + tcph->seq = otcph->ack_seq; + tcph->ack_seq = 0; + } else { + needs_ack = 1; + tcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn + otcph->fin + + otcplen - (otcph->doff<<2)); + tcph->seq = 0; + } + + /* Reset flags */ + ((u_int8_t *)tcph)[13] = 0; + tcph->rst = 1; + tcph->ack = needs_ack; + + tcph->window = 0; + tcph->urg_ptr = 0; + + /* Adjust TCP checksum */ + tcph->check = 0; + tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr), + nskb->nh.iph->saddr, + nskb->nh.iph->daddr, + csum_partial((char *)tcph, + sizeof(struct tcphdr), 0)); + + /* Adjust IP TTL, DF */ + nskb->nh.iph->ttl = MAXTTL; + /* Set DF, id = 0 */ + nskb->nh.iph->frag_off = htons(IP_DF); + nskb->nh.iph->id = 0; + + /* Adjust IP checksum */ + nskb->nh.iph->check = 0; + nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, + nskb->nh.iph->ihl); + + /* Routing */ + if (ip_route_output(&rt, nskb->nh.iph->daddr, nskb->nh.iph->saddr, + RT_TOS(nskb->nh.iph->tos) | RTO_CONN, + 0) != 0) + goto free_nskb; + + dst_release(nskb->dst); + nskb->dst = &rt->u.dst; + + /* "Never happens" */ + if (nskb->len > nskb->dst->pmtu) + goto free_nskb; + + NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev, + ip_finish_output); + return; + + free_nskb: + kfree_skb(nskb); +} +#endif + +static unsigned int reject6_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_reject_info *reject = targinfo; + + /* WARNING: This code causes reentry within ip6tables. + This means that the ip6tables jump stack is now crap. We + must return an absolute verdict. --RR */ + DEBUGP("REJECTv6: calling icmpv6_send\n"); + switch (reject->with) { + case IP6T_ICMP6_NO_ROUTE: + icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_NOROUTE, 0, out); + break; + case IP6T_ICMP6_ADM_PROHIBITED: + icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_ADM_PROHIBITED, 0, out); + break; + case IP6T_ICMP6_NOT_NEIGHBOUR: + icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_NOT_NEIGHBOUR, 0, out); + break; + case IP6T_ICMP6_ADDR_UNREACH: + icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, out); + break; + case IP6T_ICMP6_PORT_UNREACH: + icmpv6_send(*pskb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, out); + break; +#if 0 + case IPT_ICMP_ECHOREPLY: { + struct icmp6hdr *icmph = (struct icmphdr *) + ((u_int32_t *)(*pskb)->nh.iph + (*pskb)->nh.iph->ihl); + unsigned int datalen = (*pskb)->len - (*pskb)->nh.iph->ihl * 4; + + /* Not non-head frags, or truncated */ + if (((ntohs((*pskb)->nh.iph->frag_off) & IP_OFFSET) == 0) + && datalen >= 4) { + /* Usually I don't like cut & pasting code, + but dammit, my party is starting in 45 + mins! --RR */ + struct icmp_bxm icmp_param; + + icmp_param.icmph=*icmph; + icmp_param.icmph.type=ICMP_ECHOREPLY; + icmp_param.data_ptr=(icmph+1); + icmp_param.data_len=datalen; + icmp_reply(&icmp_param, *pskb); + } + } + break; + case IPT_TCP_RESET: + send_reset(*pskb); + break; +#endif + default: + printk(KERN_WARNING "REJECTv6: case %u not handled yet\n", reject->with); + break; + } + + return NF_DROP; +} + +static inline int find_ping_match(const struct ip6t_entry_match *m) +{ + const struct ip6t_icmp *icmpinfo = (const struct ip6t_icmp *)m->data; + + if (strcmp(m->u.kernel.match->name, "icmp6") == 0 + && icmpinfo->type == ICMPV6_ECHO_REQUEST + && !(icmpinfo->invflags & IP6T_ICMP_INV)) + return 1; + + return 0; +} + +static int check(const char *tablename, + const struct ip6t_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + const struct ip6t_reject_info *rejinfo = targinfo; + + if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_reject_info))) { + DEBUGP("REJECTv6: targinfosize %u != 0\n", targinfosize); + return 0; + } + + /* Only allow these for packet filtering. */ + if (strcmp(tablename, "filter") != 0) { + DEBUGP("REJECTv6: bad table `%s'.\n", tablename); + return 0; + } + if ((hook_mask & ~((1 << NF_IP6_LOCAL_IN) + | (1 << NF_IP6_FORWARD) + | (1 << NF_IP6_LOCAL_OUT))) != 0) { + DEBUGP("REJECTv6: bad hook mask %X\n", hook_mask); + return 0; + } + + if (rejinfo->with == IP6T_ICMP6_ECHOREPLY) { + /* Must specify that it's an ICMP ping packet. */ + if (e->ipv6.proto != IPPROTO_ICMPV6 + || (e->ipv6.invflags & IP6T_INV_PROTO)) { + DEBUGP("REJECTv6: ECHOREPLY illegal for non-icmp\n"); + return 0; + } + /* Must contain ICMP match. */ + if (IP6T_MATCH_ITERATE(e, find_ping_match) == 0) { + DEBUGP("REJECTv6: ECHOREPLY illegal for non-ping\n"); + return 0; + } + } else if (rejinfo->with == IP6T_TCP_RESET) { + /* Must specify that it's a TCP packet */ + if (e->ipv6.proto != IPPROTO_TCP + || (e->ipv6.invflags & IP6T_INV_PROTO)) { + DEBUGP("REJECTv6: TCP_RESET illegal for non-tcp\n"); + return 0; + } + } + + return 1; +} + +static struct ip6t_target ip6t_reject_reg += { { NULL, NULL }, "REJECT", reject6_target, check, NULL, THIS_MODULE }; + +static int __init init(void) +{ + if (ip6t_register_target(&ip6t_reject_reg)) + return -EINVAL; + return 0; +} + +static void __exit fini(void) +{ + ip6t_unregister_target(&ip6t_reject_reg); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv6/netfilter/ip6t_fuzzy.c linux-2.6.3/net/ipv6/netfilter/ip6t_fuzzy.c --- linux-2.6.3.org/net/ipv6/netfilter/ip6t_fuzzy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv6/netfilter/ip6t_fuzzy.c 2004-02-19 08:47:27.856666262 +0100 @@ -0,0 +1,189 @@ +/* + * This module implements a simple TSK FLC + * (Takagi-Sugeno-Kang Fuzzy Logic Controller) that aims + * to limit , in an adaptive and flexible way , the packet rate crossing + * a given stream . It serves as an initial and very simple (but effective) + * example of how Fuzzy Logic techniques can be applied to defeat DoS attacks. + * As a matter of fact , Fuzzy Logic can help us to insert any "behavior" + * into our code in a precise , adaptive and efficient manner. + * The goal is very similar to that of "limit" match , but using techniques of + * Fuzzy Control , that allow us to shape the transfer functions precisely , + * avoiding over and undershoots - and stuff like that . + * + * + * 2002-08-10 Hime Aguiar e Oliveira Jr. : Initial version. + * 2002-08-17 : Changed to eliminate floating point operations . + * 2002-08-23 : Coding style changes . + * 2003-04-08 Maciej Soltysiak : IPv6 Port + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + Packet Acceptance Rate - LOW and Packet Acceptance Rate - HIGH + Expressed in percentage +*/ + +#define PAR_LOW 1/100 +#define PAR_HIGH 1 + +static spinlock_t fuzzy_lock = SPIN_LOCK_UNLOCKED; + +MODULE_AUTHOR("Hime Aguiar e Oliveira Junior "); +MODULE_DESCRIPTION("IP tables Fuzzy Logic Controller match module"); +MODULE_LICENSE("GPL"); + +static u_int8_t mf_high(u_int32_t tx,u_int32_t mini,u_int32_t maxi) +{ + if (tx >= maxi) return 100; + + if (tx <= mini) return 0; + + return ((100 * (tx-mini)) / (maxi-mini)); +} + +static u_int8_t mf_low(u_int32_t tx,u_int32_t mini,u_int32_t maxi) +{ + if (tx <= mini) return 100; + + if (tx >= maxi) return 0; + + return ((100 * (maxi - tx)) / (maxi - mini)); + +} + +static int +ip6t_fuzzy_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + /* From userspace */ + + struct ip6t_fuzzy_info *info = (struct ip6t_fuzzy_info *) matchinfo; + + u_int8_t random_number; + unsigned long amount; + u_int8_t howhigh, howlow; + + + spin_lock_bh(&fuzzy_lock); /* Rise the lock */ + + info->bytes_total += pskb->len; + info->packets_total++; + + info->present_time = jiffies; + + if (info->present_time >= info->previous_time) + amount = info->present_time - info->previous_time; + else { + /* There was a transition : I choose to re-sample + and keep the old acceptance rate... + */ + + amount = 0; + info->previous_time = info->present_time; + info->bytes_total = info->packets_total = 0; + }; + + if ( amount > HZ/10) {/* More than 100 ms elapsed ... */ + + info->mean_rate = (u_int32_t) ((HZ * info->packets_total) \ + / amount); + + info->previous_time = info->present_time; + info->bytes_total = info->packets_total = 0; + + howhigh = mf_high(info->mean_rate,info->minimum_rate,info->maximum_rate); + howlow = mf_low(info->mean_rate,info->minimum_rate,info->maximum_rate); + + info->acceptance_rate = (u_int8_t) \ + (howhigh * PAR_LOW + PAR_HIGH * howlow); + + /* In fact, the above defuzzification would require a denominator + * proportional to (howhigh+howlow) but, in this particular case, + * that expression is constant. + * An imediate consequence is that it is not necessary to call + * both mf_high and mf_low - but to keep things understandable, + * I did so. + */ + + } + + spin_unlock_bh(&fuzzy_lock); /* Release the lock */ + + + if (info->acceptance_rate < 100) + { + get_random_bytes((void *)(&random_number), 1); + + /* If within the acceptance , it can pass => don't match */ + if (random_number <= (255 * info->acceptance_rate) / 100) + return 0; + else + return 1; /* It can't pass (It matches) */ + }; + + return 0; /* acceptance_rate == 100 % => Everything passes ... */ + +} + +static int +ip6t_fuzzy_checkentry(const char *tablename, + const struct ip6t_ip6 *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + + const struct ip6t_fuzzy_info *info = matchinfo; + + if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_fuzzy_info))) { + printk("ip6t_fuzzy: matchsize %u != %u\n", matchsize, + IP6T_ALIGN(sizeof(struct ip6t_fuzzy_info))); + return 0; + } + + if ((info->minimum_rate < MINFUZZYRATE) || (info->maximum_rate > MAXFUZZYRATE) + || (info->minimum_rate >= info->maximum_rate)) { + printk("ip6t_fuzzy: BAD limits , please verify !!!\n"); + return 0; + } + + return 1; +} + +static struct ip6t_match ip6t_fuzzy_reg = { + {NULL, NULL}, + "fuzzy", + ip6t_fuzzy_match, + ip6t_fuzzy_checkentry, + NULL, + THIS_MODULE }; + +static int __init init(void) +{ + if (ip6t_register_match(&ip6t_fuzzy_reg)) + return -EINVAL; + + return 0; +} + +static void __exit fini(void) +{ + ip6t_unregister_match(&ip6t_fuzzy_reg); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv6/netfilter/ip6t_nth.c linux-2.6.3/net/ipv6/netfilter/ip6t_nth.c --- linux-2.6.3.org/net/ipv6/netfilter/ip6t_nth.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv6/netfilter/ip6t_nth.c 2004-02-19 08:47:29.580251094 +0100 @@ -0,0 +1,173 @@ +/* + This is a module which is used for match support for every Nth packet + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: + ftp://prep.ai.mit.edu/pub/gnu/GPL + + 2001-07-18 Fabrice MARIE : initial implementation. + 2001-09-20 Richard Wagner (rwagner@cloudnet.com) + * added support for multiple counters + * added support for matching on individual packets + in the counter cycle + 2003-04-30 Maciej Soltysiak : IPv6 Port + +*/ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +/* + * State information. + */ +struct state { + spinlock_t lock; + u_int16_t number; +}; + +static struct state states[IP6T_NTH_NUM_COUNTERS]; + +static int +ip6t_nth_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + /* Parameters from userspace */ + const struct ip6t_nth_info *info = matchinfo; + unsigned counter = info->counter; + if((counter < 0) || (counter >= IP6T_NTH_NUM_COUNTERS)) + { + printk(KERN_WARNING "nth: invalid counter %u. counter between 0 and %u\n", counter, IP6T_NTH_NUM_COUNTERS-1); + return 0; + }; + + spin_lock(&states[counter].lock); + + /* Are we matching every nth packet?*/ + if (info->packet == 0xFF) + { + /* We're matching every nth packet and only every nth packet*/ + /* Do we match or invert match? */ + if (info->not == 0) + { + if (states[counter].number == 0) + { + ++states[counter].number; + goto match; + } + if (states[counter].number >= info->every) + states[counter].number = 0; /* reset the counter */ + else + ++states[counter].number; + goto dontmatch; + } + else + { + if (states[counter].number == 0) + { + ++states[counter].number; + goto dontmatch; + } + if (states[counter].number >= info->every) + states[counter].number = 0; + else + ++states[counter].number; + goto match; + } + } + else + { + /* We're using the --packet, so there must be a rule for every value */ + if (states[counter].number == info->packet) + { + /* only increment the counter when a match happens */ + if (states[counter].number >= info->every) + states[counter].number = 0; /* reset the counter */ + else + ++states[counter].number; + goto match; + } + else + goto dontmatch; + } + + dontmatch: + /* don't match */ + spin_unlock(&states[counter].lock); + return 0; + + match: + spin_unlock(&states[counter].lock); + return 1; +} + +static int +ip6t_nth_checkentry(const char *tablename, + const struct ip6t_ip6 *e, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + /* Parameters from userspace */ + const struct ip6t_nth_info *info = matchinfo; + unsigned counter = info->counter; + if((counter < 0) || (counter >= IP6T_NTH_NUM_COUNTERS)) + { + printk(KERN_WARNING "nth: invalid counter %u. counter between 0 and %u\n", counter, IP6T_NTH_NUM_COUNTERS-1); + return 0; + }; + + if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_nth_info))) { + printk("nth: matchsize %u != %u\n", matchsize, + IP6T_ALIGN(sizeof(struct ip6t_nth_info))); + return 0; + } + + states[counter].number = info->startat; + + return 1; +} + +static struct ip6t_match ip6t_nth_reg = { + {NULL, NULL}, + "nth", + ip6t_nth_match, + ip6t_nth_checkentry, + NULL, + THIS_MODULE }; + +static int __init init(void) +{ + unsigned counter; + memset(&states, 0, sizeof(states)); + if (ip6t_register_match(&ip6t_nth_reg)) + return -EINVAL; + + for(counter = 0; counter < IP6T_NTH_NUM_COUNTERS; counter++) + { + spin_lock_init(&(states[counter].lock)); + }; + + printk("ip6t_nth match loaded\n"); + return 0; +} + +static void __exit fini(void) +{ + ip6t_unregister_match(&ip6t_nth_reg); + printk("ip6t_nth match unloaded\n"); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.3.org/net/ipv6/netfilter/ip6t_random.c linux-2.6.3/net/ipv6/netfilter/ip6t_random.c --- linux-2.6.3.org/net/ipv6/netfilter/ip6t_random.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.3/net/ipv6/netfilter/ip6t_random.c 2004-02-19 08:47:32.336587163 +0100 @@ -0,0 +1,97 @@ +/* + This is a module which is used for a "random" match support. + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: + ftp://prep.ai.mit.edu/pub/gnu/GPL + + 2001-10-14 Fabrice MARIE : initial implementation. + 2003-04-30 Maciej Soltysiak : IPv6 Port +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +static int +ip6t_rand_match(const struct sk_buff *pskb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + /* Parameters from userspace */ + const struct ip6t_rand_info *info = matchinfo; + u_int8_t random_number; + + /* get 1 random number from the kernel random number generation routine */ + get_random_bytes((void *)(&random_number), 1); + + /* Do we match ? */ + if (random_number <= info->average) + return 1; + else + return 0; +} + +static int +ip6t_rand_checkentry(const char *tablename, + const struct ip6t_ip6 *e, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + /* Parameters from userspace */ + const struct ip6t_rand_info *info = matchinfo; + + if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_rand_info))) { + printk("ip6t_random: matchsize %u != %u\n", matchsize, + IP6T_ALIGN(sizeof(struct ip6t_rand_info))); + return 0; + } + + /* must be 1 <= average % <= 99 */ + /* 1 x 2.55 = 2 */ + /* 99 x 2.55 = 252 */ + if ((info->average < 2) || (info->average > 252)) { + printk("ip6t_random: invalid average %u\n", info->average); + return 0; + } + + return 1; +} + +static struct ip6t_match ip6t_rand_reg = { + {NULL, NULL}, + "random", + ip6t_rand_match, + ip6t_rand_checkentry, + NULL, + THIS_MODULE }; + +static int __init init(void) +{ + if (ip6t_register_match(&ip6t_rand_reg)) + return -EINVAL; + + printk("ip6t_random match loaded\n"); + return 0; +} + +static void __exit fini(void) +{ + ip6t_unregister_match(&ip6t_rand_reg); + printk("ip6t_random match unloaded\n"); +} + +module_init(init); +module_exit(fini);