diff -urN linux-2.4.16-vanilla/Documentation/Configure.help linux-2.4.16-newnat/Documentation/Configure.help --- linux-2.4.16-vanilla/Documentation/Configure.help Thu Nov 22 18:52:44 2001 +++ linux-2.4.16-newnat/Documentation/Configure.help Tue Nov 27 21:48:15 2001 @@ -2368,6 +2368,31 @@ 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'. + FTP protocol support CONFIG_IP_NF_FTP Tracking FTP connections is problematic: special helpers are diff -urN linux-2.4.16-vanilla/Makefile linux-2.4.16-newnat/Makefile --- linux-2.4.16-vanilla/Makefile Mon Nov 26 13:29:17 2001 +++ linux-2.4.16-newnat/Makefile Tue Nov 27 23:38:49 2001 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 17 -EXTRAVERSION = +EXTRAVERSION =-Newnat KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -71,7 +71,7 @@ # images. Uncomment if you want to place them anywhere other than root. # -#export INSTALL_PATH=/boot +export INSTALL_PATH=/boot # # INSTALL_MOD_PATH specifies a prefix to MODLIB for module directory diff -urN linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_conntrack.h --- linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_conntrack.h Tue Oct 30 23:08:12 2001 +++ linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_conntrack.h Tue Nov 27 23:09:19 2001 @@ -6,6 +6,7 @@ #include #include +#include enum ip_conntrack_info { @@ -62,30 +63,90 @@ IPS_ASSURED = (1 << IPS_ASSURED_BIT), }; +#ifdef CONFIG_IP_NF_NAT_NEEDED +#include +#endif + +/* Add protocol helper include file here */ +#if defined(CONFIG_IP_NF_FTP) || defined(CONFIG_IP_NF_FTP_MODULE) +#include +#endif + +#if defined(CONFIG_IP_NF_IRC) || defined(CONFIG_IP_NF_IRC_MODULE) +#include +#endif + +#if defined(CONFIG_IP_NF_TALK) || defined(CONFIG_IP_NF_TALK_MODULE) +#include +#endif + +#if defined(CONFIG_IP_NF_H323) || defined(CONFIG_IP_NF_H323_MODULE) +#include +#endif + struct ip_conntrack_expect { - /* Internal linked list */ + /* Internal linked list (global expectation list) */ struct list_head list; + /* expectation list for this master */ + struct list_head expected_list; + + /* The conntrack of the master connection */ + struct ip_conntrack *expectant; + + /* The conntrack of the sibling connection, set after + * expectation arrived */ + struct ip_conntrack *sibling; + + /* Tuple saved for conntrack */ + struct ip_conntrack_tuple ct_tuple; + + /* Data filled out by the conntrack helpers follow: */ + /* We expect this tuple, with the following mask */ struct ip_conntrack_tuple tuple, mask; /* Function to call after setup and insertion */ int (*expectfn)(struct ip_conntrack *new); - /* The conntrack we are part of (set iff we're live) */ - struct ip_conntrack *expectant; -}; + /* At which sequence number did this expectation occur */ + u_int32_t seq; -#ifdef CONFIG_IP_NF_NAT_NEEDED -#include + union { + /* insert conntrack helper private data (expect) here */ +#if defined(CONFIG_IP_NF_FTP) || defined(CONFIG_IP_NF_FTP_MODULE) + struct ip_ct_ftp_expect exp_ftp_info; #endif -#include - #if defined(CONFIG_IP_NF_IRC) || defined(CONFIG_IP_NF_IRC_MODULE) -#include + struct ip_ct_irc_expect exp_irc_info; +#endif + +#if defined(CONFIG_IP_NF_TALK) || defined(CONFIG_IP_NF_TALK_MODULE) + struct ip_ct_talk_expect exp_talk_info; +#endif + +#if defined(CONFIG_IP_NF_H323) || defined(CONFIG_IP_NF_H323_MODULE) + struct ip_ct_h225_expect exp_h225_info; +#endif + +#ifdef CONFIG_IP_NF_NAT_NEEDED + union { + /* insert nat helper private data here */ + } nat; #endif + } help; +}; + +#define IP_CONNTRACK_EXPECT_SIZEOF_HEAD \ + (2*sizeof(struct list_head) \ + + 2*sizeof(struct ip_conntrack *) \ + + 3*sizeof(struct ip_conntrack_tuple)) +#define IP_CONNTRACK_EXPECT_SIZEOF_DATA \ + (sizeof(struct ip_conntrack_expect) \ + - IP_CONNTRACK_EXPECT_SIZEOF_HEAD) + struct ip_conntrack { @@ -104,10 +165,13 @@ /* If we're expecting another related connection, this will be in expected linked list */ - struct ip_conntrack_expect expected; + struct list_head sibling_list; + + /* Current number of expected connections */ + unsigned int expecting; - /* If we were expected by another connection, this will be it */ - struct nf_ct_info master; + /* If we were expected by an expectation, this will be it */ + struct ip_conntrack_expect *master; /* Helper, if any. */ struct ip_conntrack_helper *helper; @@ -124,10 +188,23 @@ } proto; union { - struct ip_ct_ftp ct_ftp_info; + /* insert conntrack helper private data (master) here */ +#if defined(CONFIG_IP_NF_FTP) || defined(CONFIG_IP_NF_FTP_MODULE) + struct ip_ct_ftp_master ct_ftp_info; +#endif + #if defined(CONFIG_IP_NF_IRC) || defined(CONFIG_IP_NF_IRC_MODULE) - struct ip_ct_irc ct_irc_info; + struct ip_ct_irc_master ct_irc_info; #endif + +#if defined(CONFIG_IP_NF_TALK) || defined(CONFIG_IP_NF_TALK_MODULE) + struct ip_ct_talk_master ct_talk_info; +#endif + +#if defined(CONFIG_IP_NF_H323) || defined(CONFIG_IP_NF_H323_MODULE) + struct ip_ct_h225_master ct_h225_info; +#endif + } help; #ifdef CONFIG_IP_NF_NAT_NEEDED @@ -144,6 +221,9 @@ #endif /* CONFIG_IP_NF_NAT_NEEDED */ }; + +/* get master conntrack via master expectation */ +#define master_ct(conntr) (conntr->master ? conntr->master->expectant : NULL) /* Alter reply tuple (maybe alter helper). If it's already taken, return 0 and don't do alteration. */ diff -urN linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_conntrack_ftp.h linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_conntrack_ftp.h --- linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_conntrack_ftp.h Wed Apr 25 22:00:28 2001 +++ linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_conntrack_ftp.h Tue Nov 27 22:42:55 2001 @@ -23,18 +23,20 @@ IP_CT_FTP_EPSV, }; -/* We record seq number and length of ftp ip/port text here: all in - host order. */ -struct ip_ct_ftp +/* This structure is per expected connection */ +struct ip_ct_ftp_expect { - /* This tells NAT that this is an ftp connection */ - int is_ftp; - u_int32_t seq; - /* 0 means not found yet */ - u_int32_t len; - enum ip_ct_ftp_type ftptype; - /* Port that was to be used */ - u_int16_t port; + /* We record seq number and length of ftp ip/port text here: all in + * host order. */ + + /* sequence number of IP address in packet is in ip_conntrack_expect */ + u_int32_t len; /* length of IP address */ + enum ip_ct_ftp_type ftptype; /* PORT or PASV ? */ + u_int16_t port; /* TCP port that was to be used */ +}; + +/* This structure exists only once per master */ +struct ip_ct_ftp_master { /* Next valid seq position for cmd matching after newline */ u_int32_t seq_aft_nl[IP_CT_DIR_MAX]; /* 0 means seq_match_aft_nl not set */ diff -urN linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_conntrack_h323.h linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_conntrack_h323.h --- linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_conntrack_h323.h Thu Jan 1 00:00:00 1970 +++ linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_conntrack_h323.h Tue Nov 27 22:42:55 2001 @@ -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 IP_CT_IS_H225 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-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_conntrack_helper.h linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_conntrack_helper.h --- linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_conntrack_helper.h Mon Dec 11 21:31:23 2000 +++ linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_conntrack_helper.h Tue Nov 27 23:09:19 2001 @@ -14,6 +14,9 @@ struct ip_conntrack_tuple tuple; struct ip_conntrack_tuple mask; + /* Maximum number of concurrent expected connections */ + unsigned int max_expected; + /* Function to call when data passes; return verdict, or -1 to invalidate. */ int (*help)(const struct iphdr *, size_t len, @@ -24,11 +27,11 @@ extern int ip_conntrack_helper_register(struct ip_conntrack_helper *); extern void ip_conntrack_helper_unregister(struct ip_conntrack_helper *); -/* Add an expected connection: can only have one per connection */ +/* Add an expected connection: can have more than one per connection */ extern int ip_conntrack_expect_related(struct ip_conntrack *related_to, - const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *mask, - int (*expectfn)(struct ip_conntrack *)); -extern void ip_conntrack_unexpect_related(struct ip_conntrack *related_to); + struct ip_conntrack_expect *exp); +extern int ip_conntrack_change_expect(struct ip_conntrack_expect *expect, + struct ip_conntrack_tuple *newtuple); +extern void ip_conntrack_unexpect_related(struct ip_conntrack_expect *exp); #endif /*_IP_CONNTRACK_HELPER_H*/ diff -urN linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_conntrack_irc.h linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_conntrack_irc.h --- linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_conntrack_irc.h Tue Oct 30 23:08:12 2001 +++ linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_conntrack_irc.h Tue Nov 27 23:07:05 2001 @@ -17,9 +17,9 @@ #ifndef __KERNEL__ #error Only in kernel. #endif - +/* #include - +*/ #define IP_CONNTR_IRC 2 struct dccproto { @@ -32,16 +32,43 @@ /* We record seq number and length of irc ip/port text here: all in host order. */ + +/* This structure is per expected connection */ +struct ip_ct_irc_expect +{ + /* length of IP address */ + u_int32_t len; + /* Port that was to be used */ + u_int16_t port; +}; + +/* This structure exists only once per master */ +struct ip_ct_irc_master { +}; + +#endif /* _IP_CONNTRACK_IRC_H */ + +/* struct ip_ct_irc { +*/ /* This tells NAT that this is an IRC connection */ +/* int is_irc; +*/ /* sequence number where address part of DCC command begins */ +/* u_int32_t seq; +*/ /* 0 means not found yet */ +/* u_int32_t len; +*/ /* Port that was to be used */ +/* u_int16_t port; }; -#endif /* _IP_CONNTRACK_IRC_H */ +#endif +*/ + /* _IP_CONNTRACK_IRC_H */ diff -urN linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_conntrack_talk.h linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_conntrack_talk.h --- linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_conntrack_talk.h Thu Jan 1 00:00:00 1970 +++ linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_conntrack_talk.h Tue Nov 27 22:42:55 2001 @@ -0,0 +1,120 @@ +#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 OTALK_PORT 517 +#define TALK_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; +}; + +/* + * Client->server request message formats. + */ +struct otalk_msg { + u_char type; /* request type, see below */ +#define OLD_NAME_SIZE 9 + char l_name[OLD_NAME_SIZE];/* caller's name */ + char r_name[OLD_NAME_SIZE];/* callee's name */ + u_char pad; + u_int32_t id_num; /* message id */ + int32_t pid; /* caller's process id */ +#define TTY_SIZE 16 + char r_tty[TTY_SIZE];/* callee's tty name */ + struct talk_addr addr; /* old (4.3) style */ + struct talk_addr ctl_addr; /* old (4.3) style */ +}; + +struct talk_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 */ +#define NAME_SIZE 12 + char l_name[NAME_SIZE];/* caller's name */ + char r_name[NAME_SIZE];/* callee's name */ + char r_tty[TTY_SIZE];/* callee's tty name */ +}; + +/* + * Server->client response message formats. + */ +struct otalk_response { + 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 talk_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 */ +}; + +#define TALK_RESP(data, ver, member) (ver ? ((struct talk_response *)data)->member : ((struct otalk_response *)data)->member) +#define TALK_MSG(data, ver, member) (ver ? ((struct talk_msg *)data)->member : ((struct otalk_msg *)data)->member) + +#define OTALK_VERSION 0 +#define TALK_VERSION 1 /* protocol version */ + +/* 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 */ + +/* 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 */ + +/* We don't really need much for talk */ + +/* This structure is per expected connection */ +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-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_nat_helper.h linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_nat_helper.h --- linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_nat_helper.h Wed Apr 25 22:00:28 2001 +++ linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_nat_helper.h Tue Nov 27 23:09:40 2001 @@ -17,11 +17,24 @@ /* Helper function: returns verdict */ 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); + /* Returns verdict and sets up NAT for this connection */ + unsigned int (*expect)(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info); + + /* Returns true when the packet must be mangled even when + there hasn't been any expecation generated from it */ + int (*mangle)(struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct sk_buff **pskb); + const char *name; }; @@ -39,5 +52,5 @@ extern int ip_nat_seq_adjust(struct sk_buff *skb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo); -extern void ip_nat_delete_sack(struct sk_buff *skb, struct tcphdr *tcph); +extern void ip_nat_delete_sack(struct sk_buff *skb); #endif diff -urN linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_nat_protocol.h linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_nat_protocol.h --- linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_nat_protocol.h Mon Dec 11 21:31:32 2000 +++ linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_nat_protocol.h Tue Nov 27 23:24:34 2001 @@ -44,6 +44,10 @@ unsigned int (*print_range)(char *buffer, const struct ip_nat_range *range); + + /* Has to decide if a expectation matches one packet or not */ + int (*exp_matches_pkt)(struct ip_conntrack_expect *exp, + struct sk_buff **pskb); }; /* Protocol registration. */ diff -urN linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_nat_rule.h linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_nat_rule.h --- linux-2.4.16-vanilla/include/linux/netfilter_ipv4/ip_nat_rule.h Mon Dec 11 21:31:32 2000 +++ linux-2.4.16-newnat/include/linux/netfilter_ipv4/ip_nat_rule.h Tue Nov 27 23:09:40 2001 @@ -5,24 +5,7 @@ #include #ifdef __KERNEL__ -/* Want to be told when we first NAT an expected packet for a conntrack? */ -struct ip_nat_expect -{ - struct list_head list; - /* Returns 1 (and sets verdict) if it has setup NAT for this - connection */ - int (*expect)(struct sk_buff **pskb, - unsigned int hooknum, - struct ip_conntrack *ct, - struct ip_nat_info *info, - struct ip_conntrack *master, - struct ip_nat_info *masterinfo, - unsigned int *verdict); -}; - -extern int ip_nat_expect_register(struct ip_nat_expect *expect); -extern void ip_nat_expect_unregister(struct ip_nat_expect *expect); extern int ip_nat_rule_init(void) __init; extern void ip_nat_rule_cleanup(void); extern int ip_nat_rule_find(struct sk_buff **pskb, diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/Config.in linux-2.4.16-newnat/net/ipv4/netfilter/Config.in --- linux-2.4.16-vanilla/net/ipv4/netfilter/Config.in Tue Oct 30 23:08:12 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/Config.in Tue Nov 27 21:48:15 2001 @@ -7,6 +7,8 @@ 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 ' 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 ' IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CONNTRACK fi @@ -46,6 +48,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 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' Basic SNMP-ALG support (EXPERIMENTAL)' CONFIG_IP_NF_NAT_SNMP_BASIC $CONFIG_IP_NF_NAT fi diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/Makefile linux-2.4.16-newnat/net/ipv4/netfilter/Makefile --- linux-2.4.16-vanilla/net/ipv4/netfilter/Makefile Tue Oct 30 23:08:12 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/Makefile Tue Nov 27 21:48:15 2001 @@ -31,6 +31,15 @@ # connection tracking obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o +# talk protocol support +obj-$(CONFIG_IP_NF_TALK) += ip_conntrack_talk.o +obj-$(CONFIG_IP_NF_NAT_TALK) += ip_nat_talk.o + +# H.323 support +obj-$(CONFIG_IP_NF_H323) += ip_conntrack_h323.o +obj-$(CONFIG_IP_NF_NAT_H323) += ip_nat_h323.o + + # IRC support obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_conntrack_core.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_conntrack_core.c Tue Aug 7 15:30:50 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_conntrack_core.c Tue Nov 27 21:25:20 2001 @@ -3,7 +3,12 @@ extension. */ /* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General - Public Licence. */ + * Public Licence. + * + * 23 Apr 2001: Harald Welte + * - new API and handling of conntrack/nat helpers + * - now capable of multiple expectations for one master + * */ #ifdef MODULE #define __NO_VERSION__ @@ -44,6 +49,7 @@ #endif DECLARE_RWLOCK(ip_conntrack_lock); +DECLARE_RWLOCK(ip_conntrack_expect_tuple_lock); void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL; LIST_HEAD(expect_list); @@ -150,9 +156,52 @@ return protocol->invert_tuple(inverse, orig); } +/* remove one specific expectation from all lists and free it */ +static void unexpect_related(struct ip_conntrack_expect *expect) +{ + MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); + DEBUGP("unexpect_related(%p)\n", expect); + /* delete from global and local lists */ + list_del(&expect->list); + list_del(&expect->expected_list); + if (!expect->sibling) + expect->expectant->expecting--; + kfree(expect); +} + +/* delete all expectations for this conntrack */ +static void destroy_expectations(struct ip_conntrack *ct) +{ + struct list_head *exp_entry, *next; + struct ip_conntrack_expect *exp; + + DEBUGP("destroy_expectations(%p)\n", ct); + + for (exp_entry = ct->sibling_list.next; + exp_entry != &ct->sibling_list; exp_entry = next) { + next = exp_entry->next; + exp = list_entry(exp_entry, struct ip_conntrack_expect, + expected_list); + + /* we skip established expectations, as we want to delete + * the un-established ones only */ + if (exp->sibling) { + DEBUGP("destroy_expectations: skipping established %p of %p\n", exp->sibling, ct); + continue; + } + + IP_NF_ASSERT(list_inlist(&expect_list, exp)); + IP_NF_ASSERT(exp->expectant == ct); + + /* delete expectation from global and private lists */ + unexpect_related(exp); + } +} + static void clean_from_lists(struct ip_conntrack *ct) { + DEBUGP("clean_from_lists(%p)\n", ct); MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); /* Remove from both hash lists: must not NULL out next ptrs, otherwise we'll look unconfirmed. Fortunately, LIST_DELETE @@ -163,12 +212,9 @@ LIST_DELETE(&ip_conntrack_hash [hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple)], &ct->tuplehash[IP_CT_DIR_REPLY]); - /* If our expected is in the list, take it out. */ - if (ct->expected.expectant) { - IP_NF_ASSERT(list_inlist(&expect_list, &ct->expected)); - IP_NF_ASSERT(ct->expected.expectant == ct); - LIST_DELETE(&expect_list, &ct->expected); - } + + /* Destroy all un-established, pending expectations */ + destroy_expectations(ct); } static void @@ -176,14 +222,33 @@ { struct ip_conntrack *ct = (struct ip_conntrack *)nfct; + DEBUGP("destroy_conntrack(%p)\n", ct); IP_NF_ASSERT(atomic_read(&nfct->use) == 0); IP_NF_ASSERT(!timer_pending(&ct->timeout)); - if (ct->master.master) - nf_conntrack_put(&ct->master); + if (ct->master && master_ct(ct)) + ip_conntrack_put(master_ct(ct)); - if (ip_conntrack_destroyed) + if (ip_conntrack_destroyed) { + DEBUGP("destr_conntr: calling ip_conntrack_destroyed\n"); ip_conntrack_destroyed(ct); + } + + WRITE_LOCK(&ip_conntrack_lock); + /* remove it from the expectation list, as there may + * expectations survive and we want to leave their list + * in a consistent state -HW */ + DEBUGP("destroy_conntrack: lemme see...\n"); + /* list_del(&ct->sibling_list); */ + /* Delete our master expectation from the local list + and destroy it, if we've been expected */ + if (ct->master) { + list_del(&ct->master->expected_list); + kfree(ct->master); + } + WRITE_UNLOCK(&ip_conntrack_lock); + + DEBUGP("destr_conntr: returning ct to slab\n"); kmem_cache_free(ip_conntrack_cachep, ct); atomic_dec(&ip_conntrack_count); } @@ -465,6 +530,7 @@ static inline int expect_cmp(const struct ip_conntrack_expect *i, const struct ip_conntrack_tuple *tuple) { + MUST_BE_READ_LOCKED(&ip_conntrack_expect_tuple_lock); return ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask); } @@ -513,7 +579,7 @@ return ERR_PTR(-ENOMEM); } - memset(conntrack, 0, sizeof(struct ip_conntrack)); + memset(conntrack, 0, sizeof(*conntrack)); atomic_set(&conntrack->ct_general.use, 1); conntrack->ct_general.destroy = destroy_conntrack; conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple; @@ -532,31 +598,37 @@ conntrack->timeout.data = (unsigned long)conntrack; conntrack->timeout.function = death_by_timeout; + INIT_LIST_HEAD(&conntrack->sibling_list); + /* Mark clearly that it's not in the hash table. */ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].list.next = NULL; - /* Write lock required for deletion of expected. Without - this, a read-lock would do. */ WRITE_LOCK(&ip_conntrack_lock); conntrack->helper = LIST_FIND(&helpers, helper_cmp, struct ip_conntrack_helper *, &repl_tuple); + /* Need finding and deleting of expected ONLY if we win race */ + READ_LOCK(&ip_conntrack_expect_tuple_lock); expected = LIST_FIND(&expect_list, expect_cmp, struct ip_conntrack_expect *, tuple); + READ_UNLOCK(&ip_conntrack_expect_tuple_lock); /* If master is not in hash table yet (ie. packet hasn't left this machine yet), how can other end know about expected? Hence these are not the droids you are looking for (if master ct never got confirmed, we'd hold a reference to it and weird things would happen to future packets). */ if (expected && is_confirmed(expected->expectant)) { + DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", + conntrack, expected); /* Welcome, Mr. Bond. We've been expecting you... */ + IP_NF_ASSERT(master_ct(conntrack)); conntrack->status = IPS_EXPECTED; - conntrack->master.master = &expected->expectant->ct_general; - IP_NF_ASSERT(conntrack->master.master); + conntrack->master = expected; + expected->sibling = conntrack; LIST_DELETE(&expect_list, expected); - expected->expectant = NULL; - nf_conntrack_get(&conntrack->master); + expected->expectant->expecting--; + nf_conntrack_get(&master_ct(conntrack)->infos[0]); } atomic_inc(&ip_conntrack_count); WRITE_UNLOCK(&ip_conntrack_lock); @@ -708,60 +780,149 @@ return invert_tuple(inverse, orig, find_proto(orig->dst.protonum)); } -static void unexpect_related(struct ip_conntrack *related_to) -{ - MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); - list_del(&related_to->expected.list); - related_to->expected.expectant = NULL; +static inline int resent_expect(const struct ip_conntrack_expect *i, + const struct ip_conntrack_tuple *tuple, + const struct ip_conntrack_tuple *mask) +{ + DEBUGP("resent_expect\n"); + DEBUGP(" tuple: "); DUMP_TUPLE(&i->tuple); + DEBUGP("ct_tuple: "); DUMP_TUPLE(&i->ct_tuple); + DEBUGP("test tuple: "); DUMP_TUPLE(tuple); + return (((i->ct_tuple.dst.protonum == 0 && ip_ct_tuple_equal(&i->tuple, tuple)) + || (i->ct_tuple.dst.protonum && ip_ct_tuple_equal(&i->ct_tuple, tuple))) + && ip_ct_tuple_equal(&i->mask, mask)); } /* Would two expected things clash? */ static inline int expect_clash(const struct ip_conntrack_expect *i, - const struct ip_conntrack_expect *new) + const struct ip_conntrack_tuple *tuple, + const struct ip_conntrack_tuple *mask) { /* Part covered by intersection of masks must be unequal, otherwise they clash */ struct ip_conntrack_tuple intersect_mask - = { { i->mask.src.ip & new->mask.src.ip, - { i->mask.src.u.all & new->mask.src.u.all } }, - { i->mask.dst.ip & new->mask.dst.ip, - { i->mask.dst.u.all & new->mask.dst.u.all }, - i->mask.dst.protonum & new->mask.dst.protonum } }; + = { { i->mask.src.ip & mask->src.ip, + { i->mask.src.u.all & mask->src.u.all } }, + { i->mask.dst.ip & mask->dst.ip, + { i->mask.dst.u.all & mask->dst.u.all }, + i->mask.dst.protonum & mask->dst.protonum } }; - return ip_ct_tuple_mask_cmp(&i->tuple, &new->tuple, &intersect_mask); + return ip_ct_tuple_mask_cmp(&i->tuple, tuple, &intersect_mask); } /* Add a related connection. */ int ip_conntrack_expect_related(struct ip_conntrack *related_to, - const struct ip_conntrack_tuple *tuple, - const struct ip_conntrack_tuple *mask, - int (*expectfn)(struct ip_conntrack *)) + struct ip_conntrack_expect *expect) { + struct ip_conntrack_expect *new; + WRITE_LOCK(&ip_conntrack_lock); - if (related_to->expected.expectant) - unexpect_related(related_to); + /* Because of the write lock, no reader can walk the lists, + * so there is no need to use the tuple lock too */ - related_to->expected.tuple = *tuple; - related_to->expected.mask = *mask; - related_to->expected.expectfn = expectfn; + DEBUGP("ip_conntrack_expect_related %p\n", related_to); + DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple); + DEBUGP("mask: "); DUMP_TUPLE(&expect->mask); + + new = LIST_FIND(&expect_list, resent_expect, + struct ip_conntrack_expect *, &expect->tuple, &expect->mask); + if (new) { + /* Copy data filled out by the helper, except the tuple and mask + * (NAT may altered it) */ + memcpy((void *)new + IP_CONNTRACK_EXPECT_SIZEOF_HEAD, + (void *)expect + IP_CONNTRACK_EXPECT_SIZEOF_HEAD, + IP_CONNTRACK_EXPECT_SIZEOF_DATA); + WRITE_UNLOCK(&ip_conntrack_lock); + DEBUGP("expect_related: resent packet\n"); + return -EEXIST; + } + if (related_to->helper->max_expected + && related_to->expecting >= related_to->helper->max_expected) { + WRITE_UNLOCK(&ip_conntrack_lock); + DEBUGP("expect_related: max number of expected connections (%i) reached\n", + related_to->helper->max_expected); + return -EPERM; + } + if (LIST_FIND(&expect_list, expect_clash, - struct ip_conntrack_expect *, &related_to->expected)) { + struct ip_conntrack_expect *, &expect->tuple, &expect->mask)) { WRITE_UNLOCK(&ip_conntrack_lock); + DEBUGP("expect_related: busy!\n"); return -EBUSY; } - list_prepend(&expect_list, &related_to->expected); - related_to->expected.expectant = related_to; + new = (struct ip_conntrack_expect *) + kmalloc(sizeof(*expect), GFP_ATOMIC); + + if (!new) { + WRITE_UNLOCK(&ip_conntrack_lock); + DEBUGP("expect_relaed: OOM allocating expect\n"); + return -ENOMEM; + } + + /* Fill the structure with the data */ + memcpy(new, expect, sizeof(*expect)); + INIT_LIST_HEAD(&new->list); + INIT_LIST_HEAD(&new->expected_list); + new->expectant = related_to; + new->sibling = NULL; + memset(&new->ct_tuple, 0, sizeof(new->ct_tuple)); + + /* add to expected list for this connection */ + list_add(&new->expected_list, &related_to->sibling_list); + /* add to global list of expectations */ + list_prepend(&expect_list, &new->list); + related_to->expecting++; + WRITE_UNLOCK(&ip_conntrack_lock); return 0; } -void ip_conntrack_unexpect_related(struct ip_conntrack *related_to) +/* Change tuple in an existing expectation */ +int ip_conntrack_change_expect(struct ip_conntrack_expect *expect, + struct ip_conntrack_tuple *newtuple) +{ + MUST_BE_READ_LOCKED(&ip_conntrack_lock); + + DEBUGP("change_expect:\n"); + DEBUGP("exp tuple: "); DUMP_TUPLE(&expect->tuple); + DEBUGP("exp mask: "); DUMP_TUPLE(&expect->mask); + DEBUGP("newtuple: "); DUMP_TUPLE(newtuple); + if (expect->ct_tuple.dst.protonum == 0) { + /* Never seen before */ + DEBUGP("change expect: never seen before\n"); + if (!ip_ct_tuple_equal(&expect->tuple, newtuple) + && LIST_FIND(&expect_list, expect_clash, + struct ip_conntrack_expect *, newtuple, &expect->mask)) { + /* Force NAT to find an unused tuple */ + return -1; + } else { + WRITE_LOCK(&ip_conntrack_expect_tuple_lock); + memcpy(&expect->ct_tuple, &expect->tuple, sizeof(expect->tuple)); + memcpy(&expect->tuple, newtuple, sizeof(expect->tuple)); + WRITE_UNLOCK(&ip_conntrack_expect_tuple_lock); + return 0; + } + } else { + /* Resent packet */ + DEBUGP("change expect: resent packet\n"); + if (ip_ct_tuple_equal(&expect->tuple, newtuple)) { + return 0; + } else { + /* Force NAT to choose again the same port */ + return -1; + } + } + + return -1; +} + +void ip_conntrack_unexpect_related(struct ip_conntrack_expect *expect) { WRITE_LOCK(&ip_conntrack_lock); - unexpect_related(related_to); + unexpect_related(expect); WRITE_UNLOCK(&ip_conntrack_lock); } @@ -786,6 +947,12 @@ struct ip_conntrack_helper *, newreply); WRITE_UNLOCK(&ip_conntrack_lock); + + /* If it was an expected connection, re-run the expectfn + just because of the possible helper alteration */ + if (conntrack->master && conntrack->master->expectfn) + conntrack->master->expectfn(conntrack); + return 1; } @@ -806,12 +973,7 @@ if (i->ctrack->helper == me) { i->ctrack->helper = NULL; /* Get rid of any expected. */ - if (i->ctrack->expected.expectant) { - IP_NF_ASSERT(i->ctrack->expected.expectant - == i->ctrack); - LIST_DELETE(&expect_list, &i->ctrack->expected); - i->ctrack->expected.expectant = NULL; - } + destroy_expectations(i->ctrack); } return 0; } diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_conntrack_ftp.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_conntrack_ftp.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_conntrack_ftp.c Tue Oct 30 23:08:12 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_conntrack_ftp.c Tue Nov 27 21:28:17 2001 @@ -242,8 +242,10 @@ u_int32_t array[6] = { 0 }; int dir = CTINFO2DIR(ctinfo); unsigned int matchlen, matchoff; - struct ip_conntrack_tuple t, mask; - struct ip_ct_ftp *info = &ct->help.ct_ftp_info; + struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_ftp_expect *exp_ftp_info = &exp->help.exp_ftp_info; + unsigned int i; int found = 0; @@ -271,8 +273,8 @@ } LOCK_BH(&ip_ftp_lock); - old_seq_aft_nl_set = info->seq_aft_nl_set[dir]; - old_seq_aft_nl = info->seq_aft_nl[dir]; + old_seq_aft_nl_set = ct_ftp_info->seq_aft_nl_set[dir]; + old_seq_aft_nl = ct_ftp_info->seq_aft_nl[dir]; DEBUGP("conntrack_ftp: datalen %u\n", datalen); if ((datalen > 0) && (data[datalen-1] == '\n')) { @@ -281,8 +283,9 @@ || after(ntohl(tcph->seq) + datalen, old_seq_aft_nl)) { DEBUGP("conntrack_ftp: updating nl to %u\n", ntohl(tcph->seq) + datalen); - info->seq_aft_nl[dir] = ntohl(tcph->seq) + datalen; - info->seq_aft_nl_set[dir] = 1; + ct_ftp_info->seq_aft_nl[dir] = + ntohl(tcph->seq) + datalen; + ct_ftp_info->seq_aft_nl_set[dir] = 1; } } UNLOCK_BH(&ip_ftp_lock); @@ -335,11 +338,17 @@ LOCK_BH(&ip_ftp_lock); if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]) == ct->tuplehash[dir].tuple.src.ip) { +/* --- newnat 5 -> removed --- info->is_ftp = 21; info->seq = ntohl(tcph->seq) + matchoff; info->len = matchlen; info->ftptype = search[i].ftptype; info->port = array[4] << 8 | array[5]; +*/ + exp->seq = ntohl(tcph->seq) + matchoff; + exp_ftp_info->len = matchlen; + exp_ftp_info->ftptype = search[i].ftptype; + exp_ftp_info->port = array[4] << 8 | array[5]; } else { /* Enrico Scholz's passive FTP to partially RNAT'd ftp server: it really wants us to connect to a @@ -356,18 +365,21 @@ if (!loose) goto out; } - t = ((struct ip_conntrack_tuple) + exp->tuple = ((struct ip_conntrack_tuple) { { ct->tuplehash[!dir].tuple.src.ip, { 0 } }, { htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]), { htons(array[4] << 8 | array[5]) }, IPPROTO_TCP }}); - mask = ((struct ip_conntrack_tuple) + 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, &t, &mask, NULL); + ip_conntrack_expect_related(ct, &expect); out: UNLOCK_BH(&ip_ftp_lock); @@ -400,6 +412,7 @@ ftp[i].tuple.dst.protonum = IPPROTO_TCP; ftp[i].mask.src.u.tcp.port = 0xFFFF; ftp[i].mask.dst.protonum = 0xFFFF; + ftp[i].max_expected = 1; ftp[i].help = help; DEBUGP("ip_ct_ftp: registering helper for port %d\n", ports[i]); diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_conntrack_h323.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_conntrack_h323.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_conntrack_h323.c Thu Jan 1 00:00:00 1970 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_conntrack_h323.c Tue Nov 27 21:06:04 2001 @@ -0,0 +1,293 @@ +/* + * 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)); + + /* 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 = IP_CT_IS_H225 + 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; + +} + +/* h245 helper is not registered! + Only the helper function and the max number of expectations count */ +static struct ip_conntrack_helper h245 = { { NULL, NULL }, + { { 0, { 0 } }, + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, + { 0, { 0 }, 0xFFFF } }, + 8, + h245_help }; + +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 { + /* update the H.225 info */ + LOCK_BH(&ip_h323_lock); + info->is_h225 = IP_CT_IS_H225; + 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 }, + { { 0, { __constant_htons(IP_CT_IS_H225) } }, + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, + { 0, { 0 }, 0xFFFF } }, + 2, + h225_help }; + +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); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_conntrack_irc.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_conntrack_irc.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_conntrack_irc.c Tue Oct 30 23:08:12 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_conntrack_irc.c Tue Nov 27 21:47:49 2001 @@ -1,8 +1,8 @@ -/* IRC extension for IP connection tracking, Version 1.20 +/* IRC extension for IP connection tracking, Version 1.17 * (C) 2000-2001 by Harald Welte * based on RR's ip_conntrack_ftp.c * - * ip_conntrack_irc.c,v 1.20 2001/12/06 07:42:10 laforge Exp + * ip_conntrack_irc.c,v 1.17 2001/04/23 05:21:22 laforge Exp * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -103,23 +103,15 @@ u_int32_t tcplen = len - iph->ihl * 4; u_int32_t datalen = tcplen - tcph->doff * 4; int dir = CTINFO2DIR(ctinfo); - struct ip_conntrack_tuple t, mask; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_irc_expect *exp_irc_info = &exp->help.exp_irc_info; u_int32_t dcc_ip; u_int16_t dcc_port; int i; char *addr_beg_p, *addr_end_p; - struct ip_ct_irc *info = &ct->help.ct_irc_info; - - memset(&mask, 0, sizeof(struct ip_conntrack_tuple)); - mask.dst.u.tcp.port = 0xFFFF; - mask.dst.protonum = 0xFFFF; - DEBUGP("entered\n"); - /* Can't track connections formed before we registered */ - if (!info) - return NF_ACCEPT; /* If packet is coming from IRC server */ if (dir == IP_CT_DIR_REPLY) @@ -194,20 +186,22 @@ /* save position of address in dcc string, * neccessary for NAT */ - info->is_irc = IP_CONNTR_IRC; DEBUGP("tcph->seq = %u\n", tcph->seq); - info->seq = ntohl(tcph->seq) + (addr_beg_p - _data); - info->len = (addr_end_p - addr_beg_p); - info->port = dcc_port; + exp->seq = ntohl(tcph->seq) + (addr_beg_p - _data); + exp_irc_info->len = (addr_end_p - addr_beg_p); + exp_irc_info->port = dcc_port; DEBUGP("wrote info seq=%u (ofs=%u), len=%d\n", info->seq, (addr_end_p - _data), info->len); - memset(&t, 0, sizeof(t)); - t.src.ip = 0; - t.src.u.tcp.port = 0; - t.dst.ip = htonl(dcc_ip); - t.dst.u.tcp.port = htons(info->port); - t.dst.protonum = IPPROTO_TCP; + exp->tuple = ((struct ip_conntrack_tuple) + { { 0, { 0 } }, + { htonl(dcc_ip), { htons(dcc_port) }, + IPPROTO_TCP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = NULL; DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", NIPQUAD(t.src.ip), @@ -215,7 +209,7 @@ NIPQUAD(t.dst.ip), ntohs(t.dst.u.tcp.port)); - ip_conntrack_expect_related(ct, &t, &mask, NULL); + ip_conntrack_expect_related(ct, &expect); UNLOCK_BH(&ip_irc_lock); return NF_ACCEPT; @@ -244,6 +238,7 @@ irc_helpers[i].tuple.dst.protonum = IPPROTO_TCP; irc_helpers[i].mask.src.u.tcp.port = 0xFFFF; irc_helpers[i].mask.dst.protonum = 0xFFFF; + irc_helpers[i].max_expected = 1; irc_helpers[i].help = help; DEBUGP("port #%d: %d\n", i, ports[i]); diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_conntrack_standalone.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_conntrack_standalone.c Sun Sep 30 19:26:08 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_conntrack_standalone.c Tue Nov 27 21:25:20 2001 @@ -335,6 +335,9 @@ EXPORT_SYMBOL(ip_ct_selective_cleanup); EXPORT_SYMBOL(ip_ct_refresh); EXPORT_SYMBOL(ip_conntrack_expect_related); +EXPORT_SYMBOL(ip_conntrack_change_expect); +EXPORT_SYMBOL(ip_conntrack_unexpect_related); EXPORT_SYMBOL(ip_conntrack_tuple_taken); EXPORT_SYMBOL(ip_ct_gather_frags); EXPORT_SYMBOL(ip_conntrack_htable_size); +EXPORT_SYMBOL(ip_conntrack_lock); diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_conntrack_talk.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_conntrack_talk.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_conntrack_talk.c Thu Jan 1 00:00:00 1970 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_conntrack_talk.c Tue Nov 27 21:48:14 2001 @@ -0,0 +1,284 @@ +/* + * 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_conntrack_talk.o otalk=[0|1] talk=[0|1] + * + * The default is otalk=1 talk=1 + * i.e. support both old talk and talk protocols + * + ** + * + * 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 + +/* Default both old talk and talk are supported */ +static int otalk = 1; +static int talk = 1; +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("talk connection tracking module"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_PARM +MODULE_PARM(otalk, "i"); +MODULE_PARM_DESC(otalk, "support old talk protocol"); +MODULE_PARM(talk, "i"); +MODULE_PARM_DESC(talk, "support talk 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_help_msg(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + int version, + u_char type, + struct talk_addr *addr, + struct talk_addr *ctl_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_msg: %u.%u.%u.%u:%u, ctl %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); + + if (type != ANNOUNCE) + return NF_ACCEPT; + + DEBUGP("ip_ct_talk_help_msg: match, %u.%u.%u.%u:%u, ctl %u.%u.%u.%u:%u!\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + NIPQUAD(ctl_addr->ta_addr), ntohs(ctl_addr->ta_port)); + + /* update the talk info */ + LOCK_BH(&ip_talk_lock); + exp_talk_info->port = htons(version ? TALK_PORT : OTALK_PORT); + + /* expect callee client -> caller server message */ + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { ctl_addr->ta_addr, + { htons(version ? TALK_PORT : OTALK_PORT) }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = NULL; + + DEBUGP("ip_ct_talk_help_msg: 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.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; +} + +static int talk_help_response(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + int version, + 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 (!(type == LOOK_UP + && answer == SUCCESS)) + return NF_ACCEPT; + + DEBUGP("ip_ct_talk_help_response: match, %u.%u.%u.%u:%u!\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port)); + + /* 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 version) +{ + struct udphdr *udph = (void *)iph + iph->ihl * 4; + const char *data = (const char *)udph + sizeof(struct udphdr); + 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 (udplen == sizeof(struct udphdr) + + (version ? sizeof(struct talk_msg) : sizeof(struct otalk_msg))) + return talk_help_msg(iph, len, ct, ctinfo, version, + TALK_MSG(data, version, type), + &(TALK_MSG(data, version, addr)), + &(TALK_MSG(data, version, ctl_addr))); + else if (udplen == sizeof(struct udphdr) + + (version ? sizeof(struct talk_response) : sizeof(struct otalk_response))) + return talk_help_response(iph, len, ct, ctinfo, version, + TALK_RESP(data, version, type), + TALK_RESP(data, version, answer), + &(TALK_RESP(data, version, addr))); + else { + DEBUGP("ip_ct_talk_help: not talk message/response, datalen = %u\n", + (unsigned)udplen - sizeof(struct udphdr)); + return NF_ACCEPT; + } +} + +static int ohelp(const struct iphdr *iph, + size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, OTALK_VERSION); +} + +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_VERSION); +} + +static struct ip_conntrack_helper talk_helpers[2] = { + { { NULL, NULL }, + { { 0, { __constant_htons(OTALK_PORT) } }, + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, + { 0, { 0 }, 0xFFFF } }, + 1, + ohelp }, + { { NULL, NULL }, + { { 0, { __constant_htons(TALK_PORT) } }, + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, + { 0, { 0 }, 0xFFFF } }, + 1, + help } + }; + +static int __init init(void) +{ + if (otalk > 0) + ip_conntrack_helper_register(&talk_helpers[0]); + if (talk > 0) + ip_conntrack_helper_register(&talk_helpers[1]); + + return 0; +} + +static void __exit fini(void) +{ + if (otalk > 0) + ip_conntrack_helper_unregister(&talk_helpers[0]); + if (talk > 0) + ip_conntrack_helper_unregister(&talk_helpers[1]); +} + +module_init(init); +module_exit(fini); diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_core.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_core.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_core.c Sat Nov 10 23:36:38 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_core.c Tue Nov 27 21:25:20 2001 @@ -21,10 +21,12 @@ #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock) #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock) +#include #include #include #include #include +#include #include #if 0 @@ -34,6 +36,7 @@ #endif DECLARE_RWLOCK(ip_nat_lock); +DECLARE_RWLOCK_EXTERN(ip_conntrack_lock); /* Calculated at init based on memory size */ static unsigned int ip_nat_htable_size; @@ -717,6 +720,19 @@ #endif } +static inline int exp_for_packet(struct ip_conntrack_expect *exp, + struct sk_buff **pskb) +{ + int proto = (*pskb)->nh.iph->protocol; + int ret; + + READ_LOCK(&ip_nat_lock); + ret = find_nat_proto(proto)->exp_matches_pkt(exp, pskb); + READ_UNLOCK(&ip_nat_lock); + + return ret; +} + /* Do packet manipulations according to binding. */ unsigned int do_bindings(struct ip_conntrack *ct, @@ -728,6 +744,7 @@ unsigned int i; struct ip_nat_helper *helper; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + int is_tcp = (*pskb)->nh.iph->protocol == IPPROTO_TCP; /* Need nat lock to protect against modification, but neither conntrack (referenced) and helper (deleted with @@ -766,11 +783,71 @@ READ_UNLOCK(&ip_nat_lock); if (helper) { + struct ip_conntrack_expect *exp; + struct list_head *cur_item; + int ret = NF_ACCEPT; + + DEBUGP("do_bindings: helper existing for (%p)\n", ct); + /* Always defragged for helpers */ IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET))); - return helper->help(ct, info, ctinfo, hooknum, pskb); - } else return NF_ACCEPT; + + /* FIXME: put this in a l4-proto specific function, + * and call this function here. */ + /* Delete SACK_OK on initial TCP SYNs. */ + if (is_tcp) + ip_nat_delete_sack(*pskb); + + /* Have to grab read lock before sibling_list traversal */ + /* FIXME: move checksumming from the helpers to this function */ + READ_LOCK(&ip_conntrack_lock); + list_for_each(cur_item, &ct->sibling_list) { + exp = list_entry(cur_item, struct ip_conntrack_expect, + expected_list); + + /* if this expectation is already established, skip */ + if (exp->sibling) + continue; + + if (exp_for_packet(exp, pskb)) { + /* FIXME: May be true multiple times in the case of UDP!! */ + DEBUGP("calling nat helper (exp=%p) for packet\n", + exp); + ret = helper->help(ct, exp, info, ctinfo, + hooknum, pskb); + if (ret != NF_ACCEPT) { + READ_UNLOCK(&ip_conntrack_lock); + return ret; + } + } + } + /* Helper might have to manip the packet even when there is no expectation */ + if (helper->mangle && helper->mangle(ct, ctinfo, pskb)) { + DEBUGP("calling nat helper for packet without expectation\n"); + ret = helper->help(ct, NULL, info, ctinfo, + hooknum, pskb); + if (ret != NF_ACCEPT) { + READ_UNLOCK(&ip_conntrack_lock); + return ret; + } + } + READ_UNLOCK(&ip_conntrack_lock); + + /* FIXME: put this in a l4-proto specific function, + * and call this function here. */ + DEBUGP("ip_nat_core: adjusting sequence number\n"); + /* Adjust sequence number only once per packet + * (helper is called at all hooks) */ + if (is_tcp && hooknum == NF_IP_POST_ROUTING) + ip_nat_seq_adjust(*pskb, ct, ctinfo); + + return ret; + + } else + return NF_ACCEPT; + + /* not reached */ } unsigned int diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_ftp.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_ftp.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_ftp.c Tue Oct 30 23:08:12 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_ftp.c Tue Nov 27 23:24:01 2001 @@ -28,38 +28,31 @@ /* FIXME: Time out? --RR */ -static int -ftp_nat_expected(struct sk_buff **pskb, - unsigned int hooknum, - struct ip_conntrack *ct, - struct ip_nat_info *info, - struct ip_conntrack *master, - struct ip_nat_info *masterinfo, - unsigned int *verdict) -{ - struct ip_nat_multi_range mr; - u_int32_t newdstip, newsrcip, newip; - struct ip_ct_ftp *ftpinfo; - - IP_NF_ASSERT(info); - IP_NF_ASSERT(master); - IP_NF_ASSERT(masterinfo); - - IP_NF_ASSERT(!(info->initialized & (1<help.ct_ftp_info; + static unsigned int + ftp_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; + struct ip_ct_ftp_expect *exp_ftp_info; + + struct ip_conntrack *master = master_ct(ct); + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + + IP_NF_ASSERT(!(info->initialized & (1<is_ftp != 21) { - UNLOCK_BH(&ip_ftp_lock); - DEBUGP("nat_expected: master not ftp\n"); - return 0; - } + DEBUGP("nat_expected: We have a connection!\n"); + exp_ftp_info = &ct->master->help.exp_ftp_info; + + LOCK_BH(&ip_ftp_lock); + + if (exp_ftp_info->ftptype == IP_CT_FTP_PORT + || exp_ftp_info->ftptype == IP_CT_FTP_EPRT) { - if (ftpinfo->ftptype == IP_CT_FTP_PORT - || ftpinfo->ftptype == IP_CT_FTP_EPRT) { /* PORT command: 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; @@ -92,11 +85,9 @@ mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; mr.range[0].min = mr.range[0].max = ((union ip_conntrack_manip_proto) - { htons(ftpinfo->port) }); + { htons(exp_ftp_info->port) }); } - *verdict = ip_nat_setup_info(ct, &mr, hooknum); - - return 1; + return ip_nat_setup_info(ct, &mr, hooknum); } static int @@ -176,27 +167,22 @@ [IP_CT_FTP_EPSV] mangle_epsv_packet }; -static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info, +static int ftp_data_fixup(const struct ip_ct_ftp_expect *ct_ftp_info, struct ip_conntrack *ct, - unsigned int datalen, struct sk_buff **pskb, - enum ip_conntrack_info ctinfo) + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *expect) { u_int32_t newip; struct iphdr *iph = (*pskb)->nh.iph; struct tcphdr *tcph = (void *)iph + iph->ihl*4; u_int16_t port; - struct ip_conntrack_tuple tuple; - /* Don't care about source port */ - const struct ip_conntrack_tuple mask - = { { 0xFFFFFFFF, { 0 } }, - { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF } }; + struct ip_conntrack_tuple newtuple; - memset(&tuple, 0, sizeof(tuple)); MUST_BE_LOCKED(&ip_ftp_lock); - DEBUGP("FTP_NAT: seq %u + %u in %u + %u\n", - ct_ftp_info->seq, ct_ftp_info->len, - ntohl(tcph->seq), datalen); + DEBUGP("FTP_NAT: seq %u + %u in %u\n", + expect->seq, ct_ftp_info->len, + ntohl(tcph->seq)); /* Change address inside packet to match way we're mapping this connection. */ @@ -206,29 +192,34 @@ is */ newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; /* Expect something from client->server */ - tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; - tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + 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 { /* PORT command: must be where server thinks client is */ newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; /* Expect something from server->client */ - tuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; - tuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + newtuple.src.ip = + ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newtuple.dst.ip = + ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; } - tuple.dst.protonum = IPPROTO_TCP; + newtuple.dst.protonum = IPPROTO_TCP; + newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port; /* Try to get same port: if not, try to change it. */ for (port = ct_ftp_info->port; port != 0; port++) { - tuple.dst.u.tcp.port = htons(port); + newtuple.dst.u.tcp.port = htons(port); - if (ip_conntrack_expect_related(ct, &tuple, &mask, NULL) == 0) + if (ip_conntrack_change_expect(expect, &newtuple) == 0) break; } if (port == 0) return 0; if (!mangle[ct_ftp_info->ftptype](pskb, newip, port, - ct_ftp_info->seq - ntohl(tcph->seq), + expect->seq - ntohl(tcph->seq), ct_ftp_info->len, ct, ctinfo)) return 0; @@ -236,6 +227,7 @@ } 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, @@ -245,13 +237,12 @@ struct tcphdr *tcph = (void *)iph + iph->ihl*4; unsigned int datalen; int dir; - int score; - struct ip_ct_ftp *ct_ftp_info - = &ct->help.ct_ftp_info; - - /* Delete SACK_OK on initial TCP SYNs. */ - if (tcph->syn && !tcph->ack) - ip_nat_delete_sack(*pskb, tcph); + struct ip_ct_ftp_expect *ct_ftp_info; + + if (!exp) + DEBUGP("ip_nat_ftp: no exp!!"); + + ct_ftp_info = &exp->help.exp_ftp_info; /* Only mangle things once: original direction in POST_ROUTING and reply direction on PRE_ROUTING. */ @@ -267,51 +258,35 @@ } datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; - score = 0; LOCK_BH(&ip_ftp_lock); - if (ct_ftp_info->len) { - /* If it's in the right range... */ - score += between(ct_ftp_info->seq, ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - score += between(ct_ftp_info->seq + ct_ftp_info->len, - ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - if (score == 1) { - /* Half a match? This means a partial retransmisison. - It's a cracker being funky. */ - if (net_ratelimit()) { - printk("FTP_NAT: partial packet %u/%u in %u/%u\n", - ct_ftp_info->seq, ct_ftp_info->len, - ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - } + /* If it's in the right range... */ + if (between(exp->seq + ct_ftp_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) { + if (!ftp_data_fixup(ct_ftp_info, ct, pskb, ctinfo, exp)) { UNLOCK_BH(&ip_ftp_lock); return NF_DROP; - } else if (score == 2) { - if (!ftp_data_fixup(ct_ftp_info, ct, datalen, - pskb, ctinfo)) { - UNLOCK_BH(&ip_ftp_lock); - return NF_DROP; - } - /* skb may have been reallocated */ - iph = (*pskb)->nh.iph; - tcph = (void *)iph + iph->ihl*4; } + } else { + /* Half a match? This means a partial retransmisison. + It's a cracker being funky. */ + if (net_ratelimit()) { + printk("FTP_NAT: partial packet %u/%u in %u/%u\n", + exp->seq, ct_ftp_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + } + UNLOCK_BH(&ip_ftp_lock); + return NF_DROP; } - UNLOCK_BH(&ip_ftp_lock); - ip_nat_seq_adjust(*pskb, ct, ctinfo); - return NF_ACCEPT; } static struct ip_nat_helper ftp[MAX_PORTS]; static char ftp_names[MAX_PORTS][6]; -static struct ip_nat_expect ftp_expect -= { { NULL, NULL }, ftp_nat_expected }; - /* Not __exit: called from init() */ static void fini(void) { @@ -321,49 +296,45 @@ DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]); ip_nat_helper_unregister(&ftp[i]); } - - ip_nat_expect_unregister(&ftp_expect); } static int __init init(void) { - int i, ret; + int i, ret = 0; char *tmpname; - ret = ip_nat_expect_register(&ftp_expect); - if (ret == 0) { - if (ports[0] == 0) - ports[0] = 21; - - for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { - - memset(&ftp[i], 0, sizeof(struct ip_nat_helper)); - - ftp[i].tuple.dst.protonum = IPPROTO_TCP; - ftp[i].tuple.src.u.tcp.port = htons(ports[i]); - ftp[i].mask.dst.protonum = 0xFFFF; - ftp[i].mask.src.u.tcp.port = 0xFFFF; - ftp[i].help = help; - - tmpname = &ftp_names[i][0]; - sprintf(tmpname, "ftp%2.2d", i); - ftp[i].name = tmpname; - - DEBUGP("ip_nat_ftp: Trying to register for port %d\n", - ports[i]); - ret = ip_nat_helper_register(&ftp[i]); - - if (ret) { - printk("ip_nat_ftp: error registering helper for port %d\n", ports[i]); - fini(); - return ret; - } - ports_c++; - } + if (ports[0] == 0) + ports[0] = 21; - } else { - ip_nat_expect_unregister(&ftp_expect); + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + + memset(&ftp[i], 0, sizeof(struct ip_nat_helper)); + + ftp[i].tuple.dst.protonum = IPPROTO_TCP; + ftp[i].tuple.src.u.tcp.port = htons(ports[i]); + ftp[i].mask.dst.protonum = 0xFFFF; + ftp[i].mask.src.u.tcp.port = 0xFFFF; + ftp[i].help = help; + ftp[i].expect = ftp_nat_expected; + ftp[i].mangle = NULL; + + tmpname = &ftp_names[i][0]; + sprintf(tmpname, "ftp%2.2d", i); + ftp[i].name = tmpname; + + DEBUGP("ip_nat_ftp: Trying to register for port %d\n", + ports[i]); + ret = ip_nat_helper_register(&ftp[i]); + + if (ret) { + printk("ip_nat_ftp: error registering " + "helper for port %d\n", ports[i]); + fini(); + return ret; + } + ports_c++; } + return ret; } diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_h323.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_h323.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_h323.c Thu Jan 1 00:00:00 1970 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_h323.c Tue Nov 27 21:47:12 2001 @@ -0,0 +1,412 @@ +/* + * 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"); + +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 }, + { { 0, { 0 } }, + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, + { 0, { 0 }, 0xFFFF } }, + h225_nat_help, + h225_nat_expected, + NULL, + "H.245" }; + +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 == IP_CT_IS_H225; + 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; + 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: 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) + tcplen - tcph->doff * 4)) + continue; + if (!between(info->seq[i] + 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", + info->seq[i], + ntohl(tcph->seq), + ntohl(tcph->seq) + tcplen - tcph->doff * 4); + } + return NF_DROP; + } + + /* 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, + 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 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 NF_DROP; + } + + /* 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 == IP_CT_IS_H225); + + 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 int h225_nat_mangle(struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct sk_buff **pskb) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); + unsigned int datalen; + struct ip_ct_h225_master *info = &ct->help.ct_h225_info; + + datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4; + + return (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)); +} + + +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); + 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 }, + { { 0, { __constant_htons(IP_CT_IS_H225) } }, + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, + { 0, { 0 }, 0xFFFF } }, + h225_nat_help, + h225_nat_expected, + h225_nat_mangle, + "H.225" }; + +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-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_helper.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_helper.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_helper.c Tue Aug 28 14:09:44 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_helper.c Tue Nov 27 21:25:20 2001 @@ -19,6 +19,7 @@ #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock) #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock) +#include #include #include #include @@ -249,10 +250,16 @@ /* Grrr... SACK. Fuck me even harder. Don't want to fix it on the fly, so blow it away. */ void -ip_nat_delete_sack(struct sk_buff *skb, struct tcphdr *tcph) +ip_nat_delete_sack(struct sk_buff *skb) { + struct iphdr *iph = skb->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl*4; unsigned int i; u_int8_t *opt = (u_int8_t *)tcph; + + /* Delete SACK_OK on initial TCP SYNs. */ + if (!(tcph->syn && !tcph->ack)) + return; DEBUGP("Seeking SACKPERM in SYN packet (doff = %u).\n", tcph->doff * 4); diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_irc.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_irc.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_irc.c Tue Oct 30 23:08:12 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_irc.c Tue Nov 27 21:54:35 2001 @@ -2,7 +2,7 @@ * (C) 2000-2001 by Harald Welte * based on a copy of RR's ip_nat_ftp.c * - * ip_nat_irc.c,v 1.16 2001/12/06 07:42:10 laforge Exp + * ip_nat_irc.c,v 1.14 2001/04/23 05:21:22 laforge Exp * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -40,7 +40,6 @@ MODULE_AUTHOR("Harald Welte "); MODULE_DESCRIPTION("IRC (DCC) network address translation module"); -MODULE_LICENSE("GPL"); #ifdef MODULE_PARM MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); MODULE_PARM_DESC(ports, "port numbers of IRC servers"); @@ -51,42 +50,29 @@ /* FIXME: Time out? --RR */ -static int +static unsigned int irc_nat_expected(struct sk_buff **pskb, unsigned int hooknum, struct ip_conntrack *ct, - struct ip_nat_info *info, - struct ip_conntrack *master, - struct ip_nat_info *masterinfo, unsigned int *verdict) + struct ip_nat_info *info) { struct ip_nat_multi_range mr; u_int32_t newdstip, newsrcip, newip; - struct ip_ct_irc *ircinfo; + + struct ip_conntrack *master = master_ct(ct); IP_NF_ASSERT(info); IP_NF_ASSERT(master); - IP_NF_ASSERT(masterinfo); IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); DEBUGP("nat_expected: We have a connection!\n"); - /* Master must be an irc connection */ - ircinfo = &master->help.ct_irc_info; - LOCK_BH(&ip_irc_lock); - if (ircinfo->is_irc != IP_CONNTR_IRC) { - UNLOCK_BH(&ip_irc_lock); - DEBUGP("nat_expected: master not irc\n"); - return 0; - } - newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; DEBUGP("nat_expected: DCC cmd. %u.%u.%u.%u->%u.%u.%u.%u\n", NIPQUAD(newsrcip), NIPQUAD(newdstip)); - UNLOCK_BH(&ip_irc_lock); - if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) newip = newsrcip; else @@ -99,16 +85,14 @@ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; mr.range[0].min_ip = mr.range[0].max_ip = newip; - *verdict = ip_nat_setup_info(ct, &mr, hooknum); - - return 1; + return ip_nat_setup_info(ct, &mr, hooknum); } -static int irc_data_fixup(const struct ip_ct_irc *ct_irc_info, +static int irc_data_fixup(const struct ip_ct_irc_expect *ct_irc_info, struct ip_conntrack *ct, - unsigned int datalen, struct sk_buff **pskb, - enum ip_conntrack_info ctinfo) + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *expect) { u_int32_t newip; struct ip_conntrack_tuple t; @@ -121,9 +105,9 @@ MUST_BE_LOCKED(&ip_irc_lock); - DEBUGP("IRC_NAT: info (seq %u + %u) packet(seq %u + %u)\n", - ct_irc_info->seq, ct_irc_info->len, - ntohl(tcph->seq), datalen); + DEBUGP("IRC_NAT: info (seq %u + %u) in %u\n", + expect->seq, ct_irc_info->len, + ntohl(tcph->seq)); newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; @@ -133,13 +117,11 @@ only set in ip_conntrack_irc, with ip_irc_lock held writable */ - t = ct->expected.tuple; + t = expect->tuple; t.dst.ip = newip; for (port = ct_irc_info->port; port != 0; port++) { t.dst.u.tcp.port = htons(port); - if (ip_conntrack_expect_related(ct, &t, - &ct->expected.mask, - NULL) == 0) { + if (ip_conntrack_change_expect(expect, &t) == 0) { DEBUGP("using port %d", port); break; } @@ -166,26 +148,28 @@ buffer, NIPQUAD(newip), port); return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, - ct_irc_info->seq - ntohl(tcph->seq), + expect->seq - ntohl(tcph->seq), ct_irc_info->len, buffer, strlen(buffer)); } 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) + unsigned int hooknum, + struct sk_buff **pskb) { struct iphdr *iph = (*pskb)->nh.iph; struct tcphdr *tcph = (void *) iph + iph->ihl * 4; unsigned int datalen; int dir; - int score; - struct ip_ct_irc *ct_irc_info = &ct->help.ct_irc_info; + struct ip_ct_irc_expect *ct_irc_info; - /* Delete SACK_OK on initial TCP SYNs. */ - if (tcph->syn && !tcph->ack) - ip_nat_delete_sack(*pskb, tcph); + if (!exp) + DEBUGP("ip_nat_irc: no exp!!"); + + ct_irc_info = &exp->help.exp_irc_info; /* Only mangle things once: original direction in POST_ROUTING and reply direction on PRE_ROUTING. */ @@ -202,56 +186,36 @@ DEBUGP("got beyond not touching\n"); datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; - score = 0; LOCK_BH(&ip_irc_lock); - if (ct_irc_info->len) { - DEBUGP("got beyond ct_irc_info->len\n"); - - /* If it's in the right range... */ - score += between(ct_irc_info->seq, ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - score += between(ct_irc_info->seq + ct_irc_info->len, - ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - if (score == 1) { - /* Half a match? This means a partial retransmisison. - It's a cracker being funky. */ - if (net_ratelimit()) { - printk - ("IRC_NAT: partial packet %u/%u in %u/%u\n", - ct_irc_info->seq, ct_irc_info->len, - ntohl(tcph->seq), - ntohl(tcph->seq) + datalen); - } + /* Check wether the whole IP/address pattern is carried in the payload */ + if (between(exp->seq + ct_irc_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) { + if (!irc_data_fixup(ct_irc_info, ct, pskb, ctinfo, exp)) { UNLOCK_BH(&ip_irc_lock); return NF_DROP; - } else if (score == 2) { - DEBUGP("IRC_NAT: score=2, calling fixup\n"); - if (!irc_data_fixup(ct_irc_info, ct, datalen, - pskb, ctinfo)) { - UNLOCK_BH(&ip_irc_lock); - return NF_DROP; - } - /* skb may have been reallocated */ - iph = (*pskb)->nh.iph; - tcph = (void *) iph + iph->ihl * 4; } + } else { + /* Half a match? This means a partial retransmisison. + It's a cracker being funky. */ + if (net_ratelimit()) { + printk + ("IRC_NAT: partial packet %u/%u in %u/%u\n", + exp->seq, ct_irc_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + } + UNLOCK_BH(&ip_irc_lock); + return NF_DROP; } - UNLOCK_BH(&ip_irc_lock); - ip_nat_seq_adjust(*pskb, ct, ctinfo); - return NF_ACCEPT; } static struct ip_nat_helper ip_nat_irc_helpers[MAX_PORTS]; static char ip_nih_names[MAX_PORTS][6]; -static struct ip_nat_expect irc_expect - = { {NULL, NULL}, irc_nat_expected }; - - /* This function is intentionally _NOT_ defined as __exit, because * it is needed by init() */ static void fini(void) @@ -262,52 +226,50 @@ DEBUGP("ip_nat_irc: unregistering helper for port %d\n", ports[i]); ip_nat_helper_unregister(&ip_nat_irc_helpers[i]); - } - ip_nat_expect_unregister(&irc_expect); + } } + static int __init init(void) { - int ret; + int ret = 0; int i; struct ip_nat_helper *hlpr; char *tmpname; - ret = ip_nat_expect_register(&irc_expect); - if (ret == 0) { - - if (ports[0] == 0) { - ports[0] = 6667; - } + if (ports[0] == 0) { + ports[0] = 6667; + } - for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++) { - hlpr = &ip_nat_irc_helpers[i]; - memset(hlpr, 0, - sizeof(struct ip_nat_helper)); - - hlpr->tuple.dst.protonum = IPPROTO_TCP; - hlpr->tuple.src.u.tcp.port = htons(ports[i]); - hlpr->mask.src.u.tcp.port = 0xFFFF; - hlpr->mask.dst.protonum = 0xFFFF; - hlpr->help = help; - - tmpname = &ip_nih_names[i][0]; - sprintf(tmpname, "irc%2.2d", i); - - hlpr->name = tmpname; - DEBUGP - ("ip_nat_irc: Trying to register helper for port %d: name %s\n", - ports[i], hlpr->name); - ret = ip_nat_helper_register(hlpr); - - if (ret) { - printk - ("ip_nat_irc: error registering helper for port %d\n", - ports[i]); - fini(); - return 1; - } - ports_c++; + for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++) { + hlpr = &ip_nat_irc_helpers[i]; + memset(hlpr, 0, + sizeof(struct ip_nat_helper)); + + hlpr->tuple.dst.protonum = IPPROTO_TCP; + hlpr->tuple.src.u.tcp.port = htons(ports[i]); + hlpr->mask.src.u.tcp.port = 0xFFFF; + hlpr->mask.dst.protonum = 0xFFFF; + hlpr->help = help; + hlpr->expect = irc_nat_expected; + hlpr->mangle = NULL; + + tmpname = &ip_nih_names[i][0]; + sprintf(tmpname, "irc%2.2d", i); + + hlpr->name = tmpname; + DEBUGP + ("ip_nat_irc: Trying to register helper for port %d: name %s\n", + ports[i], hlpr->name); + ret = ip_nat_helper_register(hlpr); + + if (ret) { + printk + ("ip_nat_irc: error registering helper for port %d\n", + ports[i]); + fini(); + return -EBUSY; } + ports_c++; } return ret; } diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_proto_icmp.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_proto_icmp.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_proto_icmp.c Fri Mar 17 18:56:20 2000 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_proto_icmp.c Tue Nov 27 21:25:20 2001 @@ -87,11 +87,18 @@ else return 0; } +static int icmp_exp_matches_pkt(struct ip_conntrack_expect *exp, + struct sk_buff **pskb) +{ + return 1; +} + struct ip_nat_protocol ip_nat_protocol_icmp = { { NULL, NULL }, "ICMP", IPPROTO_ICMP, icmp_manip_pkt, icmp_in_range, icmp_unique_tuple, icmp_print, - icmp_print_range + icmp_print_range, + icmp_exp_matches_pkt }; diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_proto_tcp.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_proto_tcp.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_proto_tcp.c Tue Aug 7 15:30:50 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_proto_tcp.c Tue Nov 27 21:25:20 2001 @@ -5,6 +5,8 @@ #include #include +#include + #include #include #include @@ -140,11 +142,24 @@ else return 0; } +static int tcp_exp_matches_pkt(struct ip_conntrack_expect *exp, + struct sk_buff **pskb) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); + unsigned int datalen; + + datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4; + + return between(exp->seq, ntohl(tcph->seq), ntohl(tcph->seq) + datalen); +} + struct ip_nat_protocol ip_nat_protocol_tcp = { { NULL, NULL }, "TCP", IPPROTO_TCP, tcp_manip_pkt, tcp_in_range, tcp_unique_tuple, tcp_print, - tcp_print_range + tcp_print_range, + tcp_exp_matches_pkt }; diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_proto_udp.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_proto_udp.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_proto_udp.c Fri Aug 4 20:07:24 2000 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_proto_udp.c Tue Nov 27 21:25:20 2001 @@ -132,11 +132,18 @@ else return 0; } +static int udp_exp_matches_pkt(struct ip_conntrack_expect *exp, + struct sk_buff **pskb) +{ + return 1; +} + struct ip_nat_protocol ip_nat_protocol_udp = { { NULL, NULL }, "UDP", IPPROTO_UDP, udp_manip_pkt, udp_in_range, udp_unique_tuple, udp_print, - udp_print_range + udp_print_range, + udp_exp_matches_pkt }; diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_rule.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_rule.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_rule.c Fri Apr 27 21:15:01 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_rule.c Tue Nov 27 21:25:20 2001 @@ -106,8 +106,6 @@ = { { NULL, NULL }, "nat", &nat_initial_table.repl, NAT_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL }; -LIST_HEAD(nat_expect_list); - /* Source NAT */ static unsigned int ipt_snat_target(struct sk_buff **pskb, unsigned int hooknum, @@ -242,19 +240,6 @@ return ip_nat_setup_info(conntrack, &mr, hooknum); } -static inline int call_expect(const struct ip_nat_expect *i, - struct sk_buff **pskb, - unsigned int hooknum, - struct ip_conntrack *ct, - struct ip_nat_info *info, - struct ip_conntrack *master, - struct ip_nat_info *masterinfo, - unsigned int *verdict) -{ - return i->expect(pskb, hooknum, ct, info, master, masterinfo, - verdict); -} - int ip_nat_rule_find(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, @@ -264,41 +249,14 @@ { int ret; - /* Master won't vanish while this ctrack still alive */ - if (ct->master.master) { - struct ip_conntrack *master; - - master = (struct ip_conntrack *)ct->master.master; - if (LIST_FIND(&nat_expect_list, - call_expect, - struct ip_nat_expect *, - pskb, hooknum, ct, info, - master, &master->nat.info, &ret)) - return ret; - } ret = ipt_do_table(pskb, hooknum, in, out, &nat_table, NULL); + if (ret == NF_ACCEPT) { if (!(info->initialized & (1 << HOOK2MANIP(hooknum)))) /* NUL mapping */ ret = alloc_null_binding(ct, info, hooknum); } return ret; -} - -int ip_nat_expect_register(struct ip_nat_expect *expect) -{ - WRITE_LOCK(&ip_nat_lock); - list_prepend(&nat_expect_list, expect); - WRITE_UNLOCK(&ip_nat_lock); - - return 0; -} - -void ip_nat_expect_unregister(struct ip_nat_expect *expect) -{ - WRITE_LOCK(&ip_nat_lock); - LIST_DELETE(&nat_expect_list, expect); - WRITE_UNLOCK(&ip_nat_lock); } static struct ipt_target ipt_snat_reg diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_standalone.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_standalone.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_standalone.c Sun Sep 30 19:26:08 2001 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_standalone.c Tue Nov 27 21:25:20 2001 @@ -5,7 +5,12 @@ */ /* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General - Public Licence. */ + * Public Licence. + * + * 23 Apr 2001: Harald Welte + * - new API and handling of conntrack/nat helpers + * - now capable of multiple expectations for one master + * */ #include #include @@ -43,6 +48,15 @@ : ((hooknum) == NF_IP_LOCAL_OUT ? "LOCAL_OUT" \ : "*ERROR*"))) +static inline int call_expect(struct ip_conntrack *master, + struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + return master->nat.info.helper->expect(pskb, hooknum, ct, info); +} + static unsigned int ip_nat_fn(unsigned int hooknum, struct sk_buff **pskb, @@ -103,8 +117,16 @@ int in_hashes = info->initialized; unsigned int ret; - ret = ip_nat_rule_find(pskb, hooknum, in, out, - ct, info); + if (ct->master + && master_ct(ct)->nat.info.helper + && master_ct(ct)->nat.info.helper->expect) { + ret = call_expect(master_ct(ct), pskb, + hooknum, ct, info); + } else { + ret = ip_nat_rule_find(pskb, hooknum, in, out, + ct, info); + } + if (ret != NF_ACCEPT) { WRITE_UNLOCK(&ip_nat_lock); return ret; @@ -337,8 +359,6 @@ EXPORT_SYMBOL(ip_nat_setup_info); EXPORT_SYMBOL(ip_nat_helper_register); EXPORT_SYMBOL(ip_nat_helper_unregister); -EXPORT_SYMBOL(ip_nat_expect_register); -EXPORT_SYMBOL(ip_nat_expect_unregister); EXPORT_SYMBOL(ip_nat_cheat_check); EXPORT_SYMBOL(ip_nat_mangle_tcp_packet); EXPORT_SYMBOL(ip_nat_seq_adjust); diff -urN linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_talk.c linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_talk.c --- linux-2.4.16-vanilla/net/ipv4/netfilter/ip_nat_talk.c Thu Jan 1 00:00:00 1970 +++ linux-2.4.16-newnat/net/ipv4/netfilter/ip_nat_talk.c Tue Nov 27 21:48:14 2001 @@ -0,0 +1,410 @@ +/* + * 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 otalk=[0|1] talk=[0|1] + * + * The default is otalk=1 talk=1 + * i.e. support both old talk and talk protocols + * + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Default both old talk and talk are supported */ +static int otalk = 1; +static int talk = 1; + +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("talk network address translation module"); +#ifdef MODULE_PARM +MODULE_PARM(otalk, "i"); +MODULE_PARM_DESC(otalk, "support old talk protocol"); +MODULE_PARM(talk, "i"); +MODULE_PARM_DESC(talk, "support talk protocol"); +#endif + +DECLARE_LOCK_EXTERN(ip_talk_lock); + +#if 0 +#define DEBUGP printk +#define IP_NAT_TALK_DEBUG +#else +#define DEBUGP(format, args...) +#endif + +/* FIXME: Time out? --RR */ + +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; + + 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("talk_nat_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("talk_nat_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("talk_nat_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("talk_nat_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("talk_nat_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("talk_nat_expected: IP to %u.%u.%u.%u, port %u\n", NIPQUAD(newip), ntohs(talkinfo->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 }); + } + return ip_nat_setup_info(ct, &mr, hooknum); +} + +static int talk_nat_mangle_msg(struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct sk_buff **pskb, + int version) +{ + struct iphdr *iph = (*pskb)->nh.iph; + unsigned int udplen = (*pskb)->len - iph->ihl * 4; + + return (udplen == sizeof(struct udphdr) + + (version ? sizeof(struct talk_msg) : sizeof(struct otalk_msg))); +} + +static int otalk_nat_mangle(struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct sk_buff **pskb) +{ + return talk_nat_mangle_msg(ct, ctinfo, pskb, OTALK_VERSION); +} + +static int talk_nat_mangle(struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct sk_buff **pskb) +{ + return talk_nat_mangle_msg(ct, ctinfo, pskb, TALK_VERSION); +} + +static int +mangle_packet(struct sk_buff **pskb, + 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; + + /* response: no success || msg in DNAT */ + if (addr->ta_addr == INADDR_ANY || addr->ta_addr == newip) + return 1; + + MUST_BE_LOCKED(&ip_talk_lock); + + 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. */ + + addr->ta_addr = newip; + if (ctl_addr != NULL) { + ctl_addr->ta_addr = newip; + ctl_addr->ta_port = port; + } + + DEBUGP("talk_mangle_packet: orig %u.%u.%u.%u, inserting %u.%u.%u.%u:%u\n", + NIPQUAD(addr->ta_addr), NIPQUAD(newip), ntohs(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("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("talk_help_msg: inserting: %u.%u.%u.%u:%u\n", + NIPQUAD(newip), ntohs(port)); + + if (!mangle_packet(pskb, 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; + unsigned int verdict = NF_ACCEPT; + + DEBUGP("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; + + /* Callee server -> caller client && not simultaneous talk request */ + if (ct_talk_info->port == 0) { + UNLOCK_BH(&ip_talk_lock); + return verdict; + } + + DEBUGP("talk_help_response: talkinfo port %u\n", ntohs(talkinfo->port)); + + /* Change address inside packet to match way we're mapping + this connection. */ + newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].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++) { + t.dst.u.tcp.port = htons(port); + + if (ip_conntrack_change_expect(exp, &t) == 0) { + DEBUGP("talk_help_response: using %u.%u.%u.%u:%u\n", NIPQUAD(newip), ntohs(port)); + break; + } + } + UNLOCK_BH(&ip_talk_lock); + + if (port == 0 || !mangle_packet(pskb, newip, port, addr, NULL)) + verdict = NF_DROP; + + return verdict; +} + +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 version) +{ + 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("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("talk_help: dir %s at hook %s, %u.%u.%u.%u:%u->%u.%u.%u.%u:%u, version %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), + version); + + if (udplen == sizeof(struct udphdr) + + (version ? sizeof(struct talk_msg) : sizeof(struct otalk_msg))) + return talk_help_msg(ct, pskb, + TALK_MSG(data, version, type), + &(TALK_MSG(data, version, addr)), + &(TALK_MSG(data, version, ctl_addr))); + else if (udplen == sizeof(struct udphdr) + + (version ? sizeof(struct talk_response) : sizeof(struct otalk_response))) + return talk_help_response(ct, exp, pskb, + TALK_RESP(data, version, type), + TALK_RESP(data, version, answer), + &(TALK_RESP(data, version, addr))); + else { + DEBUGP("talk_help: not talk message/response, datalen = %u\n", (unsigned)udplen - sizeof(struct udphdr)); + return NF_DROP; + } +} + +static unsigned int ohelp(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, OTALK_VERSION); +} + +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_VERSION); +} + +static struct ip_nat_helper talk_helpers[2] = { + { { NULL, NULL }, + { { 0, { __constant_htons(OTALK_PORT) } }, + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, + { 0, { 0 }, 0xFFFF } }, + ohelp, + talk_nat_expected, + otalk_nat_mangle, + "otalk" }, + { { NULL, NULL }, + { { 0, { __constant_htons(TALK_PORT) } }, + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, + { 0, { 0 }, 0xFFFF } }, + help, + talk_nat_expected, + talk_nat_mangle, + "talk" } }; + +static int __init init(void) +{ + int ret = 0; + + if (otalk > 0) { + ret = ip_nat_helper_register(&talk_helpers[0]); + + if (ret != 0) + return ret; + } + if (talk > 0) { + ret = ip_nat_helper_register(&talk_helpers[1]); + + if (ret != 0 && otalk > 0) + ip_nat_helper_unregister(&talk_helpers[0]); + } + return ret; +} + +static void __exit fini(void) +{ + if (otalk > 0) + ip_nat_helper_unregister(&talk_helpers[0]); + if (talk > 0) + ip_nat_helper_unregister(&talk_helpers[1]); +} + +module_init(init); +module_exit(fini);