diff -urN linux-2419p5-ipt126a-base/Documentation/Configure.help linux-2419p5-ipt126a-extra/Documentation/Configure.help --- linux-2419p5-ipt126a-base/Documentation/Configure.help Sat Mar 30 23:23:09 2002 +++ linux-2419p5-ipt126a-extra/Documentation/Configure.help Sat Mar 30 23:39:59 2002 @@ -2435,6 +2435,50 @@ If you want to compile it as a module, say 'M' here and read Documentation/modules.txt. If unsure, say 'N'. +Eggdrop bot support +CONFIG_IP_NF_EGG + If you are running an eggdrop hub bot on this machine, then you + may want to enable this feature. This enables eggdrop bots to share + their user file to other eggdrop bots. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +H.323 (netmeeting) support +CONFIG_IP_NF_H323 + H.323 is a standard signalling protocol used by teleconferencing + softwares like netmeeting. With the ip_conntrack_h323 and + the ip_nat_h323 modules you can support the protocol on a connection + tracking/NATing firewall. + + If you want to compile it as a module, say 'M' here and read + Documentation/modules.txt. If unsure, say 'N'. + +Talk protocol support +CONFIG_IP_NF_TALK + The talk protocols (both otalk/talk - or talk/ntalk, to confuse + you by the different namings about which is old or which is new :-) + use an additional channel to setup the talk session and a separated + data channel for the actual conversation (like in FTP). Both the + initiating and the setup channels are over UDP, while the data channel + is over TCP, on a random port. The conntrack part of this extension + will enable you to let in/out talk sessions easily by matching these + connections as RELATED by the state match, while the NAT part helps + you to let talk sessions trough a NAT machine. + + If you want to compile it as a module, say 'M' here and read + Documentation/modules.txt. If unsure, say 'N'. + +TFTP protocol support +CONFIG_IP_NF_TFTP + TFTP connection tracking helper, this is required depending + on how restrictive your ruleset is. + If you are using a tftp client behind -j SNAT or -j MASQUERADING + you will need this. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `Y'. + FTP protocol support CONFIG_IP_NF_FTP Tracking FTP connections is problematic: special helpers are @@ -2627,6 +2671,17 @@ Documentation/modules.txt. If unsure, say `N'. +recent match support +CONFIG_IP_NF_MATCH_RECENT + This option allows you to create lists of recently seen source + addresses and then match against that list based on number of + times seen and the time since last seen. An option is also + availible to require a match against TTL in addition to source + address. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + TOS match support CONFIG_IP_NF_MATCH_TOS TOS matching allows you to match packets based on the Type Of @@ -2647,6 +2702,19 @@ Documentation/modules.txt. If unsure, say `N'. +RPC match support +CONFIG_IP_NF_MATCH_RPC + This adds CONFIG_IP_NF_MATCH_RPC, which supplies two modules, + ip_conntrack_rpc_udp and ip_conntrack_rpc_tcp, which track + portmapper requests using UDP and TCP respectively. It also adds + the record_rpc match for iptables, which matches if the source of + the packet has requested that port through the portmapper before, + or it is a new GET request to the portmapper, allowing effective RPC + filtering. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + Connection state match support CONFIG_IP_NF_MATCH_STATE Connection state matching allows you to match packets based on their @@ -2656,6 +2724,14 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +String match support (EXPERIMENTAL) +CONFIG_IP_NF_MATCH_STRING + String matching alows you to match packets which contain a + specified string of characters. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + Unclean match support CONFIG_IP_NF_MATCH_UNCLEAN Unclean packet matching matches any strange or invalid packets, by @@ -2863,6 +2939,14 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. + +Helper match support +CONFIG_IP_NF_MATCH_HELPER + Helper matching allows you to match packets in dynamic connections + tracked by a conntrack-helper, ie. ip_conntrack_ftp + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `Y'. TCPMSS match support CONFIG_IP_NF_MATCH_TCPMSS diff -urN linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ip_conntrack.h linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ip_conntrack.h --- linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ip_conntrack.h Sat Mar 30 23:03:53 2002 +++ linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ip_conntrack.h Sat Mar 30 23:39:24 2002 @@ -68,6 +68,9 @@ #endif /* Add protocol helper include file here */ +#include +#include + #include #include @@ -105,6 +108,8 @@ union { /* insert conntrack helper private data (expect) here */ + struct ip_ct_talk_expect exp_talk_info; + struct ip_ct_h225_expect exp_h225_info; struct ip_ct_ftp_expect exp_ftp_info; struct ip_ct_irc_expect exp_irc_info; @@ -157,6 +162,8 @@ union { /* insert conntrack helper private data (master) here */ + struct ip_ct_talk_master ct_talk_info; + struct ip_ct_h225_master ct_h225_info; struct ip_ct_ftp_master ct_ftp_info; struct ip_ct_irc_master ct_irc_info; } help; diff -urN linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ip_conntrack_h323.h linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ip_conntrack_h323.h --- linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ip_conntrack_h323.h Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ip_conntrack_h323.h Sat Mar 30 23:35:21 2002 @@ -0,0 +1,32 @@ +#ifndef _IP_CONNTRACK_H323_H +#define _IP_CONNTRACK_H323_H +/* H.323 connection tracking. */ + +#ifndef __KERNEL__ +#error Only in kernel. +#endif + +/* Protects H.323 related data */ +DECLARE_LOCK_EXTERN(ip_h323_lock); + +/* Default H.225 port */ +#define H225_PORT 1720 + +/* This structure is per expected connection */ +struct ip_ct_h225_expect { + u_int16_t port; /* Port of the H.225 helper/RTCP/RTP channel */ + enum ip_conntrack_dir dir; /* Direction of the original connection */ + unsigned int offset; /* offset of the address in the payload */ +}; + +/* This structure exists only once per master */ +struct ip_ct_h225_master { + int is_h225; /* H.225 or H.245 connection */ +#ifdef CONFIG_IP_NF_NAT_NEEDED + enum ip_conntrack_dir dir; /* Direction of the original connection */ + u_int32_t seq[IP_CT_DIR_MAX]; /* Exceptional packet mangling for signal addressess... */ + unsigned int offset[IP_CT_DIR_MAX]; /* ...and the offset of the addresses in the payload */ +#endif +}; + +#endif /* _IP_CONNTRACK_H323_H */ diff -urN linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ip_conntrack_rpc.h linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ip_conntrack_rpc.h --- linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ip_conntrack_rpc.h Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ip_conntrack_rpc.h Sat Mar 30 23:38:12 2002 @@ -0,0 +1,71 @@ +/* RPC extension for IP connection tracking. */ +/* Marcelo Barbosa Lima - marcelo.lima@dcc.unicamp.br */ + +#include +#include +#include +#include +#include + +#include +#include + +/* Data in RPC packet is encoded in XDR */ +#define IXDR_GET_INT32(buf) ((u_int32_t) ntohl((uint32_t)*buf)) + +#if 0 +#define DEBUGRPC printk +#else +#define DEBUGRPC(format, args...) +#endif + +/* Fast timeout, to deny DoS atacks */ +#define EXP (60 * HZ) + +/* Normal timeouts */ +#define EXPIRES (180 * HZ) + + /* For future conections RPC, using client's cache bindings */ + /* I'll use ip_conntrack_lock to lock these lists */ + +/* This identifies each request and stores protocol */ + +struct request_p { + struct list_head list; + + u_int32_t xid; + u_int32_t ip; + u_int16_t port; + + /* Protocol */ + u_int16_t proto; + + struct timer_list timeout; +}; + +struct expect_rpc { + /* list */ + struct list_head list; + + /* binding */ + u_int32_t ip; + u_int16_t port; + u_int32_t ip_rmt; + u_int16_t proto; + + /* Expect RPC conection for long time, but this time expires */ + struct timer_list timeout; +}; + +inline int expect_rpc_cmp(const struct expect_rpc *l, u_int16_t port, + u_int32_t ip, u_int32_t ip_rmt, u_int16_t proto) +{ + return (l->ip == ip && l->port == port && + l->ip_rmt == ip_rmt && l->proto == proto); +} + +inline int request_p_cmp(const struct request_p *p, u_int32_t xid, + u_int32_t ip, u_int32_t port) { + return(p->xid == xid && p->ip == ip && p->port); + +} diff -urN linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ip_conntrack_talk.h linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ip_conntrack_talk.h --- linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ip_conntrack_talk.h Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ip_conntrack_talk.h Sat Mar 30 23:39:23 2002 @@ -0,0 +1,153 @@ +#ifndef _IP_CONNTRACK_TALK_H +#define _IP_CONNTRACK_TALK_H +/* TALK tracking. */ + +#ifndef __KERNEL__ +#error Only in kernel. +#endif + +#include +#include + +/* Protects talk part of conntracks */ +DECLARE_LOCK_EXTERN(ip_talk_lock); + +#define TALK_PORT 517 +#define NTALK_PORT 518 + +/* talk structures and constants from */ + +/* + * 4.3BSD struct sockaddr + */ +struct talk_addr { + u_int16_t ta_family; + u_int16_t ta_port; + u_int32_t ta_addr; + u_int32_t ta_junk1; + u_int32_t ta_junk2; +}; + +#define TALK_OLD_NSIZE 9 +#define TALK_NSIZE 12 +#define TALK_TTY_NSIZE 16 + +/* + * Client->server request message formats. + */ +struct talk_msg { + u_char type; /* request type, see below */ + char l_name[TALK_OLD_NSIZE];/* caller's name */ + char r_name[TALK_OLD_NSIZE];/* callee's name */ + u_char pad; + u_int32_t id_num; /* message id */ + int32_t pid; /* caller's process id */ + char r_tty[TALK_TTY_NSIZE];/* callee's tty name */ + struct talk_addr addr; /* old (4.3) style */ + struct talk_addr ctl_addr; /* old (4.3) style */ +}; + +struct ntalk_msg { + u_char vers; /* protocol version */ + u_char type; /* request type, see below */ + u_char answer; /* not used */ + u_char pad; + u_int32_t id_num; /* message id */ + struct talk_addr addr; /* old (4.3) style */ + struct talk_addr ctl_addr; /* old (4.3) style */ + int32_t pid; /* caller's process id */ + char l_name[TALK_NSIZE];/* caller's name */ + char r_name[TALK_NSIZE];/* callee's name */ + char r_tty[TALK_TTY_NSIZE];/* callee's tty name */ +}; + +struct ntalk2_msg { + u_char vers; /* talk protocol version */ + u_char type; /* request type */ + u_char answer; /* */ + u_char extended; /* !0 if additional parts */ + u_int32_t id_num; /* message id number (dels) */ + struct talk_addr addr; /* target address */ + struct talk_addr ctl_addr; /* reply to address */ + int32_t pid; /* caller's process id */ + char l_name[TALK_NSIZE]; /* caller's name */ + char r_name[TALK_NSIZE]; /* callee's name */ + char r_tty[TALK_TTY_NSIZE]; /* callee's tty */ +}; + +/* + * Server->client response message formats. + */ +struct talk_response { + u_char type; /* type of request message, see below */ + u_char answer; /* response to request message, see below */ + u_char pad[2]; + u_int32_t id_num; /* message id */ + struct talk_addr addr; /* address for establishing conversation */ +}; + +struct ntalk_response { + u_char vers; /* protocol version */ + u_char type; /* type of request message, see below */ + u_char answer; /* response to request message, see below */ + u_char pad; + u_int32_t id_num; /* message id */ + struct talk_addr addr; /* address for establishing conversation */ +}; + +struct ntalk2_response { + u_char vers; /* protocol version */ + u_char type; /* type of request message */ + u_char answer; /* response to request */ + u_char rvers; /* Version of answering vers*/ + u_int32_t id_num; /* message id number */ + struct talk_addr addr; /* address for connection */ + /* This is at the end to compatiblize this with NTALK version. */ + char r_name[TALK_NSIZE]; /* callee's name */ +}; + +#define TALK_STR(data, talk_str, member) ((struct talk_str *)data)->member) +#define TALK_RESP(data, ver, member) (ver ? ((struct ntalk_response *)data)->member : ((struct talk_response *)data)->member) +#define TALK_MSG(data, ver, member) (ver ? ((struct ntalk_msg *)data)->member : ((struct talk_msg *)data)->member) + +#define TALK_VERSION 0 /* protocol versions */ +#define NTALK_VERSION 1 +#define NTALK2_VERSION 2 + +/* message type values */ +#define LEAVE_INVITE 0 /* leave invitation with server */ +#define LOOK_UP 1 /* check for invitation by callee */ +#define DELETE 2 /* delete invitation by caller */ +#define ANNOUNCE 3 /* announce invitation by caller */ +/* NTALK2 */ +#define REPLY_QUERY 4 /* request reply data from local daemon */ + +/* answer values */ +#define SUCCESS 0 /* operation completed properly */ +#define NOT_HERE 1 /* callee not logged in */ +#define FAILED 2 /* operation failed for unexplained reason */ +#define MACHINE_UNKNOWN 3 /* caller's machine name unknown */ +#define PERMISSION_DENIED 4 /* callee's tty doesn't permit announce */ +#define UNKNOWN_REQUEST 5 /* request has invalid type value */ +#define BADVERSION 6 /* request has invalid protocol version */ +#define BADADDR 7 /* request has invalid addr value */ +#define BADCTLADDR 8 /* request has invalid ctl_addr value */ +/* NTALK2 */ +#define NO_CALLER 9 /* no-one calling answer from REPLY */ +#define TRY_HERE 10 /* Not on this machine, try this */ +#define SELECTIVE_REFUSAL 11 /* User Filter refusal. */ +#define MAX_RESPONSE_TYPE 11 /* Make sure this is updated */ + +/* We don't really need much for talk */ +struct ip_ct_talk_expect +{ + /* Port that was to be used */ + u_int16_t port; +}; + +/* This structure exists only once per master */ +struct ip_ct_talk_master +{ +}; + +#endif /* _IP_CONNTRACK_TALK_H */ diff -urN linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ip_conntrack_tcp.h linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ip_conntrack_tcp.h --- linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ip_conntrack_tcp.h Sat Dec 1 18:27:13 2001 +++ linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ip_conntrack_tcp.h Sat Mar 30 23:39:34 2002 @@ -8,25 +8,35 @@ enum tcp_conntrack { TCP_CONNTRACK_NONE, - TCP_CONNTRACK_ESTABLISHED, TCP_CONNTRACK_SYN_SENT, TCP_CONNTRACK_SYN_RECV, + TCP_CONNTRACK_ESTABLISHED, TCP_CONNTRACK_FIN_WAIT, - TCP_CONNTRACK_TIME_WAIT, - TCP_CONNTRACK_CLOSE, TCP_CONNTRACK_CLOSE_WAIT, TCP_CONNTRACK_LAST_ACK, + TCP_CONNTRACK_TIME_WAIT, + TCP_CONNTRACK_CLOSE, TCP_CONNTRACK_LISTEN, TCP_CONNTRACK_MAX }; +struct ip_ct_tcp_state { + u_int32_t td_end; /* max of seq + len */ + u_int32_t td_maxend; /* max of ack + max(win, 1) */ + u_int32_t td_maxwin; /* max(win) */ + u_int8_t td_scale; /* window scale factor */ +}; + struct ip_ct_tcp { - enum tcp_conntrack state; - - /* Poor man's window tracking: sequence number of valid ACK - handshake completion packet */ - u_int32_t handshake_ack; + enum tcp_conntrack state; /* state of the connection */ + struct ip_ct_tcp_state seen[2]; /* connection parameters per direction */ }; + +/* For NAT, when it mangles the packet */ +extern void ip_conntrack_tcp_update(struct ip_conntrack *conntrack, int dir, + struct iphdr *iph, size_t newlen, + struct tcphdr *tcph); + #endif /* _IP_CONNTRACK_TCP_H */ diff -urN linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ip_conntrack_tftp.h linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ip_conntrack_tftp.h --- linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ip_conntrack_tftp.h Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ip_conntrack_tftp.h Sat Mar 30 23:39:59 2002 @@ -0,0 +1,13 @@ +#ifndef _IP_CT_TFTP +#define _IP_CT_TFTP + +#define TFTP_PORT 69 + +struct tftphdr { + u_int16_t opcode; +}; + +#define TFTP_OPCODE_READ 1 +#define TFTP_OPCODE_WRITE 2 + +#endif /* _IP_CT_TFTP */ diff -urN linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ipt_helper.h linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ipt_helper.h --- linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ipt_helper.h Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ipt_helper.h Sat Mar 30 23:35:32 2002 @@ -0,0 +1,8 @@ +#ifndef _IPT_HELPER_H +#define _IPT_HELPER_H + +struct ipt_helper_info { + int invert; + char name[30]; +}; +#endif /* _IPT_HELPER_H */ diff -urN linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ipt_recent.h linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ipt_recent.h --- linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ipt_recent.h Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ipt_recent.h Sat Mar 30 23:37:36 2002 @@ -0,0 +1,18 @@ +#ifndef _IPT_RECENT_H +#define _IPT_RECENT_H + +const static int IPT_RECENT_CHECK = 1; +const static int IPT_RECENT_SET = 2; +const static int IPT_RECENT_UPDATE = 4; +const static int IPT_RECENT_REMOVE = 8; +const static int IPT_RECENT_TTL = 16; + +struct ipt_recent_info { + u_int32_t seconds; + u_int32_t hit_count; + u_int8_t check_set; + u_int8_t invert; + char name[200]; +}; + +#endif /*_IPT_RECENT_H*/ diff -urN linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ipt_string.h linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ipt_string.h --- linux-2419p5-ipt126a-base/include/linux/netfilter_ipv4/ipt_string.h Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/include/linux/netfilter_ipv4/ipt_string.h Sat Mar 30 23:38:25 2002 @@ -0,0 +1,21 @@ +#ifndef _IPT_STRING_H +#define _IPT_STRING_H + +/* *** PERFORMANCE TWEAK *** + * Packet size and search string threshold, + * above which sublinear searches is used. */ +#define IPT_STRING_HAYSTACK_THRESH 100 +#define IPT_STRING_NEEDLE_THRESH 20 + +#define BM_MAX_NLEN 256 +#define BM_MAX_HLEN 1024 + +typedef char *(*proc_ipt_search) (char *, char *, int, int); + +struct ipt_string_info { + char string[BM_MAX_NLEN]; + u_int16_t invert; + u_int16_t len; +}; + +#endif /* _IPT_STRING_H */ diff -urN linux-2419p5-ipt126a-base/include/linux/sysctl.h linux-2419p5-ipt126a-extra/include/linux/sysctl.h --- linux-2419p5-ipt126a-base/include/linux/sysctl.h Sat Mar 30 22:55:40 2002 +++ linux-2419p5-ipt126a-extra/include/linux/sysctl.h Sat Mar 30 23:39:34 2002 @@ -232,6 +232,7 @@ NET_IPV4_NEIGH=17, NET_IPV4_ROUTE=18, NET_IPV4_FIB_HASH=19, + NET_IPV4_NETFILTER=20, NET_IPV4_TCP_TIMESTAMPS=33, NET_IPV4_TCP_WINDOW_SCALING=34, @@ -340,6 +341,28 @@ NET_IPV4_CONF_MEDIUM_ID=14, }; +enum +{ + NET_IPV4_NF_CONNTRACK_MAX=1, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_NONE=2, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT=3, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_RECV=4, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED=5, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_FIN_WAIT=6, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE_WAIT=7, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LAST_ACK=8, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_TIME_WAIT=9, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE=10, + NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LISTEN=11, + NET_IPV4_NF_CONNTRACK_TCP_LOG_INVALID_SCALE=12, + NET_IPV4_NF_CONNTRACK_TCP_LOG_OUT_OF_WINDOW=13, + NET_IPV4_NF_CONNTRACK_TCP_BE_LIBERAL=14, + NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT=15, + NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT_STREAM=16, + NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT=17, + NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT=18 +}; + /* /proc/sys/net/ipv6 */ enum { NET_IPV6_CONF=16, diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/Config.in linux-2419p5-ipt126a-extra/net/ipv4/netfilter/Config.in --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/Config.in Sun Mar 31 00:08:49 2002 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/Config.in Sun Mar 31 00:08:03 2002 @@ -7,6 +7,10 @@ tristate 'Connection tracking (required for masq/NAT)' CONFIG_IP_NF_CONNTRACK if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then dep_tristate ' FTP protocol support' CONFIG_IP_NF_FTP $CONFIG_IP_NF_CONNTRACK + dep_tristate ' TFTP protocol support' CONFIG_IP_NF_TFTP $CONFIG_IP_NF_CONNTRACK + dep_tristate ' talk protocol support' CONFIG_IP_NF_TALK $CONFIG_IP_NF_CONNTRACK + dep_tristate ' H.323 (netmeeting) support' CONFIG_IP_NF_H323 $CONFIG_IP_NF_CONNTRACK + dep_tristate ' Eggdrop bot support' CONFIG_IP_NF_EGG $CONFIG_IP_NF_CONNTRACK dep_tristate ' IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CONNTRACK fi @@ -24,6 +28,7 @@ dep_tristate ' Multiple port match support' CONFIG_IP_NF_MATCH_MULTIPORT $CONFIG_IP_NF_IPTABLES dep_tristate ' Multiple port with ranges match support' CONFIG_IP_NF_MATCH_MPORT $CONFIG_IP_NF_IPTABLES dep_tristate ' TOS match support' CONFIG_IP_NF_MATCH_TOS $CONFIG_IP_NF_IPTABLES + dep_tristate ' recent match support' CONFIG_IP_NF_MATCH_RECENT $CONFIG_IP_NF_IPTABLES dep_tristate ' TIME match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_TIME $CONFIG_IP_NF_IPTABLES dep_tristate ' random match support' CONFIG_IP_NF_MATCH_RANDOM $CONFIG_IP_NF_IPTABLES dep_tristate ' psd match support' CONFIG_IP_NF_MATCH_PSD $CONFIG_IP_NF_IPTABLES @@ -33,14 +38,19 @@ dep_tristate ' LENGTH match support' CONFIG_IP_NF_MATCH_LENGTH $CONFIG_IP_NF_IPTABLES dep_tristate ' TTL match support' CONFIG_IP_NF_MATCH_TTL $CONFIG_IP_NF_IPTABLES dep_tristate ' tcpmss match support' CONFIG_IP_NF_MATCH_TCPMSS $CONFIG_IP_NF_IPTABLES + if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then + dep_tristate ' Helper match support' CONFIG_IP_NF_MATCH_HELPER $CONFIG_IP_NF_IPTABLES + fi dep_tristate ' realm match support' CONFIG_IP_NF_MATCH_REALM $CONFIG_IP_NF_IPTABLES if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then dep_tristate ' Connection state match support' CONFIG_IP_NF_MATCH_STATE $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES dep_tristate ' Connections/IP limit match support' CONFIG_IP_NF_MATCH_IPLIMIT $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES + dep_tristate ' RPC match support' CONFIG_IP_NF_MATCH_RPC $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES dep_tristate ' Connection tracking match support' CONFIG_IP_NF_MATCH_CONNTRACK $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES + dep_tristate ' String match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_STRING $CONFIG_IP_NF_IPTABLES dep_tristate ' Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES fi # The targets @@ -60,6 +70,22 @@ define_bool CONFIG_IP_NF_NAT_NEEDED y dep_tristate ' MASQUERADE target support' CONFIG_IP_NF_TARGET_MASQUERADE $CONFIG_IP_NF_NAT dep_tristate ' REDIRECT target support' CONFIG_IP_NF_TARGET_REDIRECT $CONFIG_IP_NF_NAT + # If they want talk, set to $CONFIG_IP_NF_NAT (m or y), + # or $CONFIG_IP_NF_TALK (m or y), whichever is weaker. Argh. + if [ "$CONFIG_IP_NF_TALK" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_TALK m + else + if [ "$CONFIG_IP_NF_TALK" = "y" ]; then + define_tristate CONFIG_IP_NF_NAT_TALK $CONFIG_IP_NF_NAT + fi + fi + if [ "$CONFIG_IP_NF_H323" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_H323 m + else + if [ "$CONFIG_IP_NF_H323" = "y" ]; then + define_tristate CONFIG_IP_NF_NAT_H323 $CONFIG_IP_NF_NAT + fi + fi dep_tristate ' SAME target support' CONFIG_IP_NF_TARGET_SAME $CONFIG_IP_NF_NAT dep_tristate ' NETMAP target support' CONFIG_IP_NF_TARGET_NETMAP $CONFIG_IP_NF_NAT bool ' NAT of local connections (READ HELP)' CONFIG_IP_NF_NAT_LOCAL @@ -80,6 +106,13 @@ else if [ "$CONFIG_IP_NF_FTP" = "y" ]; then define_tristate CONFIG_IP_NF_NAT_FTP $CONFIG_IP_NF_NAT + fi + fi + if [ "$CONFIG_IP_NF_TFTP" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_TFTP m + else + if [ "$CONFIG_IP_NF_TFTP" = "y" ]; then + define_tristate CONFIG_IP_NF_NAT_TFTP $CONFIG_IP_NF_NAT fi fi fi diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/Makefile linux-2419p5-ipt126a-extra/net/ipv4/netfilter/Makefile --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/Makefile Sat Mar 30 23:55:01 2002 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/Makefile Sun Mar 31 00:06:23 2002 @@ -31,8 +31,32 @@ # connection tracking obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o +ifdef CONFIG_IP_NF_NAT_NEEDED + export-objs += ip_conntrack_proto_tcp.o +endif + + +# talk protocol support +obj-$(CONFIG_IP_NF_TALK) += ip_conntrack_talk.o +obj-$(CONFIG_IP_NF_NAT_TALK) += ip_nat_talk.o +ifdef CONFIG_IP_NF_NAT_TALK + export-objs += ip_conntrack_talk.o +endif + + +# H.323 support +obj-$(CONFIG_IP_NF_H323) += ip_conntrack_h323.o +obj-$(CONFIG_IP_NF_NAT_H323) += ip_nat_h323.o +ifdef CONFIG_IP_NF_NAT_H323 + export-objs += ip_conntrack_h323.o +endif + + # connection tracking helpers obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o +obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o +obj-$(CONFIG_IP_NF_EGG) += ip_conntrack_egg.o + ifdef CONFIG_IP_NF_NAT_FTP export-objs += ip_conntrack_ftp.o endif @@ -44,6 +68,7 @@ # NAT helpers obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o +obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o # generic IP tables @@ -55,6 +80,7 @@ obj-$(CONFIG_IP_NF_NAT) += iptable_nat.o # 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_MARK) += ipt_mark.o @@ -67,6 +93,11 @@ obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o +obj-$(CONFIG_IP_NF_MATCH_RPC) += ip_conntrack_rpc_tcp.o ip_conntrack_rpc_udp.o ipt_record_rpc.o +export-objs += ip_conntrack_rpc_tcp.o ip_conntrack_rpc_udp.o + +obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o + obj-$(CONFIG_IP_NF_MATCH_TIME) += ipt_time.o @@ -88,6 +119,7 @@ obj-$(CONFIG_IP_NF_MATCH_IPLIMIT) += ipt_iplimit.o obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o +obj-$(CONFIG_IP_NF_MATCH_STRING) += ipt_string.o obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o obj-$(CONFIG_IP_NF_MATCH_REALM) += ipt_realm.o diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_core.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_core.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_core.c Sat Mar 30 23:03:53 2002 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_core.c Sat Mar 30 23:39:34 2002 @@ -58,7 +58,7 @@ LIST_HEAD(protocol_list); static LIST_HEAD(helpers); unsigned int ip_conntrack_htable_size = 0; -static int ip_conntrack_max = 0; +int ip_conntrack_max = 0; static atomic_t ip_conntrack_count = ATOMIC_INIT(0); struct list_head *ip_conntrack_hash; static kmem_cache_t *ip_conntrack_cachep; @@ -1253,29 +1253,6 @@ SO_ORIGINAL_DST, SO_ORIGINAL_DST+1, &getorigdst, 0, NULL }; -#define NET_IP_CONNTRACK_MAX 2089 -#define NET_IP_CONNTRACK_MAX_NAME "ip_conntrack_max" - -#ifdef CONFIG_SYSCTL -static struct ctl_table_header *ip_conntrack_sysctl_header; - -static ctl_table ip_conntrack_table[] = { - { NET_IP_CONNTRACK_MAX, NET_IP_CONNTRACK_MAX_NAME, &ip_conntrack_max, - sizeof(ip_conntrack_max), 0644, NULL, proc_dointvec }, - { 0 } -}; - -static ctl_table ip_conntrack_dir_table[] = { - {NET_IPV4, "ipv4", NULL, 0, 0555, ip_conntrack_table, 0, 0, 0, 0, 0}, - { 0 } -}; - -static ctl_table ip_conntrack_root_table[] = { - {CTL_NET, "net", NULL, 0, 0555, ip_conntrack_dir_table, 0, 0, 0, 0, 0}, - { 0 } -}; -#endif /*CONFIG_SYSCTL*/ - static int kill_all(const struct ip_conntrack *i, void *data) { return 1; @@ -1285,9 +1262,6 @@ supposed to kill the mall. */ void ip_conntrack_cleanup(void) { -#ifdef CONFIG_SYSCTL - unregister_sysctl_table(ip_conntrack_sysctl_header); -#endif ip_ct_attach = NULL; /* This makes sure all current packets have passed through netfilter framework. Roll on, two-stage module @@ -1366,20 +1340,6 @@ for (i = 0; i < ip_conntrack_htable_size; i++) INIT_LIST_HEAD(&ip_conntrack_hash[i]); - -/* This is fucking braindead. There is NO WAY of doing this without - the CONFIG_SYSCTL unless you don't want to detect errors. - Grrr... --RR */ -#ifdef CONFIG_SYSCTL - ip_conntrack_sysctl_header - = register_sysctl_table(ip_conntrack_root_table, 0); - if (ip_conntrack_sysctl_header == NULL) { - kmem_cache_destroy(ip_conntrack_cachep); - vfree(ip_conntrack_hash); - nf_unregister_sockopt(&so_getorigdst); - return -ENOMEM; - } -#endif /*CONFIG_SYSCTL*/ /* For use by ipt_REJECT */ ip_ct_attach = ip_conntrack_attach; diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_egg.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_egg.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_egg.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_egg.c Sat Mar 30 23:34:58 2002 @@ -0,0 +1,234 @@ +/* Eggdrop extension for IP connection tracking, Version 0.0.4 + * based on ip_conntrack_irc.c + * + * This module only supports the share userfile-send command, + * used by eggdrops to share it's userfile. + * + * There are no support for NAT at the moment. + * + * 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. + * + * Module load syntax: + * + * please give the ports of all Eggdrops You have running + * on your system, the default port is 3333. + * + * 2001-04-19: Security update. IP addresses are now compared + * to prevent unauthorized "related" access. + * + * 2002-03-25: Harald Welte : + * Port to netfilter 'newnat' API. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_c = 0; +static unsigned int egg_timeout = 300; + +MODULE_AUTHOR("Magnus Sandin "); +MODULE_DESCRIPTION("Eggdrop (userfile-sharing) connection tracking module"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of eggdrop servers"); +#endif + +DECLARE_LOCK(ip_egg_lock); +struct module *ip_conntrack_egg = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +int parse_command(char *data, char *data_end, u_int32_t * ip, u_int16_t * port) +/* tries to get the ip_addr and port out of a eggdrop command + return value: -1 on failure, 0 on success + data pointer to first byte of DCC command data + data_end pointer to last byte of dcc command data + ip returns parsed ip of dcc command + port returns parsed port of dcc command */ +{ + if (data > data_end) + return -1; + + *ip = simple_strtoul(data, &data, 10); + + /* skip blanks between ip and port */ + while (*data == ' ' && data < data_end) + data++; + + *port = simple_strtoul(data, &data, 10); + return 0; +} + + +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + /* tcplen not negative guarenteed by ip_conntrack_tcp.c */ + struct tcphdr *tcph = (void *) iph + iph->ihl * 4; + char *data = (char *) tcph + tcph->doff * 4; + char *data_limit; + u_int32_t tcplen = len - iph->ihl * 4; + u_int32_t datalen = tcplen - tcph->doff * 4; + int dir = CTINFO2DIR(ctinfo); + int bytes_scanned = 0; + struct ip_conntrack_expect exp; + + u_int32_t egg_ip; + u_int16_t egg_port; + + DEBUGP("entered\n"); + + /* If packet is coming from IRC server */ + if (dir != IP_CT_DIR_REPLY) + return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) { + DEBUGP("tcplen = %u\n", (unsigned) tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *) tcph, tcplen, 0))) { + DEBUGP("bad csum: %p %u %u.%u.%u.%u -> %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + data_limit = (char *) data + datalen; + while (datalen > 5 && bytes_scanned < 128) { + if (memcmp(data, "s us ", 5)) { + data++; + datalen--; + bytes_scanned++; + continue; + } + + data += 5; + + DEBUGP("Userfile-share found in connection " + "%u.%u.%u.%u -> %u.%u.%u.%u\n", + NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); + + if (parse_command((char *) data, data_limit, &egg_ip, + &egg_port)) { + DEBUGP("no data in userfile-share pkt\n"); + return NF_ACCEPT; + } + + memset(&exp, 0, sizeof(exp)); + + if (ct->tuplehash[dir].tuple.src.ip != htonl(egg_ip)) { + if (net_ratelimit()) + printk("Forged Eggdrop command from " + "%u.%u.%u.%u: %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[dir].tuple.src.ip), + HIPQUAD(egg_ip), egg_port); + return NF_ACCEPT; + } + + exp.tuple.src.ip = iph->daddr; + exp.tuple.src.u.tcp.port = 0; + exp.tuple.dst.ip = htonl(egg_ip); + exp.tuple.dst.u.tcp.port = htons(egg_port); + exp.tuple.dst.protonum = IPPROTO_TCP; + + exp.mask.dst.u.tcp.port = 0xffff; + exp.mask.dst.protonum = 0xffff; + + DEBUGP("expect_related %u.%u.%u.%u:%u - %u.%u.%u.%u:%u\n", + NIPQUAD(t.src.ip), ntohs(t.src.u.tcp.port), + NIPQUAD(t.dst.ip), ntohs(t.dst.u.tcp.port)); + + ip_conntrack_expect_related(ct, &exp); + break; + } + return NF_ACCEPT; +} + +static struct ip_conntrack_helper egg_helpers[MAX_PORTS]; +static char egg_names[MAX_PORTS][14]; /* eggdrop-65535 */ + +static void __exit fini(void); + +static int __init init(void) +{ + int i, ret; + char *tmpname; + + /* If no port given, default to standard eggdrop port */ + if (ports[0] == 0) + ports[0] = 3333; + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + memset(&egg_helpers[i], 0, + sizeof(struct ip_conntrack_helper)); + egg_helpers[i].tuple.src.u.tcp.port = htons(ports[i]); + egg_helpers[i].tuple.dst.protonum = IPPROTO_TCP; + egg_helpers[i].mask.src.u.tcp.port = 0xFFFF; + egg_helpers[i].mask.dst.protonum = 0xFFFF; + egg_helpers[i].max_expected = 1; + egg_helpers[i].timeout = egg_timeout; + egg_helpers[i].flags = IP_CT_HELPER_F_REUSE_EXPECT; + egg_helpers[i].me = THIS_MODULE; + egg_helpers[i].help = help; + + tmpname = &egg_names[i][0]; + if (ports[i] == 3333) + sprintf(tmpname, "eggdrop"); + else + sprintf(tmpname, "eggdrop-%d", ports[i]); + egg_helpers[i].name = tmpname; + + DEBUGP("port #%d: %d\n", i, ports[i]); + + ret = ip_conntrack_helper_register(&egg_helpers[i]); + + if (ret) { + printk("ip_conntrack_egg: ERROR registering helper " + "for port %d\n", ports[i]); + fini(); + return 1; + } + ports_c++; + } + return 0; +} + +static void __exit fini(void) +{ + int i; + for (i = 0; i < ports_c; i++) { + DEBUGP("unregistering helper for port %d\n", ports[i]); + ip_conntrack_helper_unregister(&egg_helpers[i]); + } +} + +module_init(init); +module_exit(fini); diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_h323.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_h323.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_h323.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_h323.c Sat Mar 30 23:35:21 2002 @@ -0,0 +1,310 @@ +/* + * H.323 'brute force' extension for H.323 connection tracking. + * Jozsef Kadlecsik + * + * Based on ip_masq_h323.c for 2.2 kernels from CoRiTel, Sofia project. + * (http://www.coritel.it/projects/sofia/nat/) + * Uses Sampsa Ranta's excellent idea on using expectfn to 'bind' + * the unregistered helpers to the conntrack entries. + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("H.323 'brute force' connection tracking module"); +MODULE_LICENSE("GPL"); + +DECLARE_LOCK(ip_h323_lock); +struct module *ip_conntrack_h323 = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/* FIXME: This should be in userspace. Later. */ +static int h245_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + unsigned char *data = (unsigned char *) tcph + tcph->doff * 4; + unsigned char *data_limit; + u_int32_t tcplen = len - iph->ihl * 4; + u_int32_t datalen = tcplen - tcph->doff * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_ct_h225_master *info = &ct->help.ct_h225_info; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info; + u_int16_t data_port; + u_int32_t data_ip; + unsigned int i; + + DEBUGP("ct_h245_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), + NIPQUAD(iph->daddr), ntohs(tcph->dest)); + + /* Can't track connections formed before we registered */ + if (!info) + return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ct_h245_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header or too short packet? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) { + DEBUGP("ct_h245_help: tcplen = %u\n", (unsigned)tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcplen, 0))) { + DEBUGP("ct_h245_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + data_limit = (unsigned char *) data + datalen; + /* bytes: 0123 45 + ipadrr port */ + for (i = 0; data < (data_limit - 5); data++, i++) { + data_ip = *((u_int32_t *)data); + if (data_ip == iph->saddr) { + data_port = *((u_int16_t *)(data + 4)); + memset(&expect, 0, sizeof(expect)); + /* update the H.225 info */ + DEBUGP("ct_h245_help: new RTCP/RTP requested %u.%u.%u.%u:->%u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), + NIPQUAD(iph->saddr), ntohs(data_port)); + LOCK_BH(&ip_h323_lock); + info->is_h225 = H225_PORT + 1; + exp_info->port = data_port; + exp_info->dir = dir; + exp_info->offset = i; + + exp->seq = ntohl(tcph->seq) + i; + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { data_ip, + { data_port }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = NULL; + + /* Ignore failure; should only happen with NAT */ + ip_conntrack_expect_related(ct, exp); + + UNLOCK_BH(&ip_h323_lock); + } + } + + return NF_ACCEPT; + +} + +/* H.245 helper is not registered! */ +static struct ip_conntrack_helper h245 = + { { NULL, NULL }, + "H.245", /* name */ + IP_CT_HELPER_F_REUSE_EXPECT, /* flags */ + NULL, /* module */ + 8, /* max_ expected */ + 240, /* timeout */ + { { 0, { 0 } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h245_help /* helper */ + }; + +static int h225_expect(struct ip_conntrack *ct) +{ + WRITE_LOCK(&ip_conntrack_lock); + ct->helper = &h245; + DEBUGP("h225_expect: helper for %p added\n", ct); + WRITE_UNLOCK(&ip_conntrack_lock); + + return NF_ACCEPT; /* unused */ +} + +/* FIXME: This should be in userspace. Later. */ +static int h225_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + unsigned char *data = (unsigned char *) tcph + tcph->doff * 4; + unsigned char *data_limit; + u_int32_t tcplen = len - iph->ihl * 4; + u_int32_t datalen = tcplen - tcph->doff * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_ct_h225_master *info = &ct->help.ct_h225_info; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info; + u_int16_t data_port; + u_int32_t data_ip; + unsigned int i; + + DEBUGP("ct_h225_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), + NIPQUAD(iph->daddr), ntohs(tcph->dest)); + + /* Can't track connections formed before we registered */ + if (!info) + return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ct_h225_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header or too short packet? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) { + DEBUGP("ct_h225_help: tcplen = %u\n", (unsigned)tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcplen, 0))) { + DEBUGP("ct_h225_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + data_limit = (unsigned char *) data + datalen; + /* bytes: 0123 45 + ipadrr port */ + for (i = 0; data < (data_limit - 5); data++, i++) { + data_ip = *((u_int32_t *)data); + if (data_ip == iph->saddr) { + data_port = *((u_int16_t *)(data + 4)); + if (data_port == tcph->source) { + /* Signal address */ + DEBUGP("ct_h225_help: sourceCallSignalAddress from %u.%u.%u.%u\n", + NIPQUAD(iph->saddr)); + /* Update the H.225 info so that NAT can mangle the address/port + even when we have no expected connection! */ +#ifdef CONFIG_IP_NF_NAT_NEEDED + LOCK_BH(&ip_h323_lock); + info->dir = dir; + info->seq[IP_CT_DIR_ORIGINAL] = ntohl(tcph->seq) + i; + info->offset[IP_CT_DIR_ORIGINAL] = i; + UNLOCK_BH(&ip_h323_lock); +#endif + } else { + memset(&expect, 0, sizeof(expect)); + + /* update the H.225 info */ + LOCK_BH(&ip_h323_lock); + info->is_h225 = H225_PORT; + exp_info->port = data_port; + exp_info->dir = dir; + exp_info->offset = i; + + exp->seq = ntohl(tcph->seq) + i; + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { data_ip, + { data_port }, + IPPROTO_TCP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = h225_expect; + + /* Ignore failure */ + ip_conntrack_expect_related(ct, exp); + + DEBUGP("ct_h225_help: new H.245 requested %u.%u.%u.%u->%u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), + NIPQUAD(iph->saddr), ntohs(data_port)); + + UNLOCK_BH(&ip_h323_lock); + } +#ifdef CONFIG_IP_NF_NAT_NEEDED + } else if (data_ip == iph->daddr) { + data_port = *((u_int16_t *)(data + 4)); + if (data_port == tcph->dest) { + /* Signal address */ + DEBUGP("ct_h225_help: destCallSignalAddress %u.%u.%u.%u\n", + NIPQUAD(iph->daddr)); + /* Update the H.225 info so that NAT can mangle the address/port + even when we have no expected connection! */ + LOCK_BH(&ip_h323_lock); + info->dir = dir; + info->seq[IP_CT_DIR_REPLY] = ntohl(tcph->seq) + i; + info->offset[IP_CT_DIR_REPLY] = i; + UNLOCK_BH(&ip_h323_lock); + } +#endif + } + } + + return NF_ACCEPT; + +} + +static struct ip_conntrack_helper h225 = + { { NULL, NULL }, + "H.225", /* name */ + IP_CT_HELPER_F_REUSE_EXPECT, /* flags */ + THIS_MODULE, /* module */ + 2, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(H225_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h225_help /* helper */ + }; + +static int __init init(void) +{ + return ip_conntrack_helper_register(&h225); +} + +static void __exit fini(void) +{ + /* Unregister H.225 helper */ + ip_conntrack_helper_unregister(&h225); +} + +#ifdef CONFIG_IP_NF_NAT_NEEDED +EXPORT_SYMBOL(ip_h323_lock); +#endif + +module_init(init); +module_exit(fini); diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_proto_generic.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_proto_generic.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_proto_generic.c Sat Mar 30 23:03:53 2002 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_proto_generic.c Sat Mar 30 23:39:34 2002 @@ -4,7 +4,7 @@ #include #include -#define GENERIC_TIMEOUT (600*HZ) +unsigned long ip_ct_generic_timeout = 600*HZ; static int generic_pkt_to_tuple(const void *datah, size_t datalen, struct ip_conntrack_tuple *tuple) @@ -43,7 +43,7 @@ struct iphdr *iph, size_t len, enum ip_conntrack_info conntrackinfo) { - ip_ct_refresh(conntrack, GENERIC_TIMEOUT); + ip_ct_refresh(conntrack, ip_ct_generic_timeout); return NF_ACCEPT; } diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_proto_icmp.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_proto_icmp.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_proto_icmp.c Sat Mar 30 23:03:53 2002 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_proto_icmp.c Sat Mar 30 23:39:34 2002 @@ -6,7 +6,7 @@ #include #include -#define ICMP_TIMEOUT (30*HZ) +unsigned long ip_ct_icmp_timeout = 30*HZ; #if 0 #define DEBUGP printk @@ -82,7 +82,7 @@ ct->timeout.function((unsigned long)ct); } else { atomic_inc(&ct->proto.icmp.count); - ip_ct_refresh(ct, ICMP_TIMEOUT); + ip_ct_refresh(ct, ip_ct_icmp_timeout); } return NF_ACCEPT; diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_proto_tcp.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_proto_tcp.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_proto_tcp.c Sat Mar 30 23:03:53 2002 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_proto_tcp.c Sat Mar 30 23:39:34 2002 @@ -1,3 +1,14 @@ +/* + * TCP connection tracking + */ + +/* + * Changes: + * Jozsef Kadlecsik: Real stateful connection tracking + * Modified state transitions table + * Window scaling support + */ + #define __NO_VERSION__ #include #include @@ -8,6 +19,7 @@ #include #include +#include #include #include @@ -16,6 +28,7 @@ #if 0 #define DEBUGP printk +#define DDEBUGP #else #define DEBUGP(format, args...) #endif @@ -23,6 +36,14 @@ /* Protects conntrack->proto.tcp */ static DECLARE_RWLOCK(tcp_lock); +/* Logging options */ +int ip_ct_tcp_log_invalid_scale = 1; +int ip_ct_tcp_log_out_of_window = 1; + +/* "Be conservative in what you do, + be liberal in what you accept from others." */ +int ip_ct_tcp_be_liberal = 0; + /* FIXME: Examine ipfilter's timeouts and conntrack transitions more closely. They're more complex. --RR */ @@ -33,66 +54,202 @@ static const char *tcp_conntrack_names[] = { "NONE", - "ESTABLISHED", "SYN_SENT", "SYN_RECV", + "ESTABLISHED", "FIN_WAIT", - "TIME_WAIT", - "CLOSE", "CLOSE_WAIT", "LAST_ACK", + "TIME_WAIT", + "CLOSE", "LISTEN" }; -#define SECS *HZ +#define SECS * HZ #define MINS * 60 SECS #define HOURS * 60 MINS #define DAYS * 24 HOURS - -static unsigned long tcp_timeouts[] -= { 30 MINS, /* TCP_CONNTRACK_NONE, */ - 5 DAYS, /* TCP_CONNTRACK_ESTABLISHED, */ - 2 MINS, /* TCP_CONNTRACK_SYN_SENT, */ - 60 SECS, /* TCP_CONNTRACK_SYN_RECV, */ - 2 MINS, /* TCP_CONNTRACK_FIN_WAIT, */ - 2 MINS, /* TCP_CONNTRACK_TIME_WAIT, */ - 10 SECS, /* TCP_CONNTRACK_CLOSE, */ - 60 SECS, /* TCP_CONNTRACK_CLOSE_WAIT, */ - 30 SECS, /* TCP_CONNTRACK_LAST_ACK, */ - 2 MINS, /* TCP_CONNTRACK_LISTEN, */ +unsigned long ip_ct_tcp_timeout_none = 30 MINS; +unsigned long ip_ct_tcp_timeout_syn_sent = 2 MINS; +unsigned long ip_ct_tcp_timeout_syn_recv = 60 SECS; +unsigned long ip_ct_tcp_timeout_established = 5 DAYS; +unsigned long ip_ct_tcp_timeout_fin_wait = 2 MINS; +unsigned long ip_ct_tcp_timeout_close_wait = 12 HOURS; +unsigned long ip_ct_tcp_timeout_last_ack = 30 SECS; +unsigned long ip_ct_tcp_timeout_time_wait = 2 MINS; +unsigned long ip_ct_tcp_timeout_close = 10 SECS; +unsigned long ip_ct_tcp_timeout_listen = 2 MINS; + +static unsigned long * tcp_timeouts[] += { &ip_ct_tcp_timeout_none, /* TCP_CONNTRACK_NONE, */ + &ip_ct_tcp_timeout_syn_sent, /* TCP_CONNTRACK_SYN_SENT, */ + &ip_ct_tcp_timeout_syn_recv, /* TCP_CONNTRACK_SYN_RECV, */ + &ip_ct_tcp_timeout_established, /* TCP_CONNTRACK_ESTABLISHED, */ + &ip_ct_tcp_timeout_fin_wait, /* TCP_CONNTRACK_FIN_WAIT, */ + &ip_ct_tcp_timeout_close_wait, /* TCP_CONNTRACK_CLOSE_WAIT, */ + &ip_ct_tcp_timeout_last_ack, /* TCP_CONNTRACK_LAST_ACK, */ + &ip_ct_tcp_timeout_time_wait, /* TCP_CONNTRACK_TIME_WAIT, */ + &ip_ct_tcp_timeout_close, /* TCP_CONNTRACK_CLOSE, */ + &ip_ct_tcp_timeout_listen, /* TCP_CONNTRACK_LISTEN, */ }; #define sNO TCP_CONNTRACK_NONE -#define sES TCP_CONNTRACK_ESTABLISHED #define sSS TCP_CONNTRACK_SYN_SENT #define sSR TCP_CONNTRACK_SYN_RECV +#define sES TCP_CONNTRACK_ESTABLISHED #define sFW TCP_CONNTRACK_FIN_WAIT -#define sTW TCP_CONNTRACK_TIME_WAIT -#define sCL TCP_CONNTRACK_CLOSE #define sCW TCP_CONNTRACK_CLOSE_WAIT #define sLA TCP_CONNTRACK_LAST_ACK +#define sTW TCP_CONNTRACK_TIME_WAIT +#define sCL TCP_CONNTRACK_CLOSE #define sLI TCP_CONNTRACK_LISTEN #define sIV TCP_CONNTRACK_MAX -static enum tcp_conntrack tcp_conntracks[2][5][TCP_CONNTRACK_MAX] = { +/* + * The TCP state transition table needs a few words... + * + * We are the man in the middle. All the packets go through us + * but might get lost in transit to the destination. + * It is assumed that the destinations can't receive segments + * we haven't seen. + * + * The checked segment is in window. + * + * The meaning of the states are: + * + * NONE: initial state + * SYN_SENT: SYN-only packet seen + * SYN_RECV: SYN-ACK packet seen + * ESTABLISHED: ACK packet seen + * FIN_WAIT: FIN packet seen + * CLOSE_WAIT: ACK seen (after FIN) + * LAST_ACK: FIN seen (after FIN) + * TIME_WAIT: last ACK seen + * CLOSE: closed connection + * + * LISTEN state is not used. + * + */ +static enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = { { -/* ORIGINAL */ -/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI */ -/*syn*/ {sSS, sES, sSS, sSR, sSS, sSS, sSS, sSS, sSS, sLI }, -/*fin*/ {sTW, sFW, sSS, sTW, sFW, sTW, sCL, sTW, sLA, sLI }, -/*ack*/ {sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sLA, sES }, -/*rst*/ {sCL, sCL, sSS, sCL, sCL, sTW, sCL, sCL, sCL, sCL }, -/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } +/* ORIGINAL */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*syn*/ { sSS, sSS, sCL, sCL, sCL, sCL, sCL, sSS, sSS, sIV }, +/* + * sNO -> sSS Initialize a new connection + * sSS -> sSS Retransmitted SYN + * sSR -> sCL Error: SYNs in window outside the SYN_SENT state + * are errors. Receiver will either go back to the + * LISTEN state or reply with RST. + * sES -> sCL + * sFW -> sCL + * sCW -> sCL + * sLA -> sCL + * sTW -> sSS Reopened connection (RFC 1122). + * sCL -> sSS + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*synack*/ { sSR, sSR, sES, sCL, sCL, sCL, sCL, sCL, sCL, sIV }, +/* + * sNO -> sSR Assumed: hey, we've just started up! + * sSS -> sSR Simultaneous open. + * sSR -> sES Ditto. + * sES -> sCL Error. + * sFW -> sCL + * sCW -> sCL + * sLA -> sCL + * sTW -> sCL + * sCL -> sCL + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*fin*/ { sTW, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV }, +/* + * sNO -> sTW We assume TIME-WAIT state. + * sSS -> sIV Client migth not send FIN in this state. + * sSR -> sFW Close started. + * sES -> sFW + * sFW -> sLA FIN seen in both directions, waiting for + * the last ACK. + * Migth be a retransmitted FIN as well... + * sCW -> sLA + * sLA -> sLA Retransmitted FIN. Remain in the same state. + * sTW -> sTW + * sCL -> sCL + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*ack*/ { sES, sIV, sES, sES, sCW, sCW, sTW, sTW, sCL, sIV }, +/* + * sNO -> sES Assumed. + * sSS -> sIV ACK is invalid: we haven't seen a SYN/ACK yet. + * sSR -> sES Established state is reached. + * sES -> sES :-) + * sFW -> sCW Normal close request answered by ACK. + * sCW -> sCW + * sLA -> sTW Last ACK detected. + * sTW -> sTW Retransmitted last ACK. Remain in the same state. + * sCL -> sCL + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*rst*/ { sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV }, +/*none*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } }, { -/* REPLY */ -/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI */ -/*syn*/ {sSR, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSR }, -/*fin*/ {sCL, sCW, sSS, sTW, sTW, sTW, sCL, sCW, sLA, sLI }, -/*ack*/ {sCL, sES, sSS, sSR, sFW, sTW, sCL, sCW, sCL, sLI }, -/*rst*/ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sLA, sLI }, -/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } +/* REPLY */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*syn*/ { sIV, sSS, sSR, sCL, sCL, sCL, sCL, sSS, sSS, sIV }, +/* + * sNO -> sIV Never reached. + * sSS -> sSS Simultaneous open. + * sSR -> sSR Simultaneous open, retransmitted SYN. + * We have seen a SYN/ACK, but it seems + * it is delayed or got lost. + * sES -> sCL Error. + * sFW -> sCL + * sCW -> sCL + * sLA -> sCL + * sTW -> sSS Reopened connection. + * sCL -> sSS + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*synack*/ { sIV, sSR, sES, sCL, sCL, sCL, sCL, sCL, sCL, sIV }, +/* + * sSS -> sSR Standard open. + * sSR -> sES Simultaneous open. + * sES -> sCL Error. + * sFW -> sCL + * sCW -> sCL + * sLA -> sCL + * sTW -> sCL + * sCL -> sCL + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*fin*/ { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV }, +/* + * sSS -> sIV Server might not send FIN in this state. + * sSR -> sFW Close started. + * sES -> sFW + * sFW -> sLA FIN seen in both directions. + * sCW -> sLA + * sLA -> sLA Retransmitted FIN. + * sTW -> sTW + * sCL -> sCL + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*ack*/ { sIV, sIV, sES, sES, sCW, sCW, sTW, sTW, sCL, sIV }, +/* + * sSS -> sIV ACK is invalid: we haven't seen a SYN/ACK yet. + * sSR -> sES Simultaneous open. + * sES -> sES :-) + * sFW -> sCW Normal close request answered by ACK. + * sCW -> sCW + * sLA -> sTW Last ACK detected. + * sTW -> sTW Retransmitted last ACK. + * sCL -> sCL + */ +/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ +/*rst*/ { sIV, sCL, sCL, sCL, sCL, sIV, sCL, sCL, sCL, sIV }, +/*none*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV } } }; @@ -139,19 +296,337 @@ static unsigned int get_conntrack_index(const struct tcphdr *tcph) { - if (tcph->rst) return 3; - else if (tcph->syn) return 0; - else if (tcph->fin) return 1; - else if (tcph->ack) return 2; - else return 4; + if (tcph->rst) return 4; + else if (tcph->syn) return (tcph->ack ? 1 : 0); + else if (tcph->fin) return 2; + else if (tcph->ack) return 3; + else return 5; +} + +/* From ipt_LOG.c... */ +/* Use lock to serialize, so printks don't overlap */ +static spinlock_t log_lock = SPIN_LOCK_UNLOCKED; + +static void log_packet(struct iphdr *iph, struct tcphdr *tcph) +{ + /* Important fields: + * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ + /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ + printk("SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ", + NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); + + /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ + printk("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", + ntohs(iph->tot_len), iph->tos & IPTOS_TOS_MASK, + iph->tos & IPTOS_PREC_MASK, iph->ttl, ntohs(iph->id)); + + /* Max length: 6 "CE DF MF " */ + if (ntohs(iph->frag_off) & IP_CE) + printk("CE "); + if (ntohs(iph->frag_off) & IP_DF) + printk("DF "); + /* ... but conntrack don't see fragments */ + + if (iph->ihl * 4 != sizeof(struct iphdr)) { + unsigned int i; + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + printk("OPT ("); + for (i = sizeof(struct iphdr); i < iph->ihl * 4; i++) + printk("%02X", ((u_int8_t *)iph)[i]); + printk(") "); + } + + /* Max length: 10 "PROTO=TCP " */ + printk("PROTO=TCP "); + + /* Max length: 20 "SPT=65535 DPT=65535 " */ + printk("SPT=%u DPT=%u ", + ntohs(tcph->source), ntohs(tcph->dest)); + /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ + printk("SEQ=%u ACK=%u ", + ntohl(tcph->seq), ntohl(tcph->ack_seq)); + /* Max length: 13 "WINDOW=65535 " */ + printk("WINDOW=%u ", ntohs(tcph->window)); + /* Max length: 9 "RES=0x3F " */ + printk("RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(tcph) & TCP_RESERVED_BITS) >> 22)); + /* Max length: 36 "URG ACK PSH RST SYN FIN " */ + if (tcph->urg) + printk("URG "); + if (tcph->ack) + printk("ACK "); + if (tcph->psh) + printk("PSH "); + if (tcph->rst) + printk("RST "); + if (tcph->syn) + printk("SYN "); + if (tcph->fin) + printk("FIN "); + /* Max length: 11 "URGP=65535 " */ + printk("URGP=%u ", ntohs(tcph->urg_ptr)); + + if (tcph->doff * 4 != sizeof(struct tcphdr)) { + unsigned int i; + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + printk("OPT ("); + for (i =sizeof(struct tcphdr); i < tcph->doff * 4; i++) + printk("%02X", ((u_int8_t *)tcph)[i]); + printk(") "); + } + + /* Proto Max log string length */ + /* IP: 40+46+6+11+127 = 230 */ + /* TCP: 10+max(25,20+30+13+9+36+11+127) = 256 */ +} + +#define log_invalid_packet(iph, tcph, format, arg...) \ +do { \ + spin_lock_bh(&log_lock); \ + log_packet(iph, tcph); \ + printk(format, ## arg); \ + spin_unlock_bh(&log_lock); \ +} while (0); + +/* TCP connection tracking based on 'Real Stateful TCP Packet Filtering + in IP Filter' by Guido van Rooij. + + http://www.nluug.nl/events/sane2000/papers.html + http://www.iae.nl/users/guido/papers/tcp_filtering.ps.gz + + The boundaries according to the article: + + td_maxend = max(ack + max(win,1)) seen in reply packets + td_maxwin = max(max(win, 1)) seen in sent packets + td_end = max(seq + len) seen in sent packets + + I. Upper bound for valid data: seq + len <= sender.td_maxend + II. Lower bound for valid data: seq >= sender.td_end - receiver.td_maxwin + III. Upper bound for valid ack: ack <= receiver.td_end + IV. Lower bound for valid ack: ack >= receiver.td_end - MAXACKWINDOW + + The upper bound limit for a valid ack is not ignored - + we doesn't have to deal with fragments. +*/ + +#define SEGMENT_SEQ_PLUS_LEN(seq, len, iph, tcph) (seq + len - (iph->ihl + tcph->doff)*4 \ + + (tcph->syn ? 1 : 0) + (tcph->fin ? 1 : 0)) + +/* Fixme: what about big packets? */ +#define MAXACKWINCONST 66000 +#define MAXACKWINDOW(receiver) ((receiver)->td_scale && (receiver)->td_maxwin < MAXACKWINCONST \ + ? (receiver)->td_maxwin : MAXACKWINCONST) + +/* + * Simplified tcp_parse_options routine from tcp_input.c + */ +static u_int8_t tcp_scale_option(struct iphdr *iph, struct tcphdr *tcph) +{ + unsigned char *ptr; + int length = (tcph->doff*4) - sizeof(struct tcphdr); + + ptr = (unsigned char *)(tcph + 1); + + while (length > 0) { + int opcode=*ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return 0; + case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ + length--; + continue; + default: + opsize=*ptr++; + if (opsize < 2) /* "silly options" */ + return 0; + if (opsize > length) + break; /* don't parse partial options */ + + if (opcode == TCPOPT_WINDOW && opsize == TCPOLEN_WINDOW) { + u_int8_t scale = *(u_int8_t *)ptr; + + if (scale > 14) { + /* See RFC1323 for an explanation of the limit to 14 */ + if (ip_ct_tcp_log_invalid_scale && net_ratelimit()) + log_invalid_packet(iph, tcph, "Illegal window scaling value %u > 14 ignored\n", + scale); + scale = 14; + } + return scale; + } + ptr += opsize - 2; + length -= opsize; + } + } + return 0; +} + +static int tcp_in_window(struct ip_ct_tcp_state *sender, + struct ip_ct_tcp_state *receiver, + struct iphdr *iph, size_t len, + struct tcphdr *tcph) +{ + __u32 seq, ack, end, swin; + __u16 win; + int res; + + /* + * Get the required data from the packet. + */ + seq = ntohl(tcph->seq); + ack = ntohl(tcph->ack_seq); + win = ntohs(tcph->window); + end = SEGMENT_SEQ_PLUS_LEN(seq, len, iph, tcph); + + DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu seq=%u ack=%u win=%u end=%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), NIPQUAD(iph->daddr), ntohs(tcph->dest), + seq, ack, win, end); + DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i receiver end=%u maxend=%u maxwin=%u scale=%i\n", + sender->td_end, sender->td_maxend, sender->td_maxwin, sender->td_scale, + receiver->td_end, receiver->td_maxend, receiver->td_maxwin, receiver->td_scale); + + if (sender->td_end == 0) { + /* + * Initialize sender data. + */ + if (tcph->syn && tcph->ack) { + /* + * Outgoing SYN-ACK in reply to a SYN. + * + * Fixme: here we loose supporting simultaneous open... + */ + sender->td_end = + sender->td_maxend = end; + sender->td_maxwin = (win == 0 ? 1 : win); + sender->td_scale = tcp_scale_option(iph, tcph); + } else { + /* + * We are in the middle of a connection, + * its history is lost for us. + * Let's try to use the data from the packet. + */ + sender->td_end = end; + sender->td_maxwin = (win == 0 ? 1 : win); + sender->td_maxend = end + sender->td_maxwin; + sender->td_scale = 0; + } + } + + if (!(tcph->ack)) { + /* + * If there is no ACK, just pretend it was set and OK. + */ + ack = receiver->td_end; + } else if (((tcp_flag_word(tcph) & (TCP_FLAG_ACK|TCP_FLAG_RST)) == (TCP_FLAG_ACK|TCP_FLAG_RST)) + && (ack == 0)) { + /* + * Broken TCP stacks, that set ACK in RST packets as well + * with zero ack value. + */ + ack = receiver->td_end; + } + + if (seq == end) + /* + * Packets contains no data: we assume it is valid + * and check the ack value only. + */ + seq = end = sender->td_end; + + DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i receiver end=%u maxend=%u maxwin=%u scale=%i\n", + sender->td_end, sender->td_maxend, sender->td_maxwin, sender->td_scale, + receiver->td_end, receiver->td_maxend, receiver->td_maxwin, receiver->td_scale); + DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu seq=%u ack=%u win=%u end=%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), NIPQUAD(iph->daddr), ntohs(tcph->dest), + seq, ack, win, end); + DEBUGP("tcp_in_window: I=%i II=%i III=%i IV=%i\n", + before(end, sender->td_maxend + 1), + after(seq, sender->td_end - receiver->td_maxwin - 1), + before(ack, receiver->td_end + 1), + after(ack, receiver->td_end - MAXACKWINDOW(receiver))); + + if (before(end, sender->td_maxend + 1) && + after(seq, sender->td_end - receiver->td_maxwin - 1) && + before(ack, receiver->td_end + 1) && + after(ack, receiver->td_end - MAXACKWINDOW(receiver))) { + /* + * Take into account window scaling (RFC 1323). + */ + + swin = win; + + if (tcph->ack && sender->td_scale && receiver->td_scale) + swin <<= sender->td_scale; + /* + * Update sender data. + */ + if (sender->td_maxwin < swin) + sender->td_maxwin = swin; + if (after(end, sender->td_end)) + sender->td_end = end; + if (after(ack + swin, receiver->td_maxend - 1)) { + receiver->td_maxend = ack + swin; + if (win == 0) + receiver->td_maxend++; + } + res = 1; + } else { + if (ip_ct_tcp_log_out_of_window && net_ratelimit()) + log_invalid_packet(iph, tcph, "Out of window data: %s\n", + before(end, sender->td_maxend + 1) ? + after(seq, sender->td_end - receiver->td_maxwin - 1) ? + before(ack, receiver->td_end + 1) ? + after(ack, receiver->td_end - MAXACKWINDOW(receiver)) ? "BUG" + : "ACK is under the lower bound (possibly overly delayed ACK)" + : "ACK is over the upper bound (ACKed data has never seen yet)" + : "SEQ is under the lower bound (retransmitted already ACKed data)" + : "SEQ is over the upper bound (over the window of the receiver)"); + res = ip_ct_tcp_be_liberal && !tcph->rst; + } + + DEBUGP("tcp_in_window: res=%i sender end=%u maxend=%u maxwin=%u receiver end=%u maxend=%u maxwin=%u\n", + res, sender->td_end, sender->td_maxend, sender->td_maxwin, + receiver->td_end, receiver->td_maxend, receiver->td_maxwin); + + return res; +} + +/* Update sender->td_end after NAT successfully mangled the packet */ +void ip_conntrack_tcp_update(struct ip_conntrack *conntrack, int dir, + struct iphdr *iph, size_t newlen, + struct tcphdr *tcph) +{ + __u32 end; +#ifdef DDEBUGP + struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[dir]; + struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[!dir]; +#endif + + end = SEGMENT_SEQ_PLUS_LEN(ntohl(tcph->seq), newlen, iph, tcph); + + WRITE_LOCK(&tcp_lock); + /* + * We have to worry for the ack in the reply packet only... + */ + if (after(end, conntrack->proto.tcp.seen[dir].td_end)) + conntrack->proto.tcp.seen[dir].td_end = end; + WRITE_UNLOCK(&tcp_lock); + DEBUGP("tcp_update: sender end=%u maxend=%u maxwin=%u scale=%i receiver end=%u maxend=%u maxwin=%u scale=%i\n", + sender->td_end, sender->td_maxend, sender->td_maxwin, sender->td_scale, + receiver->td_end, receiver->td_maxend, receiver->td_maxwin, receiver->td_scale); } + /* Returns verdict for packet, or -1 for invalid. */ static int tcp_packet(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len, enum ip_conntrack_info ctinfo) { - enum tcp_conntrack newconntrack, oldtcpstate; + enum tcp_conntrack new_state, old_state; + enum ip_conntrack_dir dir; struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); /* We're guaranteed to have the base header, but maybe not the @@ -162,48 +637,65 @@ } WRITE_LOCK(&tcp_lock); - oldtcpstate = conntrack->proto.tcp.state; - newconntrack + old_state = conntrack->proto.tcp.state; + dir = CTINFO2DIR(ctinfo); + + new_state = tcp_conntracks - [CTINFO2DIR(ctinfo)] - [get_conntrack_index(tcph)][oldtcpstate]; + [dir] + [get_conntrack_index(tcph)][old_state]; + + if (new_state == TCP_CONNTRACK_SYN_SENT + && old_state >= TCP_CONNTRACK_TIME_WAIT) { + /* Attempt to reopen a closed connection. + * Delete this connection and look up again. */ + WRITE_UNLOCK(&tcp_lock); + if (del_timer(&conntrack->timeout)) + conntrack->timeout.function((unsigned long)conntrack); + return NF_REPEAT; + } else if (!(new_state == TCP_CONNTRACK_MAX + || tcp_in_window(&conntrack->proto.tcp.seen[dir], + &conntrack->proto.tcp.seen[!dir], + iph, len, tcph))) + new_state = TCP_CONNTRACK_MAX; + + DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), NIPQUAD(iph->daddr), ntohs(tcph->dest), + (tcph->syn ? 1 : 0), (tcph->ack ? 1 : 0), (tcph->fin ? 1 : 0), (tcph->rst ? 1 : 0), + old_state, new_state); /* Invalid */ - if (newconntrack == TCP_CONNTRACK_MAX) { + if (new_state == TCP_CONNTRACK_MAX) { DEBUGP("ip_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n", - CTINFO2DIR(ctinfo), get_conntrack_index(tcph), - conntrack->proto.tcp.state); + dir, get_conntrack_index(tcph), + old_state); WRITE_UNLOCK(&tcp_lock); return -1; } - conntrack->proto.tcp.state = newconntrack; - - /* Poor man's window tracking: record SYN/ACK for handshake check */ - if (oldtcpstate == TCP_CONNTRACK_SYN_SENT - && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY - && tcph->syn && tcph->ack) - conntrack->proto.tcp.handshake_ack - = htonl(ntohl(tcph->seq) + 1); + conntrack->proto.tcp.state = new_state; WRITE_UNLOCK(&tcp_lock); - /* If only reply is a RST, we can consider ourselves not to - have an established connection: this is a fairly common - problem case, so we can delete the conntrack - immediately. --RR */ - if (!(conntrack->status & IPS_SEEN_REPLY) && tcph->rst) { - if (del_timer(&conntrack->timeout)) - conntrack->timeout.function((unsigned long)conntrack); - } else { - /* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV */ - if (oldtcpstate == TCP_CONNTRACK_SYN_RECV - && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL - && tcph->ack && !tcph->syn - && tcph->ack_seq == conntrack->proto.tcp.handshake_ack) - set_bit(IPS_ASSURED_BIT, &conntrack->status); - - ip_ct_refresh(conntrack, tcp_timeouts[newconntrack]); + if (!(conntrack->status & IPS_SEEN_REPLY)) { + /* If only reply is a RST, we can consider ourselves not to + have an established connection: this is a fairly common + problem case, so we can delete the conntrack + immediately. --RR */ + if (tcph->rst) { + if (del_timer(&conntrack->timeout)) + conntrack->timeout.function((unsigned long)conntrack); + + return NF_ACCEPT; + } else { + /* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV + or a valid answer for an picked up connection */ + if ((old_state == TCP_CONNTRACK_SYN_RECV + || old_state == TCP_CONNTRACK_ESTABLISHED) + && new_state == TCP_CONNTRACK_ESTABLISHED) + set_bit(IPS_ASSURED_BIT, &conntrack->status); + } } + ip_ct_refresh(conntrack, *tcp_timeouts[new_state]); return NF_ACCEPT; } @@ -212,23 +704,57 @@ static int tcp_new(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len) { - enum tcp_conntrack newconntrack; + enum tcp_conntrack new_state; struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); /* Don't need lock here: this conntrack not in circulation yet */ - newconntrack + new_state = tcp_conntracks[0][get_conntrack_index(tcph)] [TCP_CONNTRACK_NONE]; /* Invalid: delete conntrack */ - if (newconntrack == TCP_CONNTRACK_MAX) { + if (new_state == TCP_CONNTRACK_MAX) { DEBUGP("ip_conntrack_tcp: invalid new deleting.\n"); return 0; } - conntrack->proto.tcp.state = newconntrack; + if (new_state == TCP_CONNTRACK_SYN_SENT) { + conntrack->proto.tcp.seen[0].td_end = + SEGMENT_SEQ_PLUS_LEN(ntohl(tcph->seq), len, iph, tcph); + conntrack->proto.tcp.seen[0].td_maxwin = ntohs(tcph->window); + if (conntrack->proto.tcp.seen[0].td_maxwin == 0) + conntrack->proto.tcp.seen[0].td_maxwin = 1; + conntrack->proto.tcp.seen[0].td_maxend = + conntrack->proto.tcp.seen[0].td_end; + conntrack->proto.tcp.seen[0].td_scale = tcp_scale_option(iph, tcph); + } else { + /* + * We are in the middle of a connection, + * its history is lost for us. + * Let's try to use the data from the packet. + */ + conntrack->proto.tcp.seen[0].td_end = + SEGMENT_SEQ_PLUS_LEN(ntohl(tcph->seq), len, iph, tcph); + conntrack->proto.tcp.seen[0].td_maxwin = ntohs(tcph->window); + if (conntrack->proto.tcp.seen[0].td_maxwin == 0) + conntrack->proto.tcp.seen[0].td_maxwin = 1; + conntrack->proto.tcp.seen[0].td_maxend = + conntrack->proto.tcp.seen[0].td_end + + conntrack->proto.tcp.seen[0].td_maxwin; + conntrack->proto.tcp.seen[0].td_scale = 0; + } + + conntrack->proto.tcp.seen[1].td_end = 0; + conntrack->proto.tcp.seen[1].td_maxend = 0; + conntrack->proto.tcp.seen[1].td_maxwin = 1; + conntrack->proto.tcp.seen[1].td_scale = 0; + + conntrack->proto.tcp.state = new_state; + return 1; } + +EXPORT_SYMBOL(ip_conntrack_tcp_update); static int tcp_exp_matches_pkt(struct ip_conntrack_expect *exp, struct sk_buff **pskb) diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_proto_udp.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_proto_udp.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_proto_udp.c Sat Mar 30 23:03:53 2002 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_proto_udp.c Sat Mar 30 23:39:34 2002 @@ -6,8 +6,8 @@ #include #include -#define UDP_TIMEOUT (30*HZ) -#define UDP_STREAM_TIMEOUT (180*HZ) +unsigned long ip_ct_udp_timeout = 30*HZ; +unsigned long ip_ct_udp_timeout_stream = 180*HZ; static int udp_pkt_to_tuple(const void *datah, size_t datalen, struct ip_conntrack_tuple *tuple) @@ -52,11 +52,11 @@ /* If we've seen traffic both ways, this is some kind of UDP stream. Extend timeout. */ if (conntrack->status & IPS_SEEN_REPLY) { - ip_ct_refresh(conntrack, UDP_STREAM_TIMEOUT); + ip_ct_refresh(conntrack, 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, UDP_TIMEOUT); + ip_ct_refresh(conntrack, ip_ct_udp_timeout); return NF_ACCEPT; } diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c Sat Mar 30 23:38:12 2002 @@ -0,0 +1,344 @@ +/* RPC extension for IP connection tracking. */ +/* Marcelo Barbosa Lima - marcelo.lima@dcc.unicamp.br */ +/* Yes, my english is very poor :-) */ + +/* 25 Mar 2002 Harald Welte + * Ported to netfilter 'newnat' API + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +DECLARE_RWLOCK(ipct_rpc_tcp_lock); +#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ipct_rpc_tcp_lock) +#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ipct_rpc_tcp_lock) +#include + + /* For future conections RPC, using client's cache bindings */ + +LIST_HEAD(expect_rpc_list_tcp); +LIST_HEAD(request_p_list_tcp); + +void delete_request_p(unsigned long request_p_ul) { + struct request_p *p = (void *)request_p_ul; + + WRITE_LOCK(&ipct_rpc_tcp_lock); + LIST_DELETE(&request_p_list_tcp, p); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + kfree(p); + return; + +} + +void delete_binding(unsigned long rpc_binding_ul) { + struct expect_rpc *l = (void *)rpc_binding_ul; + + WRITE_LOCK(&ipct_rpc_tcp_lock); + LIST_DELETE(&expect_rpc_list_tcp, l); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + kfree(l); + return; +} + +void exp_cl(struct expect_rpc * t) { + WRITE_LOCK(&ipct_rpc_tcp_lock); + del_timer(&t->timeout); + LIST_DELETE(&expect_rpc_list_tcp, t); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + kfree(t); + return; +} + + +/* Clean timers kernel list in unregister to deny crash */ +void clean_expect(struct list_head *list){ + struct list_head *first = list->prev; + struct list_head *temp = list->next; + struct list_head *aux; + + if(list_empty(list)) return; + + while(first != temp) { + aux = temp->next; + exp_cl((struct expect_rpc *)temp); + temp = aux; + } + exp_cl((struct expect_rpc *)temp); + return; +} + +void req_cl(struct request_p * r){ + WRITE_LOCK(&ipct_rpc_tcp_lock); + del_timer(&r->timeout); + LIST_DELETE(&request_p_list_tcp, r); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + kfree(r); + return; +} + +/* Clean timers kernel list in unregister to deny crash */ +void clean_request(struct list_head *list){ + struct list_head *first = list->prev; + struct list_head *temp = list->next; + struct list_head *aux; + + if(list_empty(list)) return; + + while(first != temp) { + aux = temp->next; + req_cl((struct request_p *)temp); + temp = aux; + } + req_cl((struct request_p *)temp); + return; +} + + +void alloc_request_p(u_int32_t xid, u_int16_t proto, u_int32_t ip, + u_int16_t port){ + struct request_p *req_p; + + /* Verifies if entry already exists */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + req_p = LIST_FIND(&request_p_list_tcp, request_p_cmp, + struct request_p *, xid, ip, port); + + if(req_p){ + /* Refresh timeout */ + if (del_timer(&req_p->timeout)) { + req_p->timeout.expires = jiffies + EXP; + add_timer(&req_p->timeout); + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return; + + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + + /* Allocate new request_p */ + req_p = (struct request_p *) kmalloc(sizeof(struct request_p), GFP_ATOMIC); + if(!req_p) { + DEBUGRPC("Can't allocate request_p\n"); + return; + } + *req_p = ((struct request_p) {{ NULL, NULL }, xid, ip, port, proto, + { { NULL, NULL }, jiffies + EXP, (unsigned long)req_p, + NULL }}); + + /*Initialize timer */ + init_timer(&req_p->timeout); + req_p->timeout.function = delete_request_p; + add_timer(&req_p->timeout); + + /* Put in list */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + list_prepend(&request_p_list_tcp, req_p); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return; + +} + +/* Update Internal expect list */ +void binding(u_int32_t ip, u_int16_t port, u_int32_t ip_rmt, u_int16_t proto){ + struct expect_rpc *rpc_binding; + + /* Verifies if entry already exists */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + rpc_binding = LIST_FIND(&expect_rpc_list_tcp, expect_rpc_cmp, + struct expect_rpc *, port, ip, ip_rmt, proto); + + if(rpc_binding){ + /* Refresh timeout */ + if (del_timer(&rpc_binding->timeout)) { + rpc_binding->timeout.expires = jiffies + EXPIRES; + add_timer(&rpc_binding->timeout); + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return; + + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + + /* Allocate new binding */ + rpc_binding = (struct expect_rpc *) kmalloc(sizeof(struct expect_rpc), GFP_ATOMIC); + if(!rpc_binding) { + DEBUGRPC("Can't allocate rpc_binding\n"); + return; + } + *rpc_binding = ((struct expect_rpc) {{ NULL, NULL }, ip, port, ip_rmt, proto, + { { NULL, NULL }, jiffies + EXPIRES, (unsigned long)rpc_binding, + NULL }}); + + /*Initialize timer */ + init_timer(&rpc_binding->timeout); + rpc_binding->timeout.function = delete_binding; + add_timer(&rpc_binding->timeout); + + /* Put in list */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + list_prepend(&expect_rpc_list_tcp, rpc_binding); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return; + +} + +/* RPC using TCP - help */ +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + const u_int32_t *data = (const u_int32_t *)tcph + tcph->doff; + + /* Translstion's buffer for XDR */ + u_int16_t port_buf; + + size_t tcplen = len - iph->ihl * 4; + + /* Important for tracking */ + int dir = CTINFO2DIR(ctinfo); + u_int32_t xid; + struct request_p *req_p; + + /* This works for packets like handshake packets, ignore */ + if (len == ((tcph->doff + iph->ihl) * 4)) return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED) { + DEBUGRPC("RPC_TCP: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) { + DEBUGRPC("RPC: tcplen = %u\n", (unsigned)tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcplen, 0))) { + DEBUGRPC("RPC_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, IP_PARTS(iph->saddr), + IP_PARTS(iph->daddr)); + return NF_ACCEPT; + } + + + /* If packet is client packet */ + if(!dir){ + + /* Tests if packet len is ok */ + if((tcplen - (tcph->doff * 4)) != 60) + return NF_ACCEPT; + + /* Jump RPC Packet length in XDR */ + data++; + + /* Get XID */ + xid = *data; + + /* Jump until procedure number */ + data = data + 5; + /* Is one get port procedure? */ + if(IXDR_GET_INT32(data) == 3){ + data++; + + /* Jump Credentials and Verfifier */ + data = data + IXDR_GET_INT32(data) + 2; + data = data + IXDR_GET_INT32(data) + 2; + + /* Get and store protocol in RPC client parameters */ + data = data + 2; + alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip, + ct->tuplehash[dir].tuple.src.u.all); + } + return NF_ACCEPT; + } + + /* Tests if packet len is ok */ + if((tcplen - (tcph->doff * 4)) != 32) + return NF_ACCEPT; + + /* Jump RPC Packet length in XDR */ + data++; + + /* XID is important */ + req_p = LIST_FIND(&request_p_list_tcp, request_p_cmp, + struct request_p *, *data, ct->tuplehash[!dir].tuple.src.ip, + ct->tuplehash[!dir].tuple.src.u.all); + + /* Drop packets for accurate tracking */ + if(!req_p) return NF_DROP; + data = data++; + + /* Verifies if packet is really a reply packet */ + if(IXDR_GET_INT32(data) != 1) { + DEBUGRPC("RPC_TCP: Packet with problems"); + return NF_ACCEPT; + } + data++; + + /* Is status accept? */ + if(IXDR_GET_INT32(data)) return NF_ACCEPT; + data++; + + /* Get Verifier lentgth. Jump verifier */ + data = data + IXDR_GET_INT32(data) + 2; + + /* Is accpet status "sucess"? */ + if(IXDR_GET_INT32(data)) return NF_ACCEPT; + data++; + + /* Get server port number */ + port_buf = (u_int16_t) IXDR_GET_INT32(data); + + /* All right. This binding must be in internal expect list */ + /* If port == 0, service is not registred */ + if(port_buf) + binding(ct->tuplehash[dir].tuple.src.ip, port_buf, + ct->tuplehash[!dir].tuple.src.ip, req_p->proto); + req_cl(req_p); + return NF_ACCEPT; +} + +struct ip_conntrack_helper rpc_tcp = { { NULL, NULL }, + "rpc-tcp", + 0, THIS_MODULE, 1, 0, + { { 0, { __constant_htons(111) } }, + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, + { 0, { 0 }, 0xFFFF } }, + help }; + +int init_module(void) +{ + printk("ip_conntrack_rpc_tcp.o\n"); + return ip_conntrack_helper_register(&rpc_tcp); +} + +void cleanup_module(void) +{ + clean_expect(&expect_rpc_list_tcp); + clean_request(&request_p_list_tcp); + ip_conntrack_helper_unregister(&rpc_tcp); +} + +struct module *ip_conntrack_rpc_tcp = THIS_MODULE; +EXPORT_SYMBOL(expect_rpc_list_tcp); +EXPORT_SYMBOL(ip_conntrack_rpc_tcp); +EXPORT_SYMBOL(ipct_rpc_tcp_lock); diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_rpc_udp.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_rpc_udp.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_rpc_udp.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_rpc_udp.c Sat Mar 30 23:38:12 2002 @@ -0,0 +1,336 @@ +/* RPC extension for IP connection tracking. */ +/* Marcelo Barbosa Lima - marcelo.lima@dcc.unicamp.br */ +/* Yes, my english is very poor :-) */ + +/* + * 25 Mar 2002 Harald Welte + * Port to netfilter 'newnat' API + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_RWLOCK(ipct_rpc_udp_lock); +#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ipct_rpc_udp_lock) +#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ipct_rpc_udp_lock) +#include + + /* For future conections RPC, using client's cache bindings */ +LIST_HEAD(expect_rpc_list_udp); +LIST_HEAD(request_p_list_udp); + +void delete_request_p(unsigned long request_p_ul) { + struct request_p *p = (void *)request_p_ul; + WRITE_LOCK(&ipct_rpc_udp_lock); + LIST_DELETE(&request_p_list_udp, p); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + kfree(p); + return; + +} + +void delete_binding(unsigned long rpc_binding_ul) { + struct expect_rpc *l = (void *)rpc_binding_ul; + WRITE_LOCK(&ipct_rpc_udp_lock); + LIST_DELETE(&expect_rpc_list_udp, l); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + kfree(l); + return; +} + +void exp_cl(struct expect_rpc * t) { + WRITE_LOCK(&ipct_rpc_udp_lock); + del_timer(&t->timeout); + LIST_DELETE(&expect_rpc_list_udp, t); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + kfree(t); + return; +} + + +/* Clean timers in unregister to deny crash */ +void clean_expect(struct list_head *list){ + struct list_head *first = list->prev; + struct list_head *temp = list->next; + struct list_head *aux; + + if(list_empty(list)) return; + + while(first != temp) { + aux = temp->next; + exp_cl((struct expect_rpc *)temp); + temp = aux; + } + exp_cl((struct expect_rpc *)temp); + return; +} + +void req_cl(struct request_p * r){ + WRITE_LOCK(&ipct_rpc_udp_lock); + del_timer(&r->timeout); + LIST_DELETE(&request_p_list_udp, r); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + kfree(r); + return; +} + +/* Clean timers in unregister to deny crash */ +void clean_request(struct list_head *list){ + struct list_head *first = list->prev; + struct list_head *temp = list->next; + struct list_head *aux; + + if(list_empty(list)) return; + + while(first != temp) { + aux = temp->next; + req_cl((struct request_p *)temp); + temp = aux; + } + req_cl((struct request_p *)temp); + return; +} + + +void alloc_request_p(u_int32_t xid, u_int16_t proto, u_int32_t ip, + u_int16_t port){ + struct request_p *req_p; + + /* Verifies if entry already exists */ + WRITE_LOCK(&ipct_rpc_udp_lock); + req_p = LIST_FIND(&request_p_list_udp, request_p_cmp, + struct request_p *, xid, ip, port); + + if(req_p){ + /* Refresh timeout */ + if (del_timer(&req_p->timeout)) { + req_p->timeout.expires = jiffies + EXP; + add_timer(&req_p->timeout); + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + return; + + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + + /* Allocate new request_p */ + req_p = (struct request_p *) kmalloc(sizeof(struct request_p), GFP_ATOMIC); + if(!req_p) { + DEBUGRPC("Can't allocate request_p\n"); + return; + } + *req_p = ((struct request_p) {{ NULL, NULL }, xid, ip, port, proto, + { {NULL, NULL}, jiffies + EXP, (unsigned long)req_p, + NULL }}); + + /*Initialize timer */ + init_timer(&req_p->timeout); + req_p->timeout.function = delete_request_p; + add_timer(&req_p->timeout); + + /* Put in list */ + WRITE_LOCK(&ipct_rpc_udp_lock); + list_prepend(&request_p_list_udp, req_p); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + return; + +} + +/* Update Internal expect list */ +void binding(u_int32_t ip, u_int16_t port, u_int32_t ip_rmt, u_int16_t proto){ + struct expect_rpc *rpc_binding; + + /* Verifies if entry already exists */ + WRITE_LOCK(&ipct_rpc_udp_lock); + rpc_binding = LIST_FIND(&expect_rpc_list_udp, expect_rpc_cmp, + struct expect_rpc *, port, ip, ip_rmt, proto); + + if(rpc_binding){ + /* Refresh timeout */ + if (del_timer(&rpc_binding->timeout)) { + rpc_binding->timeout.expires = jiffies + EXPIRES; + add_timer(&rpc_binding->timeout); + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + return; + + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + + /* Allocate new binding */ + rpc_binding = (struct expect_rpc *) kmalloc(sizeof(struct expect_rpc), GFP_ATOMIC); + if(!rpc_binding) { + DEBUGRPC("Can't allocate rpc_binding\n"); + return; + } + *rpc_binding = ((struct expect_rpc) {{ NULL, NULL }, ip, port, ip_rmt, proto, + { {NULL, NULL}, jiffies + EXPIRES, (unsigned long)rpc_binding, + NULL }}); + + /*Initialize timer */ + init_timer(&rpc_binding->timeout); + rpc_binding->timeout.function = delete_binding; + add_timer(&rpc_binding->timeout); + + /* Put in list */ + WRITE_LOCK(&ipct_rpc_udp_lock); + list_prepend(&expect_rpc_list_udp, rpc_binding); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + return; + +} + + +/* RPC using UDP - help */ +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct udphdr *udph = (void *)iph + iph->ihl * 4; + const u_int32_t *data = (const u_int32_t *)udph + 2; + + /* Checksum */ + const u_int16_t *chsm = (const u_int16_t *)udph + 3; + + /* Translstion's buffer for XDR */ + u_int16_t port_buf; + + size_t udplen = len - iph->ihl * 4; + + /* Important for tracking */ + int dir = CTINFO2DIR(ctinfo); + u_int32_t xid; + struct request_p *req_p; + + /* Not whole UDP header */ + if (udplen < sizeof(struct udphdr)) { + DEBUGRPC("RPC_UDP: udplen = %u\n", (unsigned)udplen); + return NF_ACCEPT; + } + + /* Checksum is calculated? */ + if(*chsm){ + /* Checksum invalid? Ignore. */ + if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, + csum_partial((char *)udph, udplen, 0))) { + DEBUGRPC("RPC_help_UDP: bad csum: %p %u%u.%u.%u.%u %u.%u.%u.%u\n", + udph, udplen, IP_PARTS(iph->saddr), + IP_PARTS(iph->daddr)); + return NF_ACCEPT; + } + } + + /* If packet is client packet */ + if (!dir) { + /* Test if packet len is ok */ + if((udplen - sizeof(struct udphdr)) != 56) + return NF_ACCEPT; + /* Get XID */ + xid = *data; + + /* Jump until procedure number */ + data = data + 5; + /* Is one get port procedure? */ + if(IXDR_GET_INT32(data) == 3){ + data++; + + /* Jump Credentials and Verfifier */ + data = data + IXDR_GET_INT32(data) + 2; + data = data + IXDR_GET_INT32(data) + 2; + + /* Get and store protocol in RPC client parameters */ + data = data + 2; + alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip, + ct->tuplehash[dir].tuple.src.u.all); + } + return NF_ACCEPT; + } + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED) { + DEBUGRPC("RPC_UDP: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Test if packet len is ok */ + if((udplen - sizeof(struct udphdr)) != 28) return NF_ACCEPT; + + /* XID is important */ + req_p = LIST_FIND(&request_p_list_udp, request_p_cmp, + struct request_p *, *data, ct->tuplehash[!dir].tuple.src.ip, + ct->tuplehash[!dir].tuple.src.u.all); + + /* Drop packets for accurate tracking */ + if(!req_p) return NF_DROP; + + data = data++; + + /* Verifies if packet is really a reply packet */ + if(IXDR_GET_INT32(data) != 1) { + DEBUGRPC("RPC_UDP: Packet with problems"); + return NF_ACCEPT; + } + data++; + + /* Is status accpet? */ + if(IXDR_GET_INT32(data)) return NF_ACCEPT; + data++; + + /* Get Verifier lentgth. Jump verifier */ + data = data + IXDR_GET_INT32(data) + 2; + + /* Is accpet status "sucess"? */ + if(IXDR_GET_INT32(data)) return NF_ACCEPT; + data++; + + /* Get server port number */ + port_buf = (u_int16_t) IXDR_GET_INT32(data); + + /* All right. This binding must be in internal expect list */ + /* If port == 0, service is not registred */ + if(port_buf) + binding(ct->tuplehash[dir].tuple.src.ip, port_buf, + ct->tuplehash[!dir].tuple.src.ip, req_p->proto); + req_cl(req_p); + return NF_ACCEPT; +} + +struct ip_conntrack_helper rpc_udp = { { NULL, NULL }, + "rpc-udp", + 0, THIS_MODULE, 1, 0, + { { 0, { __constant_htons(111) } }, + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, + { 0, { 0 }, 0xFFFF } }, + help }; + +int init_module(void) +{ + printk("ip_conntrack_rpc_udp.o\n"); + return ip_conntrack_helper_register(&rpc_udp); +} + +void cleanup_module(void) +{ + clean_expect(&expect_rpc_list_udp); + clean_request(&request_p_list_udp); + ip_conntrack_helper_unregister(&rpc_udp); +} + +struct module *ip_conntrack_rpc_udp = THIS_MODULE; +EXPORT_SYMBOL(expect_rpc_list_udp); +EXPORT_SYMBOL(ip_conntrack_rpc_udp); +EXPORT_SYMBOL(ipct_rpc_udp_lock); + diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_standalone.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_standalone.c Sat Mar 30 23:53:24 2002 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_standalone.c Sat Mar 30 23:39:34 2002 @@ -7,6 +7,7 @@ /* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General Public Licence. */ +#include #include #include #include @@ -15,6 +16,9 @@ #include #include #include +#ifdef CONFIG_SYSCTL +#include +#endif #include #include @@ -238,6 +242,114 @@ static struct nf_hook_ops ip_conntrack_local_in_ops = { { NULL, NULL }, ip_confirm, PF_INET, NF_IP_LOCAL_IN, NF_IP_PRI_LAST-1 }; +/* Sysctl support */ + +#ifdef CONFIG_SYSCTL + +/* From ip_conntrack_core.c */ +extern int ip_conntrack_max; + +/* From ip_conntrack_proto_tcp.c */ +extern unsigned long ip_ct_tcp_timeout_none; +extern unsigned long ip_ct_tcp_timeout_syn_sent; +extern unsigned long ip_ct_tcp_timeout_syn_recv; +extern unsigned long ip_ct_tcp_timeout_established; +extern unsigned long ip_ct_tcp_timeout_fin_wait; +extern unsigned long ip_ct_tcp_timeout_close_wait; +extern unsigned long ip_ct_tcp_timeout_last_ack; +extern unsigned long ip_ct_tcp_timeout_time_wait; +extern unsigned long ip_ct_tcp_timeout_close; +extern unsigned long ip_ct_tcp_timeout_listen; +extern int ip_ct_tcp_log_invalid_scale; +extern int ip_ct_tcp_log_out_of_window; +extern int ip_ct_tcp_be_liberal; + +/* From ip_conntrack_proto_udp.c */ +extern unsigned long ip_ct_udp_timeout; +extern unsigned long ip_ct_udp_timeout_stream; + +/* From ip_conntrack_proto_icmp.c */ +extern unsigned long ip_ct_icmp_timeout; + +/* From ip_conntrack_proto_icmp.c */ +extern unsigned long ip_ct_generic_timeout; + +static struct ctl_table_header *ip_ct_sysctl_header; + +static ctl_table ip_ct_sysctl_table[19] = { + {NET_IPV4_NF_CONNTRACK_MAX, "ip_conntrack_max", + &ip_conntrack_max, sizeof(int), 0644, NULL, + &proc_dointvec}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_NONE, "ip_conntrack_tcp_timeout_none", + &ip_ct_tcp_timeout_none, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT, "ip_conntrack_tcp_timeout_syn_sent", + &ip_ct_tcp_timeout_syn_sent, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_RECV, "ip_conntrack_tcp_timeout_syn_recv", + &ip_ct_tcp_timeout_syn_recv, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED, "ip_conntrack_tcp_timeout_established", + &ip_ct_tcp_timeout_established, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_FIN_WAIT, "ip_conntrack_tcp_timeout_fin_wait", + &ip_ct_tcp_timeout_fin_wait, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE_WAIT, "ip_conntrack_tcp_timeout_close_wait", + &ip_ct_tcp_timeout_close_wait, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LAST_ACK, "ip_conntrack_tcp_timeout_last_ack", + &ip_ct_tcp_timeout_last_ack, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_TIME_WAIT, "ip_conntrack_tcp_timeout_time_wait", + &ip_ct_tcp_timeout_time_wait, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE, "ip_conntrack_tcp_timeout_close", + &ip_ct_tcp_timeout_close, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LISTEN, "ip_conntrack_tcp_timeout_listen", + &ip_ct_tcp_timeout_listen, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_TCP_LOG_INVALID_SCALE, "ip_conntrack_tcp_log_invalid_scale", + &ip_ct_tcp_log_invalid_scale, sizeof(unsigned int), 0644, NULL, + &proc_dointvec}, + {NET_IPV4_NF_CONNTRACK_TCP_LOG_OUT_OF_WINDOW, "ip_conntrack_tcp_log_out_of_window", + &ip_ct_tcp_log_out_of_window, sizeof(unsigned int), 0644, NULL, + &proc_dointvec}, + {NET_IPV4_NF_CONNTRACK_TCP_BE_LIBERAL, "ip_conntrack_tcp_be_liberal", + &ip_ct_tcp_be_liberal, sizeof(unsigned int), 0644, NULL, + &proc_dointvec}, + {NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT, "ip_conntrack_udp_timeout", + &ip_ct_udp_timeout, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT_STREAM, "ip_conntrack_udp_timeout_stream", + &ip_ct_udp_timeout_stream, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT, "ip_conntrack_icmp_timeout", + &ip_ct_icmp_timeout, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT, "ip_conntrack_generic_timeout", + &ip_ct_generic_timeout, sizeof(unsigned int), 0644, NULL, + &proc_dointvec_jiffies}, + {0} +}; + +static ctl_table ip_ct_netfilter_table[] = { + {NET_IPV4_NETFILTER, "netfilter", NULL, 0, 0555, ip_ct_sysctl_table, 0, 0, 0, 0, 0}, + {0} +}; + +static ctl_table ip_ct_ipv4_table[] = { + {NET_IPV4, "ipv4", NULL, 0, 0555, ip_ct_netfilter_table, 0, 0, 0, 0, 0}, + {0} +}; + +static ctl_table ip_ct_net_table[] = { + {CTL_NET, "net", NULL, 0, 0555, ip_ct_ipv4_table, 0, 0, 0, 0, 0}, + {0} +}; +#endif + static int init_or_cleanup(int init) { struct proc_dir_entry *proc; @@ -273,10 +385,20 @@ printk("ip_conntrack: can't register local in hook.\n"); goto cleanup_inoutandlocalops; } +#ifdef CONFIG_SYSCTL + ip_ct_sysctl_header = register_sysctl_table(ip_ct_net_table, 0); + if (ip_ct_sysctl_header == NULL) { + printk("ip_conntrack: can't register to sysctl.\n"); + goto cleanup; + } +#endif return ret; cleanup: +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(ip_ct_sysctl_header); +#endif nf_unregister_hook(&ip_conntrack_local_in_ops); cleanup_inoutandlocalops: nf_unregister_hook(&ip_conntrack_out_ops); diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_talk.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_talk.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_talk.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_talk.c Sat Mar 30 23:39:23 2002 @@ -0,0 +1,362 @@ +/* + * talk extension for IP connection tracking. + * Jozsef Kadlecsik + * + * 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. + ** + * Module load syntax: + * insmod ip_nat_talk.o talk=[0|1] ntalk=[0|1] ntalk2=[01] + * + * talk=[0|1] disable|enable old talk support + * ntalk=[0|1] disable|enable ntalk support + * ntalk2=[0|1] disable|enable ntalk2 support + * + * The default is talk=1 ntalk=1 ntalk2=1 + * + * The helper does not support simultaneous talk requests. + ** + * + * ASCII art on talk protocols + * + * + * caller server callee server + * | \ / + * | \ / + * | \ / + * | / + * | / \ + * 2 | 1 / \ 3 + * caller client ----------- callee client + * 4 + * + * 1. caller client <-> callee server: LOOK_UP, then ANNOUNCE invitation + * ( 2. caller client <-> caller server: LEAVE_INVITE to server ) + * 3. callee client <-> caller server: LOOK_UP invitation + * 4. callee client <-> caller client: talk data channel + * + * [1]: M. Hunter, talk: a historical protocol for interactive communication + * draft-hunter-talk-00.txt + * [2]: D.B. Chapman, E.D. Zwicky: Building Internet Firewalls (O'Reilly) + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Default all talk protocols are supported */ +static int talk = 1; +static int ntalk = 1; +static int ntalk2 = 1; +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("talk connection tracking module"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_PARM +MODULE_PARM(talk, "i"); +MODULE_PARM_DESC(talk, "support (old) talk protocol"); +MODULE_PARM(ntalk, "i"); +MODULE_PARM_DESC(ntalk, "support ntalk protocol"); +MODULE_PARM(ntalk2, "i"); +MODULE_PARM_DESC(ntalk2, "support ntalk2 protocol"); +#endif + +DECLARE_LOCK(ip_talk_lock); +struct module *ip_conntrack_talk = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static int talk_expect(struct ip_conntrack *ct); +static int ntalk_expect(struct ip_conntrack *ct); + +static int (*talk_expectfn[2])(struct ip_conntrack *ct) = {talk_expect, ntalk_expect}; + +static int talk_help_response(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + int talk_port, + u_char mode, + u_char type, + u_char answer, + struct talk_addr *addr) +{ + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_talk_expect *exp_talk_info = &exp->help.exp_talk_info; + + DEBUGP("ip_ct_talk_help_response: %u.%u.%u.%u:%u, type %d answer %d\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + type, answer); + + if (!(answer == SUCCESS && type == mode)) + return NF_ACCEPT; + + memset(&expect, 0, sizeof(expect)); + + if (type == ANNOUNCE) { + + DEBUGP("ip_ct_talk_help_response: ANNOUNCE\n"); + + /* update the talk info */ + LOCK_BH(&ip_talk_lock); + exp_talk_info->port = htons(talk_port); + + /* expect callee client -> caller server message */ + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[dir].tuple.src.ip, + { 0 } }, + { ct->tuplehash[dir].tuple.dst.ip, + { htons(talk_port) }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = talk_expectfn[talk_port - TALK_PORT]; + + DEBUGP("ip_ct_talk_help_response: callee client %u.%u.%u.%u:%u -> caller daemon %u.%u.%u.%u:%u!\n", + NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.udp.port), + NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.udp.port)); + + /* Ignore failure; should only happen with NAT */ + ip_conntrack_expect_related(ct, &expect); + UNLOCK_BH(&ip_talk_lock); + } + if (type == LOOK_UP) { + + DEBUGP("ip_ct_talk_help_response: LOOK_UP\n"); + + /* update the talk info */ + LOCK_BH(&ip_talk_lock); + exp_talk_info->port = addr->ta_port; + + /* expect callee client -> caller client connection */ + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { addr->ta_addr, + { addr->ta_port }, + IPPROTO_TCP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = NULL; + + DEBUGP("ip_ct_talk_help_response: callee client %u.%u.%u.%u:%u -> caller client %u.%u.%u.%u:%u!\n", + NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), + NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); + + /* Ignore failure; should only happen with NAT */ + ip_conntrack_expect_related(ct, &expect); + UNLOCK_BH(&ip_talk_lock); + } + + return NF_ACCEPT; +} + +/* FIXME: This should be in userspace. Later. */ +static int talk_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + int talk_port, + u_char mode) +{ + struct udphdr *udph = (void *)iph + iph->ihl * 4; + const char *data = (const char *)udph + sizeof(struct udphdr); + int dir = CTINFO2DIR(ctinfo); + size_t udplen; + + DEBUGP("ip_ct_talk_help: help entered\n"); + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ip_ct_talk_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole UDP header? */ + udplen = len - iph->ihl * 4; + if (udplen < sizeof(struct udphdr)) { + DEBUGP("ip_ct_talk_help: too short for udph, udplen = %u\n", (unsigned)udplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, + csum_partial((char *)udph, udplen, 0))) { + DEBUGP("ip_ct_talk_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + udph, udplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + DEBUGP("ip_ct_talk_help: %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(udph->source), NIPQUAD(iph->daddr), ntohs(udph->dest)); + + if (dir == IP_CT_DIR_ORIGINAL) + return NF_ACCEPT; + + if (talk_port == TALK_PORT + && udplen == sizeof(struct udphdr) + sizeof(struct talk_response)) + return talk_help_response(iph, len, ct, ctinfo, talk_port, mode, + ((struct talk_response *)data)->type, + ((struct talk_response *)data)->answer, + &(((struct talk_response *)data)->addr)); + else if (talk_port == NTALK_PORT + && ntalk + && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_response) + && ((struct ntalk_response *)data)->vers == NTALK_VERSION) + return talk_help_response(iph, len, ct, ctinfo, talk_port, mode, + ((struct ntalk_response *)data)->type, + ((struct ntalk_response *)data)->answer, + &(((struct ntalk_response *)data)->addr)); + else if (talk_port == NTALK_PORT + && ntalk2 + && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_response) + && ((struct ntalk2_response *)data)->vers == NTALK2_VERSION) + return talk_help_response(iph, len, ct, ctinfo, talk_port, mode, + ((struct ntalk2_response *)data)->type, + ((struct ntalk2_response *)data)->answer, + &(((struct ntalk2_response *)data)->addr)); + else { + DEBUGP("ip_ct_talk_help: not ntalk/ntalk2 response, datalen %u != %u or %u + max 256\n", + (unsigned)udplen - sizeof(struct udphdr), + sizeof(struct ntalk_response), sizeof(struct ntalk2_response)); + return NF_ACCEPT; + } +} + +static int lookup_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, TALK_PORT, LOOK_UP); +} + +static int lookup_nhelp(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, NTALK_PORT, LOOK_UP); +} + +static struct ip_conntrack_helper lookup_helpers[2] = + { { { NULL, NULL }, + "talk", /* name */ + 0, /* flags */ + NULL, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(TALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + lookup_help }, /* helper */ + { { NULL, NULL }, + "ntalk", /* name */ + 0, /* flags */ + NULL, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(NTALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + lookup_nhelp } /* helper */ + }; + +static int talk_expect(struct ip_conntrack *ct) +{ + DEBUGP("ip_conntrack_talk: calling talk_expectfn for ct %p\n", ct); + WRITE_LOCK(&ip_conntrack_lock); + ct->helper = &lookup_helpers[0]; + WRITE_UNLOCK(&ip_conntrack_lock); + + return NF_ACCEPT; /* unused */ +} + +static int ntalk_expect(struct ip_conntrack *ct) +{ + DEBUGP("ip_conntrack_talk: calling ntalk_expectfn for ct %p\n", ct); + WRITE_LOCK(&ip_conntrack_lock); + ct->helper = &lookup_helpers[1]; + WRITE_UNLOCK(&ip_conntrack_lock); + + return NF_ACCEPT; /* unused */ +} + +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, TALK_PORT, ANNOUNCE); +} + +static int nhelp(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, NTALK_PORT, ANNOUNCE); +} + +static struct ip_conntrack_helper talk_helpers[2] = + { { { NULL, NULL }, + "talk", /* name */ + 0, /* flags */ + THIS_MODULE, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(TALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + help }, /* helper */ + { { NULL, NULL }, + "ntalk", /* name */ + 0, /* flags */ + THIS_MODULE, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(NTALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + nhelp } /* helper */ + }; + +static int __init init(void) +{ + if (talk > 0) + ip_conntrack_helper_register(&talk_helpers[0]); + if (ntalk > 0 || ntalk2 > 0) + ip_conntrack_helper_register(&talk_helpers[1]); + + return 0; +} + +static void __exit fini(void) +{ + if (talk > 0) + ip_conntrack_helper_unregister(&talk_helpers[0]); + if (ntalk > 0 || ntalk2 > 0) + ip_conntrack_helper_unregister(&talk_helpers[1]); +} + +#ifdef CONFIG_IP_NF_NAT_NEEDED +EXPORT_SYMBOL(ip_talk_lock); +#endif + +module_init(init); +module_exit(fini); diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_tftp.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_tftp.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_conntrack_tftp.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_conntrack_tftp.c Sat Mar 30 23:39:59 2002 @@ -0,0 +1,131 @@ +/* + * Licensed under GNU GPL version 2 Copyright Magnus Boden + * Version: 0.0.7 + * + * Thu 21 Mar 2002 Harald Welte + * - port to newnat API + * + */ + +#include +#include +#include + +#include +#include +#include +#include + +MODULE_AUTHOR("Magnus Boden "); +MODULE_DESCRIPTION("Netfilter connection tracking module for tftp"); +MODULE_LICENSE("GPL"); + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_c = 0; +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of tftp servers"); +#endif + +#if 0 +#define DEBUGP(format, args...) printk(__FILE__ ":" __FUNCTION__ ": " \ + format, ## args) +#else +#define DEBUGP(format, args...) +#endif + +static int tftp_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct udphdr *udph = (void *)iph + iph->ihl * 4; + struct tftphdr *tftph = (void *)udph + 8; + struct ip_conntrack_expect exp; + + switch (ntohs(tftph->opcode)) { + /* RRQ and WRQ works the same way */ + case TFTP_OPCODE_READ: + case TFTP_OPCODE_WRITE: + DEBUGP(""); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); + memset(&exp, 0, sizeof(exp)); + + exp.tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; + exp.mask.src.ip = 0xffffffff; + exp.mask.dst.ip = 0xffffffff; + exp.mask.dst.u.udp.port = 0xffff; + exp.mask.dst.protonum = 0xffff; + exp.expectfn = NULL; + + DEBUGP("expect: "); + DUMP_TUPLE(&exp.tuple); + DUMP_TUPLE(&exp.mask); + ip_conntrack_expect_related(ct, &exp); + break; + default: + DEBUGP("Unknown opcode\n"); + } + return NF_ACCEPT; +} + +static struct ip_conntrack_helper tftp[MAX_PORTS]; +static char tftp_names[MAX_PORTS][10]; + +static void fini(void) +{ + int i; + + for (i = 0 ; i < ports_c; i++) { + DEBUGP("unregistering helper for port %d\n", + ports[i]); + ip_conntrack_helper_unregister(&tftp[i]); + } +} + +static int __init init(void) +{ + int i, ret; + char *tmpname; + + if (!ports[0]) + ports[0]=TFTP_PORT; + + for (i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) { + /* Create helper structure */ + memset(&tftp[i], 0, sizeof(struct ip_conntrack_helper)); + + tftp[i].tuple.dst.protonum = IPPROTO_UDP; + tftp[i].tuple.src.u.udp.port = htons(ports[i]); + tftp[i].mask.dst.protonum = 0xFFFF; + tftp[i].mask.src.u.udp.port = 0xFFFF; + tftp[i].max_expected = 1; + tftp[i].timeout = 0; + tftp[i].flags = IP_CT_HELPER_F_REUSE_EXPECT; + tftp[i].me = THIS_MODULE; + tftp[i].help = tftp_help; + + tmpname = &tftp_names[i][0]; + if (ports[i] == TFTP_PORT) + sprintf(tmpname, "tftp"); + else + sprintf(tmpname, "tftp-%d", i); + tftp[i].name = tmpname; + + DEBUGP("port #%d: %d\n", i, ports[i]); + + ret=ip_conntrack_helper_register(&tftp[i]); + if (ret) { + printk("ERROR registering helper for port %d\n", + ports[i]); + fini(); + return(ret); + } + ports_c++; + } + return(0); +} + +module_init(init); +module_exit(fini); diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_nat_h323.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_nat_h323.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_nat_h323.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_nat_h323.c Sat Mar 30 23:35:21 2002 @@ -0,0 +1,419 @@ +/* + * H.323 'brute force' extension for NAT alteration. + * Jozsef Kadlecsik + * + * Based on ip_masq_h323.c for 2.2 kernels from CoRiTel, Sofia project. + * (http://www.coritel.it/projects/sofia/nat.html) + * Uses Sampsa Ranta's excellent idea on using expectfn to 'bind' + * the unregistered helpers to the conntrack entries. + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("H.323 'brute force' connection tracking module"); +MODULE_LICENSE("GPL"); + +DECLARE_LOCK_EXTERN(ip_h323_lock); +struct module *ip_nat_h323 = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/* FIXME: Time out? --RR */ + +static unsigned int +h225_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info); + +static unsigned int h225_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb); + +static struct ip_nat_helper h245 = + { { NULL, NULL }, + "H.245", /* name */ + 0, /* flags */ + NULL, /* module */ + { { 0, { 0 } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h225_nat_help, /* helper */ + h225_nat_expected /* expectfn */ + }; + +static unsigned int +h225_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + struct ip_nat_multi_range mr; + u_int32_t newdstip, newsrcip, newip; + u_int16_t port; + struct ip_ct_h225_expect *exp_info; + struct ip_ct_h225_master *master_info; + struct ip_conntrack *master = master_ct(ct); + unsigned int is_h225, ret; + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + + IP_NF_ASSERT(!(info->initialized & (1<master->expectant->help.ct_h225_info; + exp_info = &ct->master->help.exp_h225_info; + + LOCK_BH(&ip_h323_lock); + + DEBUGP("master: "); + DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_REPLY].tuple); + DEBUGP("conntrack: "); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + if (exp_info->dir == IP_CT_DIR_ORIGINAL) { + /* Make connection go to the client. */ + newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to client)\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } else { + /* Make the connection go to the server */ + newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to server)\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } + port = exp_info->port; + is_h225 = master_info->is_h225 == H225_PORT; + UNLOCK_BH(&ip_h323_lock); + + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) + newip = newsrcip; + else + newip = newdstip; + + DEBUGP("h225_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip)); + + mr.rangesize = 1; + /* We don't want to manip the per-protocol, just the IPs... */ + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + mr.range[0].min_ip = mr.range[0].max_ip = newip; + + /* ... unless we're doing a MANIP_DST, in which case, make + sure we map to the correct port */ + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { + mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + mr.range[0].min = mr.range[0].max + = ((union ip_conntrack_manip_proto) + { port }); + } + + ret = ip_nat_setup_info(ct, &mr, hooknum); + + if (is_h225) { + DEBUGP("h225_nat_expected: H.225, setting NAT helper for %p\n", ct); + /* NAT expectfn called with ip_nat_lock write-locked */ + info->helper = &h245; + } + return ret; +} + +static int h323_signal_address_fixup(struct ip_conntrack *ct, + struct sk_buff **pskb, + enum ip_conntrack_info ctinfo) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl*4; + char *data = (char *) tcph + tcph->doff * 4; + u_int32_t tcplen = (*pskb)->len - iph->ihl*4; + u_int32_t datalen = tcplen - tcph->doff*4; + struct ip_ct_h225_master *info = &ct->help.ct_h225_info; + u_int32_t newip; + u_int16_t port; + int i; + + MUST_BE_LOCKED(&ip_h323_lock); + + DEBUGP("h323_signal_address_fixup: %s %s\n", + between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen) + ? "yes" : "no", + between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen) + ? "yes" : "no"); + if (!(between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen) + || between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen))) + return 1; + + DEBUGP("h323_signal_address_fixup: offsets %u + 6 and %u + 6 in %u\n", + info->offset[IP_CT_DIR_ORIGINAL], + info->offset[IP_CT_DIR_REPLY], + tcplen); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + for (i = 0; i < IP_CT_DIR_MAX; i++) { + DEBUGP("h323_signal_address_fixup: %s %s\n", + info->dir == IP_CT_DIR_ORIGINAL ? "original" : "reply", + i == IP_CT_DIR_ORIGINAL ? "caller" : "callee"); + if (!between(info->seq[i], ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) + continue; + if (!between(info->seq[i] + 6, ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) { + /* Partial retransmisison. It's a cracker being funky. */ + if (net_ratelimit()) { + printk("H.323_NAT: partial packet %u/6 in %u/%u\n", + info->seq[i], + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + } + return 0; + } + + /* Change address inside packet to match way we're mapping + this connection. */ + if (i == IP_CT_DIR_ORIGINAL) { + newip = ct->tuplehash[!info->dir].tuple.dst.ip; + port = ct->tuplehash[!info->dir].tuple.dst.u.tcp.port; + } else { + newip = ct->tuplehash[!info->dir].tuple.src.ip; + port = ct->tuplehash[!info->dir].tuple.src.u.tcp.port; + } + + DEBUGP("h323_signal_address_fixup: orig %s IP:port %u.%u.%u.%u:%u\n", + i == IP_CT_DIR_ORIGINAL ? "source" : "dest ", + NIPQUAD(*((u_int32_t *)(data + info->offset[i]))), + ntohs(*((u_int16_t *)(data + info->offset[i] + 4)))); + + /* Modify the packet */ + *(u_int32_t *)(data + info->offset[i]) = newip; + *(u_int16_t *)(data + info->offset[i] + 4) = port; + + DEBUGP("h323_signal_address_fixup: new %s IP:port %u.%u.%u.%u:%u\n", + i == IP_CT_DIR_ORIGINAL ? "source" : "dest ", + NIPQUAD(*((u_int32_t *)(data + info->offset[i]))), + ntohs(*((u_int16_t *)(data + info->offset[i] + 4)))); + } + + /* fix checksum information */ + + (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4, + datalen, 0); + + tcph->check = 0; + tcph->check = tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcph->doff*4, + (*pskb)->csum)); + ip_send_check(iph); + + return 1; +} + +static int h323_data_fixup(struct ip_ct_h225_expect *info, + struct ip_conntrack *ct, + struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *expect) +{ + u_int32_t newip; + u_int16_t port; + struct ip_conntrack_tuple newtuple; + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl*4; + char *data = (char *) tcph + tcph->doff * 4; + u_int32_t tcplen = (*pskb)->len - iph->ihl*4; + struct ip_ct_h225_master *master_info = &ct->help.ct_h225_info; + int is_h225; + + MUST_BE_LOCKED(&ip_h323_lock); + DEBUGP("h323_data_fixup: offset %u + 6 in %u\n", info->offset, tcplen); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + if (!between(expect->seq + 6, ntohl(tcph->seq), + ntohl(tcph->seq) + tcplen - tcph->doff * 4)) { + /* Partial retransmisison. It's a cracker being funky. */ + if (net_ratelimit()) { + printk("H.323_NAT: partial packet %u/6 in %u/%u\n", + expect->seq, + ntohl(tcph->seq), + ntohl(tcph->seq) + tcplen - tcph->doff * 4); + } + return 0; + } + + /* Change address inside packet to match way we're mapping + this connection. */ + if (info->dir == IP_CT_DIR_REPLY) { + /* Must be where client thinks server is */ + newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + /* Expect something from client->server */ + newtuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + } else { + /* Must be where server thinks client is */ + newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + /* Expect something from server->client */ + newtuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + } + + is_h225 = (master_info->is_h225 == H225_PORT); + + if (is_h225) { + newtuple.dst.protonum = IPPROTO_TCP; + newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port; + } else { + newtuple.dst.protonum = IPPROTO_UDP; + newtuple.src.u.udp.port = expect->tuple.src.u.udp.port; + } + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(info->port); port != 0; port++) { + if (is_h225) + newtuple.dst.u.tcp.port = htons(port); + else + newtuple.dst.u.udp.port = htons(port); + + if (ip_conntrack_change_expect(expect, &newtuple) == 0) + break; + } + if (port == 0) { + DEBUGP("h323_data_fixup: no free port found!\n"); + return 0; + } + + port = htons(port); + + DEBUGP("h323_data_fixup: orig IP:port %u.%u.%u.%u:%u\n", + NIPQUAD(*((u_int32_t *)(data + info->offset))), + ntohs(*((u_int16_t *)(data + info->offset + 4)))); + + /* Modify the packet */ + *(u_int32_t *)(data + info->offset) = newip; + *(u_int16_t *)(data + info->offset + 4) = port; + + DEBUGP("h323_data_fixup: new IP:port %u.%u.%u.%u:%u\n", + NIPQUAD(*((u_int32_t *)(data + info->offset))), + ntohs(*((u_int16_t *)(data + info->offset + 4)))); + + /* fix checksum information */ + /* FIXME: usually repeated multiple times in the case of H.245! */ + + (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4, + tcplen - tcph->doff*4, 0); + + tcph->check = 0; + tcph->check = tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcph->doff*4, + (*pskb)->csum)); + ip_send_check(iph); + + return 1; +} + +static unsigned int h225_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + int dir; + struct ip_ct_h225_expect *exp_info; + + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + dir = CTINFO2DIR(ctinfo); + DEBUGP("nat_h323: dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { + DEBUGP("nat_h323: Not touching dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + return NF_ACCEPT; + } + + if (!exp) { + LOCK_BH(&ip_h323_lock); + if (!h323_signal_address_fixup(ct, pskb, ctinfo)) { + UNLOCK_BH(&ip_h323_lock); + return NF_DROP; + } + UNLOCK_BH(&ip_h323_lock); + return NF_ACCEPT; + } + + exp_info = &exp->help.exp_h225_info; + + LOCK_BH(&ip_h323_lock); + if (!h323_data_fixup(exp_info, ct, pskb, ctinfo, exp)) { + UNLOCK_BH(&ip_h323_lock); + return NF_DROP; + } + UNLOCK_BH(&ip_h323_lock); + + return NF_ACCEPT; +} + +static struct ip_nat_helper h225 = + { { NULL, NULL }, + "H.225", /* name */ + IP_NAT_HELPER_F_ALWAYS, /* flags */ + THIS_MODULE, /* module */ + { { 0, { __constant_htons(H225_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h225_nat_help, /* helper */ + h225_nat_expected /* expectfn */ + }; + +static int __init init(void) +{ + int ret; + + ret = ip_nat_helper_register(&h225); + + if (ret != 0) + printk("ip_nat_h323: cannot initialize the module!\n"); + + return ret; +} + +static void __exit fini(void) +{ + ip_nat_helper_unregister(&h225); +} + +module_init(init); +module_exit(fini); diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_nat_helper.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_nat_helper.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_nat_helper.c Sat Mar 30 23:03:53 2002 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_nat_helper.c Sat Mar 30 23:39:34 2002 @@ -351,6 +351,8 @@ ip_nat_sack_adjust(skb, ct, ctinfo); + ip_conntrack_tcp_update(ct, dir, iph, skb->len, tcph); + return 0; } diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_nat_talk.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_nat_talk.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_nat_talk.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_nat_talk.c Sat Mar 30 23:39:23 2002 @@ -0,0 +1,473 @@ +/* + * talk extension for UDP NAT alteration. + * Jozsef Kadlecsik + * + * 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. + ** + * Module load syntax: + * insmod ip_nat_talk.o talk=[0|1] ntalk=[0|1] ntalk2=[0|1] + * + * talk=[0|1] disable|enable old talk support + * ntalk=[0|1] disable|enable ntalk support + * ntalk2=[0|1] disable|enable ntalk2 support + * + * The default is talk=1 ntalk=1 ntalk2=1 + * + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Default all talk protocols are supported */ +static int talk = 1; +static int ntalk = 1; +static int ntalk2 = 1; +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("talk network address translation module"); +#ifdef MODULE_PARM +MODULE_PARM(talk, "i"); +MODULE_PARM_DESC(talk, "support (old) talk protocol"); +MODULE_PARM(ntalk, "i"); +MODULE_PARM_DESC(ntalk, "support ntalk protocol"); +MODULE_PARM(ntalk2, "i"); +MODULE_PARM_DESC(ntalk2, "support ntalk2 protocol"); +#endif + +#if 0 +#define DEBUGP printk +#define IP_NAT_TALK_DEBUG +#else +#define DEBUGP(format, args...) +#endif + +/* FIXME: Time out? --RR */ + +static int +mangle_packet(struct sk_buff **pskb, + struct ip_conntrack *ct, + u_int32_t newip, + u_int16_t port, + struct talk_addr *addr, + struct talk_addr *ctl_addr) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + size_t udplen = (*pskb)->len - iph->ihl * 4; + + /* Fortunately talk sends a structure with the address and + port in it. The size of the packet won't change. */ + + if (ctl_addr == NULL) { + /* response */ + if (addr->ta_addr == INADDR_ANY) + return 1; + DEBUGP("ip_nat_talk_mangle_packet: response orig %u.%u.%u.%u:%u, inserting %u.%u.%u.%u:%u\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + NIPQUAD(newip), ntohs(port)); + addr->ta_addr = newip; + addr->ta_port = port; + } else { + /* message */ + if (addr->ta_addr != INADDR_ANY) { + /* Change address inside packet to match way we're mapping + this connection. */ + DEBUGP("ip_nat_talk_mangle_packet: message orig addr %u.%u.%u.%u:%u, inserting %u.%u.%u.%u:%u\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), + ntohs(addr->ta_port)); + addr->ta_addr = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + } + DEBUGP("ip_nat_talk_mangle_packet: message orig ctl_addr %u.%u.%u.%u:%u, inserting %u.%u.%u.%u:%u\n", + NIPQUAD(ctl_addr->ta_addr), ntohs(ctl_addr->ta_port), + NIPQUAD(newip), ntohs(port)); + ctl_addr->ta_addr = newip; + ctl_addr->ta_port = port; + } + + /* Fix checksums */ + (*pskb)->csum = csum_partial((char *)udph + sizeof(struct udphdr), udplen - sizeof(struct udphdr), 0); + udph->check = 0; + udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, + csum_partial((char *)udph, sizeof(struct udphdr), (*pskb)->csum)); + + ip_send_check(iph); + return 1; +} + +static int talk_help_msg(struct ip_conntrack *ct, + struct sk_buff **pskb, + u_char type, + struct talk_addr *addr, + struct talk_addr *ctl_addr) +{ + u_int32_t newip; + u_int16_t port; + + unsigned int verdict = NF_ACCEPT; + + DEBUGP("ip_nat_talk_help_msg: addr: %u.%u.%u.%u:%u, ctl_addr: %u.%u.%u.%u:%u, type %d\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + NIPQUAD(ctl_addr->ta_addr), ntohs(ctl_addr->ta_port), + type); + + /* Change address inside packet to match way we're mapping + this connection. */ + newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port; + DEBUGP("ip_nat_talk_help_msg: inserting: %u.%u.%u.%u:%u\n", + NIPQUAD(newip), ntohs(port)); + + if (!mangle_packet(pskb, ct, newip, port, addr, ctl_addr)) + verdict = NF_DROP; + + return verdict; +} + +static int talk_help_response(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct sk_buff **pskb, + u_char type, + u_char answer, + struct talk_addr *addr) +{ + u_int32_t newip; + u_int16_t port; + struct ip_conntrack_tuple t; + struct ip_ct_talk_expect *ct_talk_info; + + DEBUGP("ip_nat_talk_help_response: addr: %u.%u.%u.%u:%u, type %d answer %d\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + type, answer); + + LOCK_BH(&ip_talk_lock); + ct_talk_info = &exp->help.exp_talk_info; + + if (!(answer == SUCCESS + && (type == LOOK_UP || type == ANNOUNCE) + && exp != NULL)) { + UNLOCK_BH(&ip_talk_lock); + return NF_ACCEPT; + } + + DEBUGP("ip_nat_talk_help_response: talkinfo port %u (%s)\n", + ntohs(ct_talk_info->port), + type == LOOK_UP ? "LOOK_UP" : "ANNOUNCE"); + + /* Change address inside packet to match way we're mapping + this connection. */ + newip = ct->tuplehash[type == LOOK_UP ? IP_CT_DIR_ORIGINAL : + IP_CT_DIR_REPLY].tuple.dst.ip; + /* We can read expect here without conntrack lock, since it's + only set in ip_conntrack_talk , with ip_talk_lock held + writable */ + t = exp->tuple; + t.dst.ip = newip; + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(ct_talk_info->port); port != 0; port++) { + if (type == LOOK_UP) + t.dst.u.tcp.port = htons(port); + else + t.dst.u.udp.port = htons(port); + + if (ip_conntrack_change_expect(exp, &t) == 0) { + DEBUGP("ip_nat_talk_help_response: using %u.%u.%u.%u:%u\n", NIPQUAD(newip), port); + break; + } + } + UNLOCK_BH(&ip_talk_lock); + + if (port == 0 || !mangle_packet(pskb, ct, newip, htons(port), addr, NULL)) + return NF_DROP; + + return NF_ACCEPT; +} + +static unsigned int talk_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb, + int talk_port) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + unsigned int udplen = (*pskb)->len - iph->ihl * 4; + char *data = (char *)udph + sizeof(struct udphdr); + int dir; + + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + dir = CTINFO2DIR(ctinfo); + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { + DEBUGP("ip_nat_talk_help: Not touching dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + return NF_ACCEPT; + } + DEBUGP("ip_nat_talk_help: dir %s at hook %s, %u.%u.%u.%u:%u->%u.%u.%u.%u:%u, talk port %d\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???", + NIPQUAD(iph->saddr), ntohs(udph->source), + NIPQUAD(iph->daddr), ntohs(udph->dest), + talk_port); + + /* Because conntrack does not drop packets, checking must be repeated here... */ + if (talk_port == TALK_PORT) { + if (dir == IP_CT_DIR_ORIGINAL + && udplen == sizeof(struct udphdr) + sizeof(struct talk_msg)) + return talk_help_msg(ct, pskb, + ((struct talk_msg *)data)->type, + &(((struct talk_msg *)data)->addr), + &(((struct talk_msg *)data)->ctl_addr)); + else if (dir == IP_CT_DIR_REPLY + && udplen == sizeof(struct udphdr) + sizeof(struct talk_response)) + return talk_help_response(ct, exp, pskb, + ((struct talk_response *)data)->type, + ((struct talk_response *)data)->answer, + &(((struct talk_response *)data)->addr)); + else { + DEBUGP("ip_nat_talk_help: not talk %s, datalen %u != %u\n", + dir == IP_CT_DIR_ORIGINAL ? "message" : "response", + (unsigned)udplen - sizeof(struct udphdr), + dir == IP_CT_DIR_ORIGINAL ? sizeof(struct talk_msg) : sizeof(struct talk_response)); + return NF_DROP; + } + } else { + if (dir == IP_CT_DIR_ORIGINAL) { + if (ntalk + && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_msg) + && ((struct ntalk_msg *)data)->vers == NTALK_VERSION) + return talk_help_msg(ct, pskb, + ((struct ntalk_msg *)data)->type, + &(((struct ntalk_msg *)data)->addr), + &(((struct ntalk_msg *)data)->ctl_addr)); + else if (ntalk2 + && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_msg) + && ((struct ntalk2_msg *)data)->vers == NTALK2_VERSION + && udplen == sizeof(struct udphdr) + + sizeof(struct ntalk2_msg) + + ((struct ntalk2_msg *)data)->extended) + return talk_help_msg(ct, pskb, + ((struct ntalk2_msg *)data)->type, + &(((struct ntalk2_msg *)data)->addr), + &(((struct ntalk2_msg *)data)->ctl_addr)); + else { + DEBUGP("ip_nat_talk_help: not ntalk/ntalk2 message, datalen %u != %u or %u + max 256\n", + (unsigned)udplen - sizeof(struct udphdr), + sizeof(struct ntalk_msg), sizeof(struct ntalk2_msg)); + return NF_DROP; + } + } else { + if (ntalk + && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_response) + && ((struct ntalk_response *)data)->vers == NTALK_VERSION) + return talk_help_response(ct, exp, pskb, + ((struct ntalk_response *)data)->type, + ((struct ntalk_response *)data)->answer, + &(((struct ntalk_response *)data)->addr)); + else if (ntalk2 + && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_response) + && ((struct ntalk2_response *)data)->vers == NTALK2_VERSION) + return talk_help_response(ct, exp, pskb, + ((struct ntalk2_response *)data)->type, + ((struct ntalk2_response *)data)->answer, + &(((struct ntalk2_response *)data)->addr)); + else { + DEBUGP("ip_nat_talk_help: not ntalk/ntalk2 response, datalen %u != %u or %u + max 256\n", + (unsigned)udplen - sizeof(struct udphdr), + sizeof(struct ntalk_response), sizeof(struct ntalk2_response)); + return NF_DROP; + } + } + } +} + +static unsigned int help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + return talk_help(ct, exp, info, ctinfo, hooknum, pskb, TALK_PORT); +} + +static unsigned int nhelp(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + return talk_help(ct, exp, info, ctinfo, hooknum, pskb, NTALK_PORT); +} + +static unsigned int +talk_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info); + +static struct ip_nat_helper talk_helpers[2] = + { { { NULL, NULL }, + "talk", /* name */ + IP_NAT_HELPER_F_ALWAYS, /* flags */ + THIS_MODULE, /* module */ + { { 0, { __constant_htons(TALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + help, /* helper */ + talk_nat_expected }, /* expectfn */ + { { NULL, NULL }, + "ntalk", /* name */ + IP_NAT_HELPER_F_ALWAYS, /* flags */ + THIS_MODULE, /* module */ + { { 0, { __constant_htons(NTALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + nhelp, /* helper */ + talk_nat_expected } /* expectfn */ + }; + +static unsigned int +talk_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + struct ip_nat_multi_range mr; + u_int32_t newdstip, newsrcip, newip; + u_int16_t port; + unsigned int ret; + + struct ip_conntrack *master = master_ct(ct); + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + + IP_NF_ASSERT(!(info->initialized & (1<master->help.exp_talk_info.port; + UNLOCK_BH(&ip_talk_lock); + + DEBUGP("ip_nat_talk_expected: dir %s at hook %s, ct %p, master %p\n", + CTINFO2DIR((*pskb)->nfct - ct->infos) == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???", + ct, master); + + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == IPPROTO_UDP) { + /* Callee client -> caller server */ +#ifdef IP_NAT_TALK_DEBUG + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + + DEBUGP("ip_nat_talk_expected: UDP %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(udph->source), + NIPQUAD(iph->daddr), ntohs(udph->dest)); +#endif + newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + DEBUGP("ip_nat_talk_expected: callee client -> caller server, newsrc: %u.%u.%u.%u, newdst: %u.%u.%u.%u\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } else { + /* Callee client -> caller client */ +#ifdef IP_NAT_TALK_DEBUG + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + + DEBUGP("ip_nat_talk_expected: TCP %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), + NIPQUAD(iph->daddr), ntohs(tcph->dest)); +#endif + newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + DEBUGP("ip_nat_talk_expected: callee client -> caller client, newsrc: %u.%u.%u.%u, newdst: %u.%u.%u.%u\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) + newip = newsrcip; + else + newip = newdstip; + + DEBUGP("ip_nat_talk_expected: IP to %u.%u.%u.%u, port %u\n", NIPQUAD(newip), ntohs(port)); + + mr.rangesize = 1; + /* We don't want to manip the per-protocol, just the IPs... */ + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + mr.range[0].min_ip = mr.range[0].max_ip = newip; + + /* ... unless we're doing a MANIP_DST, in which case, make + sure we map to the correct port */ + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { + mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + mr.range[0].min = mr.range[0].max + = ((union ip_conntrack_manip_proto) + { port }); + } + ret = ip_nat_setup_info(ct, &mr, hooknum); + + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == IPPROTO_UDP) { + DEBUGP("talk_expected: setting NAT helper for %p\n", ct); + /* NAT expectfn called with ip_nat_lock write-locked */ + info->helper = &talk_helpers[htons(port) - TALK_PORT]; + } + return ret; +} + +static int __init init(void) +{ + int ret = 0; + + if (talk > 0) { + ret = ip_nat_helper_register(&talk_helpers[0]); + + if (ret != 0) + return ret; + } + if (ntalk > 0 || ntalk2 > 0) { + ret = ip_nat_helper_register(&talk_helpers[1]); + + if (ret != 0 && talk > 0) + ip_nat_helper_unregister(&talk_helpers[0]); + } + return ret; +} + +static void __exit fini(void) +{ + if (talk > 0) + ip_nat_helper_unregister(&talk_helpers[0]); + if (ntalk > 0 || ntalk2 > 0) + ip_nat_helper_unregister(&talk_helpers[1]); +} + +module_init(init); +module_exit(fini); diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_nat_tftp.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_nat_tftp.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ip_nat_tftp.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ip_nat_tftp.c Sat Mar 30 23:39:59 2002 @@ -0,0 +1,197 @@ +/* + * Licensed under GNU GPL version 2 Copyright Magnus Boden + * Version: 0.0.7 + * + * Thu 21 Mar 2002 Harald Welte + * - Port to newnat API + * + * This module currently supports DNAT: + * iptables -t nat -A PREROUTING -d x.x.x.x -j DNAT --to-dest x.x.x.y + * + * and SNAT: + * iptables -t nat -A POSTROUTING { -j MASQUERADE , -j SNAT --to-source x.x.x.x } + * + * It has not been tested with + * -j SNAT --to-source x.x.x.x-x.x.x.y since I only have one external ip + * If you do test this please let me know if it works or not. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Magnus Boden "); +MODULE_DESCRIPTION("Netfilter NAT helper for tftp"); +MODULE_LICENSE("GPL"); + +#define MAX_PORTS 8 + +static int ports[MAX_PORTS]; +static int ports_c = 0; +#ifdef MODULE_PARM +MODULE_PARM(ports,"1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of tftp servers"); +#endif + +#if 0 +#define DEBUGP(format, args...) printk(__FILE__ ":" __FUNCTION__ ": " \ + format, ## args) +#else +#define DEBUGP(format, args...) +#endif +static unsigned int +tftp_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + int dir = CTINFO2DIR(ctinfo); + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + struct tftphdr *tftph = (void *)udph + 8; + struct ip_conntrack_tuple repl; + + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) + return NF_ACCEPT; + + if (!exp) { + DEBUGP("no conntrack expectation to modify\n"); + return NF_ACCEPT; + } + + switch (ntohs(tftph->opcode)) { + /* RRQ and WRQ works the same way */ + case TFTP_OPCODE_READ: + case TFTP_OPCODE_WRITE: + repl = ct->tuplehash[IP_CT_DIR_REPLY].tuple; + DEBUGP(""); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); + DEBUGP("expecting: "); + DUMP_TUPLE_RAW(&repl); + DUMP_TUPLE_RAW(&exp->mask); + ip_conntrack_change_expect(exp, &repl); + break; + default: + DEBUGP("Unknown opcode\n"); + } + + return NF_ACCEPT; +} + +static unsigned int +tftp_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + const struct ip_conntrack *master = ct->master->expectant; + const struct ip_conntrack_tuple *orig = + &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + struct ip_nat_multi_range mr; +#if 0 + const struct ip_conntrack_tuple *repl = + &master->tuplehash[IP_CT_DIR_REPLY].tuple; + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl*4; +#endif + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); + + mr.rangesize = 1; + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) { + mr.range[0].min_ip = mr.range[0].max_ip = orig->dst.ip; + DEBUGP("orig: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u " + "newsrc: %u.%u.%u.%u\n", + NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph->source), + NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph->dest), + NIPQUAD(orig->dst.ip)); + } else { + mr.range[0].min_ip = mr.range[0].max_ip = orig->src.ip; + mr.range[0].min.udp.port = mr.range[0].max.udp.port = + orig->src.u.udp.port; + mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + + DEBUGP("orig: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u " + "newdst: %u.%u.%u.%u:%u\n", + NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph->source), + NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph->dest), + NIPQUAD(orig->src.ip), ntohs(orig->src.u.udp.port)); + } + + return ip_nat_setup_info(ct,&mr,hooknum); +} + +static struct ip_nat_helper tftp[MAX_PORTS]; +static char tftp_names[MAX_PORTS][10]; + +static void fini(void) +{ + int i; + + for (i = 0 ; i < ports_c; i++) { + DEBUGP("unregistering helper for port %d\n", ports[i]); + ip_nat_helper_unregister(&tftp[i]); + } +} + +static int __init init(void) +{ + int i, ret; + char *tmpname; + + if (!ports[0]) + ports[0] = TFTP_PORT; + + for (i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) { + memset(&tftp[i], 0, sizeof(struct ip_nat_helper)); + + tftp[i].tuple.dst.protonum = IPPROTO_UDP; + tftp[i].tuple.src.u.udp.port = htons(ports[i]); + tftp[i].mask.dst.protonum = 0xFFFF; + tftp[i].mask.src.u.udp.port = 0xFFFF; + tftp[i].help = tftp_nat_help; + tftp[i].flags = 0; + tftp[i].me = THIS_MODULE; + tftp[i].expect = tftp_nat_expected; + + tmpname = &tftp_names[i][0]; + if (ports[i] == TFTP_PORT) + sprintf(tmpname, "tftp"); + else + sprintf(tmpname, "tftp-%d", i); + tftp[i].name = tmpname; + + DEBUGP("ip_nat_tftp: registering for port %d: name %s\n", + ports[i], tftp[i].name); + ret = ip_nat_helper_register(&tftp[i]); + + if (ret) { + printk("ip_nat_tftp: unable to register for port %d\n", + ports[i]); + fini(); + return ret; + } + ports_c++; + } + return ret; +} + +module_init(init); +module_exit(fini); diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ipt_helper.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ipt_helper.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ipt_helper.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ipt_helper.c Sat Mar 30 23:35:32 2002 @@ -0,0 +1,112 @@ +/* + * iptables module to match on related connections + * (c) 2001 Martin Josefsson + * + * Released under the terms of GNU GPLv2. + * + * 19 Mar 2002 Harald Welte +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +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_helper_info *info = matchinfo; + struct ip_conntrack_expect *exp; + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + + ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + if (!ct) { + DEBUGP("ipt_helper: Eek! invalid conntrack?\n"); + return 0; + } + + if (!ct->master) { + DEBUGP("ipt_helper: conntrack %p has no master\n", ct); + return 0; + } + + exp = ct->master; + if (!exp->expectant) { + DEBUGP("ipt_helper: expectation %p without expectant !?!\n", + exp); + return 0; + } + + if (!exp->expectant->helper) { + DEBUGP("ipt_helper: master ct %p has no helper\n", + exp->expectant); + return 0; + } + + DEBUGP("master's name = %s , info->name = %s\n", + exp->expectant->helper->name, info->name); + + return !strncmp(exp->expectant->helper->name, info->name, + strlen(exp->expectant->helper->name)) ^ info->invert; +} + +static int check(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_helper_info *info = matchinfo; + + info->name[29] = '\0'; + + /* verify size */ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_helper_info))) + return 0; + + /* verify that we actually should match anything */ + if ( strlen(info->name) == 0 ) + return 0; + + return 1; +} + +static struct ipt_match helper_match += { { NULL, NULL }, "helper", &match, &check, NULL, THIS_MODULE }; + +static int __init init(void) +{ + /* NULL if ip_conntrack not a module */ + if (ip_conntrack_module) + __MOD_INC_USE_COUNT(ip_conntrack_module); + return ipt_register_match(&helper_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&helper_match); + if (ip_conntrack_module) + __MOD_DEC_USE_COUNT(ip_conntrack_module); +} + +module_init(init); +module_exit(fini); + diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ipt_recent.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ipt_recent.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ipt_recent.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ipt_recent.c Sat Mar 30 23:37:36 2002 @@ -0,0 +1,518 @@ +/* Kernel module to check if the source address has been seen recently. */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Defaults, these can be overridden on the module command-line. */ +static int ip_list_tot = 40; +static int ip_pkt_list_tot = 10; + +MODULE_AUTHOR("Stephen Frost "); +MODULE_DESCRIPTION("IP tables recently seen matching module"); +MODULE_LICENSE("GPL"); +MODULE_PARM(ip_list_tot,"i"); +MODULE_PARM(ip_pkt_list_tot,"i"); +MODULE_PARM_DESC(ip_list_tot,"number of IPs to remember per list"); +MODULE_PARM_DESC(ip_pkt_list_tot,"number of packets per IP to remember"); + +/* Structure of our list of recently seen addresses. */ +struct recent_ip_list { + u_int32_t saddr; + u_int8_t ttl; + u_int32_t last_seen; + u_int32_t *last_pkts; + u_int32_t oldest_pkt; +}; + +/* Structure of our linked list of tables of recent lists. */ +struct recent_ip_tables { + char name[200]; + int count; + struct recent_ip_list *table; + struct recent_ip_tables *next; + spinlock_t list_lock; + struct proc_dir_entry *status_proc; +}; + +/* Our current list of addresses we have recently seen. + * Only added to on a --set, and only updated on --set || --update + */ +static struct recent_ip_tables *r_tables; + +/* We protect r_list with this spinlock so two processors are not modifying + * the list at the same time. + */ +static spinlock_t recent_lock = SPIN_LOCK_UNLOCKED; + +/* Our /proc/net/ipt_recent entry */ +static struct proc_dir_entry *proc_net_ipt_recent; + +/* Function declaration for later. */ +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); + +/* This is the function which produces the output for our /proc output + * interface which lists each IP address, the last seen time and the + * other recent times the address was seen. + */ + +static int ip_recent_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data) +{ + int len = 0, count, last_len = 0, pkt_count; + off_t pos = 0; + off_t begin = 0; + struct recent_ip_tables *curr_table; + + curr_table = (struct recent_ip_tables*) data; + + spin_lock_bh(&curr_table->list_lock); + for(count = 0; count < ip_list_tot; count++) { + if(!curr_table->table[count].saddr) continue; + last_len = len; + len += sprintf(buffer+len,"src=%u.%u.%u.%u ",NIPQUAD(curr_table->table[count].saddr)); + len += sprintf(buffer+len,"ttl: %d ",curr_table->table[count].ttl); + len += sprintf(buffer+len,"last_seen: %d ",curr_table->table[count].last_seen); + len += sprintf(buffer+len,"oldest_pkt: %d ",curr_table->table[count].oldest_pkt); + len += sprintf(buffer+len,"last_pkts: %d",curr_table->table[count].last_pkts[0]); + for(pkt_count = 1; pkt_count < ip_pkt_list_tot; pkt_count++) { + if(!curr_table->table[count].last_pkts[pkt_count]) break; + len += sprintf(buffer+len,", %d",curr_table->table[count].last_pkts[pkt_count]); + } + len += sprintf(buffer+len,"\n"); + pos = begin + len; + if(pos < offset) { len = 0; begin = pos; } + if(pos > offset + length) { len = last_len; break; } + } + + *start = buffer + (offset - begin); + len -= (offset - begin); + if(len > length) len = length; + + spin_unlock_bh(&curr_table->list_lock); + return len; +} + +/* ip_recent_ctrl provides an interface for users to modify the table + * directly. This allows adding entries, removing entries, and + * flushing the entire table. + * This is done by opening up the appropriate table for writing and + * sending one of: + * xx.xx.xx.xx -- Add entry to table with current time + * +xx.xx.xx.xx -- Add entry to table with current time + * -xx.xx.xx.xx -- Remove entry from table + * -0.0.0.0 -- Flush table, remove all entries + */ + +static int ip_recent_ctrl(struct file *file, const char *input, unsigned long size, void *data) +{ + static const u_int32_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff }; + u_int32_t val; + int base; + char c, *cp; + union iaddr { + uint8_t bytes[4]; + uint32_t word; + } res; + uint8_t *pp = res.bytes; + int digit; + + char buffer[1024]; + int len, check_set = 0, count; + u_int32_t saddr = 0; + struct sk_buff skb; + struct ipt_recent_info info; + struct recent_ip_tables *curr_table; + + curr_table = (struct recent_ip_tables*) data; + + if(size > 1024) len = 1024; else len = size; + + if(copy_from_user(buffer,input,len)) return -EFAULT; + + buffer[len-1] = '\0'; + + cp = buffer; + if(buffer[0] == '+') { check_set = IPT_RECENT_SET; cp++; } + if(buffer[0] == '-') { check_set = IPT_RECENT_REMOVE; cp++; } + if(buffer[0] >= '0' && buffer[0] <= '9') check_set = IPT_RECENT_SET; + if(!check_set) return len; + + /* Get saddr (effectively inet_aton()) */ + /* Shamelessly stolen from libc, a function in the kernel for doing + * this would, of course, be greatly preferred, but our options appear + * to be rather limited, so we will just do it ourselves here. + */ + res.word = 0; + + c = *cp; + for(;;) { + if(!isdigit(c)) return len; + val = 0; base = 10; digit = 0; + if(c == '0') { + c = *++cp; + if(c == 'x' || c == 'X') base = 16, c = *++cp; + else { base = 8; digit = 1; } + } + for(;;) { + if(isascii(c) && isdigit(c)) { + if(base == 8 && (c == '8' || c == '0')) return len; + val = (val * base) + (c - '0'); + c = *++cp; + digit = 1; + } else if(base == 16 && isascii(c) && isxdigit(c)) { + val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + digit = 1; + } else break; + } + if(c == '.') { + if(pp > res.bytes + 2 || val > 0xff) return len; + *pp++ = val; + c = *++cp; + } else break; + } + if(c != '\0' && (!isascii(c) || !isspace(c))) return len; + if(!digit) return len; + + if(val > max[pp - res.bytes]) return len; + saddr = res.word | htonl(val); + + if(!saddr && check_set == IPT_RECENT_SET) return len; + + /* Check if we are asked to flush the entire table */ + if(!saddr && check_set == IPT_RECENT_REMOVE) { + spin_lock_bh(&curr_table->list_lock); + for(count = 0; count < ip_list_tot; count++) { + curr_table->table[count].last_seen = 0; + curr_table->table[count].saddr = 0; + curr_table->table[count].ttl = 0; + memset(curr_table->table[count].last_pkts,0,ip_pkt_list_tot*sizeof(u_int32_t)); + curr_table->table[count].oldest_pkt = 0; + } + spin_unlock_bh(&curr_table->list_lock); + return len; + } + + /* Set up and just call match */ + info.seconds = 0; + info.hit_count = 0; + info.check_set = check_set; + info.invert = 0; + strncpy(info.name,curr_table->name,200); + + skb.nh.iph = kmalloc(sizeof(struct iphdr),GFP_KERNEL); + if(!skb.nh.iph) { return -ENOMEM; } + + skb.nh.iph->saddr = saddr; + match(&skb,NULL,NULL,&info,0,NULL,sizeof(info),NULL); + kfree(skb.nh.iph); + + return len; +} + +/* 'match' is our primary function, called by the kernel whenever a rule is + * hit with our module as an option to it. + * What this function does depends on what was specifically asked of it by + * the user: + * --set -- Add or update last seen time of the source address of the packet + * -- matchinfo->check_set == IPT_RECENT_SET + * --rcheck -- Just check if the source address is in the list + * -- matchinfo->check_set == IPT_RECENT_CHECK + * --update -- If the source address is in the list, update last_seen + * -- matchinfo->check_set == IPT_RECENT_UPDATE + * --remove -- If the source address is in the list, remove it + * -- matchinfo->check_set == IPT_RECENT_REMOVE + * --seconds -- Option to --rcheck/--update, only match if last_seen within seconds + * -- matchinfo->seconds + * --hitcount -- Option to --rcheck/--update, only match if seen hitcount times + * -- matchinfo->hit_count + * --seconds and --hitcount can be combined + */ +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) +{ + int count, pkt_count, oldest = 0, hits_found, ans; + unsigned long now = jiffies; + unsigned long oldest_time; + const struct ipt_recent_info *info = matchinfo; + u_int32_t saddr = skb->nh.iph->saddr; + u_int8_t ttl = skb->nh.iph->ttl; + struct recent_ip_tables *curr_table; + struct recent_ip_tables *last_table; + struct recent_ip_list *r_list; + + /* Default is false ^ info->invert */ + ans = info->invert; + + /* Find the right table */ + spin_lock_bh(&recent_lock); + curr_table = r_tables; + while( (last_table = curr_table) && strncmp(info->name,curr_table->name,200) && (curr_table = curr_table->next) ); + spin_unlock_bh(&recent_lock); + + /* Table with this name not found, match impossible */ + if(!curr_table) { return ans; } + + /* Make sure no one is changing the list while we work with it */ + spin_lock_bh(&curr_table->list_lock); + + r_list = curr_table->table; + + for(count = 0, oldest_time = r_list[0].last_seen; count < ip_list_tot; count++) { + /* Keep track of empty spaces which can be filled up later */ + if(!r_list[count].saddr) { oldest = count; continue; } + /* Keep track of the oldest entry if we have been asked to add this entry + * to the table if it does not exist. */ + if(info->check_set & IPT_RECENT_SET && r_list[count].last_seen < oldest_time) { + oldest_time = r_list[count].last_seen; + oldest = count; + } + if(r_list[count].saddr == saddr) { + /* We have a match on address, now to make sure it meets all requirements for a + * full match. */ + /* Check for TTL match if requested. If TTL is zero then a match would never + * happen, so match regardless of existing TTL in that case. Zero means the + * entry was added via the /proc interface anyway, so we will just use the + * first TTL we get for that IP address. */ + if(info->check_set & IPT_RECENT_TTL && r_list[count].ttl && r_list[count].ttl != ttl) continue; + if(info->check_set & IPT_RECENT_CHECK || info->check_set & IPT_RECENT_UPDATE) { + if(!info->seconds && !info->hit_count) ans = !info->invert; else ans = info->invert; + if(info->seconds && !info->hit_count) { + if(time_before(now,r_list[count].last_seen+info->seconds*HZ)) ans = !info->invert; else ans = info->invert; + } + if(info->seconds && info->hit_count) { + for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) { + if(time_before(now,r_list[count].last_pkts[pkt_count]+info->seconds*HZ)) hits_found++; + } + if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert; + } + if(info->hit_count && !info->seconds) { + for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) { + if(r_list[count].last_pkts[pkt_count] == 0) break; + hits_found++; + } + if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert; + } + } + /* If and only if we have been asked to SET, or to UPDATE (on match) do we add the + * current timestamp to the last_seen. */ + if((info->check_set & IPT_RECENT_SET && (ans = !info->invert)) || (info->check_set & IPT_RECENT_UPDATE && ans)) { + r_list[count].last_seen = now; + /* If asked to match on TTL we will not hit here unless the TTL already matches, + * or is zero. If zero then the IP address was added via /proc, so we need to + * add the TTL here for the IP address. If non-zero, then we are not matching + * on TTL, or the TTL already matches, so it does not hurt to set it here. */ + r_list[count].ttl = ttl; + r_list[count].last_pkts[r_list[count].oldest_pkt] = now; + r_list[count].oldest_pkt = ++r_list[count].oldest_pkt % ip_pkt_list_tot; + } + /* If we have been asked to remove the entry from the list, just set it to 0 */ + if(info->check_set & IPT_RECENT_REMOVE) { + r_list[count].last_seen = 0; + r_list[count].saddr = 0; + r_list[count].ttl = 0; + memset(r_list[count].last_pkts,0,ip_pkt_list_tot*sizeof(u_int32_t)); + r_list[count].oldest_pkt = 0; + ans = !info->invert; + } + spin_unlock_bh(&curr_table->list_lock); + return ans; + } + } + + /* If the address is not currently in the list and we have been asked to SET + * then we need to add it to the list. We do this by replacing the oldest + * entry in the list which is determined when we step through the list. */ + if(info->check_set & IPT_RECENT_SET) { + memset(r_list[oldest].last_pkts,0,ip_pkt_list_tot*sizeof(u_int32_t)); + r_list[oldest].saddr = saddr; + r_list[oldest].ttl = ttl; + r_list[oldest].last_seen = now; + r_list[oldest].last_pkts[0] = now; + r_list[oldest].oldest_pkt = 1; + + ans = !info->invert; + } + + spin_unlock_bh(&curr_table->list_lock); + return ans; +} + +/* This function is to verify that the rule given during the userspace iptables + * command is correct. + * If the command is valid then we check if the table name referred to by the + * rule exists, if not it is created. + */ +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + int flag = 0, c; + u_int32_t *hold; + const struct ipt_recent_info *info = matchinfo; + struct recent_ip_tables *curr_table, *last_table; + if (matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return 0; + + /* seconds and hit_count only valid for CHECK/UPDATE */ + if(info->check_set & IPT_RECENT_SET) { flag++; if(info->seconds || info->hit_count) return 0; } + if(info->check_set & IPT_RECENT_REMOVE) { flag++; if(info->seconds || info->hit_count) return 0; } + if(info->check_set & IPT_RECENT_CHECK) flag++; + if(info->check_set & IPT_RECENT_UPDATE) flag++; + + /* One and only one of these should ever be set */ + if(flag != 1) return 0; + + /* Name must be set to something */ + if(!info->name || !info->name[0]) return 0; + + /* Things look good, create a list for this if it does not exist */ + /* Lock the linked list while we play with it */ + spin_lock_bh(&recent_lock); + + /* Look for an entry with this name already created */ + /* Finds the end of the list and the entry before the end if current name does not exist */ + curr_table = r_tables; + while( (last_table = curr_table) && strncmp(info->name,curr_table->name,200) && (curr_table = curr_table->next) ); + + /* If a table already exists just increment the count on that table and return */ + if(curr_table) { curr_table->count++; spin_unlock_bh(&recent_lock); return 1; } + + /* Table with this name not found */ + /* Allocate memory for new linked list item */ + curr_table = kmalloc(sizeof(struct recent_ip_tables),GFP_KERNEL); + if(curr_table == NULL) { spin_unlock_bh(&recent_lock); return -ENOMEM; } + + curr_table->list_lock = SPIN_LOCK_UNLOCKED; + curr_table->next = NULL; + curr_table->count = 1; + strncpy(curr_table->name,info->name,200); + + /* Allocate memory for this table and the list of packets in each entry. */ + curr_table->table = kmalloc(sizeof(struct recent_ip_list)*ip_list_tot,GFP_KERNEL); + if(curr_table->table == NULL) { spin_unlock_bh(&recent_lock); return -ENOMEM; } + memset(curr_table->table,0,sizeof(struct recent_ip_list)*ip_list_tot); + hold = kmalloc(sizeof(u_int32_t)*ip_pkt_list_tot*ip_list_tot,GFP_KERNEL); + if(hold == NULL) { kfree(curr_table->table); spin_unlock_bh(&recent_lock); return -ENOMEM; } + for(c = 0; c < ip_list_tot; c++) { + curr_table->table[c].last_pkts = hold + c*ip_pkt_list_tot; + } + + /* Create our proc 'status' entry. */ + curr_table->status_proc = create_proc_entry(curr_table->name, 0644, proc_net_ipt_recent); + if (curr_table->status_proc) curr_table->status_proc->owner = THIS_MODULE; + else { + kfree(curr_table->table); + kfree(curr_table); + kfree(hold); + return -ENOMEM; + } + curr_table->status_proc->read_proc = ip_recent_get_info; + curr_table->status_proc->write_proc = ip_recent_ctrl; + curr_table->status_proc->data = curr_table; + + /* Put the new table in place */ + if(!last_table) r_tables = curr_table; else last_table->next = curr_table; + + spin_unlock_bh(&recent_lock); + + return 1; +} + +/* This function is called in the event that a rule matching this module is + * removed. + * When this happens we need to check if there are no other rules matching + * the table given. If that is the case then we remove the table and clean + * up its memory. + */ +static void +destroy(void *matchinfo, unsigned int matchsize) +{ + const struct ipt_recent_info *info = matchinfo; + struct recent_ip_tables *curr_table, *last_table; + if(matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return; + + /* Lock the linked list while we play with it */ + spin_lock_bh(&recent_lock); + + /* Look for an entry with this name already created */ + /* Finds the end of the list and the entry before the end if current name does not exist */ + last_table = NULL; + curr_table = r_tables; + if(!curr_table) { spin_unlock_bh(&recent_lock); return; } + while( strncmp(info->name,curr_table->name,200) && (last_table = curr_table) && (curr_table = curr_table->next) ); + + /* If a table does not exist then do nothing and return */ + if(!curr_table) { spin_unlock_bh(&recent_lock); return; } + + curr_table->count--; + /* If count is still non-zero then there are still rules referenceing it so we do nothing */ + if(curr_table->count) { spin_unlock_bh(&recent_lock); return; } + + /* Count must be zero so we remove this table from the list */ + if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next; + + /* lock to make sure any late-runners still using this after we removed it from + * the list finish up then remove everything */ + spin_lock_bh(&curr_table->list_lock); + remove_proc_entry(curr_table->name,proc_net_ipt_recent); + kfree(curr_table->table[0].last_pkts); + kfree(curr_table->table); + spin_unlock_bh(&curr_table->list_lock); + kfree(curr_table); + + spin_unlock_bh(&recent_lock); + + return; +} + +/* This is the structure we pass to ipt_register to register our + * module with iptables. + */ +static struct ipt_match recent_match += { { NULL, NULL }, "recent", &match, &checkentry, &destroy, THIS_MODULE }; + +/* Kernel module initialization. */ +static int __init init(void) +{ + proc_net_ipt_recent = proc_mkdir("ipt_recent",proc_net); + if(!proc_net_ipt_recent) return -ENOMEM; + + return ipt_register_match(&recent_match); +} + +/* Kernel module destruction. */ +static void __exit fini(void) +{ + ipt_unregister_match(&recent_match); + + remove_proc_entry("ipt_recent",proc_net); +} + +/* Register our module with the kernel. */ +module_init(init); +module_exit(fini); diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ipt_record_rpc.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ipt_record_rpc.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ipt_record_rpc.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ipt_record_rpc.c Sat Mar 30 23:38:12 2002 @@ -0,0 +1,169 @@ +/* Kernel module to match RPC connection tracking information. */ +/* Marcelo Barbosa Lima - marcelo.lima@dcc.unicamp.br */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EXPORT_NO_SYMBOLS; + +extern struct list_head expect_rpc_list_tcp; +extern struct module *ip_conntrack_rpc_tcp; +extern struct list_head expect_rpc_list_udp; +extern struct module *ip_conntrack_rpc_udp; +DECLARE_RWLOCK_EXTERN(ipct_rpc_tcp_lock); +DECLARE_RWLOCK_EXTERN(ipct_rpc_udp_lock); + +#define ASSERT_READ_LOCK(x) \ +do { \ + if (x == &expect_rpc_list_udp) \ + MUST_BE_READ_LOCKED(&ipct_rpc_udp_lock); \ + else if (x == &expect_rpc_list_tcp) \ + MUST_BE_READ_LOCKED(&ipct_rpc_tcp_lock); \ +} while (0) + +#define ASSERT_WRITE_LOCK(x) \ +do { \ + if (x == &expect_rpc_list_udp) \ + MUST_BE_WRITE_LOCKED(&ipct_rpc_udp_lock); \ + else if (x == &expect_rpc_list_tcp) \ + MUST_BE_WRITE_LOCKED(&ipct_rpc_tcp_lock); \ +} while (0) +#include + + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + struct expect_rpc *rpc_bind; + const u_int32_t *data; + enum ip_conntrack_dir dir; + const struct tcphdr *tcp; + + ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + if (ct) { + dir = CTINFO2DIR(ctinfo); + + /* This does sanity check in UDP or TCP packets like their */ + /* respective modules and permits only get port procedure in */ + /* client communications with portmapper */ + switch(ct->tuplehash[0].tuple.dst.protonum){ + + case IPPROTO_UDP: + if(offset == 0 && datalen < sizeof(struct udphdr)) { + *hotdrop = 1; + return 0; + } + if(ntohs(ct->tuplehash[dir].tuple.dst.u.all) == 111){ + + if((datalen - sizeof(struct udphdr)) != 56) + return 0; + + data = (const u_int32_t *)hdr + 7; + if(IXDR_GET_INT32(data) != 3) + return 0; + else + return (1 && (!offset)); + } + if(ntohs(ct->tuplehash[!dir].tuple.dst.u.all) == 111) + return (1 && (!offset)); + + case IPPROTO_TCP: + if(offset == 0 && datalen < sizeof(struct tcphdr)) { + *hotdrop = 1; + return 0; + } + if(ntohs(ct->tuplehash[dir].tuple.dst.u.all) == 111) { + tcp = hdr; + if(datalen == (tcp->doff * 4)) return (1 && (!offset)); + + /* Tests if packet len is ok */ + if((datalen - (tcp->doff * 4)) != 60) + return 0; + data = (const u_int32_t *)tcp + tcp->doff + 6; + + if(IXDR_GET_INT32(data) != 3) + return 0; + else + return (1 && (!offset)); + } + if(ntohs(ct->tuplehash[!dir].tuple.dst.u.all) == 111) + return (1 && (!offset)); + } + + /* Client using UDP in communication with portmapper */ + WRITE_LOCK(&ipct_rpc_udp_lock); + rpc_bind = LIST_FIND(&expect_rpc_list_udp, expect_rpc_cmp, struct expect_rpc *, + ntohs(ct->tuplehash[0].tuple.dst.u.all), + ct->tuplehash[1].tuple.src.ip, ct->tuplehash[0].tuple.src.ip, + ct->tuplehash[0].tuple.dst.protonum); + + if(rpc_bind) { + if(del_timer(&rpc_bind->timeout)) { + rpc_bind->timeout.expires = jiffies + EXPIRES; + add_timer(&rpc_bind->timeout); + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + /* Must not be a fragment */ + return (1 && (!offset)); + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + + /* Client using TCP in communication with portmapper */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + rpc_bind = LIST_FIND(&expect_rpc_list_tcp, expect_rpc_cmp, struct expect_rpc *, + ntohs(ct->tuplehash[0].tuple.dst.u.all), + ct->tuplehash[1].tuple.src.ip, ct->tuplehash[0].tuple.src.ip, + ct->tuplehash[0].tuple.dst.protonum); + + if (rpc_bind) { + if (del_timer(&rpc_bind->timeout)) { + rpc_bind->timeout.expires = jiffies + EXPIRES; + add_timer(&rpc_bind->timeout); + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return (1 && (!offset)); + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return 0; + } + return 0; +} + + +static struct ipt_match record_rpc_match += { { NULL, NULL }, "record_rpc", &match, NULL, NULL, THIS_MODULE }; + +int __init init(void) +{ + __MOD_INC_USE_COUNT(ip_conntrack_module); + __MOD_INC_USE_COUNT(ip_conntrack_rpc_udp); + __MOD_INC_USE_COUNT(ip_conntrack_rpc_tcp); + return ipt_register_match(&record_rpc_match); +} + +void __exit cleanup(void) +{ + ipt_unregister_match(&record_rpc_match); + __MOD_DEC_USE_COUNT(ip_conntrack_module); + __MOD_DEC_USE_COUNT(ip_conntrack_rpc_tcp); + __MOD_DEC_USE_COUNT(ip_conntrack_rpc_udp); +} + +module_init(init); +module_exit(cleanup); diff -urN linux-2419p5-ipt126a-base/net/ipv4/netfilter/ipt_string.c linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ipt_string.c --- linux-2419p5-ipt126a-base/net/ipv4/netfilter/ipt_string.c Thu Jan 1 01:00:00 1970 +++ linux-2419p5-ipt126a-extra/net/ipv4/netfilter/ipt_string.c Sat Mar 30 23:38:25 2002 @@ -0,0 +1,214 @@ +/* Kernel module to match a string into a packet. + * + * Copyright (C) 2000 Emmanuel Roger + * + * ChangeLog + * 19.02.2002: Gianni Tedesco + * Fixed SMP re-entrancy problem using per-cpu data areas + * for the skip/shift tables. + * 02.05.2001: Gianni Tedesco + * Fixed kernel panic, due to overrunning boyer moore string + * tables. Also slightly tweaked heuristic for deciding what + * search algo to use. + * 27.01.2001: Gianni Tedesco + * Implemented Boyer Moore Sublinear search algorithm + * alongside the existing linear search based on memcmp(). + * Also a quick check to decide which method to use on a per + * packet basis. + */ + +#include +#include +#include +#include +#include + +#include +#include + +struct string_per_cpu { + int *skip; + int *shift; + int *len; +}; + +struct string_per_cpu *bm_string_data=NULL; + +/* Boyer Moore Sublinear string search - VERY FAST */ +char *search_sublinear (char *needle, char *haystack, int needle_len, int haystack_len) +{ + int M1, right_end, sk, sh; + int ended, j, i; + + int *skip, *shift, *len; + + /* use data suitable for this CPU */ + shift=bm_string_data[smp_processor_id()].shift; + skip=bm_string_data[smp_processor_id()].skip; + len=bm_string_data[smp_processor_id()].len; + + /* Setup skip/shift tables */ + M1 = right_end = needle_len-1; + for (i = 0; i < BM_MAX_HLEN; i++) skip[i] = needle_len; + for (i = 0; needle[i]; i++) skip[needle[i]] = M1 - i; + + for (i = 1; i < needle_len; i++) { + for (j = 0; j < needle_len && needle[M1 - j] == needle[M1 - i - j]; j++); + len[i] = j; + } + + shift[0] = 1; + for (i = 1; i < needle_len; i++) shift[i] = needle_len; + for (i = M1; i > 0; i--) shift[len[i]] = i; + ended = 0; + + for (i = 0; i < needle_len; i++) { + if (len[i] == M1 - i) ended = i; + if (ended) shift[i] = ended; + } + + /* Do the search*/ + while (right_end < haystack_len) + { + for (i = 0; i < needle_len && haystack[right_end - i] == needle[M1 - i]; i++); + if (i == needle_len) { + return haystack+(right_end - M1); + } + + sk = skip[haystack[right_end - i]]; + sh = shift[i]; + right_end = max(right_end - i + sk, right_end + sh); + } + + return NULL; +} + +/* Linear string search based on memcmp() */ +char *search_linear (char *needle, char *haystack, int needle_len, int haystack_len) +{ + char *k = haystack + (haystack_len-needle_len); + char *t = haystack; + + while ( t++ < k ) { + if ( memcmp(t, needle, needle_len) == 0 ) return t; + } + + return NULL; +} + + +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_string_info *info = matchinfo; + struct iphdr *ip = skb->nh.iph; + int hlen, nlen; + char *needle, *haystack; + proc_ipt_search search=search_linear; + + if ( !ip ) return 0; + + /* get lenghts, and validate them */ + nlen=info->len; + hlen=ntohs(ip->tot_len)-(ip->ihl*4); + if ( nlen > hlen ) return 0; + + needle=(char *)&info->string; + haystack=(char *)ip+(ip->ihl*4); + + /* The sublinear search comes in to its own + * on the larger packets */ + if ( (hlen>IPT_STRING_HAYSTACK_THRESH) && + (nlen>IPT_STRING_NEEDLE_THRESH) ) { + if ( hlen < BM_MAX_HLEN ) { + search=search_sublinear; + }else{ + if (net_ratelimit()) + printk(KERN_INFO "ipt_string: Packet too big " + "to attempt sublinear string search " + "(%d bytes)\n", hlen ); + } + } + + return ((search(needle, haystack, nlen, hlen)!=NULL) ^ info->invert); +} + +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_string_info))) + return 0; + + return 1; +} + +void string_freeup_data(void) +{ + int c; + + if ( bm_string_data ) { + for(c=0; c