info about patch: Already applied: updates/01_linux-2.6.4.patch updates/02_iptables-1.2.11-multi.patch updates/02_linux-2.6.5.patch updates/03_linux-2.6.6.patch updates/04_linux-2.6.6-helper_reassign.patch updates/05_linux-2.6.6-orphaned_expect.patch updates/06_linux-2.6.6-skb_len_check.patch updates/07_linux-2.6.7.patch updates/08_linux-2.6.7-user_attr.patch updates/09_linux-2.6.7-ipt_LOG_doff_fix.patch updates/10_linux-2.6.7-tcp_opt.patch pending/CLASSIFY_more-hooks amanda_offset-fix pending/conntrack-acct pending/conntrack-cacheline-opt pending/conntrack-seqfile pending/conntrack_error-api pending/early-drop-norandom pending/expect-evict-order pending/expect-slab-cache pending/init_conntrack-optimize pending/ip_nat_helper_static pending/ip_queue_nonlinear_skbs pending/ipt_helper-invert-fix pending/mangle-reroute pending/nf_reset pending/owner-broken pending/proc_net_conntrack-permissions pending/proc-no-internal-targets pending/sctp base/HOPLIMIT base/IPV4OPTSSTRIP + require fix: s/// in net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c base/NETMAP base/REJECT base/SAME base/TTL base/connlimit base/dstlimit base/fuzzy base/iprange base/ipv4options base/mport base/nf-log base/nth base/osf base/psd base/quota base/raw base/realm base/time base/u32 diff -Nur --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.6.7/include/linux/netfilter_ipv4/ip_conntrack.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ip_conntrack.h 2004-09-07 12:07:45.184838544 +0200 +++ linux-2.6.7/include/linux/netfilter_ipv4/ip_conntrack.h 2004-09-07 12:32:45.881697944 +0200 @@ -156,6 +156,12 @@ union ip_conntrack_expect_help help; }; +struct ip_conntrack_counter +{ + u_int64_t packets; + u_int64_t bytes; +}; + struct ip_conntrack_helper; struct ip_conntrack @@ -164,15 +170,17 @@ plus 1 for any connection(s) we are `master' for */ struct nf_conntrack ct_general; - /* These are my tuples; original and reply */ - struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; - /* Have we seen traffic both ways yet? (bitset) */ unsigned long status; /* Timer function; drops refcnt when it goes off. */ struct timer_list timeout; +#ifdef CONFIG_IP_NF_CT_ACCT + /* Accounting Information (same cache line as other written members) */ + struct ip_conntrack_counter counters[IP_CT_DIR_MAX]; +#endif + /* If we're expecting another related connection, this will be in expected linked list */ struct list_head sibling_list; @@ -207,6 +215,9 @@ } nat; #endif /* CONFIG_IP_NF_NAT_NEEDED */ + /* Traversed often, so hopefully in different cacheline to top */ + /* These are my tuples; original and reply */ + struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; }; /* get master conntrack via master expectation */ @@ -245,8 +256,10 @@ const struct ip_conntrack_tuple *orig); /* Refresh conntrack for this many jiffies */ -extern void ip_ct_refresh(struct ip_conntrack *ct, - unsigned long extra_jiffies); +extern void ip_ct_refresh_acct(struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + const struct sk_buff *skb, + unsigned long extra_jiffies); /* These are for NAT. Icky. */ /* Call me when a conntrack is destroyed. */ diff -Nur --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ip_conntrack_core.h linux-2.6.7/include/linux/netfilter_ipv4/ip_conntrack_core.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ip_conntrack_core.h 2004-09-07 12:07:45.184838544 +0200 +++ linux-2.6.7/include/linux/netfilter_ipv4/ip_conntrack_core.h 2004-09-07 12:32:47.916388624 +0200 @@ -21,15 +21,17 @@ extern struct ip_conntrack_protocol *__ip_ct_find_proto(u_int8_t protocol); extern struct list_head protocol_list; -/* Returns conntrack if it dealt with ICMP, and filled in skb->nfct */ -extern struct ip_conntrack *icmp_error_track(struct sk_buff *skb, - enum ip_conntrack_info *ctinfo, - unsigned int hooknum); -extern int get_tuple(const struct iphdr *iph, - const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_protocol *protocol); +extern int +ip_ct_get_tuple(const struct iphdr *iph, + const struct sk_buff *skb, + unsigned int dataoff, + struct ip_conntrack_tuple *tuple, + const struct ip_conntrack_protocol *protocol); + +extern int +ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse, + const struct ip_conntrack_tuple *orig, + const struct ip_conntrack_protocol *protocol); /* Find a connection corresponding to a tuple. */ struct ip_conntrack_tuple_hash * diff -Nur --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ip_conntrack_protocol.h linux-2.6.7/include/linux/netfilter_ipv4/ip_conntrack_protocol.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ip_conntrack_protocol.h 2004-09-07 12:07:45.184838544 +0200 +++ linux-2.6.7/include/linux/netfilter_ipv4/ip_conntrack_protocol.h 2004-09-07 12:32:47.917388472 +0200 @@ -3,6 +3,11 @@ #define _IP_CONNTRACK_PROTOCOL_H #include +/* length of buffer to which print_tuple/print_conntrack members are + * writing */ + +#define IP_CT_PRINT_BUFLEN 100 + struct ip_conntrack_protocol { /* Next pointer. */ @@ -50,6 +55,9 @@ int (*exp_matches_pkt)(struct ip_conntrack_expect *exp, const struct sk_buff *skb); + int (*error)(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, + unsigned int hooknum); + /* Module (if any) which this is connected to. */ struct module *me; }; @@ -63,4 +71,17 @@ extern struct ip_conntrack_protocol ip_conntrack_protocol_udp; extern struct ip_conntrack_protocol ip_conntrack_protocol_icmp; extern int ip_conntrack_protocol_tcp_init(void); + +/* Log invalid packets */ +extern unsigned int ip_ct_log_invalid; + +#ifdef DEBUG_INVALID_PACKETS +#define LOG_INVALID(proto) \ + (ip_ct_log_invalid == (proto) || ip_ct_log_invalid == IPPROTO_RAW) +#else +#define LOG_INVALID(proto) \ + ((ip_ct_log_invalid == (proto) || ip_ct_log_invalid == IPPROTO_RAW) \ + && net_ratelimit()) +#endif + #endif /*_IP_CONNTRACK_PROTOCOL_H*/ diff -Nur --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ip_nat_helper.h linux-2.6.7/include/linux/netfilter_ipv4/ip_nat_helper.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ip_nat_helper.h 2004-09-07 12:07:45.156842800 +0200 +++ linux-2.6.7/include/linux/netfilter_ipv4/ip_nat_helper.h 2004-09-07 12:32:56.182132040 +0200 @@ -38,11 +38,12 @@ struct ip_nat_info *info); }; -extern struct list_head helpers; - extern int ip_nat_helper_register(struct ip_nat_helper *me); extern void ip_nat_helper_unregister(struct ip_nat_helper *me); +extern struct ip_nat_helper * +ip_nat_find_helper(const struct ip_conntrack_tuple *tuple); + /* These return true or false. */ extern int ip_nat_mangle_tcp_packet(struct sk_buff **skb, struct ip_conntrack *ct, diff -Nur --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ip_tables.h linux-2.6.7/include/linux/netfilter_ipv4/ip_tables.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ip_tables.h 2004-09-07 12:07:45.184838544 +0200 +++ linux-2.6.7/include/linux/netfilter_ipv4/ip_tables.h 2004-09-07 12:32:37.051040408 +0200 @@ -336,6 +336,7 @@ * Main firewall chains definitions and global var's definitions. */ #ifdef __KERNEL__ +extern struct semaphore ipt_mutex; static DECLARE_MUTEX(ipt_mutex); #include diff -Nur --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_TTL.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_TTL.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_TTL.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_TTL.h 2004-09-07 12:33:09.040177320 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_connlimit.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_connlimit.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_connlimit.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_connlimit.h 2004-09-07 12:33:10.090017720 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_dstlimit.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_dstlimit.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_dstlimit.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_dstlimit.h 2004-09-07 12:33:11.098864352 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_fuzzy.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_fuzzy.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_fuzzy.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_fuzzy.h 2004-09-07 12:33:12.079715240 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_ipv4options.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_ipv4options.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_ipv4options.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_ipv4options.h 2004-09-07 12:33:15.333220632 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_mport.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_mport.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_mport.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_mport.h 2004-09-07 12:33:16.383061032 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_nth.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_nth.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_nth.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_nth.h 2004-09-07 12:33:17.546884104 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_osf.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_osf.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_osf.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_osf.h 2004-09-07 12:33:18.674712648 +0200 @@ -0,0 +1,149 @@ +/* + * 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_NETLINK 8 + +#define IPT_OSF_LOGLEVEL_ALL 0 +#define IPT_OSF_LOGLEVEL_FIRST 1 + +#include +#include + +#ifndef __KERNEL__ +#include +#include + +struct list_head +{ + struct list_head *prev, *next; +}; +#endif + +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; +}; + +struct osf_finger +{ + struct list_head flist; + struct osf_wc wss; + unsigned char ttl; + unsigned char df; + unsigned long ss; + unsigned char genre[MAXGENRELEN]; + unsigned char version[MAXGENRELEN], subtype[MAXGENRELEN]; + + /* Not needed, but for consistency with original table from Michal Zalewski */ + unsigned char details[MAXDETLEN]; + + int opt_num; + struct osf_opt opt[MAX_IPOPTLEN]; /* In case it is all NOP or EOL */ + +}; + +struct ipt_osf_nlmsg +{ + struct osf_finger f; + struct iphdr ip; + struct tcphdr tcp; +}; + +#ifdef __KERNEL__ + +/* 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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_psd.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_psd.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_psd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_psd.h 2004-09-07 12:33:19.723553200 +0200 @@ -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 (300) /* old usage of HZ here was erroneously and broke under uml */ + +/* + * 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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_quota.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_quota.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_quota.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_quota.h 2004-09-07 12:33:20.689406368 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_sctp.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_sctp.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_sctp.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_sctp.h 2004-09-07 12:33:05.389732272 +0200 @@ -0,0 +1,107 @@ +#ifndef _IPT_SCTP_H_ +#define _IPT_SCTP_H_ + +#define IPT_SCTP_SRC_PORTS 0x01 +#define IPT_SCTP_DEST_PORTS 0x02 +#define IPT_SCTP_CHUNK_TYPES 0x04 + +#define IPT_SCTP_VALID_FLAGS 0x07 + +#define ELEMCOUNT(x) (sizeof(x)/sizeof(x[0])) + + +struct ipt_sctp_flag_info { + u_int8_t chunktype; + u_int8_t flag; + u_int8_t flag_mask; +}; + +#define IPT_NUM_SCTP_FLAGS 4 + +struct ipt_sctp_info { + u_int16_t dpts[2]; /* Min, Max */ + u_int16_t spts[2]; /* Min, Max */ + + u_int32_t chunkmap[256 / sizeof (u_int32_t)]; /* Bit mask of chunks to be matched according to RFC 2960 */ + +#define SCTP_CHUNK_MATCH_ANY 0x01 /* Match if any of the chunk types are present */ +#define SCTP_CHUNK_MATCH_ALL 0x02 /* Match if all of the chunk types are present */ +#define SCTP_CHUNK_MATCH_ONLY 0x04 /* Match if these are the only chunk types present */ + + u_int32_t chunk_match_type; + struct ipt_sctp_flag_info flag_info[IPT_NUM_SCTP_FLAGS]; + int flag_count; + + u_int32_t flags; + u_int32_t invflags; +}; + +#define bytes(type) (sizeof(type) * 8) + +#define SCTP_CHUNKMAP_SET(chunkmap, type) \ + do { \ + chunkmap[type / bytes(u_int32_t)] |= \ + 1 << (type % bytes(u_int32_t)); \ + } while (0) + +#define SCTP_CHUNKMAP_CLEAR(chunkmap, type) \ + do { \ + chunkmap[type / bytes(u_int32_t)] &= \ + ~(1 << (type % bytes(u_int32_t))); \ + } while (0) + +#define SCTP_CHUNKMAP_IS_SET(chunkmap, type) \ +({ \ + (chunkmap[type / bytes (u_int32_t)] & \ + (1 << (type % bytes (u_int32_t)))) ? 1: 0; \ +}) + +#define SCTP_CHUNKMAP_RESET(chunkmap) \ + do { \ + int i; \ + for (i = 0; i < ELEMCOUNT(chunkmap); i++) \ + chunkmap[i] = 0; \ + } while (0) + +#define SCTP_CHUNKMAP_SET_ALL(chunkmap) \ + do { \ + int i; \ + for (i = 0; i < ELEMCOUNT(chunkmap); i++) \ + chunkmap[i] = ~0; \ + } while (0) + +#define SCTP_CHUNKMAP_COPY(destmap, srcmap) \ + do { \ + int i; \ + for (i = 0; i < ELEMCOUNT(chunkmap); i++) \ + destmap[i] = srcmap[i]; \ + } while (0) + +#define SCTP_CHUNKMAP_IS_CLEAR(chunkmap) \ +({ \ + int i; \ + int flag = 1; \ + for (i = 0; i < ELEMCOUNT(chunkmap); i++) { \ + if (chunkmap[i]) { \ + flag = 0; \ + break; \ + } \ + } \ + flag; \ +}) + +#define SCTP_CHUNKMAP_IS_ALL_SET(chunkmap) \ +({ \ + int i; \ + int flag = 1; \ + for (i = 0; i < ELEMCOUNT(chunkmap); i++) { \ + if (chunkmap[i] != ~0) { \ + flag = 0; \ + break; \ + } \ + } \ + flag; \ +}) + +#endif /* _IPT_SCTP_H_ */ + diff -Nur --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_time.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_time.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_time.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_time.h 2004-09-07 12:33:22.072196152 +0200 @@ -0,0 +1,15 @@ +#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. */ + time_t date_start; + time_t date_stop; +}; + + +#endif /* __ipt_time_h_included__ */ diff -Nur --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_u32.h linux-2.6.7/include/linux/netfilter_ipv4/ipt_u32.h --- linux-2.6.7.org/include/linux/netfilter_ipv4/ipt_u32.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv4/ipt_u32.h 2004-09-07 12:38:33.376870648 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv6/ip6t_HL.h linux-2.6.7/include/linux/netfilter_ipv6/ip6t_HL.h --- linux-2.6.7.org/include/linux/netfilter_ipv6/ip6t_HL.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv6/ip6t_HL.h 2004-09-07 12:33:06.516560968 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv6/ip6t_REJECT.h linux-2.6.7/include/linux/netfilter_ipv6/ip6t_REJECT.h --- linux-2.6.7.org/include/linux/netfilter_ipv6/ip6t_REJECT.h 2004-09-07 12:07:45.155842952 +0200 +++ linux-2.6.7/include/linux/netfilter_ipv6/ip6t_REJECT.h 2004-09-07 12:33:08.262295576 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv6/ip6t_fuzzy.h linux-2.6.7/include/linux/netfilter_ipv6/ip6t_fuzzy.h --- linux-2.6.7.org/include/linux/netfilter_ipv6/ip6t_fuzzy.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv6/ip6t_fuzzy.h 2004-09-07 12:33:12.080715088 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/netfilter_ipv6/ip6t_nth.h linux-2.6.7/include/linux/netfilter_ipv6/ip6t_nth.h --- linux-2.6.7.org/include/linux/netfilter_ipv6/ip6t_nth.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/include/linux/netfilter_ipv6/ip6t_nth.h 2004-09-07 12:33:17.547883952 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/include/linux/sysctl.h linux-2.6.7/include/linux/sysctl.h --- linux-2.6.7.org/include/linux/sysctl.h 2004-09-07 12:07:45.225832312 +0200 +++ linux-2.6.7/include/linux/sysctl.h 2004-09-07 12:32:47.918388320 +0200 @@ -410,6 +410,7 @@ NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT=12, NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT=13, NET_IPV4_NF_CONNTRACK_BUCKETS=14, + NET_IPV4_NF_CONNTRACK_LOG_INVALID=15, }; /* /proc/sys/net/ipv6 */ diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/Kconfig linux-2.6.7/net/ipv4/netfilter/Kconfig --- linux-2.6.7.org/net/ipv4/netfilter/Kconfig 2004-09-07 12:07:46.566628480 +0200 +++ linux-2.6.7/net/ipv4/netfilter/Kconfig 2004-09-07 12:38:33.380870040 +0200 @@ -628,5 +628,174 @@ If you want to compile it as a module, say M here and read Documentation/modules.txt. If unsure, say `N'. +config IP_NF_CT_ACCT + bool "Connection tracking flow accounting" + depends on IP_NF_CONNTRACK + +config IP_NF_MATCH_SCTP + tristate 'SCTP protocol match support' + depends on IP_NF_IPTABLES + +config IP_NF_TARGET_IPV4OPTSSTRIP + tristate 'IPV4OPTSSTRIP target support' + depends on IP_NF_MANGLE + help + This option adds an IPV4OPTSSTRIP target. + This target allows you to strip all IP options in a packet. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +config IP_NF_TARGET_TTL + tristate 'TTL target support' + depends on IP_NF_MANGLE + help + This option adds a `TTL' target, which enables the user to set + the TTL value or increment / decrement the TTL value by a given + amount. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +config IP_NF_MATCH_CONNLIMIT + tristate 'Connections/IP limit match support' + depends on IP_NF_IPTABLES + help + This match allows you to restrict the number of parallel TCP + connections to a server per client IP address (or address block). + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +config IP_NF_MATCH_DSTLIMIT + tristate 'dstlimit match support' + depends on IP_NF_IPTABLES + +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 fuzzy logic based law. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +config IP_NF_MATCH_IPV4OPTIONS + tristate 'IPV4OPTIONS match support' + depends on IP_NF_IPTABLES + help + This option adds a IPV4OPTIONS match. + It allows you to filter options like source routing, + record route, timestamp and router-altert. + + If you say Y here, try iptables -m ipv4options --help for more information. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +config IP_NF_MATCH_MPORT + tristate 'Multiple port with ranges match support' + depends on IP_NF_IPTABLES + help + This is an enhanced multiport match which supports port + ranges as well as single ports. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +config IP_NF_MATCH_NTH + tristate 'Nth match support' + depends on IP_NF_IPTABLES + help + This option adds a `Nth' match, which allow you to make + rules that match every Nth packet. By default there are + 16 different counters. + + [options] + --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. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +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. + + If you say Y here, try iptables -m osf --help for more information. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +config IP_NF_MATCH_PSD + tristate 'psd match support' + depends on IP_NF_IPTABLES + help + This option adds a `psd' match, which allows you to create rules in + any iptables table wich will detect TCP and UDP port scans. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +config IP_NF_MATCH_QUOTA + tristate 'quota match support' + depends on IP_NF_IPTABLES + help + This match implements network quotas. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + + +config IP_NF_MATCH_TIME + tristate 'TIME match support' + depends on IP_NF_IPTABLES + help + This option adds a `time' match, which allows you + to match based on the packet arrival time/date + (arrival time/date at the machine which netfilter is running on) or + departure time/date (for locally generated packets). + + If you say Y here, try iptables -m time --help for more information. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +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. + endmenu diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/Makefile linux-2.6.7/net/ipv4/netfilter/Makefile --- linux-2.6.7.org/net/ipv4/netfilter/Makefile 2004-09-07 12:07:46.565628632 +0200 +++ linux-2.6.7/net/ipv4/netfilter/Makefile 2004-09-07 12:38:33.381869888 +0200 @@ -43,15 +43,36 @@ # 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_SCTP) += ipt_sctp.o obj-$(CONFIG_IP_NF_MATCH_MARK) += ipt_mark.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_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 @@ -60,8 +81,12 @@ 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 @@ -82,6 +107,8 @@ 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_IPV4OPTSSTRIP) += ipt_IPV4OPTSSTRIP.o obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt_TCPMSS.o obj-$(CONFIG_IP_NF_TARGET_NOTRACK) += ipt_NOTRACK.o diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_amanda.c linux-2.6.7/net/ipv4/netfilter/ip_conntrack_amanda.c --- linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_amanda.c 2004-09-07 12:07:46.566628480 +0200 +++ linux-2.6.7/net/ipv4/netfilter/ip_conntrack_amanda.c 2004-09-07 12:32:44.773866360 +0200 @@ -58,7 +58,7 @@ /* increase the UDP timeout of the master connection as replies from * Amanda clients to the server can be quite delayed */ - ip_ct_refresh(ct, master_timeout * HZ); + ip_ct_refresh_acct(ct, ctinfo, NULL, master_timeout * HZ); /* No data? */ dataoff = skb->nh.iph->ihl*4 + sizeof(struct udphdr); diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_core.c linux-2.6.7/net/ipv4/netfilter/ip_conntrack_core.c --- linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_core.c 2004-09-07 12:07:46.565628632 +0200 +++ linux-2.6.7/net/ipv4/netfilter/ip_conntrack_core.c 2004-09-07 12:32:54.736351832 +0200 @@ -58,6 +58,7 @@ DECLARE_RWLOCK(ip_conntrack_lock); DECLARE_RWLOCK(ip_conntrack_expect_tuple_lock); +static atomic_t ip_conntrack_count = ATOMIC_INIT(0); void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL; LIST_HEAD(ip_conntrack_expect_list); @@ -65,9 +66,9 @@ static LIST_HEAD(helpers); unsigned int ip_conntrack_htable_size = 0; int ip_conntrack_max; -static atomic_t ip_conntrack_count = ATOMIC_INIT(0); struct list_head *ip_conntrack_hash; static kmem_cache_t *ip_conntrack_cachep; +static kmem_cache_t *ip_conntrack_expect_cachep; struct ip_conntrack ip_conntrack_untracked; extern struct ip_conntrack_protocol ip_conntrack_generic_protocol; @@ -127,11 +128,11 @@ } int -get_tuple(const struct iphdr *iph, - const struct sk_buff *skb, - unsigned int dataoff, - struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_protocol *protocol) +ip_ct_get_tuple(const struct iphdr *iph, + const struct sk_buff *skb, + unsigned int dataoff, + struct ip_conntrack_tuple *tuple, + const struct ip_conntrack_protocol *protocol) { /* Never happen */ if (iph->frag_off & htons(IP_OFFSET)) { @@ -147,10 +148,10 @@ return protocol->pkt_to_tuple(skb, dataoff, tuple); } -static int -invert_tuple(struct ip_conntrack_tuple *inverse, - const struct ip_conntrack_tuple *orig, - const struct ip_conntrack_protocol *protocol) +int +ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse, + const struct ip_conntrack_tuple *orig, + const struct ip_conntrack_protocol *protocol) { inverse->src.ip = orig->dst.ip; inverse->dst.ip = orig->src.ip; @@ -177,7 +178,7 @@ IP_NF_ASSERT(atomic_read(&exp->use) == 0); IP_NF_ASSERT(!timer_pending(&exp->timeout)); - kfree(exp); + kmem_cache_free(ip_conntrack_expect_cachep, exp); } inline void ip_conntrack_expect_put(struct ip_conntrack_expect *exp) @@ -336,7 +337,7 @@ list_del(&ct->master->expected_list); master = ct->master->expectant; } - kfree(ct->master); + kmem_cache_free(ip_conntrack_expect_cachep, ct->master); } WRITE_UNLOCK(&ip_conntrack_lock); @@ -496,83 +497,6 @@ return h != NULL; } -/* Returns conntrack if it dealt with ICMP, and filled in skb fields */ -struct ip_conntrack * -icmp_error_track(struct sk_buff *skb, - enum ip_conntrack_info *ctinfo, - unsigned int hooknum) -{ - struct ip_conntrack_tuple innertuple, origtuple; - struct { - struct icmphdr icmp; - struct iphdr ip; - } inside; - struct ip_conntrack_protocol *innerproto; - struct ip_conntrack_tuple_hash *h; - int dataoff; - - IP_NF_ASSERT(skb->nfct == NULL); - - /* Not enough header? */ - if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &inside, sizeof(inside))!=0) - return NULL; - - if (inside.icmp.type != ICMP_DEST_UNREACH - && inside.icmp.type != ICMP_SOURCE_QUENCH - && inside.icmp.type != ICMP_TIME_EXCEEDED - && inside.icmp.type != ICMP_PARAMETERPROB - && inside.icmp.type != ICMP_REDIRECT) - return NULL; - - /* Ignore ICMP's containing fragments (shouldn't happen) */ - if (inside.ip.frag_off & htons(IP_OFFSET)) { - DEBUGP("icmp_error_track: fragment of proto %u\n", - inside.ip.protocol); - return NULL; - } - - innerproto = ip_ct_find_proto(inside.ip.protocol); - dataoff = skb->nh.iph->ihl*4 + sizeof(inside.icmp) + inside.ip.ihl*4; - /* Are they talking about one of our connections? */ - if (!get_tuple(&inside.ip, skb, dataoff, &origtuple, innerproto)) { - DEBUGP("icmp_error: ! get_tuple p=%u", inside.ip.protocol); - return NULL; - } - - /* Ordinarily, we'd expect the inverted tupleproto, but it's - been preserved inside the ICMP. */ - if (!invert_tuple(&innertuple, &origtuple, innerproto)) { - DEBUGP("icmp_error_track: Can't invert tuple\n"); - return NULL; - } - - *ctinfo = IP_CT_RELATED; - - h = ip_conntrack_find_get(&innertuple, NULL); - if (!h) { - /* Locally generated ICMPs will match inverted if they - haven't been SNAT'ed yet */ - /* FIXME: NAT code has to handle half-done double NAT --RR */ - if (hooknum == NF_IP_LOCAL_OUT) - h = ip_conntrack_find_get(&origtuple, NULL); - - if (!h) { - DEBUGP("icmp_error_track: no match\n"); - return NULL; - } - /* Reverse direction from that found */ - if (DIRECTION(h) != IP_CT_DIR_REPLY) - *ctinfo += IP_CT_IS_REPLY; - } else { - if (DIRECTION(h) == IP_CT_DIR_REPLY) - *ctinfo += IP_CT_IS_REPLY; - } - - /* Update skb to refer to this connection */ - skb->nfct = &h->ctrack->infos[*ctinfo]; - return h->ctrack; -} - /* There's a small race here where we may free a just-assured connection. Too bad: we're in trouble anyway. */ static inline int unreplied(const struct ip_conntrack_tuple_hash *i) @@ -628,7 +552,6 @@ size_t hash; struct ip_conntrack_expect *expected; int i; - static unsigned int drop_next; if (!ip_conntrack_hash_rnd_initted) { get_random_bytes(&ip_conntrack_hash_rnd, 4); @@ -637,15 +560,10 @@ hash = hash_conntrack(tuple); - if (ip_conntrack_max && - atomic_read(&ip_conntrack_count) >= ip_conntrack_max) { - /* Try dropping from random chain, or else from the - chain about to put into (in case they're trying to - bomb one hash chain). */ - unsigned int next = (drop_next++)%ip_conntrack_htable_size; - - if (!early_drop(&ip_conntrack_hash[next]) - && !early_drop(&ip_conntrack_hash[hash])) { + if (ip_conntrack_max + && atomic_read(&ip_conntrack_count) >= ip_conntrack_max) { + /* Try dropping from this hash chain. */ + if (!early_drop(&ip_conntrack_hash[hash])) { if (net_ratelimit()) printk(KERN_WARNING "ip_conntrack: table full, dropping" @@ -654,7 +572,7 @@ } } - if (!invert_tuple(&repl_tuple, tuple, protocol)) { + if (!ip_ct_invert_tuple(&repl_tuple, tuple, protocol)) { DEBUGP("Can't invert tuple.\n"); return NULL; } @@ -693,41 +611,48 @@ struct ip_conntrack_expect *, tuple); READ_UNLOCK(&ip_conntrack_expect_tuple_lock); - /* If master is not in hash table yet (ie. packet hasn't left - this machine yet), how can other end know about expected? - Hence these are not the droids you are looking for (if - master ct never got confirmed, we'd hold a reference to it - and weird things would happen to future packets). */ - if (expected && !is_confirmed(expected->expectant)) - expected = NULL; - - /* Look up the conntrack helper for master connections only */ - if (!expected) - conntrack->helper = ip_ct_find_helper(&repl_tuple); + if (expected) { + /* If master is not in hash table yet (ie. packet hasn't left + this machine yet), how can other end know about expected? + Hence these are not the droids you are looking for (if + master ct never got confirmed, we'd hold a reference to it + and weird things would happen to future packets). */ + if (!is_confirmed(expected->expectant)) { + conntrack->helper = ip_ct_find_helper(&repl_tuple); + goto end; + } - /* If the expectation is dying, then this is a loser. */ - if (expected - && expected->expectant->helper->timeout - && ! del_timer(&expected->timeout)) - expected = NULL; + /* Expectation is dying... */ + if (expected->expectant->helper->timeout + && !del_timer(&expected->timeout)) + goto end; - if (expected) { DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", conntrack, expected); /* Welcome, Mr. Bond. We've been expecting you... */ + IP_NF_ASSERT(master_ct(conntrack)); __set_bit(IPS_EXPECTED_BIT, &conntrack->status); conntrack->master = expected; expected->sibling = conntrack; LIST_DELETE(&ip_conntrack_expect_list, expected); expected->expectant->expecting--; nf_conntrack_get(&master_ct(conntrack)->infos[0]); - } - atomic_inc(&ip_conntrack_count); + + /* this is a braindead... --pablo */ + atomic_inc(&ip_conntrack_count); + WRITE_UNLOCK(&ip_conntrack_lock); + + if (expected->expectfn) + expected->expectfn(conntrack); + + goto ret; + } else + conntrack->helper = ip_ct_find_helper(&repl_tuple); + +end: atomic_inc(&ip_conntrack_count); WRITE_UNLOCK(&ip_conntrack_lock); - if (expected && expected->expectfn) - expected->expectfn(conntrack); - return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL]; +ret: return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL]; } /* On success, returns conntrack ptr, sets skb->nfct and ctinfo */ @@ -743,7 +668,8 @@ IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0); - if (!get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4, &tuple, proto)) + if (!ip_ct_get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4, + &tuple,proto)) return NULL; /* look for tuple match */ @@ -828,10 +754,12 @@ proto = ip_ct_find_proto((*pskb)->nh.iph->protocol); - /* It may be an icmp error... */ - if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP - && icmp_error_track(*pskb, &ctinfo, hooknum)) - return NF_ACCEPT; + /* It may be an special packet, error, unclean... + * inverse of the return code tells to the netfilter + * core what to do with the packet. */ + if (proto->error != NULL + && (ret = proto->error(*pskb, &ctinfo, hooknum)) <= 0) + return -ret; if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) /* Not valid part of a connection */ @@ -869,7 +797,8 @@ int invert_tuplepr(struct ip_conntrack_tuple *inverse, const struct ip_conntrack_tuple *orig) { - return invert_tuple(inverse, orig, ip_ct_find_proto(orig->dst.protonum)); + return ip_ct_invert_tuple(inverse, orig, + ip_ct_find_proto(orig->dst.protonum)); } static inline int resent_expect(const struct ip_conntrack_expect *i, @@ -923,9 +852,8 @@ ip_conntrack_expect_alloc(void) { struct ip_conntrack_expect *new; - - new = (struct ip_conntrack_expect *) - kmalloc(sizeof(struct ip_conntrack_expect), GFP_ATOMIC); + + new = kmem_cache_alloc(ip_conntrack_expect_cachep, GFP_ATOMIC); if (!new) { DEBUGP("expect_related: OOM allocating expect\n"); return NULL; @@ -933,6 +861,7 @@ /* tuple_cmp compares whole union, we have to initialized cleanly */ memset(new, 0, sizeof(struct ip_conntrack_expect)); + atomic_set(&new->use, 1); return new; } @@ -944,7 +873,6 @@ DEBUGP("new expectation %p of conntrack %p\n", new, related_to); new->expectant = related_to; new->sibling = NULL; - atomic_set(&new->use, 1); /* add to expected list for this connection */ list_add_tail(&new->expected_list, &related_to->sibling_list); @@ -997,7 +925,8 @@ } WRITE_UNLOCK(&ip_conntrack_lock); - kfree(expect); + /* This expectation is not inserted so no need to lock */ + kmem_cache_free(ip_conntrack_expect_cachep, expect); return -EEXIST; } else if (related_to->helper->max_expected && @@ -1015,7 +944,7 @@ related_to->helper->name, NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip)); - kfree(expect); + kmem_cache_free(ip_conntrack_expect_cachep, expect); return -EPERM; } DEBUGP("ip_conntrack: max number of expected " @@ -1049,7 +978,7 @@ WRITE_UNLOCK(&ip_conntrack_lock); DEBUGP("expect_related: busy!\n"); - kfree(expect); + kmem_cache_free(ip_conntrack_expect_cachep, expect); return -EBUSY; } @@ -1164,21 +1093,39 @@ synchronize_net(); } -/* Refresh conntrack for this many jiffies. */ -void ip_ct_refresh(struct ip_conntrack *ct, unsigned long extra_jiffies) +static inline void ct_add_counters(struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + const struct sk_buff *skb) +{ +#ifdef CONFIG_IP_NF_CT_ACCT + if (skb) { + ct->counters[CTINFO2DIR(ctinfo)].packets++; + ct->counters[CTINFO2DIR(ctinfo)].bytes += + ntohs(skb->nh.iph->tot_len); + } +#endif +} + +/* Refresh conntrack for this many jiffies and do accounting (if skb != NULL) */ +void ip_ct_refresh_acct(struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + const struct sk_buff *skb, + unsigned long extra_jiffies) { IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct); /* If not in hash table, timer will not be active yet */ - if (!is_confirmed(ct)) + if (!is_confirmed(ct)) { ct->timeout.expires = extra_jiffies; - else { + ct_add_counters(ct, ctinfo, skb); + } else { WRITE_LOCK(&ip_conntrack_lock); /* Need del_timer for race avoidance (may already be dying). */ if (del_timer(&ct->timeout)) { ct->timeout.expires = jiffies + extra_jiffies; add_timer(&ct->timeout); } + ct_add_counters(ct, ctinfo, skb); WRITE_UNLOCK(&ip_conntrack_lock); } } @@ -1368,6 +1315,7 @@ } kmem_cache_destroy(ip_conntrack_cachep); + kmem_cache_destroy(ip_conntrack_expect_cachep); vfree(ip_conntrack_hash); nf_unregister_sockopt(&so_getorigdst); } @@ -1420,6 +1368,15 @@ printk(KERN_ERR "Unable to create ip_conntrack slab cache\n"); goto err_free_hash; } + + ip_conntrack_expect_cachep = kmem_cache_create("ip_conntrack_expect", + sizeof(struct ip_conntrack_expect), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!ip_conntrack_expect_cachep) { + printk(KERN_ERR "Unable to create ip_expect slab cache\n"); + goto err_free_conntrack_slab; + } + /* Don't NEED lock here, but good form anyway. */ WRITE_LOCK(&ip_conntrack_lock); /* Sew in builtin protocols. */ @@ -1447,6 +1404,8 @@ return ret; +err_free_conntrack_slab: + kmem_cache_destroy(ip_conntrack_cachep); err_free_hash: vfree(ip_conntrack_hash); err_unreg_sockopt: diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_proto_generic.c linux-2.6.7/net/ipv4/netfilter/ip_conntrack_proto_generic.c --- linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_proto_generic.c 2004-09-07 12:07:46.565628632 +0200 +++ linux-2.6.7/net/ipv4/netfilter/ip_conntrack_proto_generic.c 2004-09-07 12:32:47.921387864 +0200 @@ -50,9 +50,9 @@ /* Returns verdict for packet, or -1 for invalid. */ static int packet(struct ip_conntrack *conntrack, const struct sk_buff *skb, - enum ip_conntrack_info conntrackinfo) + enum ip_conntrack_info ctinfo) { - ip_ct_refresh(conntrack, ip_ct_generic_timeout); + ip_ct_refresh_acct(conntrack, ctinfo, skb, ip_ct_generic_timeout); return NF_ACCEPT; } @@ -62,8 +62,14 @@ return 1; } -struct ip_conntrack_protocol ip_conntrack_generic_protocol -= { { NULL, NULL }, 0, "unknown", - generic_pkt_to_tuple, generic_invert_tuple, generic_print_tuple, - generic_print_conntrack, packet, new, NULL, NULL, NULL }; - +struct ip_conntrack_protocol ip_conntrack_generic_protocol = +{ + .proto = 0, + .name = "unknown", + .pkt_to_tuple = generic_pkt_to_tuple, + .invert_tuple = generic_invert_tuple, + .print_tuple = generic_print_tuple, + .print_conntrack = generic_print_conntrack, + .packet = packet, + .new = new, +}; diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_proto_icmp.c linux-2.6.7/net/ipv4/netfilter/ip_conntrack_proto_icmp.c --- linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_proto_icmp.c 2004-09-07 12:07:46.565628632 +0200 +++ linux-2.6.7/net/ipv4/netfilter/ip_conntrack_proto_icmp.c 2004-09-07 12:32:47.930386496 +0200 @@ -12,6 +12,11 @@ #include #include #include +#include +#include +#include +#include +#include #include unsigned long ip_ct_icmp_timeout = 30*HZ; @@ -94,7 +99,7 @@ ct->timeout.function((unsigned long)ct); } else { atomic_inc(&ct->proto.icmp.count); - ip_ct_refresh(ct, ip_ct_icmp_timeout); + ip_ct_refresh_acct(ct, ctinfo, skb, ip_ct_icmp_timeout); } return NF_ACCEPT; @@ -122,7 +127,147 @@ return 1; } -struct ip_conntrack_protocol ip_conntrack_protocol_icmp -= { { NULL, NULL }, IPPROTO_ICMP, "icmp", - icmp_pkt_to_tuple, icmp_invert_tuple, icmp_print_tuple, - icmp_print_conntrack, icmp_packet, icmp_new, NULL, NULL, NULL }; +static int +icmp_error_message(struct sk_buff *skb, + enum ip_conntrack_info *ctinfo, + unsigned int hooknum) +{ + struct ip_conntrack_tuple innertuple, origtuple; + struct { + struct icmphdr icmp; + struct iphdr ip; + } inside; + struct ip_conntrack_protocol *innerproto; + struct ip_conntrack_tuple_hash *h; + int dataoff; + + IP_NF_ASSERT(skb->nfct == NULL); + + /* Not enough header? */ + if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &inside, sizeof(inside))!=0) + return NF_ACCEPT; + + /* Ignore ICMP's containing fragments (shouldn't happen) */ + if (inside.ip.frag_off & htons(IP_OFFSET)) { + DEBUGP("icmp_error_track: fragment of proto %u\n", + inside.ip.protocol); + return NF_ACCEPT; + } + + innerproto = ip_ct_find_proto(inside.ip.protocol); + dataoff = skb->nh.iph->ihl*4 + sizeof(inside.icmp) + inside.ip.ihl*4; + /* Are they talking about one of our connections? */ + if (!ip_ct_get_tuple(&inside.ip, skb, dataoff, &origtuple, innerproto)) { + DEBUGP("icmp_error: ! get_tuple p=%u", inside.ip.protocol); + return NF_ACCEPT; + } + + /* Ordinarily, we'd expect the inverted tupleproto, but it's + been preserved inside the ICMP. */ + if (!ip_ct_invert_tuple(&innertuple, &origtuple, innerproto)) { + DEBUGP("icmp_error_track: Can't invert tuple\n"); + return NF_ACCEPT; + } + + *ctinfo = IP_CT_RELATED; + + h = ip_conntrack_find_get(&innertuple, NULL); + if (!h) { + /* Locally generated ICMPs will match inverted if they + haven't been SNAT'ed yet */ + /* FIXME: NAT code has to handle half-done double NAT --RR */ + if (hooknum == NF_IP_LOCAL_OUT) + h = ip_conntrack_find_get(&origtuple, NULL); + + if (!h) { + DEBUGP("icmp_error_track: no match\n"); + return NF_ACCEPT; + } + /* Reverse direction from that found */ + if (DIRECTION(h) != IP_CT_DIR_REPLY) + *ctinfo += IP_CT_IS_REPLY; + } else { + if (DIRECTION(h) == IP_CT_DIR_REPLY) + *ctinfo += IP_CT_IS_REPLY; + } + + /* Update skb to refer to this connection */ + skb->nfct = &h->ctrack->infos[*ctinfo]; + return -NF_ACCEPT; +} + +/* Small and modified version of icmp_rcv */ +static int +icmp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, + unsigned int hooknum) +{ + struct icmphdr icmph; + + /* Not enough header? */ + if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &icmph, sizeof(icmph))!=0) { + if (LOG_INVALID(IPPROTO_ICMP)) + nf_log_packet(PF_INET, 0, skb, NULL, NULL, + "ip_ct_icmp: short packet "); + return -NF_ACCEPT; + } + + /* See ip_conntrack_proto_tcp.c */ + if (hooknum != NF_IP_PRE_ROUTING) + goto checksum_skipped; + + switch (skb->ip_summed) { + case CHECKSUM_HW: + if (!(u16)csum_fold(skb->csum)) + break; + if (LOG_INVALID(IPPROTO_ICMP)) + nf_log_packet(PF_INET, 0, skb, NULL, NULL, + "ip_ct_icmp: bad HW ICMP checksum "); + return -NF_ACCEPT; + case CHECKSUM_NONE: + if ((u16)csum_fold(skb_checksum(skb, 0, skb->len, 0))) { + if (LOG_INVALID(IPPROTO_ICMP)) + nf_log_packet(PF_INET, 0, skb, NULL, NULL, + "ip_ct_icmp: bad ICMP checksum "); + return -NF_ACCEPT; + } + default: + break; + } + +checksum_skipped: + /* + * 18 is the highest 'known' ICMP type. Anything else is a mystery + * + * RFC 1122: 3.2.2 Unknown ICMP messages types MUST be silently + * discarded. + */ + if (icmph.type > NR_ICMP_TYPES) { + if (LOG_INVALID(IPPROTO_ICMP)) + nf_log_packet(PF_INET, 0, skb, NULL, NULL, + "ip_ct_icmp: invalid ICMP type "); + return -NF_ACCEPT; + } + + /* Need to track icmp error message? */ + if (icmph.type != ICMP_DEST_UNREACH + && icmph.type != ICMP_SOURCE_QUENCH + && icmph.type != ICMP_TIME_EXCEEDED + && icmph.type != ICMP_PARAMETERPROB + && icmph.type != ICMP_REDIRECT) + return NF_ACCEPT; + + return icmp_error_message(skb, ctinfo, hooknum); +} + +struct ip_conntrack_protocol ip_conntrack_protocol_icmp = +{ + .proto = IPPROTO_ICMP, + .name = "icmp", + .pkt_to_tuple = icmp_pkt_to_tuple, + .invert_tuple = icmp_invert_tuple, + .print_tuple = icmp_print_tuple, + .print_conntrack = icmp_print_conntrack, + .packet = icmp_packet, + .new = icmp_new, + .error = icmp_error, +}; diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_proto_tcp.c linux-2.6.7/net/ipv4/netfilter/ip_conntrack_proto_tcp.c --- linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_proto_tcp.c 2004-09-07 12:07:46.566628480 +0200 +++ linux-2.6.7/net/ipv4/netfilter/ip_conntrack_proto_tcp.c 2004-09-07 12:32:47.922387712 +0200 @@ -225,7 +225,7 @@ set_bit(IPS_ASSURED_BIT, &conntrack->status); out: WRITE_UNLOCK(&tcp_lock); - ip_ct_refresh(conntrack, *tcp_timeouts[newconntrack]); + ip_ct_refresh_acct(conntrack, ctinfo, skb, *tcp_timeouts[newconntrack]); return NF_ACCEPT; } @@ -268,7 +268,15 @@ return between(exp->seq, ntohl(tcph.seq), ntohl(tcph.seq) + datalen); } -struct ip_conntrack_protocol ip_conntrack_protocol_tcp -= { { NULL, NULL }, IPPROTO_TCP, "tcp", - tcp_pkt_to_tuple, tcp_invert_tuple, tcp_print_tuple, tcp_print_conntrack, - tcp_packet, tcp_new, NULL, tcp_exp_matches_pkt, NULL }; +struct ip_conntrack_protocol ip_conntrack_protocol_tcp = +{ + .proto = IPPROTO_TCP, + .name = "tcp", + .pkt_to_tuple = tcp_pkt_to_tuple, + .invert_tuple = tcp_invert_tuple, + .print_tuple = tcp_print_tuple, + .print_conntrack = tcp_print_conntrack, + .packet = tcp_packet, + .new = tcp_new, + .exp_matches_pkt = tcp_exp_matches_pkt, +}; diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_proto_udp.c linux-2.6.7/net/ipv4/netfilter/ip_conntrack_proto_udp.c --- linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_proto_udp.c 2004-09-07 12:07:46.565628632 +0200 +++ linux-2.6.7/net/ipv4/netfilter/ip_conntrack_proto_udp.c 2004-09-07 12:32:47.931386344 +0200 @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include unsigned long ip_ct_udp_timeout = 30*HZ; @@ -60,16 +62,17 @@ /* Returns verdict for packet, and may modify conntracktype */ static int udp_packet(struct ip_conntrack *conntrack, const struct sk_buff *skb, - enum ip_conntrack_info conntrackinfo) + enum ip_conntrack_info ctinfo) { /* If we've seen traffic both ways, this is some kind of UDP stream. Extend timeout. */ if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) { - ip_ct_refresh(conntrack, ip_ct_udp_timeout_stream); + ip_ct_refresh_acct(conntrack, ctinfo, skb, + ip_ct_udp_timeout_stream); /* Also, more likely to be important, and not a probe */ set_bit(IPS_ASSURED_BIT, &conntrack->status); } else - ip_ct_refresh(conntrack, ip_ct_udp_timeout); + ip_ct_refresh_acct(conntrack, ctinfo, skb, ip_ct_udp_timeout); return NF_ACCEPT; } @@ -80,7 +83,60 @@ return 1; } -struct ip_conntrack_protocol ip_conntrack_protocol_udp -= { { NULL, NULL }, IPPROTO_UDP, "udp", - udp_pkt_to_tuple, udp_invert_tuple, udp_print_tuple, udp_print_conntrack, - udp_packet, udp_new, NULL, NULL, NULL }; +static int udp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, + unsigned int hooknum) +{ + struct iphdr *iph = skb->nh.iph; + unsigned int udplen = skb->len - iph->ihl * 4; + struct udphdr hdr; + + /* Header is too small? */ + if (skb_copy_bits(skb, iph->ihl*4, &hdr, sizeof(hdr)) != 0) { + if (LOG_INVALID(IPPROTO_UDP)) + nf_log_packet(PF_INET, 0, skb, NULL, NULL, + "ip_ct_udp: short packet "); + return -NF_ACCEPT; + } + + /* Truncated/malformed packets */ + if (ntohs(hdr.len) > udplen || ntohs(hdr.len) < sizeof(hdr)) { + if (LOG_INVALID(IPPROTO_UDP)) + nf_log_packet(PF_INET, 0, skb, NULL, NULL, + "ip_ct_udp: truncated/malformed packet "); + return -NF_ACCEPT; + } + + /* Packet with no checksum */ + if (!hdr.check) + return NF_ACCEPT; + + /* Checksum invalid? Ignore. + * We skip checking packets on the outgoing path + * because the semantic of CHECKSUM_HW is different there + * and moreover root might send raw packets. + * FIXME: Source route IP option packets --RR */ + if (hooknum == NF_IP_PRE_ROUTING + && csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, + skb->ip_summed == CHECKSUM_HW ? skb->csum + : skb_checksum(skb, iph->ihl*4, udplen, 0))) { + if (LOG_INVALID(IPPROTO_UDP)) + nf_log_packet(PF_INET, 0, skb, NULL, NULL, + "ip_ct_udp: bad UDP checksum "); + return -NF_ACCEPT; + } + + return NF_ACCEPT; +} + +struct ip_conntrack_protocol ip_conntrack_protocol_udp = +{ + .proto = IPPROTO_UDP, + .name = "udp", + .pkt_to_tuple = udp_pkt_to_tuple, + .invert_tuple = udp_invert_tuple, + .print_tuple = udp_print_tuple, + .print_conntrack = udp_print_conntrack, + .packet = udp_packet, + .new = udp_new, + .error = udp_error, +}; diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.6.7/net/ipv4/netfilter/ip_conntrack_standalone.c --- linux-2.6.7.org/net/ipv4/netfilter/ip_conntrack_standalone.c 2004-09-07 12:07:46.565628632 +0200 +++ linux-2.6.7/net/ipv4/netfilter/ip_conntrack_standalone.c 2004-09-07 12:32:47.924387408 +0200 @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef CONFIG_SYSCTL #include #endif @@ -63,128 +64,225 @@ return len; } -/* FIXME: Don't print source proto part. --RR */ +#ifdef CONFIG_IP_NF_CT_ACCT static unsigned int -print_expect(char *buffer, const struct ip_conntrack_expect *expect) +seq_print_counters(struct seq_file *s, struct ip_conntrack_counter *counter) { - unsigned int len; - - if (expect->expectant->helper->timeout) - len = sprintf(buffer, "EXPECTING: %lu ", - timer_pending(&expect->timeout) - ? (expect->timeout.expires - jiffies)/HZ : 0); - else - len = sprintf(buffer, "EXPECTING: - "); - len += sprintf(buffer + len, "use=%u proto=%u ", - atomic_read(&expect->use), expect->tuple.dst.protonum); - len += print_tuple(buffer + len, &expect->tuple, - __ip_ct_find_proto(expect->tuple.dst.protonum)); - len += sprintf(buffer + len, "\n"); - return len; + return seq_printf(s, "packets=%llu bytes=%llu ", + counter->packets, counter->bytes); } +#else +#define seq_print_counters(x, y) 0 +#endif -static unsigned int -print_conntrack(char *buffer, struct ip_conntrack *conntrack) +static void *ct_seq_start(struct seq_file *s, loff_t *pos) { - unsigned int len; - struct ip_conntrack_protocol *proto - = __ip_ct_find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL] - .tuple.dst.protonum); - - len = sprintf(buffer, "%-8s %u %lu ", - proto->name, - conntrack->tuplehash[IP_CT_DIR_ORIGINAL] - .tuple.dst.protonum, - timer_pending(&conntrack->timeout) - ? (conntrack->timeout.expires - jiffies)/HZ : 0); + unsigned int *bucket; - len += proto->print_conntrack(buffer + len, conntrack); - len += print_tuple(buffer + len, - &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple, - proto); - if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status))) - len += sprintf(buffer + len, "[UNREPLIED] "); - len += print_tuple(buffer + len, - &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple, - proto); - if (test_bit(IPS_ASSURED_BIT, &conntrack->status)) - len += sprintf(buffer + len, "[ASSURED] "); - len += sprintf(buffer + len, "use=%u ", - atomic_read(&conntrack->ct_general.use)); - len += sprintf(buffer + len, "\n"); + /* strange seq_file api calls stop even if we fail, + * thus we need to grab lock since stop unlocks */ + READ_LOCK(&ip_conntrack_lock); + + if (*pos >= ip_conntrack_htable_size) + return NULL; - return len; + bucket = kmalloc(sizeof(unsigned int), GFP_KERNEL); + if (!bucket) { + return ERR_PTR(-ENOMEM); + } + + *bucket = *pos; + return bucket; } + +static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + unsigned int *bucket = (unsigned int *) v; -/* Returns true when finished. */ -static inline int -conntrack_iterate(const struct ip_conntrack_tuple_hash *hash, - char *buffer, off_t offset, off_t *upto, - unsigned int *len, unsigned int maxlen) + *pos = ++(*bucket); + if (*pos >= ip_conntrack_htable_size) { + kfree(v); + return NULL; + } + return bucket; +} + +static void ct_seq_stop(struct seq_file *s, void *v) { - unsigned int newlen; - IP_NF_ASSERT(hash->ctrack); + READ_UNLOCK(&ip_conntrack_lock); +} + +/* return 0 on success, 1 in case of error */ +static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash, + struct seq_file *s) +{ + struct ip_conntrack *conntrack = hash->ctrack; + struct ip_conntrack_protocol *proto; + char buffer[IP_CT_PRINT_BUFLEN]; MUST_BE_READ_LOCKED(&ip_conntrack_lock); - /* Only count originals */ + IP_NF_ASSERT(conntrack); + + /* we only want to print DIR_ORIGINAL */ if (DIRECTION(hash)) return 0; - if ((*upto)++ < offset) - return 0; + proto = __ip_ct_find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL] + .tuple.dst.protonum); + IP_NF_ASSERT(proto); - newlen = print_conntrack(buffer + *len, hash->ctrack); - if (*len + newlen > maxlen) + if (seq_printf(s, "%-8s %u %lu ", + proto->name, + conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum, + timer_pending(&conntrack->timeout) + ? (conntrack->timeout.expires - jiffies)/HZ : 0) != 0) + return 1; + + proto->print_conntrack(buffer, conntrack); + if (seq_puts(s, buffer)) + return 1; + + print_tuple(buffer, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple, + proto); + + if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_ORIGINAL])) + return 1; + + if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status))) + if (seq_printf(s, "[UNREPLIED] ")) + return 1; + + print_tuple(buffer, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple, + proto); + if (seq_puts(s, buffer)) + return 1; + + if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_REPLY])) + return 1; + + if (test_bit(IPS_ASSURED_BIT, &conntrack->status)) + if (seq_printf(s, "[ASSURED] ")) + return 1; + + if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use))) return 1; - else *len += newlen; return 0; } -static int -list_conntracks(char *buffer, char **start, off_t offset, int length) + +static int ct_seq_show(struct seq_file *s, void *v) { - unsigned int i; - unsigned int len = 0; - off_t upto = 0; - struct list_head *e; + unsigned int *bucket = (unsigned int *) v; - READ_LOCK(&ip_conntrack_lock); - /* Traverse hash; print originals then reply. */ - for (i = 0; i < ip_conntrack_htable_size; i++) { - if (LIST_FIND(&ip_conntrack_hash[i], conntrack_iterate, - struct ip_conntrack_tuple_hash *, - buffer, offset, &upto, &len, length)) - goto finished; + if (LIST_FIND(&ip_conntrack_hash[*bucket], ct_seq_real_show, + struct ip_conntrack_tuple_hash *, s)) { + /* buffer was filled and unable to print that tuple */ + return 1; } + return 0; +} + +static struct seq_operations ct_seq_ops = { + .start = ct_seq_start, + .next = ct_seq_next, + .stop = ct_seq_stop, + .show = ct_seq_show +}; + +static int ct_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &ct_seq_ops); +} - /* Now iterate through expecteds. */ +static struct file_operations ct_file_ops = { + .owner = THIS_MODULE, + .open = ct_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release +}; + +/* expects */ +static void *exp_seq_start(struct seq_file *s, loff_t *pos) +{ + struct list_head *e = &ip_conntrack_expect_list; + loff_t i; + + /* strange seq_file api calls stop even if we fail, + * thus we need to grab lock since stop unlocks */ + READ_LOCK(&ip_conntrack_lock); READ_LOCK(&ip_conntrack_expect_tuple_lock); - list_for_each(e, &ip_conntrack_expect_list) { - unsigned int last_len; - struct ip_conntrack_expect *expect - = (struct ip_conntrack_expect *)e; - if (upto++ < offset) continue; - - last_len = len; - len += print_expect(buffer + len, expect); - if (len > length) { - len = last_len; - goto finished_expects; - } + + if (list_empty(e)) + return NULL; + + for (i = 0; i <= *pos; i++) { + e = e->next; + if (e == &ip_conntrack_expect_list) + return NULL; } + return e; +} + +static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct list_head *e = v; + + e = e->next; + + if (e == &ip_conntrack_expect_list) + return NULL; + + return e; +} - finished_expects: +static void exp_seq_stop(struct seq_file *s, void *v) +{ READ_UNLOCK(&ip_conntrack_expect_tuple_lock); - finished: READ_UNLOCK(&ip_conntrack_lock); +} - /* `start' hack - see fs/proc/generic.c line ~165 */ - *start = (char *)((unsigned int)upto - offset); - return len; +static int exp_seq_show(struct seq_file *s, void *v) +{ + struct ip_conntrack_expect *expect = v; + char buffer[IP_CT_PRINT_BUFLEN]; + + if (expect->expectant->helper->timeout) + seq_printf(s, "%lu ", timer_pending(&expect->timeout) + ? (expect->timeout.expires - jiffies)/HZ : 0); + else + seq_printf(s, "- "); + + seq_printf(s, "use=%u proto=%u ", atomic_read(&expect->use), + expect->tuple.dst.protonum); + + print_tuple(buffer, &expect->tuple, + __ip_ct_find_proto(expect->tuple.dst.protonum)); + return seq_printf(s, "%s\n", buffer); } +static struct seq_operations exp_seq_ops = { + .start = exp_seq_start, + .next = exp_seq_next, + .stop = exp_seq_stop, + .show = exp_seq_show +}; + +static int exp_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &exp_seq_ops); +} + +static struct file_operations exp_file_ops = { + .owner = THIS_MODULE, + .open = exp_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release +}; + static unsigned int ip_confirm(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, @@ -334,6 +432,11 @@ /* From ip_conntrack_proto_icmp.c */ extern unsigned long ip_ct_generic_timeout; +/* Log invalid packets of a given protocol */ +unsigned int ip_ct_log_invalid = 0; +static int log_invalid_proto_min = 0; +static int log_invalid_proto_max = 255; + static struct ctl_table_header *ip_ct_sysctl_header; static ctl_table ip_ct_sysctl_table[] = { @@ -449,6 +552,17 @@ .mode = 0644, .proc_handler = &proc_dointvec_jiffies, }, + { + .ctl_name = NET_IPV4_NF_CONNTRACK_LOG_INVALID, + .procname = "ip_conntrack_log_invalid", + .data = &ip_ct_log_invalid, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &log_invalid_proto_min, + .extra2 = &log_invalid_proto_max, + }, { .ctl_name = 0 } }; @@ -494,7 +608,7 @@ #endif static int init_or_cleanup(int init) { - struct proc_dir_entry *proc; + struct proc_dir_entry *proc, *proc_exp; int ret = 0; if (!init) goto cleanup; @@ -503,14 +617,18 @@ if (ret < 0) goto cleanup_nothing; - proc = proc_net_create("ip_conntrack", 0440, list_conntracks); + proc = proc_net_create("ip_conntrack", 0440, NULL); if (!proc) goto cleanup_init; - proc->owner = THIS_MODULE; + proc->proc_fops = &ct_file_ops; + + proc_exp = proc_net_create("ip_conntrack_expect", 0440, NULL); + if (!proc_exp) goto cleanup_proc; + proc_exp->proc_fops = &exp_file_ops; ret = nf_register_hook(&ip_conntrack_defrag_ops); if (ret < 0) { printk("ip_conntrack: can't register pre-routing defrag hook.\n"); - goto cleanup_proc; + goto cleanup_proc_exp; } ret = nf_register_hook(&ip_conntrack_defrag_local_out_ops); if (ret < 0) { @@ -562,6 +680,8 @@ nf_unregister_hook(&ip_conntrack_defrag_local_out_ops); cleanup_defragops: nf_unregister_hook(&ip_conntrack_defrag_ops); +cleanup_proc_exp: + proc_net_remove("ip_conntrack_exp"); cleanup_proc: proc_net_remove("ip_conntrack"); cleanup_init: @@ -638,7 +758,7 @@ EXPORT_SYMBOL(ip_conntrack_helper_register); EXPORT_SYMBOL(ip_conntrack_helper_unregister); EXPORT_SYMBOL(ip_ct_selective_cleanup); -EXPORT_SYMBOL(ip_ct_refresh); +EXPORT_SYMBOL(ip_ct_refresh_acct); EXPORT_SYMBOL(ip_ct_find_proto); EXPORT_SYMBOL(__ip_ct_find_proto); EXPORT_SYMBOL(ip_ct_find_helper); diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ip_fw_compat_masq.c linux-2.6.7/net/ipv4/netfilter/ip_fw_compat_masq.c --- linux-2.6.7.org/net/ipv4/netfilter/ip_fw_compat_masq.c 2004-09-07 12:07:46.566628480 +0200 +++ linux-2.6.7/net/ipv4/netfilter/ip_fw_compat_masq.c 2004-09-07 12:32:47.925387256 +0200 @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -144,7 +145,8 @@ switch ((*pskb)->nh.iph->protocol) { case IPPROTO_ICMP: /* ICMP errors. */ - ct = icmp_error_track(*pskb, &ctinfo, NF_IP_PRE_ROUTING); + protocol->error(*pskb, &ctinfo, NF_IP_PRE_ROUTING); + ct = (struct ip_conntrack *)(*pskb)->nfct->master; if (ct) { /* We only do SNAT in the compatibility layer. So we can manipulate ICMP errors from @@ -165,7 +167,8 @@ case IPPROTO_UDP: IP_NF_ASSERT(((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) == 0); - if (!get_tuple((*pskb)->nh.iph, *pskb, (*pskb)->nh.iph->ihl*4, &tuple, protocol)) { + if (!ip_ct_get_tuple((*pskb)->nh.iph, *pskb, + (*pskb)->nh.iph->ihl*4, &tuple, protocol)) { if (net_ratelimit()) printk("ip_fw_compat_masq: Can't get tuple\n"); return NF_ACCEPT; diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ip_nat_core.c linux-2.6.7/net/ipv4/netfilter/ip_nat_core.c --- linux-2.6.7.org/net/ipv4/netfilter/ip_nat_core.c 2004-09-07 12:07:46.565628632 +0200 +++ linux-2.6.7/net/ipv4/netfilter/ip_nat_core.c 2004-09-07 12:32:56.183131888 +0200 @@ -49,7 +49,6 @@ static struct list_head *bysource; static struct list_head *byipsproto; LIST_HEAD(protos); -LIST_HEAD(helpers); extern struct ip_nat_protocol unknown_nat_protocol; @@ -498,13 +497,6 @@ return ret; } -static inline int -helper_cmp(const struct ip_nat_helper *helper, - const struct ip_conntrack_tuple *tuple) -{ - return ip_ct_tuple_mask_cmp(tuple, &helper->tuple, &helper->mask); -} - /* Where to manip the reply packets (will be reverse manip). */ static unsigned int opposite_hook[NF_IP_NUMHOOKS] = { [NF_IP_PRE_ROUTING] = NF_IP_POST_ROUTING, @@ -643,8 +635,7 @@ /* If there's a helper, assign it; based on new tuple. */ if (!conntrack->master) - info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, - &reply); + info->helper = ip_nat_find_helper(&reply); /* It's done. */ info->initialized |= (1 << HOOK2MANIP(hooknum)); diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ip_nat_helper.c linux-2.6.7/net/ipv4/netfilter/ip_nat_helper.c --- linux-2.6.7.org/net/ipv4/netfilter/ip_nat_helper.c 2004-09-07 12:07:46.565628632 +0200 +++ linux-2.6.7/net/ipv4/netfilter/ip_nat_helper.c 2004-09-07 12:32:56.184131736 +0200 @@ -47,6 +47,7 @@ #define DUMP_OFFSET(x) #endif +static LIST_HEAD(helpers); DECLARE_LOCK(ip_nat_seqofs_lock); /* Setup TCP sequence correction given this change at this sequence */ @@ -419,6 +420,18 @@ return ret; } +struct ip_nat_helper * +ip_nat_find_helper(const struct ip_conntrack_tuple *tuple) +{ + struct ip_nat_helper *h; + + READ_LOCK(&ip_nat_lock); + h = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, tuple); + READ_UNLOCK(&ip_nat_lock); + + return h; +} + static int kill_helper(const struct ip_conntrack *i, void *helper) { diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ip_queue.c linux-2.6.7/net/ipv4/netfilter/ip_queue.c --- linux-2.6.7.org/net/ipv4/netfilter/ip_queue.c 2004-09-07 12:07:46.566628480 +0200 +++ linux-2.6.7/net/ipv4/netfilter/ip_queue.c 2004-09-07 12:33:00.821426760 +0200 @@ -255,9 +255,10 @@ entry->skb->dev->hard_header_parse(entry->skb, pmsg->hw_addr); } - - if (data_len) - memcpy(pmsg->payload, entry->skb->data, data_len); + + if (data_len) + if (skb_copy_bits(entry->skb, 0, pmsg->payload, data_len) != 0) + goto nlmsg_failure; nlh->nlmsg_len = skb->tail - old_tail; return skb; diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c linux-2.6.7/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c 2004-09-07 12:33:07.424422952 +0200 @@ -0,0 +1,89 @@ +/** + * 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, + const struct net_device *in, + const struct net_device *out, + unsigned int hooknum, + const void *targinfo, + void *userinfo) +{ + struct iphdr *iph; + struct sk_buff *skb; + struct ip_options *opt; + unsigned char *optiph; + int l; + + if (!skb_ip_make_writable(pskb, (*pskb)->len)) + return NF_DROP; + + skb = (*pskb); + iph = (*pskb)->nh.iph; + optiph = skb->nh.raw; + 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 = { + .name = "IPV4OPTSSTRIP", + .target = target, + .checkentry = checkentry, + .me = THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_target(&ipt_ipv4optsstrip_reg); +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_ipv4optsstrip_reg); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_LOG.c linux-2.6.7/net/ipv4/netfilter/ipt_LOG.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_LOG.c 2004-09-07 12:07:46.566628480 +0200 +++ linux-2.6.7/net/ipv4/netfilter/ipt_LOG.c 2004-09-07 12:32:38.740783528 +0200 @@ -76,7 +76,7 @@ printk("FRAG:%u ", ntohs(iph.frag_off) & IP_OFFSET); if ((info->logflags & IPT_LOG_IPOPT) - && iph.ihl * 4 != sizeof(struct iphdr)) { + && iph.ihl * 4 > sizeof(struct iphdr)) { unsigned char opt[4 * 15 - sizeof(struct iphdr)]; unsigned int i, optsize; @@ -143,7 +143,7 @@ printk("URGP=%u ", ntohs(tcph.urg_ptr)); if ((info->logflags & IPT_LOG_TCPOPT) - && tcph.doff * 4 != sizeof(struct tcphdr)) { + && tcph.doff * 4 > sizeof(struct tcphdr)) { unsigned char opt[4 * 15 - sizeof(struct tcphdr)]; unsigned int i, optsize; diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_TTL.c linux-2.6.7/net/ipv4/netfilter/ipt_TTL.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_TTL.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_TTL.c 2004-09-07 12:33:09.041177168 +0200 @@ -0,0 +1,120 @@ +/* TTL modification target for IP tables + * (C) 2000 by Harald Welte + * + * Version: $Revision$ + * + * 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, const struct net_device *in, + const struct net_device *out, unsigned int hooknum, + const void *targinfo, void *userinfo) +{ + struct iphdr *iph; + const struct ipt_TTL_info *info = targinfo; + u_int16_t diffs[2]; + int new_ttl; + + if (!skb_ip_make_writable(pskb, (*pskb)->len)) + return NF_DROP; + + iph = (*pskb)->nh.iph; + + 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 = { + .name = "TTL", + .target = ipt_ttl_target, + .checkentry = ipt_ttl_checkentry, + .me = 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 --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_connlimit.c linux-2.6.7/net/ipv4/netfilter/ipt_connlimit.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_connlimit.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_connlimit.c 2004-09-07 12:33:10.091017568 +0200 @@ -0,0 +1,230 @@ +/* + * 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, + 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 = { + .name = "connlimit", + .match = &match, + .checkentry = &check, + .destroy = &destroy, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_match(&connlimit_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&connlimit_match); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_dstlimit.c linux-2.6.7/net/ipv4/netfilter/ipt_dstlimit.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_dstlimit.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_dstlimit.c 2004-09-07 12:33:11.098864352 +0200 @@ -0,0 +1,690 @@ +/* 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, + 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) { + u16 ports[2]; + + /* Must not be a fragment. */ + if (offset) + return 0; + + /* Must be big enough to read ports (both UDP and TCP have + them at the start). */ + if (skb_copy_bits(skb, skb->nh.iph->ihl*4, ports, sizeof(ports)) < 0) { + /* We've been asked to examine this packet, and we + can't. Hence, no choice but to drop. */ + *hotdrop = 1; + return 0; + } + + 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 1; + } + + /* 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 --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_fuzzy.c linux-2.6.7/net/ipv4/netfilter/ipt_fuzzy.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_fuzzy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_fuzzy.c 2004-09-07 12:33:12.080715088 +0200 @@ -0,0 +1,185 @@ +/* + * 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, + 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 = { + .name = "fuzzy", + .match = ipt_fuzzy_match, + .checkentry = ipt_fuzzy_checkentry, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_match(&ipt_fuzzy_reg); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipt_fuzzy_reg); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_ipv4options.c linux-2.6.7/net/ipv4/netfilter/ipt_ipv4options.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_ipv4options.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_ipv4options.c 2004-09-07 12:33:15.334220480 +0200 @@ -0,0 +1,172 @@ +/* + 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. + 19-february-2004 Harald Welte : merge with 2.6.x +*/ + +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Fabrice Marie "); + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + 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 = { + .name = "ipv4options", + .match = match, + .checkentry = checkentry, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_match(&ipv4options_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipv4options_match); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_mport.c linux-2.6.7/net/ipv4/netfilter/ipt_mport.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_mport.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_mport.c 2004-09-07 12:33:16.384060880 +0200 @@ -0,0 +1,116 @@ +/* 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, + int *hotdrop) +{ + u16 ports[2]; + const struct ipt_mport *minfo = matchinfo; + + if (offset) + return 0; + + /* Must be big enough to read ports (both UDP and TCP have + them at the start). */ + if (skb_copy_bits(skb, skb->nh.iph->ihl*4, ports, sizeof(ports)) < 0) { + /* We've been asked to examine this packet, and we + can't. Hence, no choice but to drop. */ + duprintf("ipt_multiport:" + " Dropping evil offset=0 tinygram.\n"); + *hotdrop = 1; + return 0; + } + + return ports_match(minfo, ntohs(ports[0]), ntohs(ports[1])); +} + +/* 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 = { + .name = "mport", + .match = &match, + .checkentry = &checkentry, + .me = 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 --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_nth.c linux-2.6.7/net/ipv4/netfilter/ipt_nth.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_nth.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_nth.c 2004-09-07 12:33:17.547883952 +0200 @@ -0,0 +1,166 @@ +/* + 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 + 2004-02-19 Harald Welte + * port to 2.6.x + +*/ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Fabrice Marie "); + +/* + * 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, + 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 = { + .name = "nth", + .match = ipt_nth_match, + .checkentry = ipt_nth_checkentry, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + unsigned counter; + + memset(&states, 0, sizeof(states)); + for (counter = 0; counter < IPT_NTH_NUM_COUNTERS; counter++) + spin_lock_init(&(states[counter].lock)); + + return ipt_register_match(&ipt_nth_reg); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&ipt_nth_reg); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_osf.c linux-2.6.7/net/ipv4/netfilter/ipt_osf.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_osf.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_osf.c 2004-09-07 12:33:18.674712648 +0200 @@ -0,0 +1,873 @@ +/* + * 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 +#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 spinlock_t ipt_osf_netlink_lock = SPIN_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, + int *); +static int checkentry(const char *, const struct ipt_ip *, void *, + unsigned int, unsigned int); + +static unsigned long seq, ipt_osf_groups = 1; +static struct sock *nts; + +static struct ipt_match osf_match = +{ + { NULL, NULL }, + "osf", + &match, + &checkentry, + NULL, + THIS_MODULE +}; + +static void ipt_osf_nlsend(struct osf_finger *f, const struct sk_buff *sk) +{ + unsigned int size; + struct sk_buff *skb; + struct ipt_osf_nlmsg *data; + struct nlmsghdr *nlh; + + size = NLMSG_SPACE(sizeof(struct ipt_osf_nlmsg)); + + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) + { + log("skb_alloc() failed.\n"); + return; + } + + nlh = NLMSG_PUT(skb, 0, seq++, NLMSG_DONE, size - sizeof(*nlh)); + + data = (struct ipt_osf_nlmsg *)NLMSG_DATA(nlh); + + memcpy(&data->f, f, sizeof(struct osf_finger)); + memcpy(&data->ip, sk->nh.iph, sizeof(struct iphdr)); + memcpy(&data->tcp, (struct tcphdr *)((u_int32_t *)sk->nh.iph + sk->nh.iph->ihl), sizeof(struct tcphdr)); + + NETLINK_CB(skb).dst_groups = ipt_osf_groups; + netlink_broadcast(nts, skb, 0, ipt_osf_groups, GFP_ATOMIC); + +nlmsg_failure: + return; +} + +static inline int smart_dec(const struct sk_buff *skb, unsigned long flags, unsigned char f_ttl) +{ + struct iphdr *ip = skb->nh.iph; + + if (flags & IPT_OSF_SMART) + { + struct in_device *in_dev = in_dev_get(skb->dev); + + for_ifa(in_dev) + { + if (inet_ifa_match(ip->saddr, ifa)) + { + in_dev_put(in_dev); + return (ip->ttl == f_ttl); + } + } + endfor_ifa(in_dev); + + in_dev_put(in_dev); + return (ip->ttl <= f_ttl); + } + 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, + 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; + + 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(skb, info->flags, 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; + int loop_cont = 0; + + fmatch = FMATCH_OK; + + + switch (*optp) + { + case OSFOPT_MSS: + mss = ntohs(*(unsigned short *)(optp+2)); + break; + case OSFOPT_TS: + loop_cont = 1; + break; + } + + if (loop_cont) + { + optp = optend; + continue; + } + + if (len != 1) + { + /* Skip kind and length fields*/ + optp += 2; + + if (f->opt[optnum].wc.val != 0) + { + unsigned long tmp = 0; + + /* Hmmm... It looks a bit ugly. :) */ + memcpy(&tmp, optp, + (len > sizeof(unsigned long)? + sizeof(unsigned long):len)); + /* 2 + 2: optlen(2 bytes) + + * kind(1 byte) + length(1 byte) */ + if (len == 4) + tmp = ntohs(tmp); + else + tmp = ntohl(tmp); + + if (f->opt[optnum].wc.wc == '%') + { + if ((tmp % f->opt[optnum].wc.val) != 0) + fmatch = FMATCH_OPT_WRONG; + } + else 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 (f->wss.val == 0 || window == f->wss.val) + fmatch = FMATCH_OK; + break; + case 1: /* MSS */ +/* Lurked in OpenBSD */ +#define SMART_MSS 1460 + if (window == f->wss.val*mss || + window == f->wss.val*SMART_MSS) + fmatch = FMATCH_OK; + break; + case 2: /* MTU */ + if (window == f->wss.val*(mss+40) || + window == f->wss.val*(SMART_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:%s:%s] : %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n", + f->genre, f->version, + f->subtype, f->details, + NIPQUAD(ip->saddr), ntohs(tcp->source), + NIPQUAD(ip->daddr), ntohs(tcp->dest), + f->ttl - ip->ttl); + if (info->flags & IPT_OSF_NETLINK) + { + spin_lock_bh(&ipt_osf_netlink_lock); + ipt_osf_nlsend(f, skb); + spin_unlock_bh(&ipt_osf_netlink_lock); + } + if ((info->flags & IPT_OSF_LOG) && + info->loglevel == IPT_OSF_LOGLEVEL_FIRST) + break; + } + } + } + if (!fcount && (info->flags & (IPT_OSF_LOG | IPT_OSF_NETLINK))) + { + unsigned char opt[4 * 15 - sizeof(struct tcphdr)]; + unsigned int i, optsize; + struct osf_finger fg; + + memset(&fg, 0, sizeof(fg)); + + if ((info->flags & IPT_OSF_LOG)) + log("Unknown: %lu:%d:%d:%lu:", window, ip->ttl, df, totlen); + if (optp) + { + optsize = tcp->doff * 4 - sizeof(struct tcphdr); + if (skb_copy_bits(skb, ip->ihl*4 + sizeof(struct tcphdr), + opt, optsize) < 0) + { + if (info->flags & IPT_OSF_LOG) + loga("TRUNCATED"); + if (info->flags & IPT_OSF_NETLINK) + strcpy(fg.details, "TRUNCATED"); + } + else + { + for (i = 0; i < optsize; i++) + { + if (info->flags & IPT_OSF_LOG) + loga("%02X", opt[i]); + } + if (info->flags & IPT_OSF_NETLINK) + memcpy(fg.details, opt, MAXDETLEN); + } + } + if ((info->flags & IPT_OSF_LOG)) + loga(" %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n", + NIPQUAD(ip->saddr), ntohs(tcp->source), + NIPQUAD(ip->daddr), ntohs(tcp->dest)); + + if (info->flags & IPT_OSF_NETLINK) + { + fg.wss.val = window; + fg.ttl = ip->ttl; + fg.df = df; + fg.ss = totlen; + strncpy(fg.genre, "Unknown", MAXGENRELEN); + + spin_lock_bh(&ipt_osf_netlink_lock); + ipt_osf_nlsend(&fg, skb); + spin_unlock_bh(&ipt_osf_netlink_lock); + } + } + + read_unlock(&osf_lock); + + if (fcount) + fmatch = FMATCH_OK; + + 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, __count, err; + + *eof = 1; + __count = count; + 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); + + err = snprintf(buf+count, __count-count, "%s - %s[%s] : %s", + f->genre, f->version, + f->subtype, f->details); + if (err == 0 || __count <= count + err) + break; + else + count += err; + 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"); + err = snprintf(buf+count, __count-count, "\n"); + if (err == 0 || __count <= count + err) + break; + else + count += err; + } + 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); + } + else if (pbeg[0] == '%') + { + finger->wss.wc = '%'; + finger->wss.val = simple_strtoul(pbeg+1, NULL, 10); + } + else 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; + + nts = netlink_kernel_create(NETLINK_NFLOG, NULL); + if (!nts) + { + log("netlink_kernel_create() failed\n"); + remove_proc_entry("sys/net/ipv4/osf", NULL); + ipt_unregister_match(&osf_match); + return -ENOMEM; + } + + 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); + if (nts && nts->sk_socket) + sock_release(nts->sk_socket); + + 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 --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_psd.c linux-2.6.7/net/ipv4/netfilter/ipt_psd.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_psd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_psd.c 2004-09-07 12:33:19.724553048 +0200 @@ -0,0 +1,358 @@ +/* + 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 + 2004-05-05 Martijn Lievaart : ported to 2.6 +*/ + +#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, + 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 = { + .name = "psd", + .match = ipt_psd_match, + .checkentry = ipt_psd_checkentry, + .me = 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 --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_quota.c linux-2.6.7/net/ipv4/netfilter/ipt_quota.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_quota.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_quota.c 2004-09-07 12:33:20.690406216 +0200 @@ -0,0 +1,91 @@ +/* + * netfilter module to enforce network quotas + * + * Sam Johnston + */ +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sam Johnston "); + +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, int *hotdrop) +{ + struct ipt_quota_info *q = (struct ipt_quota_info *) matchinfo; + unsigned int datalen; + + if (skb->len < sizeof(struct iphdr)) + return NF_ACCEPT; + + datalen = skb->len - skb->nh.iph->ihl*4; + + 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 = { + .name = "quota", + .match = match, + .checkentry = checkentry, + .me = 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 --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_sctp.c linux-2.6.7/net/ipv4/netfilter/ipt_sctp.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_sctp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_sctp.c 2004-09-07 12:33:05.390732120 +0200 @@ -0,0 +1,201 @@ +#include +#include +#include +#include + +#include +#include + +#ifdef DEBUG_SCTP +#define duprintf(format, args...) printk(format , ## args) +#else +#define duprintf(format, args...) +#endif + +#define SCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \ + || (!!((invflag) & (option)) ^ (cond))) + +static int +match_flags(const struct ipt_sctp_flag_info *flag_info, + const int flag_count, + u_int8_t chunktype, + u_int8_t chunkflags) +{ + int i; + + for (i = 0; i < flag_count; i++) { + if (flag_info[i].chunktype == chunktype) { + return (chunkflags & flag_info[i].flag_mask) == flag_info[i].flag; + } + } + + return 1; +} + +static int +match_packet(const struct sk_buff *skb, + const u_int32_t *chunkmap, + int chunk_match_type, + const struct ipt_sctp_flag_info *flag_info, + const int flag_count, + int *hotdrop) +{ + int offset; + u_int32_t chunkmapcopy[256 / sizeof (u_int32_t)]; + sctp_chunkhdr_t sch; + +#ifdef DEBUG_SCTP + int i = 0; +#endif + + if (chunk_match_type == SCTP_CHUNK_MATCH_ALL) { + SCTP_CHUNKMAP_COPY(chunkmapcopy, chunkmap); + } + + offset = skb->nh.iph->ihl * 4 + sizeof (sctp_sctphdr_t); + do { + if (skb_copy_bits(skb, offset, &sch, sizeof(sch)) < 0) { + duprintf("Dropping invalid SCTP packet.\n"); + *hotdrop = 1; + return 0; + } + + duprintf("Chunk num: %d\toffset: %d\ttype: %d\tlength: %d\tflags: %x\n", + ++i, offset, sch.type, htons(sch.length), sch.flags); + + offset += (htons(sch.length) + 3) & ~3; + + duprintf("skb->len: %d\toffset: %d\n", skb->len, offset); + + if (SCTP_CHUNKMAP_IS_SET(chunkmap, sch.type)) { + switch (chunk_match_type) { + case SCTP_CHUNK_MATCH_ANY: + if (match_flags(flag_info, flag_count, + sch.type, sch.flags)) { + return 1; + } + break; + + case SCTP_CHUNK_MATCH_ALL: + if (match_flags(flag_info, flag_count, + sch.type, sch.flags)) { + SCTP_CHUNKMAP_CLEAR(chunkmapcopy, sch.type); + } + break; + + case SCTP_CHUNK_MATCH_ONLY: + if (!match_flags(flag_info, flag_count, + sch.type, sch.flags)) { + return 0; + } + break; + } + } else { + switch (chunk_match_type) { + case SCTP_CHUNK_MATCH_ONLY: + return 0; + } + } + } while (offset < skb->len); + + switch (chunk_match_type) { + case SCTP_CHUNK_MATCH_ALL: + return SCTP_CHUNKMAP_IS_CLEAR(chunkmap); + case SCTP_CHUNK_MATCH_ANY: + return 0; + case SCTP_CHUNK_MATCH_ONLY: + return 1; + } + + /* This will never be reached, but required to stop compiler whine */ + 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, + int *hotdrop) +{ + const struct ipt_sctp_info *info; + sctp_sctphdr_t sh; + + info = (const struct ipt_sctp_info *)matchinfo; + + if (offset) { + duprintf("Dropping non-first fragment.. FIXME\n"); + return 0; + } + + if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &sh, sizeof(sh)) < 0) { + duprintf("Dropping evil TCP offset=0 tinygram.\n"); + *hotdrop = 1; + return 0; + } + duprintf("spt: %d\tdpt: %d\n", ntohs(sh.source), ntohs(sh.dest)); + + return SCCHECK(((ntohs(sh.source) >= info->spts[0]) + && (ntohs(sh.source) <= info->spts[1])), + IPT_SCTP_SRC_PORTS, info->flags, info->invflags) + && SCCHECK(((ntohs(sh.dest) >= info->dpts[0]) + && (ntohs(sh.dest) <= info->dpts[1])), + IPT_SCTP_DEST_PORTS, info->flags, info->invflags) + && SCCHECK(match_packet(skb, info->chunkmap, info->chunk_match_type, + info->flag_info, info->flag_count, + hotdrop), + IPT_SCTP_CHUNK_TYPES, info->flags, info->invflags); +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + const struct ipt_sctp_info *info; + + info = (const struct ipt_sctp_info *)matchinfo; + + return ip->proto == IPPROTO_SCTP + && !(ip->invflags & IPT_INV_PROTO) + && matchsize == IPT_ALIGN(sizeof(struct ipt_sctp_info)) + && !(info->flags & ~IPT_SCTP_VALID_FLAGS) + && !(info->invflags & ~IPT_SCTP_VALID_FLAGS) + && !(info->invflags & ~info->flags) + && ((!(info->flags & IPT_SCTP_CHUNK_TYPES)) || + (info->chunk_match_type & + (SCTP_CHUNK_MATCH_ALL + | SCTP_CHUNK_MATCH_ANY + | SCTP_CHUNK_MATCH_ONLY))); +} + +static struct ipt_match sctp_match = +{ + .list = { NULL, NULL}, + .name = "sctp", + .match = &match, + .checkentry = &checkentry, + .destroy = NULL, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_match(&sctp_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&sctp_match); +} + +module_init(init); +module_exit(fini); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kiran Kumar Immidi"); +MODULE_DESCRIPTION("Match for SCTP protocol packets"); + diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_time.c linux-2.6.7/net/ipv4/netfilter/ipt_time.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_time.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_time.c 2004-09-07 12:33:22.072196152 +0200 @@ -0,0 +1,189 @@ +/* + 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. + 2004-05-02 Fabrice : added support for date matching, from an idea of Fabien COELHO. +*/ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Fabrice MARIE "); +MODULE_DESCRIPTION("Match arrival timestamp/date"); +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; + + /* First we make sure we are in the date start-stop boundaries */ + if ((packet_local_time < info->date_start) || (packet_local_time > info->date_stop)) + return 0; /* We are outside the date boundaries */ + + /* 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 hooks */ + 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))) + /* 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 --exclude '*.orig' linux-2.6.7.org/net/ipv4/netfilter/ipt_u32.c linux-2.6.7/net/ipv4/netfilter/ipt_u32.c --- linux-2.6.7.org/net/ipv4/netfilter/ipt_u32.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv4/netfilter/ipt_u32.c 2004-09-07 12:38:33.377870496 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/net/ipv6/netfilter/Kconfig linux-2.6.7/net/ipv6/netfilter/Kconfig --- linux-2.6.7.org/net/ipv6/netfilter/Kconfig 2004-09-07 12:07:46.626619360 +0200 +++ linux-2.6.7/net/ipv6/netfilter/Kconfig 2004-09-07 12:33:17.553883040 +0200 @@ -230,5 +230,60 @@ . If unsure, say `N'. help +config IP6_NF_TARGET_HL + tristate 'HL target support' + depends on IP6_NF_MANGLE + help + This option adds a `HL' target, which allows you to modify the value of + IPv6 Hop Limit field. + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + +config IP6_NF_TARGET_REJECT + tristate 'REJECT target support' + depends on IP6_NF_FILTER + help + The REJECT target allows a filtering rule to specify that an ICMPv6 + error should be issued in response to an incoming packet, rather + than silently being dropped. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +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 fuzzy logic based law. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +config IP6_NF_MATCH_NTH + tristate 'Nth match support' + depends on IP6_NF_IPTABLES + help + This option adds a `Nth' match, which allow you to make + rules that match every Nth packet. By default there are + 16 different counters. + + [options] + --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. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + endmenu diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv6/netfilter/Makefile linux-2.6.7/net/ipv6/netfilter/Makefile --- linux-2.6.7.org/net/ipv6/netfilter/Makefile 2004-09-07 12:07:46.626619360 +0200 +++ linux-2.6.7/net/ipv6/netfilter/Makefile 2004-09-07 12:33:17.554882888 +0200 @@ -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 @@ -21,5 +22,8 @@ obj-$(CONFIG_IP6_NF_TARGET_MARK) += ip6t_MARK.o obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o + +obj-$(CONFIG_IP6_NF_MATCH_NTH) += ip6t_nth.o +obj-$(CONFIG_IP6_NF_TARGET_HL) += ip6t_HL.o obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o diff -Nur --exclude '*.orig' linux-2.6.7.org/net/ipv6/netfilter/ip6t_HL.c linux-2.6.7/net/ipv6/netfilter/ip6t_HL.c --- linux-2.6.7.org/net/ipv6/netfilter/ip6t_HL.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv6/netfilter/ip6t_HL.c 2004-09-07 12:33:06.517560816 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/net/ipv6/netfilter/ip6t_REJECT.c linux-2.6.7/net/ipv6/netfilter/ip6t_REJECT.c --- linux-2.6.7.org/net/ipv6/netfilter/ip6t_REJECT.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv6/netfilter/ip6t_REJECT.c 2004-09-07 12:33:08.255296640 +0200 @@ -0,0 +1,458 @@ +/* + * IP6 tables REJECT target module + * Linux INET6 implementation + * + * Copyright (C)2003 USAGI/WIDE Project + * + * Authors: + * Yasuyuki Kozakai + * + * Based on net/ipv4/netfilter/ipt_REJECT.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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Yasuyuki KOZAKAI "); +MODULE_DESCRIPTION("IP6 tables REJECT target module"); +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +#if 0 +static void connection_attach(struct sk_buff *new_skb, struct nf_ct_info *nfct) +{ + void (*attach)(struct sk_buff *, struct nf_ct_info *); + if (nfct && (attach = ip6_ct_attach) != NULL) { + mb(); + attach(new_skb, nfct); + } +} +#endif + +static int maybe_reroute(struct sk_buff *skb) +{ + if (skb->nfcache & NFC_ALTERED){ + if (ip6_route_me_harder(skb) != 0){ + kfree_skb(skb); + return -EINVAL; + } + } + + return dst_output(skb); +} + +/* Send RST reply */ +static void send_reset(struct sk_buff *oldskb) +{ + struct sk_buff *nskb; + struct tcphdr otcph, *tcph; + unsigned int otcplen, tcphoff, hh_len; + int needs_ack; + struct ipv6hdr *oip6h = oldskb->nh.ipv6h, *ip6h; + struct dst_entry *dst = NULL; + u8 proto; + struct flowi fl; + proto = oip6h->nexthdr; + int err; + + if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || + (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { + DEBUGP("ip6t_REJECT: addr is not unicast.\n"); + return; + } + + tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), + &proto, oldskb->len - ((u8*)(oip6h+1) + - oldskb->data)); + + if ((tcphoff < 0) || (tcphoff > oldskb->len)) { + DEBUGP("ip6t_REJECT: Can't get TCP header.\n"); + return; + } + + otcplen = oldskb->len - tcphoff; + + /* IP header checks: fragment, too short. */ + if ((proto != IPPROTO_TCP) || (otcplen < sizeof(struct tcphdr))) { + DEBUGP("ip6t_REJECT: proto(%d) != IPPROTO_TCP, or too short. otcplen = %d\n", + proto, otcplen); + return; + } + + if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) { + if (net_ratelimit()) + printk("ip6t_REJECT: Can't copy tcp header\n"); + return; + } + + /* No RST for RST. */ + if (otcph.rst) { + DEBUGP("ip6t_REJECT: RST is set\n"); + return; + } + + /* Check checksum. */ + if (csum_ipv6_magic(&oip6h->saddr, &oip6h->daddr, otcplen, IPPROTO_TCP, + skb_checksum(oldskb, tcphoff, otcplen, 0))) { + DEBUGP("ip6t_REJECT: TCP checksum is invalid\n"); + return; + } + + memset(&fl, 0, sizeof(fl)); + fl.proto = IPPROTO_TCP; + ipv6_addr_copy(&fl.fl6_src, &oip6h->daddr); + ipv6_addr_copy(&fl.fl6_dst, &oip6h->saddr); + fl.fl_ip_sport = otcph.dest; + fl.fl_ip_dport = otcph.source; + err = ip6_dst_lookup(NULL, &dst, &fl); + if (err) { + if (net_ratelimit()) + printk("ip6t_REJECT: can't find dst. err = %d\n", err); + return; + } + + hh_len = (dst->dev->hard_header_len + 15)&~15; + nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) + + sizeof(struct tcphdr) + dst->trailer_len, + GFP_ATOMIC); + + if (!nskb) { + if (net_ratelimit()) + printk("ip6t_REJECT: Can't alloc skb\n"); + dst_release(dst); + return; + } + + nskb->dst = dst; + dst_hold(dst); + + skb_reserve(nskb, hh_len + dst->header_len); + + ip6h = nskb->nh.ipv6h = (struct ipv6hdr *) + skb_put(nskb, sizeof(struct ipv6hdr)); + ip6h->version = 6; + ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT); + ip6h->nexthdr = IPPROTO_TCP; + ip6h->payload_len = htons(sizeof(struct tcphdr)); + ipv6_addr_copy(&ip6h->saddr, &oip6h->daddr); + ipv6_addr_copy(&ip6h->daddr, &oip6h->saddr); + + tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); + /* Truncate to length (no data) */ + tcph->doff = sizeof(struct tcphdr)/4; + tcph->source = otcph.dest; + tcph->dest = otcph.source; + + if (otcph.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; + tcph->check = 0; + + /* Adjust TCP checksum */ + tcph->check = csum_ipv6_magic(&nskb->nh.ipv6h->saddr, + &nskb->nh.ipv6h->daddr, + sizeof(struct tcphdr), IPPROTO_TCP, + csum_partial((char *)tcph, + sizeof(struct tcphdr), 0)); + +#if 0 + connection_attach(nskb, oldskb->nfct); +#endif + + NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, nskb, NULL, nskb->dst->dev, + maybe_reroute); + + dst_release(dst); +} + +static void send_unreach(struct sk_buff *skb_in, unsigned char code) +{ + struct ipv6hdr *ip6h, *hdr = skb_in->nh.ipv6h; + struct icmp6hdr *icmp6h; + struct dst_entry *dst = NULL; + struct rt6_info *rt; + int tmo; + __u32 csum; + unsigned int len, datalen, hh_len; + int saddr_type, daddr_type; + unsigned int ptr, ip6off; + u8 proto; + struct flowi fl; + struct sk_buff *nskb; + char *data; + + saddr_type = ipv6_addr_type(&hdr->saddr); + daddr_type = ipv6_addr_type(&hdr->daddr); + + if ((!(saddr_type & IPV6_ADDR_UNICAST)) || + (!(daddr_type & IPV6_ADDR_UNICAST))) { + DEBUGP("ip6t_REJECT: addr is not unicast.\n"); + return; + } + + ip6off = skb_in->nh.raw - skb_in->data; + proto = hdr->nexthdr; + ptr = ipv6_skip_exthdr(skb_in, ip6off + sizeof(struct ipv6hdr), &proto, + skb_in->len - ip6off); + + if ((ptr < 0) || (ptr > skb_in->len)) { + ptr = ip6off + sizeof(struct ipv6hdr); + proto = hdr->nexthdr; + } else if (proto == IPPROTO_ICMPV6) { + u8 type; + + if (skb_copy_bits(skb_in, ptr + offsetof(struct icmp6hdr, + icmp6_type), &type, 1)) { + DEBUGP("ip6t_REJECT: Can't get ICMPv6 type\n"); + return; + } + + if (!(type & ICMPV6_INFOMSG_MASK)) { + DEBUGP("ip6t_REJECT: no reply to icmp error\n"); + return; + } + } else if (proto == IPPROTO_UDP) { + int plen = skb_in->len - (ptr - ip6off); + uint16_t check; + + if (plen < sizeof(struct udphdr)) { + DEBUGP("ip6t_REJECT: too short\n"); + return; + } + + if (skb_copy_bits(skb_in, ptr + offsetof(struct udphdr, check), + &check, 2)) { + if (net_ratelimit()) + printk("ip6t_REJECT: can't get copy from skb"); + return; + } + + if (check && + csum_ipv6_magic(&hdr->saddr, &hdr->daddr, plen, + IPPROTO_UDP, + skb_checksum(skb_in, ptr, plen, 0))) { + DEBUGP("ip6t_REJECT: UDP checksum is invalid.\n"); + return; + } + } + + memset(&fl, 0, sizeof(fl)); + fl.proto = IPPROTO_ICMPV6; + ipv6_addr_copy(&fl.fl6_src, &hdr->daddr); + ipv6_addr_copy(&fl.fl6_dst, &hdr->saddr); + fl.fl_icmp_type = ICMPV6_DEST_UNREACH; + fl.fl_icmp_code = code; + + if (ip6_dst_lookup(NULL, &dst, &fl)) { + return; + } + + rt = (struct rt6_info *)dst; + tmo = 1*HZ; + + if (rt->rt6i_dst.plen < 128) + tmo >>= ((128 - rt->rt6i_dst.plen)>>5); + + if (!xrlim_allow(dst, tmo)) { + if (net_ratelimit()) + printk("ip6t_REJECT: rate limitted\n"); + goto dst_release_out; + } + + len = skb_in->len + sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr); + + if (len > dst_pmtu(dst)) + len = dst_pmtu(dst); + if (len > IPV6_MIN_MTU) + len = IPV6_MIN_MTU; + + datalen = len - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr); + hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15; + + nskb = alloc_skb(hh_len + 15 + dst->header_len + dst->trailer_len + len, + GFP_ATOMIC); + + if (!nskb) { + if (net_ratelimit()) + printk("ip6t_REJECT: can't alloc skb\n"); + goto dst_release_out; + } + + nskb->priority = 0; + nskb->dst = dst; + dst_hold(dst); + + skb_reserve(nskb, hh_len + dst->header_len); + + ip6h = nskb->nh.ipv6h = (struct ipv6hdr *) + skb_put(nskb, sizeof(struct ipv6hdr)); + ip6h->version = 6; + ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT); + ip6h->nexthdr = IPPROTO_ICMPV6; + ip6h->payload_len = htons(datalen + sizeof(struct icmp6hdr)); + ipv6_addr_copy(&ip6h->saddr, &hdr->daddr); + ipv6_addr_copy(&ip6h->daddr, &hdr->saddr); + + icmp6h = (struct icmp6hdr *) skb_put(nskb, sizeof(struct icmp6hdr)); + icmp6h->icmp6_type = ICMPV6_DEST_UNREACH; + icmp6h->icmp6_code = code; + icmp6h->icmp6_cksum = 0; + + data = skb_put(nskb, datalen); + + csum = csum_partial((unsigned char *)icmp6h, sizeof(struct icmp6hdr), 0); + csum = skb_copy_and_csum_bits(skb_in, ip6off, data, datalen, csum); + icmp6h->icmp6_cksum = csum_ipv6_magic(&hdr->saddr, &hdr->daddr, + datalen + sizeof(struct icmp6hdr), + IPPROTO_ICMPV6, csum); + +#if 0 + connection_attach(nskb, skb_in->nfct); +#endif + NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, nskb, NULL, nskb->dst->dev, + maybe_reroute); + +dst_release_out: + dst_release(dst); +} + +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; + + DEBUGP(KERN_DEBUG "%s: medium point\n", __FUNCTION__); + /* WARNING: This code causes reentry within ip6tables. + This means that the ip6tables jump stack is now crap. We + must return an absolute verdict. --RR */ + switch (reject->with) { + case IP6T_ICMP6_NO_ROUTE: + send_unreach(*pskb, ICMPV6_NOROUTE); + break; + case IP6T_ICMP6_ADM_PROHIBITED: + send_unreach(*pskb, ICMPV6_ADM_PROHIBITED); + break; + case IP6T_ICMP6_NOT_NEIGHBOUR: + send_unreach(*pskb, ICMPV6_NOT_NEIGHBOUR); + break; + case IP6T_ICMP6_ADDR_UNREACH: + send_unreach(*pskb, ICMPV6_ADDR_UNREACH); + break; + case IP6T_ICMP6_PORT_UNREACH: + send_unreach(*pskb, ICMPV6_PORT_UNREACH); + break; + case IP6T_ICMP6_ECHOREPLY: + /* Do nothing */ + break; + case IP6T_TCP_RESET: + send_reset(*pskb); + break; + default: + if (net_ratelimit()) + printk(KERN_WARNING "ip6t_REJECT: case %u not handled yet\n", reject->with); + break; + } + + return NF_DROP; +} + +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("ip6t_REJECT: targinfosize %u != 0\n", targinfosize); + return 0; + } + + /* Only allow these for packet filtering. */ + if (strcmp(tablename, "filter") != 0) { + DEBUGP("ip6t_REJECT: 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("ip6t_REJECT: bad hook mask %X\n", hook_mask); + return 0; + } + + if (rejinfo->with == IP6T_ICMP6_ECHOREPLY) { + printk("ip6t_REJECT: ECHOREPLY is not supported.\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("ip6t_REJECT: TCP_RESET illegal for non-tcp\n"); + return 0; + } + } + + return 1; +} + +static struct ip6t_target ip6t_reject_reg = { + .name = "REJECT", + .target = reject6_target, + .checkentry = check, + .me = 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 --exclude '*.orig' linux-2.6.7.org/net/ipv6/netfilter/ip6t_fuzzy.c linux-2.6.7/net/ipv6/netfilter/ip6t_fuzzy.c --- linux-2.6.7.org/net/ipv6/netfilter/ip6t_fuzzy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv6/netfilter/ip6t_fuzzy.c 2004-09-07 12:33:12.081714936 +0200 @@ -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 --exclude '*.orig' linux-2.6.7.org/net/ipv6/netfilter/ip6t_nth.c linux-2.6.7/net/ipv6/netfilter/ip6t_nth.c --- linux-2.6.7.org/net/ipv6/netfilter/ip6t_nth.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.7/net/ipv6/netfilter/ip6t_nth.c 2004-09-07 12:33:17.548883800 +0200 @@ -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);